Skip to content

Commit

Permalink
Merge pull request #728 from hcoles/enum_constructors
Browse files Browse the repository at this point in the history
adds filter for enum constructors for #556
  • Loading branch information
hcoles authored Jan 23, 2020
2 parents a387080 + e9b0c23 commit 9140754
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.pitest.mutationtest.build.intercept.javafeatures;

import org.pitest.bytecode.analysis.ClassTree;
import org.pitest.mutationtest.build.InterceptorType;
import org.pitest.mutationtest.build.MutationInterceptor;
import org.pitest.mutationtest.engine.Mutater;
import org.pitest.mutationtest.engine.MutationDetails;

import java.util.Collection;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
* Filters out mutations in Enum constructors, these are called only once
* per instance so are effectively static initializers.
*/
public class EnumConstructorFilter implements MutationInterceptor {

private boolean isEnum;

@Override
public InterceptorType type() {
return InterceptorType.FILTER;
}

@Override
public void begin(ClassTree clazz) {
this.isEnum = clazz.rawNode().superName.equals("java/lang/Enum");
}

@Override
public Collection<MutationDetails> intercept(
Collection<MutationDetails> mutations, Mutater m) {
return mutations.stream()
.filter(isInEnumConstructor().negate())
.collect(Collectors.toList());
}

private Predicate<MutationDetails> isInEnumConstructor() {
return m -> isEnum && m.getMethod().name().equals("<init>");
}


@Override
public void end() {
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.pitest.mutationtest.build.intercept.javafeatures;

import org.pitest.mutationtest.build.InterceptorParameters;
import org.pitest.mutationtest.build.MutationInterceptor;
import org.pitest.mutationtest.build.MutationInterceptorFactory;
import org.pitest.plugin.Feature;

public class EnumConstructorFilterFactory implements MutationInterceptorFactory {

@Override
public String description() {
return "Enum constructor filter";
}

@Override
public MutationInterceptor createInterceptor(InterceptorParameters params) {
return new EnumConstructorFilter();
}

@Override
public Feature provides() {
return Feature.named("FENUM")
.withOnByDefault(true)
.withDescription("Filters mutations in enum constructors");
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ org.pitest.mutationtest.build.intercept.javafeatures.InlinedFinallyBlockFilterFa
org.pitest.mutationtest.build.intercept.javafeatures.ImplicitNullCheckFilterFactory
org.pitest.mutationtest.build.intercept.javafeatures.MethodReferenceNullCheckFilterFactory
org.pitest.mutationtest.build.intercept.javafeatures.ForEachLoopFilterFactory
org.pitest.mutationtest.build.intercept.javafeatures.EnumConstructorFilterFactory
org.pitest.mutationtest.build.intercept.logging.LoggingCallsFilterFactory
org.pitest.mutationtest.build.intercept.timeout.InfiniteForLoopFilterFactory
org.pitest.mutationtest.build.intercept.timeout.InfiniteIteratorLoopFilterFactory
Expand Down
10 changes: 10 additions & 0 deletions pitest-entry/src/test/java/org/pitest/mutationtest/build/MutationDiscoveryTest.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.pitest.classpath.ClassloaderByteArraySource;
import org.pitest.mutationtest.EngineArguments;
import org.pitest.mutationtest.MutationConfig;
import org.pitest.mutationtest.build.intercept.javafeatures.AnEnum;
import org.pitest.mutationtest.build.intercept.javafeatures.ForEachFilterTest.HasForEachLoop;
import org.pitest.mutationtest.config.PluginServices;
import org.pitest.mutationtest.config.ReportOptions;
Expand Down Expand Up @@ -193,6 +194,15 @@ public void shouldFilterMutationsToForEachLoops() {
assertThat(actual.size()).isLessThan(actualWithoutFilter.size());
}

@Test
public void shouldFilterMutationsToEnumConstructors() {
final Collection<MutationDetails> actual = findMutants(AnEnum.class);

this.data.setFeatures(Collections.singletonList("-FENUM"));
final Collection<MutationDetails> actualWithoutFilter = findMutants(AnEnum.class);

assertThat(actual.size()).isLessThan(actualWithoutFilter.size());
}

@Test
public void filtersEquivalentReturnValsMutants() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.pitest.mutationtest.build.intercept.javafeatures;

public enum AnEnum {
AN_INSTANCE("hello");

AnEnum(String s) {
System.out.println(s);
}

public void aMethod() {
System.out.println("dont mutate me");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.pitest.mutationtest.build.intercept.javafeatures;

import org.junit.Test;
import org.pitest.mutationtest.engine.MutationDetails;
import java.util.function.Predicate;

import static org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator.VOID_METHOD_CALL_MUTATOR;

public class EnumConstructorTest {

EnumConstructorFilter testee = new EnumConstructorFilter();

FilterTester verifier = new FilterTester("unused", this.testee, VOID_METHOD_CALL_MUTATOR);

@Test
public void filtersMutantsFromEnumConstructor() {
this.verifier.assertFiltersMutationsMatching(inMethodNamed("<init>"), AnEnum.class);
}

@Test
public void doesNotFilterMutantsInCustomEnumMethods() {
this.verifier.assertFiltersNoMutationsMatching(inMethodNamed("aMethod"), AnEnum.class);
}

@Test
public void doesNotFilterMutantsInNonEnumConstructors() {
this.verifier.assertFiltersNoMutationsMatching(inMethodNamed("<init>"), AClass.class);
}

private Predicate<MutationDetails> inMethodNamed(String name) {
return m -> m.getMethod().name().equals(name);
}
}

class AClass {
AClass(String s) {
System.out.println(s);
}
}
64 changes: 64 additions & 0 deletions ...ntry/src/test/java/org/pitest/mutationtest/build/intercept/javafeatures/FilterTester.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,63 @@ public void assertFiltersNMutationFromClass(int n, Class<?> clazz) {
softly.assertAll();
}

public void assertFiltersNoMutationsMatching(Predicate<MutationDetails> match, Class<?> clazz) {
final Sample s = makeSampleForCurrentCompiler(clazz);

final SoftAssertions softly = new SoftAssertions();
GregorMutater mutator = mutateFromClassLoader();

assertFiltersNoMatchingMutants(match, mutator, s, softly);

softly.assertAll();
}

public void assertFiltersMutationsMatching(Predicate<MutationDetails> match, Class<?> clazz) {
final Sample s = makeSampleForCurrentCompiler(clazz);

final SoftAssertions softly = new SoftAssertions();
GregorMutater mutator = mutateFromClassLoader();

assertFiltersMatchingMutants(match, mutator, s, softly);

softly.assertAll();
}

public void assertFiltersMutationsMatching(Predicate<MutationDetails> match, String sample) {
final GregorMutater mutator = mutateFromResourceDir();
atLeastOneSampleExists(sample);

final SoftAssertions softly = new SoftAssertions();

for (final Sample s : samples(sample)) {
assertFiltersMatchingMutants(match, mutator, s, softly);
}

softly.assertAll();
}

private void assertFiltersMatchingMutants(Predicate<MutationDetails> match, GregorMutater mutator, Sample s, SoftAssertions softly) {
final List<MutationDetails> mutations = mutator.findMutations(s.className);
final Collection<MutationDetails> actual = filter(s.clazz, mutations, mutator);

checkHasMutantsMatching(match, s, softly, mutations);

softly.assertThat(actual)
.describedAs("Expected to filter out all matching mutants")
.noneMatch(match);
}

private void assertFiltersNoMatchingMutants(Predicate<MutationDetails> match, GregorMutater mutator, Sample s, SoftAssertions softly) {
final List<MutationDetails> mutations = mutator.findMutations(s.className);
final Collection<MutationDetails> actual = filter(s.clazz, mutations, mutator);

checkHasMutantsMatching(match, s, softly, mutations);

softly.assertThat(actual)
.describedAs("Expected to filter no matching mutants")
.anyMatch(match);
}

private Sample makeSampleForCurrentCompiler(Class<?> clazz) {
final ClassloaderByteArraySource source = ClassloaderByteArraySource.fromContext();
final Sample s = new Sample();
Expand Down Expand Up @@ -163,6 +220,7 @@ private Predicate<MutationDetails> notIn(
return a -> !actual.contains(a);
}


private void assertFiltersNMutants(int n, GregorMutater mutator, Sample s, SoftAssertions softly) {
final List<MutationDetails> mutations = mutator.findMutations(s.className);
final Collection<MutationDetails> actual = filter(s.clazz, mutations, mutator);
Expand All @@ -188,6 +246,12 @@ private void checkHasNMutants(int n, Sample s, SoftAssertions softly,
.isGreaterThanOrEqualTo(n);
}

private void checkHasMutantsMatching(Predicate<MutationDetails> match, Sample s, SoftAssertions softly,
List<MutationDetails> mutations) {
softly.assertThat(mutations)
.describedAs("No matching mutations produced with " + s.compiler + " compiler. This test has a bug in it.\n" + s.clazz)
.anyMatch(match);
}
private GregorMutater mutateFromResourceDir() {
return new GregorMutater(this.source, m -> true, this.mutators);
}
Expand Down

0 comments on commit 9140754

Please sign in to comment.