-
Notifications
You must be signed in to change notification settings - Fork 353
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
filter equivalent divide by minus one mutants
Filter out equivalent mutants of the form (a + b) * -1 -> (a + b) / -1 Does not filter the opposite as, although it is also equivalent, there is no good reason to express conversion to a negative as a division operation over the more common multiplication.
- Loading branch information
Showing
7 changed files
with
261 additions
and
36 deletions.
There are no files selected for viewing
30 changes: 30 additions & 0 deletions
30
...try/src/main/java/org/pitest/mutationtest/build/intercept/MutatorSpecificInterceptor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package org.pitest.mutationtest.build.intercept; | ||
|
||
import org.pitest.mutationtest.engine.Mutater; | ||
import org.pitest.mutationtest.engine.MutationDetails; | ||
import org.pitest.mutationtest.engine.gregor.MethodMutatorFactory; | ||
|
||
import java.util.Collection; | ||
import java.util.List; | ||
import java.util.function.Predicate; | ||
import java.util.stream.Collectors; | ||
|
||
public abstract class MutatorSpecificInterceptor extends RegionInterceptor { | ||
private final List<MethodMutatorFactory> mutators; | ||
|
||
protected MutatorSpecificInterceptor(List<MethodMutatorFactory> mutators) { | ||
this.mutators = mutators; | ||
} | ||
|
||
@Override | ||
public Collection<MutationDetails> intercept( | ||
Collection<MutationDetails> mutations, Mutater unused) { | ||
return mutations.stream() | ||
.filter(forRelevantMutator().negate().or(buildPredicate().negate())) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
private Predicate<MutationDetails> forRelevantMutator() { | ||
return md -> mutators.stream().anyMatch(m -> m.isMutatorFor(md.getId())); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 70 additions & 0 deletions
70
...ain/java/org/pitest/mutationtest/build/intercept/equivalent/DivisionByMinusOneFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package org.pitest.mutationtest.build.intercept.equivalent; | ||
|
||
import org.objectweb.asm.tree.AbstractInsnNode; | ||
import org.objectweb.asm.tree.LdcInsnNode; | ||
import org.pitest.bytecode.analysis.MethodTree; | ||
import org.pitest.mutationtest.build.intercept.MutatorSpecificInterceptor; | ||
import org.pitest.mutationtest.build.intercept.Region; | ||
import org.pitest.mutationtest.engine.gregor.MethodMutatorFactory; | ||
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 java.util.Arrays.asList; | ||
import static org.pitest.bytecode.analysis.InstructionMatchers.anyInstruction; | ||
import static org.pitest.bytecode.analysis.InstructionMatchers.notAnInstruction; | ||
import static org.pitest.bytecode.analysis.OpcodeMatchers.DMUL; | ||
import static org.pitest.bytecode.analysis.OpcodeMatchers.FMUL; | ||
import static org.pitest.bytecode.analysis.OpcodeMatchers.ICONST_M1; | ||
import static org.pitest.bytecode.analysis.OpcodeMatchers.IMUL; | ||
import static org.pitest.bytecode.analysis.OpcodeMatchers.LMUL; | ||
import static org.pitest.sequence.Result.result; | ||
|
||
/** | ||
* Filters equivalent mutations of the form | ||
* | ||
* (a + b) * -1 -> (a + b) / -1 | ||
* | ||
*/ | ||
class DivisionByMinusOneFilter extends MutatorSpecificInterceptor { | ||
|
||
static final Slot<AbstractInsnNode> AVOID = Slot.create(AbstractInsnNode.class); | ||
|
||
static final SequenceMatcher<AbstractInsnNode> DIVISION_BY_1 = QueryStart | ||
.any(AbstractInsnNode.class) | ||
.then(ICONST_M1.or(loads(-1L)).or(loads(-1f)).or(loads(-1d))) | ||
.then(IMUL.or(LMUL.or(FMUL).or(DMUL)).and(store(AVOID.write()))) | ||
.zeroOrMore(QueryStart.match(anyInstruction())) | ||
.compile(QueryParams.params(AbstractInsnNode.class) | ||
.withIgnores(notAnInstruction()) | ||
); | ||
|
||
DivisionByMinusOneFilter(MethodMutatorFactory... mutators) { | ||
super(asList(mutators)); | ||
} | ||
|
||
|
||
private static Match<AbstractInsnNode> loads(Object l) { | ||
return (c,n) -> | ||
result(n instanceof LdcInsnNode && ((LdcInsnNode) n).cst.equals(l), c); | ||
} | ||
private static Match<AbstractInsnNode> store(SlotWrite<AbstractInsnNode> slot) { | ||
return (c, n) -> result(true, c.store(slot, n)); | ||
} | ||
|
||
@Override | ||
protected List<Region> computeRegions(MethodTree method) { | ||
Context context = Context.start(); | ||
return DIVISION_BY_1.contextMatches(method.instructions(), context).stream() | ||
.map(c -> new Region(c.retrieve(AVOID.read()).get(), c.retrieve(AVOID.read()).get())) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
} |
28 changes: 28 additions & 0 deletions
28
...a/org/pitest/mutationtest/build/intercept/equivalent/DivisionByMinusOneFilterFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package org.pitest.mutationtest.build.intercept.equivalent; | ||
|
||
import org.pitest.mutationtest.build.InterceptorParameters; | ||
import org.pitest.mutationtest.build.MutationInterceptor; | ||
import org.pitest.mutationtest.build.MutationInterceptorFactory; | ||
import org.pitest.mutationtest.engine.gregor.mutators.MathMutator; | ||
import org.pitest.plugin.Feature; | ||
|
||
public class DivisionByMinusOneFilterFactory implements MutationInterceptorFactory { | ||
|
||
@Override | ||
public String description() { | ||
return "Division by one equivalent mutant filter"; | ||
} | ||
|
||
@Override | ||
public Feature provides() { | ||
return Feature.named("FSEQUIVDIV") | ||
.withOnByDefault(true) | ||
.withDescription("Filters equivalent mutations of the form x * -1 -> x / -1"); | ||
} | ||
|
||
@Override | ||
public MutationInterceptor createInterceptor(InterceptorParameters params) { | ||
return new DivisionByMinusOneFilter(MathMutator.MATH); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
126 changes: 126 additions & 0 deletions
126
...g/pitest/mutationtest/build/intercept/equivalent/DivisionByMinusOneFilterFactoryTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package org.pitest.mutationtest.build.intercept.equivalent; | ||
|
||
import org.junit.Test; | ||
import org.pitest.bytecode.analysis.OpcodeMatchers; | ||
import org.pitest.mutationtest.engine.gregor.mutators.MathMutator; | ||
import org.pitest.mutationtest.engine.gregor.mutators.NullMutateEverything; | ||
import org.pitest.verifier.interceptors.FactoryVerifier; | ||
import org.pitest.verifier.interceptors.InterceptorVerifier; | ||
import org.pitest.verifier.interceptors.VerifierStart; | ||
|
||
public class DivisionByMinusOneFilterFactoryTest { | ||
|
||
DivisionByMinusOneFilterFactory underTest = new DivisionByMinusOneFilterFactory(); | ||
|
||
InterceptorVerifier v = VerifierStart.forInterceptorFactory(underTest) | ||
.usingMutator(MathMutator.MATH); | ||
|
||
@Test | ||
public void isOnChain() { | ||
FactoryVerifier.confirmFactory(underTest) | ||
.isOnChain(); | ||
} | ||
|
||
@Test | ||
public void doesNotFilterNonEquivalentMutants() { | ||
v.forClass(NonEquivalentMultiplication.class) | ||
.forAnyCode() | ||
.mutantsAreGenerated() | ||
.noMutantsAreFiltered() | ||
.verify(); | ||
} | ||
|
||
@Test | ||
public void doesNotFilterMutantsFromOtherMutators() { | ||
VerifierStart.forInterceptorFactory(underTest) | ||
.usingMutator(new NullMutateEverything()) | ||
.forClass(EquivalentMultiplicationByMinus1.class) | ||
.forAnyCode() | ||
.mutantsAreGenerated() | ||
.noMutantsAreFiltered() | ||
.verify(); | ||
} | ||
|
||
@Test | ||
public void filtersMutationsToIMulNegative1() { | ||
v.forClass(EquivalentBoxedMultiplicationByMinus1.class) | ||
.forCodeMatching(OpcodeMatchers.IMUL.asPredicate()) | ||
.mutantsAreGenerated() | ||
.allMutantsAreFiltered() | ||
.verify(); | ||
} | ||
|
||
@Test | ||
public void filtersMutationsToBoxedIMulNegative1() { | ||
v.forClass(EquivalentBoxedMultiplicationByMinus1.class) | ||
.forCodeMatching(OpcodeMatchers.IMUL.asPredicate()) | ||
.mutantsAreGenerated() | ||
.allMutantsAreFiltered() | ||
.verify(); | ||
} | ||
|
||
@Test | ||
public void filtersMutationsToLMulNegative1() { | ||
v.forClass(EquivalentLongMultiplicationByMinus1.class) | ||
.forCodeMatching(OpcodeMatchers.LMUL.asPredicate()) | ||
.mutantsAreGenerated() | ||
.allMutantsAreFiltered() | ||
.verify(); | ||
} | ||
|
||
@Test | ||
public void filtersMutationsToFMulNegative1() { | ||
v.forClass(EquivalentFloatMultiplicationByMinus1.class) | ||
.forCodeMatching(OpcodeMatchers.FMUL.asPredicate()) | ||
.mutantsAreGenerated() | ||
.allMutantsAreFiltered() | ||
.verify(); | ||
} | ||
|
||
@Test | ||
public void filtersMutationsToDMulNegative1() { | ||
v.forClass(EquivalentDoubleMultiplicationByMinus1.class) | ||
.forCodeMatching(OpcodeMatchers.DMUL.asPredicate()) | ||
.mutantsAreGenerated() | ||
.allMutantsAreFiltered() | ||
.verify(); | ||
} | ||
|
||
} | ||
|
||
|
||
class NonEquivalentMultiplication { | ||
public int mutateMe(int a, int b) { | ||
return (a + b) * -2; | ||
} | ||
} | ||
|
||
class EquivalentMultiplicationByMinus1 { | ||
public int mutateMe(int a, int b) { | ||
return (a + b) * -1; | ||
} | ||
} | ||
|
||
class EquivalentBoxedMultiplicationByMinus1 { | ||
public Integer mutateMe(Integer a, Integer b) { | ||
return (a + b) * -1; | ||
} | ||
} | ||
|
||
class EquivalentLongMultiplicationByMinus1 { | ||
public long mutateMe(long a, long b) { | ||
return (a + b) * -1; | ||
} | ||
} | ||
|
||
class EquivalentFloatMultiplicationByMinus1 { | ||
public float mutateMe(float a, float b) { | ||
return (a + b) * -1; | ||
} | ||
} | ||
|
||
class EquivalentDoubleMultiplicationByMinus1 { | ||
public double mutateMe(double a, double b) { | ||
return (a + b) * -1; | ||
} | ||
} |