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 super T> 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();