From 01241311d01657a6229cb16b6619c805332b0e51 Mon Sep 17 00:00:00 2001 From: Stuart Rowe Date: Wed, 10 Jun 2020 13:29:52 -0700 Subject: [PATCH] Add special case for retry step Update CHANGELOG.md Remove unnecessary import --- CHANGELOG.md | 4 +- .../JenkinsPipelineSpecification.groovy | 18 +++++- .../testing/RetryClosureExecutionSpec.groovy | 59 +++++++++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 src/test/groovy/com/homeaway/devtools/jenkins/testing/RetryClosureExecutionSpec.groovy diff --git a/CHANGELOG.md b/CHANGELOG.md index 6080495..948ec26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a ## Unreleased -* (nothing yet) +### Added + +* The `retry()` pipeline step is special-cased and will execute its closure up to the specified count. ## 2.1.4 diff --git a/src/main/groovy/com/homeaway/devtools/jenkins/testing/JenkinsPipelineSpecification.groovy b/src/main/groovy/com/homeaway/devtools/jenkins/testing/JenkinsPipelineSpecification.groovy index 314dfae..b33c9a5 100644 --- a/src/main/groovy/com/homeaway/devtools/jenkins/testing/JenkinsPipelineSpecification.groovy +++ b/src/main/groovy/com/homeaway/devtools/jenkins/testing/JenkinsPipelineSpecification.groovy @@ -675,7 +675,23 @@ public abstract class JenkinsPipelineSpecification extends Specification { } } - } else if( _args != null && _args.length >= 1 && _args[_args.length-1] instanceof Closure ) { + } else if( _name == "retry" ) { + // special-case the retry() step to run up to count times + int left = _args[0] + Closure body = _args[1] + while (left > 0) { + try { + body() + break + } catch (Exception e) { + left-- + if (left <= 0) { + throw e + } + } + } + + } else if( _args != null && _args.length >= 1 && _args[_args.length-1] instanceof Closure ) { // there was at least one argument, and the last argument was a Closure // this almost certainly means that the user's trying to pass the closure to the function as a "body" // they probably want it to execute right now. diff --git a/src/test/groovy/com/homeaway/devtools/jenkins/testing/RetryClosureExecutionSpec.groovy b/src/test/groovy/com/homeaway/devtools/jenkins/testing/RetryClosureExecutionSpec.groovy new file mode 100644 index 0000000..50e4c4c --- /dev/null +++ b/src/test/groovy/com/homeaway/devtools/jenkins/testing/RetryClosureExecutionSpec.groovy @@ -0,0 +1,59 @@ +/* + Copyright (c) 2020 Electronic Arts Inc. + All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +package com.homeaway.devtools.jenkins.testing; + +/** + * Verifies that the retry(...) pipeline step is special-cased and + * that any closures passed to it as arguments are executed up to count times. + * + * @author stuartrowe + * + */ +public class RetryClosureExecutionSpec extends JenkinsPipelineSpecification { + + def "retry() closure is only executed once when the first execution succeeds"() { + when: + retry(2) { + sh( "echo hello" ) + } + then: + 1 * getPipelineMock("sh")(*_) + } + + def "retry() closure is executed twice when the first execution fails"() { + when: + retry(2) { + sh( "echo hello" ) + } + then: + 1 * getPipelineMock("sh")(*_) >> { throw new Exception() } + then: + 1 * getPipelineMock("sh")(*_) + } + + def "retry() propagates the exception when retries are exhausted"() { + when: + retry(2) { + sh( "echo hello" ) + } + then: + 2 * getPipelineMock("sh")(*_) >> { throw new Exception() } + then: + thrown Exception + } +} \ No newline at end of file