diff --git a/checkstyle-suppressions.xml b/checkstyle-suppressions.xml index 72976b17..87e3c4c7 100644 --- a/checkstyle-suppressions.xml +++ b/checkstyle-suppressions.xml @@ -12,4 +12,6 @@ + diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/BuildFailureScanner.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/BuildFailureScanner.java index 512cdffd..e8166993 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/BuildFailureScanner.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/BuildFailureScanner.java @@ -50,6 +50,7 @@ import hudson.tasks.test.AbstractTestResultAction; import hudson.tasks.test.TestResult; import jenkins.model.Jenkins; +import org.apache.commons.lang.StringUtils; import javax.annotation.Nonnull; import java.io.BufferedReader; @@ -252,6 +253,10 @@ public static void scan(Run build, PrintStream scanLog) { notifySlackAllFail = true; } + if (PluginImpl.getInstance().isBuildDescriptionEnabled() && !foundCauseList.isEmpty()) { + build.setDescription(generateDescriptionString(build, foundCauseList)); + } + StatisticsLogger.getInstance().log(build, foundCauseListToLog); // Check slack plugin is installed @@ -649,4 +654,47 @@ private static List findFailedTests(final Run build, final Pr return failedTestList; } + + /** + * Generate text that can be used for the build description using categories and failure causes. + * + * @param build to get current build description + * @param foundCauseList list of failure causes + * @return A String of the BFA categories and causes appended to the build's description. + */ + public static String generateDescriptionString(Run build, List foundCauseList) { + StringBuffer buildDescription = new StringBuffer(); + buildDescription.append(""); + + if (foundCauseList.get(0) != null) { + + for (int j = 0; j < foundCauseList.get(0).getCategories().size(); j++) { + buildDescription.append(""); + buildDescription.append(foundCauseList.get(0).getCategories().get(j)); + buildDescription.append(" "); + } + if (foundCauseList.get(0).getCategories().size() > 0) { + buildDescription.append(": "); + } + } + + // Append all failure causes. + if (foundCauseList.get(0) != null) { + for (int i = 0; i < foundCauseList.size(); i++) { + buildDescription.append(""); + buildDescription.append(foundCauseList.get(i).getDescription()); + buildDescription.append(""); + if (i < (foundCauseList.size() - 1)) { + buildDescription.append(" "); + } + } + } + buildDescription.append(""); + + // Append this build description to any pre-existing build description + if (StringUtils.isNotEmpty(build.getDescription())) { + buildDescription.insert(0, (build.getDescription().concat("
\n"))); + } + return buildDescription.toString(); + } } diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java index 19aff29e..4832a244 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java @@ -161,6 +161,7 @@ public class PluginImpl extends GlobalConfiguration { private int nrOfScanThreads; private int maxLogSize; + private boolean buildDescriptionEnabled; private Boolean graphsEnabled; @@ -680,6 +681,25 @@ public int getMaxLogSize() { } + /** + * Set option to append failure causes to job's build description. + * + * @param buildDescriptionEnabled should build description option be turned on + */ + @DataBoundSetter + public void setBuildDescriptionEnabled(boolean buildDescriptionEnabled) { + this.buildDescriptionEnabled = buildDescriptionEnabled; + } + + /** + * If buildDescriptionEnabled is enabled or not. Build Descriptions will be set to a concatenated + * list of the failure descriptions as a convenience. + * @return True if enabled. + */ + public boolean isBuildDescriptionEnabled() { + return buildDescriptionEnabled; + } + /** * Checks if the build with certain result should be analyzed or not. * diff --git a/src/main/resources/com/sonyericsson/jenkins/plugins/bfa/PluginImpl/config.jelly b/src/main/resources/com/sonyericsson/jenkins/plugins/bfa/PluginImpl/config.jelly index b4a9dca0..2a9b6129 100644 --- a/src/main/resources/com/sonyericsson/jenkins/plugins/bfa/PluginImpl/config.jelly +++ b/src/main/resources/com/sonyericsson/jenkins/plugins/bfa/PluginImpl/config.jelly @@ -96,5 +96,10 @@ + + + diff --git a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/BuildFailureScannerHudsonTest.java b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/BuildFailureScannerHudsonTest.java index 92b8975f..eb5ac21c 100644 --- a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/BuildFailureScannerHudsonTest.java +++ b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/BuildFailureScannerHudsonTest.java @@ -104,6 +104,7 @@ public class BuildFailureScannerHudsonTest { private static final String BUILD_LOG = "ERROR: brief\n detail\n"; private static final String BUILD_LOG_FIRST_LINE = "ERROR: brief"; + private static final String BUILD_DESCRIPTION = "This is a build description."; private static final String DESCRIPTION = "The error was: ${1,1}${2,1}"; private static final String REGEX = "ERROR: (.*?)$"; private static final String MULTILINE_REGEX = "ERROR: (.*?)$.*? detail"; @@ -1031,6 +1032,86 @@ public boolean perform(AbstractBuild build, Launcher launcher, assertEquals("Amount of failure causes does not match.", 0, causeListFromAction.size()); } + /** + * Test buildDescriptionEnabled = false does not append failure cause to build description. + * + * @throws Exception if a build description is appended when the setting is set to false. + */ + @Test + public void testTestBuildDescriptionEnabledIfDisabled() throws Exception { + PluginImpl.getInstance().setTestResultParsingEnabled(true); + PluginImpl.getInstance().setBuildDescriptionEnabled(false); + FreeStyleProject project = jenkins.createFreeStyleProject(); + + project.getBuildersList().add(new PrintToLogBuilder(BUILD_LOG)); + project.getBuildersList().add(new TestBuilder() { + public boolean perform(AbstractBuild build, Launcher launcher, + BuildListener listener) throws InterruptedException, IOException { + build.getWorkspace().child("junit.xml").copyFrom( + this.getClass().getResource("junit.xml")); + return true; + } + }); + + project.getPublishersList().add(new JUnitResultArchiver("junit.xml", false, null)); + + QueueTaskFuture future = project.scheduleBuild2(0, new Cause.UserIdCause()); + FreeStyleBuild build = future.get(10, TimeUnit.SECONDS); + jenkins.assertBuildStatus(Result.UNSTABLE, build); + + FailureCauseBuildAction action = build.getAction(FailureCauseBuildAction.class); + assertNotNull(action); + + List causeListFromAction = action.getFoundFailureCauses(); + assertEquals("Amount of failure causes does not match.", 2, causeListFromAction.size()); + + // Start with some build description not assigned by BFA and run another scan. + action.getFoundFailureCauses(); + assertEquals(build.getDescription(), null); + } + + /** + * Test buildDescriptionEnabled = true does append failure cause to existing build description. + * with categories set + * + * @throws Exception if description is not appended correctly. + */ + @Test + public void testTestBuildDescriptionEnabledWithCategoriesIfEnabled() throws Exception { + PluginImpl.getInstance().setTestResultParsingEnabled(true); + PluginImpl.getInstance().setBuildDescriptionEnabled(true); + String categories = "foo bar"; + PluginImpl.getInstance().setTestResultCategories(categories); + FreeStyleProject project = jenkins.createFreeStyleProject(); + + // Test with a preset build description + project.getBuildersList().add(new PrintToLogBuilder(BUILD_LOG)); + project.getBuildersList().add(new TestBuilder() { + public boolean perform(AbstractBuild build, Launcher launcher, + BuildListener listener) throws InterruptedException, IOException { + build.getWorkspace().child("junit.xml").copyFrom(this.getClass().getResource("junit.xml")); + build.setDescription("Hello World"); + return true; + } + }); + + project.getPublishersList().add(new JUnitResultArchiver("junit.xml", false, null)); + + QueueTaskFuture future = project.scheduleBuild2(0, new Cause.UserIdCause()); + FreeStyleBuild build = future.get(10, TimeUnit.SECONDS); + jenkins.assertBuildStatus(Result.UNSTABLE, build); + + FailureCauseBuildAction action = build.getAction(FailureCauseBuildAction.class); + assertNotNull(action); + + List causeListFromAction = action.getFoundFailureCauses(); + assertEquals("Amount of failure causes does not match.", 2, causeListFromAction.size()); + + String testDescription = "Hello World
\n" + + "foo bar : Here are details of the failure... More details"; + assertEquals(build.getDescription(), testDescription); + } + /** * ArgumentMatcher for a Statistics object. */ diff --git a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-expected.yml b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-expected.yml index 5990751a..33db670d 100644 --- a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-expected.yml +++ b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-expected.yml @@ -1,3 +1,4 @@ +buildDescriptionEnabled: false doNotAnalyzeAbortedJob: true gerritTriggerEnabled: true globalEnabled: true diff --git a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-expected.yml b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-expected.yml index 4f30360b..bce219db 100644 --- a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-expected.yml +++ b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-expected.yml @@ -1,3 +1,4 @@ +buildDescriptionEnabled: false doNotAnalyzeAbortedJob: true gerritTriggerEnabled: true globalEnabled: true