278 lines
10 KiB
Java
278 lines
10 KiB
Java
/*
|
|
* Copyright 2021 The Netty Project
|
|
*
|
|
* The Netty Project 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:
|
|
*
|
|
* https://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 io.netty.util.concurrent;
|
|
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
import java.util.concurrent.CountDownLatch;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
import static io.netty.util.concurrent.ImmediateEventExecutor.INSTANCE;
|
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
import static org.junit.jupiter.api.Assertions.assertSame;
|
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
|
|
class FuturesTest {
|
|
@Test
|
|
public void mapMustApplyMapperFunctionWhenFutureSucceeds() {
|
|
DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE);
|
|
Future<String> strFut = promise.map(i -> i.toString());
|
|
promise.setSuccess(42);
|
|
assertThat(strFut.getNow()).isEqualTo("42");
|
|
}
|
|
|
|
@Test
|
|
public void mapMustApplyMapperFunctionOnSucceededFuture() {
|
|
DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE);
|
|
promise.setSuccess(42);
|
|
assertThat(promise.map(i -> i.toString()).getNow()).isEqualTo("42");
|
|
}
|
|
|
|
@Test
|
|
public void mapOnFailedFutureMustProduceFailedFuture() {
|
|
DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE);
|
|
Exception cause = new Exception("boom");
|
|
promise.setFailure(cause);
|
|
assertThat(promise.map(i -> i.toString()).cause()).isSameAs(cause);
|
|
}
|
|
|
|
@Test
|
|
public void mapOnFailedFutureMustNotApplyMapperFunction() {
|
|
DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE);
|
|
Exception cause = new Exception("boom");
|
|
promise.setFailure(cause);
|
|
AtomicInteger counter = new AtomicInteger();
|
|
assertThat(promise.map(i -> {
|
|
counter.getAndIncrement();
|
|
return i.toString();
|
|
}).cause()).isSameAs(cause);
|
|
assertThat(counter.get()).isZero();
|
|
}
|
|
|
|
@Test
|
|
public void mapMustFailReturnedFutureWhenMapperFunctionThrows() {
|
|
DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE);
|
|
RuntimeException cause = new RuntimeException("boom");
|
|
Future<Object> future = promise.map(i -> {
|
|
throw cause;
|
|
});
|
|
promise.setSuccess(42);
|
|
assertThat(future.cause()).isSameAs(cause);
|
|
}
|
|
|
|
@Test
|
|
public void mapMustNotFailOriginalFutureWhenMapperFunctionThrows() {
|
|
DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE);
|
|
promise.map(i -> {
|
|
throw new RuntimeException("boom");
|
|
});
|
|
promise.setSuccess(42);
|
|
assertThat(promise.getNow()).isEqualTo(42);
|
|
}
|
|
|
|
@Test
|
|
public void cancelOnFutureFromMapMustCancelOriginalFuture() {
|
|
DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE);
|
|
Future<String> strFut = promise.map(i -> i.toString());
|
|
strFut.cancel(false);
|
|
assertTrue(promise.isCancelled());
|
|
assertTrue(strFut.isCancelled());
|
|
}
|
|
|
|
@Test
|
|
public void cancelOnOriginalFutureMustCancelFutureFromMap() {
|
|
DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE);
|
|
Future<String> strFut = promise.map(i -> i.toString());
|
|
promise.cancel(false);
|
|
assertTrue(promise.isCancelled());
|
|
assertTrue(strFut.isCancelled());
|
|
}
|
|
|
|
@Test
|
|
public void flatMapMustApplyMapperFunctionWhenFutureSucceeds() {
|
|
DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE);
|
|
Future<String> strFut = promise.flatMap(i -> INSTANCE.newSucceededFuture(i.toString()));
|
|
promise.setSuccess(42);
|
|
assertThat(strFut.getNow()).isEqualTo("42");
|
|
}
|
|
|
|
@Test
|
|
public void flatMapMustApplyMapperFunctionOnSucceededFuture() {
|
|
DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE);
|
|
promise.setSuccess(42);
|
|
assertThat(promise.flatMap(i -> INSTANCE.newSucceededFuture(i.toString())).getNow()).isEqualTo("42");
|
|
}
|
|
|
|
@Test
|
|
public void flatMapOnFailedFutureMustProduceFailedFuture() {
|
|
DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE);
|
|
Exception cause = new Exception("boom");
|
|
promise.setFailure(cause);
|
|
assertThat(promise.flatMap(i -> INSTANCE.newSucceededFuture(i.toString())).cause()).isSameAs(cause);
|
|
}
|
|
|
|
@Test
|
|
public void flatMapOnFailedFutureMustNotApplyMapperFunction() {
|
|
DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE);
|
|
Exception cause = new Exception("boom");
|
|
promise.setFailure(cause);
|
|
AtomicInteger counter = new AtomicInteger();
|
|
assertThat(promise.flatMap(i -> {
|
|
counter.getAndIncrement();
|
|
return INSTANCE.newSucceededFuture(i.toString());
|
|
}).cause()).isSameAs(cause);
|
|
assertThat(counter.get()).isZero();
|
|
}
|
|
|
|
@Test
|
|
public void flatMapMustFailReturnedFutureWhenMapperFunctionThrows() {
|
|
DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE);
|
|
RuntimeException cause = new RuntimeException("boom");
|
|
Future<Object> future = promise.flatMap(i -> {
|
|
throw cause;
|
|
});
|
|
promise.setSuccess(42);
|
|
assertThat(future.cause()).isSameAs(cause);
|
|
}
|
|
|
|
@Test
|
|
public void flatMapMustNotFailOriginalFutureWhenMapperFunctionThrows() {
|
|
DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE);
|
|
promise.flatMap(i -> {
|
|
throw new RuntimeException("boom");
|
|
});
|
|
promise.setSuccess(42);
|
|
assertThat(promise.getNow()).isEqualTo(42);
|
|
}
|
|
|
|
@Test
|
|
public void cancelOnFutureFromFlatMapMustCancelOriginalFuture() {
|
|
DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE);
|
|
Future<String> strFut = promise.flatMap(i -> INSTANCE.newSucceededFuture(i.toString()));
|
|
strFut.cancel(false);
|
|
assertTrue(promise.isCancelled());
|
|
assertTrue(strFut.isCancelled());
|
|
}
|
|
|
|
@Test
|
|
public void cancelOnOriginalFutureMustCancelFutureFromFlatMap() {
|
|
DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE);
|
|
Future<String> strFut = promise.flatMap(i -> INSTANCE.newSucceededFuture(i.toString()));
|
|
promise.cancel(false);
|
|
assertTrue(promise.isCancelled());
|
|
assertTrue(strFut.isCancelled());
|
|
}
|
|
|
|
@Test
|
|
public void cancelOnFutureFromFlatMapMapperMustCancelReturnedFuture() throws Exception {
|
|
DefaultPromise<Integer> promise = new DefaultPromise<>(INSTANCE);
|
|
Future<String> strFut = promise.flatMap(i -> {
|
|
Future<String> future = new DefaultPromise<>(INSTANCE);
|
|
future.cancel(false);
|
|
return future;
|
|
});
|
|
|
|
promise.setSuccess(42);
|
|
assertTrue(strFut.await(5, SECONDS));
|
|
assertTrue(strFut.isCancelled());
|
|
}
|
|
|
|
@Test
|
|
public void futureFromFlatMapMustNotCompleteUntilMappedFutureCompletes() throws Exception {
|
|
TestEventExecutor executor = new TestEventExecutor();
|
|
DefaultPromise<Integer> promise = new DefaultPromise<>(executor);
|
|
CountDownLatch mappingLatchEnter = new CountDownLatch(1);
|
|
CountDownLatch mappingLatchExit = new CountDownLatch(1);
|
|
Future<String> strFut = promise.flatMap(i -> {
|
|
return executor.submit(() -> {
|
|
mappingLatchEnter.countDown();
|
|
mappingLatchExit.await();
|
|
return i.toString();
|
|
});
|
|
});
|
|
|
|
executor.submit(() -> promise.setSuccess(42));
|
|
mappingLatchEnter.await();
|
|
assertFalse(strFut.await(100));
|
|
mappingLatchExit.countDown();
|
|
assertThat(strFut.get(5, SECONDS)).isEqualTo("42");
|
|
}
|
|
|
|
@Test
|
|
public void cascadeToNullPromise() {
|
|
TestEventExecutor executor = new TestEventExecutor();
|
|
DefaultPromise<Void> promise = new DefaultPromise<>(executor);
|
|
assertThrows(NullPointerException.class, () -> promise.cascadeTo(null));
|
|
}
|
|
|
|
@Test
|
|
public void cascadeToSuccess() throws Exception {
|
|
TestEventExecutor executor = new TestEventExecutor();
|
|
DefaultPromise<Integer> promise = new DefaultPromise<>(executor);
|
|
DefaultPromise<Integer> promise2 = new DefaultPromise<>(executor);
|
|
promise.cascadeTo(promise2);
|
|
promise.setSuccess(1);
|
|
assertTrue(promise.isSuccess());
|
|
assertThat(promise2.get(1, SECONDS)).isEqualTo(1);
|
|
}
|
|
|
|
@Test
|
|
public void cascadeToFailure() throws Exception {
|
|
TestEventExecutor executor = new TestEventExecutor();
|
|
DefaultPromise<Integer> promise = new DefaultPromise<>(executor);
|
|
DefaultPromise<Integer> promise2 = new DefaultPromise<>(executor);
|
|
promise.cascadeTo(promise2);
|
|
|
|
Exception ex = new Exception();
|
|
promise.setFailure(ex);
|
|
assertTrue(promise.isFailed());
|
|
assertTrue(promise2.await(1, SECONDS));
|
|
assertTrue(promise2.isFailed());
|
|
assertSame(promise.cause(), promise2.cause());
|
|
}
|
|
|
|
@Test
|
|
public void cascadeToCancel() throws Exception {
|
|
TestEventExecutor executor = new TestEventExecutor();
|
|
DefaultPromise<Integer> promise = new DefaultPromise<>(executor);
|
|
DefaultPromise<Integer> promise2 = new DefaultPromise<>(executor);
|
|
promise.cascadeTo(promise2);
|
|
|
|
assertTrue(promise.cancel(false));
|
|
assertTrue(promise.isCancelled());
|
|
assertTrue(promise2.await(1, SECONDS));
|
|
assertTrue(promise2.isCancelled());
|
|
}
|
|
|
|
@Test
|
|
public void cascadeToCancelSecond() throws Exception {
|
|
TestEventExecutor executor = new TestEventExecutor();
|
|
DefaultPromise<Integer> promise = new DefaultPromise<>(executor);
|
|
DefaultPromise<Integer> promise2 = new DefaultPromise<>(executor);
|
|
promise.cascadeTo(promise2);
|
|
|
|
assertTrue(promise2.cancel(false));
|
|
assertTrue(promise2.isCancelled());
|
|
|
|
//
|
|
assertTrue(promise.await(1, SECONDS));
|
|
assertTrue(promise2.isCancelled());
|
|
}
|
|
}
|