/* * Copyright (c) 2011-2017 Pivotal Software Inc, All Rights Reserved. * * 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 reactor.core.publisher; import java.time.Duration; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import org.assertj.core.api.AssertionsForClassTypes; import org.junit.Assert; import org.junit.Test; import org.reactivestreams.Subscriber; import reactor.core.scheduler.Scheduler; import reactor.core.scheduler.Schedulers; import reactor.test.StepVerifier; import static org.assertj.core.api.Assertions.assertThat; public class FluxProcessorTest { @Test(expected = NullPointerException.class) @SuppressWarnings("unchecked") public void failNullSubscriber(){ FluxProcessor.wrap(UnicastProcessor.create(), UnicastProcessor.create()) .subscribe((Subscriber)null); } @Test(expected = NullPointerException.class) public void failNullUpstream(){ FluxProcessor.wrap(null, UnicastProcessor.create()); } @Test(expected = NullPointerException.class) public void failNullDownstream(){ FluxProcessor.wrap(UnicastProcessor.create(), null); } @Test public void testCapacity(){ assertThat(FluxProcessor.wrap(UnicastProcessor.create(), UnicastProcessor .create()).getBufferSize()) .isEqualTo(Integer.MAX_VALUE); } @Test @SuppressWarnings("unchecked") public void normalBlackboxProcessor(){ UnicastProcessor<Integer> upstream = UnicastProcessor.create(); FluxProcessor<Integer, Integer> processor = FluxProcessor.wrap(upstream, upstream.map(i -> i + 1) .filter(i -> i % 2 == 0)); DelegateProcessor<Integer, Integer> delegateProcessor = (DelegateProcessor<Integer, Integer>)processor; delegateProcessor.parents().findFirst().ifPresent(s -> assertThat(s).isInstanceOf(FluxFilterFuseable.class)); StepVerifier.create(processor) .then(() -> Flux.just(1, 2, 3).subscribe(processor)) .expectNext(2, 4) .verifyComplete(); } @Test public void disconnectedBlackboxProcessor(){ UnicastProcessor<Integer> upstream = UnicastProcessor.create(); FluxProcessor<Integer, Integer> processor = FluxProcessor.wrap(upstream, Flux.just(1)); StepVerifier.create(processor) .expectNext(1) .verifyComplete(); } @Test public void symmetricBlackboxProcessor(){ UnicastProcessor<Integer> upstream = UnicastProcessor.create(); FluxProcessor<Integer, Integer> processor = FluxProcessor.wrap(upstream, upstream); StepVerifier.create(processor) .then(() -> Flux.just(1).subscribe(processor)) .expectNext(1) .verifyComplete(); } @Test public void errorSymmetricBlackboxProcessor(){ UnicastProcessor<Integer> upstream = UnicastProcessor.create(); FluxProcessor<Integer, Integer> processor = FluxProcessor.wrap(upstream, upstream); StepVerifier.create(processor) .then(() -> Flux.<Integer>error(new Exception("test")).subscribe(processor)) .verifyErrorMessage("test"); } @Test public void testSubmitSession() throws Exception { FluxProcessor<Integer, Integer> processor = EmitterProcessor.create(); AtomicInteger count = new AtomicInteger(); CountDownLatch latch = new CountDownLatch(1); Scheduler scheduler = Schedulers.parallel(); processor.publishOn(scheduler) .delaySubscription(Duration.ofMillis(1000)) .limitRate(1) .subscribe(d -> { count.incrementAndGet(); latch.countDown(); }); FluxSink<Integer> session = processor.sink(); session.next(1); //System.out.println(emission); session.complete(); latch.await(5, TimeUnit.SECONDS); Assert.assertTrue("latch : " + count, count.get() == 0); scheduler.dispose(); } @Test public void testEmitter() throws Throwable { FluxProcessor<Integer, Integer> processor = EmitterProcessor.create(); int n = 100_000; int subs = 4; final CountDownLatch latch = new CountDownLatch((n + 1) * subs); Scheduler c = Schedulers.single(); for (int i = 0; i < subs; i++) { processor.publishOn(c) .limitRate(1) .subscribe(d -> latch.countDown(), null, latch::countDown); } FluxSink<Integer> session = processor.sink(); for (int i = 0; i < n; i++) { while (session.requestedFromDownstream() == 0) { } session.next(i); } session.complete(); boolean waited = latch.await(5, TimeUnit.SECONDS); Assert.assertTrue( "latch : " + latch.getCount(), waited); c.dispose(); } @Test public void testEmitter2() throws Throwable { FluxProcessor<Integer, Integer> processor = EmitterProcessor.create(); int n = 100_000; int subs = 4; final CountDownLatch latch = new CountDownLatch((n + 1) * subs); Scheduler c = Schedulers.single(); for (int i = 0; i < subs; i++) { processor.publishOn(c) .doOnComplete(latch::countDown) .doOnNext(d -> latch.countDown()) .subscribe(); } FluxSink<Integer> session = processor.sink(); for (int i = 0; i < n; i++) { while (session.requestedFromDownstream() == 0) { } session.next(i); } session.complete(); boolean waited = latch.await(5, TimeUnit.SECONDS); Assert.assertTrue( "latch : " + latch.getCount(), waited); c.dispose(); } @Test public void serializedConcurrent() { Scheduler.Worker w1 = Schedulers.elastic().createWorker(); Scheduler.Worker w2 = Schedulers.elastic().createWorker(); CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch2 = new CountDownLatch(1); AtomicReference<Thread> ref = new AtomicReference<>(); ref.set(Thread.currentThread()); DirectProcessor<String> rp = DirectProcessor.create(); FluxProcessor<String, String> serialized = rp.serialize(); try { StepVerifier.create(serialized) .then(() -> { w1.schedule(() -> serialized.onNext("test1")); try { latch2.await(); } catch (InterruptedException e) { Assert.fail(); } w2.schedule(() -> { serialized.onNext("test2"); serialized.onNext("test3"); serialized.onComplete(); latch.countDown(); }); }) .assertNext(s -> { AssertionsForClassTypes.assertThat(s).isEqualTo("test1"); AssertionsForClassTypes.assertThat(ref.get()).isNotEqualTo(Thread.currentThread()); ref.set(Thread.currentThread()); latch2.countDown(); try { latch.await(); } catch (InterruptedException e) { Assert.fail(); } }) .assertNext(s -> { AssertionsForClassTypes.assertThat(ref.get()).isEqualTo(Thread.currentThread()); AssertionsForClassTypes.assertThat(s).isEqualTo("test2"); }) .assertNext(s -> { AssertionsForClassTypes.assertThat(ref.get()).isEqualTo(Thread.currentThread()); AssertionsForClassTypes.assertThat(s).isEqualTo("test3"); }) .verifyComplete(); } finally { w1.dispose(); w2.dispose(); } } }