package com.constellio.data.threads; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.joda.time.Duration; import org.joda.time.LocalTime; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import com.constellio.sdk.tests.ConstellioTest; import com.constellio.sdk.tests.annotations.SlowTest; public class BackgroundThreadsManagerAcceptTest extends ConstellioTest { AtomicInteger counter = new AtomicInteger(); List<LocalTime> action1ThreadActionCallsTime = new ArrayList<>(); BackgroundThreadsManager backgroundThreadsManager; @Before public void setUp() throws Exception { givenBackgroundThreadsEnabled(); backgroundThreadsManager = getDataLayerFactory().getBackgroundThreadsManager(); backgroundThreadsManager.initialize(); } @After public void tearDown() { backgroundThreadsManager.close(); } @SlowTest @Test public void givenSystemIsNotYetStartedThenWaitUntilStartedBeforeExecuting() throws Exception { backgroundThreadsManager.systemStarted.set(false); Runnable threadAction = spy(new TestSleepingRunnable(50, counter)); backgroundThreadsManager.configure(BackgroundThreadConfiguration.repeatingAction("action1", threadAction).executedEvery( Duration.standardSeconds(1))); Thread.sleep(5000); assertThat(counter.get()).isZero(); backgroundThreadsManager.systemStarted.set(true); Thread.sleep(5000); assertThat(counter.get()).isGreaterThan(1); backgroundThreadsManager.close(); } @SlowTest @Test public void whenConfiguringAThreadToExecuteAnActionOf2SecondsEvery3SecondsThenWait1SecondsBetweenRuns() throws Exception { Runnable threadAction = spy(new TestSleepingRunnable(50, counter)); backgroundThreadsManager.configure(BackgroundThreadConfiguration.repeatingAction("action1", threadAction).executedEvery( Duration.standardSeconds(1))); Thread.sleep(5000); assertThat(counter.get()).isBetween(4, 6); backgroundThreadsManager.close(); Thread.sleep(2000); int counter1 = counter.get(); Thread.sleep(3000); int counter2 = counter.get(); assertThat(counter1).isEqualTo(counter2); } @SlowTest @Test public void givenBackgroundThreadConfiguredToContinueOnExceptionThenContinueUntilAnErrorOccur() throws InterruptedException { Runnable runnable = mock(Runnable.class); doAnswer(increaseAndThrowException()).doAnswer(increaseAndThrowException()).doAnswer(increaseAndThrowError()) .doAnswer(increase()).when(runnable).run(); backgroundThreadsManager.configure(BackgroundThreadConfiguration.repeatingAction("action", runnable).executedEvery( Duration.standardSeconds(1)).handlingExceptionWith(BackgroundThreadExceptionHandling.CONTINUE)); Thread.sleep(10000); assertThat(counter.get()).isBetween(9, 11); } @SlowTest @Test public void givenBackgroundThreadConfiguredToStopOnExceptionThenStopAtTheFirstRuntimeException() throws InterruptedException { Runnable runnable = mock(Runnable.class); doAnswer(increaseAndThrowException()).doAnswer(increase()).when(runnable).run(); backgroundThreadsManager.configure(BackgroundThreadConfiguration.repeatingAction("action", runnable).executedEvery( Duration.standardSeconds(1)).handlingExceptionWith(BackgroundThreadExceptionHandling.STOP)); Thread.sleep(10000); assertThat(counter.get()).isEqualTo(1); } private Answer<Object> increaseAndThrowError() { return new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { counter.incrementAndGet(); throw new Error("** RuntimeException thrown by the test**"); } }; } private Answer<Object> increaseAndThrowException() { return new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { counter.incrementAndGet(); throw new RuntimeException("** RuntimeException thrown by the test**"); } }; } private Answer<Object> increase() { return new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { counter.incrementAndGet(); return null; } }; } public static class TestSleepingRunnable implements Runnable { private AtomicInteger atomicInteger; private int ms; public TestSleepingRunnable(int ms, AtomicInteger atomicInteger) { this.ms = ms; this.atomicInteger = atomicInteger; } @Override public void run() { System.out.println("Run!"); atomicInteger.incrementAndGet(); try { Thread.sleep(ms); } catch (InterruptedException e) { throw new RuntimeException(e); } } } }