io.cloudslang.lang.tools.build.SlangBuilderTest.java Source code

Java tutorial

Introduction

Here is the source code for io.cloudslang.lang.tools.build.SlangBuilderTest.java

Source

/*******************************************************************************
 * (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.tools.build;

import com.google.common.collect.Maps;
import io.cloudslang.lang.api.Slang;
import io.cloudslang.lang.commons.services.api.SlangCompilationService;
import io.cloudslang.lang.commons.services.api.SlangSourceService;
import io.cloudslang.lang.commons.services.impl.SlangCompilationServiceImpl;
import io.cloudslang.lang.compiler.MetadataExtractor;
import io.cloudslang.lang.compiler.SlangCompiler;
import io.cloudslang.lang.compiler.SlangSource;
import io.cloudslang.lang.compiler.modeller.DependenciesHelper;
import io.cloudslang.lang.compiler.modeller.TransformersHandler;
import io.cloudslang.lang.compiler.modeller.model.Executable;
import io.cloudslang.lang.compiler.modeller.model.Flow;
import io.cloudslang.lang.compiler.modeller.model.Metadata;
import io.cloudslang.lang.compiler.modeller.result.ExecutableModellingResult;
import io.cloudslang.lang.compiler.modeller.result.MetadataModellingResult;
import io.cloudslang.lang.compiler.modeller.transformers.PublishTransformer;
import io.cloudslang.lang.compiler.modeller.transformers.ResultsTransformer;
import io.cloudslang.lang.compiler.scorecompiler.ScoreCompiler;
import io.cloudslang.lang.compiler.validator.ExecutableValidator;
import io.cloudslang.lang.compiler.validator.ExecutableValidatorImpl;
import io.cloudslang.lang.compiler.validator.PreCompileValidator;
import io.cloudslang.lang.compiler.validator.PreCompileValidatorImpl;
import io.cloudslang.lang.compiler.validator.SystemPropertyValidator;
import io.cloudslang.lang.compiler.validator.SystemPropertyValidatorImpl;
import io.cloudslang.lang.entities.CompilationArtifact;
import io.cloudslang.lang.entities.bindings.Input;
import io.cloudslang.lang.logging.LoggingService;
import io.cloudslang.lang.logging.LoggingServiceImpl;
import io.cloudslang.lang.tools.build.SlangBuildMain.BulkRunMode;
import io.cloudslang.lang.tools.build.tester.IRunTestResults;
import io.cloudslang.lang.tools.build.tester.RunTestsResults;
import io.cloudslang.lang.tools.build.tester.SlangTestRunner;
import io.cloudslang.lang.tools.build.tester.SlangTestRunner.TestCaseRunState;
import io.cloudslang.lang.tools.build.tester.TestRun;
import io.cloudslang.lang.tools.build.tester.parallel.report.LoggingSlangTestCaseEventListener;
import io.cloudslang.lang.tools.build.tester.parallel.report.ThreadSafeRunTestResults;
import io.cloudslang.lang.tools.build.tester.parallel.services.ParallelTestCaseExecutorService;
import io.cloudslang.lang.tools.build.tester.parallel.services.TestCaseEventDispatchService;
import io.cloudslang.lang.tools.build.tester.parse.SlangTestCase;
import io.cloudslang.lang.tools.build.tester.parse.TestCasesYamlParser;
import io.cloudslang.lang.tools.build.tester.runconfiguration.BuildModeConfig;
import io.cloudslang.lang.tools.build.tester.runconfiguration.TestRunInfoService;
import io.cloudslang.lang.tools.build.tester.runconfiguration.TestRunInfoServiceImpl;
import io.cloudslang.lang.tools.build.tester.runconfiguration.strategy.ConflictResolutionStrategy;
import io.cloudslang.lang.tools.build.tester.runconfiguration.strategy.DefaultResolutionStrategy;
import io.cloudslang.lang.tools.build.validation.StaticValidator;
import io.cloudslang.lang.tools.build.validation.StaticValidatorImpl;
import io.cloudslang.lang.tools.build.verifier.SlangContentVerifier;
import io.cloudslang.score.api.ExecutionPlan;
import java.io.File;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.collections.ListUtils;
import org.apache.commons.io.FileUtils;
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.InOrder;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
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 org.yaml.snakeyaml.Yaml;

import static com.google.common.collect.Lists.newArrayList;
import static io.cloudslang.lang.tools.build.SlangBuildMain.BulkRunMode.ALL_PARALLEL;
import static io.cloudslang.lang.tools.build.SlangBuildMain.BulkRunMode.ALL_SEQUENTIAL;
import static io.cloudslang.lang.tools.build.SlangBuildMain.BulkRunMode.POSSIBLY_MIXED;
import static java.util.Arrays.asList;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyList;
import static org.mockito.Matchers.anyMap;
import static org.mockito.Matchers.anyMapOf;
import static org.mockito.Matchers.anySet;
import static org.mockito.Matchers.anySetOf;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.python.google.common.collect.Sets.newHashSet;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SlangBuilderTest.Config.class)
public class SlangBuilderTest {

    private static final Set<String> SYSTEM_PROPERTY_DEPENDENCIES = Collections.emptySet();
    private static final CompilationArtifact EMPTY_COMPILATION_ARTIFACT = new CompilationArtifact(
            new ExecutionPlan(), new HashMap<String, ExecutionPlan>(), new ArrayList<Input>(),
            new HashSet<String>());
    private static final Flow EMPTY_EXECUTABLE = new Flow(null, null, null, "no_dependencies", "empty_flow", null,
            null, null, null, new HashSet<String>(), SYSTEM_PROPERTY_DEPENDENCIES);

    private static final Metadata EMPTY_METADATA = new Metadata();

    @Autowired
    private SlangBuilder slangBuilder;

    @Autowired
    private SlangCompiler slangCompiler;

    @Autowired
    private ScoreCompiler scoreCompiler;

    @Autowired
    private MetadataExtractor metadataExtractor;

    @Autowired
    private StaticValidator staticValidator;

    @Autowired
    private SlangTestRunner slangTestRunner;

    @Autowired
    public ParallelTestCaseExecutorService parallelTestCaseExecutorService;

    @Autowired
    public TestCaseEventDispatchService testCaseEventDispatchService;

    @Autowired
    private TestRunInfoService testRunInfoService;

    @Autowired
    private LoggingService loggingService;

    @Autowired
    private LoggingSlangTestCaseEventListener loggingSlangTestCaseEventListener;

    @Rule
    public ExpectedException exception = ExpectedException.none();

    private SlangBuildMain.BuildMode buildMode = SlangBuildMain.BuildMode.BASIC;
    private Set<String> changedFiles = new HashSet<>();
    private BuildModeConfig buildModeConfig = BuildModeConfig.createBasicBuildModeConfig();

    @Before
    public void resetMocks() {
        reset(slangCompiler);
        reset(scoreCompiler);
        reset(slangTestRunner);
    }

    @Test
    public void testNullDirPath() throws Exception {
        exception.expect(IllegalArgumentException.class);
        exception.expectMessage("path");
        slangBuilder.buildSlangContent(null, null, null, null, false, false, ALL_SEQUENTIAL, buildMode,
                changedFiles);
    }

    @Test
    public void testEmptyDirPath() throws Exception {
        exception.expect(IllegalArgumentException.class);
        exception.expectMessage("path");
        slangBuilder.buildSlangContent("", "content", null, null, false, false, ALL_SEQUENTIAL, buildMode,
                changedFiles);
    }

    @Test
    public void testParallelFlag() throws Exception {
        Path testPath = null;
        try {
            String projectPath = "aaa/bb/cc";
            final List<String> suites = newArrayList("suite1", "suite2");
            testPath = Files.createTempDirectory("testPath");
            String testPathString = testPath.toString();

            final ThreadSafeRunTestResults runTestsResults = new ThreadSafeRunTestResults();
            doNothing().when(slangTestRunner).runTestsParallel(eq(projectPath), anyMap(), anyMap(),
                    any(ThreadSafeRunTestResults.class));
            doReturn(Maps.newHashMap()).when(slangTestRunner).createTestCases(anyString(), anySet());

            slangBuilder.runTests(Maps.<String, Executable>newHashMap(), projectPath, testPathString, suites,
                    ALL_PARALLEL, buildMode, changedFiles);
            verify(slangTestRunner).runTestsParallel(eq(projectPath), anyMap(), anyMap(), eq(runTestsResults));
            verify(slangTestRunner, never()).runTestsSequential(anyString(), anyMap(), anyMap(),
                    any(RunTestsResults.class));

        } finally {
            if (testPath != null) {
                FileUtils.deleteQuietly(testPath.toFile());
            }
        }
    }

    @Test
    public void testIllegalDirPath() throws Exception {
        exception.expect(IllegalArgumentException.class);
        exception.expectMessage("c/h/j");
        exception.expectMessage("directory");
        slangBuilder.buildSlangContent("c/h/j", "c/h/j/content", null, null, false, false, ALL_SEQUENTIAL,
                buildMode, changedFiles);
    }

    @Test
    public void testPreCompileIllegalSlangFile() throws Exception {
        URI resource = getClass().getResource("/no_dependencies").toURI();
        when(slangCompiler.preCompile(any(SlangSource.class))).thenThrow(new RuntimeException());
        exception.expect(RuntimeException.class);
        SlangBuildResults slangBuildResults = slangBuilder.buildSlangContent(resource.getPath(), resource.getPath(),
                null, null, false, false, ALL_SEQUENTIAL, buildMode, changedFiles);
        assertNotNull(slangBuildResults.getCompilationExceptions());
        assertTrue(slangBuildResults.getCompilationExceptions().size() > 0);
        throw slangBuildResults.getCompilationExceptions().get(0);
    }

    @Test
    public void testNotAllSlangFilesWerePreCompiled() throws Exception {
        final URI resource = getClass().getResource("/no_dependencies").toURI();
        when(slangCompiler.preCompileSource(any(SlangSource.class)))
                .thenReturn(new ExecutableModellingResult(null, new ArrayList<RuntimeException>()));
        when(metadataExtractor.extractMetadataModellingResult(any(SlangSource.class), eq(false)))
                .thenReturn(new MetadataModellingResult(EMPTY_METADATA, new ArrayList<RuntimeException>()));
        exception.expect(RuntimeException.class);
        exception.expectMessage("1");
        exception.expectMessage("0");
        exception.expectMessage("compiled");
        SlangBuildResults slangBuildResults = slangBuilder.buildSlangContent(resource.getPath(), resource.getPath(),
                null, null, false, false, ALL_SEQUENTIAL, buildMode, changedFiles);
        assertNotNull(slangBuildResults.getCompilationExceptions());
        assertTrue(slangBuildResults.getCompilationExceptions().size() > 0);
        throw slangBuildResults.getCompilationExceptions().get(0);
    }

    @Test
    public void testCompileValidSlangFileNoDependencies() throws Exception {
        final URI resource = getClass().getResource("/no_dependencies").toURI();
        when(slangCompiler.preCompileSource(any(SlangSource.class)))
                .thenReturn(new ExecutableModellingResult(EMPTY_EXECUTABLE, new ArrayList<RuntimeException>()));
        when(metadataExtractor.extractMetadataModellingResult(any(SlangSource.class), eq(false)))
                .thenReturn(new MetadataModellingResult(null, new ArrayList<RuntimeException>()));
        when(scoreCompiler.compile(EMPTY_EXECUTABLE, new HashSet<Executable>()))
                .thenReturn(EMPTY_COMPILATION_ARTIFACT);

        SlangBuildResults buildResults = slangBuilder.buildSlangContent(resource.getPath(), resource.getPath(),
                null, null, false, false, ALL_SEQUENTIAL, buildMode, changedFiles);
        int numberOfCompiledSlangFiles = buildResults.getNumberOfCompiledSources();
        assertEquals("Did not compile all Slang files. Expected to compile: 1, but compiled: "
                + numberOfCompiledSlangFiles, numberOfCompiledSlangFiles, 1);
    }

    @Test
    public void testCompileInvalidSlangFile() throws Exception {
        final URI resource = getClass().getResource("/no_dependencies").toURI();
        when(slangCompiler.preCompileSource(any(SlangSource.class)))
                .thenReturn(new ExecutableModellingResult(EMPTY_EXECUTABLE, new ArrayList<RuntimeException>()));
        when(metadataExtractor.extractMetadataModellingResult(any(SlangSource.class), eq(false)))
                .thenReturn(new MetadataModellingResult(null, new ArrayList<RuntimeException>()));
        when(scoreCompiler.compile(EMPTY_EXECUTABLE, new HashSet<Executable>())).thenThrow(new RuntimeException());
        exception.expect(RuntimeException.class);
        SlangBuildResults results = slangBuilder.buildSlangContent(resource.getPath(), resource.getPath(), null,
                null, false, false, ALL_SEQUENTIAL, buildMode, changedFiles);

        throw results.getCompilationExceptions().get(0);
    }

    @Test
    public void testNotAllSlangFilesWereCompiled() throws Exception {
        final URI resource = getClass().getResource("/no_dependencies").toURI();
        when(slangCompiler.preCompileSource(any(SlangSource.class)))
                .thenReturn(new ExecutableModellingResult(EMPTY_EXECUTABLE, new ArrayList<RuntimeException>()));
        when(metadataExtractor.extractMetadataModellingResult(any(SlangSource.class), eq(false)))
                .thenReturn(new MetadataModellingResult(null, new ArrayList<RuntimeException>()));
        when(scoreCompiler.compile(EMPTY_EXECUTABLE, new HashSet<Executable>())).thenReturn(null);
        exception.expect(RuntimeException.class);
        exception.expectMessage("1");
        exception.expectMessage("0");
        exception.expectMessage("compile");
        exception.expectMessage("models");
        SlangBuildResults results = slangBuilder.buildSlangContent(resource.getPath(), resource.getPath(), null,
                null, false, false, ALL_SEQUENTIAL, buildMode, changedFiles);

        throw results.getCompilationExceptions().get(0);
    }

    @Test
    public void testCompileValidSlangFileWithMissingDependencies() throws Exception {
        final URI resource = getClass().getResource("/no_dependencies").toURI();
        Set<String> flowDependencies = new HashSet<>();
        flowDependencies.add("dep1");
        Flow newExecutable = new Flow(null, null, null, "no_dependencies", "empty_flow", null, null, null, null,
                flowDependencies, SYSTEM_PROPERTY_DEPENDENCIES);
        when(slangCompiler.preCompileSource(any(SlangSource.class)))
                .thenReturn(new ExecutableModellingResult(newExecutable, new ArrayList<RuntimeException>()));
        when(scoreCompiler.compile(newExecutable, new HashSet<Executable>()))
                .thenReturn(EMPTY_COMPILATION_ARTIFACT);
        when(metadataExtractor.extractMetadataModellingResult(any(SlangSource.class), eq(false)))
                .thenReturn(new MetadataModellingResult(EMPTY_METADATA, new ArrayList<RuntimeException>()));
        doCallRealMethod().when(staticValidator).validateSlangFile(any(File.class), eq(newExecutable),
                eq(EMPTY_METADATA), eq(false), any(Queue.class));
        exception.expect(RuntimeException.class);
        exception.expectMessage("dependency");
        exception.expectMessage("dep1");
        SlangBuildResults results = slangBuilder.buildSlangContent(resource.getPath(), resource.getPath(), null,
                null, false, false, ALL_SEQUENTIAL, buildMode, changedFiles);

        throw results.getCompilationExceptions().get(0);
    }

    @Test
    public void testCompileValidSlangFileWithDependencies() throws Exception {
        final URI resource = getClass().getResource("/dependencies").toURI();
        final URI emptyFlowUri = getClass().getResource("/dependencies/empty_flow.sl").toURI();
        final URI dependencyUri = getClass().getResource("/dependencies/dependency.sl").toURI();
        SlangSource emptyFlowSource = SlangSource.fromFile(emptyFlowUri);
        SlangSource dependencySource = SlangSource.fromFile(dependencyUri);

        Set<String> flowDependencies = new HashSet<>();
        flowDependencies.add("dependencies.dependency");
        Flow emptyFlowExecutable = new Flow(null, null, null, "dependencies", "empty_flow", null, null, null, null,
                flowDependencies, SYSTEM_PROPERTY_DEPENDENCIES);
        when(slangCompiler.preCompileSource(emptyFlowSource))
                .thenReturn(new ExecutableModellingResult(emptyFlowExecutable, new ArrayList<RuntimeException>()));
        Flow dependencyExecutable = new Flow(null, null, null, "dependencies", "dependency", null, null, null, null,
                new HashSet<String>(), SYSTEM_PROPERTY_DEPENDENCIES);
        when(slangCompiler.preCompileSource(dependencySource))
                .thenReturn(new ExecutableModellingResult(dependencyExecutable, new ArrayList<RuntimeException>()));
        HashSet<Executable> dependencies = new HashSet<>();
        dependencies.add(dependencyExecutable);
        when(scoreCompiler.compile(emptyFlowExecutable, dependencies)).thenReturn(EMPTY_COMPILATION_ARTIFACT);
        when(scoreCompiler.compile(dependencyExecutable, new HashSet<Executable>()))
                .thenReturn(EMPTY_COMPILATION_ARTIFACT);
        when(metadataExtractor.extractMetadataModellingResult(any(SlangSource.class), eq(false)))
                .thenReturn(new MetadataModellingResult(EMPTY_METADATA, new ArrayList<RuntimeException>()));
        SlangBuildResults buildResults = slangBuilder.buildSlangContent(resource.getPath(), resource.getPath(),
                null, null, false, false, ALL_SEQUENTIAL, buildMode, changedFiles);
        int numberOfCompiledSlangFiles = buildResults.getNumberOfCompiledSources();
        // properties file should be ignored
        assertEquals("Did not compile all Slang files. Expected to compile: 2, but compiled: "
                + numberOfCompiledSlangFiles, numberOfCompiledSlangFiles, 2);
    }

    @Test
    public void testInvalidNamespaceFlow() throws Exception {
        final URI resource = getClass().getResource("/no_dependencies").toURI();
        final Flow newExecutable = new Flow(null, null, null, "wrong.namespace", "empty_flow", null, null, null,
                null, new HashSet<String>(), SYSTEM_PROPERTY_DEPENDENCIES);
        when(slangCompiler.preCompileSource(any(SlangSource.class)))
                .thenReturn(new ExecutableModellingResult(newExecutable, new ArrayList<RuntimeException>()));
        when(metadataExtractor.extractMetadataModellingResult(any(SlangSource.class), eq(false)))
                .thenReturn(new MetadataModellingResult(EMPTY_METADATA, new ArrayList<RuntimeException>()));
        doCallRealMethod().when(staticValidator).validateSlangFile(any(File.class), eq(newExecutable),
                eq(EMPTY_METADATA), eq(false), any(Queue.class));
        exception.expect(RuntimeException.class);
        exception.expectMessage("Namespace");
        exception.expectMessage("wrong.namespace");
        SlangBuildResults slangBuildResults = slangBuilder.buildSlangContent(resource.getPath(), resource.getPath(),
                null, null, false, false, ALL_SEQUENTIAL, buildMode, changedFiles);
        assertNotNull(slangBuildResults.getCompilationExceptions());
        assertTrue(slangBuildResults.getCompilationExceptions().size() > 0);
        throw slangBuildResults.getCompilationExceptions().get(0);
    }

    @Test
    public void testInvalidFlowName() throws Exception {
        final URI resource = getClass().getResource("/no_dependencies").toURI();
        final Flow newExecutable = new Flow(null, null, null, "no_dependencies", "wrong_name", null, null, null,
                null, new HashSet<String>(), SYSTEM_PROPERTY_DEPENDENCIES);
        when(slangCompiler.preCompileSource(any(SlangSource.class)))
                .thenReturn(new ExecutableModellingResult(newExecutable, new ArrayList<RuntimeException>()));
        when(metadataExtractor.extractMetadataModellingResult(any(SlangSource.class), eq(false)))
                .thenReturn(new MetadataModellingResult(EMPTY_METADATA, new ArrayList<RuntimeException>()));
        doCallRealMethod().when(staticValidator).validateSlangFile(any(File.class), eq(newExecutable),
                eq(EMPTY_METADATA), eq(false), any(Queue.class));

        exception.expect(RuntimeException.class);
        exception.expectMessage("Name");
        exception.expectMessage("wrong_name");

        SlangBuildResults slangBuildResults = slangBuilder.buildSlangContent(resource.getPath(), resource.getPath(),
                null, null, false, false, ALL_SEQUENTIAL, buildMode, changedFiles);
        assertNotNull(slangBuildResults.getCompilationExceptions());
        assertTrue(slangBuildResults.getCompilationExceptions().size() > 0);

        throw slangBuildResults.getCompilationExceptions().get(0);
    }

    @Test
    public void testValidFlowNamespaceWithAllValidCharsTypes() throws Exception {
        final URI resource = getClass().getResource("/no_dependencies-0123456789").toURI();
        final Flow executable = new Flow(null, null, null, "no_dependencies-0123456789", "empty_flow", null, null,
                null, null, new HashSet<String>(), SYSTEM_PROPERTY_DEPENDENCIES);
        when(slangCompiler.preCompileSource(any(SlangSource.class)))
                .thenReturn(new ExecutableModellingResult(executable, new ArrayList<RuntimeException>()));
        when(scoreCompiler.compile(executable, new HashSet<Executable>())).thenReturn(EMPTY_COMPILATION_ARTIFACT);
        SlangBuildResults buildResults = slangBuilder.buildSlangContent(resource.getPath(), resource.getPath(),
                null, null, false, false, ALL_SEQUENTIAL, buildMode, changedFiles);
        int numberOfCompiledSlangFiles = buildResults.getNumberOfCompiledSources();
        assertEquals("Did not compile all Slang files. Expected to compile: 1, but compiled: "
                + numberOfCompiledSlangFiles, numberOfCompiledSlangFiles, 1);
    }

    @Test
    public void testValidFlowNamespaceCaseInsensitive() throws Exception {
        final URI resource = getClass().getResource("/no_dependencies").toURI();
        final Flow executable = new Flow(null, null, null, "No_Dependencies", "empty_flow", null, null, null, null,
                new HashSet<String>(), SYSTEM_PROPERTY_DEPENDENCIES);
        when(slangCompiler.preCompileSource(any(SlangSource.class)))
                .thenReturn(new ExecutableModellingResult(executable, new ArrayList<RuntimeException>()));
        when(scoreCompiler.compile(executable, new HashSet<Executable>())).thenReturn(EMPTY_COMPILATION_ARTIFACT);
        when(metadataExtractor.extractMetadataModellingResult(any(SlangSource.class), eq(false)))
                .thenReturn(new MetadataModellingResult(EMPTY_METADATA, new ArrayList<RuntimeException>()));
        SlangBuildResults buildResults = slangBuilder.buildSlangContent(resource.getPath(), resource.getPath(),
                null, null, false, false, ALL_SEQUENTIAL, buildMode, changedFiles);
        int numberOfCompiledSlangFiles = buildResults.getNumberOfCompiledSources();
        assertEquals("Did not compile all Slang files. Expected to compile: 1, but compiled: "
                + numberOfCompiledSlangFiles, 1, numberOfCompiledSlangFiles);
    }

    @Test
    public void testNamespaceWithInvalidCharsFlow() throws Exception {
        final URI resource = getClass().getResource("/invalid-chars$").toURI();
        final Flow newExecutable = new Flow(null, null, null, "invalid-chars$", "empty_flow", null, null, null,
                null, new HashSet<String>(), SYSTEM_PROPERTY_DEPENDENCIES);
        when(slangCompiler.preCompileSource(any(SlangSource.class)))
                .thenReturn(new ExecutableModellingResult(newExecutable, new ArrayList<RuntimeException>()));
        when(metadataExtractor.extractMetadataModellingResult(any(SlangSource.class), eq(false)))
                .thenReturn(new MetadataModellingResult(EMPTY_METADATA, new ArrayList<RuntimeException>()));
        doCallRealMethod().when(staticValidator).validateSlangFile(any(File.class), eq(newExecutable),
                eq(EMPTY_METADATA), eq(false), any(Queue.class));
        exception.expect(RuntimeException.class);
        exception.expectMessage("invalid-chars$");
        exception.expectMessage("alphanumeric");
        SlangBuildResults slangBuildResults = slangBuilder.buildSlangContent(resource.getPath(), resource.getPath(),
                null, null, false, false, ALL_SEQUENTIAL, buildMode, changedFiles);
        assertNotNull(slangBuildResults.getCompilationExceptions());
        assertTrue(slangBuildResults.getCompilationExceptions().size() > 0);
        throw slangBuildResults.getCompilationExceptions().get(0);
    }

    @Test
    public void testCompileSlangFileAndRunTests() throws Exception {
        final URI contentResource = getClass().getResource("/no_dependencies").toURI();
        final URI testResource = getClass().getResource("/test/valid").toURI();
        when(slangCompiler.preCompileSource(any(SlangSource.class)))
                .thenReturn(new ExecutableModellingResult(EMPTY_EXECUTABLE, new ArrayList<RuntimeException>()));
        when(metadataExtractor.extractMetadataModellingResult(any(SlangSource.class), eq(false)))
                .thenReturn(new MetadataModellingResult(null, new ArrayList<RuntimeException>()));
        when(scoreCompiler.compile(EMPTY_EXECUTABLE, new HashSet<Executable>()))
                .thenReturn(EMPTY_COMPILATION_ARTIFACT);

        doAnswer(new Answer() {
            @Override
            public Void answer(InvocationOnMock invocationOnMock) throws Throwable {
                Object[] arguments = invocationOnMock.getArguments();
                RunTestsResults runTestsResultsInner = (RunTestsResults) arguments[arguments.length - 1];
                runTestsResultsInner.addFailedTest("test1", new TestRun(
                        new SlangTestCase("test1", "", null, null, null, null, null, null, null), "message"));
                return null;
            }
        }).when(slangTestRunner).runTestsSequential((any(String.class)), anyMap(), anyMap(),
                any(RunTestsResults.class));
        SlangBuildResults buildResults = slangBuilder.buildSlangContent(contentResource.getPath(),
                contentResource.getPath(), testResource.getPath(), null, false, false, ALL_SEQUENTIAL, buildMode,
                changedFiles);
        int numberOfCompiledSlangFiles = buildResults.getNumberOfCompiledSources();
        IRunTestResults actualRunTestsResults = buildResults.getRunTestsResults();
        assertEquals("Did not compile all Slang files. Expected to compile: 1, but compiled: "
                + numberOfCompiledSlangFiles, numberOfCompiledSlangFiles, 1);
        assertEquals("1 test case should fail", 1, actualRunTestsResults.getFailedTests().size());
    }

    @Test
    public void testTestCaseWithIncorrectTestFlowReference() throws Exception {
        final URI contentResource = getClass().getResource("/no_dependencies").toURI();
        final URI testResource = getClass().getResource("/test/valid").toURI();
        when(slangCompiler.preCompileSource(any(SlangSource.class)))
                .thenReturn(new ExecutableModellingResult(EMPTY_EXECUTABLE, new ArrayList<RuntimeException>()));
        when(metadataExtractor.extractMetadataModellingResult(any(SlangSource.class), eq(false)))
                .thenReturn(new MetadataModellingResult(null, new ArrayList<RuntimeException>()));
        when(scoreCompiler.compile(EMPTY_EXECUTABLE, new HashSet<Executable>()))
                .thenReturn(EMPTY_COMPILATION_ARTIFACT);

        doNothing().when(slangTestRunner).runTestsSequential(any(String.class),
                anyMapOf(String.class, SlangTestCase.class), anyMapOf(String.class, CompilationArtifact.class),
                any(ThreadSafeRunTestResults.class));

        Map<String, SlangTestCase> testCases = new HashMap<>();
        SlangTestCase testCaseWithIncorrectFlowPath = new SlangTestCase("i_don_t_exist", "a.b.c.i_don_t_exist", "",
                Collections.<String>emptyList(), "", Collections.<Map>emptyList(), Collections.<Map>emptyList(),
                false, "");
        testCases.put("i_don_t_exist", testCaseWithIncorrectFlowPath);
        when(slangTestRunner.createTestCases(anyString(), anySetOf(String.class))).thenReturn(testCases);

        SlangBuildResults buildResults = slangBuilder.buildSlangContent(contentResource.getPath(),
                contentResource.getPath(), testResource.getPath(), null, false, false, ALL_SEQUENTIAL, buildMode,
                changedFiles);

        // test case: test flow path points to non existing executable
        // validate execution does not return when detects this situation and coverage data is added to results
        assertEquals(1, buildResults.getRunTestsResults().getUncoveredExecutables().size());
    }

    @Test
    public void testProcessRunTestsParallel() {
        final Map<String, SlangTestCase> testCases = new LinkedHashMap<>();
        final SlangTestCase testCase1 = new SlangTestCase("test1", "testFlowPath", "desc", asList("abc", "new"),
                "mock", null, null, false, "SUCCESS");
        final SlangTestCase testCase2 = new SlangTestCase("test2", "testFlowPath", "desc", asList("efg", "new"),
                "mock", null, null, false, "SUCCESS");
        final SlangTestCase testCase3 = new SlangTestCase("test3", "testFlowPath", "desc", asList("new", "new2"),
                "mock", null, null, false, "SUCCESS");
        final SlangTestCase testCase4 = new SlangTestCase("test4", "testFlowPath", "desc", asList("jjj", "new2"),
                "mock", null, null, false, "SUCCESS");
        final SlangTestCase testCase5 = new SlangTestCase("test5", "testFlowPath", "desc",
                asList("hhh", "jjj", "abc"), "mock", null, null, false, "SUCCESS");

        testCases.put("test1", testCase1);
        testCases.put("test2", testCase2);
        testCases.put("test3", testCase3);
        testCases.put("test4", testCase4);
        testCases.put("test5", testCase5);

        final List<String> testSuites = newArrayList("abc");
        final Map<String, CompilationArtifact> compiledFlows = new HashMap<>();
        final String projectPath = "aaa";

        final AtomicReference<IRunTestResults> capturedArgument = new AtomicReference<>();
        doAnswer(getAnswer(capturedArgument)).when(slangTestRunner).splitTestCasesByRunState(any(BulkRunMode.class),
                anyMap(), anyList(), any(IRunTestResults.class), any(BuildModeConfig.class));
        doNothing().when(slangTestRunner).runTestsParallel(anyString(), anyMap(), anyMap(),
                any(ThreadSafeRunTestResults.class));

        // Tested call
        slangBuilder.processRunTests(projectPath, testSuites, ALL_PARALLEL, compiledFlows, testCases,
                buildModeConfig);

        InOrder inOrder = Mockito.inOrder(slangTestRunner);
        inOrder.verify(slangTestRunner).splitTestCasesByRunState(eq(ALL_PARALLEL), eq(testCases), eq(testSuites),
                isA(ThreadSafeRunTestResults.class), any(BuildModeConfig.class));
        inOrder.verify(slangTestRunner).runTestsParallel(eq(projectPath), anyMap(), eq(compiledFlows),
                eq((ThreadSafeRunTestResults) capturedArgument.get()));
        verifyNoMoreInteractions(slangTestRunner);
        verify(slangTestRunner, never()).runTestsSequential(anyString(), anyMap(), anyMap(),
                any(IRunTestResults.class));
    }

    @Test
    public void testProcessRunTestsSequential() {
        final Map<String, SlangTestCase> testCases = new LinkedHashMap<>();
        final SlangTestCase testCase1 = new SlangTestCase("test1", "testFlowPath", "desc", asList("abc", "new"),
                "mock", null, null, false, "SUCCESS");
        final SlangTestCase testCase2 = new SlangTestCase("test2", "testFlowPath", "desc", asList("efg", "new"),
                "mock", null, null, false, "SUCCESS");
        final SlangTestCase testCase3 = new SlangTestCase("test3", "testFlowPath", "desc", asList("new", "new2"),
                "mock", null, null, false, "SUCCESS");

        testCases.put("test1", testCase1);
        testCases.put("test2", testCase2);
        testCases.put("test3", testCase3);

        final List<String> testSuites = newArrayList("abc");
        final Map<String, CompilationArtifact> compiledFlows = new HashMap<>();
        final String projectPath = "aaa";

        final AtomicReference<IRunTestResults> theCapturedArgument = new AtomicReference<>();
        doAnswer(getAnswer(theCapturedArgument)).when(slangTestRunner).splitTestCasesByRunState(
                any(BulkRunMode.class), anyMap(), anyList(), any(IRunTestResults.class),
                any(BuildModeConfig.class));
        doNothing().when(slangTestRunner).runTestsSequential(anyString(), anyMap(), anyMap(),
                any(IRunTestResults.class));

        BuildModeConfig basic = BuildModeConfig.createBasicBuildModeConfig();
        // Tested call
        slangBuilder.processRunTests(projectPath, testSuites, ALL_SEQUENTIAL, compiledFlows, testCases, basic);

        InOrder inOrder = Mockito.inOrder(slangTestRunner);
        inOrder.verify(slangTestRunner).splitTestCasesByRunState(eq(ALL_SEQUENTIAL), eq(testCases), eq(testSuites),
                isA(RunTestsResults.class), eq(basic));
        inOrder.verify(slangTestRunner).runTestsSequential(eq(projectPath), anyMap(), eq(compiledFlows),
                eq((RunTestsResults) theCapturedArgument.get()));
        inOrder.verify(slangTestRunner, never()).runTestsParallel(anyString(), anyMap(), anyMap(),
                any(ThreadSafeRunTestResults.class));
        verifyNoMoreInteractions(slangTestRunner);
    }

    @Test
    public void testProcessRunTestsMixed() {
        final Map<String, SlangTestCase> testCases = new LinkedHashMap<>();
        final SlangTestCase testCase1 = new SlangTestCase("test1", "testFlowPath", "desc", asList("abc", "new"),
                "mock", null, null, false, "SUCCESS");
        final SlangTestCase testCase2 = new SlangTestCase("test2", "testFlowPath", "desc", asList("efg", "new"),
                "mock", null, null, false, "SUCCESS");
        final SlangTestCase testCase3 = new SlangTestCase("test3", "testFlowPath", "desc", asList("new", "new2"),
                "mock", null, null, false, "SUCCESS");
        final SlangTestCase testCase4 = new SlangTestCase("test4", "testFlowPath", "desc", asList("new", "new2"),
                "mock", null, null, false, "SUCCESS");

        testCases.put("test1", testCase1);
        testCases.put("test2", testCase2);
        testCases.put("test3", testCase3);
        testCases.put("test4", testCase4);

        final List<String> testSuites = newArrayList("new");
        final Map<String, CompilationArtifact> compiledFlows = new HashMap<>();
        final String projectPath = "aaa";

        final AtomicReference<ThreadSafeRunTestResults> theCapturedArgument = new AtomicReference<>();
        final AtomicReference<Map<String, SlangTestCase>> capturedTestsSeq = new AtomicReference<>();
        final AtomicReference<Map<String, SlangTestCase>> capturedTestsPar = new AtomicReference<>();

        doCallRealMethod().when(slangTestRunner).isTestCaseInActiveSuite(any(SlangTestCase.class), anyList());
        doReturn(SlangBuildMain.TestCaseRunMode.SEQUENTIAL).doReturn(SlangBuildMain.TestCaseRunMode.PARALLEL)
                .doReturn(SlangBuildMain.TestCaseRunMode.PARALLEL)
                .doReturn(SlangBuildMain.TestCaseRunMode.SEQUENTIAL).when(testRunInfoService)
                .getRunModeForTestCase(any(SlangTestCase.class), any(ConflictResolutionStrategy.class),
                        any(DefaultResolutionStrategy.class));

        doAnswer(new Answer() {
            @Override
            public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
                Object[] arguments = invocationOnMock.getArguments();
                Object argument = arguments[arguments.length - 2];
                theCapturedArgument.set((ThreadSafeRunTestResults) argument);

                return invocationOnMock.callRealMethod();
            }
        }).when(slangTestRunner).splitTestCasesByRunState(any(BulkRunMode.class), anyMap(), anyList(),
                any(IRunTestResults.class), eq(buildModeConfig));

        doAnswer(new Answer() {
            @Override
            @SuppressWarnings("unchecked")
            public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
                Object[] arguments = invocationOnMock.getArguments();
                capturedTestsSeq.set((Map<String, SlangTestCase>) arguments[1]);

                return null;
            }
        }).when(slangTestRunner).runTestsSequential(anyString(), anyMap(), anyMap(), any(IRunTestResults.class));
        doAnswer(new Answer() {
            @Override
            @SuppressWarnings("unchecked")
            public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
                Object[] arguments = invocationOnMock.getArguments();
                capturedTestsPar.set((Map<String, SlangTestCase>) arguments[1]);

                return null;
            }
        }).when(slangTestRunner).runTestsParallel(anyString(), anyMap(), anyMap(),
                any(ThreadSafeRunTestResults.class));

        // Tested call
        slangBuilder.processRunTests(projectPath, testSuites, POSSIBLY_MIXED, compiledFlows, testCases,
                buildModeConfig);

        InOrder inOrder = inOrder(slangTestRunner);
        inOrder.verify(slangTestRunner).splitTestCasesByRunState(eq(POSSIBLY_MIXED), eq(testCases), eq(testSuites),
                isA(ThreadSafeRunTestResults.class), eq(buildModeConfig));
        inOrder.verify(slangTestRunner).runTestsSequential(eq(projectPath), anyMap(), eq(compiledFlows),
                eq(theCapturedArgument.get()));
        inOrder.verify(slangTestRunner).runTestsParallel(eq(projectPath), anyMap(), eq(compiledFlows),
                eq(theCapturedArgument.get()));

        final List<SlangTestCase> listSeq = newArrayList(capturedTestsSeq.get().values());
        final List<SlangTestCase> listPar = newArrayList(capturedTestsPar.get().values());
        assertEquals(0, ListUtils.intersection(listSeq, listPar).size()); // assures that a test is run only once
        assertEquals(newHashSet(testCases.values()), newHashSet(ListUtils.union(listSeq, listPar)));
    }

    private Answer getAnswer(final AtomicReference<IRunTestResults> theCapturedArgument) {
        return new Answer() {
            @Override
            public Map<TestCaseRunState, Map<String, SlangTestCase>> answer(InvocationOnMock invocationOnMock)
                    throws Throwable {
                Object[] arguments = invocationOnMock.getArguments();
                Object argument = arguments[arguments.length - 2];
                if (argument instanceof ThreadSafeRunTestResults) {
                    theCapturedArgument.set((ThreadSafeRunTestResults) argument);
                } else if (argument instanceof RunTestsResults) {
                    theCapturedArgument.set((RunTestsResults) argument);
                }

                return new LinkedHashMap<>();
            }
        };
    }

    @Configuration
    static class Config {

        @Bean
        public SlangCompiler slangCompiler() {
            return mock(SlangCompiler.class);
        }

        @Bean
        public ScoreCompiler scoreCompiler() {
            return mock(ScoreCompiler.class);
        }

        @Bean
        public SlangBuilder slangBuild() {
            return new SlangBuilder();
        }

        @Bean
        public SlangContentVerifier slangContentVerifier() {
            return new SlangContentVerifier();
        }

        @Bean
        public SlangTestRunner slangTestRunner() {
            return mock(SlangTestRunner.class);
        }

        @Bean
        public TestCasesYamlParser testCasesYamlParser() {
            return mock(TestCasesYamlParser.class);
        }

        @Bean
        public Yaml yaml() {
            return mock(Yaml.class);
        }

        @Bean
        public Slang slang() {
            return mock(Slang.class);
        }

        @Bean
        public ParallelTestCaseExecutorService parallelTestCaseExecutorService() {
            return mock(ParallelTestCaseExecutorService.class);
        }

        @Bean
        public TestCaseEventDispatchService testCaseEventDispatchService() {
            return mock(TestCaseEventDispatchService.class);
        }

        @Bean
        public SlangSourceService slangSourceService() {
            return mock(SlangSourceService.class);
        }

        @Bean
        public MetadataExtractor metadataExtractor() {
            return mock(MetadataExtractor.class);
        }

        @Bean
        public StaticValidator staticValidator() {
            return spy(StaticValidatorImpl.class);
        }

        @Bean
        public TestRunInfoService test() {
            return spy(TestRunInfoServiceImpl.class);
        }

        @Bean
        public LoggingService loggingService() {
            return new LoggingServiceImpl();
        }

        @Bean
        public SlangCompilationService slangCompilationService() {
            return new SlangCompilationServiceImpl();
        }

        @Bean
        public LoggingSlangTestCaseEventListener loggingSlangTestCaseEventListener() {
            return new LoggingSlangTestCaseEventListener();
        }

        ////////////////////// Context for DependenciesHelper ////////////////////////////
        @Bean
        public DependenciesHelper dependenciesHelper() {
            return mock(DependenciesHelper.class);
        }

        @Bean
        public PublishTransformer publishTransformer() {
            return mock(PublishTransformer.class);
        }

        @Bean
        public TransformersHandler transformersHandler() {
            return mock(TransformersHandler.class);
        }

        @Bean
        public PreCompileValidator preCompileValidator() {
            return new PreCompileValidatorImpl();
        }

        @Bean
        public ResultsTransformer resultsTransformer() {
            return mock(ResultsTransformer.class);
        }

        @Bean
        public ExecutableValidator executableValidator() {
            return new ExecutableValidatorImpl();
        }

        @Bean
        public SystemPropertyValidator systemPropertyValidator() {
            return new SystemPropertyValidatorImpl();
        }

    }
}