From 6d5e6cf66b5d7f26dc8215ad0d0d9a77365e73cf Mon Sep 17 00:00:00 2001 From: "javier.salinas" Date: Wed, 24 Apr 2019 23:19:56 +0200 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 761d80ff42..4e9f2ef17e 100644 --- a/spock-core/src/main/java/spock/lang/Retry.java +++ b/spock-core/src/main/java/spock/lang/Retry.java @@ -103,7 +103,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 027ae802d2..1351327a17 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; /** @@ -47,6 +48,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. @@ -122,11 +124,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 @@ -138,11 +147,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; @@ -151,13 +159,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 48876658d1..65e39e5621 100755 --- a/spock-specs/specs.gradle +++ b/spock-specs/specs.gradle @@ -10,8 +10,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 @@ -30,7 +30,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 } ext.spockLogFileDir = file("$buildDir/spock/logFiles") 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 84ff9a6099..99c32530d1 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.Specification @@ -126,12 +127,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++ + } + } + } }