diff --git a/documentation/README.md b/documentation/README.md index b27aba3f4..6044c5baa 100644 --- a/documentation/README.md +++ b/documentation/README.md @@ -95,14 +95,14 @@ public class RepetitionsTest { Multi multi = Multi.createFrom().items(1, 1, 2, 3, 4, 5, 5, 6, 1, 4, 4); // tag::distinct[] List list = multi - .transform().byDroppingDuplicates() + .select().distinct() .collect().asList() .await().indefinitely(); // end::distinct[] // tag::repetition[] List list2 = multi - .transform().byDroppingRepetitions() + .skip().repetitions() .collect().asList() .await().indefinitely(); // end::repetition[] diff --git a/documentation/src/main/jekyll/guides/filter.adoc b/documentation/src/main/jekyll/guides/filter.adoc index 96c35dab5..98a409566 100644 --- a/documentation/src/main/jekyll/guides/filter.adoc +++ b/documentation/src/main/jekyll/guides/filter.adoc @@ -5,26 +5,27 @@ :include_dir: ../../../../src/test/java/guides/operators When observing a `Multi`, you may want to not forward all the received items to your downstream. -To _filter_ or _select_ items, you can use `multi.transform().byFilteringItemsWith(predicate)`: +Use the `multi.select()` group to select items. +To _select_ items passing a given predicate, use `multi.select().where(predicate)`: [source,java,indent=0] ---- include::{include_dir}/FilterTest.java[tag=filter] ---- -`byFilteringItemsWith` accepts a predicate called for each item. +`where` accepts a predicate called for each item. If the predicate returns `true`, the item propagated downstream. Otherwise, it drops the item. -The predicated passed to `byFilteringItemsWith` is synchronous. -The `byTestingItemsWith` method provides an asynchronous version: +The predicated passed to `where` is synchronous. +The `when` method provides an asynchronous version: [source,java,indent=0] ---- include::{include_dir}/FilterTest.java[tag=test] ---- -`byFilteringItemsWith` accepts a function called for each item. -Unlike `byFilteringItemsWith` where the predicate returns a boolean synchronously, the function returns a `Uni`. +`when` accepts a function called for each item. +Unlike `when` where the predicate returns a boolean synchronously, the function returns a `Uni`. It forwards the item downstream if the `uni` produced by the function emits `true`. Otherwise, it drops the item. diff --git a/documentation/src/main/jekyll/guides/repetitions.adoc b/documentation/src/main/jekyll/guides/repetitions.adoc index 9b602546d..b9b798eb9 100644 --- a/documentation/src/main/jekyll/guides/repetitions.adoc +++ b/documentation/src/main/jekyll/guides/repetitions.adoc @@ -5,11 +5,11 @@ :include_dir: ../../../../src/test/java/guides/operators When observing a `Multi`, you may see duplicated items or repetitions. -Mutiny has operators to removes these duplicates. +The `multi.select()` and `multi.skip()` groups provide methods to only select distinct items or drop repetitions. -== Removing duplicates +== Selecting distinct -The `.transform().byDroppingDuplicates()` operator removes all the duplicates. +The `.select().distinct()` operator removes all the duplicates. As a result, the downstream only contains distinct items: [source,java,indent=0] @@ -18,15 +18,18 @@ include::{include_dir}/RepetitionsTest.java[tag=distinct] ---- If you have a stream emitting the {1, 1, 2, 3, 4, 5, 5, 6, 1, 4, 4} items. -Applying `.transform().byDroppingDuplicates()` on such stream produces: +Applying `.select().distinct()` on such stream produces: {1, 2, 3, 4, 5, 6}. -IMPORTANT: Do not use `.transform().byDroppingDuplicates()` on large or infinite streams. +IMPORTANT: Do not use `.select().distinct()` on large or infinite streams. The operator keeps a reference on all the emitted items, and so, it could lead to memory issues if the stream contains too many distinct items. -== Removing repetitions +TIP: By default, `select().distinct()` uses the `hashCode` method from the item's class. +You can pass a custom comparator for more advanced checks. -The `.transform().byDroppingRepetitions()` operator removes subsequent repetition of an item: +== Skipping repetitions + +The `.skip().repetitions()` operator removes subsequent repetitions of an item: [source,java,indent=0] ---- @@ -34,7 +37,10 @@ include::{include_dir}/RepetitionsTest.java[tag=repetition] ---- If you have a stream emitting the {1, 1, 2, 3, 4, 5, 5, 6, 1, 4, 4} items. -Applying `.transform().byDroppingRepetitions()` on such stream produces: +Applying `.skip().repetitions()` on such stream produces: {1, 2, 3, 4, 5, 6, 1, 4}. -Unlike `.transform().byDroppingDuplicates()`, you can use this operator on large or infinite streams. \ No newline at end of file +Unlike `.skip().repetitions())`, you can use this operator on large or infinite streams. + +TIP: By default, `skip().repetitions()` uses the `equals` method from the item's class. +You can pass a custom comparator for more advanced checks. \ No newline at end of file diff --git a/documentation/src/main/jekyll/guides/take-skip.adoc b/documentation/src/main/jekyll/guides/take-skip.adoc index b88708ad3..850dbf12b 100644 --- a/documentation/src/main/jekyll/guides/take-skip.adoc +++ b/documentation/src/main/jekyll/guides/take-skip.adoc @@ -11,84 +11,93 @@ Multi provides the ability to: * skip items from the beginning of the multi, * skip the last items. -== Taking items +These actions are available from the `multi.select()` and `multi.skip()` groups, allowing to, respectively, select and skip +items from upstream. -The `multi.transform().byTakingFirstItems` method forwards on the _n_ **first** items from the multi. +== Selecting items + +The `multi.select().first` method forwards on the _n_ **first** items from the multi. It forwards that amount of items and then sends the completion signal. It also cancels the upstream subscription. [source,java,indent=0] ---- -include::{include_dir}/TakeTest.java[tag=take-first] +include::{include_dir}/SelectAndSkipTest.java[tag=take-first] ---- +NOTE: The `select().first()` method selects only the first item. + If the observed multi emits fewer items, it sends the completion event when the upstream completes. -Similarly, The `multi.transform().byTakingLastItems` operator forwards on the _n_ **last** items from the multi. +Similarly, The `multi.select().last` operator forwards on the _n_ **last** items from the multi. It discards all the items emitted beforehand. [source,java,indent=0] ---- -include::{include_dir}/TakeTest.java[tag=take-last] +include::{include_dir}/SelectAndSkipTest.java[tag=take-last] ---- -The `multi.transform().byTakingItemsWhile` operator forwards the items while the passed predicate returns `true`: +NOTE: The `select().last()` method selects only the last item. + +The `multi.select().first(Predicate)` operator forwards the items while the passed predicate returns `true`: [source,java,indent=0] ---- -include::{include_dir}/TakeTest.java[tag=take-while] +include::{include_dir}/SelectAndSkipTest.java[tag=take-while] ---- It calls the predicates for each item. Once the predicate returns `false`, it stops forwarding the items downstream. It also sends the completion event and cancels the upstream subscription. -Finally, `multi.transform().byTakingItemsFor` operator picks the first items for a given period. +Finally, `multi.select().first(Duration)` operator picks the first items emitted during a given period. Once the passed duration expires, it sends the completion event and cancels the upstream subscription. If the observes multi completes before the passed duration, it sends the completion event. [source,java,indent=0] ---- -include::{include_dir}/TakeTest.java[tag=take-for] +include::{include_dir}/SelectAndSkipTest.java[tag=take-for] ---- == Skipping items -You can also skip items. +You can also skip items using `multi.skip()`. -The `multi.transform().bySkippingFirstItems` method skips the _n_ **first** items from the multi. +The `multi.skip().first(n)` method skips the _n_ **first** items from the multi. It forwards all the remaining items and sends the completion event when the upstream multi completes. [source,java,indent=0] ---- -include::{include_dir}/TakeTest.java[tag=skip-first] +include::{include_dir}/SelectAndSkipTest.java[tag=skip-first] ---- If the observed multi emits fewer items, it sends the completion event without emitting any items. -Similarly, The `multi.transform().bySkippingLastItems` operator skips on the _n_ **last** items from the multi: +NOTE: `skip().last()` drops the very last item only. + +Similarly, The `multi.skip().last(n)` operator skips on the _n_ **last** items from the multi: [source,java,indent=0] ---- -include::{include_dir}/TakeTest.java[tag=skip-last] +include::{include_dir}/SelectAndSkipTest.java[tag=skip-last] ---- -The `multi.transform().bySkippingItemsWhile` operator skips the items while the passed predicate returns `true`: +The `multi.skip().first(Predicate)` operator skips the items while the passed predicate returns `true`: [source,java,indent=0] ---- -include::{include_dir}/TakeTest.java[tag=skip-while] +include::{include_dir}/SelectAndSkipTest.java[tag=skip-while] ---- It calls the predicates for each item. Once the predicate returns `false`, it stops discarding the items and starts forwarding downstream. -Finally, `multi.transform().bySkippingItemsFor` operator skips the first items for a given period. +Finally, `multi.skip().first(Duration)` operator skips the first items for a given period. Once the passed duration expires, it sends the items emitted after the deadline downstream. If the observes multi completes before the passed duration, it sends the completion event. [source,java,indent=0] ---- -include::{include_dir}/TakeTest.java[tag=skip-for] +include::{include_dir}/SelectAndSkipTest.java[tag=skip-for] ---- diff --git a/documentation/src/test/java/guides/CreatingMultiTest.java b/documentation/src/test/java/guides/CreatingMultiTest.java index d0a304703..1800c0a06 100644 --- a/documentation/src/test/java/guides/CreatingMultiTest.java +++ b/documentation/src/test/java/guides/CreatingMultiTest.java @@ -26,7 +26,7 @@ void pipeline(SystemOut out) { // tag::pipeline[] Multi.createFrom().items(1, 2, 3, 4, 5) .onItem().transform(i -> i * 2) - .transform().byTakingFirstItems(3) + .select().first(3) .onFailure().recoverWithItem(0) .subscribe().with(System.out::println); // end::pipeline[] @@ -109,7 +109,7 @@ public void creation() { Multi ticks = Multi.createFrom().ticks().every(Duration.ofMillis(100)); // end::ticks[] BlockingIterable longs = ticks - .transform().byTakingFirstItems(3) + .select().first(3) .subscribe().asIterable(); await().until(() -> longs.stream().count() == 3); } diff --git a/documentation/src/test/java/guides/operators/FilterTest.java b/documentation/src/test/java/guides/operators/FilterTest.java index 611e7807c..6cd2b1e7a 100644 --- a/documentation/src/test/java/guides/operators/FilterTest.java +++ b/documentation/src/test/java/guides/operators/FilterTest.java @@ -15,14 +15,14 @@ public void filter() { Multi multi = Multi.createFrom().range(1, 11); // tag::filter[] List list = multi - .transform().byFilteringItemsWith(i -> i > 6) + .select().where(i -> i > 6) .collect().asList() .await().indefinitely(); // end::filter[] // tag::test[] List list2 = multi - .transform().byTestingItemsWith(i -> Uni.createFrom().item(i > 6)) + .select().when(i -> Uni.createFrom().item(i > 6)) .collect().asList() .await().indefinitely(); // end::test[] @@ -36,17 +36,17 @@ public void take() { Multi multi = Multi.createFrom().range(1, 11); // tag::take[] List list = multi - .transform().byTakingFirstItems(2) + .select().first(2) .collect().asList() .await().indefinitely(); List list2 = multi - .transform().byTakingItemsWhile(i -> i < 3) + .select().first(i -> i < 3) .collect().asList() .await().indefinitely(); List list3 = multi - .transform().byTakingLastItems(2) + .select().last(2) .collect().asList() .await().indefinitely(); // end::take[] @@ -60,17 +60,17 @@ public void skip() { Multi multi = Multi.createFrom().range(1, 11); // tag::skip[] List list = multi - .transform().bySkippingFirstItems(8) + .skip().first(8) .collect().asList() .await().indefinitely(); List list2 = multi - .transform().bySkippingItemsWhile(i -> i < 9) + .skip().first(i -> i < 9) .collect().asList() .await().indefinitely(); List list3 = multi - .transform().bySkippingLastItems(8) + .skip().last(8) .collect().asList() .await().indefinitely(); // end::skip[] @@ -84,14 +84,14 @@ public void distinct() { Multi multi = Multi.createFrom().items(1, 1, 2, 3, 4, 5, 5, 6); // tag::distinct[] List list = multi - .transform().byDroppingDuplicates() + .select().distinct() .collect().asList() .await().indefinitely(); // end::distinct[] // tag::repetition[] List list2 = multi - .transform().byDroppingRepetitions() + .skip().repetitions() .collect().asList() .await().indefinitely(); // end::repetition[] diff --git a/documentation/src/test/java/guides/operators/RepetitionsTest.java b/documentation/src/test/java/guides/operators/RepetitionsTest.java index e35a5fcf3..c32dcf760 100644 --- a/documentation/src/test/java/guides/operators/RepetitionsTest.java +++ b/documentation/src/test/java/guides/operators/RepetitionsTest.java @@ -14,14 +14,14 @@ public void distinct() { Multi multi = Multi.createFrom().items(1, 1, 2, 3, 4, 5, 5, 6, 1, 4, 4); // tag::distinct[] List list = multi - .transform().byDroppingDuplicates() + .select().distinct() .collect().asList() .await().indefinitely(); // end::distinct[] // tag::repetition[] List list2 = multi - .transform().byDroppingRepetitions() + .skip().repetitions() .collect().asList() .await().indefinitely(); // end::repetition[] diff --git a/documentation/src/test/java/guides/operators/TakeTest.java b/documentation/src/test/java/guides/operators/SelectAndSkipTest.java similarity index 69% rename from documentation/src/test/java/guides/operators/TakeTest.java rename to documentation/src/test/java/guides/operators/SelectAndSkipTest.java index ff210fad4..a8ffa4508 100644 --- a/documentation/src/test/java/guides/operators/TakeTest.java +++ b/documentation/src/test/java/guides/operators/SelectAndSkipTest.java @@ -7,26 +7,26 @@ import static org.assertj.core.api.Assertions.assertThat; -public class TakeTest { +public class SelectAndSkipTest { @Test - public void testTake() { + public void testSelect() { Multi multi = Multi.createFrom().items(1, 2, 3, 4, 5, 6, 7, 8, 9); // tag::take-first[] - Multi firstThreeItems = multi.transform().byTakingFirstItems(3); + Multi firstThreeItems = multi.select().first(3); // end::take-first[] // tag::take-last[] - Multi lastThreeItems = multi.transform().byTakingLastItems(3); + Multi lastThreeItems = multi.select().last(3); // end::take-last[] // tag::take-while[] - Multi takeWhile = multi.transform().byTakingItemsWhile(i -> i < 4); + Multi takeWhile = multi.select().first(i -> i < 4); // end::take-while[] // tag::take-for[] - Multi takeForDuration = multi.transform().byTakingItemsFor(Duration.ofSeconds(1)); + Multi takeForDuration = multi.select().first(Duration.ofSeconds(1)); // end::take-for[] assertThat(firstThreeItems.collect().asList().await().indefinitely()).containsExactly(1, 2, 3); @@ -41,19 +41,19 @@ public void testSkip() { Multi multi = Multi.createFrom().items(1, 2, 3, 4, 5, 6, 7, 8, 9); // tag::skip-first[] - Multi skipThreeItems = multi.transform().bySkippingFirstItems(3); + Multi skipThreeItems = multi.skip().first(3); // end::skip-first[] // tag::skip-last[] - Multi skipLastThreeItems = multi.transform().bySkippingLastItems(3); + Multi skipLastThreeItems = multi.skip().last(3); // end::skip-last[] // tag::skip-while[] - Multi skipWhile = multi.transform().bySkippingItemsWhile(i -> i < 4); + Multi skipWhile = multi.skip().first(i -> i < 4); // end::skip-while[] // tag::skip-for[] - Multi skipForDuration = multi.transform().bySkippingItemsFor(Duration.ofSeconds(1)); + Multi skipForDuration = multi.skip().first(Duration.ofSeconds(1)); // end::skip-for[] assertThat(skipThreeItems.collect().asList().await().indefinitely()).containsExactly(4, 5, 6, 7, 8, 9); diff --git a/implementation/revapi.json b/implementation/revapi.json index 89310c1c9..a75026c43 100644 --- a/implementation/revapi.json +++ b/implementation/revapi.json @@ -35,6 +35,16 @@ "code": "java.method.removed", "old": "method io.smallrye.mutiny.helpers.test.AssertSubscriber io.smallrye.mutiny.helpers.test.AssertSubscriber::create(org.reactivestreams.Subscriber)", "justification": "Provide the new test API, spy support has been removed, extend the class if needed." + }, + { + "code": "java.class.externalClassExposedInAPI", + "new": "class io.smallrye.mutiny.groups.MultiSelect", + "justification": "Addition of the new `select` group to Multi. If you are impacted by such a change, we recommend extending `AbstractMulti` instead of implementing `Multi` directly." + }, + { + "code": "java.class.externalClassExposedInAPI", + "new": "class io.smallrye.mutiny.groups.MultiSkip", + "justification": "Addition of the new `skip` group to Multi. If you are impacted by such a change, we recommend extending `AbstractMulti` instead of implementing `Multi` directly." } ] } diff --git a/implementation/src/main/java/io/smallrye/mutiny/Multi.java b/implementation/src/main/java/io/smallrye/mutiny/Multi.java index cfceffdf8..1d941bc8e 100644 --- a/implementation/src/main/java/io/smallrye/mutiny/Multi.java +++ b/implementation/src/main/java/io/smallrye/mutiny/Multi.java @@ -298,9 +298,25 @@ default Multi subscribeOn(Executor executor) { * Transforms the streams by skipping, selecting, or merging. * * @return the object to configure the transformation. + * @deprecated Use {@link #select()} and {@link #skip()}instead */ + @Deprecated MultiTransform transform(); + /** + * Selects items from this {@link Multi}. + * + * @return the object to configure the selection. + */ + MultiSelect select(); + + /** + * Skips items from this {@link Multi}. + * + * @return the object to configure the skip. + */ + MultiSkip skip(); + /** * Configures the back-pressure behavior when the consumer cannot keep up with the emissions from this * {@link Multi}. @@ -346,7 +362,7 @@ default Multi subscribeOn(Executor executor) { * @return the new {@link Multi} */ default Multi filter(Predicate predicate) { - return transform().byFilteringItemsWith(predicate); + return select().where(predicate); } /** @@ -535,4 +551,21 @@ default Multi plug(Function, Multi> operatorProvider) { Function, Multi> provider = nonNull(operatorProvider, "operatorProvider"); return Infrastructure.onMultiCreation(nonNull(provider.apply(this), "multi")); } + + /** + * Produces a new {@link Multi} transforming this {@code Multi} into a hot stream. + * + * With a hot stream, when no subscribers are present, emitted items are dropped. + * Late subscribers would only receive items emitted after their subscription. + * If the upstream has already been terminated, the termination event (failure or completion) is forwarded to the + * subscribers. + * + * Note that this operator consumes the upstream stream without back-pressure. + * It still enforces downstream back-pressure. + * If the subscriber is not ready to receive an item when the upstream emits an item, the subscriber gets a + * {@link io.smallrye.mutiny.subscription.BackPressureFailure} failure. + * + * @return the new multi. + */ + Multi toHotStream(); } diff --git a/implementation/src/main/java/io/smallrye/mutiny/groups/MultiSelect.java b/implementation/src/main/java/io/smallrye/mutiny/groups/MultiSelect.java new file mode 100644 index 000000000..92a14d319 --- /dev/null +++ b/implementation/src/main/java/io/smallrye/mutiny/groups/MultiSelect.java @@ -0,0 +1,239 @@ +package io.smallrye.mutiny.groups; + +import static io.smallrye.mutiny.helpers.ParameterValidation.nonNull; + +import java.time.Duration; +import java.util.Comparator; +import java.util.function.Function; +import java.util.function.Predicate; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.infrastructure.Infrastructure; +import io.smallrye.mutiny.operators.multi.*; + +/** + * Selects items from the upstream {@link Multi}. + * + * @param the type of item + * @see MultiSkip + */ +public class MultiSelect { + + private final Multi upstream; + + public MultiSelect(Multi upstream) { + this.upstream = upstream; + } + + /** + * Select the first item from the {@link Multi}. + *

+ * If the upstream {@link Multi} contains more than one item, the others are dropped. + * If the upstream emits a failure before emitting an item, the produced {@link Multi} emits the same failure. + * If the upstream completes without emitting an item first, the produced {@link Multi} is empty. + * + * @return the resulting {@link Multi} + */ + public Multi first() { + return first(1); + } + + /** + * Select the last item from the {@link Multi}. + *

+ * If the upstream {@link Multi} contains more than one item, the others are dropped, only the last one is emitted + * by the produced {@link Multi}. + * If the upstream emits a failure, the produced {@link Multi} emits the same failure. + * If the upstream completes without emitting an item first, the produced {@link Multi} is empty. + * + * @return the resulting {@link Multi} + */ + public Multi last() { + return last(1); + } + + /** + * Selects the first {@code n} items from the {@link Multi}. + *

+ * If the upstream {@link Multi} contains more than n items, the others are dropped. + * If the upstream {@link Multi} emits less than n items, all the items are emitted by the produced {@link Multi}. + * If the upstream emits a failure before emitting n items, the produced {@link Multi} emits the same failure after + * having emitted the first items. + * If the upstream completes without emitting an item first, the produced {@link Multi} is empty. + * + * @param n the number of items to select, must be positive. If 0, the resulting {@link Multi} is empty. + * @return the resulting {@link Multi} + */ + public Multi first(long n) { + return Infrastructure.onMultiCreation(new MultiSelectFirstOp<>(upstream, n)); + } + + /** + * Selects the last {@code n} items from the {@link Multi}. + *

+ * If the upstream {@link Multi} contains more than n items, the others are dropped. + * If the upstream {@link Multi} emits less than n items, all the items are emitted by the produced {@link Multi}. + * If the upstream emits a failure, the produced {@link Multi} emits the same failure after. No items will + * be emitted by the produced {@link Multi}. + * If the upstream completes without emitting an item first, the produced {@link Multi} is empty. + * + * @param n the number of items to select, must be positive. If 0, the resulting {@link Multi} is empty. + * @return the resulting {@link Multi} + */ + public Multi last(int n) { + return Infrastructure.onMultiCreation(new MultiSelectLastOp<>(upstream, n)); + } + + /** + * Selects the first items while the given predicate returns {@code true}. + * It calls the predicates for each items, until the predicate returns {@code false}. + * Each item for which the predicates returned {@code true} is emitted by the produced {@link Multi}. + * As soon as the predicate returns {@code false} for an item, it stops emitting the item and sends the completion + * event. The last checked item is not emitted. + *

+ * If the upstream {@link Multi} is empty, the produced {@link Multi} is empty. + * If the upstream {@link Multi} is emitting a failure, while the predicate has not returned {@code false} yet, the + * failure is emitted by the produced {@link Multi}. + * If the predicates throws an exception while testing an item, the produced {@link Multi} emits that exception as + * failure. No more items will be tested or emitted. + * If the predicates returns {@code true} for each items from upstream, all the items are selected. + * Once the predicate returns {@code false}, it cancels the subscription to the upstream, and completes the produced + * {@link Multi}. + * + * @param predicate the predicate to test the items, must not be {@code null} + * @return the resulting {@link Multi} + */ + public Multi first(Predicate predicate) { + return Infrastructure.onMultiCreation(new MultiSelectFirstWhileOp<>(upstream, nonNull(predicate, "predicate"))); + } + + /** + * Selects the first items for the given duration. + * It selects each items emitted after the subscription for the given duration. + *

+ * If the upstream {@link Multi} is empty, the produced {@link Multi} is empty. + * If the upstream {@link Multi} is emitting a failure, before the duration expires, the failure is emitted by the + * produced {@link Multi}. + * If the upstream completes before the given duration, all the items are selected. + *

+ * Once the duration expires, it cancels the subscription to the upstream, and completes the produced + * {@link Multi}. + * + * @param duration the duration, must not be {@code null}, must be strictly positive. + * @return the resulting {@link Multi} + */ + public Multi first(Duration duration) { + Multi ticks = Multi.createFrom().ticks().startingAfter(duration).every(duration); + return Infrastructure.onMultiCreation(new MultiSelectFirstUntilOtherOp<>(upstream, ticks)); + } + + /** + * Selects the items where the given predicate returns {@code true}. + * It calls the predicates for each items. + * Each item for which the predicates returned {@code true} is emitted by the produced {@link Multi}. + * Others are dropped. + *

+ * If the upstream {@link Multi} is empty, the produced {@link Multi} is empty. + * If the upstream {@link Multi} is emitting a failure, the failure is emitted by the produced {@link Multi}. + * If the predicates throws an exception while testing an item, the produced {@link Multi} emits that exception as + * failure. No more items will be tested or emitted. + * If the predicates returns {@code true} for each items from upstream, all the items are selected. + * The produced {@link Multi} completes when the upstream completes. + * + * @param predicate the predicate to test the items, must not be {@code null} + * @return the resulting {@link Multi} + * @see #when(Function) + */ + public Multi where(Predicate predicate) { + return Infrastructure.onMultiCreation(new MultiSelectWhereOp<>(upstream, nonNull(predicate, "predicate"))); + } + + /** + * Like {@link #when(Function)}, but select at most {@code limit} items. + * + * @param predicate the predicate to test the items, must not be {@code null} + * @param limit the maximum number of item to select, must be positive. 0 would produce an empty {@link Multi} + * @return the resulting {@link Multi} + * @see #when(Function) + */ + public Multi where(Predicate predicate, int limit) { + return where(predicate) + .select().first(limit); + } + + /** + * Selects the items where the given function produced a {@link Uni} emitting {@code true}. + * This method is the asynchronous version of {@link #where(Predicate)}. + * Instead of a synchronous predicate, it accepts a function producing {@link Uni}. + * It calls the function for every item, and depending of the produced {@link Uni}, it emits the item downstream or + * drops it. If the returned {@link Uni} produces {@code true}, the item is selected and emitted by the produced + * {@link Multi}, otherwise the item is dropped. + * The item is only emitted when {@link Uni} produced for that item emits {@code true}. + *

+ * If the upstream {@link Multi} is empty, the produced {@link Multi} is empty. + * If the upstream {@link Multi} is emitting a failure, the failure is emitted by the produced {@link Multi}. + * If the function throws an exception while testing an item, the produced {@link Multi} emits that exception as + * failure. No more items will be tested or emitted. + * If the function produced a {@code null} Uni, the produced {@link Multi} emits an {@link NullPointerException} as + * failure. No more items will be tested or emitted. + * If the function produced a failing Uni, the produced {@link Multi} emits that failure. No more items will be + * tested or emitted. + * If the function produced a {@link Uni} emitting {@code null}, the produced {@link Multi} emits a failure. + * No more items will be tested or emitted. + * If the function accepts all the items from the upstream, all the items are selected. + * The produced {@link Multi} completes when the upstream completes. + *

+ * This method preserves the item orders. + * + * @param predicate the function to test the items, must not be {@code null}, must not produced {@code null} + * @return the resulting {@link Multi} + */ + public Multi when(Function> predicate) { + nonNull(predicate, "predicate"); + return upstream.onItem().transformToMultiAndConcatenate(res -> { + Uni uni = predicate.apply(res); + return uni.map(pass -> pass ? res : null).toMulti(); + }); + } + + /** + * Selects all the distinct items from the upstream. + * This methods uses {@link Object#hashCode()} to compare items. + *

+ * Do NOT call this method on unbounded upstream, as it would lead to an {@link OutOfMemoryError}. + *

+ * If the comparison throws an exception, the produced {@link Multi} fails. + * The produced {@link Multi} completes when the upstream sends the completion event. + * + * @return the resulting {@link Multi}. + * @see MultiSkip#repetitions() + * @see #distinct(Comparator) + */ + public Multi distinct() { + return Infrastructure.onMultiCreation(new MultiDistinctOp<>(upstream)); + } + + /** + * Selects all the distinct items from the upstream. + * This methods uses the given comparator to compare the items. + *

+ * Do NOT call this method on unbounded upstream, as it would lead to an {@link OutOfMemoryError}. + *

+ * If the comparison throws an exception, the produced {@link Multi} fails. + * The produced {@link Multi} completes when the upstream sends the completion event. + *

+ * Unlike {@link #distinct()} which uses a {@link java.util.HashSet} internally, this variant uses a + * {@link java.util.TreeSet} initialized with the given comparator. If the comparator is {@code null}, it uses a + * {@link java.util.HashSet} as backend. + * + * @param comparator the comparator used to compare items. If {@code null}, it will uses the item's {@code hashCode} + * method. + * @return the resulting {@link Multi}. + * @see MultiSkip#repetitions() + */ + public Multi distinct(Comparator comparator) { + return Infrastructure.onMultiCreation(new MultiDistinctOp<>(upstream, comparator)); + } + +} diff --git a/implementation/src/main/java/io/smallrye/mutiny/groups/MultiSkip.java b/implementation/src/main/java/io/smallrye/mutiny/groups/MultiSkip.java new file mode 100644 index 000000000..5c829a8dc --- /dev/null +++ b/implementation/src/main/java/io/smallrye/mutiny/groups/MultiSkip.java @@ -0,0 +1,229 @@ +package io.smallrye.mutiny.groups; + +import static io.smallrye.mutiny.helpers.ParameterValidation.nonNull; +import static io.smallrye.mutiny.helpers.ParameterValidation.positiveOrZero; + +import java.time.Duration; +import java.util.Comparator; +import java.util.function.Function; +import java.util.function.Predicate; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.infrastructure.Infrastructure; +import io.smallrye.mutiny.operators.multi.*; + +/** + * Skips items from the upstream {@link Multi}. + * + * @param the type of item + * @see MultiSelect + */ +public class MultiSkip { + + private final Multi upstream; + + public MultiSkip(Multi upstream) { + this.upstream = upstream; + } + + /** + * Skips the first {@code n} items from the upstream and emits all the other items. + *

+ * If the n is 0, all the items from the upstreams are emitted. + * If the upstream completes, before emitting n items, the produced {@link Multi} is empty. + * If the upstream fails, before emitting n items, the produced {@link Multi} fails with the same failure. + * If the upstream fails, after having emitted n+ items, the produced {@link Multi} would emit the items + * followed by the failure. + * + * @param n the number of item to skip, must be positive. + * @return the resulting {@link Multi} + */ + public Multi first(long n) { + return Infrastructure.onMultiCreation(new MultiSkipFirstOp<>(upstream, positiveOrZero(n, "n"))); + } + + /** + * Skips the first item from the upstream and emits all the other items. + *

+ * If the upstream completes, before emitting an item, the produced {@link Multi} is empty. + * If the upstream fails, before emitting an item, the produced {@link Multi} fails with the same failure. + * If the upstream fails, after having emitted an item, the produced {@link Multi} would emit the items + * followed by the failure. + * + * @return the resulting {@link Multi} + */ + public Multi first() { + return first(1); + } + + /** + * Skips the first items passing the given {@code predicate} from the upstream. + * It calls the predicates for the first items from the upstream until the predicate returns {@code false}. + * Then, that items and all the remaining items are propagated downstream. + *

+ * If the predicate always returns {@code true}, the produced {@link Multi} is empty. + * If the predicate always returns {@code false}, the produced {@link Multi} emits all the items from the upstream, + * and the predicates is called only once on the first item. + * If the upstream completes, and the predicate didn't return {@code false} yet, the produced {@link Multi} is empty. + * If the upstream fails, and the predicate didn't return {@code false} yet, the produced {@link Multi} fails with + * the same failure. + * If the predicate throws an exception while testing an item, the produced {@link Multi} emits the exception as + * failure. + * + * @param predicate the predicate to test the item. Once the predicate returns {@code false} for an item, this item + * and all the remaining items are propagated downstream. + * @return the resulting {@link Multi} + */ + public Multi first(Predicate predicate) { + return Infrastructure.onMultiCreation(new MultiSkipFirstUntilOp<>(upstream, nonNull(predicate, "predicate"))); + } + + /** + * Skips the the items from the upstream emitted during the the given {@code duration}. + * The duration is computed from the subscription time. + *

+ * If the upstream completes before the given {@code duration}, the produced {@link Multi} is empty. + * If the upstream fails before the given {@code duration}, the produced {@link Multi} fails with the same failure. + * If the upstream didn't emit any items before the delay expired, the produced {@link Multi} emits the same events + * as the upstream. + * + * @param duration the duration for which the items from upstream are skipped. Must be strictly positive. + * @return the resulting {@link Multi} + */ + public Multi first(Duration duration) { + Multi ticks = Multi.createFrom().ticks().startingAfter(duration).every(duration); + return Infrastructure.onMultiCreation(new MultiSkipUntilOtherOp<>(upstream, ticks)); + } + + /** + * Skips the last {@code n} items from the upstream. All the previous items are emitted by the produced + * {@link Multi}. + *

+ * If the n is 0, all the items from the upstreams are emitted. + * If the upstream completes, before emitting n items, the produced {@link Multi} is empty. + * If the upstream fails, before emitting n items, the produced {@link Multi} fails with the same failure. + * the produced {@link Multi} would not emit any items. + * If the upstream fails, after having emitted n items, the produced {@link Multi} would emit the items + * followed by the failure. + * + * @param n the number of item to skip, must be positive. + * @return the resulting {@link Multi} + */ + public Multi last(int n) { + return Infrastructure.onMultiCreation(new MultiSkipLastOp<>(upstream, n)); + } + + /** + * Skips the first items from the upstream. All the previous items are emitted by the produced {@link Multi}. + *

+ * If the upstream completes, before emitting an item, the produced {@link Multi} is empty. + * If the upstream fails, before emitting an item, the produced {@link Multi} fails with the same failure. + * the produced {@link Multi} would not emit any items. + * + * @return the resulting {@link Multi} + */ + public Multi last() { + return last(1); + } + + /** + * Skips repetitions from the upstream. + * So, if the upstream emits consecutively the same item twice, it drops the second occurrence. + *

+ * The items are compared using the {@link Object#equals(Object)} method. + *

+ * If the upstream emits a failure, the produced {@link Multi} emits a failure. + * If the comparison throws an exception, the produced {@link Multi} emits that exception as failure. + * The produces {@link Multi} completes when the upstream completes. + *

+ * Unlike {@link MultiSelect#distinct()}, this method can be called on unbounded upstream, as it only keeps a + * reference on the last item. + * + * @return the resulting {@link Multi} + * @see MultiSelect#distinct() + * @see MultiSkip#repetitions(Comparator) + */ + public Multi repetitions() { + return Infrastructure.onMultiCreation(new MultiSkipRepetitionsOp<>(upstream)); + } + + /** + * Skips repetitions from the upstream. + * So, if the upstream emits consecutively the same item twice, it drops the second occurrence. + *

+ * The items are compared using the given comparator. + *

+ * If the upstream emits a failure, the produced {@link Multi} emits a failure. + * If the comparison throws an exception, the produced {@link Multi} emits that exception as failure. + * The produces {@link Multi} completes when the upstream completes. + *

+ * Unlike {@link MultiSelect#distinct()}, this method can be called on unbounded upstream, as it only keeps a + * reference on the last item. + * + * @return the resulting {@link Multi} + * @see MultiSelect#distinct() + * @see MultiSkip#repetitions() + */ + public Multi repetitions(Comparator comparator) { + return Infrastructure.onMultiCreation(new MultiSkipRepetitionsOp<>(upstream, comparator)); + } + + /** + * Skips the items where the given predicate returns {@code true}. + * It calls the predicates for each items. + * Each item for which the predicates returned {@code false} is emitted by the produced {@link Multi}. + * Others are dropped. + *

+ * If the upstream {@link Multi} is empty, the produced {@link Multi} is empty. + * If the upstream {@link Multi} is emitting a failure, the failure is emitted by the produced {@link Multi}. + * If the predicates throws an exception while testing an item, the produced {@link Multi} emits that exception as + * failure. No more items will be tested or emitted. + * If the predicates returns {@code false} for each items from upstream, all the items are propagated downstream. + * If the predicates returns {@code true} for each items from upstream, the produce {@link Multi} is empty. + * The produced {@link Multi} completes when the upstream completes. + * + * This function is the opposite of {@link MultiSelect#where(Predicate)} which selects the passing items. + * + * @param predicate the predicate to test the items, must not be {@code null} + * @return the resulting {@link Multi} + * @see #when(Function) + * @see MultiSelect#where(Predicate) + */ + public Multi where(Predicate predicate) { + return upstream.select().where(nonNull(predicate, "predicate").negate()); + } + + /** + * Skips the items where the given function produced a {@link Uni} emitting {@code true}. + * This method is the asynchronous version of {@link #where(Predicate)}. + * Instead of a synchronous predicate, it accepts a function producing {@link Uni}. + * It calls the function for every item, and depending of the produced {@link Uni}, it emits the item downstream + * (when the uni produces {@code false}) or drops it (when the uni produces {@code true}). + * The item is only emitted when {@link Uni} produced for that item emits {@code false}. + *

+ * If the upstream {@link Multi} is empty, the produced {@link Multi} is empty. + * If the upstream {@link Multi} is emitting a failure, the failure is emitted by the produced {@link Multi}. + * If the function throws an exception while testing an item, the produced {@link Multi} emits that exception as + * failure. No more items will be tested or emitted. + * If the function produced a {@code null} Uni, the produced {@link Multi} emits an {@link NullPointerException} as + * failure. No more items will be tested or emitted. + * If the function produced a failing Uni, the produced {@link Multi} emits that failure. No more items will be + * tested or emitted. + * If the function produced a {@link Uni} emitting {@code null}, the produced {@link Multi} emits a failure. + * No more items will be tested or emitted. + * The produced {@link Multi} completes when the upstream completes. + *

+ * This method preserves the item orders. + * + * @param predicate the function to test the items, must not be {@code null}, must not produced {@code null} + * @return the resulting {@link Multi} + */ + public Multi when(Function> predicate) { + nonNull(predicate, "predicate"); + return upstream.onItem().transformToMultiAndConcatenate(res -> { + Uni uni = predicate.apply(res); + return uni.map(pass -> pass ? null : res).toMulti(); + }); + } +} diff --git a/implementation/src/main/java/io/smallrye/mutiny/groups/MultiTransform.java b/implementation/src/main/java/io/smallrye/mutiny/groups/MultiTransform.java index 2818a80bd..1478c65f4 100644 --- a/implementation/src/main/java/io/smallrye/mutiny/groups/MultiTransform.java +++ b/implementation/src/main/java/io/smallrye/mutiny/groups/MultiTransform.java @@ -1,8 +1,6 @@ package io.smallrye.mutiny.groups; import static io.smallrye.mutiny.helpers.ParameterValidation.nonNull; -import static io.smallrye.mutiny.helpers.ParameterValidation.positiveOrZero; -import static io.smallrye.mutiny.helpers.ParameterValidation.validate; import java.time.Duration; import java.util.ArrayList; @@ -15,9 +13,6 @@ import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; -import io.smallrye.mutiny.infrastructure.Infrastructure; -import io.smallrye.mutiny.operators.MultiTransformation; -import io.smallrye.mutiny.operators.multi.MultiFilterOp; import io.smallrye.mutiny.operators.multi.processors.BroadcastProcessor; public class MultiTransform { @@ -29,47 +24,43 @@ public MultiTransform(Multi upstream) { } public Multi bySkippingFirstItems(long number) { - return Infrastructure - .onMultiCreation(MultiTransformation.skipFirst(upstream, positiveOrZero(number, "number"))); + return upstream.skip().first(number); } public Multi bySkippingLastItems(int number) { - return Infrastructure.onMultiCreation(MultiTransformation.skipLast(upstream, positiveOrZero(number, "number"))); + return upstream.skip().last(number); } public Multi bySkippingItemsWhile(Predicate predicate) { - return Infrastructure.onMultiCreation(MultiTransformation.skipWhile(upstream, nonNull(predicate, "predicate"))); + return upstream.skip().first(predicate); } public Multi bySkippingItemsFor(Duration duration) { - return Infrastructure - .onMultiCreation(MultiTransformation.skipForDuration(upstream, validate(duration, "duration"))); + return upstream.skip().first(duration); } public Multi byTakingFirstItems(long number) { - return Infrastructure - .onMultiCreation(MultiTransformation.takeFirst(upstream, positiveOrZero(number, "number"))); + return upstream.select().first(number); } public Multi byTakingLastItems(int number) { - return Infrastructure.onMultiCreation(MultiTransformation.takeLast(upstream, positiveOrZero(number, "number"))); + return upstream.select().last(number); } public Multi byTakingItemsFor(Duration duration) { - return Infrastructure - .onMultiCreation(MultiTransformation.takeForDuration(upstream, validate(duration, "duration"))); + return upstream.select().first(duration); } public Multi byTakingItemsWhile(Predicate predicate) { - return Infrastructure.onMultiCreation(MultiTransformation.takeWhile(upstream, nonNull(predicate, "predicate"))); + return upstream.select().first(predicate); } public Multi byDroppingDuplicates() { - return Infrastructure.onMultiCreation(MultiTransformation.distinct(upstream)); + return upstream.select().distinct(); } public Multi byDroppingRepetitions() { - return Infrastructure.onMultiCreation(MultiTransformation.dropRepetitions(upstream)); + return upstream.skip().repetitions(); } @SafeVarargs @@ -94,7 +85,7 @@ public Multi byMergingWith(Iterable> iterable) { * @return the produced {@link Multi} */ public Multi byFilteringItemsWith(Predicate predicate) { - return Infrastructure.onMultiCreation(new MultiFilterOp<>(upstream, nonNull(predicate, "predicate"))); + return upstream.select().where(predicate); } /** @@ -106,17 +97,7 @@ public Multi byFilteringItemsWith(Predicate predicate) { * @return the produced {@link Multi} */ public Multi byTestingItemsWith(Function> tester) { - nonNull(tester, "tester"); - return upstream.onItem().transformToMultiAndConcatenate(res -> { - Uni uni = tester.apply(res); - return uni.map(pass -> { - if (pass) { - return res; - } else { - return null; - } - }).toMulti(); - }); + return upstream.select().when(tester); } /** @@ -125,14 +106,16 @@ public Multi byTestingItemsWith(Function> tester) { * Late subscribers would only receive items emitted after their subscription. * If the upstream has already been terminated, the termination event (failure or completion) is forwarded to the * subscribers. - * + *

* Note that this operator consumes the upstream stream without back-pressure. * It still enforces downstream back-pressure. * If the subscriber is not ready to receive an item when the upstream emits an item, the subscriber gets a * {@link io.smallrye.mutiny.subscription.BackPressureFailure} failure. * * @return the new multi. + * @deprecated Use {@link Multi#toHotStream()} instead */ + @Deprecated public Multi toHotStream() { BroadcastProcessor processor = BroadcastProcessor.create(); upstream.subscribe(processor); diff --git a/implementation/src/main/java/io/smallrye/mutiny/helpers/test/AssertSubscriber.java b/implementation/src/main/java/io/smallrye/mutiny/helpers/test/AssertSubscriber.java index 014d2bdc2..9cb6a6507 100644 --- a/implementation/src/main/java/io/smallrye/mutiny/helpers/test/AssertSubscriber.java +++ b/implementation/src/main/java/io/smallrye/mutiny/helpers/test/AssertSubscriber.java @@ -19,7 +19,7 @@ * * @param the type of the items */ -@SuppressWarnings({ "ReactiveStreamsSubscriberImplementation" }) +@SuppressWarnings({ "ReactiveStreamsSubscriberImplementation"}) public class AssertSubscriber implements Subscriber { /** diff --git a/implementation/src/main/java/io/smallrye/mutiny/operators/AbstractMulti.java b/implementation/src/main/java/io/smallrye/mutiny/operators/AbstractMulti.java index dbbcadf28..1309d07c6 100644 --- a/implementation/src/main/java/io/smallrye/mutiny/operators/AbstractMulti.java +++ b/implementation/src/main/java/io/smallrye/mutiny/operators/AbstractMulti.java @@ -1,8 +1,8 @@ package io.smallrye.mutiny.operators; import static io.smallrye.mutiny.helpers.ParameterValidation.nonNull; +import static io.smallrye.mutiny.helpers.ParameterValidation.nonNullNpe; -import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Predicate; @@ -16,6 +16,7 @@ import io.smallrye.mutiny.operators.multi.MultiCacheOp; import io.smallrye.mutiny.operators.multi.MultiEmitOnOp; import io.smallrye.mutiny.operators.multi.MultiSubscribeOnOp; +import io.smallrye.mutiny.operators.multi.processors.BroadcastProcessor; import io.smallrye.mutiny.subscription.MultiSubscriber; public abstract class AbstractMulti implements Multi { @@ -24,11 +25,16 @@ public void subscribe(MultiSubscriber subscriber) { this.subscribe(Infrastructure.onMultiSubscription(this, subscriber)); } + @SuppressWarnings("unchecked") @Override public void subscribe(Subscriber subscriber) { - // NOTE The Reactive Streams TCK mandates throwing an NPE. - Objects.requireNonNull(subscriber, "Subscriber is `null`"); - this.subscribe(new StrictMultiSubscriber<>(subscriber)); + if (subscriber instanceof MultiOperator || subscriber instanceof StrictMultiSubscriber) { + this.subscribe((MultiSubscriber) subscriber); + } else { + // NOTE The Reactive Streams TCK mandates throwing an NPE. + nonNullNpe(subscriber, "subscriber"); + this.subscribe(new StrictMultiSubscriber<>(subscriber)); + } } @Override @@ -91,6 +97,16 @@ public MultiTransform transform() { return new MultiTransform<>(this); } + @Override + public MultiSelect select() { + return new MultiSelect<>(this); + } + + @Override + public MultiSkip skip() { + return new MultiSkip<>(this); + } + @Override public MultiOverflow onOverflow() { return new MultiOverflow<>(this); @@ -135,4 +151,11 @@ public MultiCollect collect() { public MultiGroup group() { return new MultiGroup<>(this); } + + public Multi toHotStream() { + BroadcastProcessor processor = BroadcastProcessor.create(); + this.subscribe(processor); + return processor; + } + } diff --git a/implementation/src/main/java/io/smallrye/mutiny/operators/MultiTransformation.java b/implementation/src/main/java/io/smallrye/mutiny/operators/MultiTransformation.java deleted file mode 100644 index d2b9186b8..000000000 --- a/implementation/src/main/java/io/smallrye/mutiny/operators/MultiTransformation.java +++ /dev/null @@ -1,67 +0,0 @@ -package io.smallrye.mutiny.operators; - -import java.time.Duration; -import java.util.function.Predicate; - -import io.smallrye.mutiny.Multi; -import io.smallrye.mutiny.infrastructure.Infrastructure; -import io.smallrye.mutiny.operators.multi.MultiDistinctOp; -import io.smallrye.mutiny.operators.multi.MultiDistinctUntilChangedOp; -import io.smallrye.mutiny.operators.multi.MultiSkipLastOp; -import io.smallrye.mutiny.operators.multi.MultiSkipOp; -import io.smallrye.mutiny.operators.multi.MultiSkipUntilOp; -import io.smallrye.mutiny.operators.multi.MultiSkipUntilPublisherOp; -import io.smallrye.mutiny.operators.multi.MultiTakeLastOp; -import io.smallrye.mutiny.operators.multi.MultiTakeOp; -import io.smallrye.mutiny.operators.multi.MultiTakeUntilOtherOp; -import io.smallrye.mutiny.operators.multi.MultiTakeWhileOp; - -public class MultiTransformation { - - private MultiTransformation() { - // avoid direct instantiation - } - - public static Multi skipFirst(Multi upstream, long number) { - return Infrastructure.onMultiCreation(new MultiSkipOp<>(upstream, number)); - } - - public static Multi skipLast(Multi upstream, int number) { - return Infrastructure.onMultiCreation(new MultiSkipLastOp<>(upstream, number)); - } - - public static Multi skipForDuration(Multi upstream, Duration duration) { - Multi ticks = Multi.createFrom().ticks().startingAfter(duration).every(duration); - return Infrastructure.onMultiCreation(new MultiSkipUntilPublisherOp<>(upstream, ticks)); - } - - public static Multi skipWhile(Multi upstream, Predicate predicate) { - return Infrastructure.onMultiCreation(new MultiSkipUntilOp<>(upstream, predicate)); - } - - public static Multi takeFirst(Multi upstream, long number) { - return Infrastructure.onMultiCreation(new MultiTakeOp<>(upstream, number)); - } - - public static Multi takeLast(Multi upstream, int number) { - return Infrastructure.onMultiCreation(new MultiTakeLastOp<>(upstream, number)); - } - - public static Multi takeForDuration(Multi upstream, Duration duration) { - Multi ticks = Multi.createFrom().ticks().startingAfter(duration).every(duration); - return Infrastructure.onMultiCreation(new MultiTakeUntilOtherOp<>(upstream, ticks)); - } - - public static Multi takeWhile(Multi upstream, Predicate predicate) { - return Infrastructure.onMultiCreation(new MultiTakeWhileOp<>(upstream, predicate)); - } - - public static Multi distinct(Multi upstream) { - return Infrastructure.onMultiCreation(new MultiDistinctOp<>(upstream)); - } - - public static Multi dropRepetitions(Multi upstream) { - return Infrastructure.onMultiCreation(new MultiDistinctUntilChangedOp<>(upstream)); - } - -} diff --git a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/AbstractMultiOperator.java b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/AbstractMultiOperator.java index ccd4c45e9..d9387c2d3 100755 --- a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/AbstractMultiOperator.java +++ b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/AbstractMultiOperator.java @@ -25,8 +25,4 @@ public abstract class AbstractMultiOperator extends AbstractMulti imple public AbstractMultiOperator(Multi upstream) { this.upstream = ParameterValidation.nonNull(upstream, "upstream"); } - - public Multi upstream() { - return upstream; - } } diff --git a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiDistinctOp.java b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiDistinctOp.java index 76139aece..a5019c845 100644 --- a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiDistinctOp.java +++ b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiDistinctOp.java @@ -1,10 +1,9 @@ package io.smallrye.mutiny.operators.multi; -import java.util.Collection; -import java.util.HashSet; -import java.util.Objects; +import java.util.*; import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.helpers.ParameterValidation; import io.smallrye.mutiny.subscription.MultiSubscriber; /** @@ -14,22 +13,33 @@ */ public final class MultiDistinctOp extends AbstractMultiOperator { + private final Comparator comparator; + public MultiDistinctOp(Multi upstream) { + this(upstream, null); + } + + public MultiDistinctOp(Multi upstream, Comparator comparator) { super(upstream); + this.comparator = comparator; } @Override - public void subscribe(MultiSubscriber actual) { - upstream.subscribe(new DistinctProcessor<>(Objects.requireNonNull(actual, "Subscriber must not be `null`"))); + public void subscribe(MultiSubscriber subscriber) { + upstream.subscribe(new DistinctProcessor<>(ParameterValidation.nonNullNpe(subscriber, "subscriber"), comparator)); } static final class DistinctProcessor extends MultiOperatorProcessor { final Collection collection; - DistinctProcessor(MultiSubscriber downstream) { + DistinctProcessor(MultiSubscriber downstream, Comparator comparator) { super(downstream); - this.collection = new HashSet<>(); + if (comparator == null) { + this.collection = new HashSet<>(); + } else { + this.collection = new TreeSet<>(comparator); + } } @Override diff --git a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiDistinctUntilChangedOp.java b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiDistinctUntilChangedOp.java deleted file mode 100644 index 1c379658a..000000000 --- a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiDistinctUntilChangedOp.java +++ /dev/null @@ -1,63 +0,0 @@ -package io.smallrye.mutiny.operators.multi; - -import io.smallrye.mutiny.Multi; -import io.smallrye.mutiny.subscription.MultiSubscriber; - -/** - * Eliminates the duplicated items from the upstream. - * - * @param the type of items - */ -public final class MultiDistinctUntilChangedOp extends AbstractMultiOperator { - - public MultiDistinctUntilChangedOp(Multi upstream) { - super(upstream); - } - - @Override - public void subscribe(MultiSubscriber actual) { - upstream.subscribe().withSubscriber(new DistinctProcessor<>(actual)); - } - - static final class DistinctProcessor extends MultiOperatorProcessor { - - private T last; - - DistinctProcessor(MultiSubscriber downstream) { - super(downstream); - } - - @Override - public void onItem(T t) { - if (isDone()) { - return; - } - if (last == null || !last.equals(t)) { - last = t; - downstream.onItem(t); - } else { - // Request the next one, as that item is dropped. - request(1); - } - } - - @Override - public void onFailure(Throwable t) { - super.onFailure(t); - last = null; - } - - @Override - public void onCompletion() { - super.onCompletion(); - last = null; - } - - @Override - public void cancel() { - super.cancel(); - last = null; - } - } - -} diff --git a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiOperatorProcessor.java b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiOperatorProcessor.java index 16646e6b5..881f64686 100644 --- a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiOperatorProcessor.java +++ b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiOperatorProcessor.java @@ -81,6 +81,7 @@ public void request(long numberOfItems) { if (subscription != CANCELLED) { if (numberOfItems <= 0) { onFailure(new IllegalArgumentException("Invalid number of request, must be greater than 0")); + return; } subscription.request(numberOfItems); } diff --git a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiTakeOp.java b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSelectFirstOp.java similarity index 81% rename from implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiTakeOp.java rename to implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSelectFirstOp.java index b2410c369..206ad9049 100644 --- a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiTakeOp.java +++ b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSelectFirstOp.java @@ -17,11 +17,11 @@ * * @param the type of item */ -public final class MultiTakeOp extends AbstractMultiOperator { +public final class MultiSelectFirstOp extends AbstractMultiOperator { private final long numberOfItems; - public MultiTakeOp(Multi upstream, long numberOfItems) { + public MultiSelectFirstOp(Multi upstream, long numberOfItems) { super(upstream); this.numberOfItems = ParameterValidation.positiveOrZero(numberOfItems, "numberOfItems"); } @@ -29,16 +29,16 @@ public MultiTakeOp(Multi upstream, long numberOfItems) { @Override public void subscribe(MultiSubscriber downstream) { ParameterValidation.nonNullNpe(downstream, "subscriber"); - upstream.subscribe().withSubscriber(new TakeProcessor<>(downstream, numberOfItems)); + upstream.subscribe(new MultiSelectFirstProcessor<>(downstream, numberOfItems)); } - static final class TakeProcessor extends MultiOperatorProcessor { + static final class MultiSelectFirstProcessor extends MultiOperatorProcessor { private final long numberOfItems; private long remaining; - private AtomicInteger wip = new AtomicInteger(); + private final AtomicInteger wip = new AtomicInteger(); - TakeProcessor(MultiSubscriber downstream, long numberOfItems) { + MultiSelectFirstProcessor(MultiSubscriber downstream, long numberOfItems) { super(downstream); this.numberOfItems = numberOfItems; this.remaining = numberOfItems; @@ -82,10 +82,6 @@ public void onItem(T t) { @Override public void request(long n) { - if (n <= 0) { - downstream.onFailure(Subscriptions.getInvalidRequestException()); - return; - } Subscription actual = upstream.get(); if (wip.compareAndSet(0, 1)) { if (n >= this.numberOfItems) { diff --git a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiTakeUntilOtherOp.java b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSelectFirstUntilOtherOp.java similarity index 95% rename from implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiTakeUntilOtherOp.java rename to implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSelectFirstUntilOtherOp.java index ebf501c05..e178c68e2 100644 --- a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiTakeUntilOtherOp.java +++ b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSelectFirstUntilOtherOp.java @@ -21,11 +21,11 @@ * @param the type of item from upstream * @param the type of item from the other publisher */ -public final class MultiTakeUntilOtherOp extends AbstractMultiOperator { +public final class MultiSelectFirstUntilOtherOp extends AbstractMultiOperator { private final Publisher other; - public MultiTakeUntilOtherOp(Multi upstream, Publisher other) { + public MultiSelectFirstUntilOtherOp(Multi upstream, Publisher other) { super(upstream); this.other = ParameterValidation.nonNull(other, "other"); } diff --git a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiTakeWhileOp.java b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSelectFirstWhileOp.java similarity index 67% rename from implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiTakeWhileOp.java rename to implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSelectFirstWhileOp.java index a271baa64..23b2d4355 100644 --- a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiTakeWhileOp.java +++ b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSelectFirstWhileOp.java @@ -12,25 +12,25 @@ * * @param the type of item */ -public final class MultiTakeWhileOp extends AbstractMultiOperator { +public final class MultiSelectFirstWhileOp extends AbstractMultiOperator { private final Predicate predicate; - public MultiTakeWhileOp(Multi upstream, Predicate predicate) { + public MultiSelectFirstWhileOp(Multi upstream, Predicate predicate) { super(upstream); this.predicate = ParameterValidation.nonNull(predicate, "predicate"); } @Override - public void subscribe(MultiSubscriber actual) { - ParameterValidation.nonNullNpe(actual, "subscriber"); - upstream.subscribe().withSubscriber(new TakeWhileProcessor<>(actual, predicate)); + public void subscribe(MultiSubscriber subscriber) { + ParameterValidation.nonNullNpe(subscriber, "subscriber"); + upstream.subscribe(new MultiSelectFirstWhileProcessor<>(subscriber, predicate)); } - static final class TakeWhileProcessor extends MultiOperatorProcessor { + static final class MultiSelectFirstWhileProcessor extends MultiOperatorProcessor { private final Predicate predicate; - TakeWhileProcessor(MultiSubscriber downstream, Predicate predicate) { + MultiSelectFirstWhileProcessor(MultiSubscriber downstream, Predicate predicate) { super(downstream); this.predicate = predicate; } diff --git a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiTakeLastOp.java b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSelectLastOp.java similarity index 81% rename from implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiTakeLastOp.java rename to implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSelectLastOp.java index acb001f25..e6c170c3b 100644 --- a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiTakeLastOp.java +++ b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSelectLastOp.java @@ -16,27 +16,27 @@ * * @param the type of item */ -public class MultiTakeLastOp extends AbstractMultiOperator { +public class MultiSelectLastOp extends AbstractMultiOperator { private final int numberOfItems; - public MultiTakeLastOp(Multi upstream, int numberOfItems) { + public MultiSelectLastOp(Multi upstream, int numberOfItems) { super(upstream); this.numberOfItems = ParameterValidation.positiveOrZero(numberOfItems, "numberOfItems"); } @Override - public void subscribe(MultiSubscriber actual) { + public void subscribe(MultiSubscriber subscriber) { if (numberOfItems == 0) { - upstream.subscribe().withSubscriber(new TakeLastZeroProcessor<>(actual)); + upstream.subscribe(new TakeSelectLastZeroProcessor<>(subscriber)); } else { - upstream.subscribe().withSubscriber(new TakeLastManyProcessor<>(actual, numberOfItems)); + upstream.subscribe(new MultiSelectLastProcessor<>(subscriber, numberOfItems)); } } - static final class TakeLastZeroProcessor extends MultiOperatorProcessor { + static final class TakeSelectLastZeroProcessor extends MultiOperatorProcessor { - TakeLastZeroProcessor(MultiSubscriber downstream) { + TakeSelectLastZeroProcessor(MultiSubscriber downstream) { super(downstream); } @@ -58,7 +58,7 @@ public void onItem(T t) { } } - static final class TakeLastManyProcessor extends MultiOperatorProcessor { + static final class MultiSelectLastProcessor extends MultiOperatorProcessor { private final int numberOfItems; private final ArrayDeque queue; @@ -66,7 +66,7 @@ static final class TakeLastManyProcessor extends MultiOperatorProcessor private final AtomicInteger wip = new AtomicInteger(); volatile boolean upstreamCompleted; - TakeLastManyProcessor(MultiSubscriber downstream, int numberOfItems) { + MultiSelectLastProcessor(MultiSubscriber downstream, int numberOfItems) { super(downstream); this.numberOfItems = numberOfItems; this.queue = new ArrayDeque<>(numberOfItems); @@ -74,10 +74,8 @@ static final class TakeLastManyProcessor extends MultiOperatorProcessor @Override public void request(long n) { - if (n > 0) { - Subscriptions.add(requested, n); - drain(); - } + Subscriptions.add(requested, n); + drain(); } @Override diff --git a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiFilterOp.java b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSelectWhereOp.java similarity index 75% rename from implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiFilterOp.java rename to implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSelectWhereOp.java index f11201810..819ffb67d 100644 --- a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiFilterOp.java +++ b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSelectWhereOp.java @@ -15,29 +15,27 @@ * * @param the type of item */ -public class MultiFilterOp extends AbstractMultiOperator { +public class MultiSelectWhereOp extends AbstractMultiOperator { private final Predicate predicate; - public MultiFilterOp(Multi upstream, Predicate predicate) { + public MultiSelectWhereOp(Multi upstream, Predicate predicate) { super(upstream); this.predicate = ParameterValidation.nonNull(predicate, "predicate"); } @Override - public void subscribe(MultiSubscriber downstream) { - if (downstream == null) { - throw new NullPointerException("The subscriber must not be `null`"); - } - upstream.subscribe().withSubscriber(new MultiFilterProcessor<>(downstream, predicate)); + public void subscribe(MultiSubscriber subscriber) { + ParameterValidation.nonNullNpe(subscriber, "subscriber"); + upstream.subscribe().withSubscriber(new MultiSelectWhereProcessor<>(subscriber, predicate)); } - static final class MultiFilterProcessor extends MultiOperatorProcessor { + static final class MultiSelectWhereProcessor extends MultiOperatorProcessor { private final Predicate predicate; private boolean requestedMax = false; - MultiFilterProcessor(MultiSubscriber downstream, Predicate predicate) { + MultiSelectWhereProcessor(MultiSubscriber downstream, Predicate predicate) { super(downstream); this.predicate = predicate; } diff --git a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSkipOp.java b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSkipFirstOp.java similarity index 76% rename from implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSkipOp.java rename to implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSkipFirstOp.java index 41eaa7364..c044e352c 100644 --- a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSkipOp.java +++ b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSkipFirstOp.java @@ -12,11 +12,11 @@ * Skips the first N items from upstream. * Failures and completions are propagated. */ -public final class MultiSkipOp extends AbstractMultiOperator { +public final class MultiSkipFirstOp extends AbstractMultiOperator { private final long numberOfItems; - public MultiSkipOp(Multi upstream, long numberOfItems) { + public MultiSkipFirstOp(Multi upstream, long numberOfItems) { super(upstream); this.numberOfItems = ParameterValidation.positiveOrZero(numberOfItems, "numberOfItems"); } @@ -24,17 +24,18 @@ public MultiSkipOp(Multi upstream, long numberOfItems) { @Override public void subscribe(MultiSubscriber actual) { if (numberOfItems == 0) { - upstream.subscribe().withSubscriber(actual); + // Pass-through + upstream.subscribe(actual); } else { - upstream.subscribe().withSubscriber(new SkipProcessor<>(actual, numberOfItems)); + upstream.subscribe(new SkipFirstProcessor<>(actual, numberOfItems)); } } - static final class SkipProcessor extends MultiOperatorProcessor { + static final class SkipFirstProcessor extends MultiOperatorProcessor { private final AtomicLong remaining; - SkipProcessor(MultiSubscriber downstream, long items) { + SkipFirstProcessor(MultiSubscriber downstream, long items) { super(downstream); this.remaining = new AtomicLong(items); } diff --git a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSkipUntilOp.java b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSkipFirstUntilOp.java similarity index 68% rename from implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSkipUntilOp.java rename to implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSkipFirstUntilOp.java index 53ee06430..ab1b387e2 100644 --- a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSkipUntilOp.java +++ b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSkipFirstUntilOp.java @@ -11,27 +11,27 @@ * * @param the type of item */ -public final class MultiSkipUntilOp extends AbstractMultiOperator { +public final class MultiSkipFirstUntilOp extends AbstractMultiOperator { private final Predicate predicate; - public MultiSkipUntilOp(Multi upstream, Predicate predicate) { + public MultiSkipFirstUntilOp(Multi upstream, Predicate predicate) { super(upstream); this.predicate = ParameterValidation.nonNull(predicate, "predicate"); } @Override - public void subscribe(MultiSubscriber actual) { - ParameterValidation.nonNullNpe(actual, "subscriber"); - upstream.subscribe().withSubscriber(new SkipUntilProcessor<>(actual, predicate)); + public void subscribe(MultiSubscriber subscriber) { + ParameterValidation.nonNullNpe(subscriber, "subscriber"); + upstream.subscribe(new MultiSkipFirstUntilProcessor<>(subscriber, predicate)); } - static final class SkipUntilProcessor extends MultiOperatorProcessor { + static final class MultiSkipFirstUntilProcessor extends MultiOperatorProcessor { private final Predicate predicate; private boolean gateOpen = false; - SkipUntilProcessor(MultiSubscriber downstream, Predicate predicate) { + MultiSkipFirstUntilProcessor(MultiSubscriber downstream, Predicate predicate) { super(downstream); this.predicate = predicate; } diff --git a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSkipRepetitionsOp.java b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSkipRepetitionsOp.java new file mode 100644 index 000000000..b75c34604 --- /dev/null +++ b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSkipRepetitionsOp.java @@ -0,0 +1,86 @@ +package io.smallrye.mutiny.operators.multi; + +import static io.smallrye.mutiny.helpers.ParameterValidation.nonNullNpe; + +import java.util.Comparator; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.subscription.MultiSubscriber; + +/** + * Eliminates the duplicated items from the upstream. + * + * @param the type of items + */ +public final class MultiSkipRepetitionsOp extends AbstractMultiOperator { + + private final Comparator comparator; + + public MultiSkipRepetitionsOp(Multi upstream) { + this(upstream, null); + } + + public MultiSkipRepetitionsOp(Multi upstream, Comparator comparator) { + super(upstream); + this.comparator = comparator; + } + + @Override + public void subscribe(MultiSubscriber subscriber) { + nonNullNpe(subscriber, "subscriber"); + upstream.subscribe().withSubscriber(new MultiSkipRepetitionsProcessor<>(subscriber, comparator)); + } + + static final class MultiSkipRepetitionsProcessor extends MultiOperatorProcessor { + + private final Comparator comparator; + private T last; + + public MultiSkipRepetitionsProcessor(MultiSubscriber subscriber, Comparator comparator) { + super(subscriber); + if (comparator == null) { + this.comparator = (a, b) -> a.equals(b) ? 0 : 1; + } else { + this.comparator = comparator; + } + + } + + @Override + public void onItem(T t) { + if (isDone()) { + return; + } + try { + if (last == null || comparator.compare(last, t) != 0) { + last = t; + downstream.onItem(t); + } else { + // Request the next one, as that item is dropped. + request(1); + } + } catch (Exception e) { + onFailure(e); + } + } + + @Override + public void onFailure(Throwable t) { + super.onFailure(t); + last = null; + } + + @Override + public void onCompletion() { + super.onCompletion(); + last = null; + } + + @Override + public void cancel() { + super.cancel(); + last = null; + } + } + +} diff --git a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSkipUntilPublisherOp.java b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSkipUntilOtherOp.java similarity index 95% rename from implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSkipUntilPublisherOp.java rename to implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSkipUntilOtherOp.java index 35d251b8b..2e8f6aed2 100644 --- a/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSkipUntilPublisherOp.java +++ b/implementation/src/main/java/io/smallrye/mutiny/operators/multi/MultiSkipUntilOtherOp.java @@ -20,11 +20,11 @@ * @param the type of items emitted by the upstream (and propagated downstream) * @param the type of items emitted by the other publisher */ -public final class MultiSkipUntilPublisherOp extends AbstractMultiOperator { +public final class MultiSkipUntilOtherOp extends AbstractMultiOperator { private final Publisher other; - public MultiSkipUntilPublisherOp(Multi upstream, Publisher other) { + public MultiSkipUntilOtherOp(Multi upstream, Publisher other) { super(upstream); this.other = ParameterValidation.nonNull(other, "other"); } diff --git a/implementation/src/test/java/io/smallrye/mutiny/groups/UniOnFailureRetryTest.java b/implementation/src/test/java/io/smallrye/mutiny/groups/UniOnFailureRetryTest.java index 85cfa4926..b718b3244 100644 --- a/implementation/src/test/java/io/smallrye/mutiny/groups/UniOnFailureRetryTest.java +++ b/implementation/src/test/java/io/smallrye/mutiny/groups/UniOnFailureRetryTest.java @@ -105,7 +105,7 @@ public void testRetryWhenWithCompletionInTriggerStream() { e.complete("done"); } }) - .onFailure().retry().when(stream -> stream.transform().byTakingFirstItems(1)) + .onFailure().retry().when(stream -> stream.select().first()) .await().atMost(Duration.ofSeconds(5)); assertThat(value).isNull(); } diff --git a/implementation/src/test/java/io/smallrye/mutiny/infrastructure/MutinySchedulerTest.java b/implementation/src/test/java/io/smallrye/mutiny/infrastructure/MutinySchedulerTest.java index 1a68a536a..350f9eb5e 100644 --- a/implementation/src/test/java/io/smallrye/mutiny/infrastructure/MutinySchedulerTest.java +++ b/implementation/src/test/java/io/smallrye/mutiny/infrastructure/MutinySchedulerTest.java @@ -139,7 +139,7 @@ public void testMultiRetryExpireIn() { public void testTicks() { AtomicReference thread = new AtomicReference<>(); List list = Multi.createFrom().ticks().every(Duration.ofMillis(10)) - .transform().byTakingFirstItems(5) + .select().first(5) .collect().asList() .onItem().invoke(l -> thread.set(Thread.currentThread().getName())) .await().indefinitely(); @@ -153,7 +153,7 @@ public void testCollectionBasedOnDuration() { AtomicReference thread = new AtomicReference<>(); Multi.createFrom().ticks().every(Duration.ofMillis(10)) .group().intoLists().every(Duration.ofMillis(10)) - .transform().byTakingFirstItems(5) + .select().first(5) .collect().asList() .onItem().invoke(l -> thread.set(Thread.currentThread().getName())) .await().indefinitely(); diff --git a/implementation/src/test/java/io/smallrye/mutiny/operators/MultiCreateFromGeneratorTest.java b/implementation/src/test/java/io/smallrye/mutiny/operators/MultiCreateFromGeneratorTest.java index 7f946b2fd..026b43a72 100644 --- a/implementation/src/test/java/io/smallrye/mutiny/operators/MultiCreateFromGeneratorTest.java +++ b/implementation/src/test/java/io/smallrye/mutiny/operators/MultiCreateFromGeneratorTest.java @@ -42,7 +42,7 @@ void generateInfiniteSuite() { emitter.emit(n); return counter; }) - .transform().byTakingFirstItems(6) + .select().first(6) .subscribe().withSubscriber(AssertSubscriber.create(Long.MAX_VALUE)); sub.assertCompleted(); @@ -59,7 +59,7 @@ void generateAndCancel() { emitter.emit(n); return counter; }) - .transform().byTakingFirstItems(6) + .select().first(6) .subscribe().withSubscriber(AssertSubscriber.create()); sub.assertNotTerminated(); @@ -89,7 +89,7 @@ void cancelUpfront() { emitter.emit(n); return counter; }) - .transform().byTakingFirstItems(6) + .select().first(6) .subscribe().withSubscriber(AssertSubscriber.create()); sub.cancel(); @@ -108,7 +108,7 @@ void rejectZeroSubscription() { emitter.emit(n); return counter; }) - .transform().byTakingFirstItems(6) + .select().first(6) .subscribe().withSubscriber(AssertSubscriber.create()); sub.request(0); @@ -125,7 +125,7 @@ void rejectNegativeSubscription() { emitter.emit(n); return counter; }) - .transform().byTakingFirstItems(6) + .select().first(6) .subscribe().withSubscriber(AssertSubscriber.create()); sub.request(-10); diff --git a/implementation/src/test/java/io/smallrye/mutiny/operators/MultiCreateFromItemsTest.java b/implementation/src/test/java/io/smallrye/mutiny/operators/MultiCreateFromItemsTest.java index 805da55bf..23cab72b3 100644 --- a/implementation/src/test/java/io/smallrye/mutiny/operators/MultiCreateFromItemsTest.java +++ b/implementation/src/test/java/io/smallrye/mutiny/operators/MultiCreateFromItemsTest.java @@ -167,7 +167,7 @@ public void testThatMultiBasedOnStreamCannotBeReused() { @Test public void testLimitOnMultiBasedOnStream() { Multi.createFrom().items(() -> IntStream.iterate(0, operand -> operand + 1).boxed()) - .transform().byTakingFirstItems(10) + .select().first(10) .subscribe().withSubscriber(AssertSubscriber.create(Long.MAX_VALUE)) .assertCompleted() .assertItems(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); diff --git a/implementation/src/test/java/io/smallrye/mutiny/operators/MultiDistinctTest.java b/implementation/src/test/java/io/smallrye/mutiny/operators/MultiDistinctTest.java index 0b82bfeea..ff8746154 100644 --- a/implementation/src/test/java/io/smallrye/mutiny/operators/MultiDistinctTest.java +++ b/implementation/src/test/java/io/smallrye/mutiny/operators/MultiDistinctTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; import java.io.IOException; import java.time.Duration; @@ -13,6 +14,8 @@ import java.util.function.Consumer; import org.junit.jupiter.api.Test; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.TestException; @@ -23,15 +26,54 @@ public class MultiDistinctTest { @Test - public void testDistinctWithUpstreamFailure() { - Multi.createFrom(). failure(new IOException("boom")) - .transform().byDroppingDuplicates() + public void testDistinct() { + Multi.createFrom().items(1, 2, 3, 4, 2, 4, 2, 4) + .select().distinct() .subscribe().withSubscriber(AssertSubscriber.create(10)) - .assertFailedWith(IOException.class, "boom"); + .assertCompleted() + .assertItems(1, 2, 3, 4); } @Test - public void testDistinct() { + public void testDistinctWithComparator() { + Multi.createFrom().items(1, 2, 3, 4, 2, 4, 2, 4) + .select().distinct(Integer::compareTo) + .subscribe().withSubscriber(AssertSubscriber.create(10)) + .assertCompleted() + .assertItems(1, 2, 3, 4); + } + + @Test + public void testDistinctWithNullComparator() { + Multi.createFrom().items(1, 2, 3, 4, 2, 4, 2, 4) + .select().distinct(null) + .subscribe().withSubscriber(AssertSubscriber.create(10)) + .assertCompleted() + .assertItems(1, 2, 3, 4); + } + + @Test + public void testDistinctWithComparatorReturningAlways0() { + Multi.createFrom().items(1, 2, 3, 4, 2, 4, 2, 4) + .select().distinct((a, b) -> 0) + .subscribe().withSubscriber(AssertSubscriber.create(10)) + .assertCompleted() + .assertItems(1); + } + + @Test + public void testDistinctWithComparatorReturningAlways1() { + //noinspection ComparatorMethodParameterNotUsed + Multi.createFrom().items(1, 2, 3, 4, 2, 4, 2, 4) + .select().distinct((a, b) -> 1) + .subscribe().withSubscriber(AssertSubscriber.create(10)) + .assertCompleted() + .assertItems(1, 2, 3, 4, 2, 4, 2, 4); + } + + @SuppressWarnings("deprecation") + @Test + public void testDistinctDeprecated() { Multi.createFrom().items(1, 2, 3, 4, 2, 4, 2, 4) .transform().byDroppingDuplicates() .subscribe().withSubscriber(AssertSubscriber.create(10)) @@ -39,33 +81,104 @@ public void testDistinct() { .assertItems(1, 2, 3, 4); } + @Test + public void testDistinctWithUpstreamFailure() { + Multi.createFrom(). failure(new IOException("boom")) + .select().distinct() + .subscribe().withSubscriber(AssertSubscriber.create(10)) + .assertFailedWith(IOException.class, "boom"); + } + + @Test + public void testDistinctWithComparatorWithUpstreamFailure() { + Multi.createFrom(). failure(new IOException("boom")) + .select().distinct(Integer::compareTo) + .subscribe().withSubscriber(AssertSubscriber.create(10)) + .assertFailedWith(IOException.class, "boom"); + } + + @SuppressWarnings("deprecation") + @Test + public void testDistinctWithUpstreamFailureDeprecated() { + Multi.createFrom(). failure(new IOException("boom")) + .transform().byDroppingDuplicates() + .subscribe().withSubscriber(AssertSubscriber.create(10)) + .assertFailedWith(IOException.class, "boom"); + } + @SuppressWarnings("ConstantConditions") @Test - public void testThatNullSubscriberAreRejected() { + public void testThatNullSubscriberAreRejectedDistinct() { assertThrows(NullPointerException.class, () -> Multi.createFrom().items(1, 2, 3, 4, 2, 4, 2, 4) - .transform().byDroppingDuplicates() + .select().distinct() + .subscribe(null)); + } + + @SuppressWarnings("ConstantConditions") + @Test + public void testThatNullSubscriberAreRejectedSkipRepetitions() { + assertThrows(NullPointerException.class, () -> Multi.createFrom().items(1, 2, 3, 4, 2, 4, 2, 4) + .skip().repetitions() .subscribe(null)); } @Test public void testDistinctOnAStreamWithoutDuplicates() { Multi.createFrom().range(1, 5) - .transform().byDroppingDuplicates() + .select().distinct() .subscribe().withSubscriber(AssertSubscriber.create(10)) .assertCompleted() .assertItems(1, 2, 3, 4); } @Test - public void testDropRepetitionsWithUpstreamFailure() { + public void testSkipRepetitionsWithUpstreamFailure() { Multi.createFrom(). failure(new IOException("boom")) - .transform().byDroppingRepetitions() + .skip().repetitions() .subscribe().withSubscriber(AssertSubscriber.create(10)) .assertFailedWith(IOException.class, "boom"); } @Test - public void testDropRepetitions() { + public void testSkipRepetitions() { + Multi.createFrom().items(1, 2, 3, 4, 4, 2, 2, 4, 1, 1, 2, 4) + .skip().repetitions() + .subscribe().withSubscriber(AssertSubscriber.create(10)) + .assertCompleted() + .assertItems(1, 2, 3, 4, 2, 4, 1, 2, 4); + } + + @Test + public void testSkipRepetitionsWithComparator() { + Multi.createFrom().items(1, 2, 3, 4, 4, 2, 2, 4, 1, 1, 2, 4) + .skip().repetitions(Integer::compareTo) + .subscribe().withSubscriber(AssertSubscriber.create(10)) + .assertCompleted() + .assertItems(1, 2, 3, 4, 2, 4, 1, 2, 4); + } + + @Test + public void testSkipRepetitionsWithComparatorAlwaysReturning0() { + Multi.createFrom().items(1, 2, 3, 4, 4, 2, 2, 4, 1, 1, 2, 4) + .skip().repetitions((a, b) -> 0) + .subscribe().withSubscriber(AssertSubscriber.create(10)) + .assertCompleted() + .assertItems(1); + } + + @Test + public void testSkipRepetitionsWithComparatorAlwaysReturning1() { + //noinspection ComparatorMethodParameterNotUsed + Multi.createFrom().items(1, 2, 3, 4, 4, 2, 2, 4, 1, 1, 2, 4) + .skip().repetitions((a, b) -> 1) + .subscribe().withSubscriber(AssertSubscriber.create(20)) + .assertCompleted() + .assertItems(1, 2, 3, 4, 4, 2, 2, 4, 1, 1, 2, 4); + } + + @SuppressWarnings("deprecation") + @Test + public void testDroppedRepetitionsDeprecated() { Multi.createFrom().items(1, 2, 3, 4, 4, 2, 2, 4, 1, 1, 2, 4) .transform().byDroppingRepetitions() .subscribe().withSubscriber(AssertSubscriber.create(10)) @@ -74,7 +187,7 @@ public void testDropRepetitions() { } @Test - public void testDropRepetitionsWithCancellation() { + public void testSkipRepetitionsWithCancellation() { AtomicLong count = new AtomicLong(); AtomicBoolean cancelled = new AtomicBoolean(); AssertSubscriber subscriber = Multi.createFrom().ticks().every(Duration.ofMillis(1)) @@ -86,7 +199,7 @@ public void testDropRepetitionsWithCancellation() { return l - 1; } }) - .transform().byDroppingRepetitions() + .skip().repetitions() .subscribe().withSubscriber(AssertSubscriber.create(Long.MAX_VALUE)); await().until(() -> subscriber.getItems().size() >= 10); @@ -95,7 +208,7 @@ public void testDropRepetitionsWithCancellation() { } @Test - public void testDropRepetitionsWithImmediateCancellation() { + public void testSkipRepetitionsWithImmediateCancellation() { AtomicLong count = new AtomicLong(); AtomicBoolean cancelled = new AtomicBoolean(); Multi.createFrom().ticks().every(Duration.ofMillis(1)) @@ -107,7 +220,7 @@ public void testDropRepetitionsWithImmediateCancellation() { return l - 1; } }) - .transform().byDroppingRepetitions() + .skip().repetitions() .subscribe().withSubscriber(new AssertSubscriber<>(Long.MAX_VALUE, true)); assertThat(cancelled).isTrue(); @@ -115,9 +228,9 @@ public void testDropRepetitionsWithImmediateCancellation() { } @Test - public void testDropRepetitionsOnAStreamWithoutDuplicates() { + public void testSkipRepetitionsOnAStreamWithoutDuplicates() { Multi.createFrom().range(1, 5) - .transform().byDroppingRepetitions() + .skip().repetitions() .subscribe().withSubscriber(AssertSubscriber.create(10)) .assertCompleted() .assertItems(1, 2, 3, 4); @@ -128,7 +241,7 @@ public void testNoEmissionAfterCancellation() { AtomicReference> emitter = new AtomicReference<>(); AssertSubscriber subscriber = Multi.createFrom().emitter( (Consumer>) emitter::set) - .transform().byDroppingDuplicates() + .select().distinct() .subscribe().withSubscriber(AssertSubscriber.create(10)); subscriber.assertSubscribed() @@ -143,23 +256,116 @@ public void testNoEmissionAfterCancellation() { } @Test - public void testExceptionInComparator() { - AtomicReference> emitter = new AtomicReference<>(); - AssertSubscriber subscriber = Multi.createFrom().emitter( - (Consumer>) emitter::set) - .transform().byDroppingDuplicates() + public void testDistinctExceptionInHashCode() { + AtomicReference> emitter = new AtomicReference<>(); + AssertSubscriber subscriber = Multi.createFrom().emitter( + (Consumer>) emitter::set) + .select().distinct() .subscribe().withSubscriber(AssertSubscriber.create(10)); subscriber.assertSubscribed() .assertNotTerminated(); - BadlyComparableStuff item1 = new BadlyComparableStuff(); - BadlyComparableStuff item2 = new BadlyComparableStuff(); + BadlyComparableStuffOnHashCode item1 = new BadlyComparableStuffOnHashCode(); + BadlyComparableStuffOnHashCode item2 = new BadlyComparableStuffOnHashCode(); emitter.get().emit(item1).emit(item2).complete(); subscriber.assertFailedWith(TestException.class, "boom"); } - private static class BadlyComparableStuff { + @Test + public void testDistinctExceptionInComparator() { + AtomicReference> emitter = new AtomicReference<>(); + AssertSubscriber subscriber = Multi.createFrom().emitter( + (Consumer>) emitter::set) + .select().distinct((a, b) -> { + throw new TestException("boom"); + }) + .subscribe().withSubscriber(AssertSubscriber.create(10)); + + subscriber.assertSubscribed() + .assertNotTerminated(); + + emitter.get().emit(1).emit(2).complete(); + subscriber.assertFailedWith(TestException.class, "boom"); + } + + @Test + public void testSkipRepetitionsExceptionInEquals() { + AtomicReference> emitter = new AtomicReference<>(); + AssertSubscriber subscriber = Multi.createFrom().emitter( + (Consumer>) emitter::set) + .skip().repetitions() + .subscribe().withSubscriber(AssertSubscriber.create(10)); + + subscriber.assertSubscribed() + .assertNotTerminated(); + + BadlyComparableStuffOnEquals item1 = new BadlyComparableStuffOnEquals(); + BadlyComparableStuffOnEquals item2 = new BadlyComparableStuffOnEquals(); + emitter.get().emit(item1).emit(item2).complete(); + subscriber + .await() + .assertFailedWith(TestException.class, "boom"); + } + + @Test + public void testSkipRepetitionsExceptionInComparator() { + AtomicReference> emitter = new AtomicReference<>(); + AssertSubscriber subscriber = Multi.createFrom().emitter( + (Consumer>) emitter::set) + .skip().repetitions((a, b) -> { + throw new TestException("boom"); + }) + .subscribe().withSubscriber(AssertSubscriber.create(10)); + + subscriber.assertSubscribed() + .assertNotTerminated(); + + emitter.get().emit(1).emit(2).complete(); + subscriber + .await() + .assertFailedWith(TestException.class, "boom"); + } + + @Test + public void testOnItemAfterCancellation() { + AtomicReference> ref = new AtomicReference<>(); + AbstractMulti upstream = new AbstractMulti() { + @Override + public void subscribe(Subscriber subscriber) { + subscriber.onSubscribe(mock(Subscription.class)); + ref.set(subscriber); + } + }; + + upstream + .select().distinct() + .subscribe().withSubscriber(AssertSubscriber.create(1)) + .run(() -> ref.get().onNext(1)) + .assertItems(1) + .request(1) + .run(() -> ref.get().onNext(1)) + .run(() -> ref.get().onNext(3)) + .assertItems(1, 3) + .cancel() + .run(() -> ref.get().onNext(4)) + .assertItems(1, 3); + + upstream + .skip().repetitions() + .subscribe().withSubscriber(AssertSubscriber.create(1)) + .run(() -> ref.get().onNext(1)) + .assertItems(1) + .request(1) + .run(() -> ref.get().onNext(1)) + .run(() -> ref.get().onNext(3)) + .assertItems(1, 3) + .cancel() + .run(() -> ref.get().onNext(4)) + .assertItems(1, 3); + } + + private static class BadlyComparableStuffOnHashCode { @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") @Override @@ -173,4 +379,12 @@ public int hashCode() { } } + private static class BadlyComparableStuffOnEquals { + + @Override + public boolean equals(Object obj) { + throw new TestException("boom"); + } + } + } diff --git a/implementation/src/test/java/io/smallrye/mutiny/operators/MultiGroupTest.java b/implementation/src/test/java/io/smallrye/mutiny/operators/MultiGroupTest.java index e94bdb6a6..61266d6ee 100644 --- a/implementation/src/test/java/io/smallrye/mutiny/operators/MultiGroupTest.java +++ b/implementation/src/test/java/io/smallrye/mutiny/operators/MultiGroupTest.java @@ -200,7 +200,7 @@ public void testAsListsWithDuration() { @Test public void testAsListsWithDurationWithCompletion() { Multi publisher = Multi.createFrom().publisher(Multi.createFrom().ticks().every(Duration.ofMillis(2))) - .transform().byTakingFirstItems(10); + .select().first(10); AssertSubscriber> subscriber = publisher.group().intoLists().every(Duration.ofMillis(100)) .subscribe() .withSubscriber(AssertSubscriber.create(100)); @@ -211,7 +211,7 @@ public void testAsListsWithDurationWithCompletion() { @Test public void testAsListsWithDurationWithFailure() { Multi publisher = Multi.createFrom().publisher(Multi.createFrom().ticks().every(Duration.ofMillis(2))) - .transform().byTakingFirstItems(10) + .select().first(10) .onCompletion().failWith(new IOException("boom")); AssertSubscriber> subscriber = publisher.group().intoLists().every(Duration.ofMillis(100)) .subscribe() diff --git a/implementation/src/test/java/io/smallrye/mutiny/operators/MultiIfEmptyTest.java b/implementation/src/test/java/io/smallrye/mutiny/operators/MultiIfEmptyTest.java index cd3903317..2434c56e9 100644 --- a/implementation/src/test/java/io/smallrye/mutiny/operators/MultiIfEmptyTest.java +++ b/implementation/src/test/java/io/smallrye/mutiny/operators/MultiIfEmptyTest.java @@ -66,7 +66,8 @@ public void testIfEmptyContinueWithOne() { @Test public void testIfEmptyBecauseOfSkipContinueWithOne() { - Multi.createFrom().items(1, 2, 3).transform().bySkippingFirstItems(5) + Multi.createFrom().items(1, 2, 3) + .skip().first(5) .onCompletion().ifEmpty().continueWith(25) .subscribe().withSubscriber(AssertSubscriber.create(7)) .assertCompleted() diff --git a/implementation/src/test/java/io/smallrye/mutiny/operators/MultiOnFailureRetryWhenTest.java b/implementation/src/test/java/io/smallrye/mutiny/operators/MultiOnFailureRetryWhenTest.java index 83e8ebfaa..61714eab8 100644 --- a/implementation/src/test/java/io/smallrye/mutiny/operators/MultiOnFailureRetryWhenTest.java +++ b/implementation/src/test/java/io/smallrye/mutiny/operators/MultiOnFailureRetryWhenTest.java @@ -140,8 +140,7 @@ public void testAfterOnRetryAndCompletion() { .onSubscribe().invoke(sub -> sourceSubscribed.set(true)); Multi retry = source - .onFailure().retry().when(other -> other - .transform().byTakingFirstItems(1)); + .onFailure().retry().when(other -> other.select().first()); AssertSubscriber subscriber = retry.subscribe() .withSubscriber(AssertSubscriber.create(10)); diff --git a/implementation/src/test/java/io/smallrye/mutiny/operators/MultiSelectFirstOrLast.java b/implementation/src/test/java/io/smallrye/mutiny/operators/MultiSelectFirstOrLast.java new file mode 100644 index 000000000..d3bea49ba --- /dev/null +++ b/implementation/src/test/java/io/smallrye/mutiny/operators/MultiSelectFirstOrLast.java @@ -0,0 +1,508 @@ +package io.smallrye.mutiny.operators; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import java.io.IOException; +import java.time.Duration; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Predicate; + +import org.junit.jupiter.api.Test; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.TestException; +import io.smallrye.mutiny.helpers.test.AssertSubscriber; +import io.smallrye.mutiny.subscription.MultiEmitter; +import io.smallrye.mutiny.subscription.MultiSubscriber; + +public class MultiSelectFirstOrLast { + + @Test + public void testSelectFirstWithLimit() { + List list = Multi.createFrom().range(1, 5).select().first(2) + .collectItems().asList().await().indefinitely(); + + assertThat(list).containsExactly(1, 2); + } + + @Test + public void testSelectFirst() { + List list = Multi.createFrom().range(1, 5).select().first(1) + .collectItems().asList().await().indefinitely(); + + assertThat(list).containsExactly(1); + } + + @SuppressWarnings("deprecation") + @Test + public void testSelectFirstDeprecated() { + List list = Multi.createFrom().range(1, 5).transform().byTakingFirstItems(2) + .collectItems().asList().await().indefinitely(); + + assertThat(list).containsExactly(1, 2); + } + + @Test + public void testSelectFirst0() { + List list = Multi.createFrom().range(1, 5).select().first(0) + .collectItems().asList().await().indefinitely(); + + assertThat(list).isEmpty(); + } + + @Test + public void testSelectLastWithLimit() { + List list = Multi.createFrom().range(1, 5).select().last(2) + .collectItems().asList().await().indefinitely(); + + assertThat(list).containsExactly(3, 4); + } + + @Test + public void testSelectLast() { + List list = Multi.createFrom().range(1, 5).select().last() + .collectItems().asList().await().indefinitely(); + + assertThat(list).containsExactly(4); + } + + @SuppressWarnings("deprecation") + @Test + public void testSelectLastDeprecated() { + List list = Multi.createFrom().range(1, 5).transform().byTakingLastItems(2) + .collectItems().asList().await().indefinitely(); + + assertThat(list).containsExactly(3, 4); + } + + @Test + public void testSelectLastWith0() { + List list = Multi.createFrom().range(1, 5) + .select().last(0) + .collectItems().asList().await().indefinitely(); + + assertThat(list).isEmpty(); + } + + @Test + public void testSelectFirstOnUpstreamFailure() { + Multi.createFrom(). failure(new IOException("boom")) + .select().first() + .subscribe().withSubscriber(AssertSubscriber.create(10)) + .assertFailedWith(IOException.class, "boom") + .assertHasNotReceivedAnyItem(); + } + + @Test + public void testSelectLastOnUpstreamFailure() { + Multi.createFrom(). failure(new IOException("boom")) + .select().last() + .subscribe().withSubscriber(AssertSubscriber.create(10)) + .assertFailedWith(IOException.class, "boom") + .assertHasNotReceivedAnyItem(); + } + + @Test + public void testSelectAll() { + Multi.createFrom().range(1, 5).select().first(4) + .subscribe().withSubscriber(AssertSubscriber.create(10)) + .assertCompleted() + .assertItems(1, 2, 3, 4); + } + + @Test + public void testSelectLastAll() { + Multi.createFrom().range(1, 5).select().last(4) + .subscribe().withSubscriber(AssertSubscriber.create(10)) + .assertCompleted() + .assertItems(1, 2, 3, 4); + } + + @Test + public void testInvalidLimit() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> Multi.createFrom().items(1, 2, 3).select().first(-1)); + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> Multi.createFrom().items(1, 2, 3).select().last(-1)); + } + + @Test + public void testSelectLastWithBackPressure() { + AssertSubscriber subscriber = AssertSubscriber.create(0); + + AtomicReference> emitter = new AtomicReference<>(); + Multi.createFrom(). emitter(emitter::set) + .select().last(3) + .subscribe(subscriber); + + subscriber.assertNotTerminated() + .assertHasNotReceivedAnyItem(); + + emitter.get().emit(1).emit(2); + + subscriber.request(2) + .assertNotTerminated() + .assertHasNotReceivedAnyItem(); + + emitter.get().emit(3).emit(4); + + subscriber.request(5) + .assertNotTerminated() + .assertHasNotReceivedAnyItem(); + + emitter.get().emit(5).emit(6).emit(7).emit(8).emit(9).emit(10).complete(); + + subscriber.request(5) + .assertCompleted() + .assertItems(8, 9, 10); + } + + @Test + public void testSelectSomeLastItems() { + AssertSubscriber subscriber = AssertSubscriber.create(Long.MAX_VALUE); + + Multi.createFrom().range(1, 11) + .select().last(3) + .subscribe(subscriber); + + subscriber.assertCompleted() + .assertItems(8, 9, 10); + } + + @Test + public void testSelectWhileWithMethodThrowingException() { + Multi.createFrom().range(1, 10).select().first(i -> { + throw new IllegalStateException("boom"); + }).subscribe().withSubscriber(AssertSubscriber.create(10)) + .assertFailedWith(IllegalStateException.class, "boom"); + } + + @Test + public void testSelectWhileWithUpstreamFailure() { + Multi.createFrom(). failure(new IOException("boom")) + .select().first(i -> i < 5) + .subscribe().withSubscriber(AssertSubscriber.create(10)) + .assertFailedWith(IOException.class, "boom"); + } + + @Test + public void testSelectWhileWithNullMethod() { + assertThrows(IllegalArgumentException.class, + () -> Multi.createFrom().nothing().select().first((Predicate) null)); + } + + @Test + public void testSelectWhile() { + Multi.createFrom().range(1, 10).select().first(i -> i < 5) + .subscribe().withSubscriber(AssertSubscriber.create(10)) + .assertCompleted() + .assertItems(1, 2, 3, 4); + } + + @Test + public void testSelectWhileNone() { + Multi.createFrom().items(1, 2, 3, 4).select().first(i -> false) + .subscribe().withSubscriber(AssertSubscriber.create(10)) + .assertCompleted() + .assertHasNotReceivedAnyItem(); + } + + @Test + public void testSelectWhileAll() { + Multi.createFrom().items(1, 2, 3, 4).select().first(i -> true) + .subscribe().withSubscriber(AssertSubscriber.create(10)) + .assertCompleted() + .assertItems(1, 2, 3, 4); + } + + @Test + public void testSelectWhileSomeWithBackPressure() { + AssertSubscriber subscriber = Multi.createFrom().items(1, 2, 3, 4) + .select().first(i -> i < 3) + .subscribe().withSubscriber(AssertSubscriber.create(0)); + + subscriber.assertNotTerminated() + .assertHasNotReceivedAnyItem(); + + subscriber.request(1); + + subscriber.assertNotTerminated() + .assertItems(1); + + subscriber.request(2); + + subscriber.assertCompleted() + .assertItems(1, 2); + } + + @Test + public void testLimitingInfiniteStream() { + Multi.createFrom().ticks().every(Duration.ofMillis(2)) + .select().first(5) + .subscribe().withSubscriber(AssertSubscriber.create(Long.MAX_VALUE)) + .await() + .assertCompleted() + .assertItems(0L, 1L, 2L, 3L, 4L); + } + + @Test + public void testSelectWithNullDuration() { + assertThrows(IllegalArgumentException.class, + () -> Multi.createFrom().nothing().select().first((Duration) null)); + } + + @Test + public void testSelectByTime() { + AssertSubscriber subscriber = Multi.createFrom().range(1, 100) + .select().first(Duration.ofMillis(1000)) + .subscribe().withSubscriber(AssertSubscriber.create(10)) + .await() + .assertCompleted(); + + assertThat(subscriber.getItems()).hasSize(10); + } + + @Test + public void testSelectByTimeWithFailure() { + Multi multi = Multi.createBy().concatenating().streams( + Multi.createFrom().range(1, 5), + Multi.createFrom().failure(new TestException("boom")), + Multi.createFrom().range(5, 10)); + AssertSubscriber subscriber = multi + .select().first(Duration.ofMillis(1000)) + .subscribe().withSubscriber(AssertSubscriber.create(100)) + .await() + .assertFailedWith(TestException.class, "boom"); + + assertThat(subscriber.getItems()).hasSize(4); + } + + @Test + public void testSelectByTimeWithCancellation() { + Multi multi = Multi.createBy().concatenating().streams( + Multi.createFrom().range(1, 5), + Multi.createFrom().range(5, 10)); + AssertSubscriber subscriber = multi + .select().first(Duration.ofMillis(1000)) + .subscribe().withSubscriber(AssertSubscriber.create(4)); + + await().until(() -> subscriber.getItems().size() == 4); + subscriber.cancel(); + + assertThat(subscriber.getItems()).hasSize(4); + } + + @Test + public void testSelectByTimeWithImmediateCancellation() { + Multi multi = Multi.createBy().concatenating().streams( + Multi.createFrom().range(1, 5), + Multi.createFrom().range(5, 10)); + AssertSubscriber subscriber = multi + .select().first(Duration.ofMillis(1000)) + .subscribe().withSubscriber(new AssertSubscriber<>(4, true)); + + subscriber.assertSubscribed() + .assertNotTerminated() + .assertHasNotReceivedAnyItem(); + } + + @Test + public void testSelectByTimeWithRogueUpstreamSendingFailureAfterCompletion() { + Multi rogue = new AbstractMulti() { + @Override + public void subscribe(MultiSubscriber subscriber) { + subscriber.onItem(1); + subscriber.onItem(2); + subscriber.onComplete(); + subscriber.onItem(3); + subscriber.onError(new IOException("boom")); + } + }; + AssertSubscriber subscriber = rogue + .select().first(Duration.ofMillis(1000)) + .subscribe().withSubscriber(AssertSubscriber.create(100)) + .await() + .assertCompleted(); + + assertThat(subscriber.getItems()).hasSize(2); + } + + @Test + public void testSelectByTimeWithRogueUpstreamSendingCompletionAfterFailure() { + Multi rogue = new AbstractMulti() { + @Override + public void subscribe(MultiSubscriber subscriber) { + subscriber.onItem(1); + subscriber.onItem(2); + subscriber.onError(new IOException("boom")); + subscriber.onItem(3); + subscriber.onComplete(); + } + }; + AssertSubscriber subscriber = rogue + .select().first(Duration.ofMillis(1000)) + .subscribe().withSubscriber(AssertSubscriber.create(100)) + .await() + .assertFailedWith(IOException.class, "boom"); + + assertThat(subscriber.getItems()).hasSize(2); + } + + @Test + public void testSkipByTimeWithInvalidDuration() { + assertThrows(IllegalArgumentException.class, + () -> Multi.createFrom().item(1).select().first(Duration.ofMillis(-1))); + } + + @Test + public void testSelectFirstWithTwoSubscribers() { + AbstractMulti upstream = new AbstractMulti() { + @Override + public void subscribe(Subscriber subscriber) { + Subscription subscription1 = mock(Subscription.class); + Subscription subscription2 = mock(Subscription.class); + subscriber.onSubscribe(subscription1); + subscriber.onSubscribe(subscription2); + verify(subscription2).cancel(); + subscriber.onNext(1); + subscriber.onNext(2); + subscriber.onNext(3); + subscriber.onComplete(); + } + }; + + AssertSubscriber subscriber = upstream + .select().first(2) + .subscribe().withSubscriber(AssertSubscriber.create(5)); + + subscriber.assertSubscribed() + .assertCompleted() + .assertItems(1, 2); + } + + @Test + public void testSelectLastWithTwoSubscribers() { + AbstractMulti upstream = new AbstractMulti() { + @Override + public void subscribe(Subscriber subscriber) { + Subscription subscription1 = mock(Subscription.class); + Subscription subscription2 = mock(Subscription.class); + subscriber.onSubscribe(subscription1); + subscriber.onSubscribe(subscription2); + verify(subscription2).cancel(); + subscriber.onNext(1); + subscriber.onNext(2); + subscriber.onNext(3); + subscriber.onComplete(); + } + }; + + AssertSubscriber subscriber = upstream + .select().last(2) + .subscribe().withSubscriber(AssertSubscriber.create(5)); + + subscriber.assertSubscribed() + .assertCompleted() + .assertItems(2, 3); + } + + @Test + public void testSelectFirstWith0() { + AbstractMulti upstream = new AbstractMulti() { + @Override + public void subscribe(Subscriber subscriber) { + Subscription subscription = mock(Subscription.class); + subscriber.onSubscribe(subscription); + subscriber.onNext(1); + subscriber.onNext(2); + subscriber.onNext(3); + subscriber.onComplete(); + } + }; + + AssertSubscriber subscriber = upstream + .select().first(0) + .subscribe().withSubscriber(AssertSubscriber.create(5)); + + subscriber.assertSubscribed() + .assertCompleted() + .assertHasNotReceivedAnyItem(); + } + + @Test + public void testSelectLastWithZero() { + AbstractMulti upstream = new AbstractMulti() { + @Override + public void subscribe(Subscriber subscriber) { + Subscription subscription = mock(Subscription.class); + subscriber.onSubscribe(subscription); + subscriber.onNext(1); + subscriber.onNext(2); + subscriber.onNext(3); + subscriber.onComplete(); + } + }; + + AssertSubscriber subscriber = upstream + .select().last(0) + .subscribe().withSubscriber(AssertSubscriber.create(5)); + + subscriber.assertSubscribed() + .assertCompleted() + .assertHasNotReceivedAnyItem(); + } + + @Test + public void testInvalidRequests() { + AssertSubscriber sub1 = AssertSubscriber.create(); + AssertSubscriber sub2 = AssertSubscriber.create(); + Multi.createFrom().range(1, 4) + .select().first(2) + .subscribe(sub1); + + Multi.createFrom().range(1, 4) + .select().last(2) + .subscribe(sub2); + + sub1.request(-1) + .assertFailedWith(IllegalArgumentException.class, "request"); + sub2.request(-1) + .assertFailedWith(IllegalArgumentException.class, "request"); + } + + @Test + public void testRequestGreaterThanNumberOfItems() { + AssertSubscriber subscriber = Multi.createFrom().range(1, 100) + .select().first(2) + .subscribe().withSubscriber(AssertSubscriber.create(5)); + + subscriber + .assertCompleted() + .assertItems(1, 2); + } + + @Test + public void testRequestSmallerThanNumberOfItems() { + AssertSubscriber subscriber = Multi.createFrom().range(1, 100) + .select().first(2) + .subscribe().withSubscriber(AssertSubscriber.create(0)); + + subscriber + .assertSubscribed() + .request(1) + .assertItems(1) + .request(1) + .assertCompleted() + .assertItems(1, 2); + } + +} diff --git a/implementation/src/test/java/io/smallrye/mutiny/operators/MultiFilterTest.java b/implementation/src/test/java/io/smallrye/mutiny/operators/MultiSelectWhereAndWhenTest.java similarity index 68% rename from implementation/src/test/java/io/smallrye/mutiny/operators/MultiFilterTest.java rename to implementation/src/test/java/io/smallrye/mutiny/operators/MultiSelectWhereAndWhenTest.java index f77e780de..0c0a7a2af 100644 --- a/implementation/src/test/java/io/smallrye/mutiny/operators/MultiFilterTest.java +++ b/implementation/src/test/java/io/smallrye/mutiny/operators/MultiSelectWhereAndWhenTest.java @@ -16,30 +16,38 @@ import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.helpers.spies.MultiOnCancellationSpy; +import io.smallrye.mutiny.helpers.spies.Spy; import io.smallrye.mutiny.helpers.test.AssertSubscriber; -import io.smallrye.mutiny.operators.multi.MultiFilterOp; +import io.smallrye.mutiny.operators.multi.MultiSelectWhereOp; import io.smallrye.mutiny.operators.multi.processors.BroadcastProcessor; import io.smallrye.mutiny.test.Mocks; -public class MultiFilterTest { +public class MultiSelectWhereAndWhenTest { @Test public void testThatPredicateCannotBeNull() { assertThrows(IllegalArgumentException.class, () -> Multi.createFrom().range(1, 4) - .transform().byFilteringItemsWith(null)); + .select().where(null)); + } + + @Test + public void testThatLimitCannotBeNegative() { + assertThrows(IllegalArgumentException.class, () -> Multi.createFrom().range(1, 4) + .select().where(x -> x == 2, -1)); } @Test public void testThatFunctionCannotBeNull() { assertThrows(IllegalArgumentException.class, () -> Multi.createFrom().range(1, 4) - .transform().byTestingItemsWith(null)); + .select().when(null)); } @Test public void testThatSubscriberCannotBeNull() { assertThrows(NullPointerException.class, () -> { Multi multi = Multi.createFrom().range(1, 4); - MultiFilterOp filter = new MultiFilterOp<>(multi, x -> x % 2 == 0); + MultiSelectWhereOp filter = new MultiSelectWhereOp<>(multi, x -> x % 2 == 0); filter.subscribe(null); }); } @@ -55,6 +63,38 @@ public void cannotRequestZeroItems() { @Test public void testFilteringWithPredicate() { + Predicate test = x -> x % 2 != 0; + assertThat(Multi.createFrom().range(1, 4) + .select().where(test) + .collectItems().asList() + .await().indefinitely()).containsExactly(1, 3); + } + + @Test + public void testFilteringWithPredicateAndLimit() { + Predicate test = x -> x % 2 != 0; + MultiOnCancellationSpy spy = Spy + .onCancellation(Multi.createFrom().range(1, 10)); + assertThat(spy + .select().where(test, 2) + .collectItems().asList() + .await().indefinitely()).containsExactly(1, 3); + + assertThat(spy.isCancelled()).isTrue(); + } + + @Test + public void testFilteringWithPredicateAndZeroAsLimit() { + Predicate test = x -> x % 2 != 0; + assertThat(Multi.createFrom().range(1, 10) + .select().where(test, 0) + .collectItems().asList() + .await().indefinitely()).isEmpty(); + } + + @Test + @SuppressWarnings("deprecation") + public void testFilteringWithPredicateDeprecated() { Predicate test = x -> x % 2 != 0; assertThat(Multi.createFrom().range(1, 4) .transform().byFilteringItemsWith(test) @@ -65,8 +105,17 @@ public void testFilteringWithPredicate() { @Test public void testFilteringWithUni() { assertThat(Multi.createFrom().range(1, 4) - .transform() - .byTestingItemsWith( + .select().when( + x -> Uni.createFrom().completionStage(() -> CompletableFuture.supplyAsync(() -> x % 2 != 0))) + .collectItems().asList() + .await().indefinitely()).containsExactly(1, 3); + } + + @SuppressWarnings("deprecation") + @Test + public void testFilteringWithUniDeprecated() { + assertThat(Multi.createFrom().range(1, 4) + .transform().byTestingItemsWith( x -> Uni.createFrom().completionStage(() -> CompletableFuture.supplyAsync(() -> x % 2 != 0))) .collect().asList() .await().indefinitely()).containsExactly(1, 3); @@ -87,7 +136,7 @@ public void testFilteringWithDownstreamRequestingMax() { LongAdder numberOfRequests = new LongAdder(); AssertSubscriber subscriber = Multi.createFrom().range(1, 100000) .onRequest().invoke(numberOfRequests::increment) - .transform().byFilteringItemsWith(test) + .select().where(test) .subscribe().withSubscriber(AssertSubscriber.create(Long.MAX_VALUE)); subscriber.assertCompleted(); @@ -97,7 +146,7 @@ public void testFilteringWithDownstreamRequestingMax() { @Test public void testNoEmissionAfterCompletion() { BroadcastProcessor processor = BroadcastProcessor.create(); - MultiFilterOp filter = new MultiFilterOp<>(processor, x -> x % 2 == 0); + MultiSelectWhereOp filter = new MultiSelectWhereOp<>(processor, x -> x % 2 == 0); Subscriber subscriber = Mocks.subscriber(); filter.subscribe(subscriber); @@ -120,7 +169,7 @@ public void testNoEmissionAfterCompletion() { @Test public void testNoEmissionAfterFailure() { BroadcastProcessor processor = BroadcastProcessor.create(); - MultiFilterOp filter = new MultiFilterOp<>(processor, x -> x % 2 == 0); + MultiSelectWhereOp filter = new MultiSelectWhereOp<>(processor, x -> x % 2 == 0); Subscriber subscriber = Mocks.subscriber(); filter.subscribe(subscriber); @@ -143,7 +192,7 @@ public void testNoEmissionAfterFailure() { @Test public void testNoEmissionAfterCancellation() { BroadcastProcessor processor = BroadcastProcessor.create(); - AssertSubscriber subscriber = processor.transform().byFilteringItemsWith(x -> x % 2 == 0) + AssertSubscriber subscriber = processor.select().where(x -> x % 2 == 0) .subscribe().withSubscriber(AssertSubscriber.create(20)); processor.onNext(1); @@ -161,7 +210,7 @@ public void testNoEmissionAfterCancellation() { @Test public void testWithPredicateThrowingException() { BroadcastProcessor processor = BroadcastProcessor.create(); - AssertSubscriber subscriber = processor.transform().byFilteringItemsWith(x -> { + AssertSubscriber subscriber = processor.select().where(x -> { if (x == 3) { throw new IllegalArgumentException("boom"); } diff --git a/implementation/src/test/java/io/smallrye/mutiny/operators/MultiSkipTest.java b/implementation/src/test/java/io/smallrye/mutiny/operators/MultiSkipTest.java index 1836aafcf..ae8dedb7c 100644 --- a/implementation/src/test/java/io/smallrye/mutiny/operators/MultiSkipTest.java +++ b/implementation/src/test/java/io/smallrye/mutiny/operators/MultiSkipTest.java @@ -9,6 +9,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Predicate; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -17,7 +18,7 @@ import io.smallrye.mutiny.TestException; import io.smallrye.mutiny.helpers.test.AssertSubscriber; import io.smallrye.mutiny.infrastructure.Infrastructure; -import io.smallrye.mutiny.operators.multi.MultiSkipUntilPublisherOp; +import io.smallrye.mutiny.operators.multi.MultiSkipUntilOtherOp; import io.smallrye.mutiny.subscription.MultiEmitter; public class MultiSkipTest { @@ -29,6 +30,25 @@ public void cleanup() { @Test public void testSimpleSkip() { + List list = Multi.createFrom().range(1, 5) + .skip().first(1) + .collect().asList().await().indefinitely(); + + assertThat(list).containsExactly(2, 3, 4); + } + + @Test + public void testSkipFirst() { + List list = Multi.createFrom().range(1, 5) + .skip().first() + .collect().asList().await().indefinitely(); + + assertThat(list).containsExactly(2, 3, 4); + } + + @SuppressWarnings("deprecation") + @Test + public void testSimpleSkipDeprecated() { List list = Multi.createFrom().range(1, 5).transform().bySkippingFirstItems(1) .collect().asList().await().indefinitely(); @@ -36,8 +56,9 @@ public void testSimpleSkip() { } @Test - public void testSkipZero() { - List list = Multi.createFrom().range(1, 5).transform().bySkippingFirstItems(0) + public void testSkipFirstZero() { + List list = Multi.createFrom().range(1, 5) + .skip().first(0) .collect().asList().await().indefinitely(); assertThat(list).containsExactly(1, 2, 3, 4); @@ -45,23 +66,43 @@ public void testSkipZero() { @Test public void testSimpleSkipLast() { - List list = Multi.createFrom().range(1, 5).transform().bySkippingLastItems(1) + List list = Multi.createFrom().range(1, 5) + .skip().last(1) .collect().asList().await().indefinitely(); assertThat(list).containsExactly(1, 2, 3); } + @SuppressWarnings("deprecation") @Test - public void testSimpleSkipZeroLast() { - List list = Multi.createFrom().range(1, 5).transform().bySkippingLastItems(0) + public void testSimpleSkipLastDeprecated() { + List list = Multi.createFrom().range(1, 5) + .transform().bySkippingLastItems(1) + .collectItems().asList().await().indefinitely(); + + assertThat(list).containsExactly(1, 2, 3); + } + + @Test + public void testSkipLast() { + List list = Multi.createFrom().range(1, 5) + .skip().last() .collect().asList().await().indefinitely(); + assertThat(list).containsExactly(1, 2, 3); + } + @Test + public void testSimpleSkipZeroLast() { + List list = Multi.createFrom().range(1, 5) + .skip().last(0) + .collect().asList().await().indefinitely(); assertThat(list).containsExactly(1, 2, 3, 4); } @Test public void testSkipOnUpstreamFailure() { - Multi.createFrom(). failure(new IOException("boom")).transform().bySkippingFirstItems(1) + Multi.createFrom(). failure(new IOException("boom")) + .skip().first() .subscribe().withSubscriber(AssertSubscriber.create(10)) .assertFailedWith(IOException.class, "boom") .assertHasNotReceivedAnyItem(); @@ -69,7 +110,8 @@ public void testSkipOnUpstreamFailure() { @Test public void testSkipLastOnUpstreamFailure() { - Multi.createFrom(). failure(new IOException("boom")).transform().bySkippingLastItems(1) + Multi.createFrom(). failure(new IOException("boom")) + .skip().last() .subscribe().withSubscriber(AssertSubscriber.create(10)) .assertFailedWith(IOException.class, "boom") .assertHasNotReceivedAnyItem(); @@ -77,7 +119,8 @@ public void testSkipLastOnUpstreamFailure() { @Test public void testSkipAll() { - Multi.createFrom().range(1, 5).transform().bySkippingFirstItems(4) + Multi.createFrom().range(1, 5) + .skip().first(4) .subscribe().withSubscriber(AssertSubscriber.create(10)) .assertCompleted() .assertHasNotReceivedAnyItem(); @@ -85,7 +128,8 @@ public void testSkipAll() { @Test public void testSkipLastAll() { - Multi.createFrom().range(1, 5).transform().bySkippingLastItems(4) + Multi.createFrom().range(1, 5) + .skip().last(4) .subscribe().withSubscriber(AssertSubscriber.create(10)) .assertCompleted() .assertHasNotReceivedAnyItem(); @@ -94,10 +138,12 @@ public void testSkipLastAll() { @Test public void testInvalidSkipNumber() { assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> Multi.createFrom().items(1, 2, 3).transform().bySkippingFirstItems(-1)); + .isThrownBy(() -> Multi.createFrom().items(1, 2, 3 + + ).skip().first(-1)); assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> Multi.createFrom().items(1, 2, 3).transform().bySkippingLastItems(-1)); + .isThrownBy(() -> Multi.createFrom().items(1, 2, 3).skip().last(-1)); } @Test @@ -106,7 +152,7 @@ public void testSkipLastWithBackPressure() { AtomicReference> emitter = new AtomicReference<>(); Multi.createFrom(). emitter(emitter::set) - .transform().bySkippingLastItems(3) + .skip().last(3) .subscribe(subscriber); subscriber.assertNotTerminated() @@ -136,7 +182,7 @@ public void testSkipSomeLastItems() { AssertSubscriber subscriber = AssertSubscriber.create(Long.MAX_VALUE); Multi.createFrom().range(1, 11) - .transform().bySkippingLastItems(3) + .skip().last(3) .subscribe(subscriber); subscriber.assertCompleted() @@ -145,7 +191,7 @@ public void testSkipSomeLastItems() { @Test public void testSkipWhileWithMethodThrowingException() { - Multi.createFrom().range(1, 10).transform().bySkippingItemsWhile(i -> { + Multi.createFrom().range(1, 10).skip().first(i -> { throw new IllegalStateException("boom"); }).subscribe().withSubscriber(AssertSubscriber.create(10)) .assertFailedWith(IllegalStateException.class, "boom"); @@ -154,7 +200,7 @@ public void testSkipWhileWithMethodThrowingException() { @Test public void testSkipWhileWithUpstreamFailure() { Multi.createFrom(). failure(new IOException("boom")) - .transform().bySkippingItemsWhile(i -> i < 5) + .skip().first(i -> i < 5) .subscribe().withSubscriber(AssertSubscriber.create(10)) .assertFailedWith(IOException.class, "boom"); } @@ -162,12 +208,18 @@ public void testSkipWhileWithUpstreamFailure() { @Test public void testSkipWhileWithNullMethod() { assertThrows(IllegalArgumentException.class, - () -> Multi.createFrom().nothing().transform().bySkippingItemsWhile(null)); + () -> Multi.createFrom().nothing().skip().first((Predicate) null)); + } + + @Test + public void testSkipDurationWithNullAsDuration() { + assertThrows(IllegalArgumentException.class, + () -> Multi.createFrom().nothing().skip().first((Duration) null)); } @Test public void testSkipWhile() { - Multi.createFrom().range(1, 10).transform().bySkippingItemsWhile(i -> i < 5) + Multi.createFrom().range(1, 10).skip().first(i -> i < 5) .subscribe().withSubscriber(AssertSubscriber.create(10)) .assertCompleted() .assertItems(5, 6, 7, 8, 9); @@ -175,7 +227,7 @@ public void testSkipWhile() { @Test public void testSkipWhileNone() { - Multi.createFrom().items(1, 2, 3, 4).transform().bySkippingItemsWhile(i -> false) + Multi.createFrom().items(1, 2, 3, 4).skip().first(i -> false) .subscribe().withSubscriber(AssertSubscriber.create(10)) .assertCompleted() .assertItems(1, 2, 3, 4); @@ -183,7 +235,7 @@ public void testSkipWhileNone() { @Test public void testSkipWhileAll() { - Multi.createFrom().items(1, 2, 3, 4).transform().bySkippingItemsWhile(i -> true) + Multi.createFrom().items(1, 2, 3, 4).skip().first(i -> true) .subscribe().withSubscriber(AssertSubscriber.create(10)) .assertCompleted() .assertHasNotReceivedAnyItem(); @@ -191,8 +243,8 @@ public void testSkipWhileAll() { @Test public void testSkipWhileSomeWithBackPressure() { - AssertSubscriber subscriber = Multi.createFrom().items(1, 2, 3, 4).transform() - .bySkippingItemsWhile(i -> i < 3) + AssertSubscriber subscriber = Multi.createFrom().items(1, 2, 3, 4) + .skip().first(i -> i < 3) .subscribe().withSubscriber(AssertSubscriber.create(0)); subscriber.assertNotTerminated() @@ -212,7 +264,7 @@ public void testSkipWhileSomeWithBackPressure() { @Test public void testSkipByTime() { Multi.createFrom().range(1, 100) - .transform().bySkippingItemsFor(Duration.ofMillis(2000)) + .skip().first(Duration.ofMillis(2000)) .subscribe().withSubscriber(AssertSubscriber.create(10)) .assertCompleted() .assertHasNotReceivedAnyItem(); @@ -221,13 +273,13 @@ public void testSkipByTime() { @Test public void testSkipByTimeWithInvalidDuration() { assertThrows(IllegalArgumentException.class, - () -> Multi.createFrom().item(1).transform().bySkippingItemsFor(Duration.ofMillis(-1))); + () -> Multi.createFrom().item(1).skip().first(Duration.ofMillis(-1))); } @Test public void testMultiSkipUntilPublisherOpeningOnItem() { Multi upstream = Multi.createFrom().items(1, 2, 3, 4, 5, 6); - new MultiSkipUntilPublisherOp<>(upstream, Multi.createFrom().item(0)) + new MultiSkipUntilOtherOp<>(upstream, Multi.createFrom().item(0)) .subscribe().withSubscriber(AssertSubscriber.create(10)) .assertItems(1, 2, 3, 4, 5, 6) .assertCompleted(); @@ -236,7 +288,7 @@ public void testMultiSkipUntilPublisherOpeningOnItem() { @Test public void testMultiSkipUntilPublisherOpeningOnCompletion() { Multi upstream = Multi.createFrom().items(1, 2, 3, 4, 5, 6); - new MultiSkipUntilPublisherOp<>(upstream, Multi.createFrom().empty()) + new MultiSkipUntilOtherOp<>(upstream, Multi.createFrom().empty()) .subscribe().withSubscriber(AssertSubscriber.create(10)) .assertItems(1, 2, 3, 4, 5, 6) .assertCompleted(); @@ -245,7 +297,7 @@ public void testMultiSkipUntilPublisherOpeningOnCompletion() { @Test public void testMultiSkipUntilPublisherWithOtherFailing() { Multi upstream = Multi.createFrom().items(1, 2, 3, 4, 5, 6); - new MultiSkipUntilPublisherOp<>(upstream, Multi.createFrom().failure(new IOException("boom"))) + new MultiSkipUntilOtherOp<>(upstream, Multi.createFrom().failure(new IOException("boom"))) .subscribe().withSubscriber(AssertSubscriber.create(10)) .assertFailedWith(IOException.class, "boom"); } @@ -254,7 +306,7 @@ public void testMultiSkipUntilPublisherWithOtherFailing() { public void testMultiSkipUntilPublisherWithUpstreamFailing() { Multi upstream1 = Multi.createFrom().items(1, 2, 3, 4, 5, 6); Multi upstream2 = Multi.createFrom().failure(new TestException("boom")); - new MultiSkipUntilPublisherOp<>(Multi.createBy().concatenating().streams(upstream1, upstream2), + new MultiSkipUntilOtherOp<>(Multi.createBy().concatenating().streams(upstream1, upstream2), Multi.createFrom().item(0)) .subscribe().withSubscriber(AssertSubscriber.create(10)) .assertItems(1, 2, 3, 4, 5, 6) @@ -270,7 +322,7 @@ public void testMultiSkipUntilPublisherWithDownstreamCancellationAndVerifyOtherI .onOverflow().drop() .onCancellation().invoke(() -> upstreamCancelled.set(true)); - AssertSubscriber subscriber = new MultiSkipUntilPublisherOp<>(upstream, + AssertSubscriber subscriber = new MultiSkipUntilOtherOp<>(upstream, Multi.createFrom().nothing().onCancellation().invoke(() -> otherCancelled.set(true))) .subscribe().withSubscriber(AssertSubscriber.create(1)); @@ -285,8 +337,8 @@ public void testItDoesNotRequest0() { AtomicBoolean called = new AtomicBoolean(); Infrastructure.setDroppedExceptionHandler(t -> called.set(true)); Multi.createFrom().range(1, 10) - .transform().bySkippingFirstItems(3) - .transform().byFilteringItemsWith(n -> n % 2 == 0) + .skip().first(3) + .select().where(n -> n % 2 == 0) .onItem().transform(n -> n * 10) .subscribe().withSubscriber(AssertSubscriber.create(100)) diff --git a/implementation/src/test/java/io/smallrye/mutiny/operators/MultiSkipWhereAndWhenTest.java b/implementation/src/test/java/io/smallrye/mutiny/operators/MultiSkipWhereAndWhenTest.java new file mode 100644 index 000000000..ce0fa5d6b --- /dev/null +++ b/implementation/src/test/java/io/smallrye/mutiny/operators/MultiSkipWhereAndWhenTest.java @@ -0,0 +1,110 @@ +package io.smallrye.mutiny.operators; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.LongAdder; +import java.util.function.Predicate; + +import org.junit.jupiter.api.Test; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.helpers.test.AssertSubscriber; +import io.smallrye.mutiny.operators.multi.processors.BroadcastProcessor; + +public class MultiSkipWhereAndWhenTest { + + @Test + public void testThatPredicateCannotBeNull() { + assertThrows(IllegalArgumentException.class, () -> Multi.createFrom().range(1, 4) + .skip().where(null)); + } + + @Test + public void testThatFunctionCannotBeNull() { + assertThrows(IllegalArgumentException.class, () -> Multi.createFrom().range(1, 4) + .skip().when(null)); + } + + @Test + public void cannotRequestZeroItems() { + AssertSubscriber sub = Multi.createFrom().range(1, 4) + .skip().where(n -> n % 2 == 0) + .subscribe().withSubscriber(AssertSubscriber.create()); + sub.request(0); + sub.assertFailedWith(IllegalArgumentException.class, "request must be positive"); + } + + @Test + public void testFilteringWithPredicate() { + Predicate test = x -> x % 2 != 0; + assertThat(Multi.createFrom().range(1, 4) + .skip().where(test) + .collectItems().asList() + .await().indefinitely()).containsExactly(2); + } + + @Test + public void testFilteringWithUni() { + assertThat(Multi.createFrom().range(1, 4) + .skip().when( + x -> Uni.createFrom().completionStage(() -> CompletableFuture.supplyAsync(() -> x % 2 != 0))) + .collectItems().asList() + .await().indefinitely()).containsExactly(2); + } + + @Test + public void testFilteringWithDownstreamRequestingMax() { + Predicate test = x -> x % 2 != 0; + LongAdder numberOfRequests = new LongAdder(); + AssertSubscriber subscriber = Multi.createFrom().range(1, 100000) + .onRequest().invoke(numberOfRequests::increment) + .skip().where(test) + .subscribe().withSubscriber(AssertSubscriber.create(Long.MAX_VALUE)); + + subscriber.assertCompleted(); + assertThat(numberOfRequests.longValue()).isEqualTo(1L); + } + + @Test + public void testNoEmissionAfterCancellation() { + BroadcastProcessor processor = BroadcastProcessor.create(); + AssertSubscriber subscriber = processor.skip().where(x -> x % 2 == 0) + .subscribe().withSubscriber(AssertSubscriber.create(20)); + + processor.onNext(1); + processor.onNext(2); + processor.onNext(3); + processor.onNext(4); + subscriber.cancel(); + processor.onNext(5); + processor.onNext(6); + processor.onComplete(); + subscriber.assertItems(1, 3) + .assertNotTerminated(); + } + + @Test + public void testWithPredicateThrowingException() { + BroadcastProcessor processor = BroadcastProcessor.create(); + AssertSubscriber subscriber = processor.skip().where(x -> { + if (x == 3) { + throw new IllegalArgumentException("boom"); + } + return x % 2 == 0; + }) + .subscribe().withSubscriber(AssertSubscriber.create(20)); + + processor.onNext(1); + processor.onNext(2); + processor.onNext(3); + processor.onNext(4); + processor.onNext(5); + processor.onNext(6); + processor.onComplete(); + subscriber.assertItems(1) + .assertFailedWith(IllegalArgumentException.class, "boom"); + } +} diff --git a/implementation/src/test/java/io/smallrye/mutiny/operators/MultiSubscribeTest.java b/implementation/src/test/java/io/smallrye/mutiny/operators/MultiSubscribeTest.java index d1e2d5546..e18582281 100644 --- a/implementation/src/test/java/io/smallrye/mutiny/operators/MultiSubscribeTest.java +++ b/implementation/src/test/java/io/smallrye/mutiny/operators/MultiSubscribeTest.java @@ -38,6 +38,7 @@ public void testSubscribeWithItemAndFailure() { } + @SuppressWarnings("ConstantConditions") @Test public void testSubscribeWithItemFailureAndCompletion() { List items = new CopyOnWriteArrayList<>(); @@ -45,7 +46,7 @@ public void testSubscribeWithItemFailureAndCompletion() { AtomicReference failure = new AtomicReference<>(); Multi.createFrom().ticks().every(Duration.ofMillis(10)) - .transform().byTakingFirstItems(10) + .select().first(10) .subscribe().with(items::add, failure::set, () -> completion.set(true)); await().until(() -> items.size() > 5); diff --git a/implementation/src/test/java/io/smallrye/mutiny/operators/UniRepeatTest.java b/implementation/src/test/java/io/smallrye/mutiny/operators/UniRepeatTest.java index 963ce1cfb..7e256a9f0 100644 --- a/implementation/src/test/java/io/smallrye/mutiny/operators/UniRepeatTest.java +++ b/implementation/src/test/java/io/smallrye/mutiny/operators/UniRepeatTest.java @@ -156,7 +156,7 @@ public void testRepeatCancelledWithTake() { int value = Uni.createFrom().item(count::incrementAndGet) .repeat().indefinitely() .runSubscriptionOn(Infrastructure.getDefaultWorkerPool()) - .transform().byTakingFirstItems(num) + .select().first(num) .collect().last() .await().atMost(Duration.ofSeconds(5)); assertThat(num).isEqualTo(value); @@ -174,7 +174,7 @@ public void testRepeatUntilCancelledWithTake() { return false; }) .runSubscriptionOn(Infrastructure.getDefaultWorkerPool()) - .transform().byTakingFirstItems(num) + .select().first(num) .collect().last() .await().atMost(Duration.ofSeconds(5)); assertThat(num).isEqualTo(value); @@ -193,7 +193,7 @@ public void testRepeatWhilstCancelledWithTake() { return true; }) .runSubscriptionOn(Infrastructure.getDefaultWorkerPool()) - .transform().byTakingFirstItems(num) + .select().first(num) .collect().last() .await().atMost(Duration.ofSeconds(5)); assertThat(num).isEqualTo(value); @@ -205,7 +205,7 @@ public void testRepeatWhilstCancelledWithTake() { public void testNoStackOverflow() { int value = Uni.createFrom().item(1).repeat().indefinitely() .runSubscriptionOn(Infrastructure.getDefaultWorkerPool()) - .transform().byTakingFirstItems(100000L) + .select().first(100000) .collect().last() .await().atMost(Duration.ofSeconds(5)); assertThat(value).isEqualTo(1); @@ -216,7 +216,7 @@ public void testNoStackOverflowWithRepeatUntil() { AtomicInteger count = new AtomicInteger(); int value = Uni.createFrom().item(1).repeat().until(x -> count.incrementAndGet() > 100000000L) .runSubscriptionOn(Infrastructure.getDefaultWorkerPool()) - .transform().byTakingFirstItems(100000L) + .select().first(100000) .collect().last() .await().atMost(Duration.ofSeconds(5)); assertThat(value).isEqualTo(1); @@ -227,7 +227,7 @@ public void testNoStackOverflowWithRepeatWhilst() { AtomicInteger count = new AtomicInteger(); int value = Uni.createFrom().item(1).repeat().whilst(x -> count.incrementAndGet() < 100000000L) .runSubscriptionOn(Infrastructure.getDefaultWorkerPool()) - .transform().byTakingFirstItems(100000L) + .select().first(100000) .collect().last() .await().atMost(Duration.ofSeconds(5)); assertThat(value).isEqualTo(1); @@ -238,7 +238,7 @@ public void testNumberOfRepeat() { Subscriber subscriber = Mocks.subscriber(); Uni.createFrom().item(1).repeat().indefinitely() - .transform().byTakingFirstItems(10) + .select().first(10) .subscribe(subscriber); verify(subscriber, times(10)).onNext(1); @@ -251,7 +251,7 @@ public void testFailurePropagation() { Subscriber subscriber = Mocks.subscriber(); Uni.createFrom(). failure(() -> new IOException("boom")).repeat().indefinitely() - .transform().byTakingFirstItems(10) + .select().first(10) .subscribe(subscriber); verify(subscriber).onError(any(IOException.class)); @@ -264,7 +264,7 @@ public void testFailurePropagationWithRepeatUntil() { Subscriber subscriber = Mocks.subscriber(); Uni.createFrom(). failure(() -> new IOException("boom")).repeat().until(x -> false) - .transform().byTakingFirstItems(10) + .select().first(10) .subscribe(subscriber); verify(subscriber).onError(any(IOException.class)); @@ -277,7 +277,7 @@ public void testFailurePropagationWithRepeatWhilst() { Subscriber subscriber = Mocks.subscriber(); Uni.createFrom(). failure(() -> new IOException("boom")).repeat().whilst(x -> true) - .transform().byTakingFirstItems(10) + .select().first(10) .subscribe(subscriber); verify(subscriber).onError(any(IOException.class)); diff --git a/implementation/src/test/java/io/smallrye/mutiny/operators/multi/MultiToHotStreamTest.java b/implementation/src/test/java/io/smallrye/mutiny/operators/multi/MultiToHotStreamTest.java index d13ab1da9..8576cb92b 100644 --- a/implementation/src/test/java/io/smallrye/mutiny/operators/multi/MultiToHotStreamTest.java +++ b/implementation/src/test/java/io/smallrye/mutiny/operators/multi/MultiToHotStreamTest.java @@ -20,6 +20,34 @@ public class MultiToHotStreamTest { public void testWithTwoSubscribers() { UnicastProcessor processor = UnicastProcessor.create(); + Multi multi = processor.map(s -> s).toHotStream(); + AssertSubscriber subscriber1 = multi.subscribe() + .withSubscriber(AssertSubscriber.create(10)); + + processor.onNext("one"); + processor.onNext("two"); + processor.onNext("three"); + + AssertSubscriber subscriber2 = multi.subscribe() + .withSubscriber(AssertSubscriber.create(10)); + + processor.onNext("four"); + processor.onComplete(); + + subscriber1 + .assertItems("one", "two", "three", "four") + .assertCompleted(); + + subscriber2 + .assertItems("four") + .assertCompleted(); + } + + @SuppressWarnings("deprecation") + @Test + public void testWithTwoSubscribersDeprecated() { + UnicastProcessor processor = UnicastProcessor.create(); + Multi multi = processor.map(s -> s).transform().toHotStream(); AssertSubscriber subscriber1 = multi.subscribe() .withSubscriber(AssertSubscriber.create(10)); @@ -47,7 +75,7 @@ public void testWithTwoSubscribers() { public void testSubscriptionAfterCompletion() { BroadcastProcessor processor = BroadcastProcessor.create(); - Multi multi = processor.map(s -> s).transform().toHotStream(); + Multi multi = processor.map(s -> s).toHotStream(); AssertSubscriber subscriber1 = multi.subscribe() .withSubscriber(AssertSubscriber.create(10)); @@ -79,7 +107,7 @@ public void testSubscriptionAfterCompletion() { @Test public void testSubscriptionAfterFailure() { BroadcastProcessor processor = BroadcastProcessor.create(); - Multi multi = processor.map(s -> s).transform().toHotStream(); + Multi multi = processor.map(s -> s).toHotStream(); AssertSubscriber subscriber1 = multi.subscribe() .withSubscriber(AssertSubscriber.create(10)); @@ -112,7 +140,7 @@ public void testSubscriptionAfterFailure() { @Test public void testFailureAfterCompletion() { BroadcastProcessor processor = BroadcastProcessor.create(); - Multi multi = processor.map(s -> s).transform().toHotStream(); + Multi multi = processor.map(s -> s).toHotStream(); AssertSubscriber subscriber = multi.subscribe() .withSubscriber(AssertSubscriber.create(10)); @@ -128,7 +156,7 @@ public void testFailureAfterCompletion() { @Test public void testNoItemAfterCancellation() { BroadcastProcessor processor = BroadcastProcessor.create(); - Multi multi = processor.map(s -> s).transform().toHotStream(); + Multi multi = processor.map(s -> s).toHotStream(); AssertSubscriber subscriber1 = multi.subscribe() .withSubscriber(AssertSubscriber.create(10)); @@ -166,7 +194,7 @@ public void testNoItemAfterCancellation() { @Test public void testResubscription() { BroadcastProcessor processor = BroadcastProcessor.create(); - Multi multi = processor.map(s -> s).transform().toHotStream(); + Multi multi = processor.map(s -> s).toHotStream(); AssertSubscriber subscriber1 = multi.subscribe() .withSubscriber(AssertSubscriber.create(10)); @@ -191,7 +219,7 @@ public void testResubscription() { @Test public void testResubscriptionAfterCompletion() { BroadcastProcessor processor = BroadcastProcessor.create(); - Multi multi = processor.map(s -> s).transform().toHotStream(); + Multi multi = processor.map(s -> s).toHotStream(); AssertSubscriber subscriber1 = multi.subscribe() .withSubscriber(AssertSubscriber.create(10)); @@ -213,7 +241,7 @@ public void testResubscriptionAfterCompletion() { @Test public void testResubscriptionAfterFailure() { BroadcastProcessor processor = BroadcastProcessor.create(); - Multi multi = processor.map(s -> s).transform().toHotStream(); + Multi multi = processor.map(s -> s).toHotStream(); AssertSubscriber subscriber1 = multi.subscribe() .withSubscriber(AssertSubscriber.create(10)); @@ -236,7 +264,7 @@ public void testResubscriptionAfterFailure() { @Test public void testWhenSubscriberDoesNotHaveRequestedEnough() { BroadcastProcessor processor = BroadcastProcessor.create(); - Multi multi = processor.map(s -> s).transform().toHotStream(); + Multi multi = processor.map(s -> s).toHotStream(); AssertSubscriber s1 = multi.subscribe() .withSubscriber(AssertSubscriber.create(10)); @@ -257,7 +285,7 @@ public void testWhenSubscriberDoesNotHaveRequestedEnough() { @Test public void testWithTransformToMultiAndMerge() { BroadcastProcessor processor = BroadcastProcessor.create(); - Multi multi = processor.map(s -> s).transform().toHotStream(); + Multi multi = processor.map(s -> s).toHotStream(); AssertSubscriber subscriber = AssertSubscriber.create(10); @@ -278,8 +306,8 @@ public void testWithTransformToMultiAndMerge() { @Test public void testMakingTicksAHotStream() throws InterruptedException { Multi ticks = Multi.createFrom().ticks().every(Duration.ofMillis(1)) - .transform().byTakingFirstItems(100) - .transform().toHotStream(); + .select().first(100) + .toHotStream(); Thread.sleep(50); // NOSONAR AssertSubscriber subscriber = ticks.subscribe() diff --git a/implementation/src/test/java/io/smallrye/mutiny/operators/multi/builders/MultiFromIterableTest.java b/implementation/src/test/java/io/smallrye/mutiny/operators/multi/builders/MultiFromIterableTest.java index a71bc5698..44c43bde9 100644 --- a/implementation/src/test/java/io/smallrye/mutiny/operators/multi/builders/MultiFromIterableTest.java +++ b/implementation/src/test/java/io/smallrye/mutiny/operators/multi/builders/MultiFromIterableTest.java @@ -227,7 +227,7 @@ public Integer next() { }; Multi.createFrom().iterable(iterable) - .transform().byTakingFirstItems(1) + .select().first(1) .subscribe().withSubscriber(AssertSubscriber.create(10)); assertFalse(called.get()); } diff --git a/implementation/src/test/java/io/smallrye/mutiny/operators/multi/builders/MultiFromResourceTest.java b/implementation/src/test/java/io/smallrye/mutiny/operators/multi/builders/MultiFromResourceTest.java index ea7c16fd2..4e0d77281 100644 --- a/implementation/src/test/java/io/smallrye/mutiny/operators/multi/builders/MultiFromResourceTest.java +++ b/implementation/src/test/java/io/smallrye/mutiny/operators/multi/builders/MultiFromResourceTest.java @@ -23,6 +23,7 @@ import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.helpers.test.AssertSubscriber; +@SuppressWarnings("ConstantConditions") public class MultiFromResourceTest { @Test @@ -450,7 +451,7 @@ public void testThatCancellationDueToPartialConsumptionCallsOnCancel() { Multi multi = Multi.createFrom().resource(() -> resource, FakeTransactionalResource::infinite) .withFinalizer(FakeTransactionalResource::commit, FakeTransactionalResource::rollback, FakeTransactionalResource::cancel) - .transform().byTakingFirstItems(3); + .select().first(3); multi.subscribe().withSubscriber(AssertSubscriber.create(Long.MAX_VALUE)) .await() @@ -470,7 +471,7 @@ public void testThatCancellationFailureAreNotPropagated() { Multi multi = Multi.createFrom().resource(() -> resource, FakeTransactionalResource::infinite) .withFinalizer(FakeTransactionalResource::commit, FakeTransactionalResource::rollback, r -> r.cancel().onItem().failWith(x -> new IOException("boom"))) - .transform().byTakingFirstItems(3); + .select().first(3); multi.subscribe().withSubscriber(AssertSubscriber.create(Long.MAX_VALUE)) .await() @@ -490,7 +491,7 @@ public void testThatCancellationReturningNullAreNotPropagated() { Multi multi = Multi.createFrom().resource(() -> resource, FakeTransactionalResource::infinite) .withFinalizer(FakeTransactionalResource::commit, FakeTransactionalResource::rollback, r -> null) - .transform().byTakingFirstItems(3); + .select().first(3); multi.subscribe().withSubscriber(AssertSubscriber.create(Long.MAX_VALUE)) .await() @@ -511,7 +512,7 @@ public void testThatCompletionFailureArePropagated() { .withFinalizer(r -> r.commit().onItem().failWith(x -> new IOException("boom")), FakeTransactionalResource::rollback, FakeTransactionalResource::cancel) - .transform().byTakingFirstItems(3); + .select().first(3); multi.subscribe().withSubscriber(AssertSubscriber.create(Long.MAX_VALUE)) .await() @@ -532,7 +533,7 @@ public void testThatCompletionFailureArePropagated2() { .withFinalizer(FakeTransactionalResource::commitFailure, FakeTransactionalResource::rollback, FakeTransactionalResource::cancel) - .transform().byTakingFirstItems(3); + .select().first(3); multi.subscribe().withSubscriber(AssertSubscriber.create(Long.MAX_VALUE)) .await() @@ -554,7 +555,7 @@ public void testWithOnCompletionReturningNull() { FakeTransactionalResource::commitReturningNull, FakeTransactionalResource::rollback, FakeTransactionalResource::cancel) - .transform().byTakingFirstItems(3); + .select().first(3); multi.subscribe().withSubscriber(AssertSubscriber.create(Long.MAX_VALUE)) .await() @@ -652,7 +653,7 @@ public void testOnCancellationWithSingleFinalizer() { .onSubscribe().invoke(s -> subscribed.set(true)) .onItem().ignore().andContinueWithNull(); }) - .transform().byTakingFirstItems(5); + .select().first(5); multi.subscribe().withSubscriber(AssertSubscriber.create(20)) .await() .assertCompleted() diff --git a/implementation/src/test/java/io/smallrye/mutiny/operators/multi/processors/BroadcastProcessorTest.java b/implementation/src/test/java/io/smallrye/mutiny/operators/multi/processors/BroadcastProcessorTest.java index c87dece7b..54e35eae1 100644 --- a/implementation/src/test/java/io/smallrye/mutiny/operators/multi/processors/BroadcastProcessorTest.java +++ b/implementation/src/test/java/io/smallrye/mutiny/operators/multi/processors/BroadcastProcessorTest.java @@ -454,7 +454,7 @@ public void testWithTransformToMultiAndMerge() { @Test public void testMakingTicksAHotStream() throws InterruptedException { Multi ticks = Multi.createFrom().ticks().every(Duration.ofMillis(1)) - .transform().byTakingFirstItems(100); + .select().first(100); BroadcastProcessor processor = BroadcastProcessor.create(); ticks.subscribe().withSubscriber(processor); Thread.sleep(50); // NOSONAR diff --git a/implementation/src/test/java/tck/MultiFirstTckTest.java b/implementation/src/test/java/tck/MultiCollectFirstTckTest.java similarity index 96% rename from implementation/src/test/java/tck/MultiFirstTckTest.java rename to implementation/src/test/java/tck/MultiCollectFirstTckTest.java index 686cd27d4..34f429362 100644 --- a/implementation/src/test/java/tck/MultiFirstTckTest.java +++ b/implementation/src/test/java/tck/MultiCollectFirstTckTest.java @@ -9,7 +9,7 @@ import io.smallrye.mutiny.Multi; -public class MultiFirstTckTest extends AbstractTck { +public class MultiCollectFirstTckTest extends AbstractTck { @Test public void findFirstStageShouldFindTheFirstElement() { int res = await( diff --git a/implementation/src/test/java/tck/MultiFlatMapTckTest.java b/implementation/src/test/java/tck/MultiFlatMapTckTest.java index 9a52b315d..6a349ee4f 100644 --- a/implementation/src/test/java/tck/MultiFlatMapTckTest.java +++ b/implementation/src/test/java/tck/MultiFlatMapTckTest.java @@ -117,7 +117,7 @@ public void flatMapStageShouldPropagateCancelToSubstreams() { await(infiniteStream() .onTermination().invoke(() -> outerCancelled.complete(null)) .flatMap(i -> infiniteStream().onTermination().invoke(() -> innerCancelled.complete(null))) - .transform().byTakingFirstItems(5) + .select().first(5) .collect().asList() .subscribeAsCompletionStage()); diff --git a/implementation/src/test/java/tck/MultiOnITemTransformToMultiAndConcatenateTckTest.java b/implementation/src/test/java/tck/MultiOnITemTransformToMultiAndConcatenateTckTest.java index aac4ac765..944a3d010 100644 --- a/implementation/src/test/java/tck/MultiOnITemTransformToMultiAndConcatenateTckTest.java +++ b/implementation/src/test/java/tck/MultiOnITemTransformToMultiAndConcatenateTckTest.java @@ -108,7 +108,7 @@ public void flatMapStageShouldPropagateCancelToSubstreams() { .onItem() .transformToMultiAndConcatenate( i -> infiniteStream().onTermination().invoke(() -> innerCancelled.complete(null))) - .transform().byTakingFirstItems(5) + .select().first(5) .collect().asList() .subscribeAsCompletionStage()); diff --git a/implementation/src/test/java/tck/MultiOnITemTransformToMultiAndMergeTckTest.java b/implementation/src/test/java/tck/MultiOnITemTransformToMultiAndMergeTckTest.java index 5060a9298..fd9327a69 100644 --- a/implementation/src/test/java/tck/MultiOnITemTransformToMultiAndMergeTckTest.java +++ b/implementation/src/test/java/tck/MultiOnITemTransformToMultiAndMergeTckTest.java @@ -106,7 +106,7 @@ public void flatMapStageShouldPropagateCancelToSubstreams() { .onTermination().invoke(() -> outerCancelled.complete(null)) .onItem().transformToMultiAndMerge(i -> infiniteStream().onTermination() .invoke(() -> innerCancelled.complete(null))) - .transform().byTakingFirstItems(5) + .select().first(5) .collect().asList() .subscribeAsCompletionStage()); diff --git a/implementation/src/test/java/tck/MultiDistinctTckTest.java b/implementation/src/test/java/tck/MultiSelectDistinctTckTest.java similarity index 84% rename from implementation/src/test/java/tck/MultiDistinctTckTest.java rename to implementation/src/test/java/tck/MultiSelectDistinctTckTest.java index c73c8c164..0b1d70736 100644 --- a/implementation/src/test/java/tck/MultiDistinctTckTest.java +++ b/implementation/src/test/java/tck/MultiSelectDistinctTckTest.java @@ -16,12 +16,12 @@ import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.helpers.Subscriptions; -public class MultiDistinctTckTest extends AbstractPublisherTck { +public class MultiSelectDistinctTckTest extends AbstractPublisherTck { @Test public void distinctStageShouldReturnDistinctElements() { assertEquals(await( Multi.createFrom().items(1, 2, 2, 3, 2, 1, 3) - .transform().byDroppingDuplicates() + .select().distinct() .collect().asList() .subscribeAsCompletionStage()), Arrays.asList(1, 2, 3)); @@ -30,7 +30,7 @@ public void distinctStageShouldReturnDistinctElements() { @Test public void distinctStageShouldReturnAnEmptyStreamWhenCalledOnEmptyStreams() { assertEquals(await(Multi.createFrom().empty() - .transform().byDroppingDuplicates() + .select().distinct() .collect().asList() .subscribeAsCompletionStage()), Collections.emptyList()); } @@ -39,7 +39,7 @@ public void distinctStageShouldReturnAnEmptyStreamWhenCalledOnEmptyStreams() { public void distinctStageShouldPropagateUpstreamExceptions() { assertThrows(QuietRuntimeException.class, () -> await(Multi.createFrom().failure(new QuietRuntimeException("failed")) - .transform().byDroppingDuplicates() + .select().distinct() .collect().asList() .subscribeAsCompletionStage())); } @@ -62,7 +62,8 @@ public boolean equals(Object obj) { CompletionStage> result = Multi.createFrom().items( new ObjectThatThrowsFromEquals(), new ObjectThatThrowsFromEquals()) .onTermination().invoke(() -> cancelled.complete(null)) - .transform().byDroppingDuplicates().collect().asList().subscribeAsCompletionStage(); + .select().distinct() + .collect().asList().subscribeAsCompletionStage(); await(cancelled); await(result); }); @@ -73,7 +74,7 @@ public void distinctStageShouldPropagateCancel() { CompletableFuture cancelled = new CompletableFuture<>(); infiniteStream() .onTermination().invoke(() -> cancelled.complete(null)) - .transform().byDroppingDuplicates().subscribe() + .select().distinct().subscribe() .withSubscriber(new Subscriptions.CancelledSubscriber<>()); await(cancelled); } @@ -81,13 +82,13 @@ public void distinctStageShouldPropagateCancel() { @Override public Publisher createPublisher(long elements) { return upstream(elements) - .transform().byDroppingDuplicates(); + .select().distinct(); } @Override public Publisher createFailedPublisher() { return failedUpstream() - .transform().byDroppingDuplicates(); + .select().distinct(); } } diff --git a/implementation/src/test/java/tck/MultiTakeFirstItemsTckTest.java b/implementation/src/test/java/tck/MultiSelectFirstItemsTckTest.java similarity index 84% rename from implementation/src/test/java/tck/MultiTakeFirstItemsTckTest.java rename to implementation/src/test/java/tck/MultiSelectFirstItemsTckTest.java index 7a0bfcb35..96503a084 100644 --- a/implementation/src/test/java/tck/MultiTakeFirstItemsTckTest.java +++ b/implementation/src/test/java/tck/MultiSelectFirstItemsTckTest.java @@ -13,12 +13,12 @@ import io.smallrye.mutiny.Multi; -public class MultiTakeFirstItemsTckTest extends AbstractPublisherTck { +public class MultiSelectFirstItemsTckTest extends AbstractPublisherTck { @Test public void limitStageShouldLimitTheOutputElements() { assertEquals(await(infiniteStream() - .transform().byTakingFirstItems(3) + .select().first(3) .collect().asList() .subscribeAsCompletionStage()), Arrays.asList(1, 2, 3)); } @@ -26,7 +26,7 @@ public void limitStageShouldLimitTheOutputElements() { @Test public void limitStageShouldAllowLimitingToZero() { assertEquals(await(infiniteStream() - .transform().byTakingFirstItems(0) + .select().first(0) .collect().asList() .subscribeAsCompletionStage()), Collections.emptyList()); } @@ -41,7 +41,8 @@ public void request(long n) { @Override public void cancel() { } - })).transform().byTakingFirstItems(0) + })) + .select().first(0) .collect().asList() .subscribeAsCompletionStage()), Collections.emptyList()); } @@ -51,7 +52,7 @@ public void limitStageShouldCancelUpStreamWhenDone() { CompletableFuture cancelled = new CompletableFuture<>(); infiniteStream() .onTermination().invoke(() -> cancelled.complete(null)) - .transform().byTakingFirstItems(1) + .select().first(1) .collect().asList() .subscribeAsCompletionStage(); await(cancelled); @@ -68,7 +69,7 @@ public void limitStageShouldIgnoreSubsequentErrorsWhenDone() { return Multi.createFrom().item(i); } }) - .transform().byTakingFirstItems(3) + .select().first(3) .collect().asList() .subscribeAsCompletionStage()), Arrays.asList(1, 2, 3)); @@ -85,8 +86,8 @@ public void limitStageShouldPropagateCancellation() { cancelled.completeExceptionally(new RuntimeException("Was not cancelled")); } }) - .transform().byTakingFirstItems(100) - .transform().byTakingFirstItems(3) + .select().first(100) + .select().first(3) .collect().asList() .subscribeAsCompletionStage()); await(cancelled); @@ -95,12 +96,12 @@ public void limitStageShouldPropagateCancellation() { @Override public Publisher createPublisher(long elements) { return upstream(elements) - .transform().byTakingFirstItems(Long.MAX_VALUE); + .select().first(Long.MAX_VALUE); } @Override public Publisher createFailedPublisher() { return failedUpstream() - .transform().byTakingFirstItems(Long.MAX_VALUE); + .select().first(Long.MAX_VALUE); } } diff --git a/implementation/src/test/java/tck/MultiSelectFirstTckTest.java b/implementation/src/test/java/tck/MultiSelectFirstTckTest.java new file mode 100644 index 000000000..1e1a8b3b0 --- /dev/null +++ b/implementation/src/test/java/tck/MultiSelectFirstTckTest.java @@ -0,0 +1,17 @@ +package tck; + +import org.reactivestreams.Publisher; + +public class MultiSelectFirstTckTest extends AbstractPublisherTck { + @Override + public Publisher createPublisher(long elements) { + return upstream(elements) + .select().first(elements); + } + + @Override + public Publisher createFailedPublisher() { + return failedUpstream() + .select().first(); + } +} diff --git a/implementation/src/test/java/tck/MultiSelectLastTckTest.java b/implementation/src/test/java/tck/MultiSelectLastTckTest.java new file mode 100644 index 000000000..cf7c92952 --- /dev/null +++ b/implementation/src/test/java/tck/MultiSelectLastTckTest.java @@ -0,0 +1,22 @@ +package tck; + +import org.reactivestreams.Publisher; + +public class MultiSelectLastTckTest extends AbstractPublisherTck { + @Override + public Publisher createPublisher(long elements) { + return upstream(elements) + .select().last((int) elements); + } + + @Override + public Publisher createFailedPublisher() { + return failedUpstream() + .select().last(); + } + + @Override + public long maxElementsFromPublisher() { + return 1024; + } +} diff --git a/implementation/src/test/java/tck/MultiSelectUntilOtherSubscriberTckTest.java b/implementation/src/test/java/tck/MultiSelectUntilOtherSubscriberTckTest.java new file mode 100644 index 000000000..ab46e623a --- /dev/null +++ b/implementation/src/test/java/tck/MultiSelectUntilOtherSubscriberTckTest.java @@ -0,0 +1,17 @@ +package tck; + +import org.reactivestreams.Subscriber; + +import io.smallrye.mutiny.helpers.test.AssertSubscriber; +import io.smallrye.mutiny.operators.multi.MultiSelectFirstUntilOtherOp; + +public class MultiSelectUntilOtherSubscriberTckTest extends AbstractBlackBoxSubscriberTck { + @Override + public Subscriber createSubscriber() { + AssertSubscriber subscriber = AssertSubscriber.create(1024); + MultiSelectFirstUntilOtherOp.TakeUntilMainProcessor main = new MultiSelectFirstUntilOtherOp.TakeUntilMainProcessor<>( + subscriber); + return new MultiSelectFirstUntilOtherOp.TakeUntilOtherSubscriber<>(main); + } + +} diff --git a/implementation/src/test/java/tck/MultiSelectWhenTckTest.java b/implementation/src/test/java/tck/MultiSelectWhenTckTest.java new file mode 100644 index 000000000..57b8b81b5 --- /dev/null +++ b/implementation/src/test/java/tck/MultiSelectWhenTckTest.java @@ -0,0 +1,23 @@ +package tck; + +import org.reactivestreams.Publisher; + +import io.smallrye.mutiny.Uni; + +public class MultiSelectWhenTckTest extends AbstractPublisherTck { + + private static final Uni UNI_PRODUCING_TRUE = Uni.createFrom().item(true); + + @Override + public Publisher createPublisher(long elements) { + return upstream(elements) + .select().when(l -> UNI_PRODUCING_TRUE); + } + + @Override + public Publisher createFailedPublisher() { + return failedUpstream() + .select().when(l -> UNI_PRODUCING_TRUE); + } + +} diff --git a/implementation/src/test/java/tck/MultiDistinctUntilChangedTckTest.java b/implementation/src/test/java/tck/MultiSelectWhereTckTest.java similarity index 59% rename from implementation/src/test/java/tck/MultiDistinctUntilChangedTckTest.java rename to implementation/src/test/java/tck/MultiSelectWhereTckTest.java index b952fe6b9..ee426aca3 100644 --- a/implementation/src/test/java/tck/MultiDistinctUntilChangedTckTest.java +++ b/implementation/src/test/java/tck/MultiSelectWhereTckTest.java @@ -2,18 +2,18 @@ import org.reactivestreams.Publisher; -public class MultiDistinctUntilChangedTckTest extends AbstractPublisherTck { +public class MultiSelectWhereTckTest extends AbstractPublisherTck { @Override public Publisher createPublisher(long elements) { return upstream(elements) - .transform().byDroppingRepetitions(); + .select().where(l -> true); } @Override public Publisher createFailedPublisher() { return failedUpstream() - .transform().byDroppingRepetitions(); + .select().where(l -> true); } } diff --git a/implementation/src/test/java/tck/MultiTakeItemsWhileTckTest.java b/implementation/src/test/java/tck/MultiSelectWhileTckTest.java similarity index 84% rename from implementation/src/test/java/tck/MultiTakeItemsWhileTckTest.java rename to implementation/src/test/java/tck/MultiSelectWhileTckTest.java index 16f0b4c34..d1b2220af 100644 --- a/implementation/src/test/java/tck/MultiTakeItemsWhileTckTest.java +++ b/implementation/src/test/java/tck/MultiSelectWhileTckTest.java @@ -15,12 +15,12 @@ import io.smallrye.mutiny.Multi; -public class MultiTakeItemsWhileTckTest extends AbstractPublisherTck { +public class MultiSelectWhileTckTest extends AbstractPublisherTck { @Test public void takeWhileStageShouldTakeWhileConditionIsTrue() { assertEquals(await(Multi.createFrom().items(1, 2, 3, 4, 5, 6, 1, 2) - .transform().byTakingItemsWhile(i -> i < 5) + .select().first(i -> i < 5) .collect().asList() .subscribeAsCompletionStage()), Arrays.asList(1, 2, 3, 4)); } @@ -28,7 +28,7 @@ public void takeWhileStageShouldTakeWhileConditionIsTrue() { @Test public void takeWhileStageShouldEmitEmpty() { assertEquals(await(Multi.createFrom().items(1, 2, 3, 4, 5, 6) - .transform().byTakingItemsWhile(i -> false) + .select().first(i -> false) .collect().asList() .subscribeAsCompletionStage()), Collections.emptyList()); } @@ -38,7 +38,7 @@ public void takeWhileShouldCancelUpStreamWhenDone() { CompletableFuture cancelled = new CompletableFuture<>(); infiniteStream() .onTermination().invoke(() -> cancelled.complete(null)) - .transform().byTakingItemsWhile(t -> false) + .select().first(t -> false) .collect().asList() .subscribeAsCompletionStage(); await(cancelled); @@ -55,7 +55,7 @@ public void takeWhileShouldIgnoreSubsequentErrorsWhenDone() { return Multi.createFrom().items(i); } }) - .transform().byTakingItemsWhile(t -> t < 3) + .select().first(t -> t < 3) .collect().asList() .subscribeAsCompletionStage()), Arrays.asList(1, 2)); @@ -67,7 +67,7 @@ public void takeWhileStageShouldHandleErrors() { CompletableFuture cancelled = new CompletableFuture<>(); CompletionStage> result = infiniteStream() .onTermination().invoke(() -> cancelled.complete(null)) - .transform().byTakingItemsWhile(i -> { + .select().first(i -> { throw new QuietRuntimeException("failed"); }) .collect().asList() @@ -80,12 +80,12 @@ public void takeWhileStageShouldHandleErrors() { @Override public Publisher createPublisher(long elements) { return upstream(elements) - .transform().byTakingItemsWhile(x -> true); + .select().first(x -> true); } @Override public Publisher createFailedPublisher() { return failedUpstream() - .transform().byTakingItemsWhile(x -> true); + .select().first(x -> true); } } diff --git a/implementation/src/test/java/tck/MultiSkipFirstItemsTckTest.java b/implementation/src/test/java/tck/MultiSkipFirstItemsTckTest.java new file mode 100644 index 000000000..11738e0c3 --- /dev/null +++ b/implementation/src/test/java/tck/MultiSkipFirstItemsTckTest.java @@ -0,0 +1,18 @@ +package tck; + +import org.reactivestreams.Publisher; + +public class MultiSkipFirstItemsTckTest extends AbstractPublisherTck { + + @Override + public Publisher createPublisher(long elements) { + return upstream(elements) + .skip().first(0); + } + + @Override + public Publisher createFailedPublisher() { + return failedUpstream() + .skip().first(0); + } +} diff --git a/implementation/src/test/java/tck/MultiSkipRepetitionsTest.java b/implementation/src/test/java/tck/MultiSkipRepetitionsTest.java new file mode 100644 index 000000000..3854ef975 --- /dev/null +++ b/implementation/src/test/java/tck/MultiSkipRepetitionsTest.java @@ -0,0 +1,19 @@ +package tck; + +import org.reactivestreams.Publisher; + +public class MultiSkipRepetitionsTest extends AbstractPublisherTck { + + @Override + public Publisher createPublisher(long elements) { + return upstream(elements) + .skip().repetitions(); + } + + @Override + public Publisher createFailedPublisher() { + return failedUpstream() + .skip().repetitions(); + } + +} diff --git a/implementation/src/test/java/tck/MultiSkipWhenTckTest.java b/implementation/src/test/java/tck/MultiSkipWhenTckTest.java new file mode 100644 index 000000000..ef6b5ab4c --- /dev/null +++ b/implementation/src/test/java/tck/MultiSkipWhenTckTest.java @@ -0,0 +1,23 @@ +package tck; + +import org.reactivestreams.Publisher; + +import io.smallrye.mutiny.Uni; + +public class MultiSkipWhenTckTest extends AbstractPublisherTck { + + private static final Uni UNI_PRODUCING_FALSE = Uni.createFrom().item(false); + + @Override + public Publisher createPublisher(long elements) { + return upstream(elements) + .skip().when(l -> UNI_PRODUCING_FALSE); + } + + @Override + public Publisher createFailedPublisher() { + return failedUpstream() + .skip().when(l -> UNI_PRODUCING_FALSE); + } + +} diff --git a/implementation/src/test/java/tck/MultiSkipWhereTckTest.java b/implementation/src/test/java/tck/MultiSkipWhereTckTest.java new file mode 100644 index 000000000..e28bfa451 --- /dev/null +++ b/implementation/src/test/java/tck/MultiSkipWhereTckTest.java @@ -0,0 +1,19 @@ +package tck; + +import org.reactivestreams.Publisher; + +public class MultiSkipWhereTckTest extends AbstractPublisherTck { + + @Override + public Publisher createPublisher(long elements) { + return upstream(elements) + .skip().where(l -> false); + } + + @Override + public Publisher createFailedPublisher() { + return failedUpstream() + .skip().where(l -> false); + } + +} diff --git a/implementation/src/test/java/tck/MultiSkipItemsWhileTckTest.java b/implementation/src/test/java/tck/MultiSkipWhileTckTest.java similarity index 84% rename from implementation/src/test/java/tck/MultiSkipItemsWhileTckTest.java rename to implementation/src/test/java/tck/MultiSkipWhileTckTest.java index 39ecaf77d..5f98efd2a 100644 --- a/implementation/src/test/java/tck/MultiSkipItemsWhileTckTest.java +++ b/implementation/src/test/java/tck/MultiSkipWhileTckTest.java @@ -16,12 +16,12 @@ import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.helpers.test.AssertSubscriber; -public class MultiSkipItemsWhileTckTest extends AbstractPublisherTck { +public class MultiSkipWhileTckTest extends AbstractPublisherTck { @Test public void dropWhileStageShouldSupportDroppingElements() { assertEquals(await(Multi.createFrom().items(1, 2, 3, 4, 0) - .transform().bySkippingItemsWhile(i -> i < 3) + .skip().first(i -> i < 3) .collect().asList() .subscribeAsCompletionStage()), Arrays.asList(3, 4, 0)); } @@ -32,7 +32,7 @@ public void dropWhileStageShouldHandleErrors() { CompletableFuture cancelled = new CompletableFuture<>(); CompletionStage> result = infiniteStream() .onTermination().invoke(() -> cancelled.complete(null)) - .transform().bySkippingItemsWhile(i -> { + .skip().first(i -> { throw new QuietRuntimeException("failed"); }) .collect().asList() @@ -46,7 +46,7 @@ public void dropWhileStageShouldHandleErrors() { public void dropWhileStageShouldPropagateUpstreamErrorsWhileDropping() { assertThrows(QuietRuntimeException.class, () -> await(Multi.createFrom(). failure(new QuietRuntimeException("failed")) - .transform().bySkippingItemsWhile(i -> i < 3) + .skip().first(i -> i < 3) .collect().asList() .subscribeAsCompletionStage())); } @@ -59,7 +59,7 @@ public void dropWhileStageShouldPropagateUpstreamErrorsAfterFinishedDropping() { throw new QuietRuntimeException("failed"); } }) - .transform().bySkippingItemsWhile(i -> i < 3) + .skip().first(i -> i < 3) .collect().asList() .subscribeAsCompletionStage())); } @@ -67,7 +67,7 @@ public void dropWhileStageShouldPropagateUpstreamErrorsAfterFinishedDropping() { @Test public void dropWhileStageShouldNotRunPredicateOnceItsFinishedDropping() { assertEquals(await(Multi.createFrom().items(1, 2, 3, 4) - .transform().bySkippingItemsWhile(i -> { + .skip().first(i -> { if (i < 3) { return true; } else if (i == 4) { @@ -83,7 +83,7 @@ public void dropWhileStageShouldNotRunPredicateOnceItsFinishedDropping() { @Test public void dropWhileStageShouldAllowCompletionWhileDropping() { assertEquals(await(Multi.createFrom().items(1, 1, 1, 1) - .transform().bySkippingItemsWhile(i -> i < 3) + .skip().first(i -> i < 3) .collect().asList() .subscribeAsCompletionStage()), Collections.emptyList()); } @@ -93,7 +93,7 @@ public void dropWhileStageShouldPropagateCancel() { CompletableFuture cancelled = new CompletableFuture<>(); infiniteStream() .onTermination().invoke(() -> cancelled.complete(null)) - .transform().bySkippingItemsWhile(i -> i < 3) + .skip().first(i -> i < 3) .subscribe().withSubscriber(new AssertSubscriber<>(10, true)); await(cancelled); } @@ -101,12 +101,12 @@ public void dropWhileStageShouldPropagateCancel() { @Override public Publisher createPublisher(long elements) { return upstream(elements) - .transform().bySkippingItemsWhile(i -> false); + .skip().first(i -> false); } @Override public Publisher createFailedPublisher() { return failedUpstream() - .transform().bySkippingItemsWhile(i -> false); + .skip().first(i -> false); } } diff --git a/implementation/src/test/java/tck/TakeUntilOtherSubscriberTckTest.java b/implementation/src/test/java/tck/TakeUntilOtherSubscriberTckTest.java deleted file mode 100644 index fdaf3c779..000000000 --- a/implementation/src/test/java/tck/TakeUntilOtherSubscriberTckTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package tck; - -import org.reactivestreams.Subscriber; - -import io.smallrye.mutiny.helpers.test.AssertSubscriber; -import io.smallrye.mutiny.operators.multi.MultiTakeUntilOtherOp; - -public class TakeUntilOtherSubscriberTckTest extends AbstractBlackBoxSubscriberTck { - @Override - public Subscriber createSubscriber() { - AssertSubscriber subscriber = AssertSubscriber.create(1024); - MultiTakeUntilOtherOp.TakeUntilMainProcessor main = new MultiTakeUntilOtherOp.TakeUntilMainProcessor<>( - subscriber); - return new MultiTakeUntilOtherOp.TakeUntilOtherSubscriber<>(main); - } - -} diff --git a/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/DistinctStageFactory.java b/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/DistinctStageFactory.java index 8a9ab2dc5..3d35afa2e 100644 --- a/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/DistinctStageFactory.java +++ b/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/DistinctStageFactory.java @@ -20,6 +20,6 @@ public class DistinctStageFactory implements ProcessingStageFactory ProcessingStage create(Engine engine, Stage.Distinct stage) { Objects.requireNonNull(stage); - return source -> (Multi) source.transform().byDroppingDuplicates(); + return source -> (Multi) source.select().distinct(); } } diff --git a/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/DropWhileStageFactory.java b/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/DropWhileStageFactory.java index 338fdaa70..a62c7e964 100644 --- a/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/DropWhileStageFactory.java +++ b/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/DropWhileStageFactory.java @@ -33,7 +33,7 @@ private static class TakeWhile implements ProcessingStage { @Override public Multi apply(Multi source) { - return source.transform().bySkippingItemsWhile(predicate); + return source.skip().first(predicate); } } } diff --git a/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/FilterStageFactory.java b/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/FilterStageFactory.java index f6556bfb0..89936d13f 100644 --- a/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/FilterStageFactory.java +++ b/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/FilterStageFactory.java @@ -22,6 +22,6 @@ public class FilterStageFactory implements ProcessingStageFactory public ProcessingStage create(Engine engine, Stage.Filter stage) { Objects.requireNonNull(stage); Predicate predicate = Objects.requireNonNull(stage.getPredicate()); - return source -> (Multi) source.transform().byFilteringItemsWith(predicate); + return source -> (Multi) source.select().where(predicate); } } diff --git a/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/LimitStageFactory.java b/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/LimitStageFactory.java index d7bfefd2d..8a60c5cec 100644 --- a/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/LimitStageFactory.java +++ b/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/LimitStageFactory.java @@ -18,6 +18,6 @@ public class LimitStageFactory implements ProcessingStageFactory { @Override public ProcessingStage create(Engine engine, Stage.Limit stage) { long limit = stage.getLimit(); - return source -> (Multi) source.transform().byTakingFirstItems(limit); + return source -> (Multi) source.select().first(limit); } } diff --git a/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/SkipStageFactory.java b/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/SkipStageFactory.java index f0a89d42d..6e9dd1e27 100644 --- a/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/SkipStageFactory.java +++ b/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/SkipStageFactory.java @@ -18,6 +18,6 @@ public class SkipStageFactory implements ProcessingStageFactory { @Override public ProcessingStage create(Engine engine, Stage.Skip stage) { long skip = stage.getSkip(); - return source -> (Multi) source.transform().bySkippingFirstItems(skip); + return source -> (Multi) source.skip().first(skip); } } diff --git a/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/TakeWhileStageFactory.java b/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/TakeWhileStageFactory.java index cc8faae33..deb36d5b2 100644 --- a/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/TakeWhileStageFactory.java +++ b/reactive-streams-operators/src/main/java/io/smallrye/mutiny/streams/stages/TakeWhileStageFactory.java @@ -21,6 +21,6 @@ public class TakeWhileStageFactory implements ProcessingStageFactory ProcessingStage create(Engine engine, Stage.TakeWhile stage) { Predicate predicate = (Predicate) Objects.requireNonNull(stage.getPredicate()); - return source -> (Multi) source.transform().byTakingItemsWhile(predicate); + return source -> (Multi) source.select().first(predicate); } } diff --git a/reactor/revapi.json b/reactor/revapi.json index 013c5e2af..f9e192c86 100644 --- a/reactor/revapi.json +++ b/reactor/revapi.json @@ -27,7 +27,18 @@ "id": "breaking-changes", "configuration": { "criticality": "highlight", - "differences": [] + "differences": [ + { + "code": "java.class.externalClassExposedInAPI", + "new": "class io.smallrye.mutiny.groups.MultiSelect", + "justification": "Addition of the new `select` group to Multi. If you are impacted by such a change, we recommend extending `AbstractMulti` instead of implementing `Multi` directly." + }, + { + "code": "java.class.externalClassExposedInAPI", + "new": "class io.smallrye.mutiny.groups.MultiSkip", + "justification": "Addition of the new `skip` group to Multi. If you are impacted by such a change, we recommend extending `AbstractMulti` instead of implementing `Multi` directly." + } + ] } }, diff --git a/rxjava/revapi.json b/rxjava/revapi.json index 013c5e2af..f9e192c86 100644 --- a/rxjava/revapi.json +++ b/rxjava/revapi.json @@ -27,7 +27,18 @@ "id": "breaking-changes", "configuration": { "criticality": "highlight", - "differences": [] + "differences": [ + { + "code": "java.class.externalClassExposedInAPI", + "new": "class io.smallrye.mutiny.groups.MultiSelect", + "justification": "Addition of the new `select` group to Multi. If you are impacted by such a change, we recommend extending `AbstractMulti` instead of implementing `Multi` directly." + }, + { + "code": "java.class.externalClassExposedInAPI", + "new": "class io.smallrye.mutiny.groups.MultiSkip", + "justification": "Addition of the new `skip` group to Multi. If you are impacted by such a change, we recommend extending `AbstractMulti` instead of implementing `Multi` directly." + } + ] } },