Skip to content

Commit

Permalink
untilAsserted takes a ThrowingRunnable
Browse files Browse the repository at this point in the history
  • Loading branch information
johanhaleby committed Apr 6, 2017
1 parent 69b9244 commit d6a41fd
Show file tree
Hide file tree
Showing 8 changed files with 35 additions and 63 deletions.
Expand Up @@ -16,6 +16,7 @@
package org.awaitility.groovy package org.awaitility.groovy


import org.awaitility.core.ConditionFactory import org.awaitility.core.ConditionFactory
import org.awaitility.core.ThrowingRunnable


import java.util.concurrent.Callable import java.util.concurrent.Callable


Expand All @@ -27,11 +28,17 @@ class AwaitilityExtensionModule {
timeout_message = "Condition was not fulfilled" timeout_message = "Condition was not fulfilled"
} }


@SuppressWarnings("ChangeToOperator")
static void until(ConditionFactory self, Runnable runnable) { static void until(ConditionFactory self, Runnable runnable) {
if (runnable instanceof Closure) { if (runnable instanceof Closure) {
self.until({ (runnable as Closure).call().asBoolean() } as Callable<Boolean>) self.until({ (runnable as Closure).call().asBoolean() } as Callable<Boolean>)
} else { } else {
self.untilAsserted(runnable) self.untilAsserted(new ThrowingRunnable() {
@Override
void run() throws Throwable {
runnable.run()
}
})
} }
} }
} }
Expand Up @@ -22,6 +22,7 @@
import org.awaitility.classes.FakeRepositoryImpl; import org.awaitility.classes.FakeRepositoryImpl;
import org.awaitility.core.ConditionEvaluationLogger; import org.awaitility.core.ConditionEvaluationLogger;
import org.awaitility.core.ConditionTimeoutException; import org.awaitility.core.ConditionTimeoutException;
import org.awaitility.core.ThrowingRunnable;
import org.awaitility.support.CountDown; import org.awaitility.support.CountDown;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
Expand All @@ -33,7 +34,8 @@
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
import static org.awaitility.Awaitility.*; import static org.awaitility.Awaitility.await;
import static org.awaitility.Awaitility.with;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;


