Search code examples
mavenintegration-testingcode-coveragemaven-war-pluginclover

How to revert Maven project.build variables after Clover plugin finishes?


I'm attempting to add integration tests to a large Maven project. Here is the desired order of events:

  1. Clean existing artifacts.
  2. Resolve dependencies and build project.
  3. Run unit tests via Surefire plugin.
  4. Fork lifecycle for Clover plugin.
  5. --- Instrument sources using Clover plugin.
  6. --- Modify project.build.directory and project.build.finalName for Clover.
  7. --- Build Clover instrumented project in a new directory.
  8. --- Run Clover instrumented unit tests via Surefire plugin.
  9. --- Check code coverage from Clover instrumented unit tests.
  10. --- Reset project.build.directory and project.build.finalName to original values.
  11. --- End forked lifecycle (Clover only forks through 'test' phase).
  12. Package project as a WAR file.
  13. Locally host project WAR file via Tomcat7 plugin.
  14. Run integration tests against Tomcat7 instance via Surefire plugin.

All of this works as expected except step #9 (hence the question). Instead, the build directory is still set to the Clover version, and the project name has "-clover" appended to it. I've also found that when "MyProject-clover.war" is hosted by Tomcat7 that it does not function as expected (returns a 404 error in the browser).

Even if it did work, We do not need/want Clover instrumentation in the WAR file which we're testing against because the integration tests don't touch any of the production code (it's all Selenium UI stuff under src/test/java which interacts with the locally hosted pages instead of the production code itself).

As mentioned, this is a large project containing hundreds of dependencies and dozens of plugins. I believe the following are relevant to my issue, though I can dig up more if necessary (posting the entire pom file seems unreasonable).

Here's the pom configuration for the Clover plugin:

<plugin>
    <groupId>com.atlassian.maven.plugins</groupId>
    <artifactId>maven-clover2-plugin</artifactId>
    <version>3.1.11</version>
    <configuration>
        <generateHtml>true</generateHtml>
        <generateXml>true</generateXml>
        <licenseLocation>${basedir}/src/test/resources/clover.license</licenseLocation>
        <targetPercentage>92%</targetPercentage>
      <excludes>
        <exclude>**/mock/*.java</exclude>
        <exclude>**/com/mycompany/somestuff/*.java</exclude>
      </excludes>
    </configuration>
    <executions>
        <execution>
            <id>generate-clover-report</id>
            <phase>test</phase>
            <goals>
                <goal>instrument</goal>
                <goal>clover</goal>
                <goal>check</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Here's the pom configuration for the WAR plugin:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.1.1</version>
    <configuration>
        <finalName>${project.artifactId}</finalName>
        <appendAssemblyId>false</appendAssemblyId>
        <archive>
            <manifest>
                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
            </manifest>
            <manifestEntries>
                <!--Implementation-Build>${buildNumber}_${timestamp}</Implementation-Build -->
                <Build-Time>${timestamp}</Build-Time>
            </manifestEntries>
        </archive>
    </configuration>
</plugin>

Here's the pom configuration for the Tomcat7 plugin:

<plugin>
    <groupId>org.apache.tomcat.maven</groupId>
    <artifactId>tomcat7-maven-plugin</artifactId>
    <version>2.2</version>
    <configuration>
        <ajpPort>8009</ajpPort>
        <backgroundProcessorDelay>2</backgroundProcessorDelay>
        <configurationDir>${project.build.directory}/tomcat7_plugin</configurationDir>
        <contextFile>${CATALINA_HOME}/conf/context.xml</contextFile>
        <contextReloadable>false</contextReloadable>
        <fork>true</fork>
        <hostName>localhost</hostName>
        <httpsPort>8443</httpsPort>
        <ignorePackaging>false</ignorePackaging>
        <jarScanAllDirectories>false</jarScanAllDirectories>
        <path>/contentmain</path>
        <port>8080</port>
        <serverXml>${CATALINA_HOME}/conf/server.xml</serverXml>
        <tomcatUsers>${CATALINA_HOME}/conf/tomcat-users.xml</tomcatUsers>
        <tomcatWebXml>${CATALINA_HOME}/conf/web.xml</tomcatWebXml>
        <useNaming>true</useNaming>
        <useTestClasspath>true</useTestClasspath>
        <update>true</update>
        <warDirectory>${project.build.directory}/${project.build.finalName}</warDirectory>
    </configuration>
    <executions>
        <execution>
            <id>start-tomcat</id>
            <phase>pre-integration-test</phase>
            <goals>
                <goal>run-war-only</goal>
            </goals>
            <configuration>
                <fork>true</fork>
            </configuration>
        </execution>
        <execution>
            <id>stop-tomcat</id>
            <phase>post-integration-test</phase>
            <goals>
                <goal>shutdown</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Here's the current (desired) behavior when I run "mvn clean install -Dmaven.clover.skip":

[INFO] --- maven-clover2-plugin:3.1.11:check (generate-clover-report) @ MyProject ---
[INFO] 
[INFO] --- maven-dependency-plugin:2.1:unpack (unpack) @ MyProject ---
[INFO] Configured Artifact: com.mycompany:somedependency:?:jar
[INFO] Configured Artifact: com.mycompany:somedependency:?:jar
[INFO] Configured Artifact: com.mycompany:somedependency:?:jar
[INFO] Unpacking /mydir/.m2/myrepo/mycompany/somedir/somedependency.jar to mydir/MyProject/target/MyProject with includes css/*.css and excludes:null
[INFO] Unpacking /mydir/.m2/myrepo/mycompany/somedir/somedependency.jar to mydir/MyProject/target/MyProject with includes scripts/*/*.* and excludes:null
[INFO] Unpacking /mydir/.m2/myrepo/mycompany/somedir/somedependency.jar to mydir/MyProject/target/MyProject with includes images/*.* and excludes:null
[INFO] 
[INFO] --- maven-war-plugin:2.1.1:war (default-war) @ MyProject ---
[INFO] Packaging webapp
[INFO] Assembling webapp [MyProject] in [mydir/MyProject/target/MyProject]
[INFO] Processing war project
[INFO] Copying webapp resources [mydir/MyProject/src/main/webapp]
[INFO] Webapp assembled in [2019 msecs]
[INFO] Building war: /mydir/MyProject/target/MyProject.war
[INFO] WEB-INF/web.xml already added, skipping
[INFO] 
[INFO] >>> maven-source-plugin:2.2.1:jar (default) @ MyProject >>>
[INFO] 
[INFO] --- maven-dependency-plugin:2.1:copy (default) @ MyProject ---
[INFO] 
[INFO] --- buildnumber-maven-plugin:1.1:create-timestamp (default) @ MyProject ---
[INFO] 
[INFO] <<< maven-source-plugin:2.2.1:jar (default) @ MyProject <<<
[INFO] 
[INFO] --- maven-source-plugin:2.2.1:jar (default) @ MyProject ---
[INFO] Building jar: /mydir/MyProject/target/MyProject-sources.jar
[INFO] 
[INFO] --- tomcat7-maven-plugin:2.2:run-war-only (start-tomcat) @ MyProject ---
[INFO] Running war on http://localhost:8080/contentmain

Here's the current (undesired) behavior when I run "mvn clean install" (note the paths listed by maven-war-plugin and warning message from maven-source-plugin:

[INFO] --- maven-clover2-plugin:3.1.11:check (generate-clover-report) @ MyProject ---
[INFO] 
[INFO] --- maven-dependency-plugin:2.1:unpack (unpack) @ MyProject ---
[INFO] Configured Artifact: com.mycompany:somedependency:?:jar
[INFO] Configured Artifact: com.mycompany:somedependency:?:jar
[INFO] Configured Artifact: com.mycompany:somedependency:?:jar
[INFO] Unpacking /mydir/.m2/myrepo/mycompany/somedir/somedependency.jar to mydir/MyProject/target/MyProject with includes css/*.css and excludes:null
[INFO] Unpacking /mydir/.m2/myrepo/mycompany/somedir/somedependency.jar to mydir/MyProject/target/MyProject with includes scripts/*/*.* and excludes:null
[INFO] Unpacking /mydir/.m2/myrepo/mycompany/somedir/somedependency.jar to mydir/MyProject/target/MyProject with includes images/*.* and excludes:null
[INFO] 
[INFO] --- maven-war-plugin:2.1.1:war (default-war) @ MyProject ---
[INFO] Packaging webapp
[INFO] Assembling webapp [MyProject] in [mydir/MyProject/target/clover/MyProject-clover]
[INFO] Processing war project
[INFO] Copying webapp resources [mydir/MyProject/src/main/webapp]
[INFO] Webapp assembled in [1770 msecs]
[INFO] Building war: /mydir/MyProject/target/clover/MyProject-clover.war
[INFO] WEB-INF/web.xml already added, skipping
[INFO] 
[INFO] >>> maven-source-plugin:2.2.1:jar (default) @ MyProject >>>
[INFO] 
[INFO] --- maven-dependency-plugin:2.1:copy (default) @ MyProject ---
[INFO] 
[INFO] --- buildnumber-maven-plugin:1.1:create-timestamp (default) @ MyProject ---
[INFO] 
[INFO] <<< maven-source-plugin:2.2.1:jar (default) @ MyProject <<<
[INFO] 
[INFO] --- maven-source-plugin:2.2.1:jar (default) @ MyProject ---
[WARNING] NOT adding sources to artifacts with classifier as Maven only supports one classifier per artifact. Current artifact [com.mycompany:MyProject:war:clover:ParentProject-SNAPSHOT] has a [clover] classifier.
[INFO] 
[INFO] --- tomcat7-maven-plugin:2.2:run-war-only (start-tomcat) @ MyProject ---
[INFO] Running war on http://localhost:8080/contentmain

What can I do to make sure that the {project.build.directory} and {project.build.finalName} values are reset to their original values after Clover has finished executing the maven-clover2-plugin:check goal during the test phase of the forked lifecycle?

I've already tried browsing the online manuals for Clover, the WAR plugin, and Tomcat7. I see no mention of any settings I can use to revert the build variables that are altered by Clover. I can always hard-code the paths in my pom file, but I'd prefer a less brittle solution.


Solution

  • It turns out that Clover will always alter these variables unless the clover2:setup target is used. However, if you still wish to fork the lifecycle for Clover (IE: clover2:instrument or clover2:instrument-test) then these variables will always be altered.

    We wish to continue forking the lifecycle using clover:instrument-test, so I've come up with a workaround. Instead of using the project.build.finalName and project.build.directory variables, I'm using my own variables that get copied and saved on Maven execution before Clover can mess with them:

    <properties>
        <!-- Saving these variables now before Clover alters them -->
        <original.build.finalName>${project.build.finalName}</original.build.finalName>
        <original.build.directory>${project.build.directory}</original.build.directory>
    </properties>
    

    I then tell all of the subsequent plugins to use these variables instead of the project variables which Clover has altered:

    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>2.4</version>
            <configuration>
                <cacheFile>${original.build.directory}/war/work/webapp-cache.xml</cacheFile>
                <outputDirectory>${original.build.directory}</outputDirectory>
                <warName>${original.build.finalName}</warName>
                <webappDirectory>${original.build.directory}/${original.build.finalName}</webappDirectory>
                <workDirectory>${original.build.directory}</workDirectory>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.2</version>
            <configuration>
                <configurationDir>${original.build.directory}/tomcat7_plugin</configurationDir>
                <warDirectory>${original.build.directory}/${original.build.finalName}</warDirectory>
            </configuration>
        </plugin>
    </plugins>
    

    Alternative solutions might include creating or utilizing an additional plugin in a subsequent Maven phase to revert the project.build variables after Clover has finished executing. Either of these solutions is probably better than hard-coding the paths in all of your plugins.