/******************************************************************************* * Copyright (c) 2009, 2018 Mountainminds GmbH & Co. KG and Contributors * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * John Keeping - initial implementation * Marc R. Hoffmann - rework * *******************************************************************************/ package org.jacoco.cli.internal.commands; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.jacoco.cli.internal.Command; import org.jacoco.core.analysis.Analyzer; import org.jacoco.core.analysis.CoverageBuilder; import org.jacoco.core.analysis.IBundleCoverage; import org.jacoco.core.analysis.IClassCoverage; import org.jacoco.core.data.ExecutionDataStore; import org.jacoco.core.tools.ExecFileLoader; import org.jacoco.report.DirectorySourceFileLocator; import org.jacoco.report.FileMultiReportOutput; import org.jacoco.report.IReportVisitor; import org.jacoco.report.ISourceFileLocator; import org.jacoco.report.MultiReportVisitor; import org.jacoco.report.MultiSourceFileLocator; import org.jacoco.report.csv.CSVFormatter; import org.jacoco.report.html.HTMLFormatter; import org.jacoco.report.xml.XMLFormatter; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.Option; /** * The report command. */ public class Report extends Command { @Argument(usage = "list of JaCoCo *.exec files to read", metaVar = "") List execfiles = new ArrayList(); @Option(name = "--classfiles", usage = "location of Java class files", metaVar = "", required = true) List classfiles = new ArrayList(); @Option(name = "--sourcefiles", usage = "location of the source files", metaVar = "") List sourcefiles = new ArrayList(); @Option(name = "--tabwith", usage = "tab stop width for the source pages (default 4)", metaVar = "") int tabwidth = 4; @Option(name = "--name", usage = "name used for this report", metaVar = "") String name = "JaCoCo Coverage Report"; @Option(name = "--encoding", usage = "source file encoding (by default platform encoding is used)", metaVar = "") String encoding; @Option(name = "--xml", usage = "output file for the XML report", metaVar = "") File xml; @Option(name = "--csv", usage = "output file for the CSV report", metaVar = "") File csv; @Option(name = "--html", usage = "output directory for the HTML report", metaVar = "") File html; @Override public String description() { return "Generate reports in different formats by reading exec and Java class files."; } @Override public int execute(final PrintWriter out, final PrintWriter err) throws IOException { final ExecFileLoader loader = loadExecutionData(out); final IBundleCoverage bundle = analyze(loader.getExecutionDataStore(), out); writeReports(bundle, loader, out); return 0; } private ExecFileLoader loadExecutionData(final PrintWriter out) throws IOException { final ExecFileLoader loader = new ExecFileLoader(); if (execfiles.isEmpty()) { out.println("[WARN] No execution data files provided."); } else { for (final File file : execfiles) { out.printf("[INFO] Loading execution data file %s.%n", file.getAbsolutePath()); loader.load(file); } } return loader; } private IBundleCoverage analyze(final ExecutionDataStore data, final PrintWriter out) throws IOException { final CoverageBuilder builder = new CoverageBuilder(); final Analyzer analyzer = new Analyzer(data, builder); for (final File f : classfiles) { analyzer.analyzeAll(f); } printNoMatchWarning(builder.getNoMatchClasses(), out); return builder.getBundle(name); } private void printNoMatchWarning(final Collection nomatch, final PrintWriter out) { if (!nomatch.isEmpty()) { out.println( "[WARN] Some classes do not match with execution data."); out.println( "[WARN] For report generation the same class files must be used as at runtime."); for (final IClassCoverage c : nomatch) { out.printf( "[WARN] Execution data for class %s does not match.%n", c.getName()); } } } private void writeReports(final IBundleCoverage bundle, final ExecFileLoader loader, final PrintWriter out) throws IOException { out.printf("[INFO] Analyzing %s classes.%n", Integer.valueOf(bundle.getClassCounter().getTotalCount())); final IReportVisitor visitor = createReportVisitor(); visitor.visitInfo(loader.getSessionInfoStore().getInfos(), loader.getExecutionDataStore().getContents()); visitor.visitBundle(bundle, getSourceLocator()); visitor.visitEnd(); } private IReportVisitor createReportVisitor() throws IOException, IOException { final List visitors = new ArrayList(); if (xml != null) { final XMLFormatter formatter = new XMLFormatter(); visitors.add(formatter.createVisitor(new FileOutputStream(xml))); } if (csv != null) { final CSVFormatter formatter = new CSVFormatter(); visitors.add(formatter.createVisitor(new FileOutputStream(csv))); } if (html != null) { final HTMLFormatter formatter = new HTMLFormatter(); visitors.add( formatter.createVisitor(new FileMultiReportOutput(html))); } return new MultiReportVisitor(visitors); } private ISourceFileLocator getSourceLocator() { final MultiSourceFileLocator multi = new MultiSourceFileLocator( tabwidth); for (final File f : sourcefiles) { multi.add(new DirectorySourceFileLocator(f, encoding, tabwidth)); } return multi; } }