/*******************************************************************************
* (c) Copyright 2016 Hewlett-Packard Development Company, L.P.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Apache License v2.0 which accompany this distribution.
*
* The Apache License is available at
* http://www.apache.org/licenses/LICENSE-2.0
*
*******************************************************************************/
package io.cloudslang.lang.runtime.steps;
import io.cloudslang.dependency.api.services.DependencyService;
import io.cloudslang.dependency.api.services.MavenConfig;
import io.cloudslang.dependency.impl.services.DependencyServiceImpl;
import io.cloudslang.dependency.impl.services.MavenConfigImpl;
import io.cloudslang.lang.entities.ListLoopStatement;
import io.cloudslang.lang.entities.ResultNavigation;
import io.cloudslang.lang.entities.ScoreLangConstants;
import io.cloudslang.lang.entities.bindings.Output;
import io.cloudslang.lang.entities.bindings.ScriptFunction;
import io.cloudslang.lang.entities.bindings.values.Value;
import io.cloudslang.lang.entities.bindings.values.ValueFactory;
import io.cloudslang.lang.entities.encryption.DummyEncryptor;
import io.cloudslang.lang.runtime.RuntimeConstants;
import io.cloudslang.lang.runtime.bindings.LoopsBinding;
import io.cloudslang.lang.runtime.bindings.OutputsBinding;
import io.cloudslang.lang.runtime.bindings.ParallelLoopBinding;
import io.cloudslang.lang.runtime.bindings.scripts.ScriptEvaluator;
import io.cloudslang.lang.runtime.env.Context;
import io.cloudslang.lang.runtime.env.ReturnValues;
import io.cloudslang.lang.runtime.env.RunEnvironment;
import io.cloudslang.lang.runtime.events.LanguageEventData;
import io.cloudslang.runtime.api.python.PythonRuntimeService;
import io.cloudslang.runtime.impl.python.PythonExecutionCachedEngine;
import io.cloudslang.runtime.impl.python.PythonExecutionEngine;
import io.cloudslang.runtime.impl.python.PythonRuntimeServiceImpl;
import io.cloudslang.score.api.EndBranchDataContainer;
import io.cloudslang.score.events.EventBus;
import io.cloudslang.score.events.EventBusImpl;
import io.cloudslang.score.lang.ExecutionRuntimeServices;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.python.google.common.collect.Lists.newArrayList;
/**
* Date: 4/7/2015
*
* @author Bonczidai Levente
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ParallelLoopStepsTest.Config.class)
public class ParallelLoopStepsTest {
private static final String BRANCH_EXCEPTION_MESSAGE = "Exception details placeholder";
private static final String SUCCESS_RESULT = "SUCCESS";
@Autowired
private ParallelLoopExecutionData parallelLoopSteps;
@Autowired
private ParallelLoopBinding parallelLoopBinding;
@Autowired
private OutputsBinding outputsBinding;
@Autowired
private LoopsBinding loopsBinding;
@Rule
public ExpectedException exception = ExpectedException.none();
@Before
public void resetMocks() {
reset(parallelLoopBinding);
reset(outputsBinding);
reset(loopsBinding);
}
@Test
public void testBranchesAreCreated() throws Exception {
// prepare arguments
ListLoopStatement parallelLoopStatement = new ListLoopStatement("varName", "expression",
new HashSet<ScriptFunction>(), new HashSet<String>(), true);
RunEnvironment runEnvironment = new RunEnvironment();
Map<String, Value> variables = new HashMap<>();
Context context = new Context(variables);
runEnvironment.getStack().pushContext(context);
String nodeName = "nodeName";
String refId = "branch_id";
// prepare mocks
ExecutionRuntimeServices executionRuntimeServices = mock(ExecutionRuntimeServices.class);
List<Value> expectedSplitData = newArrayList(ValueFactory.create(1),
ValueFactory.create(2), ValueFactory.create(3));
when(parallelLoopBinding.bindParallelLoopList(eq(parallelLoopStatement),
eq(context), eq(runEnvironment.getSystemProperties()), eq(nodeName)))
.thenReturn(expectedSplitData);
Long branchBeginStepId = 3L;
// call method
parallelLoopSteps.addBranches(
parallelLoopStatement,
runEnvironment,
executionRuntimeServices,
nodeName,
1234L,
5L,
branchBeginStepId,
refId
);
// verify expected behaviour
ArgumentCaptor<Map> branchContextArgumentCaptor = ArgumentCaptor.forClass(Map.class);
//noinspection unchecked
verify(executionRuntimeServices, times(3))
.addBranch(eq(branchBeginStepId), eq(refId), branchContextArgumentCaptor.capture());
List<Map> branchContexts = branchContextArgumentCaptor.getAllValues();
List<Value> actualSplitData = newArrayList();
for (Map branchContext : branchContexts) {
assertTrue("runtime environment not found in branch context",
branchContext.containsKey(ScoreLangConstants.RUN_ENV));
RunEnvironment branchRunEnvironment = (RunEnvironment) branchContext.get(ScoreLangConstants.RUN_ENV);
Map<String, Value> branchVariables =
branchRunEnvironment.getStack().popContext().getImmutableViewOfVariables();
actualSplitData.add(branchVariables.get("varName"));
}
Assert.assertEquals(expectedSplitData, actualSplitData);
Assert.assertEquals(5, (long) runEnvironment.removeNextStepPosition());
}
@Test
public void testAddBranchesEventsAreFired() throws Exception {
// prepare arguments
ListLoopStatement parallelLoopStatement = new ListLoopStatement("varName", "expression",
new HashSet<ScriptFunction>(), new HashSet<String>(), true);
RunEnvironment runEnvironment = new RunEnvironment();
Map<String, Value> variables = new HashMap<>();
Context context = new Context(variables);
runEnvironment.getStack().pushContext(context);
String nodeName = "nodeName";
String refId = "branch_id";
// prepare mocks
ExecutionRuntimeServices executionRuntimeServices = mock(ExecutionRuntimeServices.class);
List<Value> expectedSplitData = newArrayList(ValueFactory.create(1),
ValueFactory.create(2), ValueFactory.create(3));
when(parallelLoopBinding.bindParallelLoopList(eq(parallelLoopStatement),
eq(context), eq(runEnvironment.getSystemProperties()), eq(nodeName)))
.thenReturn(expectedSplitData);
Long branchBeginStepId = 0L;
// call method
parallelLoopSteps.addBranches(
parallelLoopStatement,
runEnvironment,
executionRuntimeServices,
nodeName,
1234L,
5L,
branchBeginStepId,
refId
);
// verify expected behaviour
ArgumentCaptor<String> eventTypeArgumentCaptor = ArgumentCaptor.forClass(String.class);
verify(executionRuntimeServices, times(4))
.addEvent(eventTypeArgumentCaptor.capture(), any(LanguageEventData.class));
List<String> expectedEventTypesInOrder = newArrayList(
ScoreLangConstants.EVENT_SPLIT_BRANCHES,
ScoreLangConstants.EVENT_BRANCH_START,
ScoreLangConstants.EVENT_BRANCH_START,
ScoreLangConstants.EVENT_BRANCH_START
);
List<String> actualEventTypesInOrder = eventTypeArgumentCaptor.getAllValues();
Assert.assertEquals(expectedEventTypesInOrder, actualEventTypesInOrder);
Assert.assertEquals(5, (long) runEnvironment.removeNextStepPosition());
}
@Test
public void testJoinBranchesPublish() throws Exception {
// prepare arguments
RunEnvironment runEnvironment = new RunEnvironment();
runEnvironment.getExecutionPath().down();
Map<String, Value> variables = new HashMap<>();
Context context = new Context(variables);
runEnvironment.getStack().pushContext(context);
Map<String, ResultNavigation> stepNavigationValues = new HashMap<>();
ResultNavigation successNavigation = new ResultNavigation(0L, ScoreLangConstants.SUCCESS_RESULT);
ResultNavigation failureNavigation = new ResultNavigation(0L, ScoreLangConstants.FAILURE_RESULT);
stepNavigationValues.put(ScoreLangConstants.SUCCESS_RESULT, successNavigation);
stepNavigationValues.put(ScoreLangConstants.FAILURE_RESULT, failureNavigation);
// prepare mocks
Map<String, Serializable> runtimeContext1 = new HashMap<>();
runtimeContext1.put("branch1Output", 1);
runtimeContext1.put(ScoreLangConstants.BRANCH_RESULT_KEY, SUCCESS_RESULT);
Map<String, Serializable> runtimeContext2 = new HashMap<>();
runtimeContext2.put("branch2Output", 2);
runtimeContext2.put(ScoreLangConstants.BRANCH_RESULT_KEY, SUCCESS_RESULT);
Map<String, Serializable> runtimeContext3 = new HashMap<>();
runtimeContext3.put("branch3Output", 3);
runtimeContext3.put(ScoreLangConstants.BRANCH_RESULT_KEY, SUCCESS_RESULT);
List<Output> stepPublishValues =
newArrayList(new Output("outputName", ValueFactory.create("outputExpression")));
String nodeName = "nodeName";
ExecutionRuntimeServices executionRuntimeServices =
createAndConfigureExecutionRuntimeServicesMock(runtimeContext1, runtimeContext2, runtimeContext3);
// call method
parallelLoopSteps.joinBranches(runEnvironment, executionRuntimeServices,
stepPublishValues, stepNavigationValues, nodeName);
// verify expected behaviour
ArgumentCaptor<Map> aggregateContextArgumentCaptor = ArgumentCaptor.forClass(Map.class);
//noinspection unchecked
verify(outputsBinding)
.bindOutputs(eq(context.getImmutableViewOfVariables()), aggregateContextArgumentCaptor.capture(),
eq(runEnvironment.getSystemProperties()), eq(stepPublishValues));
@SuppressWarnings("unchecked")
List<Map<String, Serializable>> expectedBranchContexts =
newArrayList(runtimeContext1, runtimeContext2, runtimeContext3);
@SuppressWarnings("unchecked")
Map<String, Value> aggregateContext = aggregateContextArgumentCaptor.getValue();
assertTrue(aggregateContext.containsKey(RuntimeConstants.BRANCHES_CONTEXT_KEY));
@SuppressWarnings("unchecked")
List<Map<String, Value>> actualBranchesContexts =
(List<Map<String, Value>>) aggregateContext.get(RuntimeConstants.BRANCHES_CONTEXT_KEY).get();
Assert.assertEquals(expectedBranchContexts, actualBranchesContexts);
}
@Test
public void testJoinBranchesNavigationAllBranchesSucced() throws Exception {
// prepare arguments
RunEnvironment runEnvironment = new RunEnvironment();
runEnvironment.getExecutionPath().down();
Map<String, Value> variables = new HashMap<>();
Context context = new Context(variables);
runEnvironment.getStack().pushContext(context);
final List<Output> stepPublishValues =
newArrayList(new Output("outputName", ValueFactory.create("outputExpression")));
Map<String, ResultNavigation> stepNavigationValues = new HashMap<>();
ResultNavigation successNavigation = new ResultNavigation(0L, "CUSTOM_SUCCESS");
ResultNavigation failureNavigation = new ResultNavigation(0L, "CUSTOM_FAILURE");
stepNavigationValues.put(ScoreLangConstants.SUCCESS_RESULT, successNavigation);
stepNavigationValues.put(ScoreLangConstants.FAILURE_RESULT, failureNavigation);
final String nodeName = "nodeName";
// prepare mocks
Map<String, Serializable> runtimeContext1 = new HashMap<>();
Map<String, Serializable> runtimeContext2 = new HashMap<>();
Map<String, Serializable> runtimeContext3 = new HashMap<>();
runtimeContext1.put("branch1Output", 1);
runtimeContext2.put("branch2Output", 2);
runtimeContext3.put("branch3Output", 3);
ExecutionRuntimeServices executionRuntimeServices = createAndConfigureExecutionRuntimeServicesMock(
runtimeContext1,
runtimeContext2,
runtimeContext3
);
// call method
parallelLoopSteps.joinBranches(
runEnvironment,
executionRuntimeServices,
stepPublishValues,
stepNavigationValues,
nodeName
);
// verify expected behaviour
Assert.assertEquals(0, (long) runEnvironment.removeNextStepPosition());
ReturnValues returnValues = runEnvironment.removeReturnValues();
Assert.assertNotNull("return values not found in runtime environment", returnValues);
Assert.assertEquals("CUSTOM_SUCCESS", returnValues.getResult());
}
@Test
public void testJoinBranchesNavigationOneBranchFails() throws Exception {
// prepare arguments
RunEnvironment runEnvironment = new RunEnvironment();
runEnvironment.getExecutionPath().down();
Map<String, Value> variables = new HashMap<>();
Context context = new Context(variables);
runEnvironment.getStack().pushContext(context);
final List<Output> stepPublishValues =
newArrayList(new Output("outputName", ValueFactory.create("outputExpression")));
Map<String, ResultNavigation> stepNavigationValues = new HashMap<>();
ResultNavigation successNavigation = new ResultNavigation(0L, "CUSTOM_SUCCESS");
ResultNavigation failureNavigation = new ResultNavigation(0L, "CUSTOM_FAILURE");
stepNavigationValues.put(ScoreLangConstants.SUCCESS_RESULT, successNavigation);
stepNavigationValues.put(ScoreLangConstants.FAILURE_RESULT, failureNavigation);
final String nodeName = "nodeName";
// prepare mocks
Map<String, Serializable> runtimeContext1 = new HashMap<>();
Map<String, Serializable> runtimeContext2 = new HashMap<>();
Map<String, Serializable> runtimeContext3 = new HashMap<>();
runtimeContext1.put("branch1Output", 1);
runtimeContext2.put("branch2Output", 2);
runtimeContext3.put("branch3Output", 3);
ReturnValues returnValues1 = new ReturnValues(new HashMap<String, Value>(), ScoreLangConstants.SUCCESS_RESULT);
ReturnValues returnValues2 = new ReturnValues(new HashMap<String, Value>(), ScoreLangConstants.FAILURE_RESULT);
ReturnValues returnValues3 = new ReturnValues(new HashMap<String, Value>(), ScoreLangConstants.SUCCESS_RESULT);
ExecutionRuntimeServices executionRuntimeServices = createAndConfigureExecutionRuntimeServicesMock(
runtimeContext1,
runtimeContext2,
runtimeContext3,
returnValues1,
returnValues2,
returnValues3
);
// call method
parallelLoopSteps.joinBranches(
runEnvironment,
executionRuntimeServices,
stepPublishValues,
stepNavigationValues,
nodeName
);
// verify expected behaviour
Assert.assertEquals(0, (long) runEnvironment.removeNextStepPosition());
ReturnValues returnValues = runEnvironment.removeReturnValues();
Assert.assertNotNull("return values not found in runtime environment", returnValues);
Assert.assertEquals("CUSTOM_FAILURE", returnValues.getResult());
}
@Test
public void testJoinBranchesEventsAreFired() throws Exception {
// prepare arguments
RunEnvironment runEnvironment = new RunEnvironment();
runEnvironment.getExecutionPath().down();
Map<String, Value> variables = new HashMap<>();
Context context = new Context(variables);
runEnvironment.getStack().pushContext(context);
final List<Output> stepPublishValues =
newArrayList(new Output("outputName", ValueFactory.create("outputExpression")));
Map<String, ResultNavigation> stepNavigationValues = new HashMap<>();
ResultNavigation successNavigation = new ResultNavigation(0L, ScoreLangConstants.SUCCESS_RESULT);
ResultNavigation failureNavigation = new ResultNavigation(0L, ScoreLangConstants.FAILURE_RESULT);
stepNavigationValues.put(ScoreLangConstants.SUCCESS_RESULT, successNavigation);
stepNavigationValues.put(ScoreLangConstants.FAILURE_RESULT, failureNavigation);
final String nodeName = "nodeName";
// prepare mocks
Map<String, Serializable> runtimeContext1 = new HashMap<>();
Map<String, Serializable> runtimeContext2 = new HashMap<>();
Map<String, Serializable> runtimeContext3 = new HashMap<>();
runtimeContext1.put("branch1Output", 1);
runtimeContext2.put("branch2Output", 2);
runtimeContext3.put("branch3Output", 3);
ExecutionRuntimeServices executionRuntimeServices = createAndConfigureExecutionRuntimeServicesMock(
runtimeContext1,
runtimeContext2,
runtimeContext3
);
// call method
parallelLoopSteps.joinBranches(
runEnvironment,
executionRuntimeServices,
stepPublishValues,
stepNavigationValues,
nodeName
);
// verify expected behaviour
ArgumentCaptor<String> eventTypeArgumentCaptor = ArgumentCaptor.forClass(String.class);
verify(executionRuntimeServices, times(5))
.addEvent(eventTypeArgumentCaptor.capture(), any(LanguageEventData.class));
List<String> expectedEventTypesInOrder = newArrayList(
ScoreLangConstants.EVENT_BRANCH_END,
ScoreLangConstants.EVENT_BRANCH_END,
ScoreLangConstants.EVENT_BRANCH_END,
ScoreLangConstants.EVENT_JOIN_BRANCHES_START,
ScoreLangConstants.EVENT_JOIN_BRANCHES_END
);
List<String> actualEventTypesInOrder = eventTypeArgumentCaptor.getAllValues();
Assert.assertEquals(expectedEventTypesInOrder, actualEventTypesInOrder);
}
@Test
public void testExceptionIsCapturedFromBranches() throws Exception {
// prepare arguments
RunEnvironment runEnvironment = new RunEnvironment();
runEnvironment.getExecutionPath().down();
Map<String, Value> variables = new HashMap<>();
Context context = new Context(variables);
runEnvironment.getStack().pushContext(context);
ExecutionRuntimeServices executionRuntimeServices = createExecutionRuntimeServicesMockWithBranchException();
exception.expectMessage(BRANCH_EXCEPTION_MESSAGE);
exception.expect(RuntimeException.class);
parallelLoopSteps.joinBranches(
runEnvironment,
executionRuntimeServices,
new ArrayList<Output>(0),
new HashMap<String, ResultNavigation>(),
"nodeName"
);
}
private ExecutionRuntimeServices createAndConfigureExecutionRuntimeServicesMock(
Map<String, Serializable> runtimeContext1,
Map<String, Serializable> runtimeContext2,
Map<String, Serializable> runtimeContext3) {
ReturnValues returnValues1 = new ReturnValues(new HashMap<String, Value>(), ScoreLangConstants.SUCCESS_RESULT);
ReturnValues returnValues2 = new ReturnValues(new HashMap<String, Value>(), ScoreLangConstants.SUCCESS_RESULT);
ReturnValues returnValues3 = new ReturnValues(new HashMap<String, Value>(), ScoreLangConstants.SUCCESS_RESULT);
return createAndConfigureExecutionRuntimeServicesMock(
runtimeContext1,
runtimeContext2,
runtimeContext3,
returnValues1,
returnValues2,
returnValues3
);
}
private ExecutionRuntimeServices createAndConfigureExecutionRuntimeServicesMock(
Map<String, Serializable> runtimeContext1,
Map<String, Serializable> runtimeContext2,
Map<String, Serializable> runtimeContext3,
ReturnValues returnValues1,
ReturnValues returnValues2,
ReturnValues returnValues3) {
final ExecutionRuntimeServices executionRuntimeServices = mock(ExecutionRuntimeServices.class);
final Map<String, Serializable> branchContext1 = new HashMap<>();
final Map<String, Serializable> branchContext2 = new HashMap<>();
final Map<String, Serializable> branchContext3 = new HashMap<>();
RunEnvironment branchRuntimeEnvironment1 = new RunEnvironment();
RunEnvironment branchRuntimeEnvironment2 = new RunEnvironment();
RunEnvironment branchRuntimeEnvironment3 = new RunEnvironment();
branchRuntimeEnvironment1.getExecutionPath().down();
branchRuntimeEnvironment2.getExecutionPath().down();
branchRuntimeEnvironment3.getExecutionPath().down();
branchRuntimeEnvironment1.getStack().pushContext(createContext(runtimeContext1));
branchRuntimeEnvironment2.getStack().pushContext(createContext(runtimeContext2));
branchRuntimeEnvironment3.getStack().pushContext(createContext(runtimeContext3));
branchRuntimeEnvironment1.putReturnValues(returnValues1);
branchRuntimeEnvironment2.putReturnValues(returnValues2);
branchRuntimeEnvironment3.putReturnValues(returnValues3);
branchContext1.put(ScoreLangConstants.RUN_ENV, branchRuntimeEnvironment1);
branchContext2.put(ScoreLangConstants.RUN_ENV, branchRuntimeEnvironment2);
branchContext3.put(ScoreLangConstants.RUN_ENV, branchRuntimeEnvironment3);
List<EndBranchDataContainer> branchesContainers = newArrayList(
new EndBranchDataContainer(branchContext1, new HashMap<String, Serializable>(), null),
new EndBranchDataContainer(branchContext2, new HashMap<String, Serializable>(), null),
new EndBranchDataContainer(branchContext3, new HashMap<String, Serializable>(), null)
);
when(executionRuntimeServices.getFinishedChildBranchesData()).thenReturn(branchesContainers);
return executionRuntimeServices;
}
private Context createContext(Map<String, Serializable> runtimeContext) {
Map<String, Value> context = new HashMap<>(runtimeContext.size());
for (Map.Entry<String, Serializable> entry : runtimeContext.entrySet()) {
context.put(entry.getKey(), ValueFactory.create(entry.getValue()));
}
return new Context(context);
}
private ExecutionRuntimeServices createExecutionRuntimeServicesMockWithBranchException() {
ExecutionRuntimeServices executionRuntimeServices = mock(ExecutionRuntimeServices.class);
List<EndBranchDataContainer> branchesContainers = newArrayList(
new EndBranchDataContainer(
new HashMap<String, Serializable>(),
new HashMap<String, Serializable>(),
BRANCH_EXCEPTION_MESSAGE)
);
when(executionRuntimeServices.getFinishedChildBranchesData()).thenReturn(branchesContainers);
return executionRuntimeServices;
}
@Configuration
static class Config {
@Bean
public ParallelLoopBinding parallelLoopBinding() {
return mock(ParallelLoopBinding.class);
}
@Bean
public OutputsBinding outputsBinding() {
return mock(OutputsBinding.class);
}
@Bean
public LoopsBinding loopsBinding() {
return mock(LoopsBinding.class);
}
@Bean
public ParallelLoopExecutionData parallelLoopSteps() {
return new ParallelLoopExecutionData();
}
@Bean
public ScriptEvaluator scriptEvaluator() {
return mock(ScriptEvaluator.class);
}
@Bean
public DependencyService mavenRepositoryService() {
return new DependencyServiceImpl();
}
@Bean
public MavenConfig mavenConfig() {
return new MavenConfigImpl();
}
@Bean
public PythonRuntimeService pythonRuntimeService() {
return new PythonRuntimeServiceImpl();
}
@Bean
public PythonExecutionEngine pythonExecutionEngine() {
return new PythonExecutionCachedEngine();
}
@Bean
public DummyEncryptor dummyEncryptor() {
return new DummyEncryptor();
}
@Bean
public EventBus eventBus() {
return new EventBusImpl();
}
}
}