diff options
author | Jiri Vanek <[email protected]> | 2012-12-11 20:32:26 +0100 |
---|---|---|
committer | Jiri Vanek <[email protected]> | 2012-12-11 20:32:26 +0100 |
commit | 9f073ba18f90391ac45ca8f8f8df1ae7516b90f9 (patch) | |
tree | ce4654016af42b3dc4e6447c16c75ddb8ce32bab /tests | |
parent | 8bbf9daa0e72001d56ae9fd9aa50cc92e1643695 (diff) |
Added jacoco code coverage support
Note - in time of this commit one needs custom build of jacoco containing
the xboot.patch form approving discussion:
http://mail.openjdk.java.net/pipermail/distro-pkg-dev/attachments/20121127/ba8f6a1e/xboot-0001.patch
The tracking of upstraming can be found on jacoco page.
Diffstat (limited to 'tests')
3 files changed, 737 insertions, 0 deletions
diff --git a/tests/jacoco-operator/org/jacoco/operator/Main.java b/tests/jacoco-operator/org/jacoco/operator/Main.java new file mode 100644 index 0000000..7537ae1 --- /dev/null +++ b/tests/jacoco-operator/org/jacoco/operator/Main.java @@ -0,0 +1,288 @@ +/* +Copyright (C) 2012 Red Hat, Inc. + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 2. + +IcedTea is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. + */ + +package org.jacoco.operator; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Commandline launcher + */ +public class Main { + + //main switches + private static final String MERGE = "merge"; + private static final String REPORT = "report"; + //switches + private static final String die_on_failure = "--die-soon"; + //merge + private static final String output_file = "--output-file"; + private static final String input_files = "--input-files"; + //report + private static final String html_output = "--html-output"; + private static final String xml_output = "--xml-output"; + private static final String input_srcs = "--input-srcs"; + private static final String input_builds = "--input-builds"; + private static final String title = "--title"; + private static String input_file = "--input-file"; + /** + * * + */ + private static boolean dieOnFailure = false; + private static boolean warned = false; + + public static void main(String[] args) throws IOException { + if (args.length < 2) { + printHelp(); + System.exit(0); + } + + Runnable r = null; + if (args[0].equalsIgnoreCase(MERGE)) { + r = proceedMerge(cutFirstParam(args)); + } else if (args[0].equalsIgnoreCase(REPORT)) { + r = proceedReport(cutFirstParam(args)); + } else { + System.err.println("Unsuported main switch `" + args[0] + "`, use " + MERGE + " or " + REPORT); + printHelp(); + System.exit(1); + } + if (dieOnFailure && warned) { + System.err.println(die_on_failure + " is specified and warning occured. Exiting"); + System.exit(2); + } + r.run(); + + } + + private static void printHelp() { + System.out.println("Usage: java `classpath` org.jacoco.operator.Main [" + MERGE + "|" + REPORT + "] switches/files"); + System.out.println(" order of switches does not matter"); + System.out.println(" Merge usage: java `classpath` org.jacoco.operator.Main " + MERGE + " " + output_file + " file " + input_files + " file file file ..."); + System.out.println(" Report usage: java `classpath` org.jacoco.operator.Main " + REPORT + " " + html_output + " file " + xml_output + " file " + input_srcs + " file file file ... " + input_builds + " file file file " + title + " titleOfReport " + input_file + " file"); + System.out.println("Where:"); + System.out.println(" classpath should contain this application, and complete jacoco, and sometimes asm3 (depends on jacoco bundle)"); + System.out.println(" " + die_on_failure + " - can be set as first parameter (after main switch), each warning then will cause exit of application"); + System.out.println(" " + MERGE); + System.out.println(" " + output_file + " - is file where merged inputs will be saved"); + System.out.println(" " + input_files + " - is list of files which will be merged into output file"); + System.out.println(" " + REPORT); + System.out.println(" " + html_output + " - name of directory into which report will be generated. Should be empty or not yet exist"); + System.out.println(" " + xml_output + " - is name of file into which xml report will be written"); + System.out.println(" " + input_srcs + " - jars, zips or directories with java sources which will be used during report generation"); + System.out.println(" " + input_builds + " - jars, zips or directories with compiled java classes, debug information must be present"); + System.out.println(" " + title + " - title of report"); + System.out.println(" " + input_file + " - input file with recorded coverage-run-session. By default jacoco saves into " + MergeTask.DEFAULT_NAME); + + } + + private static String[] cutFirstParam(String[] args) { + String[] arg = new String[args.length - 1]; + System.arraycopy(args, 1, arg, 0, arg.length); + return arg; + } + + private static Runnable proceedMerge(String[] a) throws IOException { + String doing = null; + String outputFile = null; + List<String> inputFiles = new ArrayList<String>(2); + for (String s : a) { + if (s.startsWith("--")) { + if (s.equalsIgnoreCase(die_on_failure)) { + doing = null; + dieOnFailure = true; + } else if (s.equalsIgnoreCase(output_file)) { + doing = output_file; + } else if (s.equalsIgnoreCase(input_files)) { + doing = input_files; + } else { + warnOrDie("Unknown Switch for merge " + s); + doing = null; + } + } else { + if (doing == null) { + warnOrDie("Missing switch during processing of " + s); + } else { + if (doing.equalsIgnoreCase(output_file)) { + outputFile = s; + } else if (doing.equalsIgnoreCase(input_files)) { + inputFiles.add(s); + } else { + warnOrDie("Unknown processing of switch of" + doing); + } + + } + } + } + throwIfNullOrEmpty(outputFile, "empty output file"); + File ff = new File(outputFile); + if (ff.exists()) { + warnOrDie("Warning, output file " + ff.getAbsolutePath() + " exists"); + } + MergeTask m = new MergeTask(ff); + for (String string : inputFiles) { + if (checkIfNotNullOrEmpty(string)) { + File f = new File(string); + if (!f.exists()) { + warnOrDie("Warning, input coverage " + f.getAbsolutePath() + " does not exists!"); + } + m.addInputFile(f); + } + } + return m; + + } + + private static Runnable proceedReport(String[] a) throws IOException { + String doing = null; + String htmlDir = null; + String xmlFile = null; + List<String> inputSrcs = new ArrayList<String>(1); + List<String> inputBuilds = new ArrayList<String>(1); + String titleValue = null; + String inputFile = null; + for (String s : a) { + if (s.startsWith("--")) { + if (s.equalsIgnoreCase(die_on_failure)) { + doing = null; + dieOnFailure = true; + } else if (s.equalsIgnoreCase(html_output)) { + doing = html_output; + } else if (s.equalsIgnoreCase(xml_output)) { + doing = xml_output; + } else if (s.equalsIgnoreCase(input_srcs)) { + doing = input_srcs; + } else if (s.equalsIgnoreCase(input_builds)) { + doing = input_builds; + } else if (s.equalsIgnoreCase(title)) { + doing = title; + } else if (s.equalsIgnoreCase(input_file)) { + doing = input_file; + } else { + warnOrDie("Unknown Switch for report " + s); + doing = null; + } + } else { + if (doing == null) { + warnOrDie("Missing switch during processing of " + s); + } else { + if (doing.equalsIgnoreCase(html_output)) { + htmlDir = s; + } else if (doing.equalsIgnoreCase(xml_output)) { + xmlFile = s; + } else if (doing.equalsIgnoreCase(input_srcs)) { + inputSrcs.add(s); + } else if (doing.equalsIgnoreCase(input_builds)) { + inputBuilds.add(s); + } else if (doing.equalsIgnoreCase(title)) { + titleValue = s; + } else if (doing.equalsIgnoreCase(input_file)) { + inputFile = s; + } else { + warnOrDie("Unknown processing of switch of " + doing); + } + + } + } + } + File finalHtmlFile = null; + if (checkIfNotNullOrEmpty(htmlDir)) { + finalHtmlFile = new File(htmlDir); + if (finalHtmlFile.exists()) { + warnOrDie("Warning, direcotry for html report exists! " + finalHtmlFile.getAbsolutePath()); + } + } + File finalXmlFile = null; + if (checkIfNotNullOrEmpty(xmlFile)) { + finalXmlFile = new File(xmlFile); + if (finalXmlFile.exists()) { + warnOrDie("Warning, file for xml report exists! " + finalHtmlFile.getAbsolutePath()); + } + } + if (chckIfNUllOrEmpty(titleValue)) { + titleValue = "Coverage report"; + } + throwIfNullOrEmpty(inputFile, "No coverage data file specified!"); + File finalInputFile = new File(inputFile); + + ReportGenerator rg = new ReportGenerator(titleValue, finalInputFile, finalHtmlFile, finalXmlFile); + + for (String string : inputSrcs) { + if (checkIfNotNullOrEmpty(string)) { + File f = new File(string); + if (!f.exists()) { + warnOrDie("Warning, input source " + f.getAbsolutePath() + " does not exists!"); + } + rg.addSource(f); + } + } + for (String string : inputBuilds) { + if (checkIfNotNullOrEmpty(string)) { + File f = new File(string); + if (!f.exists()) { + warnOrDie("Warning, input build " + f.getAbsolutePath() + " does not exists!"); + } + rg.addClasses(f); + } + } + return rg; + } + + private static String throwIfNullOrEmpty(String outputFile, String message) throws RuntimeException { + if (chckIfNUllOrEmpty(outputFile)) { + throw new RuntimeException(message); + } + return outputFile; + } + + private static boolean checkIfNotNullOrEmpty(String string) { + return string != null && string.trim().length() != 0; + } + + private static boolean chckIfNUllOrEmpty(String outputFile) { + return outputFile == null || outputFile.trim().length() == 0; + } + + private static void warnOrDie(String string) { + System.err.println(string); + warned = true; + + } +} diff --git a/tests/jacoco-operator/org/jacoco/operator/MergeTask.java b/tests/jacoco-operator/org/jacoco/operator/MergeTask.java new file mode 100644 index 0000000..02c1b80 --- /dev/null +++ b/tests/jacoco-operator/org/jacoco/operator/MergeTask.java @@ -0,0 +1,157 @@ +/* +Copyright (C) 2012 Red Hat, Inc. + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 2. + +IcedTea is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. + */ +package org.jacoco.operator; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import org.jacoco.core.data.ExecutionDataReader; +import org.jacoco.core.data.ExecutionDataStore; +import org.jacoco.core.data.ExecutionDataWriter; +import org.jacoco.core.data.SessionInfoStore; + +/** + * Task for merging a set of execution data store files into a single file + * + * Inspired by: + * https://raw.github.com/jacoco/jacoco/master/org.jacoco.ant/src/org/jacoco/ant/MergeTask.java + */ +public class MergeTask implements Runnable { + + public static final String DEFAULT_NAME = "jacoco.exec"; + private File destfile; + private final List<File> files = new ArrayList<File>(1); + + public MergeTask(File destfile) { + this.destfile = destfile; + } + + public MergeTask(File destfile, List<File> inputs) { + this.destfile = destfile; + files.addAll(inputs); + } + + /** + * Sets the location of the merged data store + * + * @param destfile Destination data store location + */ + public void setDestfile(final File destfile) { + this.destfile = destfile; + } + + public void addInputFile(final File input) { + if (input != null) { + files.add(input); + } + } + + public void addInputFiles(final List<File> input) { + files.addAll(input); + } + + public void execute() throws IOException { + if (destfile == null) { + throw new RuntimeException("Destination file must be supplied"); + } + + final SessionInfoStore infoStore = new SessionInfoStore(); + final ExecutionDataStore dataStore = new ExecutionDataStore(); + + loadSourceFiles(infoStore, dataStore); + + OutputStream outputStream = null; + try { + + outputStream = new BufferedOutputStream(new FileOutputStream( + destfile)); + final ExecutionDataWriter dataWriter = new ExecutionDataWriter( + outputStream); + infoStore.accept(dataWriter); + dataStore.accept(dataWriter); + } finally { + if (outputStream != null) { + outputStream.close(); + } + } + + } + + private void loadSourceFiles(final SessionInfoStore infoStore, final ExecutionDataStore dataStore) throws IOException { + if (files == null || files.isEmpty()) { + throw new RuntimeException("No input files"); + } + final Iterator<?> resourceIterator = files.iterator(); + while (resourceIterator.hasNext()) { + final File resource = (File) resourceIterator.next(); + + if (resource.isDirectory()) { + continue; + } + InputStream resourceStream = null; + try { + resourceStream = new FileInputStream(resource); + final ExecutionDataReader reader = new ExecutionDataReader( + resourceStream); + reader.setSessionInfoVisitor(infoStore); + reader.setExecutionDataVisitor(dataStore); + reader.read(); + } finally { + if (resourceStream != null) { + resourceStream.close(); + } + } + } + } + + @Override + public void run() { + try { + execute(); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + +} diff --git a/tests/jacoco-operator/org/jacoco/operator/ReportGenerator.java b/tests/jacoco-operator/org/jacoco/operator/ReportGenerator.java new file mode 100644 index 0000000..84629f9 --- /dev/null +++ b/tests/jacoco-operator/org/jacoco/operator/ReportGenerator.java @@ -0,0 +1,292 @@ +/* +Copyright (C) 2012 Red Hat, Inc. + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 2. + +IcedTea is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. + */ + +package org.jacoco.operator; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import org.jacoco.core.analysis.Analyzer; +import org.jacoco.core.analysis.CoverageBuilder; +import org.jacoco.core.analysis.IBundleCoverage; +import org.jacoco.core.data.ExecutionDataReader; +import org.jacoco.core.data.ExecutionDataStore; +import org.jacoco.core.data.SessionInfoStore; +import org.jacoco.report.DirectorySourceFileLocator; +import org.jacoco.report.FileMultiReportOutput; +import org.jacoco.report.IReportVisitor; +import org.jacoco.report.MultiSourceFileLocator; +import org.jacoco.report.html.HTMLFormatter; +import org.jacoco.report.xml.XMLFormatter; + +/** + * This example creates a HTML report for eclipse like projects based on a + * single execution data store called jacoco.exec. The report contains no + * grouping information. + * + * The class files under test must be compiled with debug information, otherwise + * source highlighting will not work. + * + * Originally based on: + * http://www.eclemma.org/jacoco/trunk/doc/examples/java/ReportGenerator.java + */ +public class ReportGenerator implements Runnable { + + private final String title; + private final File executionDataFile; + private final List<File> classesDirectories = new ArrayList<File>(1); + private final List<File> sourceDirectories = new ArrayList<File>(1); + private File reportDirectory; + private File xmlOutput; + private ExecutionDataStore executionDataStore; + private SessionInfoStore sessionInfoStore; + private String XML_DEF_NAME = "coverage-summary.xml"; + + /** + * Create a new generator based for the given project. + * + * @param projectDirectory + */ + public ReportGenerator(final File projectDirectory) { + this.title = projectDirectory.getName(); + this.executionDataFile = new File(projectDirectory, MergeTask.DEFAULT_NAME); + this.classesDirectories.add(new File(projectDirectory, "bin")); + this.sourceDirectories.add(new File(projectDirectory, "src")); + this.reportDirectory = new File(projectDirectory, "coveragereport"); + this.xmlOutput = new File(projectDirectory, XML_DEF_NAME); + } + + public ReportGenerator(String title, File exec, File classes, File sources, File htmlReport, File xmlReport) { + this.title = title; + this.executionDataFile = exec; + if (classes != null) { + this.classesDirectories.add(classes); + } + if (sources != null) { + this.sourceDirectories.add(sources); + } + this.reportDirectory = htmlReport; + + this.xmlOutput = xmlReport; + } + + public ReportGenerator(String title, File exec, List<File> classes, List<File> sources, File htmlReport, File xmlReport) { + this.title = title; + this.executionDataFile = exec; + if (classes != null) { + this.classesDirectories.addAll(classes); + } + if (sources != null) { + this.sourceDirectories.addAll(sources); + } + this.reportDirectory = htmlReport; + this.xmlOutput = xmlReport; + } + + public ReportGenerator(String title, File exec, List<File> classes, List<File> sources, File report) { + this.title = title; + this.executionDataFile = exec; + if (classes != null) { + this.classesDirectories.addAll(classes); + } + if (sources != null) { + this.sourceDirectories.addAll(sources); + } + this.reportDirectory = report; + this.xmlOutput = new File(report, XML_DEF_NAME); + } + + public ReportGenerator(String title, File exec, File htmlReport, File xmlReport) { + this.title = title; + this.executionDataFile = exec; + this.reportDirectory = htmlReport; + this.xmlOutput = xmlReport; + } + + public ReportGenerator(String title, File exec, File report) { + this.title = title; + this.executionDataFile = exec; + this.reportDirectory = report; + this.xmlOutput = new File(report, XML_DEF_NAME); + } + + public void addSource(File f) { + sourceDirectories.add(f); + + } + + public void addClasses(File f) { + classesDirectories.add(f); + + } + + /** + * Create the report. + * + * @throws IOException + */ + public void execute() throws IOException { + + // Read the jacoco.exec file. Multiple data stores could be merged + // at this point + loadExecutionData(); + + // Run the structure analyzer on a single class folder to build up + // the coverage model. The process would be similar if your classes + // were in a jar file. Typically you would create a bundle for each + // class folder and each jar you want in your report. If you have + // more than one bundle you will need to add a grouping node to your + // report + final IBundleCoverage bundleCoverage = analyzeStructure(); + + if (reportDirectory != null) { + createHtmlReport(bundleCoverage); + } + if (xmlOutput != null) { + createXmlReport(bundleCoverage); + } + + } + + private void createHtmlReport(final IBundleCoverage bundleCoverage) + throws IOException { + + // Create a concrete report visitor based on some supplied + // configuration. In this case we use the defaults + final HTMLFormatter htmlFormatter = new HTMLFormatter(); + final IReportVisitor visitor = htmlFormatter.createVisitor(new FileMultiReportOutput(reportDirectory)); + + // Initialize the report with all of the execution and session + // information. At this point the report doesn't know about the + // structure of the report being created + visitor.visitInfo(sessionInfoStore.getInfos(), + executionDataStore.getContents()); + + // Populate the report structure with the bundle coverage information. + // Call visitGroup if you need groups in your report. + MultiSourceFileLocator msf = new MultiSourceFileLocator(4); + for (File file : sourceDirectories) { + msf.add(new DirectorySourceFileLocator( + file, "utf-8", 4)); + } + + visitor.visitBundle(bundleCoverage, msf); + + // Signal end of structure information to allow report to write all + // information out + visitor.visitEnd(); + + } + + private void createXmlReport(final IBundleCoverage bundleCoverage) + throws IOException { + + OutputStream fos = new FileOutputStream(xmlOutput); + try { + // Create a concrete report visitor based on some supplied + // configuration. In this case we use the defaults + final XMLFormatter htmlFormatter = new XMLFormatter(); + final IReportVisitor visitor = htmlFormatter.createVisitor(fos); + + // Initialize the report with all of the execution and session + // information. At this point the report doesn't know about the + // structure of the report being created + visitor.visitInfo(sessionInfoStore.getInfos(), + executionDataStore.getContents()); + + // Populate the report structure with the bundle coverage information. + // Call visitGroup if you need groups in your report. + visitor.visitBundle(bundleCoverage, null); + + + // Signal end of structure information to allow report to write all + // information out + visitor.visitEnd(); + } finally { + if (fos != null) { + fos.close(); + } + } + + } + + private void loadExecutionData() throws IOException { + final FileInputStream fis = new FileInputStream(executionDataFile); + try { + final ExecutionDataReader executionDataReader = new ExecutionDataReader( + fis); + executionDataStore = new ExecutionDataStore(); + sessionInfoStore = new SessionInfoStore(); + + executionDataReader.setExecutionDataVisitor(executionDataStore); + executionDataReader.setSessionInfoVisitor(sessionInfoStore); + + while (executionDataReader.read()) { + } + } finally { + if (fis != null) { + fis.close(); + } + } + } + + private IBundleCoverage analyzeStructure() throws IOException { + final CoverageBuilder coverageBuilder = new CoverageBuilder(); + final Analyzer analyzer = new Analyzer(executionDataStore, + coverageBuilder); + for (File file : classesDirectories) { + analyzer.analyzeAll(file); + + } + + return coverageBuilder.getBundle(title); + } + + @Override + public void run() { + try { + execute(); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + +} |