/* * (c) Rob Gordon 2005 */ package org.oddjob.state; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Exchanger; import java.util.concurrent.atomic.AtomicBoolean; import junit.framework.TestCase; import org.oddjob.MockStateful; import org.oddjob.Stateful; /** * */ public class JobStateHandlerTest extends TestCase { private void setState(final JobStateHandler handler, final JobState state) { boolean ran = handler.waitToWhen(new IsAnyState(), new Runnable() { public void run() { handler.setState(state); handler.fireEvent(); } }); assertTrue(ran); } private void setException(final JobStateHandler handler, final Exception e) { boolean ran = handler.waitToWhen(new IsAnyState(), new Runnable() { public void run() { handler.setStateException(JobState.EXCEPTION, e); handler.fireEvent(); } }); assertTrue(ran); } public void testAllStates() { JobStateHandler test = new JobStateHandler(new MockStateful()); assertEquals(JobState.READY, test.getState()); assertEquals(JobState.READY, test.lastStateEvent().getState()); setState(test, JobState.EXECUTING); assertEquals(JobState.EXECUTING, test.getState()); assertEquals(JobState.EXECUTING, test.lastStateEvent().getState()); setState(test, JobState.COMPLETE); assertEquals(JobState.COMPLETE, test.getState()); assertEquals(JobState.COMPLETE, test.lastStateEvent().getState()); setState(test, JobState.INCOMPLETE); assertEquals(JobState.INCOMPLETE, test.getState()); assertEquals(JobState.INCOMPLETE, test.lastStateEvent().getState()); setException(test, new Exception()); assertEquals(JobState.EXCEPTION, test.getState()); assertEquals(JobState.EXCEPTION, test.lastStateEvent().getState()); setState( test, JobState.READY); assertEquals(JobState.READY, test.getState()); assertEquals(JobState.READY, test.lastStateEvent().getState()); } private class RecordingStateListener implements StateListener { List<StateEvent> events = new ArrayList<StateEvent>(); public synchronized void jobStateChange(StateEvent event) { events.add(event); } } public void testListenersNotified() { Stateful source = new MockStateful(); JobStateHandler test = new JobStateHandler(source); RecordingStateListener l = new RecordingStateListener(); test.addStateListener(l); assertEquals(1, l.events.size()); assertEquals(JobState.READY, l.events.get(0).getState()); assertEquals(source, l.events.get(0).getSource()); setState(test, JobState.EXECUTING); assertEquals(2, l.events.size()); assertEquals(JobState.EXECUTING, l.events.get(1).getState()); setState(test, JobState.COMPLETE); assertEquals(3, l.events.size()); assertEquals(JobState.COMPLETE, l.events.get(2).getState()); setState(test, JobState.INCOMPLETE); assertEquals(4, l.events.size()); assertEquals(JobState.INCOMPLETE, l.events.get(3).getState()); Exception e = new Exception(); setException(test, e); assertEquals(5, l.events.size()); assertEquals(JobState.EXCEPTION, l.events.get(4).getState()); assertEquals(e, l.events.get(4).getException()); setState(test, JobState.READY); assertEquals(6, l.events.size()); assertEquals(JobState.READY, l.events.get(5).getState()); setState(test, JobState.DESTROYED); assertEquals(7, l.events.size()); assertEquals(JobState.DESTROYED, l.events.get(6).getState()); } public void testDuplicateEventsNotified() { Stateful source = new MockStateful(); JobStateHandler test = new JobStateHandler(source); RecordingStateListener l = new RecordingStateListener(); test.addStateListener(l); assertEquals(1, l.events.size()); assertEquals(JobState.READY, l.events.get(0).getState()); assertEquals(source, l.events.get(0).getSource()); setState(test, JobState.READY); assertEquals(2, l.events.size()); setState(test, JobState.EXECUTING); assertEquals(3, l.events.size()); setState(test, JobState.EXECUTING); assertEquals(4, l.events.size()); assertEquals(JobState.EXECUTING, l.events.get(3).getState()); setState(test, JobState.COMPLETE); assertEquals(5, l.events.size()); assertEquals(JobState.COMPLETE, l.events.get(4).getState()); } public void testManyListeners() throws Exception { RecordingStateListener l1 = new RecordingStateListener(); RecordingStateListener l2 = new RecordingStateListener(); RecordingStateListener l3 = new RecordingStateListener(); final JobStateHandler test = new JobStateHandler(new MockStateful()); Thread t = new Thread(new Runnable() { public void run() { setState(test, JobState.COMPLETE); } }); test.addStateListener(l1); test.addStateListener(l2); test.addStateListener(l3); t.start(); t.join(); assertEquals(2, l1.events.size()); assertEquals(2, l2.events.size()); assertEquals(2, l3.events.size()); assertEquals(JobState.COMPLETE, l1.events.get(1).getState()); assertEquals(JobState.COMPLETE, l2.events.get(1).getState()); assertEquals(JobState.COMPLETE, l3.events.get(1).getState()); } public void testListenerConcurrentModification() { final JobStateHandler test = new JobStateHandler(new MockStateful()); test.addStateListener(new StateListener() { public void jobStateChange(StateEvent event) { if (event.getState() == JobState.COMPLETE) { test.removeStateListener(this); } } }); assertEquals(1, test.listenerCount()); test.waitToWhen(new IsAnyState(), new Runnable() { @Override public void run() { test.setState(JobState.COMPLETE); test.fireEvent(); } }); assertEquals(0, test.listenerCount()); } public void testThatAttemptsToChangeState() { final JobStateHandler test = new JobStateHandler(new MockStateful()); StateListener listener = new StateListener() { @Override public void jobStateChange(StateEvent event) { test.waitToWhen(new IsAnyState(), new Runnable() { @Override public void run() { test.setState(JobState.COMPLETE); test.fireEvent(); } }); } }; try { test.addStateListener(listener); fail("Expected to fail."); } catch (IllegalStateException e) { // expected } final AtomicBoolean failed = new AtomicBoolean(); StateListener listener2 = new StateListener() { @Override public void jobStateChange(final StateEvent event) { if (JobState.INCOMPLETE == event.getState()) { try { test.setState(JobState.COMPLETE); test.fireEvent(); } catch (IllegalStateException e) { failed.set(true); } } } }; test.addStateListener(listener2); test.waitToWhen(new IsAnyState(), new Runnable() { @Override public void run() { test.setState(JobState.INCOMPLETE); test.fireEvent(); } }); assertTrue(failed.get()); assertEquals(JobState.INCOMPLETE, test.lastStateEvent().getState()); } public void testListenerNotificationOrder() throws InterruptedException { final JobStateHandler test = new JobStateHandler(new MockStateful()); final Exchanger<Void> exchanger = new Exchanger<Void>(); test.addStateListener(new StateListener() { @Override public void jobStateChange(StateEvent event) { try { if (event.getState() == JobState.COMPLETE) { exchanger.exchange(null); exchanger.exchange(null); } } catch (InterruptedException e) { throw new RuntimeException("Unexpected", e); } } }); final List<State> events = new ArrayList<State>(); StateListener listener = new StateListener() { @Override public void jobStateChange(StateEvent event) { events.add(event.getState()); } }; new Thread() { @Override public void run() { test.waitToWhen(new IsAnyState(), new Runnable() { @Override public void run() { test.setState(JobState.COMPLETE); test.fireEvent(); } }); } }.start(); exchanger.exchange(null); exchanger.exchange(null); // able to get event before other listener complete. test.addStateListener(listener); assertEquals(JobState.COMPLETE, events.get(0)); assertEquals(1, events.size()); assertEquals(JobState.COMPLETE, test.lastStateEvent().getState()); } public void testSleep() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); final JobStateHandler test = new JobStateHandler(new MockStateful()); final List<State> events = new ArrayList<State>(); StateListener listener = new StateListener() { @Override public void jobStateChange(StateEvent event) { events.add(event.getState()); } }; test.addStateListener(listener); Thread t1 = new Thread(new Runnable() { @Override public void run() { test.waitToWhen(new IsAnyState(), new Runnable() { @Override public void run() { latch.countDown(); try { test.sleep(0); } catch (InterruptedException e) { throw new RuntimeException(e); } test.setState(JobState.COMPLETE); test.fireEvent(); } }); } }); Thread t2 = new Thread() { @Override public void run() { try { latch.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } test.waitToWhen(new IsAnyState(), new Runnable() { @Override public void run() { test.setState(JobState.INCOMPLETE); test.fireEvent(); test.wake(); } }); } }; t1.start(); t2.start(); t1.join(); t2.join(); assertEquals(JobState.READY, events.get(0)); assertEquals(JobState.INCOMPLETE, events.get(1)); assertEquals(JobState.COMPLETE, events.get(2)); assertEquals(3, events.size()); } }