/* * org.openmicroscopy.shoola.util.concur.tasks.TestExecCommandStateTrans * *------------------------------------------------------------------------------ * Copyright (C) 2006 University of Dundee. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *------------------------------------------------------------------------------ */ package org.openmicroscopy.shoola.util.concur.tasks; //Java imports //Third-party libraries import junit.framework.TestCase; //Application-internal dependencies import org.openmicroscopy.shoola.util.concur.ThreadSupport; /** * Verifies that {@link ExecCommand} performs the correct state transitions * in a single-threaded environment. * Also verifies state-dependent actions and message acceptance. * * @author Jean-Marie Burel      * <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> * @author <br>Andrea Falconi      * <a href="mailto:a.falconi@dundee.ac.uk"> * a.falconi@dundee.ac.uk</a> * @version 2.2 * <small> * (<b>Internal version:</b> $Revision$ $Date$) * </small> * @since OME2.2 */ public class TestExecCommandStateTrans extends TestCase { private ExecCommand target; //Object under test. private int tState; //To transfer target's state; private boolean cancel; //Tells whether to cancel target's exec. private boolean nestedRun; //Tells whether to nest target.run(). private class CancelTask //Instantiated as the task that target will exec. implements Runnable { public void run() { tState = target.getState(); if (cancel) target.cancel(); if (nestedRun) target.run(); } } private void transitionToCancelled() { cancel = true; /* The above will result in the interrupted status of the thread * running target.run() being set. So if we invoke it w/in the * JUnit thread, we'll have to clear the interrupted status upon * returning from this test method. B/c implementation characteristics * of interruption-based methods are uncertain, we use a separate * thread to execute target.run() and then we simply discard that * thread. This way we won't interrupt JUnit and we avoid any possible * side effect. */ ThreadSupport ts = new ThreadSupport(new Runnable() { //Alt flow. public void run() { target.run(); } }); ts.startAltFlow(); ts.awaitAltFlow(); } public void setUp() { NullExecMonitor rethrower = new NullExecMonitor() { public void onAbort(Throwable cause) { throw new RuntimeException(cause); } }; Future future = new Future();//Not in a legal state yet (two-step init). target = new ExecCommand(new TaskAdapter(new CancelTask()), new NullResultAssembler(), future, rethrower); future.setCommand(target); //OK, init completed now. cancel = false; //Only set to true in transitionToCancelled(). nestedRun = false; //true causes an Error when calling target.run(). } public void testNewReady() { assertEquals("State should be READY after creation.", ExecCommand.READY, target.getState()); } public void testReadyCancelled() { target.cancel(); assertEquals("Should transition to CANCELLED if cancel() is dispatched"+ " when the object is in the READY state.", ExecCommand.CANCELLED, target.getState()); } public void testReadyExecuting() { target.run(); assertEquals("State should be EXECUTING if run() is dispatched "+ "when the object is in the READY state.", ExecCommand.EXECUTING, tState); //Grabbed w/in run(). } public void testExecutingFinished() { target.run(); assertEquals("State should be FINISHED if run() terminates without "+ "being cancelled.", ExecCommand.FINISHED, target.getState()); } public void testExecutingCancelled() { transitionToCancelled(); assertEquals("State should be CANCELLED if run() terminates because "+ "of cancellation.", ExecCommand.CANCELLED, target.getState()); } public void testCancelWhenCancelled() { transitionToCancelled(); target.cancel(); assertEquals("cancel() should do nothing if dispatched when the object"+ " is in the CANCELLED state and the state shouldn't change.", ExecCommand.CANCELLED, target.getState()); } public void testRunWhenCancelled() { transitionToCancelled(); tState = -1; target.run(); //Should do nothing (normally sets state = target.state). assertEquals("run() shouldn't change the state if dispatched when the "+ "object is in the CANCELLED state.", ExecCommand.CANCELLED, target.getState()); assertEquals("Service shouldn't be executed when the object is in the "+ "CANCELLED state.", -1, tState); } public void testCancelWhenFinished() { target.run(); target.cancel(); assertEquals("cancel() should do nothing if dispatched when the object"+ " is in the FINISHED state and the state shouldn't change.", ExecCommand.FINISHED, target.getState()); } public void testRunWhenFinished() { target.run(); try { tState = -1; target.run(); fail("run() should error if dispatched when the object is in "+ "the FINISHED state."); } catch (Error e) { //OK, expected, but check the state: assertEquals("run() shouldn't change the state if dispatched when "+ "the object is in the FINISHED state.", ExecCommand.FINISHED, target.getState()); assertEquals("Service shouldn't be executed when the object is in"+ " the FINISHED state.", -1, tState); } } public void testRunWhenExecuting() { nestedRun = true; //Enables nested target.run() call. try { target.run(); //Will call target.run() again. fail("run() should error if dispatched more than once when "+ "the object is in the EXECUTING state."); } catch (RuntimeException re) { //OK, expected. The NullExecMonitor (see setUp) caught the Error //when onAbort was called and re-threw a RuntimeException to wrap //it. Let's verify the original Error: assertTrue("run() should throw an Error if dispatched more than "+ "once when the object is in the EXECUTING state.", re.getCause() instanceof Error); //Now check the state to make sure exit action got dispatched: assertEquals("If an exception is thrown during run(), the exit "+ "action should be dispatched and the object eventually "+ "transitioned to the FINISHED state.", ExecCommand.FINISHED, target.getState()); } } }