Skip to content

Commit

Permalink
Parse pi-test files to stryker report.
Browse files Browse the repository at this point in the history
  • Loading branch information
Wmaarts committed Apr 8, 2019
1 parent 184f18b commit cddcd04
Show file tree
Hide file tree
Showing 8 changed files with 685 additions and 14 deletions.
3 changes: 3 additions & 0 deletions pitest-entry/src/main/java/org/pitest/util/FileUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class FileUtil {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ public List<Line> convert(final Reader source) throws IOException {
} finally {
source.close();
}

}

private Function<String, Line> stringToAnnotatedLine() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@
*/
package org.pitest.mutationtest.report.html;

import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
Expand All @@ -37,10 +34,8 @@
import org.pitest.mutationtest.ClassMutationResults;
import org.pitest.mutationtest.MutationResultListener;
import org.pitest.mutationtest.SourceLocator;
import org.pitest.util.FileUtil;
import org.pitest.util.IsolationUtils;
import org.pitest.util.Log;
import org.pitest.util.ResultOutputStrategy;
import org.pitest.mutationtest.report.html.stryker.StrykerJsonParser;
import org.pitest.util.*;

public class MutationHtmlReportListener implements MutationResultListener {

Expand All @@ -49,6 +44,7 @@ public class MutationHtmlReportListener implements MutationResultListener {
private final Collection<SourceLocator> sourceRoots;

private final PackageSummaryMap packageSummaryData = new PackageSummaryMap();
private final StrykerJsonParser strykerJsonParser;
private final CoverageDatabase coverage;
private final Set<String> mutatorNames;

Expand All @@ -60,6 +56,7 @@ public MutationHtmlReportListener(final CoverageDatabase coverage,
this.coverage = coverage;
this.outputStrategy = outputStrategy;
this.sourceRoots = new HashSet<>(Arrays.asList(locators));
this.strykerJsonParser = new StrykerJsonParser(this.sourceRoots, this.coverage);
this.mutatorNames = new HashSet<>(mutatorNames);
this.css = loadCss();
}
Expand All @@ -74,10 +71,32 @@ private String loadCss() {
return "";
}

private String loadStrykerHtml() {
final String startHtml = "<!DOCTYPE html>\n" +
"<html>\n" +
"<body>\n" +
" <mutation-test-report-app title-postfix=\"Stryker4s report\">\n" +
" Your browser doesn't support <a href=\"https://caniuse.com/#search=custom%20elements\">custom elements</a>.\n" +
" Please use a latest version of an evergreen browser (Firefox, Chrome, Safari, Opera, etc).\n" +
" </mutation-test-report-app>\n" +
" <script src=\"report.js\"></script>\n" +
" <script>";
final String endHtml = " </script>\n" +
"</body>\n" +
"</html>";
try {
return startHtml + FileUtil.readToString(IsolationUtils.getContextClassLoader()
.getResourceAsStream("templates/mutation/mutation-test-elements.js"))
+ endHtml;
} catch (final IOException e) {
Log.getLogger().log(Level.SEVERE, "Error while loading css", e);
}
return "";
}

private void generateAnnotatedSourceFile(
final MutationTestSummaryData mutationMetaData) {


final String fileName = mutationMetaData.getPackageName()
+ File.separator + mutationMetaData.getFileName() + ".html";

Expand Down Expand Up @@ -187,6 +206,26 @@ private void createCssFile() {
}
}

private void createStrykerHtml(String content) {
final Writer strykerWriter = this.outputStrategy.createWriterForFile("index2.html");
try {
strykerWriter.write(content);
strykerWriter.close();
} catch (final IOException e) {
e.printStackTrace();
}
}

private void createStrykerJs(String content) {
final Writer strykerWriter = this.outputStrategy.createWriterForFile("report.js");
try {
strykerWriter.write(content);
strykerWriter.close();
} catch (final IOException e) {
e.printStackTrace();
}
}

private void createIndexPages() {

final StringTemplateGroup group = new StringTemplateGroup("mutation_test");
Expand Down Expand Up @@ -229,23 +268,34 @@ private void createPackageIndexPage(final PackageSummaryData psData) {
} catch (final IOException e) {
e.printStackTrace();
}

}

@Override
public void runStart() {
// TODO Auto-generated method stub

}

@Override
public void runEnd() {
createIndexPages();
createCssFile();
System.out.println("Klaar met HTML report");

createStrykerHtml(loadStrykerHtml());

try {
String json = strykerJsonParser.getJson();
createStrykerJs("document.querySelector('mutation-test-report-app').report = " + json);
} catch (final IOException e) {
e.printStackTrace();
}
// createIndexPages();
// createCssFile();
}

@Override
public void handleMutationResult(final ClassMutationResults metaData) {
// Stryker addition
strykerJsonParser.addMutationResult(metaData);

final PackageSummaryData packageData = collectPackageSummaries(metaData);

generateAnnotatedSourceFile(packageData.getForSourceFile(metaData
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.pitest.mutationtest.report.html.stryker;

import java.util.List;

public class StrykerFile {
private String name;
private String source;
private List<StrykerMutant> mutants;

public StrykerFile(String name, String source, List<StrykerMutant> mutants) {
this.name = name;
this.source = source.replace("\"", "\\\"").replace("\t", " ").replace("\n", "\\n");
this.mutants = mutants;
}

public String toJson(){
return " \"" + this.name + "\": {" +
" \"source\": \"" + this.source + "\"," +
" \"mutants\": []," +
" \"language\": \"scala\"" +
" }";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package org.pitest.mutationtest.report.html.stryker;

import org.pitest.classinfo.ClassInfo;
import org.pitest.coverage.CoverageDatabase;
import org.pitest.functional.FCollection;
import org.pitest.mutationtest.ClassMutationResults;
import org.pitest.mutationtest.SourceLocator;

import java.io.IOException;
import java.io.Reader;
import java.util.*;
import java.util.stream.Collectors;

public class StrykerJsonParser {
private String exampleJson = "{\"schemaVersion\":\"1\",\"thresholds\":{\"high\":80,\"low\":60},\"files\":{\"core/src/main/scala/stryker4s/mutants/ Mutator.scala\":{\"source\":\"package stryker4s.mutants\\n\\nimport better.files.File\\nimport grizzled.slf4j.Logging\\nimport stryker4s.model.{MutatedFile, MutationsInSource, SourceTransformations}\\nimport stryker4s.mutants.applymutants.{MatchBuilder, StatementTransformer}\\nimport stryker4s.mutants.findmutants.MutantFinder\\n\\nimport scala.meta.Tree\\n\\nclass Mutator(mutantFinder: MutantFinder, transformer: StatementTransformer, matchBuilder: MatchBuilder)\\n extends Logging {\\n\\n def mutate(files: Iterable[File]): Iterable[MutatedFile] = {\\n val mutatedFiles = files\\n .map { file =>\\n val mutationsInSource = findMutants(file)\\n val transformed = transformStatements(mutationsInSource)\\n val builtTree = buildMatches(transformed)\\n\\n MutatedFile(file, builtTree, mutationsInSource.mutants, mutationsInSource.excluded)\\n }\\n .filterNot(mutatedFile => mutatedFile.mutants.isEmpty && mutatedFile.excludedMutants == 0)\\n\\n logMutationResult(mutatedFiles, files.size)\\n\\n mutatedFiles\\n }\\n\\n /** Step 1: Find mutants in the found files\\n */\\n private def findMutants(file: File): MutationsInSource = mutantFinder.mutantsInFile(file)\\n\\n /** Step 2: transform the statements of the found mutants (preparation of building pattern matches)\\n */\\n private def transformStatements(mutants: MutationsInSource): SourceTransformations =\\n transformer.transformSource(mutants.source, mutants.mutants)\\n\\n /** Step 3: Build pattern matches from transformed trees\\n */\\n private def buildMatches(transformedMutantsInSource: SourceTransformations): Tree =\\n matchBuilder.buildNewSource(transformedMutantsInSource)\\n\\n private def logMutationResult(mutatedFiles: Iterable[MutatedFile], totalAmountOfFiles: Int): Unit = {\\n val includedMutants = mutatedFiles.flatMap(_.mutants).size\\n val excludedMutants = mutatedFiles.map(_.excludedMutants).sum\\n val totalMutants = includedMutants + excludedMutants\\n\\n info(s\\\"Found ${mutatedFiles.size} of $totalAmountOfFiles file(s) to be mutated.\\\")\\n info(s\\\"$totalMutants Mutant(s) generated.${if (excludedMutants > 0)\\n s\\\" Of which $excludedMutants Mutant(s) are excluded.\\\"\\n else \\\"\\\"}\\\")\\n\\n if (totalAmountOfFiles == 0) {\\n warn(s\\\"No files marked to be mutated. ${dryRunText(\\\"mutate\\\")}\\\")\\n } else if (includedMutants == 0 && excludedMutants > 0) {\\n warn(s\\\"All found mutations are excluded. ${dryRunText(\\\"excluded-mutations\\\")}\\\")\\n } else if (totalMutants == 0) {\\n info(\\\"Files to be mutated are found, but no mutations were found in those files.\\\")\\n info(\\\"If this is not intended, please check your configuration and try again.\\\")\\n }\\n\\n def dryRunText(configProperty: String): String =\\n s\\\"\\\"\\\"Stryker4s will perform a dry-run without actually mutating anything.\\n |You can configure the `$configProperty` property in your configuration\\\"\\\"\\\".stripMargin\\n }\\n\\n}\\n\",\"mutants\":[{\"id\":\"0\",\"mutatorName\":\"LogicalOperator\",\"replacement\":\"||\",\"location\":{\"start\":{\"line\":23,\"column\":61},\"end\":{\"line\":23,\"column\":63}},\"status\":\"Killed\"},{\"id\":\"1\",\"mutatorName\":\"LogicalOperator\",\"replacement\":\"||\",\"location\":{\"start\":{\"line\":56,\"column\":37},\"end\":{\"line\":56,\"column\":39}},\"status\":\"Killed\"}],\"language\":\"scala\"}}}";

private final Collection<SourceLocator> sourceRoots;
private final CoverageDatabase coverage;

public StrykerJsonParser(final Collection<SourceLocator> sourceRoots, final CoverageDatabase coverage){
this.coverage = coverage;
this.sourceRoots = sourceRoots;
}

private List<ClassMutationResults> mutationResults = new ArrayList<>();

public String getJson() throws IOException {
// Step 1: Map mutations by file
final Map<String, List<ClassMutationResults>> mappedByName = mutationResults.stream()
.collect(Collectors.groupingBy(ClassMutationResults::getFileName));

final ArrayList<StrykerFile> strykerFiles = new ArrayList<>();

for(String key: mappedByName.keySet()){
final List<ClassMutationResults> mutationResults = mappedByName.get(key);
final ClassMutationResults result = mutationResults.get(0);
final Collection<ClassInfo> classes = this.coverage.getClassesForFile(key, result.getPackageName());
final String source = this.getSource(this.classInfoToNames(classes), result.getFileName());
if(!source.isEmpty()){
strykerFiles.add(new StrykerFile(key, source, new ArrayList<>()));
}
}

String beginJson = "{" +
" \"schemaVersion\": \"1\"," +
" \"thresholds\": {" +
" \"high\": 80," +
" \"low\": 60" +
" }," +
" \"files\": {";
String endJson = "} }";
StringBuilder builder = new StringBuilder();
builder.append(beginJson);
for(int i = 0; i < strykerFiles.size() - 2; i++){
StrykerFile file = strykerFiles.get(i);
builder.append(file.toJson());
builder.append(",");
}
StrykerFile lastFile = strykerFiles.get(strykerFiles.size() - 1);
builder.append(lastFile.toJson());
builder.append(endJson);
return builder.toString();
}

public void addMutationResult(final ClassMutationResults mutationResult){
mutationResults.add(mutationResult);
}

private String getSource(final Collection<String> classes,
final String fileName) throws IOException{
for (final SourceLocator each : this.sourceRoots) {
final Optional<Reader> maybe = each.locate(classes, fileName);
if (maybe.isPresent()) {
return readerToString(maybe.get());
}
}
// throw new IOException("File " + fileName + " not found");
return "";
}

private Collection<String> classInfoToNames(
final Collection<ClassInfo> classes) {
return FCollection.map(classes, a -> a.getName().asJavaName());
}

private String readerToString(final Reader reader) throws java.io.IOException {
final StringBuilder fileData = new StringBuilder(1000);
char[] buf = new char[1024];
int numRead = 0;

while ((numRead = reader.read(buf)) != -1) {
final String readData = String.valueOf(buf, 0, numRead);
fileData.append(readData);
buf = new char[1024];
}
return fileData.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.pitest.mutationtest.report.html.stryker;

public class StrykerLocation {
private LineAndColumn start, end;
public StrykerLocation(final LineAndColumn start, final LineAndColumn end){
this.start = start;
this.end = end;
}

public String toJson(){
return " \"start\": {" +
" \"line\": " + this.start.line + "," +
" \"column\": " + this.start.column + " " +
" }," +
" \"end\": {" +
" \"line\": " + this.end.line + "," +
" \"column\": " + this.start.column + " " +
" }";
}
}

class LineAndColumn {
int line;
int column;
public LineAndColumn(final int line, final int column){
this.line = line;
this.column = column;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.pitest.mutationtest.report.html.stryker;

public class StrykerMutant {
private String id;
private String mutatorName;
private String replacement;
private StrykerLocation location;
private String status;

public StrykerMutant(String id, String mutatorName, String replacement, StrykerLocation location, String status) {
this.id = id;
this.mutatorName = mutatorName;
this.replacement = replacement;
this.location = location;
this.status = status;
}

public String toJson(){
return "{" +
" \"id\": \"" + this.id + "\"," +
" \"mutatorName\": \"" + this.mutatorName + "\"," +
" \"replacement\": \"||\"," +
" \"location\": {" + location.toJson() +
" }," +
" \"status\": \"" + this.status + "\"" +
"}";
}
}
Loading

0 comments on commit cddcd04

Please sign in to comment.