/** * Copyright (c) 2016-present, RxJava Contributors. * * 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 io.reactivex.internal.subscribers; import static org.junit.Assert.*; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; import org.reactivestreams.*; import io.reactivex.*; import io.reactivex.Scheduler.Worker; import io.reactivex.exceptions.TestException; import io.reactivex.internal.subscribers.DeferredScalarSubscriber; import io.reactivex.internal.subscriptions.BooleanSubscription; import io.reactivex.plugins.RxJavaPlugins; import io.reactivex.processors.PublishProcessor; import io.reactivex.schedulers.Schedulers; import io.reactivex.subscribers.TestSubscriber; public class DeferredScalarSubscriberTest { @Test public void completeFirst() { TestSubscriber<Integer> ts = TestSubscriber.create(0L); TestingDeferredScalarSubscriber ds = new TestingDeferredScalarSubscriber(ts); ds.setupDownstream(); ds.onNext(1); ts.assertNoValues(); ds.onComplete(); ts.assertNoValues(); ts.request(1); ts.assertValues(1); ts.assertNoErrors(); ts.assertComplete(); } @Test public void requestFirst() { TestSubscriber<Integer> ts = TestSubscriber.create(1); TestingDeferredScalarSubscriber ds = new TestingDeferredScalarSubscriber(ts); ds.setupDownstream(); ds.onNext(1); ts.assertNoValues(); ds.onComplete(); ts.assertValues(1); ts.assertNoErrors(); ts.assertComplete(); } @Test public void empty() { TestSubscriber<Integer> ts = TestSubscriber.create(0L); TestingDeferredScalarSubscriber ds = new TestingDeferredScalarSubscriber(ts); ds.setupDownstream(); ts.assertNoValues(); ds.onComplete(); ts.assertNoValues(); ts.assertNoErrors(); ts.assertComplete(); } @Test public void error() { TestSubscriber<Integer> ts = TestSubscriber.create(0L); TestingDeferredScalarSubscriber ds = new TestingDeferredScalarSubscriber(ts); ds.setupDownstream(); ts.assertNoValues(); ds.onError(new TestException()); ts.assertNoValues(); ts.assertError(TestException.class); ts.assertNotComplete(); } @Test public void unsubscribeComposes() { PublishProcessor<Integer> ps = PublishProcessor.create(); TestSubscriber<Integer> ts = TestSubscriber.create(0L); TestingDeferredScalarSubscriber ds = new TestingDeferredScalarSubscriber(ts); ps.subscribe(ds); assertTrue("No subscribers?", ps.hasSubscribers()); ts.cancel(); ds.onNext(1); ds.onComplete(); ts.request(1); ts.assertNoValues(); ts.assertNoErrors(); ts.assertNotComplete(); assertFalse("Subscribers?", ps.hasSubscribers()); assertTrue("Deferred not unsubscribed?", ds.isCancelled()); } @Test public void emptySource() { TestSubscriber<Integer> ts = TestSubscriber.create(0L); TestingDeferredScalarSubscriber ds = new TestingDeferredScalarSubscriber(ts); Flowable.just(1).ignoreElements().<Integer>toFlowable().subscribe(ds); // we need a producer from upstream ts.assertNoValues(); ts.assertNoValues(); ts.assertNoErrors(); ts.assertComplete(); } @Test public void justSource() { TestSubscriber<Integer> ts = TestSubscriber.create(0L); TestingDeferredScalarSubscriber ds = new TestingDeferredScalarSubscriber(ts); ds.subscribeTo(Flowable.just(1)); ts.assertNoValues(); ts.request(1); ts.assertValue(1); ts.assertNoErrors(); ts.assertComplete(); } @Test public void rangeSource() { TestSubscriber<Integer> ts = TestSubscriber.create(0); TestingDeferredScalarSubscriber ds = new TestingDeferredScalarSubscriber(ts); ds.subscribeTo(Flowable.range(1, 10)); ts.assertNoValues(); ts.request(1); ts.assertValue(10); ts.assertNoErrors(); ts.assertComplete(); } @Test public void completeAfterNext() { TestSubscriber<Integer> ts = new TestSubscriber<Integer>() { @Override public void onNext(Integer t) { super.onNext(t); cancel(); } }; TestingDeferredScalarSubscriber ds = new TestingDeferredScalarSubscriber(ts); ds.setupDownstream(); ds.onNext(1); ts.assertNoValues(); ds.onComplete(); ts.assertValue(1); ts.assertNoErrors(); ts.assertNotComplete(); } @Test public void completeAfterNextViaRequest() { TestSubscriber<Integer> ts = new TestSubscriber<Integer>(0L) { @Override public void onNext(Integer t) { super.onNext(t); cancel(); } }; TestingDeferredScalarSubscriber ds = new TestingDeferredScalarSubscriber(ts); ds.setupDownstream(); ds.onNext(1); ds.onComplete(); ts.assertNoValues(); ts.request(1); ts.assertValue(1); ts.assertNoErrors(); ts.assertNotComplete(); } @Test public void doubleComplete() { TestSubscriber<Integer> ts = TestSubscriber.create(0); TestingDeferredScalarSubscriber ds = new TestingDeferredScalarSubscriber(ts); ds.setupDownstream(); ds.onNext(1); ts.request(1); ds.onComplete(); ds.onComplete(); ts.assertValue(1); ts.assertNoErrors(); ts.assertComplete(); } @Test public void doubleComplete2() { TestSubscriber<Integer> ts = TestSubscriber.create(0); TestingDeferredScalarSubscriber ds = new TestingDeferredScalarSubscriber(ts); ds.setupDownstream(); ds.onNext(1); ds.onComplete(); ds.onComplete(); ts.request(1); ts.assertValue(1); ts.assertNoErrors(); ts.assertComplete(); } @Test public void doubleRequest() { TestSubscriber<Integer> ts = TestSubscriber.create(0); TestingDeferredScalarSubscriber ds = new TestingDeferredScalarSubscriber(ts); ds.setupDownstream(); ds.onNext(1); ts.request(1); ts.request(1); ds.onComplete(); ts.assertValue(1); ts.assertNoErrors(); ts.assertComplete(); } @Test public void negativeRequest() { List<Throwable> list = TestHelper.trackPluginErrors(); TestSubscriber<Integer> ts = TestSubscriber.create(0); TestingDeferredScalarSubscriber ds = new TestingDeferredScalarSubscriber(ts); ds.setupDownstream(); ds.downstreamRequest(-99); RxJavaPlugins.reset(); TestHelper.assertError(list, 0, IllegalArgumentException.class, "n > 0 required but it was -99"); } @Test public void callsAfterUnsubscribe() { TestSubscriber<Integer> ts = TestSubscriber.create(0); TestingDeferredScalarSubscriber ds = new TestingDeferredScalarSubscriber(ts); ds.setupDownstream(); ts.cancel(); ds.downstreamRequest(1); ds.onNext(1); ds.onComplete(); ds.onComplete(); ts.assertNoValues(); ts.assertNoErrors(); ts.assertNotComplete(); } @Test public void emissionRequestRace() { Worker w = Schedulers.computation().createWorker(); try { for (int i = 0; i < 10000; i++) { final TestSubscriber<Integer> ts = TestSubscriber.create(0L); TestingDeferredScalarSubscriber ds = new TestingDeferredScalarSubscriber(ts); ds.setupDownstream(); ds.onNext(1); final AtomicInteger ready = new AtomicInteger(2); w.schedule(new Runnable() { @Override public void run() { ready.decrementAndGet(); while (ready.get() != 0) { } ts.request(1); } }); ready.decrementAndGet(); while (ready.get() != 0) { } ds.onComplete(); ts.awaitTerminalEvent(5, TimeUnit.SECONDS); ts.assertValues(1); ts.assertNoErrors(); ts.assertComplete(); } } finally { w.dispose(); } } @Test public void emissionRequestRace2() { Worker w = Schedulers.io().createWorker(); Worker w2 = Schedulers.io().createWorker(); int m = 10000; if (Runtime.getRuntime().availableProcessors() < 3) { m = 1000; } try { for (int i = 0; i < m; i++) { final TestSubscriber<Integer> ts = TestSubscriber.create(0L); TestingDeferredScalarSubscriber ds = new TestingDeferredScalarSubscriber(ts); ds.setupDownstream(); ds.onNext(1); final AtomicInteger ready = new AtomicInteger(3); w.schedule(new Runnable() { @Override public void run() { ready.decrementAndGet(); while (ready.get() != 0) { } ts.request(1); } }); w2.schedule(new Runnable() { @Override public void run() { ready.decrementAndGet(); while (ready.get() != 0) { } ts.request(1); } }); ready.decrementAndGet(); while (ready.get() != 0) { } ds.onComplete(); ts.awaitTerminalEvent(5, TimeUnit.SECONDS); ts.assertValues(1); ts.assertNoErrors(); ts.assertComplete(); } } finally { w.dispose(); w2.dispose(); } } static final class TestingDeferredScalarSubscriber extends DeferredScalarSubscriber<Integer, Integer> { private static final long serialVersionUID = 6285096158319517837L; TestingDeferredScalarSubscriber(Subscriber<? super Integer> actual) { super(actual); } @Override public void onNext(Integer t) { value = t; hasValue = true; } public void setupDownstream() { onSubscribe(new BooleanSubscription()); } public void subscribeTo(Publisher<Integer> p) { p.subscribe(this); } public void downstreamRequest(long n) { request(n); } } }