/** * 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.operators.observable; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.*; import java.util.List; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.*; import org.mockito.*; import io.reactivex.*; import io.reactivex.exceptions.TestException; import io.reactivex.functions.Function; import io.reactivex.observables.ConnectableObservable; import io.reactivex.observers.*; import io.reactivex.plugins.RxJavaPlugins; import io.reactivex.schedulers.*; public class ObservableTimerTest { @Mock Observer<Object> observer; @Mock Observer<Long> observer2; TestScheduler scheduler; @Before public void before() { observer = TestHelper.mockObserver(); observer2 = TestHelper.mockObserver(); scheduler = new TestScheduler(); } @Test public void testTimerOnce() { Observable.timer(100, TimeUnit.MILLISECONDS, scheduler).subscribe(observer); scheduler.advanceTimeBy(100, TimeUnit.MILLISECONDS); verify(observer, times(1)).onNext(0L); verify(observer, times(1)).onComplete(); verify(observer, never()).onError(any(Throwable.class)); } @Test public void testTimerPeriodically() { TestObserver<Long> ts = new TestObserver<Long>(); Observable.interval(100, 100, TimeUnit.MILLISECONDS, scheduler).subscribe(ts); scheduler.advanceTimeBy(100, TimeUnit.MILLISECONDS); ts.assertValue(0L); scheduler.advanceTimeBy(100, TimeUnit.MILLISECONDS); ts.assertValues(0L, 1L); scheduler.advanceTimeBy(100, TimeUnit.MILLISECONDS); ts.assertValues(0L, 1L, 2L); scheduler.advanceTimeBy(100, TimeUnit.MILLISECONDS); ts.assertValues(0L, 1L, 2L, 3L); ts.dispose(); scheduler.advanceTimeBy(100, TimeUnit.MILLISECONDS); ts.assertValues(0L, 1L, 2L, 3L); ts.assertNotComplete(); ts.assertNoErrors(); } @Test public void testInterval() { Observable<Long> w = Observable.interval(1, TimeUnit.SECONDS, scheduler); TestObserver<Long> ts = new TestObserver<Long>(); w.subscribe(ts); ts.assertNoValues(); ts.assertNoErrors(); ts.assertNotComplete(); scheduler.advanceTimeTo(2, TimeUnit.SECONDS); ts.assertValues(0L, 1L); ts.assertNoErrors(); ts.assertNotComplete(); ts.dispose(); scheduler.advanceTimeTo(4, TimeUnit.SECONDS); ts.assertValues(0L, 1L); ts.assertNoErrors(); ts.assertNotComplete(); } @Test public void testWithMultipleSubscribersStartingAtSameTime() { Observable<Long> w = Observable.interval(1, TimeUnit.SECONDS, scheduler); TestObserver<Long> ts1 = new TestObserver<Long>(); TestObserver<Long> ts2 = new TestObserver<Long>(); w.subscribe(ts1); w.subscribe(ts2); ts1.assertNoValues(); ts2.assertNoValues(); scheduler.advanceTimeTo(2, TimeUnit.SECONDS); ts1.assertValues(0L, 1L); ts1.assertNoErrors(); ts1.assertNotComplete(); ts2.assertValues(0L, 1L); ts2.assertNoErrors(); ts2.assertNotComplete(); ts1.dispose(); ts2.dispose(); scheduler.advanceTimeTo(4, TimeUnit.SECONDS); ts1.assertValues(0L, 1L); ts1.assertNoErrors(); ts1.assertNotComplete(); ts2.assertValues(0L, 1L); ts2.assertNoErrors(); ts2.assertNotComplete(); } @Test public void testWithMultipleStaggeredSubscribers() { Observable<Long> w = Observable.interval(1, TimeUnit.SECONDS, scheduler); TestObserver<Long> ts1 = new TestObserver<Long>(); w.subscribe(ts1); ts1.assertNoErrors(); scheduler.advanceTimeTo(2, TimeUnit.SECONDS); TestObserver<Long> ts2 = new TestObserver<Long>(); w.subscribe(ts2); ts1.assertValues(0L, 1L); ts1.assertNoErrors(); ts1.assertNotComplete(); ts2.assertNoValues(); scheduler.advanceTimeTo(4, TimeUnit.SECONDS); ts1.assertValues(0L, 1L, 2L, 3L); ts2.assertValues(0L, 1L); ts1.dispose(); ts2.dispose(); ts1.assertValues(0L, 1L, 2L, 3L); ts1.assertNoErrors(); ts1.assertNotComplete(); ts2.assertValues(0L, 1L); ts2.assertNoErrors(); ts2.assertNotComplete(); } @Test public void testWithMultipleStaggeredSubscribersAndPublish() { ConnectableObservable<Long> w = Observable.interval(1, TimeUnit.SECONDS, scheduler).publish(); TestObserver<Long> ts1 = new TestObserver<Long>(); w.subscribe(ts1); w.connect(); ts1.assertNoValues(); scheduler.advanceTimeTo(2, TimeUnit.SECONDS); TestObserver<Long> ts2 = new TestObserver<Long>(); w.subscribe(ts2); ts1.assertValues(0L, 1L); ts1.assertNoErrors(); ts1.assertNotComplete(); ts2.assertNoValues(); scheduler.advanceTimeTo(4, TimeUnit.SECONDS); ts1.assertValues(0L, 1L, 2L, 3L); ts2.assertValues(2L, 3L); ts1.dispose(); ts2.dispose(); ts1.assertValues(0L, 1L, 2L, 3L); ts1.assertNoErrors(); ts1.assertNotComplete(); ts2.assertValues(2L, 3L); ts2.assertNoErrors(); ts2.assertNotComplete(); } @Test public void testOnceObserverThrows() { Observable<Long> source = Observable.timer(100, TimeUnit.MILLISECONDS, scheduler); source.safeSubscribe(new DefaultObserver<Long>() { @Override public void onNext(Long t) { throw new TestException(); } @Override public void onError(Throwable e) { observer.onError(e); } @Override public void onComplete() { observer.onComplete(); } }); scheduler.advanceTimeBy(1, TimeUnit.SECONDS); verify(observer).onError(any(TestException.class)); verify(observer, never()).onNext(anyLong()); verify(observer, never()).onComplete(); } @Test public void testPeriodicObserverThrows() { Observable<Long> source = Observable.interval(100, 100, TimeUnit.MILLISECONDS, scheduler); InOrder inOrder = inOrder(observer); source.safeSubscribe(new DefaultObserver<Long>() { @Override public void onNext(Long t) { if (t > 0) { throw new TestException(); } observer.onNext(t); } @Override public void onError(Throwable e) { observer.onError(e); } @Override public void onComplete() { observer.onComplete(); } }); scheduler.advanceTimeBy(1, TimeUnit.SECONDS); inOrder.verify(observer).onNext(0L); inOrder.verify(observer).onError(any(TestException.class)); inOrder.verifyNoMoreInteractions(); verify(observer, never()).onComplete(); } @Test public void disposed() { TestHelper.checkDisposed(Observable.timer(1, TimeUnit.DAYS)); } @Test public void timerDelayZero() { List<Throwable> errors = TestHelper.trackPluginErrors(); try { for (int i = 0; i < 1000; i++) { Observable.timer(0, TimeUnit.MILLISECONDS).blockingFirst(); } assertTrue(errors.toString(), errors.isEmpty()); } finally { RxJavaPlugins.reset(); } } @Test public void timerInterruptible() throws Exception { ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor(); try { for (Scheduler s : new Scheduler[] { Schedulers.single(), Schedulers.computation(), Schedulers.newThread(), Schedulers.io(), Schedulers.from(exec) }) { final AtomicBoolean interrupted = new AtomicBoolean(); TestObserver<Long> ts = Observable.timer(1, TimeUnit.MILLISECONDS, s) .map(new Function<Long, Long>() { @Override public Long apply(Long v) throws Exception { try { Thread.sleep(3000); } catch (InterruptedException ex) { interrupted.set(true); } return v; } }) .test(); Thread.sleep(500); ts.cancel(); Thread.sleep(500); assertTrue(s.getClass().getSimpleName(), interrupted.get()); } } finally { exec.shutdown(); } } }