/* * org.openmicroscopy.shoola.util.concur.TestProducerLoop * *------------------------------------------------------------------------------ * 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; //Java imports //Third-party libraries import junit.framework.TestCase; //Application-internal dependencies import org.openmicroscopy.shoola.util.concur.tasks.ExecHandle; import org.openmicroscopy.shoola.util.concur.tasks.FakeCmdProcessor; /** * Tests the operation of {@link ProducerLoop} in a single-threaded * environment. * Makes sure that state-transitions are correct. * * @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 TestProducerLoop extends TestCase { private ProducerLoop target; //Object under test. private MockAsyncByteBuffer buffer; //Mock linked to target. private MockByteBufferFiller producer; //Mock linked to target. private Runnable execCmd; //To simulate service execution. private ExecHandle execHandle; //To simulate cancellation. //REPLACES setUp(). Call it b/f each test. //payload: value producer.getTotalLength() has to return. //nWrites: how many times buffer.writeToBuffer() will be invoked. Every // write will be 1-byte long, except for the nWrite call if // setLastWrite is true. In this case, this last call will // will return -1 instead. //e: if not null, it will be thrown by the last write. private void setUpTestEnvironment(int payload, int nWrites, boolean setLastWrite, Exception e) { //Create mocks. buffer = new MockAsyncByteBuffer(2, 1); //Sz irrelevant for these tests. producer = new MockByteBufferFiller(); //ProducerLoop should always call this in its constructor and only //once in its life-span. producer.getTotalLength(payload); //Do nWrites-1 calls that write 1 byte each, nWrites call has to //return -1 if setLastWrite. Do nothing if nWrites is 0. for (int i = 0; i < nWrites-1; ++i) buffer.writeToBuffer(producer, i, 1, null); if (0 < nWrites) { if (setLastWrite) buffer.writeToBuffer(producer, nWrites-1, -1, e); else buffer.writeToBuffer(producer, nWrites-1, 1, e); } //Transition mocks to verification mode. producer.activate(); //This has to be done b/f creating ProducerLoop. buffer.activate(); //Create target and link it to mocks. target = new ProducerLoop(buffer, producer); //Calls getTotalLength(). //Get the commmand that a concrete CmdProcessor would use to execute //the producer loop. FakeCmdProcessor cmdPrc = new FakeCmdProcessor(); execHandle = cmdPrc.exec(target, target); //(srv, observer) execCmd = cmdPrc.getCommand(); } private void ensureAllExpectedCallsWerePerformed() { producer.verify(); buffer.verify(); } public void setUp() { //Clean fixture so to fail if test method didn't call //setUpTestEnvironment(). target = null; buffer = null; producer = null; execCmd = null; execHandle = null; } public void testInitBadArgs() { try { setUpTestEnvironment(0, 0, false, null); //Bad payload=0. fail("ProducerLoop constructor should error if the producer "+ "provides a non-positive payload."); } catch (IllegalArgumentException iae) { //OK, expected. } ensureAllExpectedCallsWerePerformed(); try { new ProducerLoop(null, null); fail("ProducerLoop constructor shouldn't accept null args."); } catch (NullPointerException npe) { //OK, expected. } try { new ProducerLoop(new MockAsyncByteBuffer(2, 1), null); fail("ProducerLoop constructor shouldn't accept null args."); } catch (NullPointerException npe) { //OK, expected. } try { new ProducerLoop(null, new MockByteBufferFiller()); fail("ProducerLoop constructor shouldn't accept null args."); } catch (NullPointerException npe) { //OK, expected. } } public void testInit() { setUpTestEnvironment(1, 0, false, null); //payload=1 and do no write. //Test. assertEquals("State should be FILLING after creation.", ProducerLoop.FILLING, target.getState()); int bw = target.getBytesWritten(), pl = target.getPayload(); assertTrue("Count of bytes written ("+bw+") should be less than "+ "payload ("+pl+")in the FILLING state.", bw < pl); assertNull("No exception should be set in the FILLING state.", target.getDiscardCause()); ensureAllExpectedCallsWerePerformed(); } public void testFillingToDataDiscardedBecauseOfPayloadOverflow() { //Simulate writing more data than payload. Set payload to 1 and do //two calls that write 1 byte each. setUpTestEnvironment(1, 2, false, null); //Test. execCmd.run(); //Execute service. assertEquals("State should be DATA_DISCARDED after a write overflow.", ProducerLoop.DATA_DISCARDED, target.getState()); int bw = target.getBytesWritten(), pl = target.getPayload(); assertTrue("Count of bytes written ("+bw+") should be greater than "+ "payload ("+pl+") after a write overflow.", pl < bw); assertNotNull("An exception should be set in the DATA_DISCARDED state.", target.getDiscardCause()); ensureAllExpectedCallsWerePerformed(); } public void testFillingToDataDiscardedBecauseOfPayloadUnderflow() { //Simulate writing less data than payload. Set payload to 2 and do //two calls: first one writes 1 byte, second one returns -1. setUpTestEnvironment(2, 2, true, null); //Test. execCmd.run(); //Execute service. assertEquals("State should be DATA_DISCARDED after a write underflow.", ProducerLoop.DATA_DISCARDED, target.getState()); int bw = target.getBytesWritten(), pl = target.getPayload(); assertTrue("Count of bytes written ("+bw+") should be less than "+ "payload ("+pl+") after a write overflow.", bw < pl); assertNotNull("An exception should be set in the DATA_DISCARDED state.", target.getDiscardCause()); ensureAllExpectedCallsWerePerformed(); } public void testFillingToDataDiscardedBecauseOfBufferWriteException() { //Simulate writing less data than payload. Set payload to 2 and do //one call to write 1 byte. Then set up an additional call to throw //the exception. BufferWriteException exc = new BufferWriteException(""); setUpTestEnvironment(2, 1, false, exc); //Test. execCmd.run(); //Execute service. assertEquals("State should be DATA_DISCARDED after a write exception.", ProducerLoop.DATA_DISCARDED, target.getState()); int bw = target.getBytesWritten(), pl = target.getPayload(); assertTrue("Count of bytes written ("+bw+") should be less than "+ "payload ("+pl+") after a write exception.", bw < pl); assertNotNull("An exception should be set in the DATA_DISCARDED state.", target.getDiscardCause()); assertSame("The original BufferWriteException thrown by the producer "+ "should always be propagated.", exc, target.getDiscardCause()); ensureAllExpectedCallsWerePerformed(); } public void testFillingToDataDiscardedBecauseOfRuntimeException() { //Simulate writing less data than payload. Set payload to 2 and do //one call to write 1 byte. Then set up an additional call to throw //the exception. RuntimeException exc = //Simulate runtime overflow of internal buffer. new ArrayIndexOutOfBoundsException(""); setUpTestEnvironment(2, 1, false, exc); //Test. execCmd.run(); //Execute service. assertEquals("State should be DATA_DISCARDED after a write exception.", ProducerLoop.DATA_DISCARDED, target.getState()); int bw = target.getBytesWritten(), pl = target.getPayload(); assertTrue("Count of bytes written ("+bw+") should be less than "+ "payload ("+pl+") after a write exception.", bw < pl); assertNotNull("An exception should be set in the DATA_DISCARDED state.", target.getDiscardCause()); assertSame("Any runtime exception thrown by the producer should "+ "always be propagated by wrapping into a BufferWriteException.", exc, target.getDiscardCause().getCause()); ensureAllExpectedCallsWerePerformed(); } public void testFillingToDataDiscardedBecauseOfCancellation() { setUpTestEnvironment(1, 0, false, null); //payload=1 and do no write. //Test. execHandle.cancelExecution(); assertEquals("State should be DATA_DISCARDED after cancellation.", ProducerLoop.DATA_DISCARDED, target.getState()); int bw = target.getBytesWritten(), pl = target.getPayload(); assertTrue("Count of bytes written ("+bw+") shouldn't be greater than "+ "payload ("+pl+") after cancellation.", bw <= pl); assertNotNull("An exception should be set in the DATA_DISCARDED state.", target.getDiscardCause()); ensureAllExpectedCallsWerePerformed(); } public void testFillingToDone() { //Simulate writing as much data as payload. Set payload to 2 and do //three calls: first two write 1 byte, last one returns -1. setUpTestEnvironment(2, 3, true, null); //Test. execCmd.run(); //Execute service. assertEquals("State should be DONE after normal termination.", ProducerLoop.DONE, target.getState()); int bw = target.getBytesWritten(), pl = target.getPayload(); assertTrue("Count of bytes written ("+bw+") should be equal to "+ "payload ("+pl+") after normal termination.", bw == pl); assertNull("No exception should be set in the DONE state.", target.getDiscardCause()); ensureAllExpectedCallsWerePerformed(); } }