Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Maven Mojo to perform offline instrumentation #64

Merged
merged 11 commits into from Jan 15, 2013
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2009, 2013 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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>jacoco</groupId>
<artifactId>it-offline-instrumentation</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>

<artifactId>child-without-main-classes</artifactId>

</project>
@@ -0,0 +1,20 @@
/*******************************************************************************
* Copyright (c) 2009, 2013 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
*
*******************************************************************************/
import org.junit.Test;

public class ExampleTest {

@Test
public void test() {
}

}
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2009, 2013 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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>jacoco</groupId>
<artifactId>it-offline-instrumentation</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>

<artifactId>child</artifactId>

</project>
@@ -0,0 +1,18 @@
/*******************************************************************************
* Copyright (c) 2009, 2013 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
*
*******************************************************************************/
public class DoNotInstrument {

public void sayHello() {
System.out.println("Hello world");
}

}
@@ -0,0 +1,18 @@
/*******************************************************************************
* Copyright (c) 2009, 2013 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
*
*******************************************************************************/
public class Example {

public void sayHello() {
System.out.println("Hello world");
}

}
@@ -0,0 +1,21 @@
/*******************************************************************************
* Copyright (c) 2009, 2013 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
*
*******************************************************************************/
import org.junit.Test;

public class ExampleTest {

@Test
public void test() {
new Example().sayHello();
}

}
86 changes: 86 additions & 0 deletions jacoco-maven-plugin.test/it/it-offline-instrumentation/pom.xml
@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2009, 2013 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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>jacoco</groupId>
<artifactId>setup-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>

<artifactId>it-offline-instrumentation</artifactId>
<packaging>pom</packaging>

<modules>
<module>child</module>
<module>child-without-main-classes</module>
</modules>

<dependencies>
<dependency>
<groupId>@project.groupId@</groupId>
<artifactId>org.jacoco.agent</artifactId>
<classifier>runtime</classifier>
<version>@project.version@</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>@project.groupId@</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<id>instrument-classes</id>
<goals>
<goal>instrument</goal>
</goals>
<configuration>
<excludes>
<exclude>**/DoNotInstrument.class</exclude>
</excludes>
</configuration>
</execution>
<execution>
<id>restore-instrumented-classes</id>
<goals>
<goal>restore-instrumented-classes</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<dataFile>${project.build.directory}/coverage.exec</dataFile>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<jacoco-agent.destfile>${project.build.directory}/coverage.exec</jacoco-agent.destfile>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
</project>
28 changes: 28 additions & 0 deletions jacoco-maven-plugin.test/it/it-offline-instrumentation/verify.bsh
@@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright (c) 2009, 2013 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
*
*******************************************************************************/
import java.io.*;
import org.codehaus.plexus.util.*;

File file = new File( basedir, "child/target/generated-classes/jacoco/Example.class" );
if ( !file.isFile() ) {
throw new RuntimeException( "Could not find backup of instrumented class: " + file );
}
file = new File( basedir, "child/target/generated-classes/jacoco/DoNotInstrument.class" );
if ( file.isFile() ) {
throw new RuntimeException( "Excluded file should not be instrumented: " + file );
}

file = new File( basedir, "child/target/coverage.exec" );
if ( !file.isFile() )
{
throw new FileNotFoundException( "Could not find generated dump: " + file );
}
23 changes: 20 additions & 3 deletions jacoco-maven-plugin/src/org/jacoco/maven/FileFilter.java
Expand Up @@ -8,15 +8,18 @@
* Contributors: * Contributors:
* Evgeny Mandrikov - initial API and implementation * Evgeny Mandrikov - initial API and implementation
* Kyle Lieber - implementation of CheckMojo * Kyle Lieber - implementation of CheckMojo
* *
*******************************************************************************/ *******************************************************************************/
package org.jacoco.maven; package org.jacoco.maven;


import java.util.List;

import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.StringUtils; import org.codehaus.plexus.util.StringUtils;


import java.io.File;
import java.io.IOException;
import java.util.List;

/** /**
* A file filter using includes/excludes patterns. * A file filter using includes/excludes patterns.
*/ */
Expand All @@ -41,6 +44,20 @@ public FileFilter(final List<String> includes, final List<String> excludes) {
this.excludes = excludes; this.excludes = excludes;
} }


/**
* Returns a list of files.
*
* @param directory
* the directory to scan
* @return a list of files
* @throws IOException
*/
@SuppressWarnings("unchecked")
public List<String> getFileNames(final File directory) throws IOException {
return FileUtils.getFileNames(directory, getIncludes(), getExcludes(),
false);
}

/** /**
* Get the includes pattern * Get the includes pattern
* *
Expand Down
93 changes: 93 additions & 0 deletions jacoco-maven-plugin/src/org/jacoco/maven/InstrumentMojo.java
@@ -0,0 +1,93 @@
/*******************************************************************************
* Copyright (c) 2009, 2013 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
*
*******************************************************************************/
package org.jacoco.maven;

import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.IOUtil;
import org.jacoco.core.instr.Instrumenter;
import org.jacoco.core.runtime.OfflineInstrumentationAccessGenerator;

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.List;

/**
* Performs offline instrumentation. Note that after execution of test you must
* restore original classes with help of "restore-instrumented-classes" goal.
* <p>
* <strong>Warning:</strong> The preferred way for code coverage analysis with
* JaCoCo is on-the-fly instrumentation. Offline instrumentation has several
* drawbacks and should only be used if a specific scenario explicitly requires
* this mode. Please consult <a href="offline.html">documentation</a> about
* offline instrumentation before using this mode.
* </p>
*
* @phase process-classes
* @goal instrument
* @requiresProject true
* @since 0.6.2
*/
public class InstrumentMojo extends AbstractJacocoMojo {

@Override
public void executeMojo() throws MojoExecutionException,
MojoFailureException {
final File originalClassesDir = new File(getProject().getBuild()
.getDirectory(), "generated-classes/jacoco");
originalClassesDir.mkdirs();
final File classesDir = new File(getProject().getBuild()
.getOutputDirectory());
if (!classesDir.isDirectory()) {
getLog().info("skip non existing outputDirectory " + classesDir);
return;
}

final List<String> fileNames;
try {
fileNames = new FileFilter(this.getIncludes(),
this.getExcludes()).getFileNames(classesDir);
} catch (final IOException e1) {
throw new MojoExecutionException(
"Unable to get list of files to instrument.", e1);
}

final Instrumenter instrumenter = new Instrumenter(
new OfflineInstrumentationAccessGenerator());
for (final String fileName : fileNames) {
if (fileName.endsWith(".class")) {
final File source = new File(classesDir, fileName);
final File backup = new File(originalClassesDir, fileName);
InputStream input = null;
OutputStream output = null;
try {
FileUtils.copyFile(source, backup);
input = new FileInputStream(backup);
output = new FileOutputStream(source);
instrumenter.instrument(input, output);
} catch (final IOException e2) {
throw new MojoExecutionException(
"Unable to instrument file.", e2);
} finally {
IOUtil.close(input);
IOUtil.close(output);
}
}
}
}

}