package org.oddjob.jobs.structural;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import junit.framework.TestCase;
import org.oddjob.FailedToStopException;
import org.oddjob.Stateful;
import org.oddjob.Stoppable;
import org.oddjob.framework.JobDestroyedException;
import org.oddjob.framework.SimpleJob;
import org.oddjob.scheduling.MockScheduledExecutorService;
import org.oddjob.scheduling.MockScheduledFuture;
import org.oddjob.state.FlagState;
import org.oddjob.state.JobState;
import org.oddjob.state.JobStateHandler;
import org.oddjob.state.ParentState;
import org.oddjob.state.StateEvent;
import org.oddjob.state.StateListener;
import org.oddjob.tools.StateSteps;
public class ParallelJobInStepsTest extends TestCase {
private class ManualExecutor1 extends MockScheduledExecutorService {
private Runnable runnable;
public Future<?> submit(Runnable runnable) {
if (this.runnable != null) {
throw new IllegalStateException();
}
this.runnable = runnable;
return new MockScheduledFuture<Void>();
}
}
public void testStatesExecutingAndCompletingOneJob() {
FlagState job1 = new FlagState(JobState.COMPLETE);
ManualExecutor1 executor = new ManualExecutor1();
ParallelJob test = new ParallelJob();
StateSteps steps = new StateSteps(test);
steps.startCheck(ParentState.READY,
ParentState.EXECUTING, ParentState.ACTIVE);
test.setExecutorService(executor);
test.setJobs(0, job1);
test.run();
steps.checkNow();
assertNotNull(executor.runnable);
steps.startCheck(ParentState.ACTIVE,
ParentState.COMPLETE);
executor.runnable.run();
steps.checkNow();
steps.startCheck(ParentState.COMPLETE, ParentState.DESTROYED);
test.destroy();
steps.checkNow();
}
private class ManualExecutor2 extends MockScheduledExecutorService {
boolean cancelled;
public Future<?> submit(Runnable runnable) {
return new MockScheduledFuture<Void>() {
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
assertFalse(mayInterruptIfRunning);
cancelled = true;
return true;
}
};
}
}
private class CaptureStoppedJob extends SimpleJob
implements Stoppable {
@Override
protected int execute() throws Throwable {
throw new RuntimeException("Unexpected");
}
}
public void testStatesWhenStoppingJobThatHasntExecuted() throws FailedToStopException {
CaptureStoppedJob job1 = new CaptureStoppedJob();
ManualExecutor2 executor = new ManualExecutor2();
ParallelJob test = new ParallelJob();
StateSteps steps = new StateSteps(test);
steps.startCheck(ParentState.READY,
ParentState.EXECUTING, ParentState.ACTIVE);
test.setExecutorService(executor);
test.setJobs(0, job1);
test.run();
steps.checkNow();
steps.startCheck(ParentState.ACTIVE,
ParentState.READY);
test.stop();
steps.checkNow();
assertEquals(false, job1.isStop());
assertEquals(true, executor.cancelled);
steps.startCheck(ParentState.READY, ParentState.DESTROYED);
test.destroy();
steps.checkNow();
}
public void testStatesWhenDestroyedWithoutStoppingJobThatHasntExecuted() {
CaptureStoppedJob job1 = new CaptureStoppedJob();
ManualExecutor2 executor = new ManualExecutor2();
ParallelJob test = new ParallelJob();
StateSteps steps = new StateSteps(test);
steps.startCheck(ParentState.READY,
ParentState.EXECUTING, ParentState.ACTIVE);
test.setExecutorService(executor);
test.setJobs(0, job1);
test.run();
steps.checkNow();
steps.startCheck(ParentState.ACTIVE,
ParentState.DESTROYED);
test.destroy();
assertEquals(false, job1.isStop());
assertEquals(true, executor.cancelled);
steps.checkNow();
}
private class ManualExecutor3 extends MockScheduledExecutorService {
boolean cancelled;
Runnable runnable;
public Future<?> submit(Runnable runnable) {
this.runnable = runnable;
return new MockScheduledFuture<Void>() {
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
assertFalse(mayInterruptIfRunning);
cancelled = true;
return false;
}
};
}
}
private class AsyncJob implements Stateful, Runnable, Stoppable {
private final JobStateHandler stateHandler =
new JobStateHandler(this);
private void sendEvent(final JobState jobState) {
stateHandler.callLocked(new Callable<Void>() {
@Override
public Void call() throws Exception {
stateHandler.setState(jobState);
stateHandler.fireEvent();
return null;
}
});
}
@Override
public void run() {
sendEvent(JobState.EXECUTING);
}
@Override
public void addStateListener(StateListener listener)
throws JobDestroyedException {
stateHandler.addStateListener(listener);
}
@Override
public void removeStateListener(StateListener listener) {
stateHandler.removeStateListener(listener);
}
@Override
public void stop() throws FailedToStopException {
sendEvent(JobState.COMPLETE);
}
@Override
public StateEvent lastStateEvent() {
return stateHandler.lastStateEvent();
}
}
public void testStatesWhenStoppingAsyncJobThatIsStillExecuting() throws FailedToStopException {
AsyncJob job1 = new AsyncJob();
ManualExecutor3 executor = new ManualExecutor3();
ParallelJob test = new ParallelJob();
StateSteps steps = new StateSteps(test);
steps.startCheck(ParentState.READY,
ParentState.EXECUTING, ParentState.ACTIVE);
test.setExecutorService(executor);
test.setJobs(0, job1);
test.run();
executor.runnable.run();
steps.checkNow();
steps.startCheck(ParentState.ACTIVE,
ParentState.COMPLETE);
test.stop();
steps.checkNow();
assertEquals(JobState.COMPLETE, job1.lastStateEvent().getState());
assertEquals(true, executor.cancelled);
steps.startCheck(ParentState.COMPLETE, ParentState.DESTROYED);
test.destroy();
steps.checkNow();
}
public void testStatesWhenDestroyedWithoutStoppingAsyncJobThatIsStillExecuting() {
AsyncJob job1 = new AsyncJob();
ManualExecutor3 executor = new ManualExecutor3();
ParallelJob test = new ParallelJob();
StateSteps steps = new StateSteps(test);
steps.startCheck(ParentState.READY,
ParentState.EXECUTING, ParentState.ACTIVE);
test.setExecutorService(executor);
test.setJobs(0, job1);
test.run();
executor.runnable.run();
steps.checkNow();
steps.startCheck(ParentState.ACTIVE,
ParentState.DESTROYED);
test.destroy();
steps.checkNow();
assertEquals(JobState.EXECUTING, job1.lastStateEvent().getState());
assertEquals(true, executor.cancelled);
}
}