Skip to content

Commit

Permalink
Refactor: coverage map into dedicated classes (#986)
Browse files Browse the repository at this point in the history
* refactor: use now dedicated object instead of map of map of map...

* test: update the test with the new API

* feat: add API to get hit counts
  • Loading branch information
danglotb authored Jun 23, 2021
1 parent 4fdcfe4 commit 1b072a8
Show file tree
Hide file tree
Showing 17 changed files with 491 additions and 185 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
import java.util.BitSet;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
Expand Down Expand Up @@ -294,11 +293,10 @@ private boolean isSetForThisTest(Map<TestCaseInfo, BitSet> m, int i) {
TODO Implement our own reporter, that compute this information, and do not build the whole HTML report
TODO which is unused in DSpot
*/

final JSONObject jsonTestTargets = JSONObjectFactory.getJSONTestTargets(targetMethods, targetElements);
final String targetClassName = this.fileInfo.getContainingPackage().getName() + "." + this.fileInfo.getName().split("\\.")[0];
if (!CloverReader.coveragePerTestMethods.containsKey(targetClassName)) {
this.buildCoverage(sublist, jsonTestTargets, targetClassName);
}
this.buildCoverage(sublist, jsonTestTargets, targetClassName);
velocity.put("jsonTestTargets", jsonTestTargets);
velocity.put("jsonPageData", JSONObjectFactory.getJSONPageData(fileInfo));

Expand All @@ -318,22 +316,33 @@ private void buildCoverage(List<Map.Entry<TestCaseInfo, BlockMetrics>> sublist,
final TestCaseInfo testCaseInfo = getTestCaseInfo(sublist, key.split("_")[1]);
final String testClassName = testCaseInfo.getRuntimeTypeName();
final String testName = testCaseInfo.getTestName();
if (!CloverReader.coveragePerTestMethods.containsKey(testClassName)) {
CloverReader.coveragePerTestMethods.put(testClassName, new HashMap<>());
}
if (!CloverReader.coveragePerTestMethods.get(testClassName).containsKey(testName)) {
CloverReader.coveragePerTestMethods.get(testClassName).put(testName, new HashMap<>());
}
if (!CloverReader.coveragePerTestMethods.get(testClassName).get(testName).containsKey(targetClassName)) {
CloverReader.coveragePerTestMethods.get(testClassName).get(testName).put(targetClassName, new ArrayList<>());
}
JSONObject currentValues = jsonTestTargets.getJSONObject((String) key);
((List) currentValues.get("statements")).stream()
.map(list -> ((Map) list).get("sl"))
.forEach(line ->
CloverReader.coveragePerTestMethods.get(testClassName).get(testName).get(targetClassName).add((Integer) line)
);

.forEach(line -> {
try {
final int hitCount = this.fileInfo.getNamedClass(this.fileInfo.getName().split("\\.")[0])
.getAllMethods()
.stream()
.flatMap(methodInfo -> methodInfo.getStatements().stream())
.filter(statementInfo -> statementInfo.getStartLine() == (Integer) line)
.findFirst()
.get()
.getHitCount();
CloverReader.coverage.addCoverage(testClassName, testName, targetClassName, (Integer) line, hitCount);
} catch (Exception e) {
e.printStackTrace();
final int hitCount = this.fileInfo.getNamedClass(this.fileInfo.getName().split("\\.")[0])
.getAllMethods()
.stream()
.flatMap(methodInfo -> methodInfo.getStatements().stream())
.filter(statementInfo -> statementInfo.getStartLine() == (Integer) line)
.findFirst()
.get()
.getHitCount();
CloverReader.coverage.addCoverage(testClassName, testName, targetClassName, (Integer) line, hitCount);
}
});
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import eu.stamp_project.diff_test_selection.configuration.Configuration;
import eu.stamp_project.diff_test_selection.configuration.Options;
import eu.stamp_project.diff_test_selection.coverage.Coverage;
import eu.stamp_project.diff_test_selection.coverage.DiffCoverage;
import eu.stamp_project.diff_test_selection.selector.DiffTestSelection;
import eu.stamp_project.diff_test_selection.selector.DiffTestSelectionImpl;
import eu.stamp_project.diff_test_selection.selector.EnhancedDiffTestSelection;
Expand All @@ -28,7 +29,7 @@ public static void main(String[] args) {

public static void run(Configuration configuration) {
final Map<String, Set<String>> selectedTests;
final Coverage coverage = new Coverage();
final DiffCoverage coverage = new DiffCoverage();
if (configuration.enhanced) {
LOGGER.info("Running in enhanced mode...");
selectedTests = enhancedRun(configuration, coverage);
Expand All @@ -39,8 +40,8 @@ public static void run(Configuration configuration) {
output(configuration, coverage, selectedTests);
}

public static Map<String, Set<String>> _run(Configuration configuration, Coverage coverage) {
final Map<String, Map<String, Map<String, List<Integer>>>> cloverCoverage = getCoverage(configuration.pathToFirstVersion);
public static Map<String, Set<String>> _run(Configuration configuration, DiffCoverage coverage) {
final Coverage cloverCoverage = getCoverage(configuration.pathToFirstVersion);
final DiffTestSelection diffTestSelectionImpl = new DiffTestSelectionImpl(
configuration.pathToFirstVersion,
configuration.pathToSecondVersion,
Expand All @@ -51,11 +52,9 @@ public static Map<String, Set<String>> _run(Configuration configuration, Coverag
return diffTestSelectionImpl.selectTests();
}

private static Map<String, Set<String>> enhancedRun(Configuration configuration, Coverage coverage) {
final Map<String, Map<String, Map<String, List<Integer>>>> cloverCoverageV1 =
getCoverage(configuration.pathToFirstVersion);
final Map<String, Map<String, Map<String, List<Integer>>>> cloverCoverageV2 =
getCoverage(configuration.pathToSecondVersion);
private static Map<String, Set<String>> enhancedRun(Configuration configuration, DiffCoverage coverage) {
final Coverage cloverCoverageV1 = getCoverage(configuration.pathToFirstVersion);
final Coverage cloverCoverageV2 = getCoverage(configuration.pathToSecondVersion);
return new EnhancedDiffTestSelection(
configuration.pathToFirstVersion,
configuration.pathToSecondVersion,
Expand All @@ -66,7 +65,7 @@ private static Map<String, Set<String>> enhancedRun(Configuration configuration,
).selectTests();
}

private static void output(Configuration configuration, Coverage coverage, Map<String, Set<String>> selectedTests) {
private static void output(Configuration configuration, DiffCoverage coverage, Map<String, Set<String>> selectedTests) {
LOGGER.info("Saving result in " + configuration.outputPath + " ...");
configuration.reportFormat.instance.report(
configuration.outputPath,
Expand All @@ -75,7 +74,7 @@ private static void output(Configuration configuration, Coverage coverage, Map<S
);
}

private static Map<String, Map<String, Map<String, List<Integer>>>> getCoverage(final String pathToFirstVersion) {
private static Coverage getCoverage(final String pathToFirstVersion) {
LOGGER.info("Computing coverage for " + pathToFirstVersion);
new CloverExecutor().instrumentAndRunTest(pathToFirstVersion);
return new CloverReader().read(pathToFirstVersion);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package eu.stamp_project.diff_test_selection.clover;

import com.atlassian.clover.reporters.html.HtmlReporter;
import eu.stamp_project.diff_test_selection.coverage.Coverage;

import java.io.File;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
* Created by Benjamin DANGLOT
Expand All @@ -20,24 +18,26 @@ public class CloverReader {

private static final String REPORT_DIRECTORY = "/report/";

public volatile static Map<String, Map<String, Map<String, List<Integer>>>> coveragePerTestMethods = new LinkedHashMap<>();
public volatile static Coverage coverage = new Coverage();

/**
* read the database initialize by {@link CloverExecutor}.
*
* @param directory path to the directory of Clover's result
* @return a map, that associate test method names and the map of executed line in each classes
*/
public Map<String, Map<String, Map<String, List<Integer>>>> read(String directory) {
public Coverage read(String directory) {
coverage = new Coverage();
final File rootDirectoryOfCloverFiles = new File(directory + ROOT_DIRECTORY);
System.out.println("Reading Clover data " + rootDirectoryOfCloverFiles.getAbsolutePath());
HtmlReporter.runReport(new String[]{
"-i", rootDirectoryOfCloverFiles.getAbsolutePath() + DATABASE_FILE,
"-o", rootDirectoryOfCloverFiles.getAbsolutePath() + REPORT_DIRECTORY,
"--lineinfo",
"--showinner",
"--showlambda",
});
return coveragePerTestMethods;
return coverage;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.transform.sax.SAXSource;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package eu.stamp_project.diff_test_selection.coverage;

import java.util.ArrayList;
import java.util.List;

/**
* @author Benjamin DANGLOT
* [email protected]
* on 14/06/2021
*/
public class ClassCoverage {

public final String className;

public final List<LineCoverage> coverages;

public ClassCoverage(String className) {
this.className = className;
this.coverages = new ArrayList<>();
}

public void addCoverage(int line, int hitCounts) {
this.coverages.add(new LineCoverage(line, hitCounts));
}

public List<LineCoverage> getCoverages() {
return this.coverages;
}

public boolean contains(int line) {
return this.coverages.stream().anyMatch(lineCoverage -> lineCoverage.line == line);
}

public int getHitCountForLine(int line) {
return this.coverages.stream()
.filter(coverage -> coverage.line == line)
.findFirst()
.orElse(new LineCoverage(0, 0))
.hitCount;
}

@Override
public String toString() {
return "ClassCoverage{" +
"className='" + className + '\'' +
", coverages=" + coverages +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -1,80 +1,60 @@
package eu.stamp_project.diff_test_selection.coverage;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.stream.Collectors;

/**
* created by Benjamin DANGLOT
* [email protected]
* on 21/09/18
* <p>
* This class is responsible to compute the Coverage of the provided diff.
* @author Benjamin DANGLOT
* [email protected]
* on 14/06/2021
*/
public class Coverage {

private Map<String, Set<Integer>> executedLinePerQualifiedName;

private Map<String, Set<Integer>> modifiedLinePerQualifiedName;

private static final Logger LOGGER = LoggerFactory.getLogger(Coverage.class);
public final Map<String, TestClassCoverage> testClassCoverage;

public Coverage() {
this.modifiedLinePerQualifiedName = new LinkedHashMap<>();
this.executedLinePerQualifiedName = new LinkedHashMap<>();
this.testClassCoverage = new LinkedHashMap<>();
}

public void covered(String fullQualifiedName, Integer line) {
if (!this.executedLinePerQualifiedName.containsKey(fullQualifiedName)) {
this.executedLinePerQualifiedName.put(fullQualifiedName, new HashSet<>());
}
if (this.modifiedLinePerQualifiedName.containsKey(fullQualifiedName) &&
this.modifiedLinePerQualifiedName.get(fullQualifiedName).contains(line)
&& this.executedLinePerQualifiedName.get(fullQualifiedName).add(line)) {
LOGGER.info(fullQualifiedName + ":" + line + " covered.");
public void addCoverage(String testClassName, String testMethodName, String className, int line, int hitCounts) {
if (!this.testClassCoverage.containsKey(testClassName)) {
this.testClassCoverage.put(testClassName, new TestClassCoverage(testClassName));
}
this.testClassCoverage.get(testClassName).addCoverage(testMethodName, className, line, hitCounts);
}

public void addModifiedLines(String fullQualifiedName, List<Integer> lines) {
if (!this.modifiedLinePerQualifiedName.containsKey(fullQualifiedName)) {
this.modifiedLinePerQualifiedName.put(fullQualifiedName, new HashSet<>());
}
lines.forEach(this.modifiedLinePerQualifiedName.get(fullQualifiedName)::add);
LOGGER.info(fullQualifiedName + ":" + lines.toString() + " are modified.");
public Set<String> getTestClasses() {
return this.testClassCoverage.keySet();
}

public void addModifiedLine(String fullQualifiedName, Integer line) {
if (!this.modifiedLinePerQualifiedName.containsKey(fullQualifiedName)) {
this.modifiedLinePerQualifiedName.put(fullQualifiedName, new HashSet<>());
}
this.modifiedLinePerQualifiedName.get(fullQualifiedName).add(line);
LOGGER.info(fullQualifiedName + ":" + line + " is modified.");
public Set<String> getClassesForTestClassAndMethodName(String testClassName, String testMethodName) {
return this.testClassCoverage.get(testClassName).getClassesForTestMethodName(testMethodName);
}

@Deprecated
public void addModifiedLines(final Map<String, List<Integer>> newModifiedLinesPerQualifiedName) {
newModifiedLinesPerQualifiedName.keySet()
.forEach(key -> {
if (!this.modifiedLinePerQualifiedName.containsKey(key)) {
this.modifiedLinePerQualifiedName.put(key, new HashSet<>());
}
newModifiedLinesPerQualifiedName.get(key)
.forEach(line -> {
if (this.modifiedLinePerQualifiedName.get(key).add(line)) {
LOGGER.info(key + ":" + line + " is modified.");
}
}
);
}
);
public Set<String> getTestMethodsForTestClassName(String testClassName) {
return this.testClassCoverage.get(testClassName).getTestMethods();
}

public Map<String, Set<Integer>> getExecutedLinePerQualifiedName() {
return executedLinePerQualifiedName;
public List<LineCoverage> getCoverageForTestClassTestMethodAndClassNames(String testClassName, String testMethodName, String className) {
return this.testClassCoverage.get(testClassName).getCoverageForTestMethodAndClassNames(testMethodName, className);
}

public Map<String, ClassCoverage> getTestMethodCoverageForClassName(String testClassName, String testMethodName) {
return this.testClassCoverage.get(testClassName).getTestMethodCoverage(testMethodName);
}

public Map<String, Integer> getHitCountFromClassNameForLineForAll(String className, int line) {
final Map<String, Integer> allHitCountFromClassNameForLine = new HashMap<>();
for (TestClassCoverage value : this.testClassCoverage.values()) {
allHitCountFromClassNameForLine.putAll(value.getHitCountFromClassNameForLineForAll(className, line));
}
return allHitCountFromClassNameForLine;
}

public Map<String, Set<Integer>> getModifiedLinePerQualifiedName() {
return modifiedLinePerQualifiedName;
@Override
public String toString() {
return "Coverage{" +
"testClassCoverage=" + testClassCoverage.toString() +
'}';
}
}
Loading

0 comments on commit 1b072a8

Please sign in to comment.