/* * org.openmicroscopy.shoola.env.data.views.TestBatchCallTree * *------------------------------------------------------------------------------ * 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.env.data.views; //Java imports //Third-party libraries import junit.framework.TestCase; //Application-internal dependencies import org.openmicroscopy.shoola.env.data.events.DSCallFeedbackEvent; import org.openmicroscopy.shoola.env.data.events.DSCallOutcomeEvent; import org.openmicroscopy.shoola.util.concur.tasks.FakeCmdProcessor; /** * Verifies the execution of a {@link BatchCallTree} is carried out as * expected. * This test is performed with the help of a {@link FakeBatchCallTree} and * a {@link SyncBatchCallMonitor}. The first provides a concrete subclass * of {@link BatchCallTree} and builds the actual call tree. Moreover, it * replaces the default * {@link org.openmicroscopy.shoola.util.concur.tasks.CmdProcessor} with a * {@link org.openmicroscopy.shoola.util.concur.tasks.SyncProcessor} (so the * tree will be executed within the JUnit thread) and the default call monitor * (a {@link BatchCallMonitor}) with a {@link SyncBatchCallMonitor} (which will * dispach the tree's execution events in the JUnit thread). * Because all the {@link SyncBatchCallMonitor} does is overriding the * <code>deliver</code> method, this test implicitly verifies the behavior of * {@link BatchCallMonitor}. In turn, an instance of this latter class would * not work properly if the {@link BatchCallTree} didn't delegate calls to * its root node, so we also implicitly verify delegation. * * @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 TestBatchCallTree extends TestCase { //Object under test -- it's concrete BatchCallTree that allows us to //verify the code in BatchCallTree (which is abstract). private FakeBatchCallTree target; //Used to verify the operation of target. private MockDSCallEventListener observer; private void setUpFeedbackEvents(int howMany) { int leafs = target.getActualLeavesCount(); int m = Math.min(leafs, howMany); for (int i = 0, perc = 0; i < m; ++i) { perc = i*100/leafs; observer.eventFired( new DSCallFeedbackEvent(perc, target.getLeafDescription(i+1), Integer.valueOf(i+1)), null); } if (m < howMany) //Normal outcome, set last feedback event. observer.eventFired(new DSCallFeedbackEvent(100, null, null), null); } private void setUpNormalOutcome() { observer.eventFired( new DSCallOutcomeEvent(target.getExpectedResult()), null); } private void setUpExceptionOutcome(int faultyLeaf) { Exception exc = new Exception("L"+faultyLeaf); target.setFaultyLeaf(faultyLeaf, exc); observer.eventFired(new DSCallOutcomeEvent(exc), null); } private void verifySingleExecution() { try { target.exec(observer); fail("A tree can't be executed more than once."); } catch (IllegalStateException ise) { //Ok, expected. } } protected void setUp() { target = new FakeBatchCallTree(); observer = new MockDSCallEventListener(); } public void testNormalOutcome() { //Set up observer's expectations for feedback events and outcome //event to be received during target.exec(). setUpFeedbackEvents(target.getActualLeavesCount()+1); setUpNormalOutcome(); observer.activate(); //Transition mock to verification mode. target.exec(observer); //Test. observer.verify(); //Make sure all expected calls were performed. verifySingleExecution(); //Make sure won't allow re-exec. } public void testException1() { //Set up observer's expectations for feedback events and outcome //event to be received during target.exec(). setUpFeedbackEvents(1); //FE[perc: 0, status: L1] b/f L1 is exec'd. setUpExceptionOutcome(1); //L1 will throw an exc when exec'd. observer.activate(); //Transition mock to verification mode. target.exec(observer); //Test. observer.verify(); //Make sure all expected calls were performed. verifySingleExecution(); //Make sure won't allow re-exec. } public void testException2() { //Set up observer's expectations for feedback events and outcome //event to be received during target.exec(). setUpFeedbackEvents(2); //FE[perc: 0, status: L1] b/f L1 is exec'd. //FE[perc: 20, status: L2] b/f L2 is exec'd. setUpExceptionOutcome(2); //L2 will throw an exc when exec'd. observer.activate(); //Transition mock to verification mode. target.exec(observer); //Test. observer.verify(); //Make sure all expected calls were performed. verifySingleExecution(); //Make sure won't allow re-exec. } public void testException3() { //Set up observer's expectations for feedback events and outcome //event to be received during target.exec(). setUpFeedbackEvents(3); //FE[perc: 0, status: L1] b/f L1 is exec'd. //FE[perc: 20, status: L2] b/f L2 is exec'd. //FE[perc: 40, status: L3] b/f L3 is exec'd. setUpExceptionOutcome(3); //L3 will throw an exc when exec'd. observer.activate(); //Transition mock to verification mode. target.exec(observer); //Test. observer.verify(); //Make sure all expected calls were performed. verifySingleExecution(); //Make sure won't allow re-exec. } public void testException4() { //Set up observer's expectations for feedback events and outcome //event to be received during target.exec(). setUpFeedbackEvents(4); //FE[perc: 0, status: L1] b/f L1 is exec'd. //FE[perc: 20, status: L2] b/f L2 is exec'd. //FE[perc: 40, status: L3] b/f L3 is exec'd. //FE[perc: 60, status: L4] b/f L4 is exec'd. setUpExceptionOutcome(4); //L4 will throw an exc when exec'd. observer.activate(); //Transition mock to verification mode. target.exec(observer); //Test. observer.verify(); //Make sure all expected calls were performed. verifySingleExecution(); //Make sure won't allow re-exec. } public void testException5() { //Set up observer's expectations for feedback events and outcome //event to be received during target.exec(). setUpFeedbackEvents(5); //FE[perc: 0, status: L1] b/f L1 is exec'd. //FE[perc: 20, status: L2] b/f L2 is exec'd. //FE[perc: 40, status: L3] b/f L3 is exec'd. //FE[perc: 60, status: L4] b/f L4 is exec'd. //FE[perc: 80, status: L5] b/f L5 is exec'd. setUpExceptionOutcome(5); //L5 will throw an exc when exec'd. observer.activate(); //Transition mock to verification mode. target.exec(observer); //Test. observer.verify(); //Make sure all expected calls were performed. verifySingleExecution(); //Make sure won't allow re-exec. } //This tests verifies what happens if the computation is cancelled //before the executor thread has a chance to run it. public void testCancellation() { //Set up observer's expectations for outcome event. observer.eventFired(new DSCallOutcomeEvent(), null); //We need to replace the default SyncProcessor with //one that does nothing. target.setProcessor(new FakeCmdProcessor()); //Transition mock to verification mode. observer.activate(); //Test. CallHandle handle = target.exec(observer); //B/c the processor does nothing, the internal ExecCommand is not run. //So it's still sitting in the READY state. //We can now cancel. This will eventually result in a state transition //READY->CANCELLED within the internal ExecCommand. At the end of this //transition onCancel() is called, which will have to result in a call //to observer.eventFired(). Note that this transition *never* causes //an interruption, so we can safely run this test w/ the JUnit thread. handle.cancel(); //Make sure all expected calls were performed. observer.verify(); verifySingleExecution(); //Make sure won't allow re-exec. } //NOTE: what about the other cancellation paths? If cancellation is //detected after the computation has finished, then we fall back to either //the normal outcome or exception paths -- all of which are already covered //by the other tests in this class. So we would only have to verify what //happens if cancellation is detected in the midst of execution. We should //tests that feedback events are received up to the cancellation point and //then an outcome event is received that informs of cancellation. However, //this is basically what we've been doing in the exception tests. The only //difference with cancellation is that onCancel() should be invoked instead //of onAbort(). But we've already verified above what happens if onCancel() //is called. In conclusion, we can skip the test to verify cancellation in //the midst of execution. }