/** * Copyright 2014 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.operators; import static org.junit.Assert.*; import java.util.Arrays; import java.util.concurrent.*; import java.util.concurrent.atomic.*; import org.junit.Test; import rx.*; import rx.Observable.OnSubscribe; import rx.functions.*; import rx.internal.util.RxRingBuffer; import rx.observables.ConnectableObservable; import rx.observers.TestSubscriber; import rx.schedulers.Schedulers; import rx.schedulers.TestScheduler; public class OperatorPublishTest { @Test public void testPublish() throws InterruptedException { final AtomicInteger counter = new AtomicInteger(); ConnectableObservable<String> o = Observable.create(new OnSubscribe<String>() { @Override public void call(final Subscriber<? super String> observer) { new Thread(new Runnable() { @Override public void run() { counter.incrementAndGet(); observer.onNext("one"); observer.onCompleted(); } }).start(); } }).publish(); final CountDownLatch latch = new CountDownLatch(2); // subscribe once o.subscribe(new Action1<String>() { @Override public void call(String v) { assertEquals("one", v); latch.countDown(); } }); // subscribe again o.subscribe(new Action1<String>() { @Override public void call(String v) { assertEquals("one", v); latch.countDown(); } }); Subscription s = o.connect(); try { if (!latch.await(1000, TimeUnit.MILLISECONDS)) { fail("subscriptions did not receive values"); } assertEquals(1, counter.get()); } finally { s.unsubscribe(); } } @Test public void testBackpressureFastSlow() { ConnectableObservable<Integer> is = Observable.range(1, RxRingBuffer.SIZE * 2).publish(); Observable<Integer> fast = is.observeOn(Schedulers.computation()).doOnCompleted(new Action0() { @Override public void call() { System.out.println("^^^^^^^^^^^^^ completed FAST"); } }); Observable<Integer> slow = is.observeOn(Schedulers.computation()).map(new Func1<Integer, Integer>() { int c = 0; @Override public Integer call(Integer i) { if (c == 0) { try { Thread.sleep(500); } catch (InterruptedException e) { } } c++; return i; } }).doOnCompleted(new Action0() { @Override public void call() { System.out.println("^^^^^^^^^^^^^ completed SLOW"); } }); TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); Observable.merge(fast, slow).subscribe(ts); is.connect(); ts.awaitTerminalEvent(); ts.assertNoErrors(); assertEquals(RxRingBuffer.SIZE * 4, ts.getOnNextEvents().size()); } // use case from https://github.com/ReactiveX/RxJava/issues/1732 @Test public void testTakeUntilWithPublishedStreamUsingSelector() { final AtomicInteger emitted = new AtomicInteger(); Observable<Integer> xs = Observable.range(0, RxRingBuffer.SIZE * 2).doOnNext(new Action1<Integer>() { @Override public void call(Integer t1) { emitted.incrementAndGet(); } }); TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); xs.publish(new Func1<Observable<Integer>, Observable<Integer>>() { @Override public Observable<Integer> call(Observable<Integer> xs) { return xs.takeUntil(xs.skipWhile(new Func1<Integer, Boolean>() { @Override public Boolean call(Integer i) { return i <= 3; } })); } }).subscribe(ts); ts.awaitTerminalEvent(); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList(0, 1, 2, 3)); assertEquals(5, emitted.get()); System.out.println(ts.getOnNextEvents()); } // use case from https://github.com/ReactiveX/RxJava/issues/1732 @Test public void testTakeUntilWithPublishedStream() { Observable<Integer> xs = Observable.range(0, RxRingBuffer.SIZE * 2); TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); ConnectableObservable<Integer> xsp = xs.publish(); xsp.takeUntil(xsp.skipWhile(new Func1<Integer, Boolean>() { @Override public Boolean call(Integer i) { return i <= 3; } })).subscribe(ts); xsp.connect(); System.out.println(ts.getOnNextEvents()); } @Test(timeout = 10000) public void testBackpressureTwoConsumers() { final AtomicInteger sourceEmission = new AtomicInteger(); final AtomicBoolean sourceUnsubscribed = new AtomicBoolean(); final Observable<Integer> source = Observable.range(1, 100) .doOnNext(new Action1<Integer>() { @Override public void call(Integer t1) { sourceEmission.incrementAndGet(); } }) .doOnUnsubscribe(new Action0() { @Override public void call() { sourceUnsubscribed.set(true); } }).share(); ; final AtomicBoolean child1Unsubscribed = new AtomicBoolean(); final AtomicBoolean child2Unsubscribed = new AtomicBoolean(); final TestSubscriber<Integer> ts2 = new TestSubscriber<Integer>(); final TestSubscriber<Integer> ts1 = new TestSubscriber<Integer>() { @Override public void onNext(Integer t) { if (getOnNextEvents().size() == 2) { source.doOnUnsubscribe(new Action0() { @Override public void call() { child2Unsubscribed.set(true); } }).take(5).subscribe(ts2); } super.onNext(t); } }; source.doOnUnsubscribe(new Action0() { @Override public void call() { child1Unsubscribed.set(true); } }).take(5).subscribe(ts1); ts1.awaitTerminalEvent(); ts2.awaitTerminalEvent(); ts1.assertNoErrors(); ts2.assertNoErrors(); assertTrue(sourceUnsubscribed.get()); assertTrue(child1Unsubscribed.get()); assertTrue(child2Unsubscribed.get()); ts1.assertReceivedOnNext(Arrays.asList(1, 2, 3, 4, 5)); ts2.assertReceivedOnNext(Arrays.asList(4, 5, 6, 7, 8)); assertEquals(8, sourceEmission.get()); } @Test public void testConnectWithNoSubscriber() { TestScheduler scheduler = new TestScheduler(); ConnectableObservable<Long> co = Observable.timer(10, 10, TimeUnit.MILLISECONDS, scheduler).take(3).publish(); co.connect(); // Emit 0 scheduler.advanceTimeBy(15, TimeUnit.MILLISECONDS); TestSubscriber subscriber = new TestSubscriber<Long>(); co.subscribe(subscriber); // Emit 1 and 2 scheduler.advanceTimeBy(50, TimeUnit.MILLISECONDS); subscriber.assertReceivedOnNext(Arrays.asList(1L, 2L)); subscriber.assertNoErrors(); subscriber.assertTerminalEvent(); } }