Skip to content

Commit

Permalink
Filter mutants returning Boolean.TRUE & Bollean.FALSE for #957
Browse files Browse the repository at this point in the history
  • Loading branch information
Henry Coles committed Nov 8, 2021
1 parent a6388a7 commit ed97a44
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 26 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Read all about it at http://pitest.org

* #952 Mutate map return to `emptyMap` instead of null
* #954 Allow mutators to be excluded
* #957 Filter equivalent mutations to Boolean.TRUE and Boolean.FALSE

### 1.7.2

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FrameNode;
import org.objectweb.asm.tree.IincInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
Expand Down Expand Up @@ -157,6 +158,16 @@ public static Match<AbstractInsnNode> isInstruction(final SlotRead<AbstractInsn
return (c, t) -> c.retrieve(target).get() == t;
}

public static Match<AbstractInsnNode> getStatic(String owner, String field) {
return (c, t) -> {
if (t instanceof FieldInsnNode) {
FieldInsnNode fieldNode = (FieldInsnNode) t;
return t.getOpcode() == Opcodes.GETSTATIC && fieldNode.name.equals(field) && fieldNode.owner.equals(owner);
}
return false;
};
}

/**
* Records if a instruction matches the target, but always returns true
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.pitest.mutationtest.build.intercept.equivalent;

import static org.pitest.bytecode.analysis.InstructionMatchers.anyInstruction;
import static org.pitest.bytecode.analysis.InstructionMatchers.getStatic;
import static org.pitest.bytecode.analysis.InstructionMatchers.isInstruction;
import static org.pitest.bytecode.analysis.InstructionMatchers.methodCallNamed;
import static org.pitest.bytecode.analysis.InstructionMatchers.notAnInstruction;
Expand Down Expand Up @@ -40,6 +41,7 @@
import org.pitest.sequence.QueryParams;
import org.pitest.sequence.QueryStart;
import org.pitest.sequence.SequenceMatcher;
import org.pitest.sequence.SequenceQuery;
import org.pitest.sequence.Slot;

/**
Expand Down Expand Up @@ -80,16 +82,22 @@ public InterceptorType type() {

class HardCodedTrueEquivalentFilter implements MutationInterceptor {
private static final Slot<AbstractInsnNode> MUTATED_INSTRUCTION = Slot.create(AbstractInsnNode.class);

static final SequenceMatcher<AbstractInsnNode> BOXED_TRUE = QueryStart
.any(AbstractInsnNode.class)
.zeroOrMore(QueryStart.match(anyInstruction()))
.then(opCode(Opcodes.ICONST_1))
.then(methodCallNamed("valueOf"))
.then(isInstruction(MUTATED_INSTRUCTION.read()))
.zeroOrMore(QueryStart.match(anyInstruction()))
.compile(QueryParams.params(AbstractInsnNode.class)
.withIgnores(notAnInstruction())

static final SequenceQuery<AbstractInsnNode> BOXED_TRUE = QueryStart
.match(opCode(Opcodes.ICONST_1))
.then(methodCallNamed("valueOf"));

static final SequenceQuery<AbstractInsnNode> CONSTANT_TRUE = QueryStart
.match(getStatic("java/lang/Boolean","TRUE"));

static final SequenceMatcher<AbstractInsnNode> EQUIVALENT_TRUE = QueryStart
.any(AbstractInsnNode.class)
.zeroOrMore(QueryStart.match(anyInstruction()))
.then(BOXED_TRUE.or(CONSTANT_TRUE))
.then(isInstruction(MUTATED_INSTRUCTION.read()))
.zeroOrMore(QueryStart.match(anyInstruction()))
.compile(QueryParams.params(AbstractInsnNode.class)
.withIgnores(notAnInstruction())
);

private static final Set<String> MUTATOR_IDS = new HashSet<>();
Expand Down Expand Up @@ -137,7 +145,7 @@ private boolean primitiveTrue(int instruction, MethodTree method) {
private boolean boxedTrue(int instruction, MethodTree method) {
final Context<AbstractInsnNode> context = Context.start(method.instructions(), false);
context.store(MUTATED_INSTRUCTION.write(), method.instruction(instruction));
return BOXED_TRUE.matches(method.instructions(), context);
return EQUIVALENT_TRUE.matches(method.instructions(), context);
}
};
}
Expand All @@ -146,6 +154,7 @@ private boolean boxedTrue(int instruction, MethodTree method) {
public void end() {
this.currentClass = null;
}

}


Expand Down Expand Up @@ -201,21 +210,32 @@ public void end() {

}

/**
* Handles methods already returning a 0 value, and also
* those returning Boolean.FALSE
*/
class EmptyReturnsFilter implements MutationInterceptor {

private static final Slot<AbstractInsnNode> MUTATED_INSTRUCTION = Slot.create(AbstractInsnNode.class);

private static final SequenceMatcher<AbstractInsnNode> CONSTANT_ZERO = QueryStart
.any(AbstractInsnNode.class)
.zeroOrMore(QueryStart.match(anyInstruction()))
.then(isZeroConstant())
.then(methodCallNamed("valueOf"))
.then(isInstruction(MUTATED_INSTRUCTION.read()))
.zeroOrMore(QueryStart.match(anyInstruction()))
.compile(QueryParams.params(AbstractInsnNode.class)
.withIgnores(notAnInstruction())
);


static final SequenceQuery<AbstractInsnNode> CONSTANT_ZERO = QueryStart
.match(isZeroConstant())
.then(methodCallNamed("valueOf"));

static final SequenceQuery<AbstractInsnNode> CONSTANT_FALSE = QueryStart
.match(getStatic("java/lang/Boolean","FALSE"));

static final SequenceMatcher<AbstractInsnNode> ZERO_VALUES = QueryStart
.any(AbstractInsnNode.class)
.zeroOrMore(QueryStart.match(anyInstruction()))
.then(CONSTANT_ZERO.or(CONSTANT_FALSE))
.then(isInstruction(MUTATED_INSTRUCTION.read()))
.zeroOrMore(QueryStart.match(anyInstruction()))
.compile(QueryParams.params(AbstractInsnNode.class)
.withIgnores(notAnInstruction())
);


private static final Set<String> MUTATOR_IDS = new HashSet<>();
private static final Set<Integer> ZERO_CONSTANTS = new HashSet<>();
static {
Expand Down Expand Up @@ -278,7 +298,7 @@ private Boolean returnsZeroValue(MethodTree method,
int mutatedInstruction) {
final Context<AbstractInsnNode> context = Context.start(method.instructions(), false);
context.store(MUTATED_INSTRUCTION.write(), method.instruction(mutatedInstruction));
return CONSTANT_ZERO.matches(method.instructions(), context);
return ZERO_VALUES.matches(method.instructions(), context);
}

private boolean returns(MethodTree method, int mutatedInstruction, String owner, String name) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ public void filtersEquivalentPrimitiveDoubleMutants() {

@Test
public void filtersEquivalentBoxedBooleanMutants() {
this.verifier.assertFiltersNMutationFromClass(1, AlreadyReturnsBoxedFalse.class);
this.verifier.assertFiltersMutationsFromMutator(FALSE_RETURNS.getGloballyUniqueId()
, AlreadyReturnsBoxedFalse.class);
}

@Test
Expand All @@ -99,6 +100,18 @@ public void filtersEquivalentBoxedBooleanTrueMutants() {
, AlreadyReturnsBoxedTrue.class);
}

@Test
public void filtersEquivalentConstantTrueMutants() {
this.verifier.assertFiltersMutationsFromMutator(TRUE_RETURNS.getGloballyUniqueId()
, ReturnsConstantTrue.class);
}

@Test
public void filtersEquivalentConstantFalseMutants() {
this.verifier.assertFiltersMutationsFromMutator(FALSE_RETURNS.getGloballyUniqueId()
, ReturnsConstantFalse.class);
}

@Test
public void filtersEquivalentBoxedIntegerMutants() {
this.verifier.assertFiltersNMutationFromClass(1, AlreadyReturnsBoxedZeroInteger.class);
Expand Down Expand Up @@ -250,6 +263,19 @@ public Integer a() {
}
}

class ReturnsConstantTrue {
public Boolean a() {
return Boolean.TRUE;
}
}

class ReturnsConstantFalse {
public Boolean a() {
return Boolean.FALSE;
}
}


class CallsAnIntegerReturningStaticWith0 {

static Integer foo(int a) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ public void assertFiltersMutationsFromMutator(String id, Class<?> clazz) {

final List<MutationDetails> filteredOut = FCollection.filter(mutations, notIn(actual));

softly.assertThat(filteredOut).describedAs("No mutants filtered").isNotEmpty();
softly.assertThat(filteredOut).describedAs("No mutants filtered " + s).isNotEmpty();
softly.assertThat(filteredOut).have(mutatedBy(id));
softly.assertAll();

Expand Down Expand Up @@ -354,4 +354,9 @@ class Sample {
ClassName className;
String compiler;
ClassTree clazz;

@Override
public String toString() {
return "Compiled by " + compiler + "\n" + clazz.toString();
}
}

0 comments on commit ed97a44

Please sign in to comment.