From d657f0dc28519ee8700096040ddf2e726d1373a1 Mon Sep 17 00:00:00 2001 From: Christopher Fenner Date: Fri, 8 Feb 2019 12:30:59 +0100 Subject: [PATCH] testsPublishResults: add option to fail the build on test errors (#472) * add option to fail the build on test errors * fix typo * add test cases * adjust docs * set build result * add hasTestFailure utils method * use utils method * use dedicated type * adapt tests * handle missing test actions * Update testsPublishResults.md * Update JenkinsUtils.groovy * Update JenkinsUtils.groovy * Update JenkinsUtils.groovy * remove comments * adapt test case * Update TestsPublishResultsTest.groovy --- .../docs/steps/testsPublishResults.md | 2 + resources/default_pipeline_environment.yml | 1 + src/com/sap/piper/JenkinsUtils.groovy | 9 +++++ test/groovy/TestsPublishResultsTest.groovy | 39 +++++++++++++++++++ vars/testsPublishResults.groovy | 18 +++++---- 5 files changed, 61 insertions(+), 8 deletions(-) diff --git a/documentation/docs/steps/testsPublishResults.md b/documentation/docs/steps/testsPublishResults.md index 5270af979..a171c6ad2 100644 --- a/documentation/docs/steps/testsPublishResults.md +++ b/documentation/docs/steps/testsPublishResults.md @@ -33,6 +33,7 @@ Available parameters: | parameter | mandatory | default | possible values | | ----------|-----------|---------|-----------------| | script | yes | | | +| `failOnError` | no | `false` | `true`, `false` | | junit | no | `false` | true, false | | jacoco | no | `false` | true, false | | cobertura | no | `false` | true, false | @@ -43,6 +44,7 @@ Available parameters: with the `this` parameter, as in `script: this`. This allows the function to access the [`commonPipelineEnvironment`](commonPipelineEnvironment.md) for retrieving, for example, configuration parameters. +* `failOnError` - If `failOnError` it set to `true` the step will fail the build if JUnit detected any failing tests. * `junit` - Publishes test results files in JUnit format with the [JUnit Plugin](https://plugins.jenkins.io/junit). * `jacoco` - Publishes code coverage with the [JaCoCo plugin](https://plugins.jenkins.io/jacoco) . * `cobertura` - Publishes code coverage with the [Cobertura plugin](https://plugins.jenkins.io/cobertura). diff --git a/resources/default_pipeline_environment.yml b/resources/default_pipeline_environment.yml index 4f77f47df..9c781f17b 100644 --- a/resources/default_pipeline_environment.yml +++ b/resources/default_pipeline_environment.yml @@ -317,6 +317,7 @@ steps: toJson: false toHtml: false testsPublishResults: + failOnError: false junit: pattern: '**/TEST-*.xml' updateResults: false diff --git a/src/com/sap/piper/JenkinsUtils.groovy b/src/com/sap/piper/JenkinsUtils.groovy index 074172af4..0535aec73 100644 --- a/src/com/sap/piper/JenkinsUtils.groovy +++ b/src/com/sap/piper/JenkinsUtils.groovy @@ -3,6 +3,7 @@ package com.sap.piper import com.cloudbees.groovy.cps.NonCPS import jenkins.model.Jenkins import org.jenkinsci.plugins.workflow.steps.MissingContextVariableException +import hudson.tasks.junit.TestResultAction @API @NonCPS @@ -10,6 +11,14 @@ static def isPluginActive(pluginId) { return Jenkins.instance.pluginManager.plugins.find { p -> p.isActive() && p.getShortName() == pluginId } } +static boolean hasTestFailures(build){ + //build: https://javadoc.jenkins.io/plugin/workflow-support/org/jenkinsci/plugins/workflow/support/steps/build/RunWrapper.html + //getRawBuild: https://javadoc.jenkins.io/plugin/workflow-job/org/jenkinsci/plugins/workflow/job/WorkflowRun.html + //getAction: http://www.hudson-ci.org/javadoc/hudson/tasks/junit/TestResultAction.html + def action = build?.getRawBuild()?.getAction(TestResultAction.class) + return action && action.getFailCount() != 0 +} + def nodeAvailable() { try { sh "echo 'Node is available!'" diff --git a/test/groovy/TestsPublishResultsTest.groovy b/test/groovy/TestsPublishResultsTest.groovy index 1ab50266e..3a4b9ff10 100644 --- a/test/groovy/TestsPublishResultsTest.groovy +++ b/test/groovy/TestsPublishResultsTest.groovy @@ -3,6 +3,7 @@ import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain +import org.junit.rules.ExpectedException import util.BasePiperTest import util.JenkinsReadYamlRule @@ -16,12 +17,14 @@ class TestsPublishResultsTest extends BasePiperTest { Map publisherStepOptions List archiveStepPatterns + private ExpectedException thrown = ExpectedException.none() private JenkinsStepRule stepRule = new JenkinsStepRule(this) @Rule public RuleChain ruleChain = Rules .getCommonRules(this) .around(new JenkinsReadYamlRule(this)) + .around(thrown) .around(stepRule) @Before @@ -126,4 +129,40 @@ class TestsPublishResultsTest extends BasePiperTest { assertTrue('Cobertura options are not empty', publisherStepOptions.cobertura == null) assertTrue('JMeter options are not empty', publisherStepOptions.jmeter == null) } + + @Test + void testBuildResultStatus() throws Exception { + stepRule.step.testsPublishResults(script: nullScript) + assertJobStatusSuccess() + } + + @Test + void testBuildWithTestFailuresAndWithoutFailOnError() throws Exception { + nullScript.currentBuild.getRawBuild = { + return [getAction: { type -> + return [getFailCount: { + return 6 + }] + }] + } + + stepRule.step.testsPublishResults(script: nullScript) + assertJobStatusSuccess() + } + + @Test + void testBuildWithTestFailuresAndWithFailOnError() throws Exception { + nullScript.currentBuild.getRawBuild = { + return [getAction: { type -> + return [getFailCount: { + return 6 + }] + }] + } + + thrown.expect(hudson.AbortException) + thrown.expectMessage('[testsPublishResults] Some tests failed!') + + stepRule.step.testsPublishResults(script: nullScript, failOnError: true) + } } diff --git a/vars/testsPublishResults.groovy b/vars/testsPublishResults.groovy index 8c08098a1..31d7dd121 100644 --- a/vars/testsPublishResults.groovy +++ b/vars/testsPublishResults.groovy @@ -3,6 +3,7 @@ import static com.sap.piper.Prerequisites.checkScript import com.cloudbees.groovy.cps.NonCPS import com.sap.piper.ConfigurationHelper +import com.sap.piper.JenkinsUtils import com.sap.piper.MapUtils import com.sap.piper.Utils import groovy.transform.Field @@ -13,7 +14,9 @@ import groovy.transform.Field @Field def STEP_NAME = getClass().getName() @Field Set GENERAL_CONFIG_KEYS = TOOLS -@Field Set STEP_CONFIG_KEYS = TOOLS +@Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS.plus([ + 'failOnError' +]) @Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS /** @@ -24,10 +27,7 @@ import groovy.transform.Field */ void call(Map parameters = [:]) { handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) { - - def script = checkScript(this, parameters) - if (script == null) - script = this + def script = checkScript(this, parameters) ?: this prepare(parameters) @@ -46,13 +46,15 @@ void call(Map parameters = [:]) { stepParam1: parameters?.script == null ], configuration) - // UNIT TESTS publishJUnitReport(configuration.get('junit')) - // CODE COVERAGE publishJacocoReport(configuration.get('jacoco')) publishCoberturaReport(configuration.get('cobertura')) - // PERFORMANCE publishJMeterReport(configuration.get('jmeter')) + + if (configuration.failOnError && JenkinsUtils.hasTestFailures(script.currentBuild)) { + script.currentBuild.result = 'FAILURE' + error "[${STEP_NAME}] Some tests failed!" + } } }