/** * Copyright 2015 Netflix, Inc. * * 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 rx.internal.producers; import static org.junit.Assert.*; import static org.mockito.Mockito.*; import java.util.*; import java.util.concurrent.TimeUnit; import org.junit.*; import rx.*; import rx.Observable; import rx.Observable.*; import rx.exceptions.MissingBackpressureException; import rx.Observer; import rx.functions.*; import rx.internal.util.unsafe.SpscArrayQueue; import rx.observers.TestSubscriber; import rx.schedulers.*; import rx.subscriptions.SerialSubscription; public class ProducersTest { @Test public void testSingleNoBackpressure() { TestSubscriber ts = new TestSubscriber(); SingleProducer sp = new SingleProducer(ts, 1); ts.setProducer(sp); ts.assertNoErrors(); ts.assertTerminalEvent(); ts.assertReceivedOnNext(Arrays.asList(1)); } @Test public void testSingleWithBackpressure() { TestSubscriber ts = new TestSubscriber(); ts.requestMore(0); SingleProducer sp = new SingleProducer(ts, 1); ts.setProducer(sp); ts.assertNoErrors(); ts.assertReceivedOnNext(Collections.emptyList()); Assert.assertEquals(0, ts.getCompletions()); ts.requestMore(2); ts.assertNoErrors(); ts.assertTerminalEvent(); ts.assertReceivedOnNext(Arrays.asList(1)); } @Test public void testSingleDelayedNoBackpressure() { TestSubscriber ts = new TestSubscriber(); SingleDelayedProducer sdp = new SingleDelayedProducer(ts); ts.setProducer(sdp); ts.assertNoErrors(); ts.assertReceivedOnNext(Collections.emptyList()); Assert.assertEquals(0, ts.getCompletions()); sdp.setValue(1); ts.assertNoErrors(); ts.assertTerminalEvent(); ts.assertReceivedOnNext(Arrays.asList(1)); sdp.setValue(2); ts.assertReceivedOnNext(Arrays.asList(1)); } @Test public void testSingleDelayedWithBackpressure() { TestSubscriber ts = new TestSubscriber(); ts.requestMore(0); SingleDelayedProducer sdp = new SingleDelayedProducer(ts); ts.setProducer(sdp); ts.assertNoErrors(); ts.assertReceivedOnNext(Collections.emptyList()); Assert.assertEquals(0, ts.getCompletions()); sdp.setValue(1); ts.assertNoErrors(); ts.assertReceivedOnNext(Collections.emptyList()); Assert.assertEquals(0, ts.getCompletions()); ts.requestMore(2); ts.assertNoErrors(); ts.assertTerminalEvent(); ts.assertReceivedOnNext(Arrays.asList(1)); sdp.setValue(2); ts.assertReceivedOnNext(Arrays.asList(1)); } @Test public void testQueuedValueNoBackpressure() { TestSubscriber ts = new TestSubscriber(); QueuedValueProducer qvp = new QueuedValueProducer(ts); ts.setProducer(qvp); qvp.offer(1); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList(1)); qvp.offer(2); qvp.offer(3); qvp.offer(4); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList(1, 2, 3, 4)); } @Test public void testQueuedValueWithBackpressure() { TestSubscriber ts = new TestSubscriber(); ts.requestMore(0); QueuedValueProducer qvp = new QueuedValueProducer(ts); ts.setProducer(qvp); qvp.offer(1); ts.assertNoErrors(); ts.assertReceivedOnNext(Collections.emptyList()); qvp.offer(2); ts.requestMore(2); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList(1, 2)); ts.requestMore(2); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList(1, 2)); qvp.offer(3); qvp.offer(4); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList(1, 2, 3, 4)); } @Test public void testQueuedNoBackpressure() { TestSubscriber ts = new TestSubscriber(); QueuedProducer qp = new QueuedProducer(ts); ts.setProducer(qp); qp.offer(1); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList(1)); qp.offer(2); qp.offer(3); qp.offer(4); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList(1, 2, 3, 4)); qp.onCompleted(); ts.assertTerminalEvent(); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList(1, 2, 3, 4)); } @Test public void testQueuedWithBackpressure() { TestSubscriber ts = new TestSubscriber(); ts.requestMore(0); QueuedProducer qp = new QueuedProducer(ts); ts.setProducer(qp); qp.offer(1); ts.assertNoErrors(); ts.assertReceivedOnNext(Collections.emptyList()); qp.offer(2); ts.requestMore(2); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList(1, 2)); ts.requestMore(2); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList(1, 2)); qp.offer(3); qp.offer(4); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList(1, 2, 3, 4)); qp.onCompleted(); ts.assertTerminalEvent(); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList(1, 2, 3, 4)); } @Test public void testArbiter() { Producer p1 = mock(Producer.class); Producer p2 = mock(Producer.class); ProducerArbiter pa = new ProducerArbiter(); pa.request(100); pa.setProducer(p1); verify(p1).request(100); pa.produced(50); pa.setProducer(p2); verify(p2).request(50); } static final class TestProducer implements Producer { final Observer child; public TestProducer(Observer child) { this.child = child; } @Override public void request(long n) { child.onNext((int)n); } } @Test public void testObserverArbiterWithBackpressure() { final TestSubscriber ts = new TestSubscriber(); ts.requestMore(0); final ProducerObserverArbiter poa = new ProducerObserverArbiter(ts); ts.setProducer(poa); poa.setProducer(new TestProducer(poa)); ts.requestMore(1); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList(1)); poa.setProducer(null); ts.requestMore(5); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList(1)); poa.setProducer(new TestProducer(poa)); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList(1, 5)); poa.onCompleted(); ts.assertNoErrors(); ts.assertTerminalEvent(); ts.assertReceivedOnNext(Arrays.asList(1, 5)); } static final class SwitchTimer implements OnSubscribe { final List> sources; final long time; final TimeUnit unit; final Scheduler scheduler; public SwitchTimer( Iterable> sources, long time, TimeUnit unit, Scheduler scheduler) { this.scheduler = scheduler; this.sources = new ArrayList>(); this.time = time; this.unit = unit; for (Observable o : sources) { this.sources.add(o); } } @Override public void call(Subscriber child) { final ProducerObserverArbiter poa = new ProducerObserverArbiter(child); Scheduler.Worker w = scheduler.createWorker(); child.add(w); child.setProducer(poa); final SerialSubscription ssub = new SerialSubscription(); child.add(ssub); final int[] index = new int[1]; w.schedulePeriodically(new Action0() { @Override public void call() { final int idx = index[0]++; if (idx >= sources.size()) { poa.onCompleted(); return; } Subscriber s = new Subscriber() { @Override public void onNext(T t) { poa.onNext(t); } @Override public void onError(Throwable e) { poa.onError(e); } @Override public void onCompleted() { if (idx + 1 == sources.size()) { poa.onCompleted(); } } @Override public void setProducer(Producer producer) { poa.setProducer(producer); } }; ssub.set(s); sources.get(idx).unsafeSubscribe(s); } }, time, time, unit); } } final Func1 plus(final long n) { return new Func1() { @Override public Long call(Long t) { return t + n; } }; } @Test public void testObserverArbiterAsync() { TestScheduler test = Schedulers.test(); @SuppressWarnings("unchecked") List> timers = Arrays.asList( Observable.interval(100, 100, TimeUnit.MILLISECONDS, test), Observable.interval(100, 100, TimeUnit.MILLISECONDS, test) .map(plus(20)), Observable.interval(100, 100, TimeUnit.MILLISECONDS, test) .map(plus(40)) ); Observable source = Observable.create( new SwitchTimer(timers, 550, TimeUnit.MILLISECONDS, test)); TestSubscriber ts = new TestSubscriber(); ts.requestMore(100); source.subscribe(ts); test.advanceTimeBy(1, TimeUnit.MINUTES); ts.assertTerminalEvent(); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList(0L, 1L, 2L, 3L, 4L, 20L, 21L, 22L, 23L, 24L, 40L, 41L, 42L, 43L, 44L)); } @Test(timeout = 1000) public void testProducerObserverArbiterUnbounded() { Observable.range(0, Integer.MAX_VALUE) .lift(new Operator() { @Override public Subscriber call(Subscriber t) { final ProducerObserverArbiter poa = new ProducerObserverArbiter(t); Subscriber parent = new Subscriber() { @Override public void onCompleted() { poa.onCompleted(); } @Override public void onError(Throwable e) { poa.onError(e); } @Override public void onNext(Integer t) { poa.onNext(t); } @Override public void setProducer(Producer p) { poa.setProducer(p); } }; t.add(parent); t.setProducer(poa); return parent; } }).subscribe(new TestSubscriber() { int count; @Override public void onNext(Integer t) { if (++count == 2) { unsubscribe(); } } }); } @Test public void queuedProducerRequestNegative() { QueuedProducer qp = new QueuedProducer(new TestSubscriber()); try { qp.request(-99); } catch (IllegalArgumentException ex) { assertEquals("n >= 0 required", ex.getMessage()); } } @Test public void queuedProducerOfferNull() { TestSubscriber ts = TestSubscriber.create(); QueuedProducer qp = new QueuedProducer(ts); qp.offer(null); qp.request(1); ts.assertValue(null); } @Test public void queuedProducerFull() { TestSubscriber ts = TestSubscriber.create(); QueuedProducer qp = new QueuedProducer(ts, new SpscArrayQueue(1)); assertTrue(qp.offer(1)); assertFalse(qp.offer(2)); qp.request(1); ts.assertValue(1); } @Test public void queuedProducerOnNextFull() { TestSubscriber ts = TestSubscriber.create(); QueuedProducer qp = new QueuedProducer(ts, new SpscArrayQueue(1)); qp.onNext(1); qp.onNext(2); qp.request(1); ts.assertError(MissingBackpressureException.class); } @Test public void queuedProducerOnNextFullWithNull() { TestSubscriber ts = TestSubscriber.create(); QueuedProducer qp = new QueuedProducer(ts, new SpscArrayQueue(1)); qp.onNext(1); qp.onNext(null); qp.request(1); ts.assertError(MissingBackpressureException.class); } @Test public void queuedProducerRequestCompletes() { TestSubscriber ts = TestSubscriber.create(); QueuedProducer qp = new QueuedProducer(ts, new SpscArrayQueue(1)); qp.onNext(1); qp.onCompleted(); qp.request(2); ts.assertValue(1); ts.assertNoErrors(); ts.assertCompleted(); } @Test public void queuedProducerUnsubscribed() { TestSubscriber ts = TestSubscriber.create(); ts.unsubscribe(); QueuedProducer qp = new QueuedProducer(ts, new SpscArrayQueue(1)); qp.onNext(1); qp.onCompleted(); qp.request(2); ts.assertNoValues(); ts.assertNoErrors(); ts.assertNotCompleted(); } }