blob: ff67695d1b98ab4576bea20e2403e8eff8ca3a83 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
* file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
* to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.apache.tuweni.concurrent;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletionException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.jupiter.api.Test;
class DefaultCompletableAsyncCompletionTest {
@Test
void shouldReturnValueFromCompletedResult() {
AsyncCompletion completion = AsyncCompletion.completed();
assertThat(completion.isDone()).isTrue();
}
@Test
void shouldReturnExceptionFromExceptionallyCompletedResult() throws Exception {
Exception exception = new RuntimeException();
AsyncCompletion completion = AsyncCompletion.exceptional(exception);
assertThat(completion.isDone()).isTrue();
assertCompletedWithException(completion, exception);
}
@Test
void isNotDoneUntilCompleted() {
CompletableAsyncCompletion completion = AsyncCompletion.incomplete();
assertThat(completion.isDone()).isFalse();
completion.complete();
assertThat(completion.isDone()).isTrue();
}
@Test
void invokesContinuationFunctionWhenCompleted() throws Exception {
CompletableAsyncCompletion completion = AsyncCompletion.incomplete();
AsyncResult<String> asyncResult = completion.then(() -> AsyncResult.completed("Completed"));
assertThat(asyncResult.isDone()).isFalse();
completion.complete();
assertThat(asyncResult.isDone()).isTrue();
assertThat(asyncResult.get()).isEqualTo("Completed");
}
@Test
void completesExceptionallyWhenContinuationResultCompletesExceptionally() throws Exception {
Exception exception = new RuntimeException();
CompletableAsyncCompletion completion = AsyncCompletion.incomplete();
AsyncResult<String> asyncResult = completion.then(() -> AsyncResult.exceptional(exception));
assertThat(asyncResult.isDone()).isFalse();
completion.complete();
assertThat(asyncResult.isDone()).isTrue();
assertCompletedWithException(asyncResult, exception);
}
@Test
void completesExceptionallyWhenContinuationFunctionThrows() throws Exception {
RuntimeException exception = new RuntimeException();
CompletableAsyncCompletion completion = AsyncCompletion.incomplete();
AsyncResult<String> asyncResult = completion.then(() -> {
throw exception;
});
assertThat(asyncResult.isDone()).isFalse();
completion.complete();
assertThat(asyncResult.isDone()).isTrue();
assertCompletedWithException(asyncResult, exception);
}
@Test
void doesntInvokeContinuationFunctionIfCompletingExceptionally() throws Exception {
RuntimeException exception = new RuntimeException();
CompletableAsyncCompletion completion = AsyncCompletion.incomplete();
AsyncResult<String> asyncResult = completion.then(() -> {
fail("should not be invoked");
throw new RuntimeException();
});
assertThat(asyncResult.isDone()).isFalse();
completion.completeExceptionally(exception);
assertThat(asyncResult.isDone()).isTrue();
assertCompletedWithException(asyncResult, exception);
}
@Test
void completesWhenComposedCompletionCompletes() {
CompletableAsyncCompletion completion1 = AsyncCompletion.incomplete();
CompletableAsyncCompletion completion2 = AsyncCompletion.incomplete();
AtomicBoolean composed = new AtomicBoolean(false);
AsyncCompletion result = completion1.thenCompose(() -> {
composed.set(true);
return completion2;
});
assertThat(result.isDone()).isFalse();
assertThat(composed.get()).isFalse();
completion1.complete();
assertThat(result.isDone()).isFalse();
assertThat(composed.get()).isTrue();
completion2.complete();
assertThat(result.isDone()).isTrue();
}
@Test
void completesExceptionallyWhenComposedCompletionThrows() throws Exception {
CompletableAsyncCompletion completion1 = AsyncCompletion.incomplete();
RuntimeException exception = new RuntimeException();
AsyncCompletion result = completion1.thenCompose(() -> {
throw exception;
});
assertThat(result.isDone()).isFalse();
completion1.complete();
assertCompletedWithException(result, exception);
}
@Test
void completesExceptionallyWhenComposedCompletionCompletesExceptionally() throws Exception {
CompletableAsyncCompletion completion1 = AsyncCompletion.incomplete();
CompletableAsyncCompletion completion2 = AsyncCompletion.incomplete();
AtomicBoolean composed = new AtomicBoolean(false);
AsyncCompletion result = completion1.thenCompose(() -> {
composed.set(true);
return completion2;
});
assertThat(result.isDone()).isFalse();
assertThat(composed.get()).isFalse();
completion1.complete();
assertThat(result.isDone()).isFalse();
assertThat(composed.get()).isTrue();
RuntimeException exception = new RuntimeException();
completion2.completeExceptionally(exception);
assertThat(result.isDone()).isTrue();
assertCompletedWithException(result, exception);
}
@Test
void completesExceptionallyWhenComposerCompletesExceptionally() throws Exception {
CompletableAsyncCompletion completion1 = AsyncCompletion.incomplete();
CompletableAsyncCompletion completion2 = AsyncCompletion.incomplete();
AtomicBoolean composed = new AtomicBoolean(false);
AsyncCompletion result = completion1.thenCompose(() -> {
composed.set(true);
return completion2;
});
assertThat(result.isDone()).isFalse();
assertThat(composed.get()).isFalse();
RuntimeException exception = new RuntimeException();
completion1.completeExceptionally(exception);
assertThat(result.isDone()).isTrue();
assertThat(composed.get()).isFalse();
assertCompletedWithException(result, exception);
}
@Test
void completesWhenCombinedCompletionCompletes() {
CompletableAsyncCompletion completion1 = AsyncCompletion.incomplete();
CompletableAsyncCompletion completion2 = AsyncCompletion.incomplete();
AsyncCompletion result = completion1.thenCombine(completion2);
assertThat(result.isDone()).isFalse();
completion1.complete();
assertThat(result.isDone()).isFalse();
completion2.complete();
assertThat(result.isDone()).isTrue();
}
@Test
void completesExceptionallyWhenCombinedCompletionCompletesExceptionally() throws Exception {
CompletableAsyncCompletion completion1 = AsyncCompletion.incomplete();
CompletableAsyncCompletion completion2 = AsyncCompletion.incomplete();
AsyncCompletion result = completion1.thenCombine(completion2);
assertThat(result.isDone()).isFalse();
completion1.complete();
assertThat(result.isDone()).isFalse();
RuntimeException exception = new RuntimeException();
completion2.completeExceptionally(exception);
assertThat(result.isDone()).isTrue();
assertCompletedWithException(result, exception);
}
@Test
void completesExceptionallyWhenCombinerCompletesExceptionally() throws Exception {
CompletableAsyncCompletion completion1 = AsyncCompletion.incomplete();
CompletableAsyncCompletion completion2 = AsyncCompletion.incomplete();
AsyncCompletion result = completion1.thenCombine(completion2);
assertThat(result.isDone()).isFalse();
RuntimeException exception = new RuntimeException();
completion1.completeExceptionally(exception);
assertThat(result.isDone()).isTrue();
assertCompletedWithException(result, exception);
}
@Test
void completesWhenAllInCollectionComplete() {
CompletableAsyncCompletion completion1 = AsyncCompletion.incomplete();
CompletableAsyncCompletion completion2 = AsyncCompletion.incomplete();
Collection<AsyncCompletion> list = Arrays.asList(completion1, completion2);
AsyncCompletion completion = AsyncCompletion.allOf(list);
assertThat(completion.isDone()).isFalse();
completion1.complete();
assertThat(completion.isDone()).isFalse();
completion2.complete();
assertThat(completion.isDone()).isTrue();
}
@Test
void completesWithExceptionWhenAnyInCollectionFail() throws Exception {
CompletableAsyncCompletion completion1 = AsyncCompletion.incomplete();
CompletableAsyncCompletion completion2 = AsyncCompletion.incomplete();
AsyncCompletion completion = AsyncCompletion.allOf(completion1, completion2);
assertThat(completion.isDone()).isFalse();
Exception exception = new RuntimeException();
completion1.completeExceptionally(exception);
assertThat(completion.isDone()).isFalse();
completion2.complete();
assertThat(completion.isDone()).isTrue();
assertCompletedWithException(completion, exception);
}
@Test
void invokesComposedWhenCanceled() {
CompletableAsyncCompletion completion = AsyncCompletion.incomplete();
AtomicReference<Throwable> completedThrowable = new AtomicReference<>();
AsyncCompletion downstreamCompletion = completion.whenComplete(completedThrowable::set);
completion.cancel();
assertThat(completion.isDone()).isTrue();
assertThat(completion.isCancelled()).isTrue();
assertThat(completion.isCompletedExceptionally()).isTrue();
assertThat(downstreamCompletion.isDone()).isTrue();
assertThat(downstreamCompletion.isCancelled()).isFalse();
assertThat(downstreamCompletion.isCompletedExceptionally()).isTrue();
assertThat(completedThrowable.get()).isInstanceOf(CancellationException.class);
}
private void assertCompletedWithException(AsyncCompletion completion, Exception exception) throws Exception {
try {
completion.join();
fail("Expected exception not thrown");
} catch (CompletionException ex) {
assertThat(ex.getCause()).isSameAs(exception);
}
}
private void assertCompletedWithException(AsyncResult<?> asyncResult, Exception exception) throws Exception {
try {
asyncResult.get();
fail("Expected exception not thrown");
} catch (CompletionException ex) {
assertThat(ex.getCause()).isSameAs(exception);
}
}
}