/*******************************************************************************
* (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;
import com.google.common.collect.Lists;
import io.cloudslang.lang.compiler.configuration.SlangCompilerSpringConfig;
import io.cloudslang.lang.compiler.modeller.model.Executable;
import io.cloudslang.lang.compiler.modeller.model.Flow;
import io.cloudslang.lang.compiler.modeller.model.Step;
import io.cloudslang.lang.entities.CompilationArtifact;
import io.cloudslang.lang.entities.ListLoopStatement;
import io.cloudslang.lang.entities.LoopStatement;
import io.cloudslang.lang.entities.MapLoopStatement;
import io.cloudslang.lang.entities.ScoreLangConstants;
import io.cloudslang.lang.entities.bindings.Argument;
import io.cloudslang.lang.entities.bindings.Output;
import io.cloudslang.lang.entities.bindings.ScriptFunction;
import io.cloudslang.lang.entities.bindings.values.ValueFactory;
import io.cloudslang.score.api.ExecutionPlan;
import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SlangCompilerSpringConfig.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class CompileForLoopsFlowTest {
@Autowired
private SlangCompiler compiler;
@Rule
public ExpectedException exception = ExpectedException.none();
public static ListLoopStatement validateListForLoopStatement(LoopStatement statement) {
Assert.assertEquals(true, statement instanceof ListLoopStatement);
return (ListLoopStatement) statement;
}
public static MapLoopStatement validateMapForLoopStatement(LoopStatement statement) {
Assert.assertEquals(true, statement instanceof MapLoopStatement);
return (MapLoopStatement) statement;
}
@Test
public void testPreCompileLoopFlow() throws Exception {
final URI flow = getClass().getResource("/loops/simple_loop.sl").toURI();
final Executable executable = compiler.preCompile(SlangSource.fromFile(flow));
assertNotNull("executable is null", executable);
Step step = ((Flow) executable).getWorkflow()
.getSteps()
.getFirst();
assertTrue(step.getPreStepActionData().containsKey(SlangTextualKeys.FOR_KEY));
LoopStatement forStatement = (LoopStatement) step.getPreStepActionData()
.get(SlangTextualKeys.FOR_KEY);
ListLoopStatement listLoopStatement = validateListForLoopStatement(forStatement);
assertEquals("values.split(\",\")", listLoopStatement.getExpression());
assertEquals("value", listLoopStatement.getVarName());
@SuppressWarnings("unchecked") List<Output> outputs = (List<Output>) step.getPostStepActionData()
.get(SlangTextualKeys.PUBLISH_KEY);
assertEquals("a", outputs.get(0).getValue().get());
assertEquals(Collections.singletonList(ScoreLangConstants.FAILURE_RESULT),
step.getPostStepActionData().get(SlangTextualKeys.BREAK_KEY));
}
@Test
public void testPreCompileLoopStepModifiers() throws Exception {
final URI flow = getClass().getResource("/loops/for_loop_step_modifiers.sl").toURI();
final Executable executable = compiler.preCompile(SlangSource.fromFile(flow));
// usual stuff
assertNotNull("executable is null", executable);
Step step = ((Flow) executable).getWorkflow()
.getSteps()
.getFirst();
assertTrue(step.getPreStepActionData().containsKey(SlangTextualKeys.FOR_KEY));
LoopStatement forStatement = (LoopStatement) step.getPreStepActionData()
.get(SlangTextualKeys.FOR_KEY);
ListLoopStatement listLoopStatement = validateListForLoopStatement(forStatement);
assertEquals("values.split(\",\")", listLoopStatement.getExpression());
assertEquals("x", listLoopStatement.getVarName());
@SuppressWarnings("unchecked") List<Output> outputs = (List<Output>) step.getPostStepActionData()
.get(SlangTextualKeys.PUBLISH_KEY);
assertEquals("a", outputs.get(0).getValue().get());
assertEquals(Collections.singletonList(ScoreLangConstants.FAILURE_RESULT),
step.getPostStepActionData().get(SlangTextualKeys.BREAK_KEY));
// step inputs
// noinspection unchecked
List<Argument> actualStepInputs = (List<Argument>) step.getPreStepActionData().get(SlangTextualKeys.DO_KEY);
List<Argument> expectedStepInputs = getExpectedStepInputs();
assertEquals(expectedStepInputs, actualStepInputs);
}
private List<Argument> getExpectedStepInputs() {
return Lists.newArrayList(
new Argument(
"step_input_01",
ValueFactory.create("${ x }"),
true,
Collections.<ScriptFunction>emptySet(),
Collections.<String>emptySet()
),
new Argument(
"step_input_02",
ValueFactory.create(null),
false,
Collections.<ScriptFunction>emptySet(),
Collections.<String>emptySet()
),
new Argument(
"step_input_03",
ValueFactory.create("${ step_input_03_value }"),
true,
Collections.<ScriptFunction>emptySet(),
Collections.<String>emptySet()
),
new Argument(
"step_input_04",
ValueFactory.create("${ step_input_04_value }", true),
true,
Collections.<ScriptFunction>emptySet(),
Collections.<String>emptySet()
)
);
}
@Test
public void testPreCompileLoopFlowWithBreak() throws Exception {
final URI flow = getClass().getResource("/loops/loop_with_break.sl").toURI();
final Executable executable = compiler.preCompile(SlangSource.fromFile(flow));
assertNotNull("executable is null", executable);
Step step = ((Flow) executable).getWorkflow()
.getSteps()
.getFirst();
assertEquals(Arrays.asList(ScoreLangConstants.SUCCESS_RESULT, ScoreLangConstants.FAILURE_RESULT),
step.getPostStepActionData().get(SlangTextualKeys.BREAK_KEY));
}
@Test
public void testPreCompileLoopWithCustomNavigationFlow() throws Exception {
final URI flow = getClass().getResource("/loops/loop_with_custom_navigation.sl").toURI();
final Executable executable = compiler.preCompile(SlangSource.fromFile(flow));
assertNotNull("executable is null", executable);
Step step = ((Flow) executable).getWorkflow()
.getSteps()
.getFirst();
assertTrue(step.getPreStepActionData().containsKey(SlangTextualKeys.FOR_KEY));
LoopStatement forStatement = (LoopStatement) step.getPreStepActionData()
.get(SlangTextualKeys.FOR_KEY);
ListLoopStatement listLoopStatement = validateListForLoopStatement(forStatement);
assertEquals("values.split(\",\")", listLoopStatement.getExpression());
assertEquals("value", listLoopStatement.getVarName());
@SuppressWarnings("unchecked")
List<Map<String, String>> actual = (List<Map<String, String>>) step.getPostStepActionData()
.get(SlangTextualKeys.NAVIGATION_KEY);
assertEquals("print_other_values", actual.get(0).get(ScoreLangConstants.SUCCESS_RESULT));
}
@Test
public void testCompileLoopFlow() throws Exception {
final URI flow = getClass().getResource("/loops/simple_loop.sl").toURI();
final URI operation = getClass().getResource("/loops/print.sl").toURI();
Set<SlangSource> path = new HashSet<>();
path.add(SlangSource.fromFile(operation));
CompilationArtifact artifact = compiler.compile(SlangSource.fromFile(flow), path);
assertNotNull("artifact is null", artifact);
ExecutionPlan executionPlan = artifact.getExecutionPlan();
assertNotNull("executionPlan is null", executionPlan);
Map<String, ?> startStepActionData = executionPlan.getStep(2L)
.getActionData();
assertTrue(startStepActionData.containsKey(ScoreLangConstants.LOOP_KEY));
LoopStatement forStatement = (LoopStatement) startStepActionData.get(ScoreLangConstants.LOOP_KEY);
ListLoopStatement listLoopStatement = validateListForLoopStatement(forStatement);
assertEquals("values.split(\",\")", listLoopStatement.getExpression());
assertEquals("value", listLoopStatement.getVarName());
Map<String, ?> endStepActionData = executionPlan.getStep(3L)
.getActionData();
assertEquals(Collections.singletonList(ScoreLangConstants.FAILURE_RESULT),
endStepActionData.get(ScoreLangConstants.BREAK_LOOP_KEY));
}
@Test
public void testCompileEmptyLoopFlow() throws Exception {
final URI flow = getClass().getResource("/loops/simple_loop_empty.sl").toURI();
final URI operation = getClass().getResource("/loops/print.sl").toURI();
Set<SlangSource> path = new HashSet<>();
path.add(SlangSource.fromFile(operation));
exception.expect(RuntimeException.class);
exception.expectMessage("For step 'print_values' syntax is illegal.\n" +
"For statement is empty.");
compiler.compile(SlangSource.fromFile(flow), path);
}
@Test
public void testCompileEmptyStringLoopFlow() throws Exception {
final URI flow = getClass().getResource("/loops/simple_loop_empty_string.sl").toURI();
final URI operation = getClass().getResource("/loops/print.sl").toURI();
Set<SlangSource> path = new HashSet<>();
path.add(SlangSource.fromFile(operation));
exception.expect(RuntimeException.class);
exception.expectMessage("For step 'print_values' syntax is illegal.\n" +
"For statement is empty.");
compiler.compile(SlangSource.fromFile(flow), path);
}
@Test
public void testCompileLoopFlowWithBreak() throws Exception {
final URI flow = getClass().getResource("/loops/loop_with_break.sl").toURI();
final URI operation = getClass().getResource("/loops/print.sl").toURI();
Set<SlangSource> path = new HashSet<>();
path.add(SlangSource.fromFile(operation));
CompilationArtifact artifact = compiler.compile(SlangSource.fromFile(flow), path);
assertNotNull("artifact is null", artifact);
ExecutionPlan executionPlan = artifact.getExecutionPlan();
Map<String, ?> endStepActionData = executionPlan.getStep(3L)
.getActionData();
assertEquals(Arrays.asList(ScoreLangConstants.SUCCESS_RESULT, ScoreLangConstants.FAILURE_RESULT),
endStepActionData.get(ScoreLangConstants.BREAK_LOOP_KEY));
}
@Test
public void testPreCompileLoopFlowWithMap() throws Exception {
final URI flow = getClass().getResource("/loops/simple_loop_with_map.sl").toURI();
Executable executable = compiler.preCompile(SlangSource.fromFile(flow));
assertNotNull("executable is null", executable);
Step step = ((Flow) executable).getWorkflow()
.getSteps()
.getFirst();
assertTrue(step.getPreStepActionData().containsKey(SlangTextualKeys.FOR_KEY));
LoopStatement forStatement = (LoopStatement) step.getPreStepActionData()
.get(SlangTextualKeys.FOR_KEY);
MapLoopStatement mapLoopStatement = validateMapForLoopStatement(forStatement);
assertEquals("person_map", mapLoopStatement.getExpression());
assertEquals("k", mapLoopStatement.getKeyName());
assertEquals("v", mapLoopStatement.getValueName());
@SuppressWarnings("unchecked") List<Output> outputs = (List<Output>) step.getPostStepActionData()
.get(SlangTextualKeys.PUBLISH_KEY);
assertEquals("a", outputs.get(0).getValue().get());
assertEquals(Collections.singletonList(ScoreLangConstants.FAILURE_RESULT),
step.getPostStepActionData().get(SlangTextualKeys.BREAK_KEY));
}
@Test
public void testPreCompileLoopFlowWithMapWithBreak() throws Exception {
final URI flow = getClass().getResource("/loops/loop_with_break_with_map.sl").toURI();
final Executable executable = compiler.preCompile(SlangSource.fromFile(flow));
assertNotNull("executable is null", executable);
Step step = ((Flow) executable).getWorkflow()
.getSteps()
.getFirst();
assertEquals(Arrays.asList(ScoreLangConstants.SUCCESS_RESULT, ScoreLangConstants.FAILURE_RESULT),
step.getPostStepActionData().get(SlangTextualKeys.BREAK_KEY));
}
@Ignore("Remove when support for maps in loops is added")
@Test
public void testPreCompileLoopWithMapWithCustomNavigationFlow() throws Exception {
final URI flow = getClass().getResource("/loops/loop_with_custom_navigation_with_map.sl").toURI();
final Executable executable = compiler.preCompile(SlangSource.fromFile(flow));
assertNotNull("executable is null", executable);
Step step = ((Flow) executable).getWorkflow()
.getSteps()
.getFirst();
assertTrue(step.getPreStepActionData().containsKey(SlangTextualKeys.FOR_KEY));
LoopStatement forStatement = (LoopStatement) step.getPreStepActionData()
.get(SlangTextualKeys.FOR_KEY);
MapLoopStatement mapLoopStatement = validateMapForLoopStatement(forStatement);
assertEquals("person_map", mapLoopStatement.getExpression());
assertEquals("k", mapLoopStatement.getKeyName());
assertEquals("v", mapLoopStatement.getValueName());
@SuppressWarnings("unchecked")
List<Map<String, String>> actual = (List<Map<String, String>>) step.getPostStepActionData()
.get(SlangTextualKeys.NAVIGATION_KEY);
assertEquals("print_other_values", actual.get(0).get(ScoreLangConstants.SUCCESS_RESULT));
}
@Test
public void testCompileLoopWithMapFlow() throws Exception {
final URI flow = getClass().getResource("/loops/simple_loop_with_map.sl").toURI();
final URI operation = getClass().getResource("/loops/print.sl").toURI();
final Set<SlangSource> path = new HashSet<>();
path.add(SlangSource.fromFile(operation));
CompilationArtifact artifact = compiler.compile(SlangSource.fromFile(flow), path);
assertNotNull("artifact is null", artifact);
ExecutionPlan executionPlan = artifact.getExecutionPlan();
assertNotNull("executionPlan is null", executionPlan);
Map<String, ?> startStartActionData = executionPlan.getStep(2L)
.getActionData();
assertTrue(startStartActionData.containsKey(ScoreLangConstants.LOOP_KEY));
LoopStatement forStatement = (LoopStatement) startStartActionData.get(ScoreLangConstants.LOOP_KEY);
MapLoopStatement mapLoopStatement = validateMapForLoopStatement(forStatement);
assertEquals("person_map", mapLoopStatement.getExpression());
assertEquals("k", mapLoopStatement.getKeyName());
assertEquals("v", mapLoopStatement.getValueName());
Map<String, ?> endStepActionData = executionPlan.getStep(3L)
.getActionData();
assertEquals(Collections.singletonList(ScoreLangConstants.FAILURE_RESULT),
endStepActionData.get(ScoreLangConstants.BREAK_LOOP_KEY));
}
@Test
public void testCompileLoopFlowWithMapWithBreak() throws Exception {
final URI flow = getClass().getResource("/loops/loop_with_break_with_map.sl").toURI();
final URI operation = getClass().getResource("/loops/print.sl").toURI();
final Set<SlangSource> path = new HashSet<>();
path.add(SlangSource.fromFile(operation));
CompilationArtifact artifact = compiler.compile(SlangSource.fromFile(flow), path);
assertNotNull("artifact is null", artifact);
ExecutionPlan executionPlan = artifact.getExecutionPlan();
Map<String, ?> endStepActionData = executionPlan.getStep(3L)
.getActionData();
assertEquals(Arrays.asList(ScoreLangConstants.SUCCESS_RESULT, ScoreLangConstants.FAILURE_RESULT),
endStepActionData.get(ScoreLangConstants.BREAK_LOOP_KEY));
}
@Test
public void testCompileLoopFlowWithSystemProperty() throws Exception {
final URI flow = getClass().getResource("/loops/loop_from_property_with_custom_navigation.sl").toURI();
final URI operation = getClass().getResource("/loops/print.sl").toURI();
final Set<SlangSource> path = new HashSet<>();
path.add(SlangSource.fromFile(operation));
CompilationArtifact artifact = compiler.compile(SlangSource.fromFile(flow), path);
assertNotNull("artifact is null", artifact);
Assert.assertEquals(1, artifact.getSystemProperties().size());
Assert.assertEquals("loops.list", artifact.getSystemProperties().iterator().next());
}
@Test
public void testCompileLoopFlowWithBreakOnNonExistingResult() throws Exception {
final URI flow = getClass().getResource("/loops/loop_with_break_on_non_existing_result.sl").toURI();
final URI operation = getClass().getResource("/loops/print.sl").toURI();
final Set<SlangSource> path = new HashSet<>();
path.add(SlangSource.fromFile(operation));
exception.expect(IllegalArgumentException.class);
exception.expectMessage(
"Cannot compile flow 'loops.loop_with_break_on_non_existing_result' since in step" +
" 'print_values' the results [CUSTOM_1, CUSTOM_2] declared in 'break' section " +
"are not declared in the dependency 'loops.print' result section."
);
compiler.compile(SlangSource.fromFile(flow), path);
}
}