/**
* 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.flowable;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.*;
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 org.reactivestreams.Subscriber;
import io.reactivex.*;
import io.reactivex.exceptions.*;
import io.reactivex.flowables.ConnectableFlowable;
import io.reactivex.functions.Function;
import io.reactivex.plugins.RxJavaPlugins;
import io.reactivex.schedulers.*;
import io.reactivex.subscribers.*;
public class FlowableTimerTest {
@Mock
Subscriber<Object> observer;
@Mock
Subscriber<Long> observer2;
TestScheduler scheduler;
@Before
public void before() {
observer = TestHelper.mockSubscriber();
observer2 = TestHelper.mockSubscriber();
scheduler = new TestScheduler();
}
@Test
public void testTimerOnce() {
Flowable.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() {
TestSubscriber<Long> ts = new TestSubscriber<Long>();
Flowable.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() {
Flowable<Long> w = Flowable.interval(1, TimeUnit.SECONDS, scheduler);
TestSubscriber<Long> ts = new TestSubscriber<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() {
Flowable<Long> w = Flowable.interval(1, TimeUnit.SECONDS, scheduler);
TestSubscriber<Long> ts1 = new TestSubscriber<Long>();
TestSubscriber<Long> ts2 = new TestSubscriber<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() {
Flowable<Long> w = Flowable.interval(1, TimeUnit.SECONDS, scheduler);
TestSubscriber<Long> ts1 = new TestSubscriber<Long>();
w.subscribe(ts1);
ts1.assertNoErrors();
scheduler.advanceTimeTo(2, TimeUnit.SECONDS);
TestSubscriber<Long> ts2 = new TestSubscriber<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() {
ConnectableFlowable<Long> w = Flowable.interval(1, TimeUnit.SECONDS, scheduler).publish();
TestSubscriber<Long> ts1 = new TestSubscriber<Long>();
w.subscribe(ts1);
w.connect();
ts1.assertNoValues();
scheduler.advanceTimeTo(2, TimeUnit.SECONDS);
TestSubscriber<Long> ts2 = new TestSubscriber<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() {
Flowable<Long> source = Flowable.timer(100, TimeUnit.MILLISECONDS, scheduler);
source.safeSubscribe(new DefaultSubscriber<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() {
Flowable<Long> source = Flowable.interval(100, 100, TimeUnit.MILLISECONDS, scheduler);
InOrder inOrder = inOrder(observer);
source.safeSubscribe(new DefaultSubscriber<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(Flowable.timer(1, TimeUnit.DAYS));
}
@Test
public void backpressureNotReady() {
Flowable.timer(1, TimeUnit.MILLISECONDS)
.test(0L)
.awaitDone(5, TimeUnit.SECONDS)
.assertFailure(MissingBackpressureException.class);
}
@Test
public void timerCancelRace() {
for (int i = 0; i < 500; i++) {
final TestSubscriber<Long> ts = new TestSubscriber<Long>();
final TestScheduler scheduler = new TestScheduler();
Flowable.timer(1, TimeUnit.SECONDS, scheduler)
.subscribe(ts);
Runnable r1 = new Runnable() {
@Override
public void run() {
scheduler.advanceTimeBy(1, TimeUnit.SECONDS);
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
ts.cancel();
}
};
TestHelper.race(r1, r2);
}
}
@Test
public void timerDelayZero() {
List<Throwable> errors = TestHelper.trackPluginErrors();
try {
for (int i = 0; i < 1000; i++) {
Flowable.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();
TestSubscriber<Long> ts = Flowable.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();
}
}
}