Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return survived mutations as mapping of lines to a list of mutations #16

Merged
merged 5 commits into from
Apr 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>