From 33a6b0f86dbc44beeceb2446fcaf1cc1a0ab1c39 Mon Sep 17 00:00:00 2001 From: MuSTa1nE Date: Sun, 5 Jan 2020 16:50:06 +0100 Subject: [PATCH] Throw an exception when during polling conditions there is an exception invoking the closure. Co-authored-by: jordi9 --- .../src/main/java/spock/lang/Retry.java | 2 +- .../util/concurrent/PollingConditions.java | 19 +++++++-- spock-specs/specs.gradle | 6 +-- .../concurrent/PollingConditionsSpec.groovy | 42 ++++++++++++++++++- 4 files changed, 59 insertions(+), 10 deletions(-) diff --git a/spock-core/src/main/java/spock/lang/Retry.java b/spock-core/src/main/java/spock/lang/Retry.java index 0a9f9ca67c..c8289ccc98 100644 --- a/spock-core/src/main/java/spock/lang/Retry.java +++ b/spock-core/src/main/java/spock/lang/Retry.java @@ -99,7 +99,7 @@ enum Mode { ITERATION, /** - * Retry the the feature together with the setup and cleanup methods. + * Retry the feature together with the setup and cleanup methods. */ SETUP_FEATURE_CLEANUP } diff --git a/spock-core/src/main/java/spock/util/concurrent/PollingConditions.java b/spock-core/src/main/java/spock/util/concurrent/PollingConditions.java index 07c788ffa9..dd04af268c 100755 --- a/spock-core/src/main/java/spock/util/concurrent/PollingConditions.java +++ b/spock-core/src/main/java/spock/util/concurrent/PollingConditions.java @@ -19,6 +19,7 @@ import org.spockframework.lang.ConditionBlock; import org.spockframework.runtime.GroovyRuntimeUtil; import org.spockframework.runtime.SpockTimeoutError; +import org.spockframework.runtime.UnallowedExceptionThrownError; import org.spockframework.util.Beta; /** @@ -53,6 +54,7 @@ public class PollingConditions { private double initialDelay = 0; private double delay = 0.1; private double factor = 1.0; + private boolean strict = false; /** * Returns the timeout (in seconds) until which the conditions have to be satisfied. @@ -128,11 +130,18 @@ public void setFactor(double factor) { this.factor = factor; } + /** + * Returns if is strict mode. If strict mode is enabled, will fail if any exception rather than {@link AssertionError} is thrown. + * Defaults to {@code false} + */ + public boolean isStrict() { + return strict; + } + /** * Repeatedly evaluates the specified conditions until they are satisfied or the timeout has elapsed. * * @param conditions the conditions to evaluate - * * @throws InterruptedException if evaluation is interrupted */ @ConditionBlock @@ -144,11 +153,10 @@ public void eventually(Closure conditions) throws InterruptedException { * Repeatedly evaluates the specified conditions until they are satisfied or the specified timeout (in seconds) has elapsed. * * @param conditions the conditions to evaluate - * * @throws InterruptedException if evaluation is interrupted */ @ConditionBlock - public void within(double seconds, Closure conditions) throws InterruptedException { + public void within(double seconds, Closure conditions) throws InterruptedException { long timeoutMillis = toMillis(seconds); long start = System.currentTimeMillis(); long lastAttempt = 0; @@ -157,13 +165,16 @@ public void within(double seconds, Closure conditions) throws InterruptedExce long currDelay = toMillis(delay); int attempts = 0; - while(true) { + while (true) { try { attempts++; lastAttempt = System.currentTimeMillis(); GroovyRuntimeUtil.invokeClosure(conditions); return; } catch (Throwable e) { + if (strict && !(e instanceof AssertionError)) { + throw new UnallowedExceptionThrownError(e.getClass(), e); + } long elapsedTime = lastAttempt - start; if (elapsedTime >= timeoutMillis) { String msg = String.format("Condition not satisfied after %1.2f seconds and %d attempts", elapsedTime / 1000d, attempts); diff --git a/spock-specs/specs.gradle b/spock-specs/specs.gradle index d159baaf17..195fd30288 100755 --- a/spock-specs/specs.gradle +++ b/spock-specs/specs.gradle @@ -9,8 +9,8 @@ configurations { } dependencies { - testCompile project(":spock-core") - testCompile project(":spock-junit4") + testImplementation project(":spock-core") + testImplementation project(":spock-junit4") testRuntime libs.asm testRuntime libs.bytebuddy @@ -27,7 +27,7 @@ targetCompatibility = javaVersion // necessary to make @NotYetImplemented transform work (transform that ships // with Groovy and statically references third-party class junit.framwork.AssertionFailedError) tasks.withType(GroovyCompile) { - groovyClasspath += configurations.junit + groovyClasspath += configurations.junit } test { diff --git a/spock-specs/src/test/groovy/spock/util/concurrent/PollingConditionsSpec.groovy b/spock-specs/src/test/groovy/spock/util/concurrent/PollingConditionsSpec.groovy index 382ec7b536..13bb3257f4 100755 --- a/spock-specs/src/test/groovy/spock/util/concurrent/PollingConditionsSpec.groovy +++ b/spock-specs/src/test/groovy/spock/util/concurrent/PollingConditionsSpec.groovy @@ -16,6 +16,7 @@ package spock.util.concurrent import org.spockframework.runtime.ConditionNotSatisfiedError import org.spockframework.runtime.SpockTimeoutError +import org.spockframework.runtime.UnallowedExceptionThrownError import spock.lang.Issue import spock.lang.PendingFeature import spock.lang.Specification @@ -154,12 +155,49 @@ class PollingConditionsSpec extends Specification { then: condition.eventually { try { - sleep 200; - assert secondAttempt; + sleep 200 + assert secondAttempt } finally { secondAttempt = true } } } + + def "fails if an exception is thrown while polling"() { + given: + def iteration = 0 + + when: + new PollingConditions(strict: true).eventually { + try { + if (iteration < 2) { + throw new IllegalStateException("An exception is thrown") + } + assert true + } finally { + iteration++ + } + } + then: + def ex = thrown(UnallowedExceptionThrownError) + ex.unallowed == IllegalStateException + } + + def "swallow intermediate exceptions while polling"() { + given: + def iteration = 0 + + expect: + new PollingConditions(strict: false).eventually { + try { + if (iteration < 2) { + throw new IllegalStateException("An exception is thrown") + } + assert true + } finally { + iteration++ + } + } + } }