Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@
<version>1.3</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.26.3</version>
<optional>true</optional>
</dependency>
</dependencies>

<profiles>
Expand Down
53 changes: 53 additions & 0 deletions src/main/java/net/jodah/concurrentunit/Waiter.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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<org.assertj.core.api.SoftAssertions> softAssertionsThreadLocal = ThreadLocal.withInitial(org.assertj.core.api.SoftAssertions::new);

/**
* Creates a new Waiter.
Expand Down Expand Up @@ -105,6 +107,47 @@ public <T> 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 <T> void assertThat(T actual, Consumer<org.assertj.core.api.Assert<org.assertj.core.api.ObjectAssert<T>, 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 <T> org.assertj.core.api.Assert<org.assertj.core.api.ObjectAssert<T>, 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.
*
Expand Down Expand Up @@ -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.
*
Expand Down
49 changes: 49 additions & 0 deletions src/test/java/net/jodah/concurrentunit/WaiterTest.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;

/**
Expand Down Expand Up @@ -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();
Expand Down