Expand Down Expand Up @@ -72,7 +74,7 @@ public void awaitUsingLambdaVersionOfCallableBoolean() {
@Test(timeout = 2000) @Test(timeout = 2000)
public void awaitAssertJAssertionAsAnonymousClass() { public void awaitAssertJAssertionAsAnonymousClass() {
new Asynch(fakeRepository).perform(); new Asynch(fakeRepository).perform();
await().untilAsserted(new Runnable() { await().untilAsserted(new ThrowingRunnable() {
@Override @Override
public void run() { public void run() {
Assertions.assertThat(fakeRepository.getValue()).isEqualTo(1); Assertions.assertThat(fakeRepository.getValue()).isEqualTo(1);
Expand Down Expand Up @@ -162,7 +164,7 @@ public void loggingIntermediaryHandlerLogsToSystemOut() {


@Test public void @Test public void
canMakeUseOfThrowingMethodInAwaitilityToWrapRunnablesThatThrowsExceptions() { canMakeUseOfThrowingMethodInAwaitilityToWrapRunnablesThatThrowsExceptions() {
await().untilAsserted(matches(() -> stringEquals("test", "test"))); await().untilAsserted(() -> stringEquals("test", "test"));
} }


@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
Expand Down
Expand Up @@ -17,6 +17,7 @@
package org.awaitility; package org.awaitility;


import org.awaitility.core.ConditionTimeoutException; import org.awaitility.core.ConditionTimeoutException;
import org.awaitility.core.ThrowingRunnable;
import org.awaitility.support.CountDown; import org.awaitility.support.CountDown;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
Expand All @@ -27,8 +28,8 @@
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;


import static org.awaitility.Awaitility.with;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.awaitility.Awaitility.with;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
Expand Down Expand Up @@ -130,7 +131,7 @@ public void expectedMatchMessageForAssertionConditionsWhenNotUsingLambdasWithout
} }
lastMatchMessage.set(condition.getDescription()); lastMatchMessage.set(condition.getDescription());
}) })
.untilAsserted(new Runnable() { .untilAsserted(new ThrowingRunnable() {
@Override @Override
public void run() { public void run() {
assertEquals(5, (int) countDown.get()); assertEquals(5, (int) countDown.get());
Expand All @@ -154,7 +155,7 @@ public void expectedMatchMessageForAssertionConditionsWhenNotUsingLambdasWithAli
} }
lastMatchMessage.set(condition.getDescription()); lastMatchMessage.set(condition.getDescription());
}).await("my alias") }).await("my alias")
.untilAsserted(new Runnable() { .untilAsserted(new ThrowingRunnable() {
@Override @Override
public void run() { public void run() {
assertEquals(5, (int) countDown.get()); assertEquals(5, (int) countDown.get());
Expand All @@ -178,7 +179,7 @@ public void expectedMismatchMessageForAssertionConditionsWhenNotUsingLambdasWith
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
}).await().atMost(150, MILLISECONDS).untilAsserted(new Runnable() { }).await().atMost(150, MILLISECONDS).untilAsserted(new ThrowingRunnable() {
@Override @Override
public void run() { public void run() {
assertEquals(-1, (int) countDown.get()); assertEquals(-1, (int) countDown.get());
Expand All @@ -205,7 +206,7 @@ public void expectedMismatchMessageForAssertionConditionsWhenNotUsingLambdasWith
throw new RuntimeException(e); throw new RuntimeException(e);
} }
}).await("my alias").atMost(150, MILLISECONDS) }).await("my alias").atMost(150, MILLISECONDS)
.untilAsserted(new Runnable() { .untilAsserted(new ThrowingRunnable() {
@Override @Override
public void run() { public void run() {
assertEquals(5, (int) countDown.get()); assertEquals(5, (int) countDown.get());
Expand Down
Expand Up @@ -17,6 +17,7 @@ package org.awaitility.scala


import java.util.concurrent.Callable import java.util.concurrent.Callable


import org.awaitility.core.ThrowingRunnable
import org.awaitility.spi.Timeout._ import org.awaitility.spi.Timeout._


trait AwaitilitySupport { trait AwaitilitySupport {
Expand All @@ -34,9 +35,9 @@ trait AwaitilitySupport {
} }
} }


implicit def byNameFunctionToRunnable[T](function: => T): Runnable = { implicit def byNameFunctionToRunnable[T](function: => T): ThrowingRunnable = {
new Runnable { new ThrowingRunnable {
def run(): Unit = function override def run(): Unit = function
} }
} }
} }
Expand Down
42 changes: 0 additions & 42 deletions awaitility/src/main/java/org/awaitility/Awaitility.java
Expand Up @@ -476,48 +476,6 @@ public static void setDefaultConditionEvaluationListener(ConditionEvaluationList
Awaitility.defaultConditionEvaluationListener = defaultConditionEvaluationListener; Awaitility.defaultConditionEvaluationListener = defaultConditionEvaluationListener;
} }


/**
* Await until a {@link ThrowingRunnable} supplier execution passes (ends without throwing an exception).
* <p>&nbsp;</p>
* This method is intended to benefit from lambda expressions introduced in Java 8. It allows to use standard AssertJ/FEST Assert assertions
* (by the way also standard JUnit/TestNG assertions) to test asynchronous calls and systems.
* It accepts {@link ThrowingRunnable} interface instead of plain {@link Runnable} to allow passing lambda expressions that throw exceptions
* in their bodies, e.g.:
* <pre>
* await().until(matches(() -> {
* methodThatHasThrowsInItsDeclaration();
* }));
* </pre>
* when using {@link Runnable} user would have to write something like:
* <pre>
* await().until(matches(() -> {
* try {
* methodThatHasThrowsInItsDeclaration();
* } catch(Exception e) {
* throw new RuntimeException(e);
* }
* }));
* </pre>
* <p>&nbsp;</p>
* {@link java.lang.AssertionError} instances thrown by the supplier are treated as an assertion failure and proper error message is propagated on timeout.
* Other exceptions are rethrown immediately as an execution errors.
*
* @param throwingRunnable the supplier that is responsible for executing the assertion and throwing AssertionError on failure.
* @throws org.awaitility.core.ConditionTimeoutException If condition was not fulfilled within the given time period.
* @since 1.7.0
*/
public static Runnable matches(final ThrowingRunnable throwingRunnable) {
return new Runnable() {
public void run() {
try {
throwingRunnable.run();
} catch (Throwable e) {
CheckedExceptionRethrower.safeRethrow(e);
}
}
};
}

/** /**
* Await until an instance field matches something. E.g. * Await until an instance field matches something. E.g.
* <p> * <p>
Expand Down
Expand Up @@ -42,7 +42,7 @@ public class AssertionCondition implements Condition<Void> {
* @param supplier a {@link java.lang.Runnable} object. * @param supplier a {@link java.lang.Runnable} object.
* @param settings a {@link org.awaitility.core.ConditionSettings} object. * @param settings a {@link org.awaitility.core.ConditionSettings} object.
*/ */
public AssertionCondition(final Runnable supplier, final ConditionSettings settings) { public AssertionCondition(final ThrowingRunnable supplier, final ConditionSettings settings) {
if (supplier == null) { if (supplier == null) {
throw new IllegalArgumentException("You must specify a supplier (was null)."); throw new IllegalArgumentException("You must specify a supplier (was null).");
} }
Expand All @@ -59,6 +59,8 @@ public ConditionEvaluationResult eval(Duration pollInterval) throws Exception {
lastExceptionMessage = e.getMessage(); lastExceptionMessage = e.getMessage();
conditionEvaluationHandler.handleConditionResultMismatch(getMismatchMessage(supplier, lastExceptionMessage, settings.getAlias()), null, pollInterval); conditionEvaluationHandler.handleConditionResultMismatch(getMismatchMessage(supplier, lastExceptionMessage, settings.getAlias()), null, pollInterval);
return new ConditionEvaluationResult(false, null, e); return new ConditionEvaluationResult(false, null, e);
} catch (Throwable throwable) {
return CheckedExceptionRethrower.safeRethrow(throwable);
} }
} }
}; };
Expand All @@ -70,19 +72,19 @@ protected String getTimeoutMessage() {
}; };
} }


private String getMatchMessage(Runnable supplier, String conditionAlias) { private String getMatchMessage(ThrowingRunnable supplier, String conditionAlias) {
return generateDescriptionPrefix(supplier, conditionAlias) + " reached its end value"; return generateDescriptionPrefix(supplier, conditionAlias) + " reached its end value";
} }


private String getMismatchMessage(Runnable supplier, String exceptionMessage, String conditionAlias) { private String getMismatchMessage(ThrowingRunnable supplier, String exceptionMessage, String conditionAlias) {
if (exceptionMessage != null && exceptionMessage.endsWith(".")) { if (exceptionMessage != null && exceptionMessage.endsWith(".")) {
// Remove the "." of the Hamcrest match description since Awaitility adds more // Remove the "." of the Hamcrest match description since Awaitility adds more
exceptionMessage = exceptionMessage.substring(0, exceptionMessage.length() - 1); exceptionMessage = exceptionMessage.substring(0, exceptionMessage.length() - 1);
} }
return generateDescriptionPrefix(supplier, conditionAlias) + " " + exceptionMessage; return generateDescriptionPrefix(supplier, conditionAlias) + " " + exceptionMessage;
} }


private String generateDescriptionPrefix(Runnable supplier, String conditionAlias) { private String generateDescriptionPrefix(ThrowingRunnable supplier, String conditionAlias) {
String methodDescription = generateMethodDescription(supplier); String methodDescription = generateMethodDescription(supplier);
boolean hasAlias = conditionAlias != null; boolean hasAlias = conditionAlias != null;
if (isLambdaClass(supplier.getClass())) { if (isLambdaClass(supplier.getClass())) {
Expand All @@ -97,7 +99,7 @@ private String generateDescriptionPrefix(Runnable supplier, String conditionAlia
return "Assertion condition" + (hasAlias ? " with alias " + conditionAlias : "") + methodDescription; return "Assertion condition" + (hasAlias ? " with alias " + conditionAlias : "") + methodDescription;
} }


private String generateMethodDescription(Runnable supplier) { private String generateMethodDescription(ThrowingRunnable supplier) {
String methodDescription = ""; String methodDescription = "";
Method enclosingMethod = null; Method enclosingMethod = null;
try { try {
Expand Down
Expand Up @@ -647,11 +647,9 @@ public <T> T until(final Callable<T> supplier, final Matcher<? super T> matcher)
* }); * });
* </pre> * </pre>
* <p>&nbsp;</p> * <p>&nbsp;</p>
* If your condition calls a method that throws a checked exception then please wrap it in {@link org.awaitility.Awaitility#matches(ThrowingRunnable)}.
* <p>&nbsp;</p>
* <b>NOTE:</b><br> * <b>NOTE:</b><br>
* Be <i>VERY</i> careful so that you're not using this method incorrectly in languages (like Kotlin and Groovy) that doesn't * Be <i>VERY</i> careful so that you're not using this method incorrectly in languages (like Kotlin and Groovy) that doesn't
* disambiguate between a {@link Runnable} that doesn't return anything (void) and {@link Callable} that returns a value. * disambiguate between a {@link ThrowingRunnable} that doesn't return anything (void) and {@link Callable} that returns a value.
* For example in Kotlin you can do like this: * For example in Kotlin you can do like this:
* <p>&nbsp;</p> * <p>&nbsp;</p>
* <pre> * <pre>
Expand All @@ -663,7 +661,7 @@ public <T> T until(final Callable<T> supplier, final Matcher<? super T> matcher)
* @throws org.awaitility.core.ConditionTimeoutException If condition was not fulfilled within the given time period. * @throws org.awaitility.core.ConditionTimeoutException If condition was not fulfilled within the given time period.
* @since 1.6.0 * @since 1.6.0
*/ */
public void untilAsserted(final Runnable assertion) { public void untilAsserted(final ThrowingRunnable assertion) {
until(new AssertionCondition(assertion, generateConditionSettings())); until(new AssertionCondition(assertion, generateConditionSettings()));
} }


Expand Down
3 changes: 3 additions & 0 deletions changelog.txt
Expand Up @@ -15,6 +15,9 @@ Changelog next version
(issue 85) (issue 85)
* Uncaught exceptions from other threads now takes "ignore exceptions" into account. For example if any thread throws IllegalArgumentException and this exception has been marked as ignored then it won't be treated as an error. * Uncaught exceptions from other threads now takes "ignore exceptions" into account. For example if any thread throws IllegalArgumentException and this exception has been marked as ignored then it won't be treated as an error.
Previously this was only applicable to exceptions that were thrown by the polling (condition evaluation) thread. Previously this was only applicable to exceptions that were thrown by the polling (condition evaluation) thread.
* Potential backward-incompatible change: The untilAsserted method no longer takes a "Runnable" as argument but rather a "ThrowingRunnable" which allows the runnable to throw checked exceptions.
This is backward incompatible only if you've supplied an explicit instance of Runnable to the function. If using Java 8 lambda expressions then nothing should change.
* Removed org.awaitility.Awaitility#matches method since it's no longer needed


Changelog 3.0.0-rc2 (2017-03-31) Changelog 3.0.0-rc2 (2017-03-31)
-------------------------------- --------------------------------
Expand Down

0 comments on commit d6a41fd

Please sign in to comment.