/* * Copyright 2010 Henry Coles * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the License. */ package org.pitest.mutationtest.report.html; import java.io.File; import java.io.IOException; import java.io.Reader; import java.io.Writer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.logging.Level; import org.antlr.stringtemplate.StringTemplate; import org.antlr.stringtemplate.StringTemplateGroup; import org.pitest.classinfo.ClassInfo; import org.pitest.coverage.CoverageDatabase; import org.pitest.functional.F; import org.pitest.functional.FCollection; import org.pitest.functional.Option; 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; public class MutationHtmlReportListener implements MutationResultListener { private final ResultOutputStrategy outputStrategy; private final Collection<SourceLocator> sourceRoots; private final PackageSummaryMap packageSummaryData = new PackageSummaryMap(); private final CoverageDatabase coverage; private final Set<String> mutatorNames; private final String css; public MutationHtmlReportListener(final CoverageDatabase coverage, final ResultOutputStrategy outputStrategy, Collection<String> mutatorNames, final SourceLocator... locators) { this.coverage = coverage; this.outputStrategy = outputStrategy; this.sourceRoots = new HashSet<SourceLocator>(Arrays.asList(locators)); this.mutatorNames = new HashSet<String>(mutatorNames); this.css = loadCss(); } private String loadCss() { try { return FileUtil.readToString(IsolationUtils.getContextClassLoader() .getResourceAsStream("templates/mutation/style.css")); } catch (IOException e) { Log.getLogger().log(Level.SEVERE, "Error while loading css", e); } return ""; } private void generateAnnotatedSourceFile( final MutationTestSummaryData mutationMetaData) { try { final String fileName = mutationMetaData.getPackageName() + File.separator + mutationMetaData.getFileName() + ".html"; final Writer writer = this.outputStrategy.createWriterForFile(fileName); final StringTemplateGroup group = new StringTemplateGroup("mutation_test"); final StringTemplate st = group .getInstanceOf("templates/mutation/mutation_report"); st.setAttribute("css", this.css); st.setAttribute("tests", mutationMetaData.getTests()); st.setAttribute("mutators", mutationMetaData.getMutators()); final SourceFile sourceFile = createAnnotatedSourceFile(mutationMetaData); st.setAttribute("sourceFile", sourceFile); st.setAttribute("mutatedClasses", mutationMetaData.getMutatedClasses()); writer.write(st.toString()); writer.close(); } catch (final IOException ex) { Log.getLogger().log(Level.WARNING, "Error while writing report", ex); } } private PackageSummaryData collectPackageSummaries( final ClassMutationResults mutationMetaData) { final String packageName = mutationMetaData.getPackageName(); return this.packageSummaryData.update(packageName, createSummaryData(this.coverage, mutationMetaData)); } public MutationTestSummaryData createSummaryData( final CoverageDatabase coverage, final ClassMutationResults data) { return new MutationTestSummaryData(data.getFileName(), data.getMutations(), this.mutatorNames, coverage.getClassInfo(Collections.singleton(data .getMutatedClass())), coverage.getNumberOfCoveredLines(Collections .singleton(data.getMutatedClass()))); } private SourceFile createAnnotatedSourceFile( final MutationTestSummaryData mutationMetaData) throws IOException { final String fileName = mutationMetaData.getFileName(); final String packageName = mutationMetaData.getPackageName(); final MutationResultList mutationsForThisFile = mutationMetaData .getResults(); final List<Line> lines = createAnnotatedSourceCodeLines(fileName, packageName, mutationsForThisFile); return new SourceFile(fileName, lines, mutationsForThisFile.groupMutationsByLine()); } private List<Line> createAnnotatedSourceCodeLines(final String sourceFile, final String packageName, final MutationResultList mutationsForThisFile) throws IOException { final Collection<ClassInfo> classes = this.coverage.getClassesForFile( sourceFile, packageName); final Option<Reader> reader = findSourceFile(classInfoToNames(classes), sourceFile); if (reader.hasSome()) { final AnnotatedLineFactory alf = new AnnotatedLineFactory( mutationsForThisFile, this.coverage, classes); return alf.convert(reader.value()); } return Collections.emptyList(); } private Collection<String> classInfoToNames( final Collection<ClassInfo> classes) { return FCollection.map(classes, classInfoToJavaName()); } private F<ClassInfo, String> classInfoToJavaName() { return new F<ClassInfo, String>() { @Override public String apply(final ClassInfo a) { return a.getName().asJavaName(); } }; } private Option<Reader> findSourceFile(final Collection<String> classes, final String fileName) { for (final SourceLocator each : this.sourceRoots) { final Option<Reader> maybe = each.locate(classes, fileName); if (maybe.hasSome()) { return maybe; } } return Option.none(); } public void onRunEnd() { createIndexPages(); } private void createIndexPages() { final StringTemplateGroup group = new StringTemplateGroup("mutation_test"); final StringTemplate st = group .getInstanceOf("templates/mutation/mutation_package_index"); final Writer writer = this.outputStrategy.createWriterForFile("index.html"); final MutationTotals totals = new MutationTotals(); final List<PackageSummaryData> psd = new ArrayList<PackageSummaryData>( this.packageSummaryData.values()); Collections.sort(psd); for (final PackageSummaryData psData : psd) { totals.add(psData.getTotals()); createPackageIndexPage(psData); } st.setAttribute("totals", totals); st.setAttribute("packageSummaries", psd); try { writer.write(st.toString()); writer.close(); } catch (final IOException e) { e.printStackTrace(); } } private void createPackageIndexPage(final PackageSummaryData psData) { final StringTemplateGroup group = new StringTemplateGroup("mutation_test"); final StringTemplate st = group .getInstanceOf("templates/mutation/package_index"); final Writer writer = this.outputStrategy.createWriterForFile(psData .getPackageDirectory() + File.separator + "index.html"); st.setAttribute("packageData", psData); try { writer.write(st.toString()); writer.close(); } catch (final IOException e) { e.printStackTrace(); } } @Override public void runStart() { // TODO Auto-generated method stub } @Override public void runEnd() { createIndexPages(); } @Override public void handleMutationResult(final ClassMutationResults metaData) { final PackageSummaryData packageData = collectPackageSummaries(metaData); generateAnnotatedSourceFile(packageData.getForSourceFile(metaData .getFileName())); } }