From 3004f0f729ded1a576d5714f6ee157725252710d Mon Sep 17 00:00:00 2001 From: huhaosumail <995483610@qq.com> Date: Fri, 20 Dec 2024 15:24:02 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(`CFU`):=20fix=20the=20wrong=20exception?= =?UTF-8?q?=20type=20of=20`catching*`=20methods=20=F0=9F=AB=B4=20#291?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `catching`方法未调用`unwrapCfException`导致异常类型判断错误 在调用 fallback 前正确解包异常 添加测试用例 --- .../cffu/CompletableFutureUtils.java | 12 ++--- .../cffu/CompletableFutureUtilsTest.java | 50 +++++++++++++++++++ 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/cffu-core/src/main/java/io/foldright/cffu/CompletableFutureUtils.java b/cffu-core/src/main/java/io/foldright/cffu/CompletableFutureUtils.java index a5602a00..2ee67a29 100644 --- a/cffu-core/src/main/java/io/foldright/cffu/CompletableFutureUtils.java +++ b/cffu-core/src/main/java/io/foldright/cffu/CompletableFutureUtils.java @@ -2768,7 +2768,7 @@ C catching(C cfThis, Class exceptionType, Function fa requireNonNull(exceptionType, "exceptionType is null"); requireNonNull(fallback, "fallback is null"); - return (C) cfThis.handle((v, ex) -> (ex == null || !exceptionType.isAssignableFrom(ex.getClass())) + return (C) cfThis.handle((v, ex) -> (ex == null || !exceptionType.isAssignableFrom(unwrapCfException(ex).getClass())) ? cfThis : completedFuture(fallback.apply((X) ex)) ).thenCompose(x -> x); } @@ -2812,8 +2812,8 @@ C catchingAsync(C cfThis, Class exceptionType, Function (ex == null || !exceptionType.isAssignableFrom(ex.getClass())) - ? cfThis : cfThis.handleAsync((v1, ex1) -> fallback.apply((X) ex1), executor) + return (C) cfThis.handle((v, ex) -> (ex == null || !exceptionType.isAssignableFrom(unwrapCfException(ex).getClass())) + ? cfThis : cfThis.handleAsync((v1, ex1) -> fallback.apply((X) unwrapCfException(ex1)), executor) ).thenCompose(x -> x); } @@ -3095,7 +3095,7 @@ C catchingCompose(C cfThis, Class exceptionType, Function (ex == null || !exceptionType.isAssignableFrom(ex.getClass())) + return (C) cfThis.handle((v, ex) -> (ex == null || !exceptionType.isAssignableFrom(unwrapCfException(ex).getClass())) ? cfThis : fallback.apply((X) ex) ).thenCompose(x -> x); } @@ -3139,8 +3139,8 @@ public static > C c requireNonNull(fallback, "fallback is null"); requireNonNull(executor, "executor is null"); - return (C) cfThis.handle((v, ex) -> (ex == null || !exceptionType.isAssignableFrom(ex.getClass())) - ? cfThis : cfThis.handleAsync((v1, ex1) -> fallback.apply((X) ex1), executor).thenCompose(x -> x) + return (C) cfThis.handle((v, ex) -> (ex == null || !exceptionType.isAssignableFrom(unwrapCfException(ex).getClass())) + ? cfThis : cfThis.handleAsync((v1, ex1) -> fallback.apply((X) unwrapCfException(ex1)), executor).thenCompose(x -> x) ).thenCompose(x -> x); } diff --git a/cffu-core/src/test/java/io/foldright/cffu/CompletableFutureUtilsTest.java b/cffu-core/src/test/java/io/foldright/cffu/CompletableFutureUtilsTest.java index 545bf779..74fc33d5 100644 --- a/cffu-core/src/test/java/io/foldright/cffu/CompletableFutureUtilsTest.java +++ b/cffu-core/src/test/java/io/foldright/cffu/CompletableFutureUtilsTest.java @@ -1415,6 +1415,31 @@ void test_catching() throws Exception { assertEquals(n, catchingAsync(success, IndexOutOfBoundsException.class, ex -> anotherN).get()); } + @Test + void test_completableFuture_catching() throws Exception { + CompletableFuture failed = CompletableFuture.supplyAsync(() -> { + throw rte; + }); + + assertEquals(n, catching(failed, RuntimeException.class, ex -> n).get()); + assertSame(rte, assertThrowsExactly(ExecutionException.class, () -> + catching(failed, IndexOutOfBoundsException.class, ex -> n).get() + ).getCause()); + + assertEquals(n, catchingAsync(failed, RuntimeException.class, ex -> n).get()); + assertSame(rte, assertThrowsExactly(ExecutionException.class, () -> + catchingAsync(failed, IndexOutOfBoundsException.class, ex -> n).get() + ).getCause()); + + CompletableFuture success = completedFuture(n); + + assertEquals(n, catching(success, RuntimeException.class, ex -> anotherN).get()); + assertEquals(n, catching(success, IndexOutOfBoundsException.class, ex -> anotherN).get()); + + assertEquals(n, catchingAsync(success, RuntimeException.class, ex -> anotherN).get()); + assertEquals(n, catchingAsync(success, IndexOutOfBoundsException.class, ex -> anotherN).get()); + } + // endregion //////////////////////////////////////////////////////////// // region## Timeout Control Methods of CompletableFuture @@ -1552,6 +1577,31 @@ void test_catchingCompose() throws Exception { assertEquals(n, catchingComposeAsync(success, IndexOutOfBoundsException.class, ex -> completedFuture(anotherN)).get()); } + @Test + void test_completableFuture_catchingCompose() throws Exception { + CompletableFuture failed = CompletableFuture.supplyAsync(() -> { + throw rte; + }); + + assertEquals(n, catchingCompose(failed, RuntimeException.class, ex -> completedFuture(n)).get()); + assertSame(rte, assertThrowsExactly(ExecutionException.class, () -> + catchingCompose(failed, IndexOutOfBoundsException.class, ex -> completedFuture(n)).get() + ).getCause()); + + assertEquals(n, catchingComposeAsync(failed, RuntimeException.class, ex -> completedFuture(n)).get()); + assertSame(rte, assertThrowsExactly(ExecutionException.class, () -> + catchingComposeAsync(failed, IndexOutOfBoundsException.class, ex -> completedFuture(n)).get() + ).getCause()); + + CompletableFuture success = completedFuture(n); + + assertEquals(n, catchingCompose(success, RuntimeException.class, ex -> completedFuture(anotherN)).get()); + assertEquals(n, catchingCompose(success, IndexOutOfBoundsException.class, ex -> completedFuture(anotherN)).get()); + + assertEquals(n, catchingComposeAsync(success, RuntimeException.class, ex -> completedFuture(anotherN)).get()); + assertEquals(n, catchingComposeAsync(success, IndexOutOfBoundsException.class, ex -> completedFuture(anotherN)).get()); + } + @Test void test_peek() throws Exception { BiConsumer c = (v, ex) -> { From e2cb8b506a64b3455d8c9bf7caea69b5a3ee2909 Mon Sep 17 00:00:00 2001 From: Jerry Lee Date: Sun, 22 Dec 2024 18:44:29 +0800 Subject: [PATCH 2/2] =?UTF-8?q?refactor(`catching`):=20refactor=20impl;=20?= =?UTF-8?q?improve=20their=20javadoc/test=20cases=20=F0=9F=AB=B4=20#291?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/io/foldright/cffu/Cffu.java | 81 ++++++------ .../cffu/CompletableFutureUtils.java | 120 +++++++++++------- .../cffu/CompletableFutureUtilsTest.java | 101 +++++++-------- 3 files changed, 161 insertions(+), 141 deletions(-) diff --git a/cffu-core/src/main/java/io/foldright/cffu/Cffu.java b/cffu-core/src/main/java/io/foldright/cffu/Cffu.java index 23de5772..c9abdebe 100644 --- a/cffu-core/src/main/java/io/foldright/cffu/Cffu.java +++ b/cffu-core/src/main/java/io/foldright/cffu/Cffu.java @@ -1511,15 +1511,17 @@ public Cffu runAfterEitherAsync(CompletionStage other, Runnable action, //////////////////////////////////////////////////////////////////////////////// /** - * Returns a new Cffu that, when given stage completes exceptionally with the given exceptionType, - * is executed with given stage's exception as the argument to the supplied function. - * Otherwise, the returned stage contains same result as input Cffu. + * Returns a new Cffu that, when this Cffu completes exceptionally with the given exceptionType, + * is executed with the exception from this Cffu as the argument to the supplied function. + * Otherwise, the returned Cffu contains same result as this Cffu. * * @param exceptionType the exception type that triggers use of {@code fallback}. The exception type is matched - * against the input's exception. To avoid hiding bugs and other unrecoverable errors, + * against the exception from this Cffu. "the exception from this Cffu" means + * the cause of the {@link ExecutionException} thrown by {@code get()} or, if {@code get()} throws a + * different kind of exception, that exception itself. To avoid hiding bugs and other unrecoverable errors, * callers should prefer more specific types, avoiding {@code Throwable.class} in particular. - * @param fallback the Function to be called if {@code input} fails with the expected exception type. - * The function's argument is the input's exception. + * @param fallback the Function to be called if this Cffu fails with the expected exception type. + * The function's argument is the exception from this Cffu. * @see Futures#catching the equivalent Guava method catching() */ public Cffu catching(Class exceptionType, Function fallback) { @@ -1527,16 +1529,17 @@ public Cffu catching(Class exceptionType, Function"the exception from this Cffu" means + * the cause of the {@link ExecutionException} thrown by {@code get()} or, if {@code get()} throws a + * different kind of exception, that exception itself. To avoid hiding bugs and other unrecoverable errors, * callers should prefer more specific types, avoiding {@code Throwable.class} in particular. - * @param fallback the Function to be called if {@code input} fails with the expected exception type. - * The function's argument is the input's exception. + * @param fallback the Function to be called if this Cffu fails with the expected exception type. + * The function's argument is the exception from this Cffu. * @see Futures#catching the equivalent Guava method catching() */ public Cffu catchingAsync( @@ -1545,15 +1548,17 @@ public Cffu catchingAsync( } /** - * Returns a new Cffu that, when given stage completes exceptionally with the given exceptionType, - * is executed with given stage's exception as the argument to the supplied function, using the supplied Executor. - * Otherwise, the returned stage contains same result as input Cffu. + * Returns a new Cffu that, when this Cffu completes exceptionally with the given exceptionType, + * is executed with the exception from this Cffu as the argument to the supplied function, using the supplied Executor. + * Otherwise, the returned Cffu contains same result as this Cffu. * * @param exceptionType the exception type that triggers use of {@code fallback}. The exception type is matched - * against the input's exception. To avoid hiding bugs and other unrecoverable errors, + * against the exception from this Cffu. "the exception from this Cffu" means + * the cause of the {@link ExecutionException} thrown by {@code get()} or, if {@code get()} throws a + * different kind of exception, that exception itself. To avoid hiding bugs and other unrecoverable errors, * callers should prefer more specific types, avoiding {@code Throwable.class} in particular. - * @param fallback the Function to be called if {@code input} fails with the expected exception type. - * The function's argument is the input's exception. + * @param fallback the Function to be called if this Cffu fails with the expected exception type. + * The function's argument is the exception from this Cffu. * @param executor the executor to use for asynchronous execution * @see Futures#catching the equivalent Guava method catching() */ @@ -1854,14 +1859,16 @@ public Cffu thenComposeAsync(Function"the exception from this Cffu" means + * the cause of the {@link ExecutionException} thrown by {@code get()} or, if {@code get()} throws a + * different kind of exception, that exception itself. To avoid hiding bugs and other unrecoverable errors, * callers should prefer more specific types, avoiding {@code Throwable.class} in particular. - * @param fallback the Function to be called if {@code input} fails with the expected exception type. - * The function's argument is the input's exception. + * @param fallback the Function to be called if this Cffu fails with the expected exception type. + * The function's argument is the exception from this Cffu. * @see Futures#catchingAsync the equivalent Guava method catchingAsync() */ public Cffu catchingCompose( @@ -1870,15 +1877,16 @@ public Cffu catchingCompose( } /** - * Returns a new Cffu that, when given stage completes exceptionally with the given exceptionType, - * is composed using the results of the supplied function applied to given stage's exception, - * using given stage's default asynchronous execution facility. + * Returns a new Cffu that, when this Cffu completes exceptionally with the given exceptionType, + * is composed using the results of the supplied function applied to the exception from this Cffu using {@link #defaultExecutor()}. * * @param exceptionType the exception type that triggers use of {@code fallback}. The exception type is matched - * against the input's exception. To avoid hiding bugs and other unrecoverable errors, + * against the exception from this Cffu. "the exception from this Cffu" means + * the cause of the {@link ExecutionException} thrown by {@code get()} or, if {@code get()} throws a + * different kind of exception, that exception itself. To avoid hiding bugs and other unrecoverable errors, * callers should prefer more specific types, avoiding {@code Throwable.class} in particular. - * @param fallback the Function to be called if {@code input} fails with the expected exception type. - * The function's argument is the input's exception. + * @param fallback the Function to be called if this Cffu fails with the expected exception type. + * The function's argument is the exception from this Cffu. * @see Futures#catchingAsync the equivalent Guava method catchingAsync() */ public Cffu catchingComposeAsync( @@ -1887,15 +1895,16 @@ public Cffu catchingComposeAsync( } /** - * Returns a new Cffu that, when given stage completes exceptionally with the given exceptionType, - * is composed using the results of the supplied function applied to given's exception, - * using the supplied Executor. + * Returns a new Cffu that, when this Cffu completes exceptionally with the given exceptionType, + * is composed using the results of the supplied function applied to the exception from this Cffu, using the supplied Executor. * * @param exceptionType the exception type that triggers use of {@code fallback}. The exception type is matched - * against the input's exception. To avoid hiding bugs and other unrecoverable errors, + * against the exception from this Cffu. "the exception from this Cffu" means + * the cause of the {@link ExecutionException} thrown by {@code get()} or, if {@code get()} throws a + * different kind of exception, that exception itself. To avoid hiding bugs and other unrecoverable errors, * callers should prefer more specific types, avoiding {@code Throwable.class} in particular. - * @param fallback the Function to be called if {@code input} fails with the expected exception type. - * The function's argument is the input's exception. + * @param fallback the Function to be called if this Cffu fails with the expected exception type. + * The function's argument is the exception from this Cffu. * @param executor the executor to use for asynchronous execution * @see Futures#catchingAsync the equivalent Guava method catchingAsync() */ diff --git a/cffu-core/src/main/java/io/foldright/cffu/CompletableFutureUtils.java b/cffu-core/src/main/java/io/foldright/cffu/CompletableFutureUtils.java index 2ee67a29..6d4f891d 100644 --- a/cffu-core/src/main/java/io/foldright/cffu/CompletableFutureUtils.java +++ b/cffu-core/src/main/java/io/foldright/cffu/CompletableFutureUtils.java @@ -2751,14 +2751,16 @@ public static CompletableFuture runAfterEitherSuccessAsync( /** * Returns a new CompletableFuture that, when given stage completes exceptionally with the given exceptionType, - * is executed with given stage's exception as the argument to the supplied function. - * Otherwise, the returned stage contains same result as input CompletionStage. + * is executed with the exception from the given stage as the argument to the supplied function. + * Otherwise, the returned stage contains same result as the given stage. * - * @param exceptionType the exception type that triggers use of {@code fallback}. The exception type is matched - * against the input's exception. To avoid hiding bugs and other unrecoverable errors, + * @param exceptionType the exception type that triggers use of {@code fallback}. The exception type is matched against + * the exception from argument cfThis. "the exception from cfThis" means the cause of + * the {@link ExecutionException} thrown by {@code get()} or, if {@code get()} throws a different kind + * of exception, that exception itself. To avoid hiding bugs and other unrecoverable errors, * callers should prefer more specific types, avoiding {@code Throwable.class} in particular. - * @param fallback the Function to be called if {@code input} fails with the expected exception type. - * The function's argument is the input's exception. + * @param fallback the Function to be called if cfThis fails with the expected exception type. + * The function's argument is the exception from cfThis. * @see Futures#catching the equivalent Guava method catching() */ @SuppressWarnings("unchecked") @@ -2768,22 +2770,27 @@ C catching(C cfThis, Class exceptionType, Function fa requireNonNull(exceptionType, "exceptionType is null"); requireNonNull(fallback, "fallback is null"); - return (C) cfThis.handle((v, ex) -> (ex == null || !exceptionType.isAssignableFrom(unwrapCfException(ex).getClass())) - ? cfThis : completedFuture(fallback.apply((X) ex)) - ).thenCompose(x -> x); + return (C) cfThis.handle((v, ex) -> { + if (ex == null) return cfThis; + Throwable unwrap = unwrapCfException(ex); + if (!exceptionType.isAssignableFrom(unwrap.getClass())) return cfThis; + return completedFuture(fallback.apply((X) unwrap)); + }).thenCompose(x -> x); } /** * Returns a new CompletableFuture that, when given stage completes exceptionally with the given exceptionType, - * is executed with given stage's exception as the argument to the supplied function, - * using the default executor of parameter cfThis. - * Otherwise, the returned stage contains same result as input CompletionStage. - * - * @param exceptionType the exception type that triggers use of {@code fallback}. The exception type is matched - * against the input's exception. To avoid hiding bugs and other unrecoverable errors, + * is executed with the exception from the given stage as the argument to the supplied function, + * using the default executor of parameter the given stage. + * Otherwise, the returned stage contains same result as the given stage. + * + * @param exceptionType the exception type that triggers use of {@code fallback}. The exception type is matched against + * the exception from argument cfThis. "the exception from cfThis" means the cause of + * the {@link ExecutionException} thrown by {@code get()} or, if {@code get()} throws a different kind + * of exception, that exception itself. To avoid hiding bugs and other unrecoverable errors, * callers should prefer more specific types, avoiding {@code Throwable.class} in particular. - * @param fallback the Function to be called if {@code input} fails with the expected exception type. - * The function's argument is the input's exception. + * @param fallback the Function to be called if cfThis fails with the expected exception type. + * The function's argument is the exception from cfThis. * @see Futures#catching the equivalent Guava method catching() */ public static > @@ -2793,14 +2800,16 @@ C catchingAsync(C cfThis, Class exceptionType, Function"the exception from cfThis" means the cause of + * the {@link ExecutionException} thrown by {@code get()} or, if {@code get()} throws a different kind + * of exception, that exception itself. To avoid hiding bugs and other unrecoverable errors, * callers should prefer more specific types, avoiding {@code Throwable.class} in particular. - * @param fallback the Function to be called if {@code input} fails with the expected exception type. - * The function's argument is the input's exception. + * @param fallback the Function to be called if cfThis fails with the expected exception type. + * The function's argument is the exception from cfThis. * @param executor the executor to use for asynchronous execution * @see Futures#catching the equivalent Guava method catching() */ @@ -2812,9 +2821,12 @@ C catchingAsync(C cfThis, Class exceptionType, Function (ex == null || !exceptionType.isAssignableFrom(unwrapCfException(ex).getClass())) - ? cfThis : cfThis.handleAsync((v1, ex1) -> fallback.apply((X) unwrapCfException(ex1)), executor) - ).thenCompose(x -> x); + return (C) cfThis.handle((v, ex) -> { + if (ex == null) return cfThis; + Throwable unwrap = unwrapCfException(ex); + if (!exceptionType.isAssignableFrom(unwrap.getClass())) return cfThis; + return cfThis.handleAsync((v1, ex1) -> fallback.apply((X) unwrap), executor); + }).thenCompose(x -> x); } /** @@ -3079,13 +3091,15 @@ private static > C hopExecutorIfAtCfDelayerThread /** * Returns a new CompletionStage that, when given stage completes exceptionally with the given exceptionType, - * is composed using the results of the supplied function applied to given stage's exception. + * is composed using the results of the supplied function applied to the exception from the given stage. * - * @param exceptionType the exception type that triggers use of {@code fallback}. The exception type is matched - * against the input's exception. To avoid hiding bugs and other unrecoverable errors, + * @param exceptionType the exception type that triggers use of {@code fallback}. The exception type is matched against + * the exception from argument cfThis. "the exception from cfThis" means the cause of + * the {@link ExecutionException} thrown by {@code get()} or, if {@code get()} throws a different kind + * of exception, that exception itself. To avoid hiding bugs and other unrecoverable errors, * callers should prefer more specific types, avoiding {@code Throwable.class} in particular. - * @param fallback the Function to be called if {@code input} fails with the expected exception type. - * The function's argument is the input's exception. + * @param fallback the Function to be called if cfThis fails with the expected exception type. + * The function's argument is the exception from cfThis. * @see Futures#catchingAsync the equivalent Guava method catchingAsync() */ @SuppressWarnings("unchecked") @@ -3095,21 +3109,26 @@ C catchingCompose(C cfThis, Class exceptionType, Function (ex == null || !exceptionType.isAssignableFrom(unwrapCfException(ex).getClass())) - ? cfThis : fallback.apply((X) ex) - ).thenCompose(x -> x); + return (C) cfThis.handle((v, ex) -> { + if (ex == null) return cfThis; + Throwable unwrap = unwrapCfException(ex); + if (!exceptionType.isAssignableFrom(unwrap.getClass())) return cfThis; + return fallback.apply((X) unwrap); + }).thenCompose(x -> x); } /** * Returns a new CompletionStage that, when given stage completes exceptionally with the given exceptionType, - * is composed using the results of the supplied function applied to given stage's exception, - * using the default executor of parameter cfThis. + * is composed using the results of the supplied function applied to the exception from the given stage, + * using the default executor of parameter the given stage. * - * @param exceptionType the exception type that triggers use of {@code fallback}. The exception type is matched - * against the input's exception. To avoid hiding bugs and other unrecoverable errors, + * @param exceptionType the exception type that triggers use of {@code fallback}. The exception type is matched against + * the exception from argument cfThis. "the exception from cfThis" means the cause of + * the {@link ExecutionException} thrown by {@code get()} or, if {@code get()} throws a different kind + * of exception, that exception itself. To avoid hiding bugs and other unrecoverable errors, * callers should prefer more specific types, avoiding {@code Throwable.class} in particular. - * @param fallback the Function to be called if {@code input} fails with the expected exception type. - * The function's argument is the input's exception. + * @param fallback the Function to be called if cfThis fails with the expected exception type. + * The function's argument is the exception from cfThis. * @see Futures#catchingAsync the equivalent Guava method catchingAsync() */ public static > C catchingComposeAsync( @@ -3119,14 +3138,16 @@ public static > C c /** * Returns a new CompletionStage that, when given stage completes exceptionally with the given exceptionType, - * is composed using the results of the supplied function applied to given's exception, + * is composed using the results of the supplied function applied to the exception from the given stage, * using the supplied Executor. * - * @param exceptionType the exception type that triggers use of {@code fallback}. The exception type is matched - * against the input's exception. To avoid hiding bugs and other unrecoverable errors, + * @param exceptionType the exception type that triggers use of {@code fallback}. The exception type is matched against + * the exception from argument cfThis. "the exception from cfThis" means the cause of + * the {@link ExecutionException} thrown by {@code get()} or, if {@code get()} throws a different kind + * of exception, that exception itself. To avoid hiding bugs and other unrecoverable errors, * callers should prefer more specific types, avoiding {@code Throwable.class} in particular. - * @param fallback the Function to be called if {@code input} fails with the expected exception type. - * The function's argument is the input's exception. + * @param fallback the Function to be called if cfThis fails with the expected exception type. + * The function's argument is the exception from cfThis. * @param executor the executor to use for asynchronous execution * @see Futures#catchingAsync the equivalent Guava method catchingAsync() */ @@ -3139,9 +3160,12 @@ public static > C c requireNonNull(fallback, "fallback is null"); requireNonNull(executor, "executor is null"); - return (C) cfThis.handle((v, ex) -> (ex == null || !exceptionType.isAssignableFrom(unwrapCfException(ex).getClass())) - ? cfThis : cfThis.handleAsync((v1, ex1) -> fallback.apply((X) unwrapCfException(ex1)), executor).thenCompose(x -> x) - ).thenCompose(x -> x); + return (C) cfThis.handle((v, ex) -> { + if (ex == null) return cfThis; + Throwable unwrap = unwrapCfException(ex); + if (!exceptionType.isAssignableFrom(unwrap.getClass())) return cfThis; + return cfThis.handleAsync((v1, ex1) -> fallback.apply((X) unwrap), executor).thenCompose(x -> x); + }).thenCompose(x -> x); } /** diff --git a/cffu-core/src/test/java/io/foldright/cffu/CompletableFutureUtilsTest.java b/cffu-core/src/test/java/io/foldright/cffu/CompletableFutureUtilsTest.java index 74fc33d5..fb8f1647 100644 --- a/cffu-core/src/test/java/io/foldright/cffu/CompletableFutureUtilsTest.java +++ b/cffu-core/src/test/java/io/foldright/cffu/CompletableFutureUtilsTest.java @@ -1394,17 +1394,12 @@ void test_exceptionallyAsync() throws Exception { @Test void test_catching() throws Exception { - CompletableFuture failed = failedFuture(rte); - - assertEquals(n, catching(failed, RuntimeException.class, ex -> n).get()); - assertSame(rte, assertThrowsExactly(ExecutionException.class, () -> - catching(failed, IndexOutOfBoundsException.class, ex -> n).get() - ).getCause()); + test_catching_failedCf(failedFuture(rte)); + test_catching_failedCf(CompletableFuture.supplyAsync(() -> { + throw rte; + })); + test_catching_failedCf(CompletableFutureUtils.failedFuture(rte).thenApply(x -> x)); - assertEquals(n, catchingAsync(failed, RuntimeException.class, ex -> n).get()); - assertSame(rte, assertThrowsExactly(ExecutionException.class, () -> - catchingAsync(failed, IndexOutOfBoundsException.class, ex -> n).get() - ).getCause()); CompletableFuture success = completedFuture(n); @@ -1415,29 +1410,28 @@ void test_catching() throws Exception { assertEquals(n, catchingAsync(success, IndexOutOfBoundsException.class, ex -> anotherN).get()); } - @Test - void test_completableFuture_catching() throws Exception { - CompletableFuture failed = CompletableFuture.supplyAsync(() -> { - throw rte; - }); - - assertEquals(n, catching(failed, RuntimeException.class, ex -> n).get()); + private static void test_catching_failedCf(CompletableFuture failed) throws Exception { + assertEquals(n, catching(failed, RuntimeException.class, ex -> { + assertSame(rte, ex); + return n; + }).get()); assertSame(rte, assertThrowsExactly(ExecutionException.class, () -> - catching(failed, IndexOutOfBoundsException.class, ex -> n).get() + catching(failed, IndexOutOfBoundsException.class, ex -> { + assertSame(rte, ex); + return n; + }).get() ).getCause()); - assertEquals(n, catchingAsync(failed, RuntimeException.class, ex -> n).get()); + assertEquals(n, catchingAsync(failed, RuntimeException.class, ex -> { + assertSame(rte, ex); + return n; + }).get()); assertSame(rte, assertThrowsExactly(ExecutionException.class, () -> - catchingAsync(failed, IndexOutOfBoundsException.class, ex -> n).get() + catchingAsync(failed, IndexOutOfBoundsException.class, ex -> { + assertSame(rte, ex); + return n; + }).get() ).getCause()); - - CompletableFuture success = completedFuture(n); - - assertEquals(n, catching(success, RuntimeException.class, ex -> anotherN).get()); - assertEquals(n, catching(success, IndexOutOfBoundsException.class, ex -> anotherN).get()); - - assertEquals(n, catchingAsync(success, RuntimeException.class, ex -> anotherN).get()); - assertEquals(n, catchingAsync(success, IndexOutOfBoundsException.class, ex -> anotherN).get()); } // endregion @@ -1556,17 +1550,11 @@ void test_exceptionallyCompose() throws Exception { @Test void test_catchingCompose() throws Exception { - CompletableFuture failed = failedFuture(rte); - - assertEquals(n, catchingCompose(failed, RuntimeException.class, ex -> completedFuture(n)).get()); - assertSame(rte, assertThrowsExactly(ExecutionException.class, () -> - catchingCompose(failed, IndexOutOfBoundsException.class, ex -> completedFuture(n)).get() - ).getCause()); - - assertEquals(n, catchingComposeAsync(failed, RuntimeException.class, ex -> completedFuture(n)).get()); - assertSame(rte, assertThrowsExactly(ExecutionException.class, () -> - catchingComposeAsync(failed, IndexOutOfBoundsException.class, ex -> completedFuture(n)).get() - ).getCause()); + test_catchingCompose_failedCf(failedFuture(rte)); + test_catchingCompose_failedCf(CompletableFuture.supplyAsync(() -> { + throw rte; + })); + test_catchingCompose_failedCf(CompletableFutureUtils.failedFuture(rte).thenApply(x -> x)); CompletableFuture success = completedFuture(n); @@ -1577,29 +1565,28 @@ void test_catchingCompose() throws Exception { assertEquals(n, catchingComposeAsync(success, IndexOutOfBoundsException.class, ex -> completedFuture(anotherN)).get()); } - @Test - void test_completableFuture_catchingCompose() throws Exception { - CompletableFuture failed = CompletableFuture.supplyAsync(() -> { - throw rte; - }); - - assertEquals(n, catchingCompose(failed, RuntimeException.class, ex -> completedFuture(n)).get()); + private static void test_catchingCompose_failedCf(CompletableFuture failed) throws Exception { + assertEquals(n, catchingCompose(failed, RuntimeException.class, ex -> { + assertSame(rte, ex); + return completedFuture(n); + }).get()); assertSame(rte, assertThrowsExactly(ExecutionException.class, () -> - catchingCompose(failed, IndexOutOfBoundsException.class, ex -> completedFuture(n)).get() + catchingCompose(failed, IndexOutOfBoundsException.class, ex -> { + assertSame(rte, ex); + return completedFuture(n); + }).get() ).getCause()); - assertEquals(n, catchingComposeAsync(failed, RuntimeException.class, ex -> completedFuture(n)).get()); + assertEquals(n, catchingComposeAsync(failed, RuntimeException.class, ex -> { + assertSame(rte, ex); + return completedFuture(n); + }).get()); assertSame(rte, assertThrowsExactly(ExecutionException.class, () -> - catchingComposeAsync(failed, IndexOutOfBoundsException.class, ex -> completedFuture(n)).get() + catchingComposeAsync(failed, IndexOutOfBoundsException.class, ex -> { + assertSame(rte, ex); + return completedFuture(n); + }).get() ).getCause()); - - CompletableFuture success = completedFuture(n); - - assertEquals(n, catchingCompose(success, RuntimeException.class, ex -> completedFuture(anotherN)).get()); - assertEquals(n, catchingCompose(success, IndexOutOfBoundsException.class, ex -> completedFuture(anotherN)).get()); - - assertEquals(n, catchingComposeAsync(success, RuntimeException.class, ex -> completedFuture(anotherN)).get()); - assertEquals(n, catchingComposeAsync(success, IndexOutOfBoundsException.class, ex -> completedFuture(anotherN)).get()); } @Test