Skip to content

Commit

Permalink
Merge pull request #433 from rchargel/report-aggregate
Browse files Browse the repository at this point in the history
Adding an additional report-aggregate mojo
  • Loading branch information
hcoles authored Jan 29, 2018
2 parents ddd85d9 + c457c4a commit e83ae76
Show file tree
Hide file tree
Showing 24 changed files with 1,369 additions and 30 deletions.
42 changes: 42 additions & 0 deletions pitest-aggregator/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.pitest</groupId>
<artifactId>pitest-parent</artifactId>
<version>1.3.2-SNAPSHOT</version>
</parent>
<artifactId>pitest-aggregator</artifactId>
<description>Aggregates the output of report XML documents into a new combined HTML report</description>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.pitest</groupId>
<artifactId>pitest-entry</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.pitest</groupId>
<artifactId>pitest-html-report</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.pitest.aggregate;

import java.io.File;
import java.util.Collection;
import java.util.Map;

import org.pitest.classinfo.ClassName;
import org.pitest.coverage.BlockCoverage;
import org.pitest.coverage.BlockLocation;
import org.pitest.mutationtest.engine.Location;
import org.pitest.mutationtest.engine.MethodName;

class BlockCoverageDataLoader extends DataLoader<BlockCoverage> {

private static final String METHOD = "method";
private static final String CLASSNAME = "classname";
private static final String NUMBER = "number";
private static final String TESTS = "tests";

private static final String OPEN_PAREN = "(";

public BlockCoverageDataLoader(final Collection<File> filesToLoad) {
super(filesToLoad);
}

@Override
protected BlockCoverage mapToData(final Map<String, Object> map) {
final String method = (String) map.get(METHOD);
final Location location = new Location(ClassName.fromString((String) map.get(CLASSNAME)),
MethodName.fromString(method.substring(0, method.indexOf(OPEN_PAREN))), method.substring(method.indexOf(OPEN_PAREN)));

final BlockLocation blockLocation = new BlockLocation(location, Integer.parseInt((String) map.get(NUMBER)));

@SuppressWarnings("unchecked")
final Collection<String> tests = (Collection<String>) map.get(TESTS);

return new BlockCoverage(blockLocation, tests);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package org.pitest.aggregate;

import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;

import org.pitest.classinfo.ClassName;
import org.pitest.classpath.ClassFilter;
import org.pitest.classpath.ClassPath;
import org.pitest.classpath.ClassPathRoot;
import org.pitest.classpath.CodeSource;
import org.pitest.classpath.DirectoryClassPathRoot;
import org.pitest.classpath.PathFilter;
import org.pitest.classpath.ProjectClassPaths;
import org.pitest.functional.F;
import org.pitest.functional.FCollection;
import org.pitest.functional.predicate.Predicate;
import org.pitest.functional.prelude.Prelude;
import org.pitest.mutationtest.config.DefaultCodePathPredicate;
import org.pitest.mutationtest.config.DefaultDependencyPathPredicate;
import org.pitest.util.Glob;

class CodeSourceAggregator {

private final Collection<File> compiledCodeDirectories;

public CodeSourceAggregator(final Collection<File> compiledCodeDirectories) {
this.compiledCodeDirectories = Collections.unmodifiableCollection(compiledCodeDirectories);
}

public CodeSource createCodeSource() {
return new CodeSource(createProjectClassPaths());
}

private ProjectClassPaths createProjectClassPaths() {
final ClassPath classPath = new ClassPath(compiledCodeDirectories);
final Predicate<String> classPredicate = createClassPredicate();
final Predicate<ClassPathRoot> pathPredicate = new DefaultCodePathPredicate();
return new ProjectClassPaths(classPath, new ClassFilter(classPredicate, classPredicate),
new PathFilter(pathPredicate, Prelude.not(new DefaultDependencyPathPredicate())));
}

private Predicate<String> createClassPredicate() {
final Collection<String> classes = new HashSet<String>();
for (final File buildOutputDirectory : compiledCodeDirectories) {
if (buildOutputDirectory.exists()) {
final DirectoryClassPathRoot dcRoot = new DirectoryClassPathRoot(buildOutputDirectory);
classes.addAll(FCollection.map(dcRoot.classNames(), new F<String, String>() {
@Override
public String apply(final String a) {
return ClassName.fromString(a).getPackage().asJavaName() + ".*";
}
}));
}
}
return Prelude.or(FCollection.map(classes, Glob.toGlobPredicate()));
}

}
150 changes: 150 additions & 0 deletions pitest-aggregator/src/main/java/org/pitest/aggregate/DataLoader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package org.pitest.aggregate;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

abstract class DataLoader<T> {

private static final String CANNOT_CLOSE_ERR = "Unable to close input stream";

private final Set<File> filesToLoad;

protected DataLoader(final Collection<File> filesToLoad) {
if (filesToLoad == null || filesToLoad.isEmpty()) {
throw new IllegalArgumentException("Null or empty filesToLoad");
}

this.filesToLoad = Collections.unmodifiableSet(new HashSet<File>(filesToLoad));
}

public Set<T> loadData() throws ReportAggregationException {
final Set<T> data = new HashSet<T>();

for (final File file : filesToLoad) {
data.addAll(loadData(file));
}
return data;
}

protected abstract T mapToData(Map<String, Object> map);

Set<T> loadData(final File dataLocation) throws ReportAggregationException {
if (!dataLocation.exists() || !dataLocation.isFile()) {
throw new ReportAggregationException(dataLocation.getAbsolutePath() + " does not exist or is not a file");
}
final Set<T> data = new HashSet<T>();
try {
final InputStream inputStream = new BufferedInputStream(new FileInputStream(dataLocation));

final Document doc = readDocument(inputStream);

final Node docNode = doc.getFirstChild();
final NodeList nodeList = docNode.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
final Node itemNode = nodeList.item(i);
if (itemNode.getNodeType() == Node.ELEMENT_NODE) {
data.add(mapToData(nodeMap(itemNode)));
}
}
return data;
} catch (final IOException e) {
throw new ReportAggregationException("Could not read file: " + dataLocation.getAbsolutePath(), e);
}
}

/**
* Reads the input stream into a document and closes the input stream when
* finished.
*/
static Document readDocument(final InputStream inputStream) throws ReportAggregationException {
DocumentBuilder docBuilder;
try {
docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
return docBuilder.parse(inputStream);
} catch (final IOException e) {
throw new ReportAggregationException(e.getMessage(), e);
} catch (final SAXException e) {
throw new ReportAggregationException(e.getMessage(), e);
} catch (final ParserConfigurationException e) {
throw new ReportAggregationException(e.getMessage(), e);
} finally {
try {
inputStream.close();
} catch (final IOException e) {
throw new ReportAggregationException(CANNOT_CLOSE_ERR, e);
}
}
}

// converts the contents of a node into a map
static Map<String, Object> nodeMap(final Node node) {
final HashMap<String, Object> map = new HashMap<String, Object>();

final NamedNodeMap attrs = node.getAttributes();
for (int i = 0; i < attrs.getLength(); i++) {
final Node attr = attrs.item(i);
final String tc = attr.getTextContent().trim();

if (!tc.isEmpty()) {
map.put(attr.getNodeName(), tc);
}
}

final NodeList children = node.getChildNodes();

for (int i = 0; i < children.getLength(); i++) {
final Node child = children.item(i);

if (child.getNodeType() == Node.ELEMENT_NODE) {
final String tc = child.getTextContent().trim();
if (!tc.isEmpty()) {
map.put(child.getNodeName(), tc);
} else {
// may have test nodes
final List<String> tests = new ArrayList<String>();
final NodeList testNodeList = child.getChildNodes();
for (int j = 0; j < testNodeList.getLength(); j++) {
final Node testNode = testNodeList.item(j);
if (testNode.getNodeType() == Node.ELEMENT_NODE) {
final NamedNodeMap testAttrs = testNode.getAttributes();
for (int k = 0; k < testAttrs.getLength(); k++) {
final Node attr = testAttrs.item(k);
final String tn = attr.getTextContent().trim();

if (!tn.isEmpty()) {
tests.add(tn);
}
}
}
}
if (!tests.isEmpty()) {
map.put(child.getNodeName(), tests);
}
}
}
}

return map;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.pitest.aggregate;

import java.io.File;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;

import org.pitest.classinfo.ClassName;
import org.pitest.mutationtest.DetectionStatus;
import org.pitest.mutationtest.MutationResult;
import org.pitest.mutationtest.MutationStatusTestPair;
import org.pitest.mutationtest.engine.Location;
import org.pitest.mutationtest.engine.MethodName;
import org.pitest.mutationtest.engine.MutationDetails;
import org.pitest.mutationtest.engine.MutationIdentifier;

class MutationResultDataLoader extends DataLoader<MutationResult> {

private static final String MUTATED_CLASS = "mutatedClass";
private static final String MUTATED_METHOD = "mutatedMethod";
private static final String METHOD_DESCRIPTION = "methodDescription";
private static final String INDEX = "index";
private static final String MUTATOR = "mutator";
private static final String SOURCE_FILE = "sourceFile";
private static final String DESCRIPTION = "description";
private static final String LINE_NUMBER = "lineNumber";
private static final String BLOCK = "block";
private static final String NUMBER_OF_TESTS_RUN = "numberOfTestsRun";
private static final String STATUS = "status";
private static final String KILLING_TEST = "killingTest";

public MutationResultDataLoader(final Collection<File> filesToLoad) {
super(filesToLoad);
}

@Override
protected MutationResult mapToData(final Map<String, Object> map) {
final Location location = new Location(ClassName.fromString((String) map.get(MUTATED_CLASS)), MethodName.fromString((String) map.get(MUTATED_METHOD)),
(String) map.get(METHOD_DESCRIPTION));

final MutationIdentifier id = new MutationIdentifier(location, Arrays.asList(new Integer((String) map.get(INDEX))), (String) map.get(MUTATOR));

final MutationDetails md = new MutationDetails(id, (String) map.get(SOURCE_FILE), (String) map.get(DESCRIPTION),
Integer.parseInt((String) map.get(LINE_NUMBER)), Integer.parseInt((String) map.get(BLOCK)));

final MutationStatusTestPair status = new MutationStatusTestPair(Integer.parseInt((String) map.get(NUMBER_OF_TESTS_RUN)),
DetectionStatus.valueOf((String) map.get(STATUS)), (String) map.get(KILLING_TEST));

return new MutationResult(md, status);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.pitest.aggregate;

import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.pitest.mutationtest.engine.gregor.MethodMutatorFactory;

final class MutatorUtil {

private static Map<String, MethodMutatorFactory> factories = new ConcurrentHashMap<String, MethodMutatorFactory>();

@SuppressWarnings("unchecked")
static MethodMutatorFactory loadMutator(final String className) {
if (!factories.containsKey(className)) {
try {
final Class<MethodMutatorFactory> clazz = (Class<MethodMutatorFactory>) Class.forName(className);
final Method values = clazz.getMethod("values");
final Object valuesArray = values.invoke(null);
final MethodMutatorFactory mutator = (MethodMutatorFactory) Array.get(valuesArray, 0);
factories.put(className, mutator);
} catch (final Exception e) {
throw new RuntimeException("Unable to load Mutator for class: " + className, e);
}
}
return factories.get(className);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.pitest.aggregate;

public class ReportAggregationException extends Exception {

private static final long serialVersionUID = 1L;

public ReportAggregationException(final String message) {
super(message);
}

public ReportAggregationException(final String message, final Throwable cause) {
super(message, cause);
}

}
Loading

0 comments on commit e83ae76

Please sign in to comment.