Skip to content

Commit

Permalink
Merge pull request #16 from jenkinsci/survived-mutations
Browse files Browse the repository at this point in the history
Return survived mutations as mapping of lines to a list of mutations
  • Loading branch information
uhafner committed Apr 26, 2023
2 parents a36c6fc + 6694846 commit b169d28
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 188 deletions.
28 changes: 21 additions & 7 deletions src/main/java/edu/hm/hafner/coverage/FileNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
*
* @author Ullrich Hafner
*/
@SuppressWarnings("PMD.GodClass")
@SuppressWarnings({"PMD.GodClass", "PMD.CyclomaticComplexity"})
public final class FileNode extends Node {
private static final long serialVersionUID = -3795695377267542624L; // Set to 1 when release 1.0.0 is ready

Expand Down Expand Up @@ -73,7 +73,7 @@ public FileNode(final String name, final String relativePath) {
*
* @return this
*/
protected Object readResolve() {
private Object readResolve() {
if (relativePath == null) {
relativePath = TreeString.valueOf(StringUtils.EMPTY);
}
Expand Down Expand Up @@ -466,15 +466,29 @@ public NavigableSet<Integer> getMissedLines() {
}

/**
* Returns the lines that contain survived mutations. The returned map contains the line number as the key and the
* number of survived mutations as value.
* Returns the lines that contain survived mutations. The returned map contains the line number as the key and a
* list of survived mutations as value.
*
* @return the lines that have no line coverage
*/
public NavigableMap<Integer, Integer> getSurvivedMutations() {
public NavigableMap<Integer, List<Mutation>> getSurvivedMutationsPerLine() {
return createMapOfMutations(Mutation::hasSurvived);
}

/**
* Returns the lines that contain mutations. The returned map contains the line number as the key and a
* list of mutations as value.
*
* @return the lines that have no line coverage
*/
public NavigableMap<Integer, List<Mutation>> getMutationsPerLine() {
return createMapOfMutations(b -> true);
}

private NavigableMap<Integer, List<Mutation>> createMapOfMutations(final Predicate<Mutation> predicate) {
return getMutations().stream()
.filter(Mutation::hasSurvived)
.collect(Collectors.groupingBy(Mutation::getLine, TreeMap::new, Collectors.summingInt(a -> 1)));
.filter(predicate)
.collect(Collectors.groupingBy(Mutation::getLine, TreeMap::new, Collectors.toList()));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ else if (event.isEndElement()) {
}
}

@SuppressWarnings("PMD.CyclomaticComplexity") // There are a lot of properties to read
@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.CognitiveComplexity"}) // There are a lot of properties to read
private void readProperty(final XMLEventReader reader, final MutationBuilder builder)
throws XMLStreamException {
var aggregatedContent = new StringBuilder();
Expand All @@ -134,6 +134,9 @@ private void readProperty(final XMLEventReader reader, final MutationBuilder bui
if (event.isCharacters()) {
aggregatedContent.append(event.asCharacters().getData());
}
else if (event.isStartElement()) {
readProperty(reader, builder); // sometimes properties are wrapped by another container element
}
else if (event.isEndElement()) {
var content = StringUtils.defaultString(StringUtils.strip(aggregatedContent.toString()));
var name = event.asEndElement().getName();
Expand Down
177 changes: 0 additions & 177 deletions src/test/java/edu/hm/hafner/ArchitectureRules.java

This file was deleted.

2 changes: 2 additions & 0 deletions src/test/java/edu/hm/hafner/ArchitectureTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchRule;

import edu.hm.hafner.util.ArchitectureRules;

/**
* Defines several architecture rules for the code coverage model.
*
Expand Down
53 changes: 50 additions & 3 deletions src/test/java/edu/hm/hafner/coverage/parser/PitestParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,38 @@ CoverageParser createParser() {
return new PitestParser();
}

@Test
void shouldReadAllMutationProperties() {
ModuleNode tree = readReport("mutation.xml");
assertThat(tree.getAllFileNodes()).first()
.satisfies(file -> assertThat(file.getMutations())
.first().satisfies(mutation ->
assertThat(mutation).isDetected()
.hasMethod("add")
.hasSignature("(Ledu/hm/hafner/coverage/CoverageNode;)V")
.hasLine(175)
.hasMutator("NotExisting")
.hasKillingTest("edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldAddChildren()]")
.hasDescription("removed call to edu/hm/hafner/coverage/CoverageNode::setParent")
));
}

@Test
void shouldReadAllMutationPropertiesEvenIfXmlContainsBlocksAndIndexes() {
ModuleNode tree = readReport("mutation-with-blocks-and-indexes.xml");
assertThat(tree.getAllFileNodes()).first()
.satisfies(file -> assertThat(file.getMutations())
.first().satisfies(mutation ->
assertThat(mutation).isNotDetected()
.hasMethod("getFileName")
.hasSignature("()Ljava/lang/String;")
.hasLine(5555)
.hasMutator("org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator")
.hasKillingTest("")
.hasDescription("replaced return value with \"\" for edu/hm/hafner/util/LookaheadStream::getFileName")
));
}

@Test
void shouldMapLineCoveragesForPainting() {
ModuleNode tree = readReport("mutations-codingstyle.xml");
Expand Down Expand Up @@ -53,8 +85,9 @@ private void verifyEnsure(final FileNode file) {
.map(Mutation::getLine).sorted())
.containsExactly(238, 303, 340, 476, 620, 651);
assertThat(file.getPartiallyCoveredLines()).isEmpty();
assertThat(file.getSurvivedMutations()).containsExactly(
entry(238, 1), entry(303, 1), entry(340, 1), entry(476, 1), entry(620, 1), entry(651, 1));
assertThat(file.getSurvivedMutationsPerLine()).containsOnlyKeys(
238, 303, 340, 476, 620, 651);
assertThat(file.getMutationsPerLine()).hasSize(66);
assertThat(file.getMissedLines()).containsExactly(81, 248, 486, 631);
assertThat(file.getCoveredCounters()).containsExactly(1, 1,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
Expand Down Expand Up @@ -84,10 +117,24 @@ private void verifyLookaheadStream(final FileNode file) {
50, 55, 65, 77, 78, 79, 81, 84, 96, 97, 99, 115, 117, 119, 121, 130);
assertThat(file.getCoveredCounters()).containsOnly(1).hasSize(16);
assertThat(file.getMissedCounters()).containsOnly(0).hasSize(16);
assertThat(file.getSurvivedMutations()).containsExactly(entry(50, 1));
assertThat(file.getSurvivedMutationsPerLine()).containsOnlyKeys(50);
assertThat(file.getSurvivedMutationsPerLine().values()).hasSize(1)
.first().asList()
.hasSize(1).first()
.isInstanceOfSatisfying(Mutation.class, this::verifyMutation);
assertThat(file.getMissedLines()).isEmpty();
}

private void verifyMutation(final Mutation mutation) {
assertThat(mutation).hasSurvived()
.hasKillingTest("")
.hasMethod("getFileName")
.hasSignature("()Ljava/lang/String;")
.hasMutator("org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator")
.hasDescription("replaced return value with \"\" for edu/hm/hafner/util/LookaheadStream::getFileName")
.hasLine(50);
}

@Test
void shouldConvertMutationsToTree() {
ModuleNode tree = readReport("mutations.xml");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<mutations>
<mutation detected='false' status='SURVIVED' numberOfTestsRun='1'>
<sourceFile>LookaheadStream.java</sourceFile>
<mutatedClass>edu.hm.hafner.util.LookaheadStream</mutatedClass>
<mutatedMethod>getFileName</mutatedMethod>
<methodDescription>()Ljava/lang/String;</methodDescription>
<lineNumber>5555</lineNumber>
<mutator>org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator</mutator>
<indexes>
<index>5</index>
</indexes>
<blocks>
<block>0</block>
</blocks>
<killingTest/>
<description>replaced return value with &quot;&quot; for edu/hm/hafner/util/LookaheadStream::getFileName
</description>
</mutation>
</mutations>
17 changes: 17 additions & 0 deletions src/test/resources/edu/hm/hafner/coverage/parser/mutation.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<mutations>
<mutation detected='true' status='KILLED' numberOfTestsRun='5'>
<sourceFile>CoverageNode.java</sourceFile>
<mutatedClass>edu.hm.hafner.coverage.CoverageNode</mutatedClass>
<mutatedMethod>add</mutatedMethod>
<methodDescription>(Ledu/hm/hafner/coverage/CoverageNode;)V</methodDescription>
<lineNumber>175</lineNumber>
<mutator>NotExisting</mutator>
<index>12</index>
<block>1</block>
<killingTest>
edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldAddChildren()]
</killingTest>
<description>removed call to edu/hm/hafner/coverage/CoverageNode::setParent</description>
</mutation>
</mutations>

0 comments on commit b169d28

Please sign in to comment.