Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #56 from demonfiddler/master
Merge fixes for JENKINS-31258 and JENKINS-31524.
As it's merged now @demonfiddler please document the new expression in the wiki page.
  • Loading branch information
olamy committed Mar 1, 2016
2 parents 0223184 + 45712ad commit 3ba74bd
Show file tree
Hide file tree
Showing 7 changed files with 218 additions and 37 deletions.
12 changes: 6 additions & 6 deletions src/main/java/hudson/maven/reporters/SurefireArchiver.java
Expand Up @@ -74,10 +74,10 @@ public class SurefireArchiver extends TestFailureDetector {
private final AtomicBoolean hasTestFailures = new AtomicBoolean();

/**
* Store result files already parsed, so we don't parse them again,
* if a later running mojo specifies the same reports directory.
* Store result files and modification timestamps already parsed, so we don't parse them again
* if a later running MOJO specifies the same reports directory.
*/
private transient ConcurrentMap<File, File> parsedFiles = new ConcurrentHashMap<File,File>();
private transient ConcurrentMap<File, Long> parsedFiles = new ConcurrentHashMap<File, Long>();

@Override
public boolean hasTestFailures() {
Expand Down Expand Up @@ -138,7 +138,7 @@ public boolean postExecute(MavenBuildProxy build, MavenProject pom, MojoInfo moj
fileSet = Iterables.filter(fileSet, new Predicate<File>() {
@Override
public boolean apply(File input) {
return !parsedFiles.containsKey(input);
return !parsedFiles.containsKey(input) || parsedFiles.get(input) != input.lastModified();
}
});

Expand Down Expand Up @@ -213,7 +213,7 @@ private void markBuildAsSuccess(Throwable mojoError, MavenBuildInformation build
*/
private void rememberCheckedFiles(Iterable<File> fileSet) {
for (File f : fileSet) {
this.parsedFiles.put(f, f);
this.parsedFiles.put(f, f.lastModified());
}
}

Expand Down Expand Up @@ -330,7 +330,7 @@ private TestMojo getTestMojo(MojoInfo mojo) {
// I'm not sure if SurefireArchiver is actually ever (de-)serialized,
// but just to be sure, set fileSets here
protected Object readResolve() {
parsedFiles = new ConcurrentHashMap<File,File>();
parsedFiles = new ConcurrentHashMap<File, Long>();
return this;
}

Expand Down
70 changes: 59 additions & 11 deletions src/main/java/hudson/maven/reporters/TestMojo.java
Expand Up @@ -9,10 +9,12 @@
import java.util.Iterator;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;

import org.apache.maven.project.MavenProject;
import org.apache.tools.ant.types.FileSet;
import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;

import com.google.common.base.Function;
import com.google.common.collect.Iterators;
Expand Down Expand Up @@ -167,22 +169,36 @@ public boolean canRunTests(MojoInfo mojo) {
}

@CheckForNull public Iterable<File> getReportFiles(MavenProject pom, MojoInfo mojo) throws ComponentConfigurationException {
if (this.reportDirectoryConfigKey != null) {
File reportsDir = mojo.getConfigurationValue(this.reportDirectoryConfigKey, File.class);
if (reportsDir != null && reportsDir.exists()) {
return getReportFiles(reportsDir, getFileSet(reportsDir));
}

}

// some plugins just default to this:
File reportsDir = new File(pom.getBuild().getDirectory(), "surefire-reports");
File reportsDir = getReportsDirectory(pom, mojo);
if (reportsDir.exists()) {
return getReportFiles(reportsDir, getFileSet(reportsDir));
}

return null;
}

/**
* Returns the location of test reports created by the specified MOJO.
* @param pom The project model.
* @param mojo The MOJO.
* @return The directory containing the test reports.
* @throws ComponentConfigurationException if unable to retrieve the report directory from the MOJO configuration.
*/
@Nonnull protected File getReportsDirectory(MavenProject pom, MojoInfo mojo) throws ComponentConfigurationException {
// [JENKINS-31258] Allow unknown MOJOs to contribute test results in arbitrary locations by setting a Maven property.
String reportsDirectoryOverride = getReportsDirectoryOverride(mojo);
if (reportsDirectoryOverride != null)
return mojo.expressionEvaluator.alignToBaseDirectory(new File(reportsDirectoryOverride));

if (this.reportDirectoryConfigKey != null) {
File reportsDir = mojo.getConfigurationValue(this.reportDirectoryConfigKey, File.class);
if (reportsDir != null)
return reportsDir;
}

// some plugins just default to this:
return new File(pom.getBuild().getDirectory(), "surefire-reports");
}

private Iterable<File> getReportFiles(final File baseDir, FileSet set) {
final String[] includedFiles = set.getDirectoryScanner().getIncludedFiles();
Expand Down Expand Up @@ -231,12 +247,44 @@ public static TestMojo lookup(String artifactId, String groupId, String goal) {

public static TestMojo lookup(MojoInfo mojo) {
TestMojo testMojo = lookup(mojo.pluginName.groupId, mojo.pluginName.artifactId, mojo.getGoal());

if (testMojo == null && getReportsDirectoryOverride(mojo) != null) {
testMojo = FALLBACK;
}

if (testMojo != null && testMojo.canRunTests(mojo)) {
return testMojo;
}

return null;
}


/**
* For an unknown test-capable MOJO, returns the path to its reports directory as configured by a Maven property.
* @param mojo An unknown MOJO.
* @return the value of the expression <code>${jenkins.&lt;mojo-execution-id&gt;.reportsDirectory}</code>.
*/
@CheckForNull private static String getReportsDirectoryOverride(MojoInfo mojo) {
// TODO: if and when this MOJO ceases to be tested against earlier Maven versions lacking MojoExecution.getLifecyclePhase(),
// Reinstate the following validity check:
// Validity of the override property should be constrained to "test" and "integration-test" phases but there seems to be no
// way to check this, as MojoExecution.getLifecyclePhase() (added 2009-05-12) causes tests to throw NoSuchMethodError!!!
// Until then, we're obliged to assume that the property is configured for a valid test life cycle phase.
// String phase = mojo.mojoExecution.getLifecyclePhase();
// if ("test".equals(phase) || "integration-test".equals(phase)) {
try {
String reportsDirExpr = "${jenkins." + mojo.mojoExecution.getExecutionId() + ".reportsDirectory}";
Object result = mojo.expressionEvaluator.evaluate(reportsDirExpr);
if (result != null && !result.equals(reportsDirExpr) && !result.toString().trim().isEmpty())
return result.toString().trim();
} catch (ExpressionEvaluationException e) {
// Note: no access to private MavenProject.logger.
e.printStackTrace();
}
// }
return null;
}

static class Key {
private String artifactId;
private String groupId;
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/hudson/maven/Messages.properties
Expand Up @@ -55,7 +55,7 @@ MavenProbeAction.DisplayName=Monitor Maven Process
MavenProcessFactory.ClassWorldsNotFound=No classworlds*.jar found in {0} -- Is this a valid maven directory?

MavenRedeployer.DisplayName=Deploy to Maven repository
MavenVersionCallable.MavenHomeDoesntExist=Maven Home {0} doesn\u2019t exist
MavenVersionCallable.MavenHomeDoesntExist=Maven Home {0} doesn't exist
MavenVersionCallable.MavenHomeIsNotDirectory=Maven Home {0} is not a directory
ProcessCache.Reusing=Reusing existing maven process

Expand Down
47 changes: 28 additions & 19 deletions src/test/java/hudson/maven/MojoInfoBuilder.java
Expand Up @@ -19,6 +19,19 @@ public class MojoInfoBuilder {
private String artifactId;
private String goalName;
private String version = "1.0";
private String executionId;
private ExpressionEvaluator evaluator = new ExpressionEvaluator() {
@Override
public Object evaluate(String expression) {
return expression;
}

@Override
public File alignToBaseDirectory(File file) {
return file;
}
};

private Map<String, String> configValues = new HashMap<String, String>();
private long startTime = System.currentTimeMillis();

Expand All @@ -34,7 +47,7 @@ private MojoInfoBuilder(String groupId, String artifactId, String goalName) {

public MojoInfoBuilder copy() {
MojoInfoBuilder copy = new MojoInfoBuilder(this.groupId, this.artifactId, this.goalName)
.version(this.version);
.version(this.version).executionId(this.executionId).evaluator(this.evaluator);
copy.configValues.putAll(this.configValues);
return copy;
}
Expand All @@ -44,12 +57,22 @@ public MojoInfoBuilder version(String version) {
return this;
}

public MojoInfoBuilder executionId(String executionId) {
this.executionId = executionId;
return this;
}

public MojoInfoBuilder evaluator(ExpressionEvaluator evaluator) {
this.evaluator = evaluator;
return this;
}

public MojoInfoBuilder startTime(long startTime) {
this.startTime = startTime;
return this;
}

public MojoInfoBuilder configValue(String key,String value) {
public MojoInfoBuilder configValue(String key, String value) {
configValues.put(key, value);
return this;
}
Expand All @@ -64,28 +87,14 @@ public MojoInfo build() {
mojoDescriptor.setPluginDescriptor(pluginDescriptor);
mojoDescriptor.setGoal(goalName);

MojoExecution mojoExecution = new MojoExecution(mojoDescriptor);
MojoExecution mojoExecution = new MojoExecution(mojoDescriptor, executionId);

PlexusConfiguration configuration = new DefaultPlexusConfiguration("configuration");
for (Map.Entry<String, String> e : this.configValues.entrySet()) {
configuration.addChild(e.getKey(),e.getValue());
configuration.addChild(e.getKey(), e.getValue());
}

ExpressionEvaluator evaluator = new ExpressionEvaluator() {
@Override
public Object evaluate(String expression) {
return expression;
}

@Override
public File alignToBaseDirectory(File file) {
return file;
}
};

MojoInfo info = new MojoInfo(mojoExecution, null, configuration, evaluator, startTime);
MojoInfo info = new MojoInfo(mojoExecution, null, configuration, evaluator, startTime);
return info;
}


}
Expand Up @@ -3,11 +3,18 @@
import static hudson.maven.MojoInfoBuilder.mojoBuilder;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.io.File;

import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;

import hudson.maven.MojoInfo;
import hudson.maven.MojoInfoBuilder;

import org.junit.Before;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;

/**
* Regression test for the detection of test mojos in {@link SurefireArchiver}.
Expand Down Expand Up @@ -197,6 +204,28 @@ public void shouldDetectAnyMojoWithATestGoal() {
assertTrue(this.surefireArchiver.isTestMojo(mojo));
}

@Test
@Issue("JENKINS-31258")
public void shouldDetectAnyMojoWithAJenkinsReportsDirectoryProperty() {
MojoInfoBuilder builder = mojoBuilder("some.weird.internal", "xxx-mojo", "xxx-goal")
.executionId("xxx-execution-id").evaluator(new ExpressionEvaluator() {
@Override
public Object evaluate(String expression) throws ExpressionEvaluationException {
if ("${jenkins.xxx-execution-id.reportsDirectory}".equals(expression))
return "target/xxx-test-reports";
return expression;
}

@Override
public File alignToBaseDirectory(File path) {
return path;
}
});

MojoInfo mojo = builder.build();
assertTrue(this.surefireArchiver.isTestMojo(mojo));
}

@Test
public void shouldNotDetectNonTestGoal() {
MojoInfoBuilder builder = mojoBuilder("some.weird.internal","test-mojo", "verify");
Expand Down
28 changes: 28 additions & 0 deletions src/test/java/hudson/maven/reporters/SurefireArchiverUnitTest.java
Expand Up @@ -36,6 +36,7 @@
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.RandomlyFails;
import org.mockito.Matchers;
import org.mockito.invocation.InvocationOnMock;
Expand Down Expand Up @@ -153,6 +154,33 @@ public void testResultsAreNotCountedTwice() throws InterruptedException, IOExcep
assertEquals(2658, result.getTotalCount());
}

@Test
@Issue("JENKINS-31524")
public void testUpdatedExistingResultsAreCounted() throws InterruptedException, IOException, URISyntaxException, ComponentConfigurationException {
URL resource = SurefireArchiverUnitTest.class.getResource("/surefire-archiver-test2");
File reportsDir = new File(resource.toURI().getPath());
doReturn(reportsDir).when(this.mojoInfo).getConfigurationValue("reportsDirectory", File.class);

File report1 = new File(reportsDir, "junit-report-1233.xml");
File report2 = new File(reportsDir, "junit-report-1472.xml");
long startTime = this.mojoInfo.getStartTime();

// Current report files should be counted and stale ones ignored.
report1.setLastModified(startTime);
report2.setLastModified(startTime - (60 * 1000));
this.archiver.postExecute(buildProxy, null, this.mojoInfo, new NullBuildListener(), null);
SurefireReport action = this.build.getAction(SurefireReport.class);
TestResult result = action.getResult();
assertEquals(5, result.getTotalCount());

// Updated existing report files should now be included.
report2.setLastModified(startTime);
this.archiver.postExecute(buildProxy, null, this.mojoInfo, new NullBuildListener(), null);
action = this.build.getAction(SurefireReport.class);
result = action.getResult();
assertEquals(2658, result.getTotalCount());
}

@Test
public void testMultiThreaded() throws InterruptedException, IOException, URISyntaxException, ComponentConfigurationException {
File reportsDir2 = new File(SurefireArchiverUnitTest.class.getResource("/surefire-archiver-test2").toURI().getPath());
Expand Down

0 comments on commit 3ba74bd

Please sign in to comment.