/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.component.execution.internal;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.HashMap;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import org.easymock.Capture;
import org.easymock.EasyMock;
import org.easymock.IAnswer;
import org.junit.Test;
import de.rcenvironment.core.component.api.ComponentException;
import de.rcenvironment.core.component.execution.api.Component;
import de.rcenvironment.core.component.execution.api.ComponentExecutionContext;
import de.rcenvironment.core.component.execution.api.ComponentExecutionException;
import de.rcenvironment.core.component.execution.api.ComponentState;
import de.rcenvironment.core.component.execution.api.ConsoleRow;
import de.rcenvironment.core.component.execution.api.ThreadHandler;
import de.rcenvironment.core.component.execution.api.WorkflowGraph;
import de.rcenvironment.core.component.execution.internal.ComponentExecutor.ComponentExecutionType;
import de.rcenvironment.core.component.model.api.ComponentDescription;
import de.rcenvironment.core.component.model.endpoint.api.EndpointDatum;
import de.rcenvironment.core.component.testutils.ComponentDefaultStub;
import de.rcenvironment.core.datamodel.api.DataModelConstants;
import de.rcenvironment.core.datamodel.api.FinalComponentRunState;
import de.rcenvironment.core.toolkitbridge.transitional.ConcurrencyUtils;
import de.rcenvironment.toolkit.modules.concurrency.api.TaskDescription;
/**
* Test cases for {@link ComponentExecutor}.
*
* @author Doreen Seider
*/
public class ComponentExecutorTest {
private static final int WAIT_INTERVAL_100_MSEC = 100;
private static final int TEST_TIMEOUT_500_MSEC = 800;
private static final String COMP_ID = "comp-id";
private static final String COMP_EXE_ID = "comp-exe-id";
private static final String COMP_INSTANCE_NAME = "comp instance name";
private static final String WF_EXE_ID = "wf-exe-id";
private static final String WF_INSTANCE_NAME = "wf instance name";
/**
* Tests if a {@link Component}'s execution is not considered if the {@link ComponentExecutor} got cancelled before the actual run was
* performed.
*
* @throws ComponentExecutionException on unexpected error
* @throws ComponentException on unexpected error
* @throws InterruptedException on unexpected interruption
* @throws ExecutionException on unexpected interruption
*/
@Test(timeout = TEST_TIMEOUT_500_MSEC)
public void testOnCancelledBeforeExecuteByConsLimWasCalled()
throws ComponentException, ComponentExecutionException, InterruptedException, ExecutionException {
final ComponentExecutionRelatedInstances compExeRelatedInstancesStub = createComponentExecutionRelatedInstancesStub();
final ComponentExecutor compExecutor =
new ComponentExecutor(compExeRelatedInstancesStub, ComponentExecutionType.ProcessInputs);
Future<Boolean> cancelTask = ConcurrencyUtils.getAsyncTaskService().submit(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
compExecutor.onCancelled();
boolean isComponentKnownCanceled = compExeRelatedInstancesStub.compExeRelatedStates.isComponentCancelled.get();
return isComponentKnownCanceled;
}
});
assertFalse(cancelTask.get());
compExecutor.executeByConsideringLimitations();
assertTrue(compExeRelatedInstancesStub.compExeRelatedStates.isComponentCancelled.get());
}
/**
* Tests if the execution of the {@link Component}'s start method has no timeout.
*
* @throws ComponentExecutionException on unexpected error
* @throws ComponentException on unexpected error
*/
@Test(timeout = TEST_TIMEOUT_500_MSEC)
public void testSartAsInitHasNoTimeout() throws ComponentExecutionException, ComponentException {
final ComponentExecutionRelatedInstances compExeRelatedInstancesStub = createComponentExecutionRelatedInstancesStub();
Component compMock = EasyMock.createStrictMock(Component.class);
compMock.start();
final int startMethodTimeMSec = 200;
EasyMock.expectLastCall().andAnswer(new IAnswer<Void>() {
@Override
public Void answer() throws Throwable {
try {
Thread.sleep(startMethodTimeMSec);
} catch (InterruptedException e) {
throw new ComponentException("Unexpected error", e);
}
return null;
}
});
EasyMock.replay(compMock);
compExeRelatedInstancesStub.component.set(compMock);
Capture<ComponentStateMachineEvent> compStateMachineEventCapture = new Capture<>();
ComponentStateMachine compStateMachineMock = createComponentStateMachineEventForRuns(compStateMachineEventCapture);
compExeRelatedInstancesStub.compStateMachine = compStateMachineMock;
ComponentExecutor compExecutor =
new ComponentExecutor(compExeRelatedInstancesStub, ComponentExecutionType.StartAsInit);
ComponentExecutor.waitIntervalAfterCacelledCalledMSec = WAIT_INTERVAL_100_MSEC;
ComponentExecutor.waitIntervalNotRunMSec = WAIT_INTERVAL_100_MSEC;
compExecutor.executeByConsideringLimitations();
assertFalse(compExeRelatedInstancesStub.compExeRelatedStates.isComponentCancelled.get());
}
/**
* Tests if a {@link Component}'s execution is not considered if the {@link ComponentExecutor} got cancelled before the actual run was
* performed.
*
* @throws ComponentExecutionException on unexpected error
* @throws ComponentException on unexpected error
* @throws InterruptedException on unexpected interruption
* @throws ExecutionException on unexpected interruption
*/
@Test(timeout = TEST_TIMEOUT_500_MSEC)
public void testOnCancelledDuringExecuteByConsLimWasCalled()
throws ComponentException, ComponentExecutionException, InterruptedException, ExecutionException {
ComponentExecutionPermitsService compExePermitsService = createComponentExecutionPermitServiceMock();
final ComponentExecutionRelatedInstances compExeRelatedInstancesStub = createComponentExecutionRelatedInstancesStub();
final ComponentExecutor compExecutor = new ComponentExecutor(compExeRelatedInstancesStub, ComponentExecutionType.ProcessInputs);
compExecutor.bindComponentExecutionPermitsService(compExePermitsService);
compExecutor.acquireExecutionPermission();
Future<Boolean> cancelTask = ConcurrencyUtils.getAsyncTaskService().submit(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
compExecutor.onCancelled();
return compExeRelatedInstancesStub.compExeRelatedStates.isComponentCancelled.get();
}
});
assertFalse(cancelTask.get());
compExecutor.performExecutionAndReleasePermission();
EasyMock.verify(compExePermitsService);
assertTrue(compExeRelatedInstancesStub.compExeRelatedStates.isComponentCancelled.get());
}
/**
* Tests if waiting for execution permission is interrupted if component is cancelled.
*
* @throws ComponentExecutionException on unexpected error
* @throws ComponentException on unexpected error
* @throws InterruptedException on unexpected interruption
* @throws ExecutionException on unexpected interruption
*/
@Test(timeout = TEST_TIMEOUT_500_MSEC)
public void testWaitingForExecutionPermissionOnCancelled()
throws ComponentException, ComponentExecutionException, InterruptedException, ExecutionException {
ComponentExecutionPermitsService compExePermitsService = createComponentExecutionPermitServiceMock(false);
final ComponentExecutionRelatedInstances compExeRelatedInstancesStub = createComponentExecutionRelatedInstancesStub();
final ComponentExecutor compExecutor = new ComponentExecutor(compExeRelatedInstancesStub, ComponentExecutionType.ProcessInputs);
compExecutor.bindComponentExecutionPermitsService(compExePermitsService);
final int delayMsec = 100;
ConcurrencyUtils.getAsyncTaskService().scheduleAfterDelay(new Runnable() {
@TaskDescription("Delayed cancel call")
@Override
public void run() {
compExecutor.onCancelled();
}
}, delayMsec);
compExecutor.acquireExecutionPermission();
EasyMock.verify(compExePermitsService);
assertTrue(compExeRelatedInstancesStub.compExeRelatedStates.isComponentCancelled.get());
}
/**
* Tests tearing down component in success case.
*
* @throws ComponentException on unexpected error
* @throws ComponentExecutionException on unexpected error
*/
@Test
public void testTearDownSuccess() throws ComponentException, ComponentExecutionException {
Component.FinalComponentState finalCompState = Component.FinalComponentState.FINISHED;
Component compMock = EasyMock.createStrictMock(Component.class);
compMock.tearDown(finalCompState);
EasyMock.expectLastCall();
EasyMock.replay(compMock);
ComponentExecutionRelatedInstances compExeRelatedInstancesStub = createComponentExecutionRelatedInstancesStub();
compExeRelatedInstancesStub.component.set(compMock);
ComponentExecutionType compExeType = ComponentExecutionType.TearDown;
compExeType.setFinalComponentStateAfterTearedDown(finalCompState);
ComponentExecutor compExecutor =
new ComponentExecutor(compExeRelatedInstancesStub, ComponentExecutionType.TearDown);
compExecutor.executeByConsideringLimitations();
EasyMock.verify(compMock);
}
/**
* Tests tearing down component in success case.
*
* @throws ComponentException on unexpected error
* @throws ComponentExecutionException on unexpected error
*/
@Test
public void testTearDownFailure() throws ComponentException, ComponentExecutionException {
ComponentExecutionRelatedInstances compExeRelatedInstancesStub = createComponentExecutionRelatedInstancesStub();
String errorMessage = "some error in tear down";
Component.FinalComponentState finalCompState = Component.FinalComponentState.FINISHED;
Component compMock = EasyMock.createStrictMock(Component.class);
compMock.tearDown(finalCompState);
EasyMock.expectLastCall().andThrow(new RuntimeException(errorMessage));
EasyMock.replay(compMock);
compExeRelatedInstancesStub.component.set(compMock);
ComponentExecutionStorageBridge compExeStorageBridgeMock =
createComponentExecutionStorageBridgeNonRunFailure(compExeRelatedInstancesStub, DataModelConstants.TEAR_DOWN_RUN);
compExeRelatedInstancesStub.compExeStorageBridge = compExeStorageBridgeMock;
compExeRelatedInstancesStub.compExeScheduler = createComponentExecutionSchedulerMock(false);
Capture<ConsoleRow.Type> consoleRowTypeCapture = new Capture<>();
Capture<String> logMessageCapture = new Capture<>();
ConsoleRowsSender consoleRowsSenderMock = createConsoleRowsSenderMock(consoleRowTypeCapture, logMessageCapture);
compExeRelatedInstancesStub.consoleRowsSender = consoleRowsSenderMock;
ComponentExecutionType compExeType = ComponentExecutionType.TearDown;
compExeType.setFinalComponentStateAfterTearedDown(finalCompState);
ComponentExecutor compExecutor =
new ComponentExecutor(compExeRelatedInstancesStub, ComponentExecutionType.TearDown);
try {
compExecutor.executeByConsideringLimitations();
fail("ComponentException expected");
} catch (ComponentException e) {
assertFailureHandling(consoleRowTypeCapture, logMessageCapture, e, errorMessage);
}
EasyMock.verify(compMock);
EasyMock.verify(compExeStorageBridgeMock);
EasyMock.verify(consoleRowsSenderMock);
}
/**
* Tests executing of {@link Component#processInputs()} in success case.
*
* @throws ComponentExecutionException on unexpected error
* @throws ComponentException on unexpected error
* @throws InterruptedException on unexpected error
* @throws ExecutionException on unexpected error
*/
@Test(timeout = TEST_TIMEOUT_500_MSEC)
public void testProcessingInputsSuccess()
throws ComponentExecutionException, ComponentException, InterruptedException, ExecutionException {
ComponentExecutionRelatedInstances compExeRelatedInstancesStub = createComponentExecutionRelatedInstancesStub();
Component compMock = EasyMock.createStrictMock(Component.class);
compMock.processInputs();
EasyMock.expectLastCall();
EasyMock.replay(compMock);
compExeRelatedInstancesStub.component.set(compMock);
Capture<ComponentStateMachineEvent> compStateMachineEventCapture = new Capture<>();
ComponentStateMachine compStateMachineMock = createComponentStateMachineEventForRuns(compStateMachineEventCapture);
compExeRelatedInstancesStub.compStateMachine = compStateMachineMock;
ComponentExecutionStorageBridge compExeStorageBridgeMock =
createComponentExecutionStorageBridgeRunSuccess(compExeRelatedInstancesStub);
compExeRelatedInstancesStub.compExeStorageBridge = compExeStorageBridgeMock;
ComponentContextBridge compCtxBridgeMock = EasyMock.createStrictMock(ComponentContextBridge.class);
EasyMock.expect(compCtxBridgeMock.getEndpointDatumsForExecution()).andStubReturn(new HashMap<String, EndpointDatum>());
EasyMock.replay(compCtxBridgeMock);
compExeRelatedInstancesStub.compCtxBridge = compCtxBridgeMock;
compExeRelatedInstancesStub.compExeScheduler = createComponentExecutionSchedulerMock(false);
ConsoleRowsSender consoleRowsSenderMock = createConsoleRowsSenderMock(null, null);
compExeRelatedInstancesStub.consoleRowsSender = consoleRowsSenderMock;
ComponentExecutionStatsService compExeStatsServiceMock = createComponentExecutionStatsServiceMock(compExeRelatedInstancesStub);
ComponentExecutor compExecutor =
new ComponentExecutor(compExeRelatedInstancesStub, ComponentExecutionType.ProcessInputs);
compExecutor.bindComponentExecutionPermitsService(createComponentExecutionPermitServiceMock());
compExecutor.bindComponentExecutionStatsService(compExeStatsServiceMock);
compExecutor.executeByConsideringLimitations();
assertFalse(compExeRelatedInstancesStub.compExeRelatedStates.isComponentCancelled.get());
EasyMock.verify(compMock);
EasyMock.verify(compStateMachineMock);
assertEquals(ComponentStateMachineEventType.RUNNING, compStateMachineEventCapture.getValue().getType());
assertEquals(ComponentState.PROCESSING_INPUTS, compStateMachineEventCapture.getValue().getNewComponentState());
EasyMock.verify(compExeStorageBridgeMock);
EasyMock.verify(consoleRowsSenderMock);
}
/**
* Tests executing of {@link Component#completeStartOrProcessInputsAfterVerificationDone()} in case results are rejected.
*
* @throws ComponentExecutionException on unexpected error
* @throws ComponentException on unexpected error
* @throws InterruptedException on unexpected error
* @throws ExecutionException on unexpected error
*/
@Test(timeout = TEST_TIMEOUT_500_MSEC)
public void testCompleteVerificationResultsRejected()
throws ComponentExecutionException, ComponentException, InterruptedException, ExecutionException {
ComponentExecutionRelatedInstances compExeRelatedInstancesStub = createComponentExecutionRelatedInstancesStub(true);
Component compMock = EasyMock.createStrictMock(Component.class);
compMock.completeStartOrProcessInputsAfterVerificationDone();
EasyMock.expectLastCall();
EasyMock.replay(compMock);
compExeRelatedInstancesStub.component.set(compMock);
ComponentExecutionStorageBridge compExeStorageBridgeMock = EasyMock.createStrictMock(ComponentExecutionStorageBridge.class);
compExeStorageBridgeMock.setComponentExecutionFinished(FinalComponentRunState.RESULTS_REJECTED);
EasyMock.expectLastCall();
EasyMock.replay(compExeStorageBridgeMock);
compExeRelatedInstancesStub.compExeStorageBridge = compExeStorageBridgeMock;
compExeRelatedInstancesStub.compExeScheduler = createComponentExecutionSchedulerMock(false);
ConsoleRowsSender consoleRowsSenderMock = createConsoleRowsSenderMock(null, null);
compExeRelatedInstancesStub.consoleRowsSender = consoleRowsSenderMock;
ComponentExecutionType compExeType = ComponentExecutionType.CompleteVerification;
compExeType.setFinalComponentStateAfterRun(FinalComponentRunState.RESULTS_REJECTED);
ComponentExecutor compExecutor =
new ComponentExecutor(compExeRelatedInstancesStub, compExeType);
compExecutor.bindComponentExecutionPermitsService(createComponentExecutionPermitServiceMock());
compExecutor.executeByConsideringLimitations();
assertFalse(compExeRelatedInstancesStub.compExeRelatedStates.isComponentCancelled.get());
EasyMock.verify(compMock);
EasyMock.verify(compExeStorageBridgeMock);
EasyMock.verify(consoleRowsSenderMock);
}
/**
* Tests executing of {@link Component#handleVerificationToken(String)}: verification token passed properly and life cycle method call
* handled properly.
*
* @throws ComponentExecutionException on unexpected error
* @throws ComponentException on unexpected error
* @throws InterruptedException on unexpected error
* @throws ExecutionException on unexpected error
*/
@Test(timeout = TEST_TIMEOUT_500_MSEC)
public void testHandleVerificationToken()
throws ComponentExecutionException, ComponentException, InterruptedException, ExecutionException {
final String someToken = "some-token";
ComponentExecutionRelatedInstances compExeRelatedInstancesStub = createComponentExecutionRelatedInstancesStub(true);
Component compMock = EasyMock.createStrictMock(Component.class);
compMock.handleVerificationToken(someToken);
EasyMock.expectLastCall();
EasyMock.replay(compMock);
compExeRelatedInstancesStub.component.set(compMock);
ComponentExecutionType compExeType = ComponentExecutionType.HandleVerificationToken;
compExeType.setVerificationToken(someToken);
ComponentExecutor compExecutor =
new ComponentExecutor(compExeRelatedInstancesStub, compExeType);
compExecutor.bindComponentExecutionPermitsService(createComponentExecutionPermitServiceMock());
compExecutor.executeByConsideringLimitations();
assertFalse(compExeRelatedInstancesStub.compExeRelatedStates.isComponentCancelled.get());
EasyMock.verify(compMock);
}
/**
* Tests executing of {@link Component#processInputs()} in failure case, that means {@link Component#processInputs()} throws a
* {@link ComponentException} or {@link RuntimeException}.
*
* @throws ComponentExecutionException on unexpected error
* @throws ComponentException on unexpected error
* @throws InterruptedException on unexpected error
* @throws ExecutionException on unexpected error
*/
@Test
public void testProcessingInputsFailureComponentException()
throws ComponentExecutionException, ComponentException, InterruptedException, ExecutionException {
testProcessInputsFailure(new ComponentException("some error when processing inputs"));
}
/**
* Tests executing of {@link Component#processInputs()} in failure case, that means {@link Component#processInputs()} throws a
* {@link ComponentException} or {@link RuntimeException}.
*
* @throws ComponentExecutionException on unexpected error
* @throws ComponentException on unexpected error
* @throws InterruptedException on unexpected error
* @throws ExecutionException on unexpected error
*/
@Test(timeout = TEST_TIMEOUT_500_MSEC)
public void testProcessingInputsFailureRuntimeException()
throws ComponentExecutionException, ComponentException, InterruptedException, ExecutionException {
testProcessInputsFailure(new RuntimeException());
}
private void testProcessInputsFailure(Exception expectedException)
throws ComponentExecutionException, ComponentException, InterruptedException, ExecutionException {
ComponentExecutionRelatedInstances compExeRelatedInstancesStub = createComponentExecutionRelatedInstancesStub();
Component compMock = EasyMock.createStrictMock(Component.class);
compMock.processInputs();
EasyMock.expectLastCall().andThrow(expectedException);
compMock.completeStartOrProcessInputsAfterFailure();
EasyMock.expectLastCall();
EasyMock.replay(compMock);
compExeRelatedInstancesStub.component.set(compMock);
Capture<ComponentStateMachineEvent> compStateMachineEventCapture = new Capture<>();
ComponentStateMachine compStateMachineMock = createComponentStateMachineEventForRuns(compStateMachineEventCapture);
compExeRelatedInstancesStub.compStateMachine = compStateMachineMock;
ComponentExecutionStorageBridge compExeStorageBridgeMock =
createComponentExecutionStorageBridgeRunFailure(compExeRelatedInstancesStub);
compExeRelatedInstancesStub.compExeStorageBridge = compExeStorageBridgeMock;
ComponentContextBridge compCtxBridgeMock = EasyMock.createStrictMock(ComponentContextBridge.class);
EasyMock.expect(compCtxBridgeMock.getEndpointDatumsForExecution()).andStubReturn(new HashMap<String, EndpointDatum>());
EasyMock.replay(compCtxBridgeMock);
compExeRelatedInstancesStub.compCtxBridge = compCtxBridgeMock;
compExeRelatedInstancesStub.compExeScheduler = createComponentExecutionSchedulerMock(false);
Capture<ConsoleRow.Type> consoleRowTypeCapture = new Capture<>();
Capture<String> logMessageCapture = new Capture<>();
ConsoleRowsSender consoleRowsSenderMock = createConsoleRowsSenderMock(consoleRowTypeCapture, logMessageCapture);
compExeRelatedInstancesStub.consoleRowsSender = consoleRowsSenderMock;
ComponentExecutionStatsService compExeStatsServiceMock = createComponentExecutionStatsServiceMock(compExeRelatedInstancesStub);
ComponentExecutor compExecutor =
new ComponentExecutor(compExeRelatedInstancesStub, ComponentExecutionType.ProcessInputs);
compExecutor.bindComponentExecutionPermitsService(createComponentExecutionPermitServiceMock());
compExecutor.bindComponentExecutionStatsService(compExeStatsServiceMock);
try {
compExecutor.executeByConsideringLimitations();
fail("ComponentException expected");
} catch (ComponentException e) {
if (expectedException.getMessage() == null) {
assertFailureHandling(consoleRowTypeCapture, logMessageCapture, e, "Unexpected error");
} else {
assertFailureHandling(consoleRowTypeCapture, logMessageCapture, e, expectedException.getMessage());
}
}
assertFalse(compExeRelatedInstancesStub.compExeRelatedStates.isComponentCancelled.get());
EasyMock.verify(compMock);
EasyMock.verify(compStateMachineMock);
assertEquals(ComponentStateMachineEventType.RUNNING, compStateMachineEventCapture.getValue().getType());
assertEquals(ComponentState.PROCESSING_INPUTS, compStateMachineEventCapture.getValue().getNewComponentState());
EasyMock.verify(compExeStorageBridgeMock);
EasyMock.verify(consoleRowsSenderMock);
}
/**
* Tests canceling of {@link Component#processInputs()} in success case, that means {@link Component#processInputs()} returns within
* expected amount of time.
*
* @throws ComponentExecutionException on unexpected error
* @throws ComponentException on unexpected error
* @throws InterruptedException on unexpected error
* @throws ExecutionException on unexpected error
*/
@Test(timeout = TEST_TIMEOUT_500_MSEC)
public void testCancelProcessingInputsSuccess()
throws ComponentExecutionException, ComponentException, InterruptedException, ExecutionException {
ComponentExecutionRelatedInstances compExeRelatedInstancesStub = createComponentExecutionRelatedInstancesStub();
final AtomicReference<CountDownLatch> countDownLatchRef = new AtomicReference<CountDownLatch>(new CountDownLatch(1));
Component compStub = new ComponentDefaultStub.Default() {
private int processInputsCount = 0;
private int onProcessInputsInterruptedCount = 0;
@Override
public void processInputs() throws ComponentException {
if (processInputsCount > 0) {
fail("'processInputs' is expected to be called only once");
}
try {
countDownLatchRef.get().await();
} catch (InterruptedException e) {
fail("unexpected InterruptedException");
}
}
@Override
public void onProcessInputsInterrupted(ThreadHandler executingThreadHandler) {
if (onProcessInputsInterruptedCount > 0) {
fail("'onProcessInputsInterrupted' is expected to be called only once");
}
countDownLatchRef.get().countDown();
}
};
compExeRelatedInstancesStub.component.set(compStub);
Capture<ComponentStateMachineEvent> compStateMachineEventCapture = new Capture<>();
ComponentStateMachine compStateMachineMock = createComponentStateMachineEventForRuns(compStateMachineEventCapture);
compExeRelatedInstancesStub.compStateMachine = compStateMachineMock;
ComponentExecutionStorageBridge compExeStorageBridgeMock =
createComponentExecutionStorageBridgeRunSuccess(compExeRelatedInstancesStub);
compExeRelatedInstancesStub.compExeStorageBridge = compExeStorageBridgeMock;
ComponentContextBridge compCtxBridgeMock = EasyMock.createStrictMock(ComponentContextBridge.class);
EasyMock.expect(compCtxBridgeMock.getEndpointDatumsForExecution()).andStubReturn(new HashMap<String, EndpointDatum>());
EasyMock.replay(compCtxBridgeMock);
compExeRelatedInstancesStub.compCtxBridge = compCtxBridgeMock;
compExeRelatedInstancesStub.compExeScheduler = createComponentExecutionSchedulerMock(false);
ConsoleRowsSender consoleRowsSenderMock = createConsoleRowsSenderMock(null, null);
compExeRelatedInstancesStub.consoleRowsSender = consoleRowsSenderMock;
ComponentExecutionStatsService compExeStatsServiceMock = createComponentExecutionStatsServiceMock(compExeRelatedInstancesStub);
final ComponentExecutor compExecutor =
new ComponentExecutor(compExeRelatedInstancesStub, ComponentExecutionType.ProcessInputs);
compExecutor.bindComponentExecutionPermitsService(createComponentExecutionPermitServiceMock());
compExecutor.bindComponentExecutionStatsService(compExeStatsServiceMock);
final int delayMsec = 100;
ConcurrencyUtils.getAsyncTaskService().scheduleAfterDelay(new Runnable() {
@TaskDescription("Delayed cancel call")
@Override
public void run() {
compExecutor.onCancelled();
}
}, delayMsec);
compExecutor.executeByConsideringLimitations();
assertTrue(compExeRelatedInstancesStub.compExeRelatedStates.isComponentCancelled.get());
EasyMock.verify(compStateMachineMock);
assertEquals(ComponentStateMachineEventType.RUNNING, compStateMachineEventCapture.getValue().getType());
assertEquals(ComponentState.PROCESSING_INPUTS, compStateMachineEventCapture.getValue().getNewComponentState());
EasyMock.verify(compExeStorageBridgeMock);
EasyMock.verify(consoleRowsSenderMock);
}
/**
* Tests canceling of {@link Component#processInputs()} in failure case, that means {@link Component#processInputs()} doesn't return
* within expected amount of time.
*
* @throws ComponentExecutionException on unexpected error
* @throws ComponentException on unexpected error
* @throws InterruptedException on unexpected error
* @throws ExecutionException on unexpected error
*/
@Test(timeout = TEST_TIMEOUT_500_MSEC)
public void testCancelProcessingInputsFailure()
throws ComponentExecutionException, ComponentException, InterruptedException, ExecutionException {
ComponentExecutionRelatedInstances compExeRelatedInstancesStub = createComponentExecutionRelatedInstancesStub();
final CountDownLatch processInputsCalledLatch = new CountDownLatch(1);
Component compStub = new ComponentDefaultStub.Default() {
private int processInputsCount = 0;
private int onProcessInputsInterruptedCount = 0;
@Override
public void processInputs() throws ComponentException {
if (processInputsCount > 0) {
fail("'processInputs' is expected to be called only once");
}
processInputsCalledLatch.countDown();
final CountDownLatch dummyLatch = new CountDownLatch(1);
while (true) {
try {
dummyLatch.await();
} catch (InterruptedException e) {
// ignore for test purposes
e = null;
}
}
}
@Override
public void onProcessInputsInterrupted(ThreadHandler executingThreadHandler) {
if (onProcessInputsInterruptedCount > 0) {
fail("'onProcessInputsInterrupted' is expected to be called only once");
}
}
};
compExeRelatedInstancesStub.component.set(compStub);
ComponentStateMachine compStateMachineMock = EasyMock.createStrictMock(ComponentStateMachine.class);
Capture<ComponentStateMachineEvent> compStateMachineEventCapture = new Capture<>();
compStateMachineMock.postEvent(EasyMock.capture(compStateMachineEventCapture));
EasyMock.expectLastCall();
EasyMock.replay(compStateMachineMock);
compExeRelatedInstancesStub.compStateMachine = compStateMachineMock;
ComponentExecutionStorageBridge compExeStorageBridgeMock =
createComponentExecutionStorageBridgeRunFailure(compExeRelatedInstancesStub);
compExeRelatedInstancesStub.compExeStorageBridge = compExeStorageBridgeMock;
ComponentContextBridge compCtxBridgeMock = EasyMock.createStrictMock(ComponentContextBridge.class);
EasyMock.expect(compCtxBridgeMock.getEndpointDatumsForExecution()).andStubReturn(new HashMap<String, EndpointDatum>());
EasyMock.replay(compCtxBridgeMock);
compExeRelatedInstancesStub.compCtxBridge = compCtxBridgeMock;
compExeRelatedInstancesStub.compExeScheduler = createComponentExecutionSchedulerMock(false);
Capture<ConsoleRow.Type> consoleRowTypeCapture = new Capture<>();
Capture<String> logMessageCapture = new Capture<>();
ConsoleRowsSender consoleRowsSenderMock = createConsoleRowsSenderMock(consoleRowTypeCapture, logMessageCapture);
compExeRelatedInstancesStub.consoleRowsSender = consoleRowsSenderMock;
ComponentExecutionStatsService compExeStatsServiceMock = createComponentExecutionStatsServiceMock(compExeRelatedInstancesStub);
final ComponentExecutor compExecutor =
new ComponentExecutor(compExeRelatedInstancesStub, ComponentExecutionType.ProcessInputs);
compExecutor.bindComponentExecutionPermitsService(createComponentExecutionPermitServiceMock());
compExecutor.bindComponentExecutionStatsService(compExeStatsServiceMock);
ComponentExecutor.waitIntervalAfterCacelledCalledMSec = WAIT_INTERVAL_100_MSEC;
final AtomicReference<Exception> expectedExceptionRef = new AtomicReference<Exception>(null);
final CountDownLatch executedLatch = new CountDownLatch(1);
final Future<?> executeTask = ConcurrencyUtils.getAsyncTaskService().submit(new Runnable() {
@Override
public void run() {
try {
compExecutor.executeByConsideringLimitations();
} catch (ComponentException | ComponentExecutionException e) {
expectedExceptionRef.set(e);
}
executedLatch.countDown();
}
});
processInputsCalledLatch.await();
compExecutor.onCancelled();
executeTask.cancel(true);
executedLatch.await();
assertNotNull(expectedExceptionRef.get());
assertTrue(expectedExceptionRef.get() instanceof ComponentException);
assertFailureHandling(consoleRowTypeCapture, logMessageCapture, (ComponentException) expectedExceptionRef.get(),
"didn't terminate in time");
assertTrue(compExeRelatedInstancesStub.compExeRelatedStates.isComponentCancelled.get());
EasyMock.verify(compStateMachineMock);
assertEquals(ComponentStateMachineEventType.RUNNING, compStateMachineEventCapture.getValue().getType());
assertEquals(ComponentState.PROCESSING_INPUTS, compStateMachineEventCapture.getValue().getNewComponentState());
EasyMock.verify(compExeStorageBridgeMock);
EasyMock.verify(consoleRowsSenderMock);
}
private ComponentExecutionStorageBridge createComponentExecutionStorageBridgeRunFailure(
ComponentExecutionRelatedInstances compExeRelatedInstancesStub) throws ComponentExecutionException {
ComponentExecutionStorageBridge compExeStorageBridgeMock = EasyMock.createStrictMock(ComponentExecutionStorageBridge.class);
compExeStorageBridgeMock.addComponentExecution(compExeRelatedInstancesStub.compExeCtx,
compExeRelatedInstancesStub.compExeRelatedStates.executionCount.get());
EasyMock.expectLastCall();
EasyMock.expect(compExeStorageBridgeMock.hasUnfinishedComponentExecution()).andReturn(true);
Capture<FinalComponentRunState> finalStateCapture = new Capture<>();
compExeStorageBridgeMock.setComponentExecutionFinished(EasyMock.capture(finalStateCapture));
EasyMock.expectLastCall();
EasyMock.replay(compExeStorageBridgeMock);
return compExeStorageBridgeMock;
}
private ComponentStateMachine createComponentStateMachineEventForRuns(
Capture<ComponentStateMachineEvent> compStateMachineEventCapture) {
ComponentStateMachine compStateMachineMock = EasyMock.createStrictMock(ComponentStateMachine.class);
compStateMachineMock.postEvent(EasyMock.capture(compStateMachineEventCapture));
EasyMock.expectLastCall();
EasyMock.replay(compStateMachineMock);
return compStateMachineMock;
}
private ComponentExecutionStorageBridge createComponentExecutionStorageBridgeNonRunFailure(
ComponentExecutionRelatedInstances compExeRelatedInstancesStub, Integer exeCount) throws ComponentExecutionException {
ComponentExecutionStorageBridge compExeStorageBridgeMock = EasyMock.createStrictMock(ComponentExecutionStorageBridge.class);
EasyMock.expect(compExeStorageBridgeMock.hasUnfinishedComponentExecution()).andReturn(false);
compExeStorageBridgeMock.addComponentExecution(compExeRelatedInstancesStub.compExeCtx, exeCount);
EasyMock.expectLastCall();
Capture<FinalComponentRunState> finalStateCapture = new Capture<>();
compExeStorageBridgeMock.setComponentExecutionFinished(EasyMock.capture(finalStateCapture));
EasyMock.expectLastCall();
EasyMock.replay(compExeStorageBridgeMock);
return compExeStorageBridgeMock;
}
private void assertFailureHandling(Capture<ConsoleRow.Type> consoleRowTypeCapture, Capture<String> logMessageCapture,
ComponentException e, String expectedErrorMessage) {
assertTrue(e.getMessage().matches("E#[0-9]{13}"));
assertTrue(consoleRowTypeCapture.hasCaptured());
assertEquals(ConsoleRow.Type.COMPONENT_ERROR, consoleRowTypeCapture.getValue());
assertTrue(logMessageCapture.hasCaptured());
assertTrue(logMessageCapture.getValue().contains(e.getMessage()));
assertTrue(logMessageCapture.getValue().contains(expectedErrorMessage));
}
private ComponentExecutionStorageBridge createComponentExecutionStorageBridgeRunSuccess(
ComponentExecutionRelatedInstances compExeRelatedInstancesStub)
throws ComponentExecutionException {
return createComponentExecutionStorageBridgeRunSuccess(compExeRelatedInstancesStub, FinalComponentRunState.FINISHED);
}
private ComponentExecutionStorageBridge createComponentExecutionStorageBridgeRunSuccess(
ComponentExecutionRelatedInstances compExeRelatedInstancesStub, FinalComponentRunState finalState)
throws ComponentExecutionException {
ComponentExecutionStorageBridge compExeStorageBridgeMock = EasyMock.createStrictMock(ComponentExecutionStorageBridge.class);
compExeStorageBridgeMock.addComponentExecution(compExeRelatedInstancesStub.compExeCtx,
compExeRelatedInstancesStub.compExeRelatedStates.executionCount.get());
EasyMock.expectLastCall();
compExeStorageBridgeMock.setComponentExecutionFinished(finalState);
EasyMock.expectLastCall();
EasyMock.replay(compExeStorageBridgeMock);
return compExeStorageBridgeMock;
}
private ConsoleRowsSender createConsoleRowsSenderMock(Capture<ConsoleRow.Type> consoleRowTypeCapture,
Capture<String> logMessageCapture) {
ConsoleRowsSender consoleRowsSenderMock = EasyMock.createStrictMock(ConsoleRowsSender.class);
if (consoleRowTypeCapture != null) {
consoleRowsSenderMock.sendLogMessageAsConsoleRow(EasyMock.capture(consoleRowTypeCapture), EasyMock.capture(logMessageCapture),
EasyMock.anyInt());
EasyMock.expectLastCall();
}
consoleRowsSenderMock.sendLogFileWriteTriggerAsConsoleRow();
EasyMock.expectLastCall();
EasyMock.replay(consoleRowsSenderMock);
return consoleRowsSenderMock;
}
private ComponentExecutionStatsService createComponentExecutionStatsServiceMock(
ComponentExecutionRelatedInstances compExeRelatedInstancesStub) {
ComponentExecutionStatsService compExeStatsServiceMock = EasyMock.createStrictMock(ComponentExecutionStatsService.class);
compExeStatsServiceMock.addStatsAtComponentRunStart(compExeRelatedInstancesStub.compExeCtx);
EasyMock.expectLastCall();
compExeStatsServiceMock.addStatsAtComponentRunTermination(compExeRelatedInstancesStub.compExeCtx);
EasyMock.expectLastCall();
EasyMock.replay(compExeStatsServiceMock);
return compExeStatsServiceMock;
}
private ComponentExecutionPermitsService createComponentExecutionPermitServiceMock(boolean permitting)
throws InterruptedException, ExecutionException {
Future<Boolean> futureStub = new FutureStub(permitting);
ComponentExecutionPermitsService compExePermitsServiceMock = EasyMock.createStrictMock(ComponentExecutionPermitsService.class);
EasyMock.expect(compExePermitsServiceMock.acquire(COMP_ID, COMP_EXE_ID)).andReturn(futureStub);
if (permitting) {
compExePermitsServiceMock.release(COMP_ID);
EasyMock.expectLastCall();
}
EasyMock.replay(compExePermitsServiceMock);
return compExePermitsServiceMock;
}
private ComponentExecutionPermitsService createComponentExecutionPermitServiceMock() throws InterruptedException, ExecutionException {
return createComponentExecutionPermitServiceMock(true);
}
private ComponentExecutionRelatedInstances createComponentExecutionRelatedInstancesStub() throws ComponentExecutionException {
return createComponentExecutionRelatedInstancesStub(false);
}
private ComponentExecutionRelatedInstances createComponentExecutionRelatedInstancesStub(boolean requiresOutputApproval)
throws ComponentExecutionException {
ComponentDescription compDescMock = EasyMock.createStrictMock(ComponentDescription.class);
EasyMock.expect(compDescMock.getIdentifier()).andStubReturn(COMP_ID);
EasyMock.expect(compDescMock.getConfigurationDescription())
.andStubReturn(ConfigurationDescriptionMockFactory.createConfigurationDescriptionMock(requiresOutputApproval));
EasyMock.replay(compDescMock);
ComponentExecutionContext compExeCtxMock = EasyMock.createStrictMock(ComponentExecutionContext.class);
EasyMock.expect(compExeCtxMock.getComponentDescription()).andStubReturn(compDescMock);
EasyMock.expect(compExeCtxMock.getExecutionIdentifier()).andStubReturn(COMP_EXE_ID);
EasyMock.expect(compExeCtxMock.getInstanceName()).andStubReturn(COMP_INSTANCE_NAME);
EasyMock.expect(compExeCtxMock.getWorkflowExecutionIdentifier()).andStubReturn(WF_EXE_ID);
EasyMock.expect(compExeCtxMock.getWorkflowInstanceName()).andStubReturn(WF_INSTANCE_NAME);
EasyMock.expect(compExeCtxMock.getWorkflowGraph()).andStubReturn(createWorkflowGraphMock());
EasyMock.replay(compExeCtxMock);
ComponentExecutionRelatedInstances compExeRelatedInstancesStub = new ComponentExecutionRelatedInstances();
compExeRelatedInstancesStub.compExeCtx = compExeCtxMock;
compExeRelatedInstancesStub.compExeRelatedStates = new ComponentExecutionRelatedStates();
return compExeRelatedInstancesStub;
}
private ComponentExecutionScheduler createComponentExecutionSchedulerMock(boolean isLoopResetRequested) {
ComponentExecutionScheduler compExeSchedulerMock = EasyMock.createStrictMock(ComponentExecutionScheduler.class);
EasyMock.expect(compExeSchedulerMock.isLoopResetRequested()).andStubReturn(false);
EasyMock.replay(compExeSchedulerMock);
return compExeSchedulerMock;
}
private WorkflowGraph createWorkflowGraphMock() throws ComponentExecutionException {
WorkflowGraph wfGraphMock = EasyMock.createStrictMock(WorkflowGraph.class);
EasyMock.expect(wfGraphMock.getLoopDriver(COMP_EXE_ID)).andReturn(null);
EasyMock.replay(wfGraphMock);
return wfGraphMock;
}
/**
* Stub implementation for {@link Future} stubbing the permission acquiring task.
*
* @author Doreen Seider
*/
private class FutureStub implements Future<Boolean> {
private final boolean permitting;
private final CountDownLatch waitingDoneLatch = new CountDownLatch(1);
protected FutureStub(boolean permitting) {
this.permitting = permitting;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
waitingDoneLatch.countDown();
return true;
}
@Override
public boolean isCancelled() {
throw new UnsupportedOperationException();
}
@Override
public boolean isDone() {
return permitting;
}
@Override
public Boolean get() throws InterruptedException, ExecutionException {
if (permitting) {
return true;
}
waitingDoneLatch.await();
throw new CancellationException();
}
@Override
public Boolean get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
throw new UnsupportedOperationException();
}
}
}