diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..b9e31c74c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,41 @@ +name: CI +on: + push: + branches: + - 'master' + pull_request: + branches-ignore: + - 'release' +jobs: + supported-jdk: + name: ${{ matrix.title }} + continue-on-error: false + strategy: + fail-fast: false + matrix: + include: + - title: "JDK 8" + java: 8 + - title: "JDK 11" + java: 11 + - title: "JDK 15" + java: 15 + runs-on: ubuntu-latest + steps: + - name: 'Checkout from Git' + uses: actions/checkout@v2 + - name: 'Set up JDK ${{ matrix.java }}' + uses: actions/setup-java@v1 + with: + java-version: ${{ matrix.java }} + - name: 'Display JDK version' + run: java -version + - name: Cache local Maven repository + uses: actions/cache@v2 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-maven- + - name: 'Test' + # install rather than verify to ensure correct version used during integration test + run: mvn -B install diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml new file mode 100644 index 000000000..8b332ea03 --- /dev/null +++ b/.github/workflows/snapshot.yml @@ -0,0 +1,30 @@ +name: Deploy snapshot +on: + push: + branches: + - master +jobs: + snapshot: + runs-on: ubuntu-latest + steps: + - name: Checkout project + uses: actions/checkout@v2 + - name: Cache local Maven repository + uses: actions/cache@v2 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-maven- + - name: Setup Java JDK + uses: actions/setup-java@v1.4.3 + with: + java-version: 8 + server-id: ossrh + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + - name: Publish JARs + run: mvn -B deploy -DskipTests=true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} diff --git a/.gitignore b/.gitignore index b0345ac09..e0d2b0856 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ target/ dependency-reduced-pom.xml .flattened-pom.xml */bin +.DS_Store + diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 5a38e0268..000000000 --- a/.travis.yml +++ /dev/null @@ -1,48 +0,0 @@ -sudo: false - -language: java - -# Get latest install-jdk.sh script -before_install: - - wget https://github.com/sormuras/bach/raw/master/install-jdk.sh - -env: - global: - # taken first download mirror from http://maven.apache.org/ - - BASEURL=http://mirror.softaculous.com/apache/maven/maven-3/VERSION/binaries/apache-maven-VERSION-bin.tar.gz - - FILE=apache-maven-VERSION-bin.tar.gz - - DIR=apache-maven-VERSION/bin - -matrix: - include: - - jdk: openjdk8 - env: MAVEN=3.2.5 - - - jdk: openjdk9 - env: MAVEN=3.5.4 - - - jdk: openjdk10 - env: MAVEN=3.5.4 - - - jdk: openjdk11 - env: MAVEN=3.5.4 - - - jdk: openjdk12 - env: MAVEN=3.5.4 - - - jdk: openjdk13 - env: MAVEN=3.6.3 - - - env: JDK=14 MAVEN=3.6.3 - install: . ./install-jdk.sh --feature 14 - - - env: JDK='OpenJ9 11' MAVEN=3.5.4 - install: . ./install-jdk.sh --url "https://api.adoptopenjdk.net/v2/binary/releases/openjdk11?openjdk_impl=openj9&os=linux&arch=x64&release=latest&type=jdk&heap_size=normal" - -script: - - wget $(echo -n $BASEURL | sed -e 's#VERSION#'$MAVEN'#g') - - tar -xvzf $(echo -n $FILE | sed -e 's#VERSION#'$MAVEN'#') - - export PATH=$(echo -n $DIR | sed -e 's#VERSION#'$MAVEN'#'):$PATH - - java -version - - mvn -version - - mvn clean install diff --git a/README.md b/README.md index ba1d24ebd..5296543cd 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.pitest/pitest/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/org.pitest/pitest) -[![Build Status](https://travis-ci.org/hcoles/pitest.png?branch=master)](https://travis-ci.org/hcoles/pitest) +![Build Statis](https://github.com/hcoles/pitest/workflows/CI/badge.svg?branch=master) [![Build Status](https://dev.azure.com/henrycoles/pitest/_apis/build/status/hcoles.pitest?branchName=master)](https://dev.azure.com/henrycoles/pitest/_build/latest?definitionId=3&branchName=master) Pitest (aka PIT) is a state of the art mutation testing system for Java and the JVM. @@ -8,6 +8,14 @@ Read all about it at http://pitest.org ## Releases +### 1.6.1 + +* Automate release to maven central +* #774 Test strength statistic (thanks @alex859) +* #798 Enable jvm args to be passed from command line (thanks @yfrolov) +* #797 Add line coverage to console (thanks @qxo) +* #822 Mitigate NPE on accidental dependency (thanks @szpak) + ### 1.5.2 * #749 ANT support for the fullMutationMatrix option (thanks @ayaankazerouni) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c17310a53..faaf359ee 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -3,8 +3,6 @@ trigger: strategy: matrix: - linux: - imageName: 'Ubuntu-16.04' mac: imageName: 'macos-10.14' windows: diff --git a/pitest-build-config/src/main/resources/pitest/checkstyle.xml b/pitest-build-config/src/main/resources/pitest/checkstyle.xml index f174b5a15..9effbb976 100644 --- a/pitest-build-config/src/main/resources/pitest/checkstyle.xml +++ b/pitest-build-config/src/main/resources/pitest/checkstyle.xml @@ -45,6 +45,7 @@ + diff --git a/pitest-entry/pom.xml b/pitest-entry/pom.xml index 126e499a9..d7c644659 100644 --- a/pitest-entry/pom.xml +++ b/pitest-entry/pom.xml @@ -143,7 +143,7 @@ com.thoughtworks.xstream xstream - 1.4.8 + 1.4.15 test diff --git a/pitest-entry/src/main/java/org/pitest/mutationtest/ListenerArguments.java b/pitest-entry/src/main/java/org/pitest/mutationtest/ListenerArguments.java index 8b4f0a44f..146974933 100644 --- a/pitest-entry/src/main/java/org/pitest/mutationtest/ListenerArguments.java +++ b/pitest-entry/src/main/java/org/pitest/mutationtest/ListenerArguments.java @@ -1,9 +1,13 @@ package org.pitest.mutationtest; import org.pitest.coverage.CoverageDatabase; +import org.pitest.mutationtest.config.ReportOptions; import org.pitest.mutationtest.engine.MutationEngine; +import org.pitest.plugin.FeatureSetting; import org.pitest.util.ResultOutputStrategy; +import java.util.Optional; + /** * Data passed to the listener MutationResultListener factories for use when * constructing listeners. @@ -16,16 +20,35 @@ public class ListenerArguments { private final SourceLocator locator; private final MutationEngine engine; private final boolean fullMutationMatrix; + private final ReportOptions data; + private final FeatureSetting setting; + + public ListenerArguments(ResultOutputStrategy outputStrategy, + CoverageDatabase coverage, + SourceLocator locator, + MutationEngine engine, + long startTime, + boolean fullMutationMatrix, + ReportOptions data) { + this(outputStrategy, coverage, locator, engine, startTime, fullMutationMatrix, data, null); + } - public ListenerArguments(final ResultOutputStrategy outputStrategy, - final CoverageDatabase coverage, final SourceLocator locator, - final MutationEngine engine, final long startTime, final boolean fullMutationMatrix) { + ListenerArguments(ResultOutputStrategy outputStrategy, + CoverageDatabase coverage, + SourceLocator locator, + MutationEngine engine, + long startTime, + boolean fullMutationMatrix, + ReportOptions data, + FeatureSetting setting) { this.outputStrategy = outputStrategy; this.coverage = coverage; this.locator = locator; this.startTime = startTime; this.engine = engine; this.fullMutationMatrix = fullMutationMatrix; + this.data = data; + this.setting = setting; } public ResultOutputStrategy getOutputStrategy() { @@ -51,4 +74,24 @@ public MutationEngine getEngine() { public boolean isFullMutationMatrix() { return fullMutationMatrix; } + + public ReportOptions data() { + return data; + } + + public Optional settings() { + return Optional.ofNullable(setting); + } + + public ListenerArguments withSetting(FeatureSetting setting) { + return new ListenerArguments(outputStrategy, + coverage, + locator, + engine, + startTime, + fullMutationMatrix, + data, + setting); + } + } diff --git a/pitest-entry/src/main/java/org/pitest/mutationtest/MutationResultListenerFactory.java b/pitest-entry/src/main/java/org/pitest/mutationtest/MutationResultListenerFactory.java index 4251c4364..9b73aaf48 100644 --- a/pitest-entry/src/main/java/org/pitest/mutationtest/MutationResultListenerFactory.java +++ b/pitest-entry/src/main/java/org/pitest/mutationtest/MutationResultListenerFactory.java @@ -15,14 +15,23 @@ package org.pitest.mutationtest; +import org.pitest.plugin.Feature; +import org.pitest.plugin.ProvidesFeature; +import org.pitest.plugin.ToolClasspathPlugin; + import java.util.Properties; -import org.pitest.plugin.ToolClasspathPlugin; +public interface MutationResultListenerFactory extends ToolClasspathPlugin, ProvidesFeature { -public interface MutationResultListenerFactory extends ToolClasspathPlugin { + Feature LEGACY_MODE = Feature.named("_internal_activate_by_output_string") + .withOnByDefault(true); MutationResultListener getListener(Properties props, ListenerArguments args); String name(); + default Feature provides() { + return LEGACY_MODE; + } + } diff --git a/pitest-entry/src/main/java/org/pitest/mutationtest/build/CompoundInterceptorFactory.java b/pitest-entry/src/main/java/org/pitest/mutationtest/build/CompoundInterceptorFactory.java index 463b2c8bc..6b3755bad 100644 --- a/pitest-entry/src/main/java/org/pitest/mutationtest/build/CompoundInterceptorFactory.java +++ b/pitest-entry/src/main/java/org/pitest/mutationtest/build/CompoundInterceptorFactory.java @@ -1,15 +1,15 @@ package org.pitest.mutationtest.build; -import java.util.Collection; -import java.util.List; -import java.util.function.Function; - import org.pitest.classinfo.ClassByteArraySource; -import org.pitest.functional.FCollection; import org.pitest.mutationtest.config.ReportOptions; import org.pitest.plugin.FeatureSelector; import org.pitest.plugin.FeatureSetting; +import java.util.Collection; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + public class CompoundInterceptorFactory { private final FeatureSelector features; @@ -22,8 +22,9 @@ public CompoundInterceptorFactory(List features, public MutationInterceptor createInterceptor( ReportOptions data, ClassByteArraySource source) { - final List interceptors = FCollection.map(this.features.getActiveFeatures(), - toInterceptor(this.features, data, source)); + final List interceptors = this.features.getActiveFeatures().stream() + .map(toInterceptor(this.features, data, source)) + .collect(Collectors.toList()); return new CompoundMutationInterceptor(interceptors); } diff --git a/pitest-entry/src/main/java/org/pitest/mutationtest/config/CompoundListenerFactory.java b/pitest-entry/src/main/java/org/pitest/mutationtest/config/CompoundListenerFactory.java index dc17d11c7..c0f2f6072 100644 --- a/pitest-entry/src/main/java/org/pitest/mutationtest/config/CompoundListenerFactory.java +++ b/pitest-entry/src/main/java/org/pitest/mutationtest/config/CompoundListenerFactory.java @@ -14,33 +14,35 @@ */ package org.pitest.mutationtest.config; -import java.util.Properties; -import java.util.function.Function; - -import org.pitest.functional.FCollection; import org.pitest.mutationtest.ListenerArguments; import org.pitest.mutationtest.MutationResultListener; import org.pitest.mutationtest.MutationResultListenerFactory; +import org.pitest.plugin.Feature; +import org.pitest.plugin.FeatureSelector; +import org.pitest.plugin.FeatureSetting; + +import java.util.Collection; +import java.util.List; +import java.util.Properties; +import java.util.function.Function; +import java.util.stream.Collectors; public class CompoundListenerFactory implements MutationResultListenerFactory { - private final Iterable children; + private final FeatureSelector features; - public CompoundListenerFactory( - final Iterable children) { - this.children = children; + public CompoundListenerFactory(List features, final Collection children) { + this.features = new FeatureSelector<>(features, children); } @Override public MutationResultListener getListener(final Properties props, final ListenerArguments args) { - return new CompoundTestListener(FCollection.map(this.children, - factoryToListener(props, args))); - } + final List listeners = this.features.getActiveFeatures().stream() + .map(toListener(props, args)) + .collect(Collectors.toList()); - private Function factoryToListener( - final Properties props, final ListenerArguments args) { - return a -> a.getListener(props, args); + return new CompoundTestListener(listeners); } @Override @@ -53,4 +55,13 @@ public String description() { throw new UnsupportedOperationException(); } + @Override + public Feature provides() { + throw new UnsupportedOperationException(); + } + + private Function toListener(Properties props, + ListenerArguments args) { + return a -> a.getListener(props, args.withSetting(features.getSettingForFeature(a.provides().name()))); + } } diff --git a/pitest-entry/src/main/java/org/pitest/mutationtest/config/PluginServices.java b/pitest-entry/src/main/java/org/pitest/mutationtest/config/PluginServices.java index d91b65766..b3febb8d2 100644 --- a/pitest-entry/src/main/java/org/pitest/mutationtest/config/PluginServices.java +++ b/pitest-entry/src/main/java/org/pitest/mutationtest/config/PluginServices.java @@ -1,20 +1,22 @@ package org.pitest.mutationtest.config; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - import org.pitest.mutationtest.MutationEngineFactory; import org.pitest.mutationtest.MutationResultListenerFactory; import org.pitest.mutationtest.build.MutationGrouperFactory; import org.pitest.mutationtest.build.MutationInterceptorFactory; import org.pitest.mutationtest.build.TestPrioritiserFactory; import org.pitest.plugin.ClientClasspathPlugin; +import org.pitest.plugin.ProvidesFeature; import org.pitest.plugin.ToolClasspathPlugin; import org.pitest.testapi.TestPluginFactory; import org.pitest.util.IsolationUtils; import org.pitest.util.ServiceLoader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + public class PluginServices { private final ClassLoader loader; @@ -33,7 +35,7 @@ public static PluginServices makeForContextLoader() { * * @return list of plugins */ - public Iterable findToolClasspathPlugins() { + public Collection findToolClasspathPlugins() { final List l = new ArrayList<>(); l.addAll(findListeners()); l.addAll(findGroupers()); @@ -81,4 +83,11 @@ public Collection findInterceptors() { return ServiceLoader.load(MutationInterceptorFactory.class, this.loader); } + public Collection findFeatures() { + return findToolClasspathPlugins().stream() + .filter(p -> p instanceof ProvidesFeature) + .map(p -> ProvidesFeature.class.cast(p)) + .collect(Collectors.toList()); + } + } diff --git a/pitest-entry/src/main/java/org/pitest/mutationtest/config/SettingsFactory.java b/pitest-entry/src/main/java/org/pitest/mutationtest/config/SettingsFactory.java index 3e86bb554..6454c94f6 100644 --- a/pitest-entry/src/main/java/org/pitest/mutationtest/config/SettingsFactory.java +++ b/pitest-entry/src/main/java/org/pitest/mutationtest/config/SettingsFactory.java @@ -1,14 +1,5 @@ package org.pitest.mutationtest.config; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Comparator; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; - import org.pitest.coverage.CoverageExporter; import org.pitest.coverage.execute.CoverageOptions; import org.pitest.coverage.export.DefaultCoverageExporter; @@ -33,6 +24,17 @@ import org.pitest.util.PitError; import org.pitest.util.ResultOutputStrategy; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + public class SettingsFactory { private final ReportOptions options; @@ -67,7 +69,8 @@ public MutationEngineFactory createEngine() { } public MutationResultListenerFactory createListener() { - return new CompoundListenerFactory(findListeners()); + final FeatureParser parser = new FeatureParser(); + return new CompoundListenerFactory(parser.parseFeatures(this.options.getFeatures()), findListeners()); } public JavaExecutableLocator getJavaExecutable() { @@ -86,7 +89,7 @@ public MutationGrouperFactory getMutationGrouper() { public void describeFeatures(Consumer enabled, Consumer disabled) { final FeatureParser parser = new FeatureParser(); - final Collection available = new ArrayList<>(this.plugins.findInterceptors()); + final Collection available = new ArrayList<>(this.plugins.findFeatures()); final List settings = parser.parseFeatures(this.options.getFeatures()); final FeatureSelector selector = new FeatureSelector<>(settings, available); @@ -104,9 +107,22 @@ public void describeFeatures(Consumer enabled, Consumer disabl .sorted(byName()) .filter(f -> !enabledFeatures.contains(f)) .forEach(disabled); - } + public void checkRequestedFeatures() { + FeatureParser parser = new FeatureParser(); + Set available = this.plugins.findFeatures().stream() + .map(f -> f.provides().name()) + .collect(Collectors.toSet()); + + Optional unknown = parser.parseFeatures(this.options.getFeatures()).stream() + .filter(f -> !available.contains(f.feature())) + .findAny(); + + unknown.ifPresent(setting -> { + throw new IllegalArgumentException("Unknown feature " + setting.feature()); + }); + } public TestPrioritiserFactory getTestPrioritiser() { final Collection testPickers = this.plugins @@ -128,12 +144,7 @@ public CompoundInterceptorFactory getInterceptor() { return new CompoundInterceptorFactory(parser.parseFeatures(this.options.getFeatures()), new ArrayList<>(interceptors)); } - private static Predicate nameMatches( - final Iterable outputFormats) { - return a -> FCollection.contains(outputFormats, equalsIgnoreCase(a.name())); - } - - private Iterable findListeners() { + private Collection findListeners() { final Iterable listeners = this.plugins .findListeners(); final Collection matches = FCollection @@ -145,6 +156,15 @@ private Iterable findListeners() { return matches; } + private static Predicate nameMatches( + final Iterable outputFormats) { + // plugins can be either activated here by name + // or later via the feature mechanism + return a -> FCollection.contains(outputFormats, equalsIgnoreCase(a.name())) + || !a.provides().equals(MutationResultListenerFactory.LEGACY_MODE); + } + + private static Predicate equalsIgnoreCase(final String other) { return a -> a.equalsIgnoreCase(other); } diff --git a/pitest-entry/src/main/java/org/pitest/mutationtest/tooling/EntryPoint.java b/pitest-entry/src/main/java/org/pitest/mutationtest/tooling/EntryPoint.java index fce5e7f31..23ac0c60e 100644 --- a/pitest-entry/src/main/java/org/pitest/mutationtest/tooling/EntryPoint.java +++ b/pitest-entry/src/main/java/org/pitest/mutationtest/tooling/EntryPoint.java @@ -1,10 +1,5 @@ package org.pitest.mutationtest.tooling; -import java.io.File; -import java.io.IOException; -import java.io.Reader; -import java.util.Map; - import org.pitest.classpath.ClassPath; import org.pitest.classpath.ClassPathByteArraySource; import org.pitest.classpath.CodeSource; @@ -12,9 +7,6 @@ import org.pitest.coverage.CoverageGenerator; import org.pitest.coverage.execute.CoverageOptions; import org.pitest.coverage.execute.DefaultCoverageGenerator; -import java.util.Optional; -import java.util.function.Consumer; - import org.pitest.mutationtest.HistoryStore; import org.pitest.mutationtest.MutationResultListenerFactory; import org.pitest.mutationtest.config.PluginServices; @@ -31,6 +23,13 @@ import org.pitest.util.ResultOutputStrategy; import org.pitest.util.Timings; +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.util.Map; +import java.util.Optional; +import java.util.function.Consumer; + public class EntryPoint { /** @@ -72,6 +71,7 @@ public AnalysisResult execute(File baseDir, ReportOptions data, settings.describeFeatures(asInfo("+"), asInfo("-")); Log.getLogger().info("---------------------------------------------------------------------------"); } + settings.checkRequestedFeatures(); checkMatrixMode(data); diff --git a/pitest-entry/src/main/java/org/pitest/mutationtest/tooling/MutationCoverage.java b/pitest-entry/src/main/java/org/pitest/mutationtest/tooling/MutationCoverage.java index 9c136c3db..8edd0ed9b 100644 --- a/pitest-entry/src/main/java/org/pitest/mutationtest/tooling/MutationCoverage.java +++ b/pitest-entry/src/main/java/org/pitest/mutationtest/tooling/MutationCoverage.java @@ -14,18 +14,6 @@ */ package org.pitest.mutationtest.tooling; -import java.io.File; -import java.io.IOException; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - import org.pitest.classinfo.ClassByteArraySource; import org.pitest.classinfo.ClassInfo; import org.pitest.classinfo.ClassName; @@ -66,6 +54,18 @@ import org.pitest.util.StringUtil; import org.pitest.util.Timings; +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + public class MutationCoverage { private static final int MB = 1024 * 1024; @@ -174,7 +174,7 @@ private void checkExcludedRunners() { } } -private int numberOfThreads() { + private int numberOfThreads() { return Math.max(1, this.data.getNumberOfThreads()); } @@ -187,7 +187,7 @@ private List createConfig(final long t0, final ListenerArguments args = new ListenerArguments( this.strategies.output(), coverageData, new SmartSourceLocator( - this.data.getSourceDirs()), engine, t0, this.data.isFullMutationMatrix()); + this.data.getSourceDirs()), engine, t0, this.data.isFullMutationMatrix(), data); final MutationResultListener mutationReportListener = this.strategies .listenerFactory().getListener(this.data.getFreeFormProperties(), args); diff --git a/pitest-entry/src/test/java/com/example/coverage/execute/samples/simple/ParentChildInitializationTest.java b/pitest-entry/src/test/java/com/example/coverage/execute/samples/simple/ParentChildInitializationTest.java new file mode 100644 index 000000000..7de72f79a --- /dev/null +++ b/pitest-entry/src/test/java/com/example/coverage/execute/samples/simple/ParentChildInitializationTest.java @@ -0,0 +1,22 @@ +package com.example.coverage.execute.samples.simple; + +import org.junit.Test; + +public class ParentChildInitializationTest { + @Test + public void test() { + new TesteeChild(); + } +} + +class TesteeParent { + static TesteeChild child = new TesteeChild(); +} + +class TesteeChild extends TesteeParent { + final static Object f = "hello"; + + TesteeChild() { + System.out.println(f); + } +} 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 1fe5a1729..d86c06204 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 @@ -7,6 +7,7 @@ import com.example.coverage.execute.samples.exceptions.ThrowsExceptionFromLargeMethodTestee; import com.example.coverage.execute.samples.exceptions.ThrowsExceptionInFinallyBlockTestee; import com.example.coverage.execute.samples.exceptions.ThrowsExceptionTestee; +import com.example.coverage.execute.samples.simple.ParentChildInitializationTest; import com.example.coverage.execute.samples.simple.Testee; import com.example.coverage.execute.samples.simple.Testee2; import com.example.coverage.execute.samples.simple.TesteeWithComplexConstructorsTest; @@ -57,8 +58,7 @@ public class CoverageProcessSystemTest { private final MethodName foo = MethodName.fromString("foo"); @Test - public void shouldRecordSomeCoverage() throws IOException, - InterruptedException, ExecutionException { + public void shouldRecordSomeCoverage() throws Exception { final List coverage = runCoverageForTest(TestsForMultiBlockCoverage.class); assertFalse(coverage.iterator().next().getCoverage().isEmpty()); } @@ -80,8 +80,7 @@ public void shouldCalculateCoverageForSingleBlockMethods() // } @Test - public void shouldCalculateCoverageFor3BlockMethods() throws IOException, - InterruptedException, ExecutionException { + public void shouldCalculateCoverageFor3BlockMethods() throws Exception { final List coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class); assertCoverage(coveredClasses, "test3", 2); } @@ -122,22 +121,19 @@ public void shouldCalculateCoverageForBlockMethods() throws IOException, } @Test - public void shouldCalculateCoverageFor7BlockMethods() throws IOException, - InterruptedException, ExecutionException { + public void shouldCalculateCoverageFor7BlockMethods() throws Exception { final List coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class); assertCoverage(coveredClasses, "test7", 2); } @Test - public void shouldCalculateCoverageFor8BlockMethods() throws IOException, - InterruptedException, ExecutionException { + public void shouldCalculateCoverageFor8BlockMethods() throws Exception { final List coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class); assertCoverage(coveredClasses, "test8", 2); } @Test - public void shouldCalculateCoverageFor9BlockMethods() throws IOException, - InterruptedException, ExecutionException { + public void shouldCalculateCoverageFor9BlockMethods() throws Exception { final List coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class); assertCoverage(coveredClasses, "test9", 2); } @@ -150,50 +146,44 @@ public void shouldCalculateCoverageFor10BlockMethods() throws IOException, } @Test - public void shouldCalculateCoverageFor11BlockMethods() throws IOException, - InterruptedException, ExecutionException { + public void shouldCalculateCoverageFor11BlockMethods() throws Exception { final List coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class); assertCoverage(coveredClasses, "test11", 2); } @Test - public void shouldCalculateCoverageFor12BlockMethods() throws IOException, - InterruptedException, ExecutionException { + public void shouldCalculateCoverageFor12BlockMethods() throws Exception { final List coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class); assertCoverage(coveredClasses, "test12", 2); } @Test - public void shouldCalculateCoverageFor13BlockMethods() throws IOException, - InterruptedException, ExecutionException { + public void shouldCalculateCoverageFor13BlockMethods() throws Exception { final List coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class); assertCoverage(coveredClasses, "test13", 2); } @Test - public void shouldCalculateCoverageFor14BlockMethods() throws IOException, - InterruptedException, ExecutionException { + public void shouldCalculateCoverageFor14BlockMethods() throws Exception { final List coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class); assertCoverage(coveredClasses, "test14", 2); } @Test - public void shouldCalculateCoverageFor15BlockMethods() throws IOException, - InterruptedException, ExecutionException { + public void shouldCalculateCoverageFor15BlockMethods() throws Exception { final List coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class); assertCoverage(coveredClasses, "test15", 2); } @Test - public void shouldCalculateCoverageForLargeBlockMethods() throws IOException, - InterruptedException, ExecutionException { + public void shouldCalculateCoverageForLargeBlockMethods() throws Exception { final List coveredClasses = runCoverageForTest(TestsForMultiBlockCoverage.class); assertCoverage(coveredClasses, "testMany", 2); } @Test public void shouldCalculateCoverageForAllRelevantClasses() - throws IOException, InterruptedException, ExecutionException { + throws Exception{ final List coveredClasses = runCoverageForTest(Tests.class); @@ -204,7 +194,7 @@ public void shouldCalculateCoverageForAllRelevantClasses() @Test public void shouldCalculateCoverageForSmallMethodThatThrowsException() - throws IOException, InterruptedException, ExecutionException { + throws Exception { final List coveredClasses = runCoverageForTest(TestsClassWithException.class); assertThat(coveredClasses).anyMatch(coverageFor(CoveredBeforeExceptionTestee.class)); @@ -222,7 +212,7 @@ public void shouldCalculateCoverageForSmallMethodThatThrowsException() @Test public void shouldCalculateCoverageForMethodThatThrowsExceptionWithFinallyBlock() - throws IOException, InterruptedException, ExecutionException { + throws Exception { final List coveredClasses = runCoverageForTest(TestThrowsExceptionInFinallyBlock.class); final ClassName clazz = ClassName @@ -237,7 +227,7 @@ public void shouldCalculateCoverageForMethodThatThrowsExceptionWithFinallyBlock( @Test public void shouldCalculateCoverageForLargeMethodThatThrowsException() - throws IOException, InterruptedException, ExecutionException { + throws IOException, InterruptedException { final List coveredClasses = runCoverageForTest(TestThrowsExceptionFromLargeMethodTestee.class); final ClassName clazz = ClassName @@ -262,7 +252,7 @@ public void testFoo() { @Test public void shouldCalculateCoverageOfClassesRunInDifferentClassLoader() - throws IOException, InterruptedException, ExecutionException { + throws IOException, InterruptedException { final List coveredClasses = runCoverageForTest(TestInDifferentClassLoader.class); assertThat(coveredClasses).anyMatch(coverageFor(Testee2.class)); assertThat(coveredClasses).anyMatch(coverageFor(Testee.class)); @@ -314,6 +304,13 @@ public void shouldFailWithExitCode() throws Exception { assertEquals(ExitCode.JUNIT_ISSUE, exitCode); } + @Test + public void handlesParentChildInitializationOrderIssues() throws Exception { + final List coveredClasses = runCoverageForTest(ParentChildInitializationTest.class); + assertThat(coveredClasses) + .anyMatch(coverageFor(ClassName.fromString("com.example.coverage.execute.samples.simple.TesteeChild"))); + } + private ClassPath classPathWithoutJUnit() { final List cpWithoutJUnit = ClassPath.getClassPathElementsAsFiles().stream() @@ -328,7 +325,7 @@ private Predicate failingTest() { } private List runCoverageForTest(final Class test) - throws IOException, InterruptedException, ExecutionException { + throws IOException, InterruptedException { final List coveredClasses = new ArrayList<>(); @@ -360,16 +357,20 @@ private void runCoverageProcess(final Class test, } } - private Predicate coverageFor(final Class class1) { + private Predicate coverageFor(final Class clazz) { + return coverageFor(ClassName.fromClass(clazz)); + } + + private Predicate coverageFor(ClassName clazz) { return new Predicate() { @Override public boolean test(final CoverageResult a) { - return FCollection.contains(a.getCoverage(), resultFor(class1)); + return FCollection.contains(a.getCoverage(), resultFor(clazz)); } - private Predicate resultFor(final Class class1) { - return a -> a.isFor(ClassName.fromClass(class1)); + private Predicate resultFor(ClassName clazz) { + return a -> a.isFor(clazz); } }; } diff --git a/pitest-entry/src/test/java/org/pitest/mutationtest/config/CompoundListenerFactoryTest.java b/pitest-entry/src/test/java/org/pitest/mutationtest/config/CompoundListenerFactoryTest.java index 7ca3321c4..a11f50a1f 100644 --- a/pitest-entry/src/test/java/org/pitest/mutationtest/config/CompoundListenerFactoryTest.java +++ b/pitest-entry/src/test/java/org/pitest/mutationtest/config/CompoundListenerFactoryTest.java @@ -14,15 +14,6 @@ */ package org.pitest.mutationtest.config; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.Arrays; -import java.util.Properties; - import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -31,6 +22,16 @@ import org.pitest.mutationtest.MutationResultListener; import org.pitest.mutationtest.MutationResultListenerFactory; +import java.util.Properties; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + public class CompoundListenerFactoryTest { private CompoundListenerFactory testee; @@ -44,7 +45,11 @@ public class CompoundListenerFactoryTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); - this.testee = new CompoundListenerFactory(Arrays.asList(this.firstChild, + + when(firstChild.provides()).thenReturn(MutationResultListenerFactory.LEGACY_MODE); + when(secondChild.provides()).thenReturn(MutationResultListenerFactory.LEGACY_MODE); + + this.testee = new CompoundListenerFactory(emptyList(),asList(this.firstChild, this.secondChild)); } @@ -58,9 +63,14 @@ public void shouldCreateACombinedListenerForAllChildFactories() { when( this.secondChild.getListener(any(Properties.class), any(ListenerArguments.class))).thenReturn(listenerTwo); - this.testee.getListener(null, null).runStart(); + this.testee.getListener(null, someArgs()).runStart(); verify(listenerOne, times(1)).runStart(); verify(listenerTwo, times(1)).runStart(); } + private ListenerArguments someArgs() { + return new ListenerArguments(null, null, null, null, + 0, false, null); + } + } diff --git a/pitest-entry/src/test/java/org/pitest/mutationtest/config/PluginServicesTest.java b/pitest-entry/src/test/java/org/pitest/mutationtest/config/PluginServicesTest.java index 469a62178..69baf5e71 100644 --- a/pitest-entry/src/test/java/org/pitest/mutationtest/config/PluginServicesTest.java +++ b/pitest-entry/src/test/java/org/pitest/mutationtest/config/PluginServicesTest.java @@ -1,15 +1,18 @@ package org.pitest.mutationtest.config; -import static org.junit.Assert.assertTrue; - -import java.util.function.Predicate; - import org.junit.Test; import org.pitest.functional.FCollection; +import org.pitest.mutationtest.MutationResultListenerFactory; +import org.pitest.mutationtest.build.MutationInterceptorFactory; import org.pitest.mutationtest.engine.gregor.config.GregorEngineFactory; import org.pitest.mutationtest.filter.LimitNumberOfMutationsPerClassFilterFactory; import org.pitest.mutationtest.report.csv.CSVReportFactory; +import java.util.function.Predicate; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertTrue; + public class PluginServicesTest { private final PluginServices testee = PluginServices.makeForContextLoader(); @@ -32,6 +35,12 @@ public void shouldListDefaultMutationFilterAsToolClasspathPlugin() { theClass(LimitNumberOfMutationsPerClassFilterFactory.class))); } + @Test + public void shouldListAllTypesOfFeature() { + assertThat(testee.findFeatures()).hasAtLeastOneElementOfType(MutationInterceptorFactory.class); + assertThat(testee.findFeatures()).hasAtLeastOneElementOfType(MutationResultListenerFactory.class); + } + private static Predicate theClass(final Class clss) { return a -> a.getClass().equals(clss); } diff --git a/pitest-entry/src/test/java/org/pitest/mutationtest/config/SettingsFactoryTest.java b/pitest-entry/src/test/java/org/pitest/mutationtest/config/SettingsFactoryTest.java index 2153503fd..cdd540913 100644 --- a/pitest-entry/src/test/java/org/pitest/mutationtest/config/SettingsFactoryTest.java +++ b/pitest-entry/src/test/java/org/pitest/mutationtest/config/SettingsFactoryTest.java @@ -1,17 +1,5 @@ package org.pitest.mutationtest.config; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; - -import java.io.File; -import java.util.Arrays; -import java.util.Collections; -import java.util.function.Consumer; - import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; @@ -22,6 +10,19 @@ import org.pitest.testapi.TestGroupConfig; import org.pitest.util.PitError; +import java.io.File; +import java.util.Arrays; +import java.util.Collections; +import java.util.function.Consumer; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + public class SettingsFactoryTest { private final ReportOptions options = new ReportOptions(); @@ -127,4 +128,13 @@ public void shouldDescribeDisabledFeatures() { verify(disabled).accept(Feature.named("FSTATINIT")); } + @Test + public void shouldErrorWhenUnkownFeatureRequested() { + this.options.setFeatures(Arrays.asList("+UNKOWN")); + + assertThatCode( () ->this.testee.checkRequestedFeatures()) + .hasMessageContaining(("UNKOWN")); + + } + } diff --git a/pitest-html-report/src/main/java/org/pitest/mutationtest/report/html/HtmlReportFactory.java b/pitest-html-report/src/main/java/org/pitest/mutationtest/report/html/HtmlReportFactory.java index f7d2b13b3..4394869a4 100644 --- a/pitest-html-report/src/main/java/org/pitest/mutationtest/report/html/HtmlReportFactory.java +++ b/pitest-html-report/src/main/java/org/pitest/mutationtest/report/html/HtmlReportFactory.java @@ -15,12 +15,12 @@ package org.pitest.mutationtest.report.html; -import java.util.Properties; - import org.pitest.mutationtest.ListenerArguments; import org.pitest.mutationtest.MutationResultListener; import org.pitest.mutationtest.MutationResultListenerFactory; +import java.util.Properties; + public class HtmlReportFactory implements MutationResultListenerFactory { @Override @@ -40,4 +40,5 @@ public String name() { public String description() { return "Default html report plugin"; } + } diff --git a/pitest-maven-verification/src/test/java/org/pitest/PitMojoIT.java b/pitest-maven-verification/src/test/java/org/pitest/PitMojoIT.java index bfdc8158f..4601cccb2 100755 --- a/pitest-maven-verification/src/test/java/org/pitest/PitMojoIT.java +++ b/pitest-maven-verification/src/test/java/org/pitest/PitMojoIT.java @@ -15,18 +15,6 @@ */ package org.pitest; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.junit.Assume.assumeFalse; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.Properties; - import org.apache.commons.io.FileUtils; import org.apache.maven.it.VerificationException; import org.apache.maven.it.Verifier; @@ -43,6 +31,18 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeFalse; + /** * @author Stefan Penndorf */ @@ -563,4 +563,14 @@ public void shouldFindOccupiedTestPackages() throws IOException, VerificationExc "DiscoveredClass.java"); } + @Test + public void shouldNotNullPointerWhenEnumInitializerNotCalled() throws IOException, VerificationException { + File testDir = prepare("/pit-enum-constructor-npe"); + verifier.executeGoal("test"); + verifier.executeGoal("org.pitest:pitest-maven:mutationCoverage"); + + String actual = readResults(testDir); + assertThat(actual).isNotEmpty(); + } + } diff --git a/pitest-maven-verification/src/test/resources/pit-158-coverage/pom.xml b/pitest-maven-verification/src/test/resources/pit-158-coverage/pom.xml index c6e1912e7..62b649736 100644 --- a/pitest-maven-verification/src/test/resources/pit-158-coverage/pom.xml +++ b/pitest-maven-verification/src/test/resources/pit-158-coverage/pom.xml @@ -22,7 +22,7 @@ junit junit - 4.8.2 + 4.13.1 diff --git a/pitest-maven-verification/src/test/resources/pit-263-yatspec/pom.xml b/pitest-maven-verification/src/test/resources/pit-263-yatspec/pom.xml index b273c1dc8..f2768c584 100644 --- a/pitest-maven-verification/src/test/resources/pit-263-yatspec/pom.xml +++ b/pitest-maven-verification/src/test/resources/pit-263-yatspec/pom.xml @@ -47,7 +47,7 @@ junit junit - 4.12 + 4.13.1 test diff --git a/pitest-maven-verification/src/test/resources/pit-33-setUserDir/pit-33-setUserDir-subModule/pom.xml b/pitest-maven-verification/src/test/resources/pit-33-setUserDir/pit-33-setUserDir-subModule/pom.xml index bf34832ad..c941dae55 100644 --- a/pitest-maven-verification/src/test/resources/pit-33-setUserDir/pit-33-setUserDir-subModule/pom.xml +++ b/pitest-maven-verification/src/test/resources/pit-33-setUserDir/pit-33-setUserDir-subModule/pom.xml @@ -36,6 +36,6 @@ - 4.8.1 + 4.13.1 \ No newline at end of file diff --git a/pitest-maven-verification/src/test/resources/pit-33-setUserDir/pom.xml b/pitest-maven-verification/src/test/resources/pit-33-setUserDir/pom.xml index 69a28a7bc..f51907c7a 100644 --- a/pitest-maven-verification/src/test/resources/pit-33-setUserDir/pom.xml +++ b/pitest-maven-verification/src/test/resources/pit-33-setUserDir/pom.xml @@ -39,7 +39,7 @@ - 4.8.1 + 4.13.1 pit-33-setUserDir-subModule diff --git a/pitest-maven-verification/src/test/resources/pit-deterministic-coverage/pom.xml b/pitest-maven-verification/src/test/resources/pit-deterministic-coverage/pom.xml index ca60c0566..6a77356df 100644 --- a/pitest-maven-verification/src/test/resources/pit-deterministic-coverage/pom.xml +++ b/pitest-maven-verification/src/test/resources/pit-deterministic-coverage/pom.xml @@ -41,7 +41,7 @@ - 4.8.1 + 4.13.1 diff --git a/pitest-maven-verification/src/test/resources/pit-enum-constructor-npe/pom.xml b/pitest-maven-verification/src/test/resources/pit-enum-constructor-npe/pom.xml new file mode 100644 index 000000000..d0cb3472c --- /dev/null +++ b/pitest-maven-verification/src/test/resources/pit-enum-constructor-npe/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + com.example + pitest-sample + 0.1-SNAPSHOT + pit #770 npe in constructor + + + UTF-8 + + + + + junit + junit + 4.13.1 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.4 + + 1.7 + 1.7 + + + + org.pitest + pitest-maven + ${pit.version} + + true + + XML + + false + true + + VOID_METHOD_CALLS + + + + + + + diff --git a/pitest-maven-verification/src/test/resources/pit-enum-constructor-npe/src/main/java/com/example/Test.java b/pitest-maven-verification/src/test/resources/pit-enum-constructor-npe/src/main/java/com/example/Test.java new file mode 100644 index 000000000..e101f84fd --- /dev/null +++ b/pitest-maven-verification/src/test/resources/pit-enum-constructor-npe/src/main/java/com/example/Test.java @@ -0,0 +1,7 @@ +package com.example; + +class Test { + public Test() { + System.out.println("I only exist so that pitest thinks there is something to mutate"); + } +} diff --git a/pitest-maven-verification/src/test/resources/pit-enum-constructor-npe/src/test/java/test/com/example/NestedClassTest.java b/pitest-maven-verification/src/test/resources/pit-enum-constructor-npe/src/test/java/test/com/example/NestedClassTest.java new file mode 100644 index 000000000..c4a3b8574 --- /dev/null +++ b/pitest-maven-verification/src/test/resources/pit-enum-constructor-npe/src/test/java/test/com/example/NestedClassTest.java @@ -0,0 +1,15 @@ +package com.example; + +import org.junit.Test; + +public class NestedClassTest { + public static enum MyEnum { + A { } + } + + @Test + public void test() throws ClassNotFoundException { + Class.forName("com.example.NestedClassTest$MyEnum$1"); + } +} + diff --git a/pitest-maven-verification/src/test/resources/pit-findOccupiedTestPackages/pom.xml b/pitest-maven-verification/src/test/resources/pit-findOccupiedTestPackages/pom.xml index 7877d7c93..177a3fdc0 100644 --- a/pitest-maven-verification/src/test/resources/pit-findOccupiedTestPackages/pom.xml +++ b/pitest-maven-verification/src/test/resources/pit-findOccupiedTestPackages/pom.xml @@ -16,7 +16,7 @@ junit junit - 4.8.2 + 4.13.1 diff --git a/pitest-maven-verification/src/test/resources/pit-junit-categories/pom.xml b/pitest-maven-verification/src/test/resources/pit-junit-categories/pom.xml index 9938e8d09..8060a837e 100644 --- a/pitest-maven-verification/src/test/resources/pit-junit-categories/pom.xml +++ b/pitest-maven-verification/src/test/resources/pit-junit-categories/pom.xml @@ -10,7 +10,7 @@ junit junit - 4.12 + 4.13.1 test diff --git a/pitest-maven-verification/src/test/resources/pit-powermock/pom.xml b/pitest-maven-verification/src/test/resources/pit-powermock/pom.xml index e125e3c09..9734b81d1 100644 --- a/pitest-maven-verification/src/test/resources/pit-powermock/pom.xml +++ b/pitest-maven-verification/src/test/resources/pit-powermock/pom.xml @@ -69,7 +69,7 @@ - 4.12 + 4.13.1 1.7.3 diff --git a/pitest/pom.xml b/pitest/pom.xml index 4880effc9..24af74b71 100644 --- a/pitest/pom.xml +++ b/pitest/pom.xml @@ -226,7 +226,7 @@ com.thoughtworks.xstream xstream - 1.4.8 + 1.4.15 test diff --git a/pitest/src/main/java/org/pitest/coverage/CoverageClassVisitor.java b/pitest/src/main/java/org/pitest/coverage/CoverageClassVisitor.java index 46bf64a4c..db641e1ec 100644 --- a/pitest/src/main/java/org/pitest/coverage/CoverageClassVisitor.java +++ b/pitest/src/main/java/org/pitest/coverage/CoverageClassVisitor.java @@ -40,6 +40,7 @@ public class CoverageClassVisitor extends MethodFilteringAdapter { private String className; private boolean foundClinit; + private boolean isInterface; public CoverageClassVisitor(final int classId, final ClassWriter writer) { super(writer, BridgeMethodFilter.INSTANCE); @@ -55,6 +56,7 @@ public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(version, access, name, signature, superName, interfaces); this.className = name; + this.isInterface = (access & Opcodes.ACC_INTERFACE) != 0; } @@ -93,12 +95,10 @@ public void visitEnd() { private void addCoverageProbeField() { - super.visitField(Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_PUBLIC - | Opcodes.ACC_SYNTHETIC, CodeCoverageStore.PROBE_FIELD_NAME, "[Z", null, + super.visitField(fieldModifiers(), CodeCoverageStore.PROBE_FIELD_NAME, "[Z", null, null); - super.visitField(Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_PUBLIC - | Opcodes.ACC_SYNTHETIC, CodeCoverageStore.PROBE_LENGTH_FIELD_NAME, "I", + super.visitField(fieldModifiers(), CodeCoverageStore.PROBE_LENGTH_FIELD_NAME, "I", null, this.probeCount + 1); //If there is no , then generate one that sets the probe field directly @@ -107,7 +107,6 @@ private void addCoverageProbeField() { .visitMethod(Opcodes.ACC_STATIC, "", "()V", null, null); clinitMv.visitCode(); - pushConstant(clinitMv, this.classId); pushConstant(clinitMv, this.probeCount); clinitMv @@ -122,6 +121,20 @@ private void addCoverageProbeField() { } } + private int fieldModifiers() { + // For interfaces field must be final. + // For classes cannot be final as we must cover the corner case of child classes + // referenced in their parent's static initializer. The probe field for these + // must be initialised lazyily outside of the static initializer block. + if (isInterface) { + return Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_PUBLIC + | Opcodes.ACC_SYNTHETIC; + } + + return Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT | Opcodes.ACC_PRIVATE + | Opcodes.ACC_SYNTHETIC; + } + private void pushConstant(MethodVisitor mv, int value) { switch (value) { case 0: diff --git a/pitest/src/main/java/org/pitest/coverage/analysis/ArrayProbeCoverageMethodVisitor.java b/pitest/src/main/java/org/pitest/coverage/analysis/ArrayProbeCoverageMethodVisitor.java index 6307bbd4e..d0733e9b1 100644 --- a/pitest/src/main/java/org/pitest/coverage/analysis/ArrayProbeCoverageMethodVisitor.java +++ b/pitest/src/main/java/org/pitest/coverage/analysis/ArrayProbeCoverageMethodVisitor.java @@ -17,14 +17,15 @@ package org.pitest.coverage.analysis; -import java.util.List; - +import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.pitest.mutationtest.engine.gregor.analysis.InstructionCounter; import sun.pitest.CodeCoverageStore; +import java.util.List; + /** * * Instruments a method adding probes at each block. @@ -100,6 +101,26 @@ void prepare() { this.mv.visitFieldInsn(Opcodes.GETSTATIC, className, CodeCoverageStore.PROBE_FIELD_NAME, "[Z"); + this.mv.visitInsn(DUP); //duplicate array reference, one for null check and one to use + + //Check if PROBE_FIELD_NAME has been initialised + Label notnull = new Label(); + this.mv.visitJumpInsn(Opcodes.IFNONNULL,notnull); + + //if not then initialise + this.mv.visitInsn(POP); //gte rid of null on top of stack + pushConstant(this.classId); + this.mv.visitFieldInsn(Opcodes.GETSTATIC, this.className, CodeCoverageStore.PROBE_LENGTH_FIELD_NAME,"I"); + this.mv + .visitMethodInsn(Opcodes.INVOKESTATIC, CodeCoverageStore.CLASS_NAME, + "getOrRegisterClassProbes", "(II)[Z", false); + this.mv.visitInsn(DUP);//duplicate array reference, one to store and one to use + this.mv.visitFieldInsn(Opcodes.PUTSTATIC, className, + CodeCoverageStore.PROBE_FIELD_NAME, "[Z"); + + //else do nothing + this.mv.visitLabel(notnull); + //Make sure that we recorded that the class was hit this.mv.visitInsn(DUP); this.mv.visitInsn(ICONST_0); diff --git a/pitest/src/main/java/org/pitest/plugin/FeatureSelector.java b/pitest/src/main/java/org/pitest/plugin/FeatureSelector.java index d9bee99ee..9a0b3c316 100644 --- a/pitest/src/main/java/org/pitest/plugin/FeatureSelector.java +++ b/pitest/src/main/java/org/pitest/plugin/FeatureSelector.java @@ -1,5 +1,7 @@ package org.pitest.plugin; +import org.pitest.functional.FCollection; + import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -7,8 +9,6 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.pitest.functional.FCollection; - public class FeatureSelector { private final Map> settings; @@ -25,14 +25,14 @@ public List getActiveFeatures() { public FeatureSetting getSettingForFeature(String feature) { FeatureSetting conf = null; - final Collection groupedSettings = this.settings.get(feature); + final Collection groupedSettings = this.settings.get(feature.toLowerCase()); if (groupedSettings != null) { conf = groupedSettings.iterator().next(); } return conf; } - public List selectFeatures(List features, Collection filters) { + private List selectFeatures(List features, Collection filters) { final List factories = new ArrayList<>(filters); final Map> featureMap = FCollection.bucket(factories, byFeatureName()); @@ -41,7 +41,7 @@ public List selectFeatures(List features, Collection filte for ( final FeatureSetting each : features ) { final Collection providers = featureMap.get(each.feature()); if ((providers == null) || providers.isEmpty()) { - throw new IllegalArgumentException("Pitest and its installed plugins do not recognise the feature " + each.feature()); + continue; } if (each.addsFeature()) { @@ -65,7 +65,7 @@ private Function byFeatureName() { } private Function byFeature() { - return a -> a.feature(); + return a -> a.feature().toLowerCase(); } } \ No newline at end of file diff --git a/pitest/src/test/java/org/pitest/plugin/FeatureSelectorTest.java b/pitest/src/test/java/org/pitest/plugin/FeatureSelectorTest.java index 285b34d6d..3c6de733a 100644 --- a/pitest/src/test/java/org/pitest/plugin/FeatureSelectorTest.java +++ b/pitest/src/test/java/org/pitest/plugin/FeatureSelectorTest.java @@ -1,15 +1,15 @@ package org.pitest.plugin; -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; +import static org.assertj.core.api.Assertions.assertThat; public class FeatureSelectorTest { @@ -38,7 +38,7 @@ public void shouldSelectFeaturesThatAreOffByDefault() { @Test public void shouldEnableFeaturesWhenRequested() { - final FeatureSetting enableBar = new FeatureSetting("bar", ToggleStatus.ACTIVATE, new HashMap>()); + final FeatureSetting enableBar = new FeatureSetting("bar", ToggleStatus.ACTIVATE, new HashMap<>()); this.testee = new FeatureSelector<>(Arrays.asList(enableBar), features(this.onByDefault, this.offByDefault)); assertThat(this.testee.getActiveFeatures()).containsOnly(this.offByDefault, this.onByDefault); @@ -46,27 +46,27 @@ public void shouldEnableFeaturesWhenRequested() { @Test public void shouldDisableFeaturesWhenRequested() { - final FeatureSetting disableFoo = new FeatureSetting("foo", ToggleStatus.DEACTIVATE, new HashMap>()); + final FeatureSetting disableFoo = new FeatureSetting("foo", ToggleStatus.DEACTIVATE, new HashMap<>()); this.testee = new FeatureSelector<>(Arrays.asList(disableFoo), features(this.onByDefault)); assertThat(this.testee.getActiveFeatures()).isEmpty(); } @Test - public void shouldThrowErrorWhenConfigForUnknownFeatureProvided() { - final FeatureSetting wrong = new FeatureSetting("unknown", ToggleStatus.DEACTIVATE, new HashMap>()); + public void shouldProvideConfigurationForFeatureWhenProvided() { + final FeatureSetting fooConfig = new FeatureSetting("foo", ToggleStatus.DEACTIVATE, new HashMap<>()); + this.testee = new FeatureSelector<>(Arrays.asList(fooConfig), features(this.onByDefault)); - this.thrown.expect(IllegalArgumentException.class); - this.testee = new FeatureSelector<>(Arrays.asList(wrong), features(this.onByDefault)); + assertThat(this.testee.getSettingForFeature("foo")).isEqualTo(fooConfig); + assertThat(this.testee.getSettingForFeature("bar")).isNull(); } @Test - public void shouldProvideConfigurationForFeatureWhenProvided() { - final FeatureSetting fooConfig = new FeatureSetting("foo", ToggleStatus.DEACTIVATE, new HashMap>()); + public void featureNamesAreCaseInsensitive() { + final FeatureSetting fooConfig = new FeatureSetting("foo", ToggleStatus.DEACTIVATE, new HashMap<>()); this.testee = new FeatureSelector<>(Arrays.asList(fooConfig), features(this.onByDefault)); - assertThat(this.testee.getSettingForFeature("foo")).isEqualTo(fooConfig); - assertThat(this.testee.getSettingForFeature("bar")).isNull(); + assertThat(this.testee.getSettingForFeature("FOO")).isEqualTo(fooConfig); } private List noSettings() { diff --git a/pitest/src/test/java/org/pitest/util/PitXmlDriver.java b/pitest/src/test/java/org/pitest/util/PitXmlDriver.java index b9814b6e6..3c6cf1acb 100644 --- a/pitest/src/test/java/org/pitest/util/PitXmlDriver.java +++ b/pitest/src/test/java/org/pitest/util/PitXmlDriver.java @@ -14,12 +14,10 @@ */ package org.pitest.util; -import org.xmlpull.mxp1.MXParser; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - import com.thoughtworks.xstream.io.xml.AbstractXppDriver; import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder; +import org.xmlpull.mxp1.MXParser; +import org.xmlpull.v1.XmlPullParser; /** * Pull parser driver that creates hard coded parser type to avoid clashes with @@ -32,8 +30,7 @@ public PitXmlDriver() { } @Override - protected synchronized XmlPullParser createParser() - throws XmlPullParserException { + protected synchronized XmlPullParser createParser() { return new MXParser(); } } diff --git a/pitest/src/test/java/org/pitest/util/XStreamCloning.java b/pitest/src/test/java/org/pitest/util/XStreamCloning.java index eea891ef1..afdaa3f5d 100644 --- a/pitest/src/test/java/org/pitest/util/XStreamCloning.java +++ b/pitest/src/test/java/org/pitest/util/XStreamCloning.java @@ -1,13 +1,13 @@ package org.pitest.util; -import static org.pitest.util.Unchecked.translateCheckedException; +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.io.xml.CompactWriter; import java.io.StringWriter; import java.io.Writer; import java.util.WeakHashMap; -import com.thoughtworks.xstream.XStream; -import com.thoughtworks.xstream.io.xml.CompactWriter; +import static org.pitest.util.Unchecked.translateCheckedException; public class XStreamCloning { @@ -32,6 +32,8 @@ private static XStream getXStreamForLoader(final ClassLoader loader) { if (foreginXstream == null) { foreginXstream = new XStream(new PitXmlDriver()); foreginXstream.setClassLoader(loader); + XStream.setupDefaultSecurity(foreginXstream); + foreginXstream.allowTypesByWildcard(new String[] {"**"}); // possible that more than one instance will be created // per loader, but probably better than synchronizing the whole method synchronized (CACHE) {