/** * 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.assertEquals; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.util.Arrays; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; import rx.Observable; import rx.Subscriber; import rx.functions.Action0; import rx.functions.Action1; import rx.functions.Func1; import rx.functions.Func2; import rx.observers.TestSubscriber; import rx.schedulers.Schedulers; import rx.subjects.AsyncSubject; import rx.subjects.BehaviorSubject; import rx.subjects.PublishSubject; import rx.subjects.ReplaySubject; import rx.subjects.Subject; public class OnSubscribeCacheTest { @Test public void testCache() throws InterruptedException { final AtomicInteger counter = new AtomicInteger(); Observable<String> o = Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(final Subscriber<? super String> observer) { new Thread(new Runnable() { @Override public void run() { counter.incrementAndGet(); System.out.println("published observable being executed"); observer.onNext("one"); observer.onCompleted(); } }).start(); } }).cache(); // we then expect the following 2 subscriptions to get that same value final CountDownLatch latch = new CountDownLatch(2); // subscribe once o.subscribe(new Action1<String>() { @Override public void call(String v) { assertEquals("one", v); System.out.println("v: " + v); latch.countDown(); } }); // subscribe again o.subscribe(new Action1<String>() { @Override public void call(String v) { assertEquals("one", v); System.out.println("v: " + v); latch.countDown(); } }); if (!latch.await(1000, TimeUnit.MILLISECONDS)) { fail("subscriptions did not receive values"); } assertEquals(1, counter.get()); } private void testWithCustomSubjectAndRepeat(Subject<Integer, Integer> subject, Integer... expected) { Observable<Integer> source0 = Observable.just(1, 2, 3) .subscribeOn(Schedulers.io()) .flatMap(new Func1<Integer, Observable<Integer>>() { @Override public Observable<Integer> call(final Integer i) { return Observable.timer(i * 20, TimeUnit.MILLISECONDS).map(new Func1<Long, Integer>() { @Override public Integer call(Long t1) { return i; } }); } }); Observable<Integer> source1 = Observable.create(new OnSubscribeCache<Integer>(source0, subject)); Observable<Integer> source2 = source1 .repeat(4) .zipWith(Observable.timer(0, 10, TimeUnit.MILLISECONDS, Schedulers.newThread()), new Func2<Integer, Long, Integer>() { @Override public Integer call(Integer t1, Long t2) { return t1; } }); TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); source2.subscribe(ts); ts.awaitTerminalEvent(); ts.assertNoErrors(); System.out.println(ts.getOnNextEvents()); ts.assertReceivedOnNext(Arrays.asList(expected)); } @Test(timeout = 10000) public void testWithAsyncSubjectAndRepeat() { testWithCustomSubjectAndRepeat(AsyncSubject.<Integer> create(), 3, 3, 3, 3); } @Test(timeout = 10000) public void testWithBehaviorSubjectAndRepeat() { // BehaviorSubject just completes when repeated testWithCustomSubjectAndRepeat(BehaviorSubject.create(0), 0, 1, 2, 3); } @Test(timeout = 10000) public void testWithPublishSubjectAndRepeat() { // PublishSubject just completes when repeated testWithCustomSubjectAndRepeat(PublishSubject.<Integer> create(), 1, 2, 3); } @Test public void testWithReplaySubjectAndRepeat() { testWithCustomSubjectAndRepeat(ReplaySubject.<Integer> create(), 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3); } @Test public void testUnsubscribeSource() { Action0 unsubscribe = mock(Action0.class); Observable<Integer> o = Observable.just(1).doOnUnsubscribe(unsubscribe).cache(); o.subscribe(); o.subscribe(); o.subscribe(); verify(unsubscribe, times(1)).call(); } }