Skip to content

Commit

Permalink
move assert filtering to interceptor
Browse files Browse the repository at this point in the history
  • Loading branch information
Henry Coles committed Jun 9, 2022
1 parent 6424f24 commit aab7c35
Show file tree
Hide file tree
Showing 9 changed files with 211 additions and 248 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package org.pitest.mutationtest.build.intercept.javafeatures;

import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.pitest.bytecode.analysis.ClassTree;
import org.pitest.bytecode.analysis.MethodTree;
import org.pitest.classinfo.ClassName;
import org.pitest.mutationtest.build.InterceptorType;
import org.pitest.mutationtest.build.MutationInterceptor;
import org.pitest.mutationtest.build.intercept.Region;
import org.pitest.mutationtest.build.intercept.RegionInterceptor;
import org.pitest.mutationtest.engine.Mutater;
import org.pitest.mutationtest.engine.MutationDetails;
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,99 @@
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(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();
}
}
}
}
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.

Original file line number Diff line number Diff line change
Expand Up @@ -105,18 +105,13 @@ private MethodVisitor visitMethodForMutation(
next = each.create(methodContext, methodInfo, next);
}

return wrapWithDecorators(methodContext, wrapWithAssertFilter(methodContext, next), methodInfo);
return wrapWithDecorators(methodContext, next, methodInfo);
}

private static MethodVisitor wrapWithLineTracker(
MethodMutationContext methodContext, final MethodVisitor mv) {
return new LineTrackingMethodVisitor(methodContext, mv);
}

private static MethodVisitor wrapWithAssertFilter(
MethodMutationContext methodContext,
final MethodVisitor wrappedMethodVisitor) {
return new AvoidAssertsMethodAdapter(methodContext, wrappedMethodVisitor);
}

}
Loading

0 comments on commit aab7c35

Please sign in to comment.