-
Notifications
You must be signed in to change notification settings - Fork 357
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
filter mutations to defensive unmodifiable collection returns
- Loading branch information
Showing
6 changed files
with
183 additions
and
2 deletions.
There are no files selected for viewing
57 changes: 57 additions & 0 deletions
57
.../java/org/pitest/mutationtest/build/intercept/defensive/ReturnUnmodifiableCollection.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,57 @@ | ||
package org.pitest.mutationtest.build.intercept.defensive; | ||
|
||
import org.objectweb.asm.tree.AbstractInsnNode; | ||
import org.objectweb.asm.tree.LabelNode; | ||
import org.pitest.bytecode.analysis.MethodTree; | ||
import org.pitest.classinfo.ClassName; | ||
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.Collections; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
import static org.pitest.bytecode.analysis.InstructionMatchers.anyInstruction; | ||
import static org.pitest.bytecode.analysis.InstructionMatchers.isA; | ||
import static org.pitest.bytecode.analysis.InstructionMatchers.methodCallTo; | ||
import static org.pitest.bytecode.analysis.InstructionMatchers.notAnInstruction; | ||
import static org.pitest.bytecode.analysis.OpcodeMatchers.ARETURN; | ||
import static org.pitest.bytecode.analysis.OpcodeMatchers.INVOKESTATIC; | ||
import static org.pitest.sequence.Result.result; | ||
|
||
public class ReturnUnmodifiableCollection extends RegionInterceptor { | ||
|
||
private static final ClassName COLLECTIONS = ClassName.fromClass(Collections.class); | ||
|
||
static final Slot<AbstractInsnNode> MUTATED_INSTRUCTION = Slot.create(AbstractInsnNode.class); | ||
|
||
static final SequenceMatcher<AbstractInsnNode> DEFENSIVE_RETURN = QueryStart | ||
.any(AbstractInsnNode.class) | ||
.then(INVOKESTATIC.and(methodCallTo(COLLECTIONS, "unmodifiableSet")).and(store(MUTATED_INSTRUCTION.write()))) | ||
.then(ARETURN) | ||
.zeroOrMore(QueryStart.match(anyInstruction())) | ||
.compile(QueryParams.params(AbstractInsnNode.class) | ||
.withIgnores(notAnInstruction().or(isA(LabelNode.class))) | ||
); | ||
|
||
|
||
@Override | ||
protected List<Region> computeRegions(MethodTree method) { | ||
Context context = Context.start(); | ||
return DEFENSIVE_RETURN.contextMatches(method.instructions(), context).stream() | ||
.map(c -> c.retrieve(MUTATED_INSTRUCTION.read()).get()) | ||
.map(n -> new Region(n, n)) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
private static Match<AbstractInsnNode> store(SlotWrite<AbstractInsnNode> slot) { | ||
return (c,n) -> result(true, c.store(slot, n)); | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
...rg/pitest/mutationtest/build/intercept/defensive/ReturnUnmodifiableCollectionFactory.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,25 @@ | ||
package org.pitest.mutationtest.build.intercept.defensive; | ||
|
||
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 ReturnUnmodifiableCollectionFactory implements MutationInterceptorFactory { | ||
@Override | ||
public MutationInterceptor createInterceptor(InterceptorParameters params) { | ||
return new ReturnUnmodifiableCollection(); | ||
} | ||
|
||
@Override | ||
public Feature provides() { | ||
return Feature.named("DEFENSIVERETURN") | ||
.withOnByDefault(true) | ||
.withDescription(description()); | ||
} | ||
|
||
@Override | ||
public String description() { | ||
return "Filter mutations to defensive return wrappers such as unmodifiableCollection"; | ||
} | ||
} |
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
97 changes: 97 additions & 0 deletions
97
...itest/mutationtest/build/intercept/defensive/ReturnUnmodifiableCollectionFactoryTest.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,97 @@ | ||
package org.pitest.mutationtest.build.intercept.defensive; | ||
|
||
import org.junit.Test; | ||
import org.pitest.mutationtest.build.InterceptorType; | ||
import org.pitest.mutationtest.build.MutationInterceptorFactory; | ||
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; | ||
|
||
import java.util.Collections; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
import static org.pitest.bytecode.analysis.OpcodeMatchers.INVOKESTATIC; | ||
|
||
|
||
public class ReturnUnmodifiableCollectionFactoryTest { | ||
private final MutationInterceptorFactory underTest = new ReturnUnmodifiableCollectionFactory(); | ||
InterceptorVerifier v = VerifierStart.forInterceptorFactory(underTest) | ||
.usingMutator(new NullMutateEverything()); | ||
|
||
@Test | ||
public void isOnChain() { | ||
FactoryVerifier.confirmFactory(underTest) | ||
.isOnChain(); | ||
} | ||
|
||
@Test | ||
public void isOnByDefault() { | ||
FactoryVerifier.confirmFactory(underTest) | ||
.isOnByDefault(); | ||
} | ||
|
||
@Test | ||
public void featureIsCalledLombok() { | ||
FactoryVerifier.confirmFactory(underTest) | ||
.featureName().isEqualTo("defensivereturn"); | ||
} | ||
|
||
@Test | ||
public void createsFilters() { | ||
FactoryVerifier.confirmFactory(underTest) | ||
.createsInterceptorsOfType(InterceptorType.FILTER); | ||
} | ||
|
||
|
||
@Test | ||
public void filtersMutationsToReturnUnmodifiableSet() { | ||
v.forClass(HasUnmodifiableSetReturn.class) | ||
.forCodeMatching(INVOKESTATIC.asPredicate()) | ||
.allMutantsAreFiltered() | ||
.verify(); | ||
} | ||
|
||
@Test | ||
public void doesNotFilterOtherCode() { | ||
v.forClass(HasUnmodifiableSetReturn.class) | ||
.forCodeMatching(INVOKESTATIC.asPredicate().negate()) | ||
.noMutantsAreFiltered() | ||
.verify(); | ||
} | ||
|
||
@Test | ||
public void doesNotFilterOtherCallsToUnModifiableSet() { | ||
v.forClass(HasUnmodifiableSetNonReturn.class) | ||
.forAnyCode() | ||
.noMutantsAreFiltered() | ||
.verify(); | ||
} | ||
} | ||
|
||
class HasUnmodifiableSetReturn { | ||
private final Set<String> s = new HashSet<>(); | ||
|
||
public Set<String> mutateMe(int i) { | ||
if (i != 1) { | ||
return Collections.unmodifiableSet(s); | ||
} | ||
|
||
return s; | ||
} | ||
} | ||
|
||
class HasUnmodifiableSetNonReturn { | ||
private final Set<String> s = new HashSet<>(); | ||
private Set<String> copy; | ||
|
||
|
||
public Set<String> dontMutateME(int i) { | ||
if (i != 1) { | ||
copy = Collections.unmodifiableSet(s); | ||
} | ||
|
||
return s; | ||
} | ||
} |
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