Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move assert filtering to interceptor #1027

Merged
merged 2 commits into from
Jun 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package org.pitest.mutationtest.build.intercept.javafeatures;

import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.pitest.bytecode.analysis.MethodTree;
import org.pitest.mutationtest.build.intercept.Region;
import org.pitest.mutationtest.build.intercept.RegionInterceptor;
import org.pitest.sequence.Context;
import org.pitest.sequence.Match;
import org.pitest.sequence.QueryParams;
import org.pitest.sequence.QueryStart;
import org.pitest.sequence.SequenceMatcher;
import org.pitest.sequence.Slot;
import org.pitest.sequence.SlotWrite;

import java.util.List;
import java.util.stream.Collectors;

import static org.pitest.bytecode.analysis.InstructionMatchers.anyInstruction;
import static org.pitest.bytecode.analysis.InstructionMatchers.jumpsTo;
import static org.pitest.bytecode.analysis.InstructionMatchers.notAnInstruction;
import static org.pitest.bytecode.analysis.OpcodeMatchers.IFNE;
import static org.pitest.sequence.Result.result;


public class AssertFilter extends RegionInterceptor {

static final Slot<AbstractInsnNode> START = Slot.create(AbstractInsnNode.class);
static final Slot<LabelNode> END = Slot.create(LabelNode.class);

static final SequenceMatcher<AbstractInsnNode> ASSERT_GET = QueryStart
.any(AbstractInsnNode.class)
.then(getStatic("$assertionsDisabled").and(store(START.write())))
.then(IFNE.and(jumpsTo(END.write())))
.zeroOrMore(QueryStart.match(anyInstruction()))
.compile(QueryParams.params(AbstractInsnNode.class)
.withIgnores(notAnInstruction())
);

private static Match<AbstractInsnNode> getStatic(String name) {
return (c,n) -> {
if (n instanceof FieldInsnNode) {
return result(((FieldInsnNode) n).name.equals(name), c);
}
return result(false,c);
};
}


private static Match<AbstractInsnNode> store(SlotWrite<AbstractInsnNode> slot) {
return (c,n) -> result(true, c.store(slot, n));
}

protected List<Region> computeRegions(MethodTree method) {
Context context = Context.start();
List<Region> regions = ASSERT_GET.contextMatches(method.instructions(), context).stream()
.map(c -> new Region(c.retrieve(START.read()).get(), c.retrieve(END.read()).get()))
.collect(Collectors.toList());
return regions;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
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 AssertionsFilterFactory implements MutationInterceptorFactory {

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

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

@Override
public Feature provides() {
return Feature.named("FASSERT")
.withOnByDefault(true)
.withDescription("Filters mutations in compiler generated code for assertions");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ org.pitest.mutationtest.build.intercept.javafeatures.ForEachLoopFilterFactory
org.pitest.mutationtest.build.intercept.javafeatures.EnumConstructorFilterFactory
org.pitest.mutationtest.build.intercept.javafeatures.RecordFilterFactory
org.pitest.mutationtest.build.intercept.javafeatures.StringSwitchFilterFactory
org.pitest.mutationtest.build.intercept.javafeatures.AssertionsFilterFactory
org.pitest.mutationtest.build.intercept.logging.LoggingCallsFilterFactory
org.pitest.mutationtest.build.intercept.timeout.InfiniteForLoopFilterFactory
org.pitest.mutationtest.build.intercept.timeout.InfiniteIteratorLoopFilterFactory
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package org.pitest.mutationtest.build.intercept.javafeatures;

import org.junit.Test;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.pitest.mutationtest.engine.gregor.mutators.NullMutateEverything;
import org.pitest.verifier.interceptors.InterceptorVerifier;
import org.pitest.verifier.interceptors.VerifierStart;

import java.util.function.Predicate;

import static org.pitest.bytecode.analysis.InstructionMatchers.isA;
import static org.pitest.bytecode.analysis.OpcodeMatchers.ATHROW;

public class AssertionsTest {
AssertionsFilterFactory underTest = new AssertionsFilterFactory();
InterceptorVerifier v = VerifierStart.forInterceptorFactory(underTest)
.usingMutator(new NullMutateEverything());

@Test
public void doesNotFilterCodeWithNoAsserts() {
v.forClass(NoAsserts.class)
.forAnyCode()
.mutantsAreGenerated()
.noMutantsAreFiltered()
.verify();
}

@Test
public void filtersAssertCode() {
v.forClass(HasAssertStatement.class)
.forMethod("foo")
.forCodeMatching(assertInstructions())
.mutantsAreGenerated()
.allMutantsAreFiltered()
.verify();
}

@Test
public void filtersAssertCodeWhenOtherStatementsPresent() {
v.forClass(HasAssertStatementAndOtherStatements.class)
.forMethod("foo")
.forCodeMatching(sysoutFieldReference())
.mutantsAreGenerated()
.noMutantsAreFiltered()
.verify();
}

@Test
public void leavesCodeNotInAssert() {
v.forClass(HasAssertStatementAndOtherStatements.class)
.forMethod("foo")
.forCodeMatching(sysoutFieldReference())
.mutantsAreGenerated()
.noMutantsAreFiltered()
.verify();
}

@Test
public void filtersMultipleAsserts() {
v.forClass(MultipleAsserts.class)
.forMethod("foo")
.forCodeMatching(assertInstructions())
.mutantsAreGenerated()
.allMutantsAreFiltered()
.verify();
}

@Test
public void leavesCodeNotInAssertWhenMultipleAsserts() {
v.forClass(MultipleAsserts.class)
.forMethod("foo")
.forCodeMatching(assertInstructions())
.mutantsAreGenerated()
.allMutantsAreFiltered()
.verify();
}

private Predicate<AbstractInsnNode> assertInstructions() {
return isA(JumpInsnNode.class).or(ATHROW).asPredicate().or(fieldAccess());
}

private Predicate<? super AbstractInsnNode> fieldAccess() {
return isA(FieldInsnNode.class).asPredicate().and(sysoutFieldReference().negate());
}

private Predicate<AbstractInsnNode> sysoutFieldReference() {
return n -> n instanceof FieldInsnNode && ((FieldInsnNode) n).owner.equals("java/lang/System");
}

static class HasAssertStatement {
public void foo(final int i) {
assert ((i + 20) > 10);
}
}

static class HasAssertStatementAndOtherStatements {
public int state;

public void foo(final int i) {
System.out.println("before");
assert ((i + 20) > 10);
System.out.println("after");
}
}

static class NoAsserts {
static boolean aField = true;
public void foo(final int i) {
if (aField) {
throw new AssertionError();
}
if (i > 10) {
throw new AssertionError();
}
}
}

static class MultipleAsserts {
public int state;

public void foo(final int i) {
assert (i != 11);
System.out.println("before");
assert ((i + 20) > 10);
System.out.println("middle");
assert (i != 10);
System.out.println("after");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,30 @@ public class Verifier {
private final Sample sample;
private final MutationInterceptor interceptor;
private final Mutater mutator;
private final Predicate<MutationDetails> match;


public Verifier(Sample sample, MutationInterceptor interceptor, Mutater m) {
this(sample,interceptor, m, mut -> true);
}

public Verifier(Sample sample, MutationInterceptor interceptor, Mutater m, Predicate<MutationDetails> match) {
this.sample = sample;
this.interceptor = interceptor;
this.mutator = m;
this.match = match;
}

public Verifier forMethod(String name) {
return new Verifier(sample, interceptor, mutator, match.and(m -> m.getMethod().equals(name)));
}

public MutantVerifier forAnyCode() {
return forMutantsMatching(m -> true);
}

public MutantVerifier forCodeMatching(Predicate<AbstractInsnNode> match) {
return forMutantsMatching(mutates(match, sample));
return forMutantsMatching(mutates(match, sample).and(this.match));
}

public Collection<MutationDetails> findMutants() {
Expand All @@ -43,7 +54,7 @@ public MutantVerifier forMutantsMatching(Predicate<MutationDetails> match) {
List<MutationDetails> filtered = new ArrayList<>(mutations);
filtered.removeAll(afterFiltering);

return new MutantVerifier(sample, match, mutations, afterFiltering, filtered);
return new MutantVerifier(sample, this.match.and(match), mutations, afterFiltering, filtered);
}

private Collection<MutationDetails> filter(ClassTree clazz,
Expand Down

This file was deleted.

Loading