/* * Copyright 2012 Jason Miller * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jj.execution; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.*; import static org.mockito.BDDMockito.*; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import jj.JJServerLifecycle; import jj.event.MockPublisher; import jj.logging.Emergency; import jj.script.ScriptEnvironment; import jj.util.MockClock; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class TaskRunnerImplTest { private Runnable monitorTask; private @Mock ScriptEnvironment<?> scriptEnvironment; private @Mock Executors executors; private CurrentTask currentTask; private MockPublisher publisher; private TaskRunnerImpl executor; private @Captor ArgumentCaptor<JJTask<?>> taskCaptor; private @Captor ArgumentCaptor<Runnable> runnableCaptor; private String baseName = "test"; private MockClock clock = new MockClock(); private @Mock JJServerLifecycle lifecycle; @Before public void before() { currentTask = new CurrentTask(); executor = new TaskRunnerImpl(executors, currentTask, publisher = new MockPublisher(), clock, lifecycle); verify(executors).executeTask(taskCaptor.capture(), runnableCaptor.capture()); monitorTask = runnableCaptor.getValue(); reset(executors); given(scriptEnvironment.name()).willReturn(baseName); } private Runnable getRunnable() { verify(executors).executeTask(taskCaptor.capture(), runnableCaptor.capture()); // reset after pulling a runnable so that a test can control execution // one task at a time reset(executors); return runnableCaptor.getValue(); } private void runTask() { getRunnable().run(); } @Test public void testPromiseKeeping() { final AtomicInteger counter = new AtomicInteger(); ServerTask task1 = new ServerTask("test task 1") { @Override protected void run() throws Exception { counter.incrementAndGet(); } }; ServerTask task2 = new ServerTask("test task 2") { @Override protected void run() throws Exception { counter.incrementAndGet(); } }; ServerTask task3 = new ServerTask("test task 3") { @Override protected void run() throws Exception { counter.incrementAndGet(); } }; executor.execute(task1).then(task2).then(task3); runTask(); runTask(); runTask(); assertThat(counter.get(), is(3)); } @Test public void testExecuteTask() { final AtomicBoolean flag = new AtomicBoolean(false); ServerTask task = new ServerTask("test task") { @Override protected void run() throws Exception { flag.set(currentTask.current() == this); } }; assertThat(currentTask.current(), is(nullValue())); executor.execute(task); runTask(); assertThat(flag.get(), is(true)); assertThat(currentTask.current(), is(nullValue())); } @Test public void testExecuteErrorLogged() { final Exception toThrow = new Exception(); executor.execute(new ServerTask("test task") { @Override protected void run() throws Exception { throw toThrow; } }); runTask(); assertThat(publisher.events.get(0), is(instanceOf(Emergency.class))); } @Test public void testInterruption() throws Throwable { final CountDownLatch latch1 = new CountDownLatch(1); final CountDownLatch latch2 = new CountDownLatch(1); final AtomicBoolean completed = new AtomicBoolean(); final AtomicBoolean interrupted = new AtomicBoolean(); final ServerTask task = new ServerTask("interruption test task") { @Override protected void run() throws Exception { latch1.countDown(); try { Thread.sleep(300); completed.set(true); } catch (InterruptedException ie) { interrupted.set(true); latch2.countDown(); throw ie; } } }; final ServerTask task2 = new ServerTask("promised and ignored test task") { @Override protected void run() throws Exception { // doesn't even matter, just shouldn't end up even trying } }; executor.execute(task).then(task2); Thread runningThread = new Thread(getRunnable()); runningThread.setDaemon(true); runningThread.start(); assertTrue(latch1.await(500, MILLISECONDS)); task.interrupt(); assertTrue(latch2.await(500, MILLISECONDS)); assertTrue(interrupted.get()); assertFalse(completed.get()); verifyZeroInteractions(executors); } @Ignore // this refuses to work consistently @Test public void testMonitor() throws Throwable { // given // okay, gotta start the monitorTask up to test it, right? final Thread t = new Thread(monitorTask, "test thread"); t.setDaemon(true); t.start(); final ServerTask task = new ServerTask("test task") { @Override protected void run() throws Exception { System.out.println("hello darkness my old friend"); } }; // still in the given, we're putting a task in the queue executor.execute(task); // and expiring it clock.advance(TaskTracker.MAX_QUEUED_TIME + 1, MILLISECONDS); // and we need to coordinate final CountDownLatch latch = new CountDownLatch(1); publisher.onPublish = event -> { System.out.println("totally got called " + event); latch.countDown(); }; // now we need to trigger the monitor to wake up. this requires yet another // thread. this one is not a daemon on purpose new Thread(() -> { System.out.println("well at least i got called"); // causes the queue to get polled and have the system realize something has expired // takes advantage of the implementation of DelayQueue, so not ideal, but i got nothing // else! executor.execute(task); reset(executors); }); //.start(); assertTrue("timed out", latch.await(2, SECONDS)); assertThat(publisher.events.get(0), is(instanceOf(Emergency.class))); } }