aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Gaston <jeffrygaston@google.com>2017-05-22 17:04:47 -0700
committerJeff Gaston <jeffrygaston@google.com>2017-05-22 17:13:44 -0700
commit369c1a464f4b750a0df7a750d5ab6bcc90202697 (patch)
treec93d8c578354ad447bf2871b69cbf5374869651e
parentce2d33f3145d39d9e9aa6e8b59d409f803a13955 (diff)
parent0e905ab139d981b47dc4ba121d0f58d5b7f6a968 (diff)
downloadjacoco-369c1a464f4b750a0df7a750d5ab6bcc90202697.tar.gz
Merge remote-tracking branch 'aosp/upstream-pull-525' into masterandroid-o-preview-3
* aosp/upstream-pull-525: Increase checked size for build result Fix description. Remove unnecessary .gitignore New option -quiet for all commands Tests for XML documentation generator. Test and documentation for multiple values. Adjust test case names to new conventions. Command to print version. Update documentation for command line interface Close ServerSocket in tests. Wrap long usage lines in documentation. Make sure the build runs with Java 5. Flush output automatically Use -dest for option name as output can be folder or file. Correct test method naming. Remove obsolete, commented configuration. Github #525: Simple Command Line Interface Change-Id: Ia4c47f78760fc756ecc029ff4bc21d265e22d82a
-rw-r--r--README.android1
-rw-r--r--jacoco/assembly.xml8
-rw-r--r--jacoco/pom.xml10
-rw-r--r--org.jacoco.build/pom.xml21
-rw-r--r--org.jacoco.cli.test/.classpath20
-rw-r--r--org.jacoco.cli.test/.project30
-rw-r--r--org.jacoco.cli.test/about.html27
-rw-r--r--org.jacoco.cli.test/pom.xml41
-rw-r--r--org.jacoco.cli.test/src/org/jacoco/cli/internal/CommandTestBase.java73
-rw-r--r--org.jacoco.cli.test/src/org/jacoco/cli/internal/MainTest.java77
-rw-r--r--org.jacoco.cli.test/src/org/jacoco/cli/internal/XmlDocumentationTest.java130
-rw-r--r--org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/ClassInfoTest.java45
-rw-r--r--org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/DumpTest.java131
-rw-r--r--org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/ExecInfoTest.java70
-rw-r--r--org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/InstrumentTest.java132
-rw-r--r--org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/MergeTest.java84
-rw-r--r--org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/ReportTest.java142
-rw-r--r--org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/VersionTest.java31
-rw-r--r--org.jacoco.cli/.classpath20
-rw-r--r--org.jacoco.cli/.project30
-rw-r--r--org.jacoco.cli/about.html27
-rw-r--r--org.jacoco.cli/pom.xml89
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/Command.java91
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/CommandHandler.java89
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/CommandParser.java33
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/Main.java106
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/XmlDocumentation.java78
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/commands/AllCommands.java49
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/commands/ClassInfo.java71
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/commands/Dump.java77
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/commands/ExecInfo.java90
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/commands/Instrument.java96
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/commands/Merge.java62
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/commands/Report.java170
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/commands/Version.java37
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/tools/ExecDumpClientTest.java26
-rw-r--r--org.jacoco.core/src/org/jacoco/core/tools/ExecDumpClient.java5
-rw-r--r--org.jacoco.doc/docroot/doc/changes.html2
-rw-r--r--org.jacoco.doc/docroot/doc/index.html1
-rw-r--r--org.jacoco.doc/docroot/doc/integrations.html2
-rw-r--r--org.jacoco.doc/docroot/index.html6
-rw-r--r--org.jacoco.doc/pom.xml39
-rw-r--r--org.jacoco.doc/xsl/cli.xsl113
-rw-r--r--org.jacoco.tests/pom.xml3
44 files changed, 2475 insertions, 10 deletions
diff --git a/README.android b/README.android
index cad30c6c..1e6118ab 100644
--- a/README.android
+++ b/README.android
@@ -14,3 +14,4 @@ is the list of the changes:
1) Remove the creation of JmxRegistration in org.jacoco.agent.rt.internal.Agent.
2) Change default OutputMode to none in org.jacoco.core.runtime.AgentOptions
+3) Merge https://github.com/jacoco/jacoco/pull/525
diff --git a/jacoco/assembly.xml b/jacoco/assembly.xml
index b25483f9..81a39386 100644
--- a/jacoco/assembly.xml
+++ b/jacoco/assembly.xml
@@ -65,6 +65,14 @@
</dependencySet>
<dependencySet>
<outputDirectory>lib</outputDirectory>
+ <outputFileNameMapping>jacococli.jar</outputFileNameMapping>
+ <useProjectArtifact>false</useProjectArtifact>
+ <includes>
+ <include>${project.groupId}:org.jacoco.cli:jar:nodeps</include>
+ </includes>
+ </dependencySet>
+ <dependencySet>
+ <outputDirectory>lib</outputDirectory>
<outputFileNameMapping>jacocoagent.jar</outputFileNameMapping>
<useProjectArtifact>false</useProjectArtifact>
<includes>
diff --git a/jacoco/pom.xml b/jacoco/pom.xml
index 5a1875da..95e29fd8 100644
--- a/jacoco/pom.xml
+++ b/jacoco/pom.xml
@@ -55,6 +55,12 @@
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
+ <artifactId>org.jacoco.cli</artifactId>
+ <version>${project.version}</version>
+ <classifier>nodeps</classifier>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
<artifactId>org.jacoco.examples</artifactId>
<version>${project.version}</version>
<type>zip</type>
@@ -104,8 +110,8 @@
<configuration>
<rules>
<requireFilesSize>
- <maxsize>3500000</maxsize>
- <minsize>2500000</minsize>
+ <maxsize>4100000</maxsize>
+ <minsize>3400000</minsize>
<files>
<file>${project.build.directory}/jacoco-${qualified.bundle.version}.zip</file>
</files>
diff --git a/org.jacoco.build/pom.xml b/org.jacoco.build/pom.xml
index e746d6ac..17e18437 100644
--- a/org.jacoco.build/pom.xml
+++ b/org.jacoco.build/pom.xml
@@ -94,12 +94,12 @@
<module>../org.jacoco.agent.rt</module>
<module>../org.jacoco.agent</module>
<module>../org.jacoco.ant</module>
-
+ <module>../org.jacoco.cli</module>
+ <module>../org.jacoco.examples</module>
<module>../jacoco-maven-plugin</module>
<module>../org.jacoco.tests</module>
- <module>../org.jacoco.examples</module>
<module>../org.jacoco.doc</module>
<module>../jacoco</module>
</modules>
@@ -143,6 +143,7 @@
<!-- Dependencies versions -->
<asm.version>5.2</asm.version>
<ant.version>1.7.1</ant.version>
+ <args4j.version>2.0.28</args4j.version>
<junit.version>4.8.2</junit.version>
<!-- ================== -->
@@ -198,6 +199,11 @@
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
+ <artifactId>org.jacoco.cli</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
<artifactId>org.jacoco.examples</artifactId>
<version>${project.version}</version>
</dependency>
@@ -223,6 +229,11 @@
<version>1.2</version>
</dependency>
<dependency>
+ <groupId>args4j</groupId>
+ <artifactId>args4j</artifactId>
+ <version>${args4j.version}</version>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
@@ -394,6 +405,12 @@
<artifactId>xml-maven-plugin</artifactId>
<version>1.0</version>
</plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <!-- latest version which runs with Java 5 -->
+ <version>1.5.0</version>
+ </plugin>
<!-- Third-party plugins -->
<plugin>
<groupId>org.codehaus.groovy.maven</groupId>
diff --git a/org.jacoco.cli.test/.classpath b/org.jacoco.cli.test/.classpath
new file mode 100644
index 00000000..0ed344a5
--- /dev/null
+++ b/org.jacoco.cli.test/.classpath
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5">
+ <attributes>
+ <attribute name="maven.pomderived" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+ <attributes>
+ <attribute name="maven.pomderived" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry including="**/*.java" kind="src" output="target/classes" path="src">
+ <attributes>
+ <attribute name="optional" value="true"/>
+ <attribute name="maven.pomderived" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/org.jacoco.cli.test/.project b/org.jacoco.cli.test/.project
new file mode 100644
index 00000000..fbd366b0
--- /dev/null
+++ b/org.jacoco.cli.test/.project
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.jacoco.cli.test</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.m2e.core.maven2Builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.m2e.core.maven2Nature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+ <linkedResources>
+ <link>
+ <name>.settings</name>
+ <type>2</type>
+ <locationURI>PARENT-1-PROJECT_LOC/org.jacoco.core.test/.settings</locationURI>
+ </link>
+ </linkedResources>
+</projectDescription>
diff --git a/org.jacoco.cli.test/about.html b/org.jacoco.cli.test/about.html
new file mode 100644
index 00000000..d31112db
--- /dev/null
+++ b/org.jacoco.cli.test/about.html
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<title>About</title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>
+ @build.date@
+</p>
+
+<h3>License</h3>
+
+<p>
+ All Content in this plug-in is made available by Mountainminds GmbH &amp; Co.
+ KG, Munich. Unless otherwise indicated below, the Content is provided to you
+ under the terms and conditions of the Eclipse Public License Version 1.0
+ (&quot;EPL&quot;). A copy of the EPL is available at
+ <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+ For purposes of the EPL, "Program" will mean the Content.
+</p>
+
+</body>
+</html> \ No newline at end of file
diff --git a/org.jacoco.cli.test/pom.xml b/org.jacoco.cli.test/pom.xml
new file mode 100644
index 00000000..bc3c6b7d
--- /dev/null
+++ b/org.jacoco.cli.test/pom.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2009, 2017 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:
+ Evgeny Mandrikov - initial API and implementation
+-->
+<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/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.jacoco</groupId>
+ <artifactId>org.jacoco.tests</artifactId>
+ <version>0.7.10-SNAPSHOT</version>
+ <relativePath>../org.jacoco.tests</relativePath>
+ </parent>
+
+ <artifactId>org.jacoco.cli.test</artifactId>
+
+ <name>JaCoCo :: Test :: Command Line Interface</name>
+
+ <properties>
+ <jacoco.includes>org.jacoco.cli.*</jacoco.includes>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>org.jacoco.cli</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/org.jacoco.cli.test/src/org/jacoco/cli/internal/CommandTestBase.java b/org.jacoco.cli.test/src/org/jacoco/cli/internal/CommandTestBase.java
new file mode 100644
index 00000000..4d12a373
--- /dev/null
+++ b/org.jacoco.cli.test/src/org/jacoco/cli/internal/CommandTestBase.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+
+import org.junit.Before;
+
+/**
+ * Base class for command tests.
+ */
+public abstract class CommandTestBase {
+
+ protected StringWriter out;
+ protected StringWriter err;
+ protected int result;
+
+ @Before
+ public void before() {
+ out = new StringWriter();
+ err = new StringWriter();
+ }
+
+ protected int execute(String... args) throws Exception {
+ result = new Main(args).execute(new PrintWriter(out),
+ new PrintWriter(err));
+ return result;
+ }
+
+ protected void assertOk() {
+ assertEquals(err.toString(), 0, result);
+ }
+
+ protected void assertFailure() {
+ assertEquals(-1, result);
+ }
+
+ protected void assertNoOutput(StringWriter buffer) {
+ assertEquals("", buffer.toString());
+ }
+
+ protected void assertContains(String expected, StringWriter buffer) {
+ final String content = buffer.toString();
+ assertTrue(content, content.contains(expected));
+ }
+
+ protected String getClassPath() {
+ final String name = getClass().getName();
+ final String res = "/" + name.replace('.', '/') + ".class";
+ String loc = getClass().getResource(res).getFile();
+ try {
+ loc = URLDecoder.decode(loc, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ }
+ return loc.substring(0, loc.length() - res.length());
+ }
+
+}
diff --git a/org.jacoco.cli.test/src/org/jacoco/cli/internal/MainTest.java b/org.jacoco.cli.test/src/org/jacoco/cli/internal/MainTest.java
new file mode 100644
index 00000000..b60780c2
--- /dev/null
+++ b/org.jacoco.cli.test/src/org/jacoco/cli/internal/MainTest.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link Main}.
+ */
+public class MainTest extends CommandTestBase {
+
+ @Test
+ public void should_print_usage_when_no_arguments_given() throws Exception {
+ execute();
+
+ assertFailure();
+ assertNoOutput(out);
+ assertContains("Argument \"<command>\" is required", err);
+ assertContains("Usage: java -jar jacococli.jar -help | <command>", err);
+ assertContains("Command line interface for JaCoCo.", err);
+ }
+
+ @Test
+ public void should_print_error_message_when_invalid_command_is_given()
+ throws Exception {
+ execute("foo");
+
+ assertFailure();
+ assertNoOutput(out);
+ assertContains("\"foo\" is not a valid value for \"<command>\"", err);
+ assertContains("Usage: java -jar jacococli.jar -help | <command>", err);
+ }
+
+ @Test
+ public void should_print_general_usage_when_help_option_is_given()
+ throws Exception {
+ execute("-help");
+
+ assertOk();
+ assertNoOutput(err);
+ assertContains("Usage: java -jar jacococli.jar -help | <command>", out);
+ assertContains("<command> : dump|instrument|merge|report", out);
+ }
+
+ @Test
+ public void should_print_command_usage_when_command_and_help_option_is_given()
+ throws Exception {
+ execute("dump", "-help");
+
+ assertOk();
+ assertNoOutput(err);
+ assertContains("Usage: java -jar jacococli.jar dump", out);
+ assertContains(
+ "Request execution data from a JaCoCo agent running in 'tcpserver' output mode.",
+ out);
+ }
+
+ @Test
+ public void should_not_print_any_output_when_quiet_option_is_given()
+ throws Exception {
+ execute("version", "-quiet");
+
+ assertOk();
+ assertNoOutput(out);
+ assertNoOutput(err);
+ }
+
+}
diff --git a/org.jacoco.cli.test/src/org/jacoco/cli/internal/XmlDocumentationTest.java b/org.jacoco.cli.test/src/org/jacoco/cli/internal/XmlDocumentationTest.java
new file mode 100644
index 00000000..83f79bc9
--- /dev/null
+++ b/org.jacoco.cli.test/src/org/jacoco/cli/internal/XmlDocumentationTest.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.w3c.dom.Document;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * Unit tests for {@link XmlDocumentation}.
+ */
+public class XmlDocumentationTest {
+
+ @Rule
+ public TemporaryFolder tmp = new TemporaryFolder();
+
+ private DocumentBuilder builder;
+ private XPath xpath;
+
+ @Before
+ public void before() throws Exception {
+ final DocumentBuilderFactory builderFactory = DocumentBuilderFactory
+ .newInstance();
+ builder = builderFactory.newDocumentBuilder();
+ builder.setErrorHandler(new ErrorHandler() {
+ public void error(SAXParseException exception) throws SAXException {
+ fail(exception.getMessage());
+ }
+
+ public void fatalError(SAXParseException exception)
+ throws SAXException {
+ fail(exception.getMessage());
+ }
+
+ public void warning(SAXParseException exception)
+ throws SAXException {
+ fail(exception.getMessage());
+ }
+ });
+
+ xpath = XPathFactory.newInstance().newXPath();
+ }
+
+ @Test
+ public void should_create_documentation() throws Exception {
+ File file = new File(tmp.getRoot(), "doc.xml");
+
+ XmlDocumentation.main(file.getAbsolutePath());
+
+ Document doc = parse(file);
+
+ assertContains("java -jar jacococli.jar report",
+ "/documentation/command[@name='report']/usage/text()", doc);
+
+ assertContains("Generate reports",
+ "/documentation/command[@name='report']/description/text()",
+ doc);
+
+ assertContains("<execfiles>",
+ "/documentation/command[@name='report']/option[1]/usage/text()",
+ doc);
+
+ assertContains("false",
+ "/documentation/command[@name='report']/option[1]/@required",
+ doc);
+
+ assertContains("true",
+ "/documentation/command[@name='report']/option[1]/@multiple",
+ doc);
+
+ assertContains("-classfiles <path>",
+ "/documentation/command[@name='report']/option[2]/usage/text()",
+ doc);
+
+ assertContains("true",
+ "/documentation/command[@name='report']/option[2]/@multiple",
+ doc);
+
+ }
+
+ private Document parse(File file) throws Exception {
+ InputStream in = new FileInputStream(file);
+ try {
+ return builder.parse(new InputSource(in));
+ } finally {
+ in.close();
+ }
+ }
+
+ private void assertContains(String expected, String query, Document doc)
+ throws XPathExpressionException {
+ final String actual = eval(query, doc);
+ assertTrue(actual, actual.contains(expected));
+ }
+
+ private String eval(String query, Document doc)
+ throws XPathExpressionException {
+ return (String) xpath.evaluate(query, doc, XPathConstants.STRING);
+ }
+
+}
diff --git a/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/ClassInfoTest.java b/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/ClassInfoTest.java
new file mode 100644
index 00000000..802be964
--- /dev/null
+++ b/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/ClassInfoTest.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal.commands;
+
+import org.jacoco.cli.internal.CommandTestBase;
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link ClassInfo}.
+ */
+public class ClassInfoTest extends CommandTestBase {
+
+ @Test
+ public void should_print_usage_when_invalid_option_is_given()
+ throws Exception {
+ execute("classinfo", "-invalid");
+
+ assertFailure();
+ assertContains("\"-invalid\" is not a valid option", err);
+ assertContains(
+ "java -jar jacococli.jar classinfo [<classlocations> ...]",
+ err);
+ }
+
+ @Test
+ public void should_print_class_info() throws Exception {
+ execute("classinfo", getClassPath());
+
+ assertOk();
+ assertContains(
+ "class name: org/jacoco/cli/internal/commands/ClassInfoTest",
+ out);
+ assertContains("methods: 3", out);
+ }
+
+}
diff --git a/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/DumpTest.java b/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/DumpTest.java
new file mode 100644
index 00000000..62e7dc44
--- /dev/null
+++ b/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/DumpTest.java
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal.commands;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import org.jacoco.cli.internal.CommandTestBase;
+import org.jacoco.core.runtime.IRemoteCommandVisitor;
+import org.jacoco.core.runtime.RemoteControlReader;
+import org.jacoco.core.runtime.RemoteControlWriter;
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * Unit tests for {@link Dump}.
+ */
+public class DumpTest extends CommandTestBase {
+
+ @Rule
+ public TemporaryFolder tmp = new TemporaryFolder();
+
+ private ServerSocket serverSocket;
+
+ @After
+ public void after() throws IOException {
+ if (serverSocket != null) {
+ serverSocket.close();
+ }
+ }
+
+ @Test
+ public void should_print_usage_when_no_argument_is_given()
+ throws Exception {
+ execute("dump");
+ assertFailure();
+ assertContains("Option \"-destfile\" is required", err);
+ assertContains("java -jar jacococli.jar dump [-address <address>]",
+ err);
+ }
+
+ @Test
+ public void should_write_dump() throws Exception {
+
+ File execfile = new File(tmp.getRoot(), "jacoco.exec");
+ int port = startMockServer();
+
+ execute("dump", "-destfile", execfile.getAbsolutePath(), "-port",
+ String.valueOf(port));
+
+ assertOk();
+ assertContains("[INFO] Connecting to ", out);
+ assertContains("[INFO] Writing execution data to "
+ + execfile.getAbsolutePath(), out);
+ assertTrue(execfile.exists());
+ }
+
+ @Test
+ public void should_log_connection_error_when_retry_is_specified()
+ throws Exception {
+
+ File execfile = new File(tmp.getRoot(), "jacoco.exec");
+ int port = unusedPort();
+
+ try {
+ execute("dump", "-destfile", execfile.getAbsolutePath(), "-port",
+ String.valueOf(port), "-retry", "1");
+ fail("IOException expected");
+ } catch (IOException ignore) {
+ }
+
+ assertContains("[WARN] Connection refused", err);
+ }
+
+ private int startMockServer() throws IOException {
+ serverSocket = new ServerSocket(0, 0, InetAddress.getByName(null));
+ new Thread() {
+ @Override
+ public void run() {
+ try {
+ serveRequest(serverSocket.accept());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }.start();
+ return serverSocket.getLocalPort();
+ }
+
+ private void serveRequest(Socket socket) throws IOException {
+ final RemoteControlWriter writer = new RemoteControlWriter(
+ socket.getOutputStream());
+ final RemoteControlReader reader = new RemoteControlReader(
+ socket.getInputStream());
+ reader.setRemoteCommandVisitor(new IRemoteCommandVisitor() {
+
+ public void visitDumpCommand(boolean dump, boolean reset)
+ throws IOException {
+ writer.sendCmdOk();
+ }
+ });
+ while (reader.read()) {
+ }
+ }
+
+ private int unusedPort() throws IOException {
+ final ServerSocket serverSocket = new ServerSocket(0, 0,
+ InetAddress.getByName(null));
+ final int port = serverSocket.getLocalPort();
+ serverSocket.close();
+ return port;
+ }
+
+}
diff --git a/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/ExecInfoTest.java b/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/ExecInfoTest.java
new file mode 100644
index 00000000..fdf1fd43
--- /dev/null
+++ b/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/ExecInfoTest.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal.commands;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.jacoco.cli.internal.CommandTestBase;
+import org.jacoco.core.data.ExecutionData;
+import org.jacoco.core.data.ExecutionDataWriter;
+import org.jacoco.core.data.SessionInfo;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * Unit tests for {@link ExecInfo}.
+ */
+public class ExecInfoTest extends CommandTestBase {
+
+ @Rule
+ public TemporaryFolder tmp = new TemporaryFolder();
+
+ @Test
+ public void should_print_usage_when_invalid_argument_is_given()
+ throws Exception {
+ execute("execinfo", "-invalid");
+
+ assertFailure();
+ assertContains("\"-invalid\" is not a valid option", err);
+ assertContains("java -jar jacococli.jar execinfo [<execfiles> ...]",
+ err);
+ }
+
+ @Test
+ public void should_print_execution_data_info() throws Exception {
+ File execfile = createExecFile();
+
+ execute("execinfo", execfile.getAbsolutePath());
+
+ assertOk();
+ assertContains("[INFO] Loading exec file " + execfile.getAbsolutePath(),
+ out);
+ assertContains("CLASS ID HITS/PROBES CLASS NAME", out);
+ assertContains("Session \"testid\":", out);
+ assertContains("0000000000001234 2 of 3 foo/MyClass", out);
+ }
+
+ private File createExecFile() throws IOException {
+ File f = new File(tmp.getRoot(), "test.exec");
+ final FileOutputStream out = new FileOutputStream(f);
+ final ExecutionDataWriter writer = new ExecutionDataWriter(out);
+ writer.visitSessionInfo(new SessionInfo("testid", 1, 2));
+ writer.visitClassExecution(new ExecutionData(0x1234, "foo/MyClass",
+ new boolean[] { false, true, true }));
+ out.close();
+ return f;
+ }
+
+}
diff --git a/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/InstrumentTest.java b/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/InstrumentTest.java
new file mode 100644
index 00000000..6b9d5039
--- /dev/null
+++ b/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/InstrumentTest.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal.commands;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+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.HashSet;
+import java.util.Set;
+
+import org.jacoco.cli.internal.CommandTestBase;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Unit tests for {@link Instrument}.
+ */
+public class InstrumentTest extends CommandTestBase {
+
+ @Rule
+ public TemporaryFolder tmp = new TemporaryFolder();
+
+ @Test
+ public void should_print_usage_when_no_options_are_given()
+ throws Exception {
+ execute("instrument");
+ assertFailure();
+ assertContains("Option \"-dest\" is required", err);
+ assertContains(
+ "Usage: java -jar jacococli.jar instrument [<sourcefiles> ...]",
+ err);
+ }
+
+ @Test
+ public void should_instrument_class_files_and_copy_resources()
+ throws Exception {
+ File destdir = tmp.getRoot();
+
+ execute("instrument", "-dest", destdir.getAbsolutePath(),
+ getClassPath());
+
+ assertOk();
+ assertContains("[INFO] 14 classes instrumented to "
+ + destdir.getAbsolutePath(), out);
+
+ // non class-file resources are copied:
+ assertTrue(new File(destdir, "about.html").isFile());
+
+ assertInstrumented(new File(destdir,
+ "org/jacoco/cli/internal/commands/InstrumentTest.class"));
+ }
+
+ @Test
+ public void should_not_instrument_anything_when_no_source_is_given()
+ throws Exception {
+ File destdir = tmp.getRoot();
+
+ execute("instrument", "-dest", destdir.getAbsolutePath());
+
+ assertOk();
+ assertArrayEquals(new String[0], destdir.list());
+ }
+
+ @Test
+ public void should_not_create_dest_file_when_source_class_is_broken()
+ throws Exception {
+ File srcdir = new File(tmp.getRoot(), "src");
+ srcdir.mkdir();
+ File destdir = new File(tmp.getRoot(), "dest");
+ destdir.mkdir();
+
+ OutputStream out = new FileOutputStream(
+ new File(srcdir, "Broken.class"));
+ out.write((byte) 0xca);
+ out.write((byte) 0xfe);
+ out.write((byte) 0xba);
+ out.write((byte) 0xbe);
+ out.write((byte) 0x00);
+ out.write((byte) 0x00);
+ out.write((byte) 0x00);
+ out.write((byte) 50);
+ out.close();
+
+ try {
+ execute("instrument", "-dest", destdir.getAbsolutePath(),
+ srcdir.getAbsolutePath());
+ fail("exception expected");
+ } catch (IOException expected) {
+ }
+
+ assertFalse(new File(destdir, "Broken.class").exists());
+ }
+
+ private void assertInstrumented(File classfile) throws IOException {
+ InputStream in = new FileInputStream(classfile);
+ ClassReader reader = new ClassReader(in);
+ in.close();
+ final Set<String> fields = new HashSet<String>();
+ reader.accept(new ClassVisitor(Opcodes.ASM5) {
+ @Override
+ public FieldVisitor visitField(int access, String name, String desc,
+ String signature, Object value) {
+ fields.add(name);
+ return null;
+ }
+ }, 0);
+ assertTrue(fields.contains("$jacocoData"));
+ }
+
+}
diff --git a/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/MergeTest.java b/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/MergeTest.java
new file mode 100644
index 00000000..f9132d91
--- /dev/null
+++ b/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/MergeTest.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal.commands;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.jacoco.cli.internal.CommandTestBase;
+import org.jacoco.core.data.ExecutionData;
+import org.jacoco.core.data.ExecutionDataWriter;
+import org.jacoco.core.tools.ExecFileLoader;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * Unit tests for {@link Merge}.
+ */
+public class MergeTest extends CommandTestBase {
+
+ @Rule
+ public TemporaryFolder tmp = new TemporaryFolder();
+
+ @Test
+ public void should_print_usage_when_no_options_are_given()
+ throws Exception {
+ execute("merge");
+
+ assertFailure();
+ assertContains("Option \"-destfile\" is required", err);
+ assertContains("java -jar jacococli.jar merge [<execfiles> ...]", err);
+ }
+
+ @Test
+ public void should_merge_exec_files() throws Exception {
+ File a = createExecFile("a");
+ File b = createExecFile("b");
+ File c = createExecFile("c");
+ File dest = new File(tmp.getRoot(), "merged.exec");
+
+ execute("merge", "-destfile", dest.getAbsolutePath(),
+ a.getAbsolutePath(), b.getAbsolutePath(), c.getAbsolutePath());
+
+ assertOk();
+ Set<String> names = loadExecFile(dest);
+ assertEquals(new HashSet<String>(Arrays.asList("a", "b", "c")), names);
+ }
+
+ private File createExecFile(String name) throws IOException {
+ File file = new File(tmp.getRoot(), name + ".exec");
+ final FileOutputStream execout = new FileOutputStream(file);
+ ExecutionDataWriter writer = new ExecutionDataWriter(execout);
+ writer.visitClassExecution(new ExecutionData(name.hashCode(), name,
+ new boolean[] { true }));
+ execout.close();
+ return file;
+ }
+
+ private Set<String> loadExecFile(File file) throws IOException {
+ ExecFileLoader loader = new ExecFileLoader();
+ loader.load(file);
+ Set<String> names = new HashSet<String>();
+ for (ExecutionData d : loader.getExecutionDataStore().getContents()) {
+ names.add(d.getName());
+ }
+ return names;
+ }
+
+}
diff --git a/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/ReportTest.java b/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/ReportTest.java
new file mode 100644
index 00000000..e5385ee5
--- /dev/null
+++ b/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/ReportTest.java
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal.commands;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+import org.jacoco.cli.internal.CommandTestBase;
+import org.jacoco.core.data.ExecutionData;
+import org.jacoco.core.data.ExecutionDataWriter;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * Unit tests for {@link Report}.
+ */
+public class ReportTest extends CommandTestBase {
+
+ @Rule
+ public TemporaryFolder tmp = new TemporaryFolder();
+
+ @Test
+ public void should_print_usage_when_no_options_are_given()
+ throws Exception {
+ execute("report");
+
+ assertFailure();
+ assertContains("Option \"-classfiles\" is required", err);
+ assertContains(
+ "Usage: java -jar jacococli.jar report [<execfiles> ...]", err);
+ }
+
+ @Test
+ public void should_print_number_of_analyzed_classes() throws Exception {
+ execute("report", "-classfiles", getClassPath());
+
+ assertOk();
+ assertContains("[INFO] Writing report with 14 classes.", out);
+ }
+
+ @Test
+ public void should_print_warning_when_exec_data_does_not_match()
+ throws Exception {
+ File exec = new File(tmp.getRoot(), "jacoco.exec");
+ final FileOutputStream execout = new FileOutputStream(exec);
+ ExecutionDataWriter writer = new ExecutionDataWriter(execout);
+ // Add probably invalid id for this test class:
+ writer.visitClassExecution(
+ new ExecutionData(0x123, getClass().getName().replace('.', '/'),
+ new boolean[] { true }));
+ execout.close();
+
+ execute("report", exec.getAbsolutePath(), "-classfiles",
+ getClassPath());
+
+ assertOk();
+ assertContains("[WARN] Some classes do not match with execution data.",
+ out);
+ assertContains(
+ "[WARN] For report generation the same class files must be used as at runtime.",
+ out);
+ assertContains(
+ "[WARN] Execution data for class org/jacoco/cli/internal/commands/ReportTest does not match.",
+ out);
+ }
+
+ @Test
+ public void should_create_xml_report_when_xml_option_is_provided()
+ throws Exception {
+ File xml = new File(tmp.getRoot(), "coverage.xml");
+
+ execute("report", "-classfiles", getClassPath(), "-xml",
+ xml.getAbsolutePath());
+
+ assertOk();
+ assertTrue(xml.isFile());
+ }
+
+ @Test
+ public void should_create_csv_report_when_csv_option_is_provided()
+ throws Exception {
+ File csv = new File(tmp.getRoot(), "coverage.csv");
+
+ execute("report", "-classfiles", getClassPath(), "-csv",
+ csv.getAbsolutePath());
+
+ assertOk();
+ assertTrue(csv.isFile());
+ }
+
+ @Test
+ public void should_create_html_report_when_html_option_is_provided()
+ throws Exception {
+ File html = new File(tmp.getRoot(), "coverage");
+
+ execute("report", "-classfiles", getClassPath(), "-sourcefiles",
+ "./src", "-html", html.getAbsolutePath());
+
+ assertOk();
+ assertTrue(html.isDirectory());
+ assertTrue(new File(html,
+ "org.jacoco.cli.internal.commands/ReportTest.html").isFile());
+ assertTrue(new File(html,
+ "org.jacoco.cli.internal.commands/ReportTest.java.html")
+ .isFile());
+ }
+
+ @Test
+ public void should_use_all_values_when_multiple_classfiles_options_are_provided()
+ throws Exception {
+ File html = new File(tmp.getRoot(), "coverage");
+
+ final String c1 = getClassPath()
+ + "/org/jacoco/cli/internal/commands/ReportTest.class";
+ final String c2 = getClassPath()
+ + "/org/jacoco/cli/internal/commands/DumpTest.class";
+
+ execute("report", "-classfiles", c1, "-classfiles", c2, "-html",
+ html.getAbsolutePath());
+
+ assertOk();
+ assertTrue(html.isDirectory());
+ assertTrue(new File(html,
+ "org.jacoco.cli.internal.commands/ReportTest.html").isFile());
+ assertTrue(
+ new File(html, "org.jacoco.cli.internal.commands/DumpTest.html")
+ .isFile());
+ }
+
+}
diff --git a/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/VersionTest.java b/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/VersionTest.java
new file mode 100644
index 00000000..56b259a5
--- /dev/null
+++ b/org.jacoco.cli.test/src/org/jacoco/cli/internal/commands/VersionTest.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal.commands;
+
+import org.jacoco.cli.internal.CommandTestBase;
+import org.jacoco.core.JaCoCo;
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link Version}.
+ */
+public class VersionTest extends CommandTestBase {
+
+ @Test
+ public void should_print_version() throws Exception {
+ execute("version");
+
+ assertOk();
+ assertContains(JaCoCo.VERSION, out);
+ }
+
+}
diff --git a/org.jacoco.cli/.classpath b/org.jacoco.cli/.classpath
new file mode 100644
index 00000000..0ed344a5
--- /dev/null
+++ b/org.jacoco.cli/.classpath
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5">
+ <attributes>
+ <attribute name="maven.pomderived" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+ <attributes>
+ <attribute name="maven.pomderived" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry including="**/*.java" kind="src" output="target/classes" path="src">
+ <attributes>
+ <attribute name="optional" value="true"/>
+ <attribute name="maven.pomderived" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/org.jacoco.cli/.project b/org.jacoco.cli/.project
new file mode 100644
index 00000000..828621af
--- /dev/null
+++ b/org.jacoco.cli/.project
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.jacoco.cli</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.m2e.core.maven2Builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.m2e.core.maven2Nature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+ <linkedResources>
+ <link>
+ <name>.settings</name>
+ <type>2</type>
+ <locationURI>PARENT-1-PROJECT_LOC/org.jacoco.core/.settings</locationURI>
+ </link>
+ </linkedResources>
+</projectDescription>
diff --git a/org.jacoco.cli/about.html b/org.jacoco.cli/about.html
new file mode 100644
index 00000000..d31112db
--- /dev/null
+++ b/org.jacoco.cli/about.html
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<title>About</title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>
+ @build.date@
+</p>
+
+<h3>License</h3>
+
+<p>
+ All Content in this plug-in is made available by Mountainminds GmbH &amp; Co.
+ KG, Munich. Unless otherwise indicated below, the Content is provided to you
+ under the terms and conditions of the Eclipse Public License Version 1.0
+ (&quot;EPL&quot;). A copy of the EPL is available at
+ <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+ For purposes of the EPL, "Program" will mean the Content.
+</p>
+
+</body>
+</html> \ No newline at end of file
diff --git a/org.jacoco.cli/pom.xml b/org.jacoco.cli/pom.xml
new file mode 100644
index 00000000..553472e0
--- /dev/null
+++ b/org.jacoco.cli/pom.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2009, 2017 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:
+ Marc R. Hoffmann - initial API and implementation
+-->
+<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/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.jacoco</groupId>
+ <artifactId>org.jacoco.build</artifactId>
+ <version>0.7.10-SNAPSHOT</version>
+ <relativePath>../org.jacoco.build</relativePath>
+ </parent>
+
+ <artifactId>org.jacoco.cli</artifactId>
+
+ <name>JaCoCo :: Command Line Interface</name>
+ <description>JaCoCo Command Line Interface</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>org.jacoco.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>org.jacoco.report</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>args4j</groupId>
+ <artifactId>args4j</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <sourceDirectory>src</sourceDirectory>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>java</goal>
+ </goals>
+ <configuration>
+ <mainClass>org.jacoco.cli.internal.XmlDocumentation</mainClass>
+ <arguments>
+ <argument>${project.build.directory}/generated-documentation/cli.xml</argument>
+ </arguments>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ <configuration>
+ <shadedArtifactAttached>true</shadedArtifactAttached>
+ <shadedClassifierName>nodeps</shadedClassifierName>
+ <minimizeJar>true</minimizeJar>
+ <transformers>
+ <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+ <manifestEntries>
+ <Main-Class>org.jacoco.cli.internal.Main</Main-Class>
+ </manifestEntries>
+ </transformer>
+ </transformers>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/Command.java b/org.jacoco.cli/src/org/jacoco/cli/internal/Command.java
new file mode 100644
index 00000000..06a6cea0
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/Command.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.kohsuke.args4j.Option;
+
+/**
+ * Common interface for all commands.
+ */
+public abstract class Command {
+
+ /**
+ * Common command line prefix.
+ */
+ public static final String JAVACMD = "java -jar jacococli.jar ";
+
+ /**
+ * Flag whether help should be printed for this command.
+ */
+ @Option(name = "-help", usage = "show help", help = true)
+ public boolean help = false;
+
+ /**
+ * Flag whether output to stdout should be suppressed.
+ */
+ @Option(name = "-quiet", usage = "suppress all output on stdout")
+ public boolean quiet = false;
+
+ /**
+ * @return Short description of the command.
+ */
+ public abstract String description();
+
+ /**
+ * @return name of the command
+ */
+ public String name() {
+ return getClass().getSimpleName().toLowerCase();
+ }
+
+ /**
+ * @param parser
+ * parser for this command
+ * @return usage string displayed for help
+ */
+ public String usage(final CommandParser parser) {
+ final StringWriter writer = new StringWriter();
+ parser.printSingleLineUsage(writer, null);
+ return JAVACMD + name() + writer;
+ }
+
+ /**
+ * Executes the given command.
+ *
+ * @param out
+ * std out
+ * @param err
+ * std err
+ * @return exit code, should be 0 for normal operation
+ * @throws Exception
+ * any exception that my occur during execution
+ */
+ public abstract int execute(PrintWriter out, PrintWriter err)
+ throws Exception;
+
+ /**
+ * Prints textual help for this command.
+ *
+ * @param writer
+ * output destination
+ */
+ protected void printHelp(final PrintWriter writer) {
+ final CommandParser parser = new CommandParser(this);
+ writer.println("Usage: " + parser.getCommand().usage(parser));
+ writer.println(description());
+ parser.printUsage(writer, null);
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/CommandHandler.java b/org.jacoco.cli/src/org/jacoco/cli/internal/CommandHandler.java
new file mode 100644
index 00000000..3c6a2a54
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/CommandHandler.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal;
+
+import java.util.AbstractList;
+
+import org.jacoco.cli.internal.commands.AllCommands;
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.CmdLineParser;
+import org.kohsuke.args4j.OptionDef;
+import org.kohsuke.args4j.spi.Messages;
+import org.kohsuke.args4j.spi.OptionHandler;
+import org.kohsuke.args4j.spi.Parameters;
+import org.kohsuke.args4j.spi.Setter;
+
+/**
+ * {@link OptionHandler} which uses {@link CommandParser} internally to provide
+ * help context also for sub-commands.
+ */
+public class CommandHandler extends OptionHandler<Command> {
+
+ /**
+ * This constructor is required by the args4j framework.
+ *
+ * @param parser
+ * @param option
+ * @param setter
+ */
+ public CommandHandler(final CmdLineParser parser, final OptionDef option,
+ final Setter<Object> setter) {
+ super(parser,
+ new OptionDef(AllCommands.names(), "<command>",
+ option.required(), option.help(), option.hidden(),
+ CommandHandler.class, option.isMultiValued()) {
+ }, setter);
+ }
+
+ @Override
+ public int parseArguments(final Parameters params) throws CmdLineException {
+ final String subCmd = params.getParameter(0);
+
+ for (final Command c : AllCommands.get()) {
+ if (c.name().equals(subCmd)) {
+ parseSubArguments(c, params);
+ setter.addValue(c);
+ return params.size(); // consume all the remaining tokens
+ }
+ }
+
+ throw new CmdLineException(owner,
+ Messages.ILLEGAL_OPERAND.format(option.toString(), subCmd));
+ }
+
+ private void parseSubArguments(final Command c, final Parameters params)
+ throws CmdLineException {
+ final CmdLineParser p = new CommandParser(c);
+ p.parseArgument(new AbstractList<String>() {
+ @Override
+ public String get(final int index) {
+ try {
+ return params.getParameter(index + 1);
+ } catch (final CmdLineException e) {
+ // invalid index was accessed.
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ @Override
+ public int size() {
+ return params.size() - 1;
+ }
+ });
+ }
+
+ @Override
+ public String getDefaultMetaVariable() {
+ return "<command>";
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/CommandParser.java b/org.jacoco.cli/src/org/jacoco/cli/internal/CommandParser.java
new file mode 100644
index 00000000..0e31093b
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/CommandParser.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal;
+
+import org.kohsuke.args4j.CmdLineParser;
+
+/**
+ * Parser which remembers the parsed command to have additional context
+ * information to produce help output.
+ */
+public class CommandParser extends CmdLineParser {
+
+ private final Command command;
+
+ CommandParser(final Command command) {
+ super(command);
+ this.command = command;
+ }
+
+ Command getCommand() {
+ return command;
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/Main.java b/org.jacoco.cli/src/org/jacoco/cli/internal/Main.java
new file mode 100644
index 00000000..8c6f3be4
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/Main.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.CmdLineException;
+
+/**
+ * Entry point for all command line operations.
+ */
+public class Main extends Command {
+
+ private static final PrintWriter NUL = new PrintWriter(new Writer() {
+
+ @Override
+ public void write(final char[] arg0, final int arg1, final int arg2)
+ throws IOException {
+ }
+
+ @Override
+ public void flush() throws IOException {
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+ });
+
+ private final String[] args;
+
+ Main(final String... args) {
+ this.args = args;
+ }
+
+ @Argument(handler = CommandHandler.class, required = true)
+ Command command;
+
+ @Override
+ public String description() {
+ return "Command line interface for JaCoCo.";
+ }
+
+ @Override
+ public String usage(final CommandParser parser) {
+ return JAVACMD + "-help | <command>";
+ }
+
+ @Override
+ public int execute(PrintWriter out, final PrintWriter err)
+ throws Exception {
+
+ final CommandParser mainParser = new CommandParser(this);
+ try {
+ mainParser.parseArgument(args);
+ } catch (final CmdLineException e) {
+ err.println(e.getMessage());
+ err.println();
+ ((CommandParser) e.getParser()).getCommand().printHelp(err);
+ return -1;
+ }
+
+ if (help) {
+ printHelp(out);
+ return 0;
+ }
+
+ if (command.help) {
+ command.printHelp(out);
+ return 0;
+ }
+
+ if (command.quiet) {
+ out = NUL;
+ }
+
+ return command.execute(out, err);
+ }
+
+ /**
+ * Main entry point for program invocations.
+ *
+ * @param args
+ * program arguments
+ * @throws Exception
+ * All internal exceptions are directly passed on to get printed
+ * on the console
+ */
+ public static void main(final String... args) throws Exception {
+ new Main(args).execute(new PrintWriter(System.out, true),
+ new PrintWriter(System.err, true));
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/XmlDocumentation.java b/org.jacoco.cli/src/org/jacoco/cli/internal/XmlDocumentation.java
new file mode 100644
index 00000000..83241c9a
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/XmlDocumentation.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+import org.jacoco.cli.internal.commands.AllCommands;
+import org.jacoco.report.internal.xml.XMLDocument;
+import org.jacoco.report.internal.xml.XMLElement;
+import org.kohsuke.args4j.spi.OptionHandler;
+
+/**
+ * Internal utility to dump all command descriptions as XML.
+ */
+public final class XmlDocumentation {
+
+ private XmlDocumentation() {
+ }
+
+ private static void writeCommand(final Command command,
+ final XMLElement parent) throws IOException {
+ final CommandParser parser = new CommandParser(command);
+ final XMLElement element = parent.element("command");
+ element.attr("name", command.name());
+ element.element("usage").text(command.usage(parser));
+ element.element("description").text(command.description());
+ writeOptions(element, parser.getArguments());
+ writeOptions(element, parser.getOptions());
+ }
+
+ private static void writeOptions(final XMLElement parent,
+ @SuppressWarnings("rawtypes") final List<OptionHandler> list)
+ throws IOException {
+ for (final OptionHandler<?> o : list) {
+ final XMLElement optionNode = parent.element("option");
+ optionNode.attr("required", String.valueOf(o.option.required()));
+ optionNode.attr("multiple",
+ String.valueOf(o.setter.isMultiValued()));
+ optionNode.element("usage").text(o.getNameAndMeta(null));
+ optionNode.element("description").text(o.option.usage());
+ }
+ }
+
+ /**
+ * Called during the build process.
+ *
+ * @param args
+ * exactly one argument expected with the target location
+ * @throws IOException
+ * if XML document cannot be written
+ */
+ public static void main(final String... args) throws IOException {
+ final File file = new File(args[0]);
+ file.getParentFile().mkdirs();
+
+ final XMLElement root = new XMLDocument("documentation", null, null,
+ "UTF-8", true, new FileOutputStream(file));
+
+ for (final Command c : AllCommands.get()) {
+ writeCommand(c, root);
+ }
+
+ root.close();
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/commands/AllCommands.java b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/AllCommands.java
new file mode 100644
index 00000000..e0df1d91
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/AllCommands.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal.commands;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.jacoco.cli.internal.Command;
+
+/**
+ * List of all available commands.
+ */
+public final class AllCommands {
+
+ private AllCommands() {
+ }
+
+ /**
+ * @return list of new instances of all available commands
+ */
+ public static List<Command> get() {
+ return Arrays.asList(new Dump(), new Instrument(), new Merge(),
+ new Report(), new ClassInfo(), new ExecInfo(), new Version());
+ }
+
+ /**
+ * @return String containing all available command names
+ */
+ public static String names() {
+ final StringBuilder sb = new StringBuilder();
+ for (final Command c : get()) {
+ if (sb.length() > 0) {
+ sb.append('|');
+ }
+ sb.append(c.name());
+ }
+ return sb.toString();
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/commands/ClassInfo.java b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/ClassInfo.java
new file mode 100644
index 00000000..6cf66e14
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/ClassInfo.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal.commands;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jacoco.cli.internal.Command;
+import org.jacoco.core.analysis.Analyzer;
+import org.jacoco.core.analysis.IClassCoverage;
+import org.jacoco.core.analysis.ICoverageVisitor;
+import org.jacoco.core.data.ExecutionDataStore;
+import org.kohsuke.args4j.Argument;
+
+/**
+ * The <code>classinfo</code> command.
+ */
+public class ClassInfo extends Command {
+
+ @Argument(usage = "location of Java class files", metaVar = "<classlocations>")
+ List<File> classfiles = new ArrayList<File>();
+
+ @Override
+ public String description() {
+ return "Print information about Java class files at the provided location.";
+ }
+
+ @Override
+ public int execute(final PrintWriter out, final PrintWriter err)
+ throws IOException {
+ final Analyzer analyzer = new Analyzer(new ExecutionDataStore(),
+ new ICoverageVisitor() {
+ public void visitCoverage(final IClassCoverage coverage) {
+ print(coverage, out);
+ }
+ });
+
+ for (final File file : classfiles) {
+ analyzer.analyzeAll(file);
+ }
+ return 0;
+ }
+
+ private void print(final IClassCoverage coverage, final PrintWriter out) {
+ out.printf("class name: %s%n", coverage.getName());
+ out.printf("class id: %016x%n", Long.valueOf(coverage.getId()));
+ out.printf("instructions: %s%n", Integer
+ .valueOf(coverage.getInstructionCounter().getTotalCount()));
+ out.printf("branches: %s%n",
+ Integer.valueOf(coverage.getBranchCounter().getTotalCount()));
+ out.printf("lines: %s%n",
+ Integer.valueOf(coverage.getLineCounter().getTotalCount()));
+ out.printf("methods: %s%n",
+ Integer.valueOf(coverage.getMethodCounter().getTotalCount()));
+ out.printf("complexity: %s%n%n", Integer
+ .valueOf(coverage.getComplexityCounter().getTotalCount()));
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Dump.java b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Dump.java
new file mode 100644
index 00000000..d56a2a18
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Dump.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal.commands;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.InetAddress;
+
+import org.jacoco.cli.internal.Command;
+import org.jacoco.core.runtime.AgentOptions;
+import org.jacoco.core.tools.ExecDumpClient;
+import org.jacoco.core.tools.ExecFileLoader;
+import org.kohsuke.args4j.Option;
+
+/**
+ * The <code>dump</code> command.
+ */
+public class Dump extends Command {
+
+ @Option(name = "-address", usage = "host name or ip address to connect to (default localhost)", metaVar = "<address>")
+ String address = AgentOptions.DEFAULT_ADDRESS;
+
+ @Option(name = "-port", usage = "the port to connect to (default 6300)", metaVar = "<port>")
+ int port = AgentOptions.DEFAULT_PORT;
+
+ @Option(name = "-destfile", usage = "file to write execution data to", metaVar = "<path>", required = true)
+ File destfile;
+
+ @Option(name = "-reset", usage = "reset execution data on test target after dump")
+ boolean reset = false;
+
+ @Option(name = "-retry", usage = "number of retries (default 0)", metaVar = "<count>")
+ int retrycount = 0;
+
+ @Override
+ public String description() {
+ return "Request execution data from a JaCoCo agent running in 'tcpserver' output mode.";
+ }
+
+ @Override
+ public int execute(final PrintWriter out, final PrintWriter err)
+ throws Exception {
+ final ExecDumpClient client = new ExecDumpClient() {
+ @Override
+ protected void onConnecting(final InetAddress address,
+ final int port) {
+ out.printf("[INFO] Connecting to %s:%s.%n", address,
+ Integer.valueOf(port));
+ }
+
+ @Override
+ protected void onConnectionFailure(final IOException exception) {
+ err.printf("[WARN] %s.%n", exception.getMessage());
+ }
+ };
+ client.setReset(reset);
+ client.setRetryCount(retrycount);
+
+ final ExecFileLoader loader = client.dump(address, port);
+ out.printf("[INFO] Writing execution data to %s.%n",
+ destfile.getAbsolutePath());
+ loader.save(destfile, true);
+
+ return 0;
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/commands/ExecInfo.java b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/ExecInfo.java
new file mode 100644
index 00000000..e798bf6f
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/ExecInfo.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal.commands;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.jacoco.cli.internal.Command;
+import org.jacoco.core.data.ExecutionData;
+import org.jacoco.core.data.ExecutionDataReader;
+import org.jacoco.core.data.IExecutionDataVisitor;
+import org.jacoco.core.data.ISessionInfoVisitor;
+import org.jacoco.core.data.SessionInfo;
+import org.kohsuke.args4j.Argument;
+
+/**
+ * The <code>execinfo</code> command.
+ */
+public class ExecInfo extends Command {
+
+ @Argument(usage = "list of JaCoCo *.exec files to read", metaVar = "<execfiles>")
+ List<File> execfiles = new ArrayList<File>();
+
+ @Override
+ public String description() {
+ return "Print exec file content in human readable format.";
+ }
+
+ @Override
+ public int execute(final PrintWriter out, final PrintWriter err)
+ throws IOException {
+ for (final File file : execfiles) {
+ dump(file, out);
+ }
+ return 0;
+ }
+
+ private void dump(final File file, final PrintWriter out)
+ throws IOException {
+ out.printf("[INFO] Loading exec file %s.%n", file);
+ out.println("CLASS ID HITS/PROBES CLASS NAME");
+
+ final FileInputStream in = new FileInputStream(file);
+ final ExecutionDataReader reader = new ExecutionDataReader(in);
+ reader.setSessionInfoVisitor(new ISessionInfoVisitor() {
+ public void visitSessionInfo(final SessionInfo info) {
+ out.printf("Session \"%s\": %s - %s%n", info.getId(),
+ new Date(info.getStartTimeStamp()),
+ new Date(info.getDumpTimeStamp()));
+ }
+ });
+ reader.setExecutionDataVisitor(new IExecutionDataVisitor() {
+ public void visitClassExecution(final ExecutionData data) {
+ out.printf("%016x %3d of %3d %s%n",
+ Long.valueOf(data.getId()),
+ Integer.valueOf(getHitCount(data.getProbes())),
+ Integer.valueOf(data.getProbes().length),
+ data.getName());
+ }
+ });
+ reader.read();
+ in.close();
+ out.println();
+ }
+
+ private int getHitCount(final boolean[] data) {
+ int count = 0;
+ for (final boolean hit : data) {
+ if (hit) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Instrument.java b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Instrument.java
new file mode 100644
index 00000000..5fefc9dc
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Instrument.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jacoco.cli.internal.Command;
+import org.jacoco.core.instr.Instrumenter;
+import org.jacoco.core.runtime.OfflineInstrumentationAccessGenerator;
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.Option;
+
+/**
+ * The <code>instrument</code> command.
+ */
+public class Instrument extends Command {
+
+ @Option(name = "-dest", usage = "path to write instrumented Java classes to", metaVar = "<dir>", required = true)
+ File dest;
+
+ @Argument(usage = "list of folder or files to instrument recusively", metaVar = "<sourcefiles>")
+ List<File> source = new ArrayList<File>();
+
+ private Instrumenter instrumenter;
+
+ @Override
+ public String description() {
+ return "Off-line instrumentation of Java class files and JAR files.";
+ }
+
+ @Override
+ public int execute(final PrintWriter out, final PrintWriter err)
+ throws IOException {
+ instrumenter = new Instrumenter(
+ new OfflineInstrumentationAccessGenerator());
+ int total = 0;
+ for (final File s : source) {
+ total += instrumentRecursive(s, dest);
+ }
+ out.printf("[INFO] %s classes instrumented to %s.%n",
+ Integer.valueOf(total), dest.getAbsolutePath());
+ return 0;
+ }
+
+ private int instrumentRecursive(final File src, final File dest)
+ throws IOException {
+ int total = 0;
+ if (src.isDirectory()) {
+ for (final File child : src.listFiles()) {
+ total += instrumentRecursive(child,
+ new File(dest, child.getName()));
+ }
+ } else {
+ total += instrument(src, dest);
+ }
+ return total;
+ }
+
+ private int instrument(final File src, final File dest) throws IOException {
+ dest.getParentFile().mkdirs();
+ final InputStream input = new FileInputStream(src);
+ try {
+ final OutputStream output = new FileOutputStream(dest);
+ try {
+ return instrumenter.instrumentAll(input, output,
+ src.getAbsolutePath());
+ } finally {
+ output.close();
+ }
+ } catch (final IOException e) {
+ dest.delete();
+ throw e;
+ } finally {
+ input.close();
+ }
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Merge.java b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Merge.java
new file mode 100644
index 00000000..530285c8
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Merge.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal.commands;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jacoco.cli.internal.Command;
+import org.jacoco.core.tools.ExecFileLoader;
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.Option;
+
+/**
+ * The <code>merge</code> command.
+ */
+public class Merge extends Command {
+
+ @Argument(usage = "list of JaCoCo *.exec files to read", metaVar = "<execfiles>")
+ List<File> execfiles = new ArrayList<File>();
+
+ @Option(name = "-destfile", usage = "file to write merged execution data to", metaVar = "<path>", required = true)
+ File destfile;
+
+ @Override
+ public String description() {
+ return "Merges multiple exec files into a new one.";
+ }
+
+ @Override
+ public int execute(final PrintWriter out, final PrintWriter err)
+ throws IOException {
+ final ExecFileLoader loader = loadExecutionData(out);
+ out.printf("[INFO] Writing execution data to %s.%n",
+ destfile.getAbsolutePath());
+ loader.save(destfile, true);
+ return 0;
+ }
+
+ private ExecFileLoader loadExecutionData(final PrintWriter out)
+ throws IOException {
+ final ExecFileLoader loader = new ExecFileLoader();
+ for (final File file : execfiles) {
+ out.printf("[INFO] Loading execution data file %s.%n",
+ file.getAbsolutePath());
+ loader.load(file);
+ }
+ return loader;
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Report.java b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Report.java
new file mode 100644
index 00000000..be2fbfe4
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Report.java
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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 <code>report</code> command.
+ */
+public class Report extends Command {
+
+ @Argument(usage = "list of JaCoCo *.exec files to read", metaVar = "<execfiles>")
+ List<File> execfiles = new ArrayList<File>();
+
+ @Option(name = "-classfiles", usage = "location of Java class files", metaVar = "<path>", required = true)
+ List<File> classfiles = new ArrayList<File>();
+
+ @Option(name = "-sourcefiles", usage = "location of the source files", metaVar = "<path>")
+ List<File> sourcefiles = new ArrayList<File>();
+
+ @Option(name = "-tabwith", usage = "tab stop width for the source pages (default 4)", metaVar = "<n>")
+ int tabwidth = 4;
+
+ @Option(name = "-name", usage = "name used for this report", metaVar = "<name>")
+ String name = "JaCoCo Coverage Report";
+
+ @Option(name = "-encoding", usage = "source file encoding (default platform encoding)", metaVar = "<charset>")
+ String encoding;
+
+ @Option(name = "-xml", usage = "output file for the XML report", metaVar = "<file>")
+ File xml;
+
+ @Option(name = "-csv", usage = "output file for the CSV report", metaVar = "<file>")
+ File csv;
+
+ @Option(name = "-html", usage = "output directory for the HTML report", metaVar = "<dir>")
+ 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();
+ 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<IClassCoverage> 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] Writing report with %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<IReportVisitor> visitors = new ArrayList<IReportVisitor>();
+
+ 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;
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Version.java b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Version.java
new file mode 100644
index 00000000..ef1e33a1
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Version.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal.commands;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import org.jacoco.cli.internal.Command;
+import org.jacoco.core.JaCoCo;
+
+/**
+ * The <code>version</code> command.
+ */
+public class Version extends Command {
+
+ @Override
+ public String description() {
+ return "Print JaCoCo version information.";
+ }
+
+ @Override
+ public int execute(final PrintWriter out, final PrintWriter err)
+ throws IOException {
+ out.println(JaCoCo.VERSION);
+ return 0;
+ }
+
+}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/tools/ExecDumpClientTest.java b/org.jacoco.core.test/src/org/jacoco/core/tools/ExecDumpClientTest.java
index 3fe1688f..80918149 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/tools/ExecDumpClientTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/tools/ExecDumpClientTest.java
@@ -101,7 +101,7 @@ public class ExecDumpClientTest {
// 3. Retry
"onConnectionFailure", "onConnecting")
- , callbacks);
+ , callbacks);
}
@Test
@@ -126,6 +126,12 @@ public class ExecDumpClientTest {
assertTrue(resetRequested);
}
+ @Test(expected = IOException.class)
+ public void testNopServer() throws IOException {
+ int port = createNopServer();
+ client.dump((String) null, port);
+ }
+
private int getFreePort() throws IOException {
final ServerSocket server = new ServerSocket(0, 0,
InetAddress.getByName(null));
@@ -159,11 +165,27 @@ public class ExecDumpClientTest {
dumpRequested = dump;
resetRequested = reset;
if (dump) {
- writer.visitSessionInfo(new SessionInfo("TestId", 100, 200));
+ writer.visitSessionInfo(
+ new SessionInfo("TestId", 100, 200));
}
writer.sendCmdOk();
}
});
reader.read();
}
+
+ private int createNopServer() throws IOException {
+ server = new ServerSocket(0, 0, InetAddress.getByName(null));
+ new Thread(new Runnable() {
+ public void run() {
+ try {
+ server.accept().close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }).start();
+ return server.getLocalPort();
+ }
+
}
diff --git a/org.jacoco.core/src/org/jacoco/core/tools/ExecDumpClient.java b/org.jacoco.core/src/org/jacoco/core/tools/ExecDumpClient.java
index 39fd8f34..e99204da 100644
--- a/org.jacoco.core/src/org/jacoco/core/tools/ExecDumpClient.java
+++ b/org.jacoco.core/src/org/jacoco/core/tools/ExecDumpClient.java
@@ -123,7 +123,10 @@ public class ExecDumpClient {
.setExecutionDataVisitor(loader.getExecutionDataStore());
remoteWriter.visitDumpCommand(dump, reset);
- remoteReader.read();
+
+ if (!remoteReader.read()) {
+ throw new IOException("Socket closed unexpectedly.");
+ }
} finally {
socket.close();
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index 8d2e4f8c..6561c813 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -38,6 +38,8 @@
(GitHub <a href="https://github.com/jacoco/jacoco/issues/529">#529</a>).</li>
<li>Maven aggregated reports will now also include modules of runtime dependencies
(GitHub <a href="https://github.com/jacoco/jacoco/issues/498">#498</a>).</li>
+ <li>JaCoCo now comes with a simple command line interface
+ (GitHub <a href="https://github.com/jacoco/jacoco/issues/525">#525</a>).</li>
</ul>
<h3>Non-functional Changes</h3>
diff --git a/org.jacoco.doc/docroot/doc/index.html b/org.jacoco.doc/docroot/doc/index.html
index 4de1a589..e17a3491 100644
--- a/org.jacoco.doc/docroot/doc/index.html
+++ b/org.jacoco.doc/docroot/doc/index.html
@@ -44,6 +44,7 @@
<li><a href="examples/build/pom.xml">Maven Usage Example</a> -
<a href="examples/build/pom-offline.xml">Offline Example</a></li>
<li><a href="agent.html">Java Agent</a></li>
+ <li><a href="cli.html">Command Line Interface</a></li>
<li><a href="classids.html">Class Ids</a></li>
<li><a href="offline.html">Offline Instrumentation</a></li>
<li><a href="faq.html">FAQ</a></li>
diff --git a/org.jacoco.doc/docroot/doc/integrations.html b/org.jacoco.doc/docroot/doc/integrations.html
index 992417c3..beadc01a 100644
--- a/org.jacoco.doc/docroot/doc/integrations.html
+++ b/org.jacoco.doc/docroot/doc/integrations.html
@@ -40,7 +40,7 @@
</tr>
<tr>
<td>Command Line</td>
- <td><a href="agent.html">JaCoCo Manual</a></td>
+ <td><a href="cli.html">JaCoCo Manual</a></td>
<td></td>
</tr>
<tr>
diff --git a/org.jacoco.doc/docroot/index.html b/org.jacoco.doc/docroot/index.html
index 9a7991ad..f9f082c8 100644
--- a/org.jacoco.doc/docroot/index.html
+++ b/org.jacoco.doc/docroot/index.html
@@ -68,6 +68,12 @@
<td>Ant <i>(all other dependencies included)</i></td>
</tr>
<tr>
+ <td><span class="el_jar">jacococli.jar</span></td>
+ <td>no</td>
+ <td>JaCoCo Command Line Interface</td>
+ <td>- <i>(all dependencies included)</i></td>
+ </tr>
+ <tr>
<td><span class="el_jar">org.jacoco.agent_@qualified.bundle.version@.jar</span></td>
<td>yes</td>
<td>JaCoCo agent</td>
diff --git a/org.jacoco.doc/pom.xml b/org.jacoco.doc/pom.xml
index 505eb10b..c43b3ec7 100644
--- a/org.jacoco.doc/pom.xml
+++ b/org.jacoco.doc/pom.xml
@@ -82,6 +82,16 @@
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
+ <artifactId>org.jacoco.cli</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>org.jacoco.cli.test</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
<artifactId>org.jacoco.examples</artifactId>
<version>${project.version}</version>
</dependency>
@@ -152,6 +162,7 @@
<fileset dir="../org.jacoco.agent.test/target" includes="surefire-reports/**/*.xml"/>
<fileset dir="../org.jacoco.agent.rt.test/target" includes="surefire-reports/**/*.xml"/>
<fileset dir="../org.jacoco.ant.test/target" includes="surefire-reports/**/*.xml"/>
+ <fileset dir="../org.jacoco.cli.test/target" includes="surefire-reports/**/*.xml"/>
<fileset dir="../org.jacoco.core.test/target" includes="surefire-reports/**/*.xml"/>
<fileset dir="../org.jacoco.report.test/target" includes="surefire-reports/**/*.xml"/>
<fileset dir="../org.jacoco.examples.test/target" includes="surefire-reports/**/*.xml"/>
@@ -230,7 +241,7 @@
<artifactId>xml-maven-plugin</artifactId>
<executions>
<execution>
- <id>default-transform</id>
+ <id>transform-generated-doc</id>
<goals>
<goal>transform</goal>
</goals>
@@ -262,6 +273,32 @@
</parameter>
</parameters>
</transformationSet>
+ <transformationSet>
+ <dir>../org.jacoco.cli/target/generated-documentation</dir>
+ <includes>
+ <include>*.xml</include>
+ </includes>
+ <stylesheet>xsl/cli.xsl</stylesheet>
+ <fileMappers>
+ <fileMapper implementation="org.codehaus.plexus.components.io.filemappers.FileExtensionMapper">
+ <targetExtension>.html</targetExtension>
+ </fileMapper>
+ </fileMappers>
+ <parameters>
+ <parameter>
+ <name>qualified.bundle.version</name>
+ <value>${qualified.bundle.version}</value>
+ </parameter>
+ <parameter>
+ <name>jacoco.home.url</name>
+ <value>${jacoco.home.url}</value>
+ </parameter>
+ <parameter>
+ <name>copyright.years</name>
+ <value>${copyright.years}</value>
+ </parameter>
+ </parameters>
+ </transformationSet>
</transformationSets>
</configuration>
</execution>
diff --git a/org.jacoco.doc/xsl/cli.xsl b/org.jacoco.doc/xsl/cli.xsl
new file mode 100644
index 00000000..644d92d1
--- /dev/null
+++ b/org.jacoco.doc/xsl/cli.xsl
@@ -0,0 +1,113 @@
+<?xml version="1.0"?>
+
+<!--
+ Copyright (c) 2009, 2017 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:
+ Marc R. Hoffmann - initial API and implementation
+-->
+
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns="http://www.w3.org/1999/xhtml" exclude-result-prefixes="xdoc">
+
+ <xsl:output method="xml" indent="yes" encoding="UTF-8"
+ doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" />
+
+ <xsl:param name="qualified.bundle.version" />
+ <xsl:param name="jacoco.home.url" />
+ <xsl:param name="copyright.years" />
+
+ <xsl:template match="/">
+ <html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <link rel="stylesheet" href="resources/doc.css" charset="UTF-8"
+ type="text/css" />
+ <link rel="shortcut icon" href="resources/report.gif" type="image/gif" />
+ <title>
+ JaCoCo - Command Line Interface
+ </title>
+ </head>
+ <body>
+ <div class="breadcrumb">
+ <a href="../index.html" class="el_report">JaCoCo</a> &gt;
+ <a href="index.html" class="el_group">Documentation</a> &gt;
+ <span class="el_source">Command Line Interface</span>
+ </div>
+ <div id="content">
+
+ <h1>Command Line Interface</h1>
+
+ <p>
+ JaCoCo comes with a command line interface to perform
+ basic operations from the command line. The command line
+ tools with all dependencies are packaged in
+ <code>jacococli.jar</code> and are available with the
+ JaCoCo download. A Java VM with version 1.5 or greater is
+ required for execution.
+ </p>
+
+ <p>
+ For more sophisticated usage especially with larger
+ projects please use our
+ <a href="integrations.html">integrations</a> with various
+ build tools.
+ </p>
+
+ <p>
+ The following commands are available. Each command has a
+ list of optional and required parameters. Some parameters
+ can be specified multiple times to provide multiple values.
+ </p>
+ <xsl:apply-templates select="documentation" />
+ </div>
+ <div class="footer">
+ <span class="right">
+ <a href="{$jacoco.home.url}">JaCoCo</a>
+ &#160;
+ <xsl:value-of select="$qualified.bundle.version" />
+ </span>
+ <a href="../doc/license.html">Copyright</a>
+ &#169;
+ <xsl:value-of select="$copyright.years" />
+ Mountainminds GmbH &amp; Co. KG and Contributors
+ </div>
+ </body>
+ </html>
+ </xsl:template>
+
+ <xsl:template match="command">
+ <h2><xsl:value-of select="@name" /></h2>
+ <pre class="source" style="white-space: pre-wrap">
+ <xsl:value-of select="usage" />
+ </pre>
+ <p><xsl:value-of select="description" /></p>
+ <table class="coverage">
+ <thead>
+ <tr>
+ <td>Option</td>
+ <td>Description</td>
+ <td>Required</td>
+ <td>Multiple</td>
+ </tr>
+ </thead>
+ <tbody>
+ <xsl:for-each select="option">
+ <tr>
+ <td><code><xsl:value-of select="usage" /></code></td>
+ <td><xsl:value-of select="description" /></td>
+ <td style="text-align:center"><xsl:if test="@required = 'true'">&#x25fc;</xsl:if></td>
+ <td style="text-align:center"><xsl:if test="@multiple = 'true'">&#x25fc;</xsl:if></td>
+ </tr>
+ </xsl:for-each>
+ </tbody>
+ </table>
+ </xsl:template>
+
+</xsl:stylesheet>
+
diff --git a/org.jacoco.tests/pom.xml b/org.jacoco.tests/pom.xml
index d3632123..2e676f98 100644
--- a/org.jacoco.tests/pom.xml
+++ b/org.jacoco.tests/pom.xml
@@ -31,8 +31,9 @@
<module>../org.jacoco.agent.rt.test</module>
<module>../org.jacoco.agent.test</module>
<module>../org.jacoco.ant.test</module>
- <module>../jacoco-maven-plugin.test</module>
+ <module>../org.jacoco.cli.test</module>
<module>../org.jacoco.examples.test</module>
+ <module>../jacoco-maven-plugin.test</module>
</modules>
<properties>