All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.atlassian.maven.plugin.clover.internal.AbstractCloverInstrumentMojo Maven / Gradle / Ivy

package com.atlassian.maven.plugin.clover.internal;

import clover.org.apache.commons.lang3.StringUtils;
import com.atlassian.clover.util.IOStreamUtils;
import com.atlassian.maven.plugin.clover.DistributedCoverage;
import com.atlassian.maven.plugin.clover.MethodWithMetricsContext;
import com.atlassian.maven.plugin.clover.TestSources;
import com.atlassian.maven.plugin.clover.internal.lifecycle.BuildLifecycleAnalyzer;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.lifecycle.LifecycleExecutor;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;

/**
 * Common settings for clover:instr / clover:setup MOJOs.
 */
public abstract class AbstractCloverInstrumentMojo extends AbstractCloverMojo implements CompilerConfiguration {

    /**
     * 

The difference (in milliseconds) that a -clover classified artifact can have to a non-clover classified artifact.

*

If the -clover classified artifact is more than cloveredArtifactExpiryInMillis older than the non-clover classified * artifact, then the non-classified artifact will be used.

*

This setting defaults to 2000.

*/ @Parameter(property = "maven.clover.cloveredArtifactExpiryInMillis", defaultValue = "2000") protected long cloveredArtifactExpiryInMillis; /** * If set, then the clover-maven-plugin will not copy files that were excluded, across to the target/clover directory. * This is useful if the build is also using plugins such as the maven-gwt-plugin, that scans for resources, and * skips a step if none are found. Otherwise, setting this to false could well cause build failures. */ @Parameter(property = "maven.clover.copyExcludedFiles", defaultValue = "true") protected boolean copyExcludedFiles = true; /** *

The configuration for distributed coverage collection by Clover.

*

If present, default values will be used and coverage will be collected across JVMs.

*

Optional nested elements (and their defaults) of distributedCoverage are:

*
    *
  • host - the host name of the JVM running the tests. default: localhost
  • *
  • port - the port that Clover can bind to in the host JVM. default: 1198
  • *
  • numClients - the number of clients expected to attach to the Test JVM. The test JVM will wait until numClients * have connected before continuing. default: 0
  • *
  • timeout - the amount of time to wait for a response from a remote JVM before shunning it. default: 5000
  • *
  • retryPeriod - the amount of time a client should wait between reconnect attempts. default: 1000
  • *
*/ @Parameter protected DistributedCoverage distributedCoverage; /** * The character encoding to use when parsing source files. */ @Parameter(property = "maven.clover.encoding") protected String encoding; /** * The list of file to exclude from the instrumentation. Patterns are resolved against source roots. */ @Parameter protected Set excludes = new HashSet(); /** * The comma seperated list of file to exclude from the instrumentation. Patterns are resolved against source roots. */ @Parameter(property = "maven.clover.excludesList") protected String excludesList = null; /** * The file containing a list of file paths, separated by new line, to exclude from the instrumentation. Patterns are resolved against source roots. * See also {@link #excludes} and {@link #excludesList} */ @Parameter(property = "maven.clover.excludesFile") protected String excludesFile = null; /** * The Clover flush policy to use. * Valid values are directed, interval and threaded. */ @Parameter(property = "maven.clover.flushPolicy", defaultValue = "threaded") protected String flushPolicy; /** *

By default, Clover Maven Plugin generates the ${java.io.tmpdir}/grover*.jar file during setup, * which is next being added as the dependent artifact to the build. As the file has generated, unique * name and the jar is not being removed at the end of the build, these files can litter the temporary * directory.

*

By setting this parameter you can:

*

a) specify constant file name for generated artifact,

*

b) choose location different than ${java.io.tmpdir}.

*

However, you must ensure that:

*

a) grover.jar will not be deleted till end of the build (for example don't put into ./target directory * and next run mvn clover:setup clean)

*

b) grover.jar will not be shared among builds with different Clover Maven Plugin versions used (for * example if ProjectA uses Clover v 3.1.8 and ProjectB uses Clover v 3.1.9 then they shall have different * groverJar locations defined)

* * @since 3.1.8 */ @Parameter(property = "maven.clover.groverJar") protected File groverJar; /** * The list of file to include in the instrumentation. Patterns are resolved against source roots. * Defaults are '**/*.java, **/*.groovy' which are overwritten if <includes> is set by the user */ @Parameter protected Set includes = new HashSet(Arrays.asList(new String[]{"**/*.java", "**/*.groovy"})); /** * The comma seperated list of files to include in the instrumentation. Patterns are resolved against source roots. * Defaults are **.java which are overwritten if <includes> is set by the user */ @Parameter(property = "maven.clover.includesList") protected String includesList = null; /** * The file containing a list of file paths, separated by new line, to include in the instrumentation. Patterns are resolved against source roots. * See also {@link #includes} and {@link #includesList} */ @Parameter(property = "maven.clover.includesFile") protected String includesFile = null; /** *

Till 3.1.11: whether the Clover plugin should instrument all source roots (for example * src/main/java, src/main/groovy, target/generated-sources, so including the generated sources) * or whether it should only instrument the main source root (usually src/main/java).

*

Since 3.1.12: whether the Clover plugin should instrument all source roots (for example * src/main/java, src/main/groovy, target/generated-sources, so including the generated sources) * or whether it should instrument non-generated source roots (i.e. all roots except target/generated-sources/*)

*/ @Parameter(property = "maven.clover.includesAllSourceRoots", defaultValue = "false") protected boolean includesAllSourceRoots; /** * Whether the Clover plugin should instrument test source roots. */ @Parameter(property = "maven.clover.includesTestSourceRoots", defaultValue = "true") protected boolean includesTestSourceRoots; /** *

The level to instrument to. Valid values are 'method' or 'statement'. Default is 'statement'.

*

Setting this to 'method' greatly reduces the overhead of enabling Clover, however limited or no reporting is * available. The current use of setting this to method is for Test Optimization only.

*/ @Parameter(property = "maven.clover.instrumentation", defaultValue = "statement") protected String instrumentation; /** *

Define whether lambda functions shall be instrumented: Valid values are:

*
    *
  • none - do not instrument lambda functions (note: statements inside lambdas will become a part of a parent function)
  • *
  • expression - instrument only expression-like lambdas, e.g. (a,b) -> a + b
  • *
  • block - instrument block lambdas, e.g. () -> { foo(); }
  • *
  • all_but_reference - instrument lambdas written in any form except method references, e.g. Math::abs
  • *
  • all - instrument all forms of lambda functions
  • *
*

Default is 'all' for 3.2.2-4.0.2 and 'none' since 4.0.3.

*

IMPORTANT: Due to Clover's restrictions related with code instrumentation and javac compiler's type inference * capabilities, you may get compilation errors when expression-like lambda functions are passed to generic methods * or types. In such case disable instrumentation of expression-like form (i.e. use the 'none' or 'block' setting). * See the * Java 8 code instrumented by Clover fails to compile Knowledge Base article for more details. *

* * @since 3.2.2 */ @Parameter(property = "maven.clover.instrumentLambda", defaultValue = "none") private String instrumentLambda; /** *

Which Java language level Clover shall use to parse sources. Valid values are:

*
    *
  • 1.7 (String in switch, try with resources, binary literals, underscores in literals)
  • *
  • 1.8 (lambda expressions, default methods in interfaces)
  • *
  • 9 / 1.9 (module-info.java)
  • *
*

By default Clover instruments using the highest language level supported.

*/ @Parameter(property = "maven.clover.jdk") protected String jdk; /** *

Specifies the custom method contexts to use for filtering specific methods from Clover reports.

* e.g.
<main>public static void main\(String args\[\]\).*</main>
*

will define the context called 'main' which will match all public static void main methods.

*/ @Parameter protected Map methodContexts = new HashMap(); /** *

Specifies the custom method contexts to use for filtering specific methods from Clover reports. * This is more detailed format compared to methodContexts, which allows to set also code metrics to be * matched. Example:

*
     * <methodWithMetricsContexts>
     *     <methodWithMetricsContext>
     *         <name>simpleGetter</name> <!-- (mandatory) -->
     *         <regexp>public .* get.*\(\)</regexp> <!-- (mandatory) -->
     *         <maxComplexity>1</maxComplexity> <!-- at most 1 cycle (optional) -->
     *         <maxStatements>1</maxStatements> <!-- at most 1 statement (optional) -->
     *         <maxAggregatedComplexity>2</maxAggregatedComplexity> <!-- no more than 2 cycles including inline classes (optional) -->
     *         <maxAggregatedStatements>10</maxAggregatedStatements> <!-- no more than 10 statements including inline classes (optional) -->
     *     </methodWithMetricsContext>
     *     <!-- can add more methodWithMetricsContext -->
     * </methodWithMetricsContexts>
     * 
*

will define a context called 'simpleGetter' which matches all public getXyz() methods containing at most one * statement; this statement may contain more complex logic (an anonymous inline class) but not bigger than 9 * statements.

*/ @Parameter protected Set methodWithMetricsContexts = new HashSet(); /** * If set to 'false', test results will not be recorded; instead, results can be added via the * <testResults> fileset at report time. Useful when a test uses a custom * Rule expecting an exception, which OpenClover cannot recognize. */ @Parameter(property = "maven.clover.recordTestResults", defaultValue = "true") protected boolean recordTestResults = true; /** *

Try to protect your build from installing instrumented artifacts into local ~/.m2 cache * or deploying them to a binaries repository. If this option is enabled, Clover will fail a build whenever * it detects that 'install' or 'deploy' phase is about to be called. It will also fail a build if * it detects that an artifact having multiple classifiers (e.g. "-clover-tests.jar"), which are not supported by * Maven, is about to be installed under original name (e.g. "-tests.jar").

*

Please note that this flag may not protect from all possible cases.

* * @since 4.0.4 */ @Parameter(property = "maven.clover.repositoryPollutionProtection", defaultValue = "false") protected boolean repositoryPollutionProtection; /** * When creating the clover.jar dependency, what scope to use. * This may be one of: compile, test, provided etc. If not specified - provided will be used. */ @Parameter(property = "maven.clover.scope") protected String scope; /** *

If set to true, Clover will add several properties to the build configuration which * disable a build failure for following plugins:

*
    *
  • maven-surefire-plugin (maven.test.failure.ignore=true)
  • *
  • maven-failsafe-plugin (maven.test.failure.ignore=true)
  • *
  • maven-checkstyle-plugin (checkstyle.failOnViolation=false)
  • *
  • maven-pmd-plugin (pmd.failOnViolation=false)
  • *
*

Thanks to this, build continues despite test failures or code validation failures and thus * it is possible to generate a Clover coverage report for failed tests at the end of the build.

*

Note: before version 3.1.9 the testFailureIgnore property was set to true for * the forked Clover lifecycle ('instrument' goal) for 'test' and 'integration-test' phases. Since * 3.1.9 it is no longer set.

* * @since 3.1.9 */ @Parameter(property = "maven.clover.setTestFailureIgnore", defaultValue = "false") protected boolean setTestFailureIgnore; /** *

By default, Clover Maven Plugin generates the ${java.io.tmpdir}/grover*.jar file during setup, * which is next being added as the dependent artifact to the build. As the file has generated, unique * name and the jar is not being removed at the end of the build, these files can litter the temporary * directory.

*

In case when there is no Groovy code in the project, this parameter can be set to true in order * to disable generation of grover.jar artifact.

* * @since 3.1.8 */ @Parameter(property = "maven.clover.skipGroverJar", defaultValue = "false") protected boolean skipGroverJar = false; /** * Specifies the custom statement contexts to use for filtering specific statements from Clover reports. * e.g.
<log>^LOG\..*</log>
* defines a statement context called "log" which matches all LOG statements. */ @Parameter protected Map statementContexts = new HashMap(); /** * Sets the granularity in milliseconds of the last modification date for testing whether a source needs reinstrumentation. */ @Parameter(property = "maven.clover.staleMillis", defaultValue = "0") protected int staleMillis; /** * Specifies a custom test detector configuration. Useful in case your tests are not following JUnit/TestNG * naming convention. Example: * *
     * <testSources>
     *    <includes>
     *        <include>**/*</include>
     *        <include>*WebTest.java</include>
     *        <include>**/*IT.java</include>
     *    </includes>
     *    <excludes>
     *        <exclude>deprecated/**</exclude>
     *    </excludes>
     *    <testClasses>
     *        <testClass> <!-- 0..N occurrences -->
     *            <name>.*Test</name>
     *            <super>WebTest</super>
     *            <annotation>@Repeat</annotation>
     *            <package>org\.openclover\..*</package>
     *            <tag>@chrome</tag>
     *            <testMethods> <!-- 0..N occurrences -->
     *               <testMethod>
     *                   <name>check.*</name>
     *                   <annotation>@Test</annotation>
     *                   <tag>@web</tag>
     *                   <returnType>void</returnType>
     *               </testMethod>
     *            </testMethods>
     *        </testClass>
     *    </testClasses>
     * </testSources>
     * 
* * Note: every tag is optional. * * @since 4.4.0 */ @Parameter protected TestSources testSources; /** * Whether or not to include the -clover classifier on artifacts. */ @Parameter(property = "maven.clover.useCloverClassifier", defaultValue = "true") protected boolean useCloverClassifier; /** * Use the fully qualified package name for java.lang.* classes. */ @Parameter(property = "maven.clover.useFullyQualifiedJavaLang", defaultValue = "true") protected boolean useFullyQualifiedJavaLang; /////////////////////////////////////////////////////////////////////////// /** * Used to learn about lifecycles and phases */ @Component private LifecycleExecutor lifecycleExecutor; /** * Used to learn about current build session. */ @Parameter(defaultValue = "${session}", readonly = true) private MavenSession mavenSession; /** */ @Parameter(defaultValue = "${project}", readonly = true) private MavenProject mavenProject; /////////////////////////////////////////////////////////////////////////// @Override public void execute() throws MojoExecutionException { super.execute(); if (repositoryPollutionProtection) { final BuildLifecycleAnalyzer lifecycleAnalyzer = new BuildLifecycleAnalyzer( getLog(), lifecycleExecutor, mavenProject, mavenSession); failIfDeployPhaseIsPresent(lifecycleAnalyzer); failIfInstallPhaseIsPresent(lifecycleAnalyzer); failIfCustomClassifierIsPresent(); } } protected abstract boolean shouldRedirectArtifacts(); protected abstract boolean shouldRedirectOutputDirectories(); @Override public boolean isCopyExcludedFiles() { return copyExcludedFiles; } @Override public String getEncoding() { return encoding; } @Override public DistributedCoverage getDistributedCoverage() { return distributedCoverage; } @Override public Set getExcludes() { if (excludesList == null && excludesFile == null) { return excludes; } else if (excludesFile != null) { try { return readPathPatternsFromFile(excludesFile); } catch (IOException e) { getLog().error("Could not read excludesFile: " + excludesFile, e); return Collections.emptySet(); } } else { excludes.addAll(Arrays.asList(excludesList.split(","))); return excludes; } } @Override public String getFlushPolicy() { return this.flushPolicy; } @Override public Set getIncludes() { if (includesList == null && includesFile == null) { return this.includes; } else if (includesFile != null) { try { return readPathPatternsFromFile(includesFile); } catch (IOException e) { getLog().error("Could not read includesFile: " + includesFile, e); return Collections.emptySet(); } } else { return new HashSet(Arrays.asList(includesList.split(","))); } } @Override public String getInstrumentation() { return instrumentation; } @Override public String getInstrumentLambda() { return instrumentLambda; } @Override public String getJdk() { return this.jdk; } @Override public Map getMethodContexts() { return methodContexts; } @Override public Set getMethodWithMetricsContexts() { return methodWithMetricsContexts; } @Override public Map getStatementContexts() { return statementContexts; } @Override public int getStaleMillis() { return staleMillis; } @Override public boolean isIncludesAllSourceRoots() { return this.includesAllSourceRoots; } @Override public boolean isUseFullyQualifiedJavaLang() { return useFullyQualifiedJavaLang; } @Override public TestSources getTestSources() { return testSources; } @Override public boolean isRecordTestResults() { return recordTestResults; } private static final String PROTECTION_ENABLED_MSG = "Clover's repository pollution protection is enabled. "; private static final String DISABLING_PROTECTION_MSG = "You can also disable repository pollution protection (-Dmaven.clover.repositoryPollutionProtection=false) if this is intentional."; /** * Read list of file paths to exclude/include from file * * @param file path to external file with list of files to exclude/include separated by new line * @return set of files to include/exclude * @throws IOException if can't read external file */ private Set readPathPatternsFromFile(final String file) throws IOException { Set files = new HashSet(); BufferedReader br = null; try { String line; br = new BufferedReader(new FileReader(file)); while ((line = br.readLine()) != null) { files.add(line); } } finally { IOStreamUtils.close(br); } return files; } /** * Check if the build life cycle contains the 'install' phase. * * @param lifecycleAnalyzer analyser * @throws org.apache.maven.plugin.MojoExecutionException if 'install' phase is present */ protected void failIfInstallPhaseIsPresent(final BuildLifecycleAnalyzer lifecycleAnalyzer) throws MojoExecutionException { if (lifecycleAnalyzer.isInstallPresent() && (!useCloverClassifier || !shouldRedirectArtifacts())) { throw new MojoExecutionException(PROTECTION_ENABLED_MSG + "Your build runs 'install' phase which can put instrumented JARs into ~/.m2 local cache. " + "In order to fix this: \n" + " - run a build till the 'verify' phase (the latest)\n" + " - check if some build plug-in does not fork a parallel build cycle which runs till the 'install' phase\n" + DISABLING_PROTECTION_MSG); } } /** * Check if the build life cycle contains the 'deploy' phase. * * @param lifecycleAnalyzer analyser * @throws org.apache.maven.plugin.MojoExecutionException if 'deploy' phase is present */ protected void failIfDeployPhaseIsPresent(final BuildLifecycleAnalyzer lifecycleAnalyzer) throws MojoExecutionException { if (lifecycleAnalyzer.isDeployPresent() && (!useCloverClassifier || !shouldRedirectArtifacts())) { throw new MojoExecutionException(PROTECTION_ENABLED_MSG + "Your build runs 'deploy' phase which can upload instrumented JARs into your repository. " + "In order to fix this: \n" + " - run a build till the 'verify' phase (the latest)\n" + " - check if some build plug-in does not fork a parallel build cycle which runs till the 'deploy' phase\n" + DISABLING_PROTECTION_MSG); } } /** * Check if an artifact has a custom classifier (except the 'javadoc' and 'sources' ones). * If a custom classifier is present then adding a second 'clover' classifier may not work correctly * as Maven does not support multiple classifiers. * * @throws org.apache.maven.plugin.MojoExecutionException if custom classifier is present */ protected void failIfCustomClassifierIsPresent() throws MojoExecutionException { final String classifier = getProject().getArtifact().getClassifier(); final boolean customClassifierUsed = StringUtils.isNotEmpty(classifier) && !"javadoc".equals(classifier) && !"sources".equals(classifier); if (customClassifierUsed && useCloverClassifier && shouldRedirectArtifacts()) { throw new MojoExecutionException(PROTECTION_ENABLED_MSG + "Your build produces an artifact (" + getProject().getArtifact() + ") with a custom classifier. " + "As Maven does not support multiple " + "classifiers for an artifact, appending second 'clover' classifier may not be handled correctly. " + "You can: \n - remove a custom classifier or\n - configure Clover to not append the '-clover' classifier \n" + "to fix it. You can also disable pollution protection " + "(-Dmaven.clover.repositoryPollutionProtection=false) if you know " + "that it doesn't affect your build. "); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy