/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.component.testutils; import de.rcenvironment.core.component.api.ComponentException; import de.rcenvironment.core.component.execution.api.Component; import de.rcenvironment.core.component.execution.api.Component.FinalComponentState; import de.rcenvironment.core.component.execution.api.ComponentContext; import de.rcenvironment.core.component.execution.api.ThreadHandler; /** * Wraps {@link Component} instances for integration testing to ensure proper {@link ComponentContext} behavior. For example, in an actual * workflow context, all inputs are discarded after {@link Component#processInputs()}. Likewise, the execution counter in the * {@link ComponentContext} must be incremented on each component invocation. By using this wrapper, test authors can focus on the actual * test semantics. * <p> * IMPORTANT: When using this wrapper, no methods of the wrapped {@link Component} instance should be called directly. This includes * {@link Component#setComponentContext(ComponentContext)} - the context is set automatically by the wrapper. Usually, you can just pass the * {@link Component} instance to test to the wrapper without keeping a reference to it (see code below). * <p> * Typical initialization code: * * <code><pre>context = new ComponentContextMock(); // can also be a subclass component = new ComponentTestWrapper(new XYComponent(), context);</pre></code> * * @author Robert Mischke */ public class ComponentTestWrapper { private final Component component; private final ComponentContextMock context; private boolean started; public ComponentTestWrapper(Component component, ComponentContextMock context) { this.component = component; this.context = context; component.setComponentContext(context); } /** * Calls {@link Component#start()} on the component with added checks and context calls. * * @throws ComponentException as thrown by {@link Component#start()} */ public synchronized void start() throws ComponentException { if (started) { throw new IllegalStateException("start() was called more than once"); } this.started = true; if (component.treatStartAsComponentRun()) { context.incrementExecutionCount(); context.resetOutputClosings(); } component.start(); } /** * Calls {@link Component#processInputs()} on the component with added checks and context calls. * * @throws ComponentException as thrown by {@link Component#processInputs()} */ public synchronized void processInputs() throws ComponentException { if (!started) { throw new IllegalStateException("processInputs() called before start()"); } // consistency check to catch test errors if (context.getInputsWithDatum().isEmpty()) { throw new IllegalStateException("processInputs() was called without input"); } context.incrementExecutionCount(); context.resetOutputData(); context.resetOutputClosings(); context.resetOutputResets(); component.processInputs(); context.resetInputData(); } /** * Calls {@link Component#reset()} on the component with added checks and context calls. * @throws ComponentException as thrown by {@link Component#processInputs()} */ public synchronized void reset() throws ComponentException { if (!started) { throw new IllegalStateException("reset() called before start()"); } component.reset(); } /** * Calls {@link Component#dispose()} on the component. */ public synchronized void dispose() { if (!started) { throw new IllegalStateException("dispose() called before start()"); } component.dispose(); } /** * Calls {@link Component#tearDown(FinalComponentState)} on the component. * * @param state the component's final state */ public synchronized void tearDown(FinalComponentState state) { if (!started) { throw new IllegalStateException("tearDown() called before start()"); } component.tearDown(state); } /** * Calls {@link Component#onStartInterrupted(ThreadHandler)} on the component. * * @param executingThreadHandler allows to interrupt the thread of {@link #start(ComponentContext)} */ public void onStartInterrupted(ThreadHandler executingThreadHandler) { component.onStartInterrupted(executingThreadHandler); } /** * Convenience method combining {@link #tearDown(FinalComponentState)} and {@link #dispose()}. * * @param state the component's final state, as in {@link Component#tearDown(FinalComponentState)} */ public synchronized void tearDownAndDispose(FinalComponentState state) { tearDown(state); dispose(); } }