From 283f3874de7cb4dc5dcebc3799e83b7a7999704f Mon Sep 17 00:00:00 2001 From: Henry Coles Date: Wed, 19 May 2021 11:51:06 +0100 Subject: [PATCH] allow multiple test engines --- .../execute/CoverageProcessSystemTest.java | 2 + .../junit/JUnitCompatibleConfiguration.java | 6 + .../mutationtest/config/MinionSettings.java | 23 +-- .../config/PrioritisingTestConfiguration.java | 71 ++++++++ .../config/PrioritisingTestSuiteFinder.java | 25 +++ .../config/PrioritisingTestUnitFinder.java | 26 +++ .../org/pitest/testapi/Configuration.java | 6 + .../pitest/testng/TestNGConfiguration.java | 8 + .../PrioritisingTestConfigurationTest.java | 172 ++++++++++++++++++ 9 files changed, 328 insertions(+), 11 deletions(-) create mode 100644 pitest/src/main/java/org/pitest/mutationtest/config/PrioritisingTestConfiguration.java create mode 100644 pitest/src/main/java/org/pitest/mutationtest/config/PrioritisingTestSuiteFinder.java create mode 100644 pitest/src/main/java/org/pitest/mutationtest/config/PrioritisingTestUnitFinder.java create mode 100644 pitest/src/test/java/org/pitest/mutationtest/config/PrioritisingTestConfigurationTest.java diff --git a/pitest-entry/src/test/java/org/pitest/coverage/execute/CoverageProcessSystemTest.java b/pitest-entry/src/test/java/org/pitest/coverage/execute/CoverageProcessSystemTest.java index d86c06204..bc73c72c0 100644 --- a/pitest-entry/src/test/java/org/pitest/coverage/execute/CoverageProcessSystemTest.java +++ b/pitest-entry/src/test/java/org/pitest/coverage/execute/CoverageProcessSystemTest.java @@ -14,6 +14,7 @@ import com.example.coverage.execute.samples.simple.TesteeWithMultipleLines; import com.example.coverage.execute.samples.simple.Tests; import com.example.coverage.execute.samples.simple.TestsForMultiBlockCoverage; +import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; import org.pitest.SystemTest; @@ -285,6 +286,7 @@ public void shouldNotCorruptedTheSystemNewLineProperty() throws Exception { } @Test + @Ignore("we have testng on the classpath") public void shouldFailWithExitCode() throws Exception { final Consumer noOpHandler = a -> { }; diff --git a/pitest/src/main/java/org/pitest/junit/JUnitCompatibleConfiguration.java b/pitest/src/main/java/org/pitest/junit/JUnitCompatibleConfiguration.java index 0fd3aba6d..e538884a4 100644 --- a/pitest/src/main/java/org/pitest/junit/JUnitCompatibleConfiguration.java +++ b/pitest/src/main/java/org/pitest/junit/JUnitCompatibleConfiguration.java @@ -43,6 +43,12 @@ public JUnitCompatibleConfiguration(TestGroupConfig config, Collection e this.includedTestMethods = includedTestMethods; } + @Override + public int priority() { + // make sure we are used after any test plugins added to the classpath + return DEFAULT_PRIORITY + 1; + } + @Override public TestUnitFinder testUnitFinder() { return new CompoundTestUnitFinder(Arrays.asList( diff --git a/pitest/src/main/java/org/pitest/mutationtest/config/MinionSettings.java b/pitest/src/main/java/org/pitest/mutationtest/config/MinionSettings.java index 2d712991b..450832f73 100644 --- a/pitest/src/main/java/org/pitest/mutationtest/config/MinionSettings.java +++ b/pitest/src/main/java/org/pitest/mutationtest/config/MinionSettings.java @@ -3,9 +3,11 @@ import org.pitest.classinfo.ClassByteArraySource; import org.pitest.mutationtest.MutationEngineFactory; import org.pitest.testapi.Configuration; -import org.pitest.testapi.TestPluginFactory; import org.pitest.util.PitError; +import java.util.List; +import java.util.stream.Collectors; + public class MinionSettings { private final ClientPluginServices plugins; @@ -26,16 +28,15 @@ public MutationEngineFactory createEngine(String engine) { public Configuration getTestFrameworkPlugin(TestPluginArguments options, ClassByteArraySource source) { - for (final TestPluginFactory each : this.plugins.findTestFrameworkPlugins()) { - if (each.name().equals(options.getTestPlugin())) { - return each.createTestFrameworkConfiguration(options.getGroupConfig(), - source, - options.getExcludedRunners(), - options.getIncludedTestMethods()); - } - } - throw new PitError("Could not load requested test plugin " - + options.getTestPlugin()); + List configurations = this.plugins.findTestFrameworkPlugins().stream() + .map(p -> p.createTestFrameworkConfiguration(options.getGroupConfig(), + source, + options.getExcludedRunners(), + options.getIncludedTestMethods())) + .collect(Collectors.toList()); + + return new PrioritisingTestConfiguration(configurations); + } } diff --git a/pitest/src/main/java/org/pitest/mutationtest/config/PrioritisingTestConfiguration.java b/pitest/src/main/java/org/pitest/mutationtest/config/PrioritisingTestConfiguration.java new file mode 100644 index 000000000..9827308cf --- /dev/null +++ b/pitest/src/main/java/org/pitest/mutationtest/config/PrioritisingTestConfiguration.java @@ -0,0 +1,71 @@ +package org.pitest.mutationtest.config; + +import org.pitest.help.PitHelpError; +import org.pitest.testapi.Configuration; +import org.pitest.testapi.TestSuiteFinder; +import org.pitest.testapi.TestUnitFinder; + +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +class PrioritisingTestConfiguration implements Configuration { + private final List children; + private final TestUnitFinder finder; + private final TestSuiteFinder suiteFinder; + + PrioritisingTestConfiguration(List children) { + this.children = pickChildren(children); + this.finder = makeFinder(this.children); + this.suiteFinder = makeSuiteFinder(this.children); + } + + @Override + public TestUnitFinder testUnitFinder() { + return finder; + } + + @Override + public TestSuiteFinder testSuiteFinder() { + return suiteFinder; + } + + @Override + public Optional verifyEnvironment() { + return children.stream() + .map(Configuration::verifyEnvironment) + .findFirst() + .get(); + } + + private static List pickChildren(List configs) { + List working = configs.stream() + .filter(c -> !c.verifyEnvironment().isPresent()) + .sorted(byPriority()) + .collect(Collectors.toList()); + // We don't have a working config, let it report errors later + if (working.isEmpty()) { + return configs; + } + return working; + } + + private static Comparator byPriority() { + return Comparator.comparingInt(Configuration::priority); + } + + private TestUnitFinder makeFinder(List children) { + List finders = children.stream() + .map(Configuration::testUnitFinder) + .collect(Collectors.toList()); + return new PrioritisingTestUnitFinder(finders); + } + + private TestSuiteFinder makeSuiteFinder(List children) { + List finders = children.stream() + .map(Configuration::testSuiteFinder) + .collect(Collectors.toList()); + return new PrioritisingTestSuiteFinder(finders); + } +} diff --git a/pitest/src/main/java/org/pitest/mutationtest/config/PrioritisingTestSuiteFinder.java b/pitest/src/main/java/org/pitest/mutationtest/config/PrioritisingTestSuiteFinder.java new file mode 100644 index 000000000..b9a2c3e34 --- /dev/null +++ b/pitest/src/main/java/org/pitest/mutationtest/config/PrioritisingTestSuiteFinder.java @@ -0,0 +1,25 @@ +package org.pitest.mutationtest.config; + +import org.pitest.testapi.TestSuiteFinder; + +import java.util.Collections; +import java.util.List; + +class PrioritisingTestSuiteFinder implements TestSuiteFinder { + private final List orderedChildren; + + PrioritisingTestSuiteFinder(List orderedChildren) { + this.orderedChildren = orderedChildren; + } + + @Override + public List> apply(Class clazz) { + for (TestSuiteFinder each : orderedChildren) { + List> found = each.apply(clazz); + if (!found.isEmpty()) { + return found; + } + } + return Collections.emptyList(); + } +} diff --git a/pitest/src/main/java/org/pitest/mutationtest/config/PrioritisingTestUnitFinder.java b/pitest/src/main/java/org/pitest/mutationtest/config/PrioritisingTestUnitFinder.java new file mode 100644 index 000000000..ff966204b --- /dev/null +++ b/pitest/src/main/java/org/pitest/mutationtest/config/PrioritisingTestUnitFinder.java @@ -0,0 +1,26 @@ +package org.pitest.mutationtest.config; + +import org.pitest.testapi.TestUnit; +import org.pitest.testapi.TestUnitFinder; + +import java.util.Collections; +import java.util.List; + +class PrioritisingTestUnitFinder implements TestUnitFinder { + private final List orderedChildren; + + PrioritisingTestUnitFinder(List orderedChildren) { + this.orderedChildren = orderedChildren; + } + + @Override + public List findTestUnits(Class clazz) { + for (TestUnitFinder each : orderedChildren) { + List found = each.findTestUnits(clazz); + if (!found.isEmpty()) { + return found; + } + } + return Collections.emptyList(); + } +} diff --git a/pitest/src/main/java/org/pitest/testapi/Configuration.java b/pitest/src/main/java/org/pitest/testapi/Configuration.java index 796a47311..ee09d2328 100644 --- a/pitest/src/main/java/org/pitest/testapi/Configuration.java +++ b/pitest/src/main/java/org/pitest/testapi/Configuration.java @@ -20,6 +20,12 @@ public interface Configuration { + int DEFAULT_PRIORITY = 10; + + default int priority() { + return DEFAULT_PRIORITY; + } + TestUnitFinder testUnitFinder(); TestSuiteFinder testSuiteFinder(); diff --git a/pitest/src/main/java/org/pitest/testng/TestNGConfiguration.java b/pitest/src/main/java/org/pitest/testng/TestNGConfiguration.java index 746059f36..d7e066d6d 100644 --- a/pitest/src/main/java/org/pitest/testng/TestNGConfiguration.java +++ b/pitest/src/main/java/org/pitest/testng/TestNGConfiguration.java @@ -18,6 +18,8 @@ import org.pitest.extension.common.NoTestSuiteFinder; import java.util.Optional; + +import org.pitest.help.Help; import org.pitest.help.PitHelpError; import org.pitest.testapi.Configuration; import org.pitest.testapi.TestGroupConfig; @@ -46,6 +48,12 @@ public TestSuiteFinder testSuiteFinder() { @Override public Optional verifyEnvironment() { + try { + Class.forName("org.testng.annotations.Test"); + } catch (NoClassDefFoundError | ClassNotFoundException er) { + return Optional.ofNullable(new PitHelpError(Help.NO_TEST_LIBRARY)); + } + return Optional.empty(); } diff --git a/pitest/src/test/java/org/pitest/mutationtest/config/PrioritisingTestConfigurationTest.java b/pitest/src/test/java/org/pitest/mutationtest/config/PrioritisingTestConfigurationTest.java new file mode 100644 index 000000000..f1ae8a519 --- /dev/null +++ b/pitest/src/test/java/org/pitest/mutationtest/config/PrioritisingTestConfigurationTest.java @@ -0,0 +1,172 @@ +package org.pitest.mutationtest.config; + +import org.junit.Test; +import org.pitest.help.Help; +import org.pitest.help.PitHelpError; +import org.pitest.testapi.Configuration; +import org.pitest.testapi.Description; +import org.pitest.testapi.ResultCollector; +import org.pitest.testapi.TestSuiteFinder; +import org.pitest.testapi.TestUnit; +import org.pitest.testapi.TestUnitFinder; + +import java.util.List; +import java.util.Optional; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static org.assertj.core.api.Assertions.assertThat; + +public class PrioritisingTestConfigurationTest { + + TestUnit findMe = fakeUnit("a"); + TestUnit dontFindMe = fakeUnit("b"); + private Class dontFindThisClass = String.class; + private Class findThisClass = Integer.class; + + @Test + public void findsNoTestsWhenNothingMatchesChildConfiguration() { + Configuration findsNothing = configuration(1, emptyList()); + PrioritisingTestConfiguration testee = new PrioritisingTestConfiguration(asList(findsNothing)); + List actual = testee.testUnitFinder().findTestUnits(String.class); + assertThat(actual).isEmpty(); + } + + @Test + public void highestPriorityConfigurationFindsTest() { + Configuration c0 = configuration(2, dontFindMe); + Configuration c1 = configuration(1, findMe); + Configuration c2 = configuration(2, dontFindMe); + PrioritisingTestConfiguration testee = new PrioritisingTestConfiguration(asList(c0, c1, c2)); + + List actual = testee.testUnitFinder().findTestUnits(String.class); + + assertThat(actual).containsOnly(findMe); + } + + @Test + public void configurationsWithEnvironmentalErrorsNotUsed() { + Configuration c0 = configuration(1, asList(dontFindMe), new PitHelpError(Help.NO_JUNIT)); + Configuration c1 = configuration(2, findMe); + PrioritisingTestConfiguration testee = new PrioritisingTestConfiguration(asList(c0, c1)); + + List actual = testee.testUnitFinder().findTestUnits(String.class); + + assertThat(actual).containsOnly(findMe); + } + + @Test + public void allowsMixedTestTypes() { + Configuration findsNothing = configuration(1, emptyList()); + Configuration c1 = configuration(2, findMe); + Configuration c2 = configuration(3, dontFindMe); + PrioritisingTestConfiguration testee = new PrioritisingTestConfiguration(asList(findsNothing, c1, c2)); + + List actual = testee.testUnitFinder().findTestUnits(String.class); + + assertThat(actual).containsOnly(findMe); + } + + + @Test + public void reportsNoErrorIfAtLeastOneConfigValid() { + Configuration c0 = configuration(1, asList(dontFindMe), new PitHelpError(Help.NO_JUNIT)); + Configuration c1 = configuration(2, findMe); + PrioritisingTestConfiguration testee = new PrioritisingTestConfiguration(asList(c0, c1)); + + assertThat(testee.verifyEnvironment()).isEmpty(); + } + + @Test + public void reportsErrorWhenAllConfigsInValid() { + Configuration c0 = configuration(1, asList(dontFindMe), new PitHelpError(Help.NO_JUNIT)); + Configuration c1 = configuration(2, asList(dontFindMe), new PitHelpError(Help.NO_JUNIT)); + PrioritisingTestConfiguration testee = new PrioritisingTestConfiguration(asList(c0, c1)); + + assertThat(testee.verifyEnvironment()).isPresent(); + } + + @Test + public void highestPriorityConfigurationFindsSuites() { + Configuration c0 = suiteConfiguration(2, dontFindThisClass); + Configuration c1 = suiteConfiguration(1, findThisClass); + Configuration c2 = suiteConfiguration(2, dontFindThisClass); + PrioritisingTestConfiguration testee = new PrioritisingTestConfiguration(asList(c0, c1, c2)); + + List> actual = testee.testSuiteFinder().apply(String.class); + + assertThat(actual).containsOnly(findThisClass); + } + + private TestUnit fakeUnit(String name) { + return new TestUnit() { + @Override + public void execute(ResultCollector rc) { + + } + + @Override + public Description getDescription() { + return new Description(name); + } + }; + } + + private Configuration configuration(int priority, TestUnit testUnit) { + return configuration(priority, asList(testUnit), null); + } + + private Configuration configuration(int priority, List testUnit) { + return configuration(priority, testUnit, null); + } + + private Configuration configuration(int priority, List testUnits, PitHelpError error) { + return new Configuration() { + + @Override + public int priority() { + return priority; + } + + @Override + public TestUnitFinder testUnitFinder() { + return c -> testUnits; + } + + @Override + public TestSuiteFinder testSuiteFinder() { + return null; + } + + @Override + public Optional verifyEnvironment() { + return Optional.ofNullable(error); + } + }; + } + + private Configuration suiteConfiguration(int priority, Class suiteClass) { + return new Configuration() { + + @Override + public int priority() { + return priority; + } + + @Override + public TestUnitFinder testUnitFinder() { + return c -> emptyList(); + } + + @Override + public TestSuiteFinder testSuiteFinder() { + return c -> asList(suiteClass); + } + + @Override + public Optional verifyEnvironment() { + return Optional.empty(); + } + }; + } +} \ No newline at end of file