/******************************************************************************* * (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, 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, 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, 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, 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, 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, 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, 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, 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, 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(); } } }