package com.github.drapostolos.rdp4j; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.mockito.Mockito.never; import java.io.IOException; import java.util.LinkedHashSet; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.assertj.core.api.Assertions; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import com.github.drapostolos.rdp4j.spi.FileElement; import com.github.drapostolos.rdp4j.spi.PolledDirectory; public class AcceptanceTest extends EventVerifier { private DirectoryPoller dp; private DirectoryPollerBuilder builder; @Before public void testFixture() throws Exception { directoryMock = Mockito.mock(PolledDirectory.class); listenerMock = Mockito.mock(AbstractRdp4jListener.class); inOrder = Mockito.inOrder(listenerMock); builder = DirectoryPoller.newBuilder(); } @After public void cleanup() throws Exception { if (dp != null && !dp.isTerminated()) { dp.stop(); } } @Test(timeout = 1000) public void canInterruptBeforePollingCycle() throws Exception { // given final StringBuffer checker = new StringBuffer(); final CountDownLatch latch = new CountDownLatch(1); Mockito.when(directoryMock.listFiles()) .thenReturn(list("file1.txt/1")); // when dp = builder .addPolledDirectory(directoryMock) .addListener(new AbstractRdp4jListener() { @Override public void beforePollingCycle(BeforePollingCycleEvent event) throws InterruptedException { latch.countDown(); SECONDS.sleep(5); } @Override public void afterPollingCycle(AfterPollingCycleEvent event) throws InterruptedException { checker.append("X"); // this should not be called. } @Override public void afterStop(AfterStopEvent event) { checker.append("PASS"); } }) .enableFileAddedEventsForInitialContent() .setPollingInterval(10, TimeUnit.MILLISECONDS) .start(); // then latch.await(); dp.stopNow(); assertThat(checker.toString()).isEqualTo("PASS"); } @Test(timeout = 1000) public void canInterruptAfterPollingCycle() throws Exception { // given final StringBuffer checker = new StringBuffer(); final CountDownLatch latch = new CountDownLatch(1); Mockito.when(directoryMock.listFiles()) .thenReturn(list("file1.txt/1")); // when dp = builder .addPolledDirectory(directoryMock) .addListener(new AbstractRdp4jListener() { @Override public void afterPollingCycle(AfterPollingCycleEvent event) throws InterruptedException { latch.countDown(); SECONDS.sleep(5); } @Override public void afterStop(AfterStopEvent event) { checker.append("PASS"); } }) .enableFileAddedEventsForInitialContent() .setPollingInterval(10, TimeUnit.MILLISECONDS) .start(); // then latch.await(); dp.stopNow(); assertThat(checker.toString()).isEqualTo("PASS"); } @Test(timeout = 1000) public void canInterruptFileAddedEvent() throws Exception { // given final StringBuffer checker = new StringBuffer(); final CountDownLatch latch = new CountDownLatch(1); Mockito.when(directoryMock.listFiles()) .thenReturn(list("file1.txt/1")); // when dp = builder .addPolledDirectory(directoryMock) .addListener(new AbstractRdp4jListener() { @Override public void fileAdded(FileAddedEvent event) throws InterruptedException { latch.countDown(); SECONDS.sleep(5); } @Override public void afterPollingCycle(AfterPollingCycleEvent event) throws InterruptedException { checker.append("X"); // this should not be called. } @Override public void afterStop(AfterStopEvent event) { checker.append("PASS"); } }) .enableFileAddedEventsForInitialContent() .setPollingInterval(10, TimeUnit.MILLISECONDS) .start(); // then latch.await(); dp.stopNow(); assertThat(checker.toString()).isEqualTo("PASS"); } @Test(timeout = 1000) public void canInterruptWhenProcessingMultipleListeners() throws Exception { // given final CountDownLatch latch = new CountDownLatch(1); Mockito.when(directoryMock.listFiles()) .thenReturn(list("file1.txt/1")); // when dp = builder .addPolledDirectory(directoryMock) .addListener(new AbstractRdp4jListener() { @Override public void beforePollingCycle(BeforePollingCycleEvent event) throws InterruptedException { latch.countDown(); SECONDS.sleep(10); } }) .addListener(new AbstractRdp4jListener() { @Override public void beforePollingCycle(BeforePollingCycleEvent event) throws InterruptedException { latch.countDown(); SECONDS.sleep(10); } }) .enableFileAddedEventsForInitialContent() .setPollingInterval(10, TimeUnit.MILLISECONDS) .start(); // then latch.await(); dp.stopNow(); } @Test public void canStartAsynchronously() throws Exception { // given final CountDownLatch latch = new CountDownLatch(1); // when DirectoryPollerFuture future = builder .addPolledDirectory(directoryMock) .addListener(new AbstractRdp4jListener() { @Override public void beforeStart(BeforeStartEvent event) { try { latch.await(); } catch (InterruptedException e) { fail("should not be interrupted here!"); } } }) .setPollingInterval(10, TimeUnit.MILLISECONDS) .startAsync(); assertThat(future.isStarted()).isFalse(); latch.countDown(); dp = future.get(); assertThat(dp).isNotNull(); assertThat(future.isStarted()).isTrue(); } @Test(timeout = 2000) public void shouldStopDirectoryPollerWhenRuntimeExceptionIsThrownByImplementation() throws Exception { // given Mockito.when(directoryMock.listFiles()) .thenReturn(list()) .thenThrow(new RuntimeException()); // when dp = builder .addPolledDirectory(new PolledDirectory() { @Override public Set<FileElement> listFiles() throws IOException { throw new RuntimeException(); } }) .setPollingInterval(10, TimeUnit.MILLISECONDS) .start(); // then should stop automatically. dp.awaitTermination(); } @Test public void enableParallelDirectoryPolling() throws Exception { // given Mockito.when(directoryMock.listFiles()) .thenReturn(list()); // when dp = builder .addPolledDirectory(directoryMock) .enableParallelPollingOfDirectories() .setPollingInterval(10, TimeUnit.MILLISECONDS) .start(); // then Assertions.assertThat(dp.isParallelDirectoryPollingEnabled()).isTrue(); } @Test public void defaultParallelDirectoryPolling() throws Exception { // given Mockito.when(directoryMock.listFiles()) .thenReturn(list()); // when dp = builder .addPolledDirectory(directoryMock) .start(); // then Assertions.assertThat(dp.isParallelDirectoryPollingEnabled()).isFalse(); } @Test public void enableFileAddedEventsForInitialContent() throws Exception { // given Mockito.when(directoryMock.listFiles()) .thenReturn(list()); // when dp = builder .addPolledDirectory(directoryMock) .enableFileAddedEventsForInitialContent() .setPollingInterval(10, TimeUnit.MILLISECONDS) .start(); // then Assertions.assertThat(dp.isFileAdedEventForInitialContentEnabled()).isTrue(); } @Test public void defaultFileAddedEventsForInitialContent() throws Exception { // given Mockito.when(directoryMock.listFiles()) .thenReturn(list()); // when dp = builder .addPolledDirectory(directoryMock) .start(); // then Assertions.assertThat(dp.isFileAdedEventForInitialContentEnabled()).isFalse(); } @Test public void fileAddedEventEnabledForInitialContent() throws Exception { // given Mockito.when(directoryMock.listFiles()) .thenReturn(list("file1.txt/1")); // when dp = builder .addPolledDirectory(directoryMock) .addListener(new PollCycleCounter().stopPollingAfterNumOfCycles(2)) .addListener(listenerMock) .enableFileAddedEventsForInitialContent() .setPollingInterval(10, TimeUnit.MILLISECONDS) .start(); dp.awaitTermination(); // then verifyEventsInOrder( BeforeStartEvent.class, BeforePollingCycleEvent.class, FileAddedEvent.class, InitialContentEvent.class, AfterPollingCycleEvent.class, BeforePollingCycleEvent.class, AfterPollingCycleEvent.class, AfterStopEvent.class); } @Test public void addRemoveDirectories() throws Exception { // given PolledDirectory directoryMock2 = Mockito.mock(PolledDirectory.class); // when PollCycleCounter latch = new PollCycleCounter(); dp = builder .addPolledDirectory(directoryMock) .addListener(latch) .setPollingInterval(5, TimeUnit.MILLISECONDS) .start(); dp.addPolledDirectory(directoryMock2); // then latch.awaitAtLeastNumPollCycles(1); Assertions.assertThat(dp.getPolledDirectories()).contains(directoryMock, directoryMock2); // when dp.removePolledDirectory(directoryMock); dp.removePolledDirectory(directoryMock2); // then latch.awaitAtLeastNumPollCycles(1); Assertions.assertThat(dp.getPolledDirectories()).isEmpty(); } // listenerCanReceiveFileAddedEventWhenListenerAddedAfterStart @Test public void listenerCanReceiveFileAddedEventWhenListenerAddedAfterStart() throws Exception { // given Mockito.when(directoryMock.listFiles()) .thenReturn(list()) .thenReturn(list()) .thenReturn(list("file.txt/12")); // when PollCycleCounter latch = new PollCycleCounter(); dp = DirectoryPoller.newBuilder() .addListener(latch) .addPolledDirectory(directoryMock) .setPollingInterval(10, MILLISECONDS) .start(); dp.addListener(listenerMock); latch.awaitAtLeastNumPollCycles(5); dp.stop(); // then verifyEventsInOrder( BeforePollingCycleEvent.class, FileAddedEvent.class, AfterPollingCycleEvent.class); } @Test public void removeListener() throws Exception { // given Mockito.when(directoryMock.listFiles()) .thenReturn(list()) .thenReturn(list("file.txt/12")); // when PollCycleCounter counter = new PollCycleCounter(); dp = builder .addPolledDirectory(directoryMock) .addListener(counter) .addListener(listenerMock) .setPollingInterval(10, TimeUnit.MILLISECONDS) .start(); counter.awaitAtLeastNumPollCycles(1); dp.removeListener(listenerMock); counter.awaitAtLeastNumPollCycles(1); verifyEventsInOrder( BeforePollingCycleEvent.class, AfterPollingCycleEvent.class); Mockito.verify(listenerMock, never()).afterStop(Mockito.any(AfterStopEvent.class)); } @Test public void toStringValue() throws Exception { // given Mockito.when(directoryMock.listFiles()) .thenReturn(list()); // when dp = builder .addPolledDirectory(directoryMock) .start(); Assertions.assertThat(dp.toString()).matches("DirectoryPoller-\\d+: .*\\[polling every: 1000 milliseconds\\]"); } @Test public void OneSuccesfulPoll() throws Exception { // given Mockito.when(directoryMock.listFiles()) .thenReturn(list()); // when PollCycleCounter counter = new PollCycleCounter(); dp = builder .addListener(listenerMock) .addListener(counter.stopPollingAfterNumOfCycles(1)) .addPolledDirectory(directoryMock) .setPollingInterval(20, MILLISECONDS) .start(); dp.awaitTermination(); // then Assertions.assertThat(dp.getThreadName()).matches("DirectoryPoller-\\d+"); verifyEventsInOrder( BeforeStartEvent.class, BeforePollingCycleEvent.class, InitialContentEvent.class, AfterPollingCycleEvent.class, AfterStopEvent.class); } @Test public void OneSuccesfulPollUsingFileFilter() throws Exception { // given Mockito.when(directoryMock.listFiles()) .thenReturn(list("a.txt/12", "b.xml/11")); final Set<FileElement> files = new LinkedHashSet<FileElement>(); Mockito.doAnswer(new Answer<InitialContentEvent>() { @Override public InitialContentEvent answer(InvocationOnMock invocation) throws Throwable { Set<FileElement> s = ((InitialContentEvent) invocation.getArguments()[0]).getFiles(); files.addAll(s); return null; } }).when(listenerMock).initialContent(Mockito.any(InitialContentEvent.class)); // when dp = builder .addListener(new PollCycleCounter().stopPollingAfterNumOfCycles(1)) .addListener(listenerMock) .addPolledDirectory(directoryMock) .setDefaultFileFilter(new RegexFileFilter(".*\\.txt")) .setThreadName("NAME") .setPollingInterval(20, TimeUnit.MILLISECONDS) .start(); dp.awaitTermination(); // then Assertions.assertThat(files).containsExactly(array("a.txt/12")); Assertions.assertThat(dp.getThreadName()).isEqualTo("NAME"); Assertions.assertThat(dp.getPollingIntervalInMillis()).isEqualTo(20); verifyEventsInOrder( BeforeStartEvent.class, BeforePollingCycleEvent.class, InitialContentEvent.class, AfterPollingCycleEvent.class, AfterStopEvent.class); Mockito.verifyNoMoreInteractions(listenerMock); } }