diff --git a/pom.xml b/pom.xml index 544cf0c..835e647 100644 --- a/pom.xml +++ b/pom.xml @@ -106,6 +106,12 @@ 1.3 true + + org.assertj + assertj-core + 3.26.3 + true + diff --git a/src/main/java/net/jodah/concurrentunit/Waiter.java b/src/main/java/net/jodah/concurrentunit/Waiter.java index f6bcc83..b4f32e9 100644 --- a/src/main/java/net/jodah/concurrentunit/Waiter.java +++ b/src/main/java/net/jodah/concurrentunit/Waiter.java @@ -18,6 +18,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; import net.jodah.concurrentunit.internal.ReentrantCircuit; @@ -31,6 +32,7 @@ public class Waiter { private AtomicInteger remainingResumes = new AtomicInteger(0); private final ReentrantCircuit circuit = new ReentrantCircuit(); private volatile Throwable failure; + private final ThreadLocal softAssertionsThreadLocal = ThreadLocal.withInitial(org.assertj.core.api.SoftAssertions::new); /** * Creates a new Waiter. @@ -105,6 +107,47 @@ public void assertThat(T actual, org.hamcrest.Matcher matcher) { } } + /** + * Asserts using AssertJ that {@code actual} satisfies the condition specified by {@code consumer} + * @param actual actual value to assert + * @param consumer consumer to provide assertions + * + * @throws AssertionError on main thread when the assertion fails + */ + public void assertThat(T actual, Consumer, T>> consumer) { + assertThat(() -> consumer.accept(org.assertj.core.api.Assertions.assertThat(actual))); + } + + /** + * add a soft assertion using AssertJ to Waiter object on this thread. Use {@code assertAndResume()} to verify assertions + * and resume Waiter. + * @param actual actual value to assert + * @return SoftAssertion + */ + public org.assertj.core.api.Assert, T> softlyAssertThat(T actual) { + return softAssertionsThreadLocal.get().assertThat(actual); + } + + /** + * Assert all AssertJ SoftAssertions + * @param softly soft assertions + */ + public void assertThat(org.assertj.core.api.SoftAssertions softly) { + assertThat(softly::assertAll); + } + + /** + * assert that this runnable is not throwing an AssertionError + * @param assertionRunnable runnable to be called + */ + public void assertThat(Runnable assertionRunnable) { + try { + assertionRunnable.run(); + } catch (AssertionError e) { + fail(e); + } + } + /** * Waits until {@link #resume()} is called, or the test is failed. * @@ -203,6 +246,16 @@ public synchronized void resume() { circuit.close(); } + /** + * Verifies existing SoftAssertions and resumes the waiter when the expected number of + * {@link #resume()} calls have occurred. + */ + public void assertAndResume() { + assertThat(softAssertionsThreadLocal.get()); + resume(); + softAssertionsThreadLocal.remove(); + } + /** * Fails the current test. * diff --git a/src/test/java/net/jodah/concurrentunit/WaiterTest.java b/src/test/java/net/jodah/concurrentunit/WaiterTest.java index 447848e..fed5aad 100644 --- a/src/test/java/net/jodah/concurrentunit/WaiterTest.java +++ b/src/test/java/net/jodah/concurrentunit/WaiterTest.java @@ -1,5 +1,6 @@ package net.jodah.concurrentunit; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; @@ -8,6 +9,7 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; +import org.assertj.core.api.SoftAssertions; import org.testng.annotations.Test; /** @@ -235,6 +237,53 @@ public void run() { w.await(1000); } + @Test(expectedExceptions = AssertionError.class) + public void shouldSupportAssertJ() throws Throwable { + final Waiter w = new Waiter(); + new Thread(new Runnable() { + public void run() { + w.assertThat(() -> assertThat("one").isEqualTo("two")); + } + }).start(); + w.await(1000); + } + + @Test(expectedExceptions = AssertionError.class) + public void shouldSupportAssertJFluently() throws Throwable { + final Waiter w = new Waiter(); + new Thread(new Runnable() { + public void run() { + w.assertThat("one", (assertion) -> assertion.isEqualTo("two")); + } + }).start(); + w.await(1000); + } + + @Test(expectedExceptions = AssertionError.class) + public void shouldSupportAssertJSoftAssertions() throws Throwable { + final Waiter w = new Waiter(); + new Thread(new Runnable() { + public void run() { + SoftAssertions assertions = new SoftAssertions(); + assertions.assertThat("one").isEqualTo("two"); + w.assertThat(assertions); + } + }).start(); + w.await(1000); + } + + @Test(expectedExceptions = AssertionError.class) + public void shouldSupportAssertJSoftAssertionsFluent() throws Throwable { + final Waiter w = new Waiter(); + new Thread(new Runnable() { + public void run() { + w.softlyAssertThat("one").isEqualTo("two"); + w.assertAndResume(); + } + }).start(); + w.await(1000); + } + public void shouldSupportSuccessiveResumeAwaits() throws Throwable { final Waiter w = new Waiter(); w.resume();