/******************************************************************************* * Copyright (c) 2018 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. * * Licensed under the Apache License, Version 2.0 (the "License"); * You may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package org.eclipse.microprofile.reactive.streams.operators.tck.api; import org.eclipse.microprofile.reactive.streams.operators.CompletionRunner; import org.eclipse.microprofile.reactive.streams.operators.ProcessorBuilder; import org.eclipse.microprofile.reactive.streams.operators.PublisherBuilder; import org.eclipse.microprofile.reactive.streams.operators.ReactiveStreamsFactory; import org.eclipse.microprofile.reactive.streams.operators.SubscriberBuilder; import org.eclipse.microprofile.reactive.streams.operators.spi.Graph; import org.eclipse.microprofile.reactive.streams.operators.spi.ReactiveStreamsEngine; import org.eclipse.microprofile.reactive.streams.operators.spi.Stage; import org.eclipse.microprofile.reactive.streams.operators.spi.SubscriberWithCompletionStage; import org.eclipse.microprofile.reactive.streams.operators.spi.UnsupportedStageException; import org.reactivestreams.Processor; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.testng.annotations.Test; import java.util.Arrays; import java.util.Iterator; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Collector; import java.util.stream.Collectors; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNull; import static org.testng.Assert.assertSame; import static org.testng.Assert.assertTrue; /** * Verification for the {@link org.eclipse.microprofile.reactive.streams.operators.PublisherBuilder} class. */ public class PublisherBuilderVerification extends AbstractReactiveStreamsApiVerification { public PublisherBuilderVerification(ReactiveStreamsFactory rs) { super(rs); } @Test public void map() { Graph graph = graphFor(builder().map(i -> i + 1)); assertEquals(((Function) getAddedStage(Stage.Map.class, graph).getMapper()).apply(1), 2); } @Test(expectedExceptions = NullPointerException.class) public void mapNullFunction() { builder().map(null); } @Test public void peek() { AtomicInteger peeked = new AtomicInteger(); Graph graph = graphFor(builder().peek(peeked::set)); ((Consumer) getAddedStage(Stage.Peek.class, graph).getConsumer()).accept(1); assertEquals(peeked.get(), 1); } @Test(expectedExceptions = NullPointerException.class) public void peekNullConsumer() { builder().peek(null); } @Test public void filter() { Graph graph = graphFor(builder().filter(i -> i < 3)); assertTrue(((Predicate) getAddedStage(Stage.Filter.class, graph).getPredicate()).test(1)); } @Test(expectedExceptions = NullPointerException.class) public void filterNullPredicate() { builder().filter(null); } @Test public void distinct() { Graph graph = graphFor(builder().distinct()); getAddedStage(Stage.Distinct.class, graph); } @Test public void flatMap() { Graph graph = graphFor(builder().flatMap(i -> rs.empty())); Function flatMap = getAddedStage(Stage.FlatMap.class, graph).getMapper(); Object result = flatMap.apply(1); assertTrue(result instanceof Graph); Graph innerGraph = (Graph) result; assertEquals(innerGraph.getStages().size(), 1); assertEmptyStage(innerGraph.getStages().iterator().next()); } @Test public void flatMapToBuilderFromDifferentReactiveStreamsImplementation() { Graph graph = graphFor(builder().flatMap(i -> Mocks.EMPTY_PUBLISHER_BUILDER)); Function flatMap = getAddedStage(Stage.FlatMap.class, graph).getMapper(); Object result = flatMap.apply(1); assertTrue(result instanceof Graph); assertSame(result, Mocks.EMPTY_PUBLISHER_GRAPH); } @Test(expectedExceptions = NullPointerException.class) public void flatMapNullMapper() { builder().flatMap(null); } @Test public void flatMapRsPublisher() { Graph graph = graphFor(builder().flatMapRsPublisher(i -> Mocks.PUBLISHER)); Function flatMap = getAddedStage(Stage.FlatMap.class, graph).getMapper(); Object result = flatMap.apply(1); assertTrue(result instanceof Graph); Graph innerGraph = (Graph) result; assertEquals(innerGraph.getStages().size(), 1); Stage inner = innerGraph.getStages().iterator().next(); assertTrue(inner instanceof Stage.PublisherStage); assertEquals(((Stage.PublisherStage) inner).getRsPublisher(), Mocks.PUBLISHER); } @Test(expectedExceptions = NullPointerException.class) public void flatMapRsPublisherNullMapper() { builder().flatMapRsPublisher(null); } @Test public void flatMapCompletionStage() throws Exception { Graph graph = graphFor(builder().flatMapCompletionStage(i -> CompletableFuture.completedFuture(i + 1))); CompletionStage result = (CompletionStage) ((Function) getAddedStage(Stage.FlatMapCompletionStage.class, graph).getMapper()).apply(1); assertEquals(result.toCompletableFuture().get(1, TimeUnit.SECONDS), 2); } @Test(expectedExceptions = NullPointerException.class) public void flatMapCompletionStageNullMapper() { builder().flatMapCompletionStage(null); } @Test public void flatMapIterable() { Graph graph = graphFor(builder().flatMapIterable(i -> Arrays.asList(i, i + 1))); assertEquals(((Function) getAddedStage(Stage.FlatMapIterable.class, graph).getMapper()).apply(1), Arrays.asList(1, 2)); } @Test(expectedExceptions = NullPointerException.class) public void flatMapIterableNullMapper() { builder().flatMapIterable(null); } @Test public void limit() { Graph graph = graphFor(builder().limit(3)); assertEquals(getAddedStage(Stage.Limit.class, graph).getLimit(), 3); } @Test(expectedExceptions = IllegalArgumentException.class) public void limitNegative() { builder().limit(-1); } @Test public void skip() { Graph graph = graphFor(builder().skip(3)); assertEquals(getAddedStage(Stage.Skip.class, graph).getSkip(), 3); } @Test(expectedExceptions = IllegalArgumentException.class) public void skipNegative() { builder().skip(-1); } @Test public void takeWhile() { Graph graph = graphFor(builder().takeWhile(i -> i < 3)); assertTrue(((Predicate) getAddedStage(Stage.TakeWhile.class, graph).getPredicate()).test(1)); } @Test(expectedExceptions = NullPointerException.class) public void takeWhileNullPredicate() { builder().takeWhile(null); } @Test public void dropWhile() { Graph graph = graphFor(builder().dropWhile(i -> i < 3)); assertTrue(((Predicate) getAddedStage(Stage.DropWhile.class, graph).getPredicate()).test(1)); } @Test(expectedExceptions = NullPointerException.class) public void dropWhileNullPredicate() { builder().dropWhile(null); } @Test public void forEach() { AtomicInteger received = new AtomicInteger(); Graph graph = graphFor(builder().forEach(received::set)); Collector collector = getAddedStage(Stage.Collect.class, graph).getCollector(); Object container = collector.supplier().get(); collector.accumulator().accept(container, 1); assertEquals(received.get(), 1); assertNull(collector.finisher().apply(container)); } @Test(expectedExceptions = NullPointerException.class) public void forEachNullConsumer() { builder().forEach(null); } @Test public void ignore() { Graph graph = graphFor(builder().ignore()); Collector collector = getAddedStage(Stage.Collect.class, graph).getCollector(); Object container = collector.supplier().get(); collector.accumulator().accept(container, 1); assertNull(collector.finisher().apply(container)); } @Test public void cancel() { Graph graph = graphFor(builder().cancel()); getAddedStage(Stage.Cancel.class, graph); } @Test public void reduceWithIdentity() { Graph graph = graphFor(builder().reduce(1, (a, b) -> a - b)); Collector collector = getAddedStage(Stage.Collect.class, graph).getCollector(); Object container1 = collector.supplier().get(); assertEquals(collector.finisher().apply(container1), 1); // Create a new container because we don't necessarily want to require that the container be reusable after // the finishers has been applied to it. Object container2 = collector.supplier().get(); collector.accumulator().accept(container2, 3); assertEquals(collector.finisher().apply(container2), -2); } @Test public void reduceWithIdentityNullIdentityAllowed() { builder().reduce(null, (a, b) -> a); } @Test(expectedExceptions = NullPointerException.class) public void reduceWithIdentityNullAccumulator() { builder().reduce(1, null); } @Test public void reduce() { Graph graph = graphFor(builder().reduce((a, b) -> a - b)); Collector collector = getAddedStage(Stage.Collect.class, graph).getCollector(); Object container1 = collector.supplier().get(); assertEquals(collector.finisher().apply(container1), Optional.empty()); Object container2 = collector.supplier().get(); collector.accumulator().accept(container2, 2); assertEquals(collector.finisher().apply(container2), Optional.of(2)); Object container3 = collector.supplier().get(); collector.accumulator().accept(container3, 5); collector.accumulator().accept(container3, 2); assertEquals(collector.finisher().apply(container3), Optional.of(3)); } @Test(expectedExceptions = NullPointerException.class) public void reduceNullAccumulator() { builder().reduce(null); } @Test public void findFirst() { Graph graph = graphFor(builder().findFirst()); getAddedStage(Stage.FindFirst.class, graph); } @Test public void collect() { Collector collector = Collectors.toList(); Graph graph = graphFor(builder().collect(collector)); assertSame(getAddedStage(Stage.Collect.class, graph).getCollector(), collector); } @Test(expectedExceptions = NullPointerException.class) public void collectNull() { builder().collect(null); } @Test public void collectComponents() { Supplier supplier = () -> null; BiConsumer accumulator = (a, b) -> {}; Graph graph = graphFor(builder().collect(supplier, accumulator)); Collector collector = getAddedStage(Stage.Collect.class, graph).getCollector(); assertSame(collector.supplier(), supplier); assertSame(collector.accumulator(), accumulator); // Finisher must be identity function Object myObject = new Object(); assertSame(collector.finisher().apply(myObject), myObject); } @Test(expectedExceptions = NullPointerException.class) public void collectComponentsSupplierNull() { builder().collect(null, (a, b) -> {}); } @Test(expectedExceptions = NullPointerException.class) public void collectComponentsAccumulatorNull() { builder().collect(() -> null, null); } @Test public void toList() { Graph graph = graphFor(builder().toList()); Collector collector = getAddedStage(Stage.Collect.class, graph).getCollector(); Object container = collector.supplier().get(); collector.accumulator().accept(container, 1); collector.accumulator().accept(container, 2); collector.accumulator().accept(container, 3); assertEquals(collector.finisher().apply(container), Arrays.asList(1, 2, 3)); } @Test public void toSubscriber() { Graph graph = graphFor(builder().to(Mocks.SUBSCRIBER)); assertSame(getAddedStage(Stage.SubscriberStage.class, graph).getRsSubscriber(), Mocks.SUBSCRIBER); } @Test(expectedExceptions = NullPointerException.class) public void toSubscriberNull() { builder().to((Subscriber) null); } @Test public void to() { Graph graph = graphFor(builder().to(rs.fromSubscriber(Mocks.SUBSCRIBER))); assertSame(getAddedStage(Stage.SubscriberStage.class, graph).getRsSubscriber(), Mocks.SUBSCRIBER); } @Test public void toBuilderFromDifferentReactiveStreamsImplementation() { Graph graph = graphFor(builder().to(Mocks.SUBSCRIBER_BUILDER)); assertEquals(graph.getStages().size(), 3); Iterator<Stage> stages = graph.getStages().iterator(); assertTrue(stages.next() instanceof Stage.Of); assertTrue(stages.next() instanceof Stage.Distinct); assertTrue(stages.next() instanceof Stage.Cancel); } @Test public void toMultipleStages() { Graph graph = graphFor(builder().to( rs.<Integer>builder().map(Function.identity()).cancel())); assertEquals(graph.getStages().size(), 3); Iterator<Stage> stages = graph.getStages().iterator(); assertTrue(stages.next() instanceof Stage.Of); assertTrue(stages.next() instanceof Stage.Map); assertTrue(stages.next() instanceof Stage.Cancel); } @Test(expectedExceptions = NullPointerException.class) public void toNull() { builder().to((SubscriberBuilder) null); } @Test public void viaProcessor() { Graph graph = graphFor(builder().via(Mocks.PROCESSOR)); assertSame(getAddedStage(Stage.ProcessorStage.class, graph).getRsProcessor(), Mocks.PROCESSOR); } @Test(expectedExceptions = NullPointerException.class) public void viaProcessorNull() { builder().via((Processor) null); } @Test public void via() { Graph graph = graphFor(builder().via(rs.fromProcessor(Mocks.PROCESSOR))); assertSame(getAddedStage(Stage.ProcessorStage.class, graph).getRsProcessor(), Mocks.PROCESSOR); } @Test public void viaBuilderFromDifferentReactiveStreamsImplementation() { Graph graph = graphFor(builder().via(Mocks.PROCESSOR_BUILDER)); assertEquals(graph.getStages().size(), 3); Iterator<Stage> stages = graph.getStages().iterator(); assertTrue(stages.next() instanceof Stage.Of); assertTrue(stages.next() instanceof Stage.Distinct); assertTrue(stages.next() instanceof Stage.Limit); } @Test public void viaEmpty() { Graph graph = graphFor(builder().via(rs.builder())); assertEquals(graph.getStages().size(), 1); assertTrue(graph.getStages().iterator().next() instanceof Stage.Of); } @Test public void viaMultipleStages() { Graph graph = graphFor(builder().via( rs.<Integer>builder().map(Function.identity()).filter(t -> true))); assertEquals(graph.getStages().size(), 3); Iterator<Stage> stages = graph.getStages().iterator(); assertTrue(stages.next() instanceof Stage.Of); assertTrue(stages.next() instanceof Stage.Map); assertTrue(stages.next() instanceof Stage.Filter); } @Test(expectedExceptions = NullPointerException.class) public void viaNull() { builder().via((ProcessorBuilder) null); } @Test public void onError() { Consumer consumer = t -> {}; Graph graph = graphFor(builder().onError(consumer)); assertSame(getAddedStage(Stage.OnError.class, graph).getConsumer(), consumer); } @Test(expectedExceptions = NullPointerException.class) public void onErrorNullConsumer() { builder().onError(null); } @Test public void onErrorResume() { Graph graph = graphFor(builder().onErrorResume(t -> 2)); assertEquals(getAddedStage(Stage.OnErrorResume.class, graph).getFunction().apply(new RuntimeException()), 2); } @Test(expectedExceptions = NullPointerException.class) public void onErrorResumeNull() { builder().onErrorResume(null); } @Test public void onErrorResumeWith() { Graph graph = graphFor(builder().onErrorResumeWith(t -> rs.empty())); Graph resumeWith = getAddedStage(Stage.OnErrorResumeWith.class, graph).getFunction().apply(new RuntimeException()); assertEquals(resumeWith.getStages().size(), 1); assertEmptyStage(resumeWith.getStages().iterator().next()); } @Test public void onErrorResumeWithToBuilderFromDifferentReactiveStreamsImplementation() { Graph graph = graphFor(builder().onErrorResumeWith(t -> Mocks.EMPTY_PUBLISHER_BUILDER)); Graph resumeWith = getAddedStage(Stage.OnErrorResumeWith.class, graph).getFunction().apply(new RuntimeException()); assertSame(resumeWith, Mocks.EMPTY_PUBLISHER_GRAPH); } @Test(expectedExceptions = NullPointerException.class) public void onErrorResumeWithNull() { builder().onErrorResumeWith(null); } @Test public void onErrorResumeWithRsPublisher() { Graph graph = graphFor(builder().onErrorResumeWithRsPublisher(t -> Mocks.PUBLISHER)); Graph resumeWith = getAddedStage(Stage.OnErrorResumeWith.class, graph).getFunction().apply(new RuntimeException()); assertEquals(resumeWith.getStages().size(), 1); assertSame(((Stage.PublisherStage) resumeWith.getStages().iterator().next()).getRsPublisher(), Mocks.PUBLISHER); } @Test(expectedExceptions = NullPointerException.class) public void onErrorResumeWithRsPublisherNull() { builder().onErrorResumeWithRsPublisher(null); } @Test public void onTerminate() { Runnable action = () -> {}; Graph graph = graphFor(builder().onTerminate(action)); assertSame(getAddedStage(Stage.OnTerminate.class, graph).getAction(), action); } @Test(expectedExceptions = NullPointerException.class) public void onTerminateNull() { builder().onTerminate(null); } @Test public void onComplete() { Runnable action = () -> {}; Graph graph = graphFor(builder().onComplete(action)); assertSame(getAddedStage(Stage.OnComplete.class, graph).getAction(), action); } @Test(expectedExceptions = NullPointerException.class) public void onCompleteNull() { builder().onComplete(null); } @Test public void buildRs() { AtomicReference<Graph> builtGraph = new AtomicReference<>(); Publisher publisher = builder().distinct().buildRs(new ReactiveStreamsEngine() { @Override public <T> Publisher<T> buildPublisher(Graph graph) throws UnsupportedStageException { builtGraph.set(graph); return Mocks.PUBLISHER; } @Override public <T, R> SubscriberWithCompletionStage<T, R> buildSubscriber(Graph graph) throws UnsupportedStageException { throw new RuntimeException("Wrong method invoked"); } @Override public <T, R> Processor<T, R> buildProcessor(Graph graph) throws UnsupportedStageException { throw new RuntimeException("Wrong method invoked"); } @Override public <T> CompletionStage<T> buildCompletion(Graph graph) throws UnsupportedStageException { throw new RuntimeException("Wrong method invoked"); } }); assertSame(publisher, Mocks.PUBLISHER); getAddedStage(Stage.Distinct.class, builtGraph.get()); } @Test(expectedExceptions = NullPointerException.class) public void buildRsNull() { builder().buildRs(null); } @Test public void builderShouldBeImmutable() { PublisherBuilder<Integer> builder = builder(); PublisherBuilder<Integer> mapped = builder.map(Function.identity()); PublisherBuilder<Integer> distinct = builder.distinct(); CompletionRunner<Void> cancelled = builder.cancel(); getAddedStage(Stage.Map.class, graphFor(mapped)); getAddedStage(Stage.Distinct.class, graphFor(distinct)); getAddedStage(Stage.Cancel.class, graphFor(cancelled)); } private PublisherBuilder<Integer> builder() { return rs.of(1); } private <S extends Stage> S getAddedStage(Class<S> clazz, Graph graph) { assertEquals(graph.getStages().size(), 2, "Graph does not have two stages"); Iterator<Stage> stages = graph.getStages().iterator(); Stage first = stages.next(); assertTrue(first instanceof Stage.Of, "First stage " + first + " is not a " + Stage.Of.class); Stage second = stages.next(); assertTrue(clazz.isInstance(second), "Second stage " + second + " is not a " + clazz); return clazz.cast(second); } }