Can I get maven to generate two test jars with different classifiers? - unit-testing

I have a maven project where I generate some test code for some modules. I'd like this generated test code to be available for testing by other modules. Typically if the module bar wants to use the test code of module foo, the foo module must generate a foo-tests.jar and the module bar adds a dependency such as:
<dependency>
<artifactId>foo</artifactId>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
Which is fine, except that I only want to pull in the generated test code of foo, not all of foo's unit tests and helper classes (there may be unintended class conflicts, for example). I'd like to define a dependency such as:
<dependency>
<artifactId>foo</artifactId>
<classifier>test-libs</classifier>
<scope>test</scope>
</dependency>
Typically maven-jar-plugin is used to generate the foo-test artifact, so I was hoping I could configure that plugin to generate two test artifacts: one that contains the usual test code (unit tests, etc) in foo-tests and one that contains the generated code in foo-test-libs, using two different classifiers, e.g.:
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>generate-test-jar</id>
<phase>package</package>
<goals><goal>test-jar</goal></goals>
<configuration>
<excludes>... all the generated code ...</excludes>
</configuration>
</execution>
<execution>
<id>generate-test-libs-jar</id>
<phase>package</package>
<goals><goal>test-jar</goal></goals>
<classifier>test-libs</classifier>
<configuration>
<includes>... all the generated code ...</includes>
</configuration>
</execution>
</executions>
</plugin>
The problem here is that, unlike the jar goal, the test-jar goal of maven-jar-plugin does not support the classifier element. I assume the goal uses the test classifier by default, so I cannot generated two test jars with different classifiers.
I am wondering if there's a good way of splitting the test jars for a maven module? If all else fails, I can go back to adding dependencies on the complete test jar, but I'm hoping for a more elegant solution.
(And I know that using classifier is frowned upon, but I'm not sure if it can be avoided here ...)

I think I figured it out, using a combination of maven-antrun-plugin to create multiple test jar files (using the jar task), and build-helper-maven-plugin to attach the generated jar file as artifacts to the project.
Generate test jar files.
I use maven-antrun-plugin combined with the ant jar task to split my test code:
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>run</goal></goals>
<configuration>
<target>
<!-- The test-libs jar contains all test code in the x.y.z package. -->
<jar destfile="${project.build.directory}/artifacts/test-libs/${project.build.finalName}.jar"
basedir="${project.build.directory}/test-classes"
includes="x/y/z/**/*.class"/>
<!-- The tests jar contains all test code exception the x.y.z package. -->
<jar destfile="${project.build.directory}/artifacts/tests/${project.build.finalName}.jar"
basedir="${project.build.directory}/test-classes"
excludes="x/y/z/**/*.class"/>
</target>
</configuration>
</execution>
</executions>
</plugin>
Note that I had to use the same name ${project.build.finalName}.jar for each generated jar file (this is important later) so I put each jar file into its own directory:
target/artifacts/test-libs/foo.jar
target/artifacts/tests/foo.jar
Attaching the jar files to the project.
Generating the jar files is only the first step. The jar files need to be attached to the maven project so that they will be installed. For this the build-helper-maven-plugin is required. This allows attaching of files as artifacts, where each file has a location, a type, and a classifier. Here the type is jar and the classifier will be tests or test-libs as appropriate:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.9</version>
<executions>
<execution>
<id>attach-test-jars</id>
<phase>package</phase>
<goals><goal>attach-artifact</goal></goals>
<configuration>
<artifacts>
<!-- Attach the test-libs artifact. -->
<artifact>
<file>${project.build.directory}/artifacts/test-libs/${project.build.finalName}.jar</file>
<type>jar</type>
<classifier>test-libs</classifier>
</artifact>
<!-- Attach the tests artifact. -->
<artifact>
<file>${project.build.directory}/artifacts/tests/${project.build.finalName}.jar</file>
<type>jar</type>
<classifier>tests</classifier>
</artifact>
</artifacts>
</configuration>
</execution>
</executions>
</plugin>
With this I now see the following output:
[INFO] Installing ...\target\artifacts\test-libs\foo-1.0.jar to ...\foo\1.0\foo-1.0-test-libs.jar
[INFO] Installing ...\target\artifacts\tests\foo-1.0.jar to ...\foo\1.0\foo-1.0-tests.jar
[INFO] Installing ...\target\foo-1.0.jar to ...\foo\1.0\foo-1.0.jar
My other projects can now add dependencies on either foo-test.jar or foo-test-lib.jar as required. Huzzah!

Related

Include native binaries in JAR (issues w/ maven-shade-plugin)

I am building a library that requires an SO file.
My goals:
automatically pick up the SO file from the artifact repository
include it as a resource in the installed JAR so that I do not need to add it manually and therefore not have to also put in the GitHub repository.
I am referencing it in my library POM like this:
<dependency>
<groupId>com.myco.mygrp</groupId>
<artifactId>native</artifactId>
<type>so</type>
<scope>runtime</scope>
<version>0.0.1</version>
</dependency>
This does add it to my local Maven repository but when I attempt to build an uber JAR from a second project that is a client to this project and therefore imports it and references it in its POM, I get
Failed to execute goal org.apache.maven.plugins:maven-shade-plugin:3.2.4:shade (default) on project i-am-the-client: Error creating shaded jar: error in opening zip file /Users/me/.m2/repository/com/myco/mygrp/0.0.1/native-0.0.1.so -> [Help 1]
While I want that SO file in my client uber JAR, I don't need it to be unzipped or opened. How do I get maven-shade-plugin to include it in the JAR but without treating the SO file like it's a JAR to be unzipped? Here's the maven-shade-plugin for reference:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.myco.somegrp.Client</mainClass>
</transformer>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>scala/tools/nsc/doc/html/resource/lib/jquery*</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
I can circumvent this by just adding the native SO into the resources directory manually but that will go against one of the goals and also raise eyebrows during code review because, per the goal, the binary is already in Nexus and is not necessarily wanted in GitHub as well.
As a secondary approach, I resort to my maven-dependency-plugin as so:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.2</version>
<executions>
<execution>
<id>copy</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>com.myco.mygrp</groupId>
<artifactId>native</artifactId>
<type>so</type>
<overWrite>false</overWrite>
<destFileName>my_native.so</destFileName>
</artifactItem>
</artifactItems>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
When I mvn install, I get the expected "my_native.so" file in target/lib but not in the installed JAR. I would like to have this file included in the installed JAR so that it can be loaded up by the JNI code. I intend to System.load it up even if it is in the same JAR by writing it out to a temp file in the file system right before and loading that temp file instead.
I have been working on this for some days including scouring the interwebs before I settled upon this strategy but I am open to suggestions if this is the wrong approach.

How to get deterministic builds using lein?

Running lein uberjar twice in a row, I get two different builds. After some unzip / find / sort / diff shell magic I saw it came down to some Maven file: more specifically the pom.properties file.
Here's a diff:
< #Tue Jan 14 07:07:50 CET 2014
---
> #Tue Jan 14 07:07:01 CET 2014
How can I get deterministic Clojure builds using Leiningen (and hence Maven)?
I have a local patch to lein-voom (a project I maintain with Chouser) which will address this, fixing the pom.properties header time to VCS (currently only git) commit time if the working copy is entirely clean. I expect this commit to finalize sometime next week, though I'm still thinking about the configurability of this feature.
This alone doesn't make for stable jars but it is the first trivial piece. Also of interest are timestamps of files within the jar which will change the zip header. Normalizing timestamps should also be straightforward but is a separate step.
Deterministic builds are of interest to lein-voom, a project which may generally be of interest to you since it allows pointing dependencies directly to a particular source version by commit sha, avoiding artifacts altogether.
lein-voom is quite young and the documentation and CLI are pretty rough but the core functionality is solid. Feel free to post issues or questions on the GitHub project.
I wrote up an article a while back covering deterministic builds with Maven. I have extracted the salient points here:
Use the assembly plugin and configure it like this:
src/main/assembly/zip.xml:
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<id>deterministic</id>
<baseDirectory>/</baseDirectory>
<formats>
<format>zip</format>
</formats>
<fileSets>
<fileSet>
<directory>${project.build.directory}/classes</directory>
<outputDirectory>/</outputDirectory>
</fileSet>
</fileSets>
</assembly>
Add in your own MANIFEST.MF remembering the extra CRLF at the end or it won't be valid.
src/main/resources/META-INF/MANIFEST.MF:
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: yourapp
Build-Jdk: 1.7.0
Add some plugins into your pom.xml:
pom.xml:
<plugins>
... other plugins ...
<!-- Step 1: Set all timestamps to same value -->
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>1-touch-classes</id>
<phase>prepare-package</phase>
<configuration>
<target>
<touch datetime="01/01/2000 00:10:00 am">
<fileset dir="target/classes"/>
</touch>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Step 2: Assemble as a ZIP to avoid MANIFEST.MF timestamp -->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2.1</version>
<configuration>
<descriptors>
<descriptor>src/main/assembly/zip.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>2-make-assembly</id>
<phase>prepare-package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Step 3: Rename ZIP as JAR -->
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>3-rename-assembly</id>
<phase>package</phase>
<configuration>
<target>
<move file="${project.build.directory}/${project.build.finalName}-deterministic.zip"
tofile="${project.build.directory}/${project.build.finalName}-deterministic.jar"/>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
... more plugins ...
</plugins>
This will create a deterministic JAR, but it will still depend on the exact version of the JVM and operating system you build it with. To overcome that you should explore the gitian approach used by the Bitcoin Core project and mandate a particular JVM within the VirtualBox environment. In this manner multiple developers can build from the source independently and then sign the binary to state that they are in agreement. When a certain threshold is reached the code is considered proven to be deterministic and can be released.

What is the difference between "Generating reports via build.xml" and "Generating via pom.xml"?

Earlier I was using build.xml (ant) to run my test cases but now I use pom.xml (maven) to run the test cases.
When I had ant I was able to get testng-xslt reports but after reading many blogs and tutorials I couldnt generate via pom. I wanted to investigate that what is the difference and I saw that I had saxon.jar in my class path but it was missing in pom.xml so I added a dependency. Second thing that I notice that I haven't specified the .xml path in pom.xml(I dont know where to add it in pom).
I am giving both pom.xml and build.xml here, please take a look on both and let me know what I have missed to generate testng-xslt reports via pom.xml but it was present in build.xml and how I can fix that.
build.xml
<target name="testng-xslt-report">
<delete dir="${basedir}/testng-xslt">
</delete>
<mkdir dir="${basedir}/testng-xslt">
</mkdir>
<xslt in="${basedir}/test-output/testng-results.xml" style="${basedir}/testng-results.xsl" out="${basedir}/testng-xslt/index.html">
<param expression="${basedir}/testng-xslt/" name="testNgXslt.outputDir" />
<param expression="true" name="testNgXslt.sortTestCaseLinks" />
<param expression="FAIL,SKIP,PASS,CONF,BY_CLASS" name="testNgXslt.testDetailsFilter" />
<param expression="true" name="testNgXslt.showRuntimeTotals" />
<classpath location="D:\automation\windowsproject\zookeeper\lib\saxon-8.7.jar">
</classpath>
</xslt>
</target>
pom.xml
<build>
<testResources>
<testResource>
<directory>src/test/resource</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</testResource>
</testResources>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>
C:/Users/windowspc/workspace/windows-project/Chrome.xml
</suiteXmlFile>
</suiteXmlFiles>
<testFailureIgnore>
true
</testFailureIgnore>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.testng.xslt</groupId>
<artifactId>testng-xslt-plugin</artifactId>
<version>1.1</version>
<configuration>
<outputDir>C:/Users/windowspc/workspace/windows-project/target/testng-xslt-report</outputDir>
<showRuntimeTotals>true</showRuntimeTotals>
<sortTestCaseLinks>true</sortTestCaseLinks>
<testDetailsFilter>FAIL,PASS,SKIP,CONF</testDetailsFilter>
</configuration>
</plugin>
</plugins>
</reporting>
<pluginRepositories>
<pluginRepository>
<id>testng-xslt-plugin</id>
<url>http://uhftopic.com/maven/</url>
</pluginRepository>
</pluginRepositories>
...
...
<dependency>
<groupId>net.sourceforge.jexcelapi</groupId>
<artifactId>jxl</artifactId>
<version>2.6.12</version>
</dependency>
<dependency>
<groupId>net.sf.saxon</groupId>
<artifactId>saxon</artifactId>
<version>8.7</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.5.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
There is a new version of this plugin. It's groupId/artifactId as changed. Here is what you can use:
<groupId>org.reportyng</groupId>
<artifactId>reporty-ng</artifactId>
<version>1.2</version>
Here is the project site: https://github.com/cosminaru/reporty-ng/wiki/MavenPlugin
Here is the github repository: https://github.com/cosminaru/reporty-ng and the plugin repository is now here:
<pluginRepository>
<id>reporty-ng</id>
<url>https://github.com/cosminaru/reporty-ng/raw/master/dist/maven</url>
</pluginRepository>
The major differences between ant and maven are:
maven use conventions over configuration
ant need to be fully configured
It means that with ant you can quite easily do anything you need, but you have to write ant script to achieve what you need. On the other hand with maven make a lot of assumptions about your project structure and if your project structure is conform to those assumptions: you can do a lot of usefull jobs like building, testing, packaging, generating doc with almost nothing in your pom.xml.
One important thing to understand with maven is the concept of life-cycle. There are 3 life-cycle defined by maven : the clean-life-cycle, the default-life-cycle and the site-life-cycle.
clean: use it to clean any previously build product
default: use it to build, test, package your project
site: use it to generate documentation and reports
Each life-cycle is a succession of phases. Additionaly, each phase is bound with a plugin-goal (a plugin-goal is at some point similar to an ant target). The concept of life-cycle introduce a dependency between plugin execution (and so you don't have to specify it). For instance maven knows that it must compile the sources and the tests sources before running the tests.
In your case: you need to generate a report, so it is something that can be done during the site-lyfe-cycle. You need to generate a report based on test results, so you need to run the test prior to generate reports. To do that simply run this maven command:
mvn clean test site:site
This command indicate to maven to:
cleaning any previous build product (clean life-cycle)
running the test (default life-cycle) (maven knows that it must compile before running the tests)
generating a site with the documentation and since you have define a reporting plugin : it will be executed during the site generation.
You will find your reports under target/site directory.
Another thing to note: the saxon dependency is a dependency of the plugin, not a project dependency so you must specify it under the <plugin> element:
<plugin>
<groupId>org.testng.xslt</groupId>
<artifactId>testng-xslt-plugin</artifactId>
<version>1.1</version>
<configuration>
<outputDir>C:/Users/windowspc/workspace/windows-project/target/testng-xslt-report</outputDir>
<showRuntimeTotals>true</showRuntimeTotals>
<sortTestCaseLinks>true</sortTestCaseLinks>
<testDetailsFilter>FAIL,PASS,SKIP,CONF</testDetailsFilter>
</configuration>
<dependencies>
<dependency>
<groupId>net.sf.saxon</groupId>
<artifactId>saxon</artifactId>
<version>8.7</version>
</dependency>
</dependencies>
</plugin>

I can't get automatic unit testing to work with Jenkins and Sonar (and finally with Maven)

I don't know if I understand something wrong. I' trying to get automatic unit testing to work in my project.
I made a Eclipse Plugin Project and converted it into a Maven Project. I made a JUnit test case class testing the only class in the project. I placed every code in the Maven way (i.e. src/main/java main code and src/test/java test code).
I placed the test class in src/test/java in a package called pluginmaventest.actions .
The test case automatically fails if started in Eclipse as a JUnit test.
I shared the project in a SVN repository and made a Jenkins job (Maven 2/3 project). I added the JUnit dependency to the pom.xml and all necessary tycho dependencies.
The project build is successful. Shouldn't it be failing because my JUnit test fails?
I tried
mvn clean install
and
mvn test
as goals. I even tried to use -Dtest=SomeClassTest. The build never fails.
This obviously means that the unit test is neither compiled nor performed, doesn't it?
I'm just trying to get a grip with Jenkins, Sonar and unit tests. My momentary goal is to get unit tests running with Jenkins, and then try to get code analysis and test coverage running with Sonar, which is integrated into Jenkins. Is there a comprehensive and understandable tutorial or how-to on the web?
ADDENDUM:
My Jenkins installation can produce failing builds, so that should not be the problem.
My test class is really named SomeClassTest. Oh, oers already edited that.
Local Maven calls of the test goal produce no results, either.
Here is my whole project pom.xml. (without surefire maven didn't produce any output, either)
<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>
<groupId>test.maven.plugin</groupId>
<artifactId>pluginmaventest</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>Bla</name>
<packaging>eclipse-plugin</packaging>
<properties>
<tycho-version>0.13.0</tycho-version>
</properties>
<repositories>
<repository>
<id>indigo</id>
<layout>p2</layout>
<url>http://download.eclipse.org/releases/indigo/</url>
</repository>
</repositories>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-maven-plugin</artifactId>
<version>${tycho-version}</version>
<type>maven-plugin</type>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.11</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-maven-plugin</artifactId>
<version>${tycho-version}</version>
<extensions>true</extensions>
</plugin>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>target-platform-configuration</artifactId>
<version>${tycho-version}</version>
<configuration>
<resolver>p2</resolver>
<environments>
<environment>
<os>linux</os>
<ws>gtk</ws>
<arch>x86</arch>
</environment>
<environment>
<os>linux</os>
<ws>gtk</ws>
<arch>x86_64</arch>
</environment>
<environment>
<os>win32</os>
<ws>win32</ws>
<arch>x86</arch>
</environment>
<environment>
<os>win32</os>
<ws>win32</ws>
<arch>x86_64</arch>
</environment>
<environment>
<os>macosx</os>
<ws>cocoa</ws>
<arch>x86_64</arch>
</environment>
</environments>
</configuration>
</plugin>
</plugins>
You should make sure that the build runs the tests locally when you build with maven. If that is the case, then it could be that Jenkins isn't getting the updates from SVN. Configure it to checkout rather than update for the build and see if it now runs the tests. If that doesn't cause the failure (or at least cause the tests to run) then have a look at the console output from the Jenkins build and see what clues are in there for your build.
Jenkins should mark the build as UNSTABLE (yellow ball) if a test fails. Most people consider this superior to a FAILED status (red ball) because it provides more information. You ought to be able to see a graph of test passes/failures for all jobs on the job detail page.
If you would like to see what a failure looks like (say you want a sanity check), you could try committing uncompilable code, or calling invalid Maven goals.
check your project setup against the working demo example with tests:
http://git.eclipse.org/c/tycho/org.eclipse.tycho.git/tree/tycho-demo/itp01

Running tests after packaging

One of my projects needs a pretty complex setup for the resulting JAR file, so I'd like to run a test after the package phase to make sure the JAR contains what it should.
How do I do that with Maven 2?
You can use the surefire-plugin for this. what you need to do is associate a phase with an execution (see below). You will need to change the phase to be whatever you want it to be in your case one after the package phase.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>unittests</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<skip>false</skip>
<includes>
<include>**/**/**/*Test.java</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
Convert your project into a multi-module build. In the first module, build your original project. In the second module, add a dependency to the first.
This will add the first JAR to the classpath.
Update by OP: This works but I had to add this to my POM:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${version.maven-surefire-plugin}</version>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
</configuration>
</plugin>
The important part is <useSystemClassLoader>false</useSystemClassLoader>. Without this, my classpath only contained a couple of VM JARs plus the surefire bootstrap JAR (which contains the test classpath in the MANIFEST.MF). I have no idea why this test classpath isn't visible from the classes loaded from it.