/*******************************************************************************
* (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.compiler.scorecompiler;
import io.cloudslang.lang.compiler.SlangTextualKeys;
import io.cloudslang.lang.compiler.modeller.model.Action;
import io.cloudslang.lang.compiler.modeller.model.Executable;
import io.cloudslang.lang.compiler.modeller.model.Flow;
import io.cloudslang.lang.compiler.modeller.model.Operation;
import io.cloudslang.lang.compiler.modeller.model.Step;
import io.cloudslang.lang.compiler.modeller.model.Workflow;
import io.cloudslang.lang.entities.ExecutableType;
import io.cloudslang.lang.entities.ResultNavigation;
import io.cloudslang.lang.entities.ScoreLangConstants;
import io.cloudslang.lang.entities.bindings.Argument;
import io.cloudslang.lang.entities.bindings.Input;
import io.cloudslang.lang.entities.bindings.Output;
import io.cloudslang.lang.entities.bindings.Result;
import io.cloudslang.score.api.ExecutionPlan;
import io.cloudslang.score.api.ExecutionStep;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyListOf;
import static org.mockito.Matchers.anyMapOf;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.same;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class ExecutionPlanBuilderTest {
@Rule
public ExpectedException exception = ExpectedException.none();
@InjectMocks
private ExecutionPlanBuilder executionPlanBuilder;
@Mock
private ExecutionStepFactory stepFactory;
private Set<String> systemPropertyDependencies = Collections.emptySet();
private Step createSimpleCompiledParallelStep(String stepName) {
return createSimpleCompiledStep(stepName, true);
}
private Step createSimpleCompiledStep(String stepName) {
return createSimpleCompiledStep(stepName, false);
}
private Step createSimpleCompiledStep(String stepName, List<Map<String, String>> navigationStrings) {
return createSimpleCompiledStep(stepName, false, navigationStrings);
}
private Step createSimpleCompiledStep(String stepName, boolean isParallelLoop) {
List<Map<String, String>> navigationStrings = new ArrayList<>();
Map<String, String> successMap = new HashMap<>();
successMap.put(ScoreLangConstants.SUCCESS_RESULT, ScoreLangConstants.SUCCESS_RESULT);
Map<String, String> failureMap = new HashMap<>();
failureMap.put(ScoreLangConstants.FAILURE_RESULT, ScoreLangConstants.FAILURE_RESULT);
navigationStrings.add(successMap);
navigationStrings.add(failureMap);
return createSimpleCompiledStep(stepName, isParallelLoop, navigationStrings);
}
private Step createSimpleCompiledStep(String stepName, boolean isParallelLoop,
List<Map<String, String>> navigationStrings) {
Map<String, Serializable> preStepActionData = new HashMap<>();
if (isParallelLoop) {
preStepActionData.put(SlangTextualKeys.PARALLEL_LOOP_KEY, "value in values");
}
Map<String, Serializable> postStepActionData = new HashMap<>();
String refId = "refId";
return new Step(
stepName,
preStepActionData,
postStepActionData,
null,
navigationStrings,
refId,
isParallelLoop,
false);
}
private List<Result> defaultFlowResults() {
List<Result> results = new ArrayList<>();
results.add(new Result(ScoreLangConstants.SUCCESS_RESULT, null));
results.add(new Result(ScoreLangConstants.FAILURE_RESULT, null));
return results;
}
private void mockStartStep(Executable executable) {
Map<String, Serializable> preExecActionData = executable.getPreExecActionData();
String execName = executable.getName();
List<Input> inputs = executable.getInputs();
when(stepFactory
.createStartStep(eq(1L), same(preExecActionData), same(inputs), same(execName),
eq(ExecutableType.FLOW))).thenReturn(new ExecutionStep(1L));
when(stepFactory
.createStartStep(eq(1L), same(preExecActionData), same(inputs), same(execName),
eq(ExecutableType.OPERATION))).thenReturn(new ExecutionStep(1L));
}
private void mockEndStep(Long stepId, Executable executable, ExecutableType executableType) {
Map<String, Serializable> postExecActionData = executable.getPostExecActionData();
List<Output> outputs = executable.getOutputs();
List<Result> results = executable.getResults();
String execName = executable.getName();
when(stepFactory
.createEndStep(eq(stepId), same(postExecActionData), same(outputs), same(results),
same(execName), same(executableType))).thenReturn(new ExecutionStep(stepId));
}
private void mockFinishStep(Long stepId, Step step) {
mockFinishStep(stepId, step, false);
}
private void mockFinishStep(Long stepId, Step step, boolean isParallelLoop) {
Map<String, Serializable> postStepActionData = step.getPostStepActionData();
String stepName = step.getName();
when(stepFactory
.createFinishStepStep(eq(stepId), eq(postStepActionData),
anyMapOf(String.class, ResultNavigation.class), eq(stepName),
eq(isParallelLoop))).thenReturn(new ExecutionStep(stepId));
}
private void mockFinishParallelLoopStep(Long stepId, Step step) {
mockFinishStep(stepId, step, true);
}
private void mockBeginStep(Long stepId, Step step) {
Map<String, Serializable> preStepActionData = step.getPreStepActionData();
String refId = step.getRefId();
String name = step.getName();
when(stepFactory
.createBeginStepStep(eq(stepId), anyListOf(Argument.class),
eq(preStepActionData), eq(refId), eq(name))).thenReturn(new ExecutionStep(stepId));
}
private void mockAddBranchesStep(Long stepId, Long nextStepId, Long branchBeginStepId, Step step, Flow flow) {
Map<String, Serializable> preStepActionData = step.getPreStepActionData();
String refId = flow.getId();
String name = step.getName();
when(stepFactory.createAddBranchesStep(eq(stepId), eq(nextStepId),
eq(branchBeginStepId), eq(preStepActionData), eq(refId), eq(name)))
.thenReturn(new ExecutionStep(stepId));
}
private void mockJoinBranchesStep(Long stepId, Step step) {
Map<String, Serializable> postStepActionData = step.getPostStepActionData();
String stepName = step.getName();
when(stepFactory.createJoinBranchesStep(eq(stepId), eq(postStepActionData),
anyMapOf(String.class, ResultNavigation.class), eq(stepName)))
.thenReturn(new ExecutionStep(stepId));
}
@Test
public void testCreateOperationExecutionPlan() throws Exception {
Map<String, Serializable> preOpActionData = new HashMap<>();
Map<String, Serializable> postOpActionData = new HashMap<>();
Map<String, Serializable> actionData = new HashMap<>();
Action action = new Action(actionData);
String operationName = "operationName";
String opNamespace = "user.flows";
List<Input> inputs = new ArrayList<>();
List<Output> outputs = new ArrayList<>();
List<Result> results = new ArrayList<>();
Operation compiledOperation =
new Operation(preOpActionData, postOpActionData, action, opNamespace,
operationName, inputs, outputs, results, null, systemPropertyDependencies);
mockStartStep(compiledOperation);
when(stepFactory.createActionStep(eq(2L), same(actionData))).thenReturn(new ExecutionStep(2L));
mockEndStep(3L, compiledOperation, ExecutableType.OPERATION);
ExecutionPlan executionPlan = executionPlanBuilder.createOperationExecutionPlan(compiledOperation);
assertEquals("different number of execution steps than expected", 3, executionPlan.getSteps().size());
assertEquals("operation name is different than expected", operationName, executionPlan.getName());
assertEquals("language name is different than expected", "CloudSlang", executionPlan.getLanguage());
assertEquals("begin step is different than expected", Long.valueOf(1), executionPlan.getBeginStep());
}
@Test
public void createSimpleFlow() throws Exception {
Map<String, Serializable> preFlowActionData = new HashMap<>();
Map<String, Serializable> postFlowActionData = new HashMap<>();
Deque<Step> steps = new LinkedList<>();
Step step = createSimpleCompiledStep("stepName");
steps.add(step);
Workflow workflow = new Workflow(steps);
String flowName = "flowName";
String flowNamespace = "user.flows";
List<Input> inputs = new ArrayList<>();
List<Output> outputs = new ArrayList<>();
List<Result> results = defaultFlowResults();
Flow compiledFlow =
new Flow(preFlowActionData, postFlowActionData, workflow, flowNamespace,
flowName, inputs, outputs, results, null, systemPropertyDependencies);
mockStartStep(compiledFlow);
mockEndStep(0L, compiledFlow, ExecutableType.FLOW);
mockBeginStep(2L, step);
mockFinishStep(3L, step);
ExecutionPlan executionPlan = executionPlanBuilder.createFlowExecutionPlan(compiledFlow);
assertEquals("different number of execution steps than expected", 4, executionPlan.getSteps().size());
assertEquals("flow name is different than expected", flowName, executionPlan.getName());
assertEquals("language name is different than expected", "CloudSlang", executionPlan.getLanguage());
assertEquals("begin step is different than expected", Long.valueOf(1), executionPlan.getBeginStep());
}
@Test
public void createSimpleFlowWithParallelLoop() throws Exception {
Map<String, Serializable> preFlowActionData = new HashMap<>();
Map<String, Serializable> postFlowActionData = new HashMap<>();
Deque<Step> steps = new LinkedList<>();
Step step = createSimpleCompiledParallelStep("stepName");
steps.add(step);
Workflow workflow = new Workflow(steps);
String flowName = "flowName";
String flowNamespace = "user.flows";
List<Input> inputs = new ArrayList<>();
List<Output> outputs = new ArrayList<>();
List<Result> results = defaultFlowResults();
Flow compiledFlow =
new Flow(preFlowActionData, postFlowActionData, workflow, flowNamespace,
flowName, inputs, outputs, results, null, systemPropertyDependencies);
mockStartStep(compiledFlow);
mockEndStep(0L, compiledFlow, ExecutableType.FLOW);
mockAddBranchesStep(2L, 5L, 3L, step, compiledFlow);
mockBeginStep(3L, step);
mockFinishParallelLoopStep(4L, step);
mockJoinBranchesStep(5L, step);
final ExecutionPlan executionPlan = executionPlanBuilder.createFlowExecutionPlan(compiledFlow);
verify(stepFactory).createAddBranchesStep(
eq(2L),
eq(5L),
eq(3L),
eq(step.getPreStepActionData()),
eq(compiledFlow.getId()),
eq(step.getName()));
verify(stepFactory)
.createBeginStepStep(eq(3L), anyListOf(Argument.class),
eq(step.getPreStepActionData()), eq(step.getRefId()), eq(step.getName()));
verify(stepFactory)
.createFinishStepStep(eq(4L), eq(step.getPostStepActionData()),
anyMapOf(String.class, ResultNavigation.class), eq(step.getName()), eq(step.isParallelLoop()));
verify(stepFactory)
.createJoinBranchesStep(eq(5L), eq(step.getPostStepActionData()),
anyMapOf(String.class, ResultNavigation.class), eq(step.getName()));
assertEquals("different number of execution steps than expected", 6, executionPlan.getSteps().size());
assertEquals("flow name is different than expected", flowName, executionPlan.getName());
assertEquals("language name is different than expected", "CloudSlang", executionPlan.getLanguage());
assertEquals("begin step is different than expected", Long.valueOf(1), executionPlan.getBeginStep());
}
@Test
public void createFlowWithTwoSteps() throws Exception {
final Deque<Step> steps = new LinkedList<>();
String secondStepName = "2ndStep";
List<Map<String, String>> navigationStrings = new ArrayList<>();
Map<String, String> successMap = new HashMap<>();
successMap.put(ScoreLangConstants.SUCCESS_RESULT, secondStepName);
Map<String, String> failureMap = new HashMap<>();
failureMap.put(ScoreLangConstants.FAILURE_RESULT, ScoreLangConstants.FAILURE_RESULT);
navigationStrings.add(successMap);
navigationStrings.add(failureMap);
Step firstStep = createSimpleCompiledStep("firstStepName", navigationStrings);
Step secondStep = createSimpleCompiledStep(secondStepName);
steps.add(firstStep);
steps.add(secondStep);
Map<String, Serializable> preFlowActionData = new HashMap<>();
Map<String, Serializable> postFlowActionData = new HashMap<>();
Workflow workflow = new Workflow(steps);
String flowName = "flowName";
String flowNamespace = "user.flows";
List<Input> inputs = new ArrayList<>();
List<Output> outputs = new ArrayList<>();
List<Result> results = defaultFlowResults();
Flow compiledFlow =
new Flow(preFlowActionData, postFlowActionData, workflow, flowNamespace,
flowName, inputs, outputs, results, null, systemPropertyDependencies);
mockStartStep(compiledFlow);
mockEndStep(0L, compiledFlow, ExecutableType.FLOW);
mockBeginStep(2L, firstStep);
mockFinishStep(3L, firstStep);
mockBeginStep(4L, secondStep);
mockFinishStep(5L, secondStep);
ExecutionPlan executionPlan = executionPlanBuilder.createFlowExecutionPlan(compiledFlow);
assertEquals("different number of execution steps than expected", 6, executionPlan.getSteps().size());
assertEquals("flow name is different than expected", flowName, executionPlan.getName());
assertEquals("language name is different than expected", "CloudSlang", executionPlan.getLanguage());
assertEquals("begin step is different than expected", Long.valueOf(1), executionPlan.getBeginStep());
}
@Test
public void createFlowWithNoStepsShouldThrowException() throws Exception {
Map<String, Serializable> preFlowActionData = new HashMap<>();
Map<String, Serializable> postFlowActionData = new HashMap<>();
Deque<Step> steps = new LinkedList<>();
Workflow workflow = new Workflow(steps);
String flowName = "flowName";
String flowNamespace = "user.flows";
List<Input> inputs = new ArrayList<>();
List<Output> outputs = new ArrayList<>();
List<Result> results = new ArrayList<>();
Flow compiledFlow =
new Flow(preFlowActionData, postFlowActionData, workflow, flowNamespace,
flowName, inputs, outputs, results, null, systemPropertyDependencies);
mockStartStep(compiledFlow);
mockEndStep(0L, compiledFlow, ExecutableType.FLOW);
exception.expect(RuntimeException.class);
exception.expectMessage(flowName);
executionPlanBuilder.createFlowExecutionPlan(compiledFlow);
}
}