/**
* 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.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
import java.util.Arrays;
import java.util.Collections;
import org.junit.Test;
import rx.Observable;
import rx.Observable.OnSubscribe;
import rx.Observer;
import rx.Subscriber;
import rx.exceptions.MissingBackpressureException;
import rx.exceptions.TestException;
import rx.internal.util.RxRingBuffer;
import rx.observers.TestObserver;
import rx.observers.TestSubscriber;
import rx.schedulers.Schedulers;
import rx.subjects.PublishSubject;
/**
* Test the onBackpressureBlock() behavior.
*/
public class OnBackpressureBlockTest {
static final int WAIT = 200;
@Test(timeout = 1000)
public void testSimpleBelowCapacity() {
Observable<Integer> source = Observable.just(1).onBackpressureBlock(10);
TestObserver<Integer> o = new TestObserver<Integer>();
source.subscribe(o);
o.assertReceivedOnNext(Arrays.asList(1));
o.assertTerminalEvent();
assertTrue(o.getOnErrorEvents().isEmpty());
}
@Test(timeout = 10000)
public void testSimpleAboveCapacity() throws InterruptedException {
Observable<Integer> source = Observable.range(1, 11).subscribeOn(Schedulers.newThread())
.onBackpressureBlock(10);
TestSubscriber<Integer> o = new TestSubscriber<Integer>() {
@Override
public void onStart() {
request(0); // make sure it doesn't start in unlimited mode
}
};
source.subscribe(o);
o.requestMore(10);
Thread.sleep(WAIT);
o.assertReceivedOnNext(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
o.requestMore(10);
Thread.sleep(WAIT);
o.assertReceivedOnNext(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11));
o.assertTerminalEvent();
assertTrue(o.getOnErrorEvents().isEmpty());
}
@Test(timeout = 3000)
public void testNoMissingBackpressureException() {
final int NUM_VALUES = RxRingBuffer.SIZE * 3;
Observable<Integer> source = Observable.create(new OnSubscribe<Integer>() {
@Override
public void call(Subscriber<? super Integer> t1) {
for (int i = 0; i < NUM_VALUES; i++) {
t1.onNext(i);
}
t1.onCompleted();
}
}).subscribeOn(Schedulers.newThread());
@SuppressWarnings("unchecked")
Observer<Integer> o = mock(Observer.class);
TestSubscriber<Integer> s = new TestSubscriber<Integer>(o);
source.onBackpressureBlock(RxRingBuffer.SIZE).observeOn(Schedulers.newThread()).subscribe(s);
s.awaitTerminalEvent();
verify(o, never()).onError(any(MissingBackpressureException.class));
s.assertNoErrors();
verify(o, times(NUM_VALUES)).onNext(any(Integer.class));
verify(o).onCompleted();
}
@Test(timeout = 10000)
public void testBlockedProducerCanBeUnsubscribed() throws InterruptedException {
Observable<Integer> source = Observable.range(1, 11).subscribeOn(Schedulers.newThread())
.onBackpressureBlock(5);
TestSubscriber<Integer> o = new TestSubscriber<Integer>() {
@Override
public void onStart() {
request(0); // make sure it doesn't start in unlimited mode
}
};
source.subscribe(o);
o.requestMore(5);
Thread.sleep(WAIT);
o.unsubscribe();
Thread.sleep(WAIT);
o.assertReceivedOnNext(Arrays.asList(1, 2, 3, 4, 5));
o.assertNoErrors();
assertTrue(o.getOnCompletedEvents().isEmpty());
}
@Test(timeout = 10000)
public void testExceptionIsDelivered() throws InterruptedException {
Observable<Integer> source = Observable.range(1, 10)
.concatWith(Observable.<Integer>error(new TestException("Forced failure")))
.subscribeOn(Schedulers.newThread())
.onBackpressureBlock(5);
TestSubscriber<Integer> o = new TestSubscriber<Integer>() {
@Override
public void onStart() {
request(0); // make sure it doesn't start in unlimited mode
}
};
source.subscribe(o);
o.requestMore(7);
Thread.sleep(WAIT);
o.assertReceivedOnNext(Arrays.asList(1, 2, 3, 4, 5, 6, 7));
o.assertNoErrors();
assertTrue(o.getOnCompletedEvents().isEmpty());
o.requestMore(3);
Thread.sleep(WAIT);
o.assertReceivedOnNext(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
o.assertTerminalEvent();
assertEquals(1, o.getOnErrorEvents().size());
assertTrue(o.getOnErrorEvents().get(0) instanceof TestException);
o.requestMore(10);
Thread.sleep(WAIT);
o.assertReceivedOnNext(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
o.assertTerminalEvent();
assertEquals(1, o.getOnErrorEvents().size());
assertTrue(o.getOnErrorEvents().get(0) instanceof TestException);
}
@Test(timeout = 10000)
public void testExceptionIsDeliveredAfterValues() throws InterruptedException {
Observable<Integer> source = Observable.range(1, 10)
.concatWith(Observable.<Integer>error(new TestException("Forced failure")))
.subscribeOn(Schedulers.newThread())
.onBackpressureBlock(5);
TestSubscriber<Integer> o = new TestSubscriber<Integer>() {
@Override
public void onStart() {
request(0); // make sure it doesn't start in unlimited mode
}
};
source.subscribe(o);
o.requestMore(7);
Thread.sleep(WAIT);
o.assertReceivedOnNext(Arrays.asList(1, 2, 3, 4, 5, 6, 7));
o.assertNoErrors();
assertTrue(o.getOnCompletedEvents().isEmpty());
o.requestMore(7);
Thread.sleep(WAIT);
o.assertReceivedOnNext(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
assertEquals(1, o.getOnErrorEvents().size());
assertTrue(o.getOnErrorEvents().get(0) instanceof TestException);
assertTrue(o.getOnCompletedEvents().isEmpty());
}
@Test(timeout = 10000)
public void testTakeWorksWithSubscriberRequesting() {
Observable<Integer> source = Observable.range(1, 10)
.concatWith(Observable.<Integer>error(new TestException("Forced failure")))
.subscribeOn(Schedulers.newThread())
.onBackpressureBlock(5).take(7);
TestSubscriber<Integer> o = new TestSubscriber<Integer>() {
@Override
public void onStart() {
request(0); // make sure it doesn't start in unlimited mode
}
};
source.subscribe(o);
o.requestMore(7);
o.awaitTerminalEvent();
o.assertReceivedOnNext(Arrays.asList(1, 2, 3, 4, 5, 6, 7));
o.assertNoErrors();
o.assertTerminalEvent();
}
@Test(timeout = 10000)
public void testTakeWorksSubscriberRequestUnlimited() {
Observable<Integer> source = Observable.range(1, 10)
.concatWith(Observable.<Integer>error(new TestException("Forced failure")))
.subscribeOn(Schedulers.newThread())
.onBackpressureBlock(5).take(7);
TestSubscriber<Integer> o = new TestSubscriber<Integer>();
source.subscribe(o);
o.awaitTerminalEvent();
o.assertReceivedOnNext(Arrays.asList(1, 2, 3, 4, 5, 6, 7));
o.assertNoErrors();
o.assertTerminalEvent();
}
@Test(timeout = 10000)
public void testTakeWorksSubscriberRequestUnlimitedBufferedException() {
Observable<Integer> source = Observable.range(1, 10)
.concatWith(Observable.<Integer>error(new TestException("Forced failure")))
.subscribeOn(Schedulers.newThread())
.onBackpressureBlock(11).take(7);
TestSubscriber<Integer> o = new TestSubscriber<Integer>();
source.subscribe(o);
o.awaitTerminalEvent();
o.assertReceivedOnNext(Arrays.asList(1, 2, 3, 4, 5, 6, 7));
o.assertNoErrors();
o.assertTerminalEvent();
}
@Test(timeout = 10000)
public void testOnCompletedDoesntWaitIfNoEvents() {
TestSubscriber<Integer> o = new TestSubscriber<Integer>() {
@Override
public void onStart() {
request(0); // make sure it doesn't start in unlimited mode
}
};
Observable.<Integer>empty().onBackpressureBlock(2).subscribe(o);
o.assertNoErrors();
o.assertTerminalEvent();
o.assertReceivedOnNext(Collections.<Integer>emptyList());
}
@Test(timeout = 10000)
public void testOnCompletedDoesWaitIfEvents() {
TestSubscriber<Integer> o = new TestSubscriber<Integer>() {
@Override
public void onStart() {
request(0); // make sure it doesn't start in unlimited mode
}
};
Observable.just(1).onBackpressureBlock(2).subscribe(o);
o.assertReceivedOnNext(Collections.<Integer>emptyList());
assertTrue(o.getOnErrorEvents().isEmpty());
assertTrue(o.getOnCompletedEvents().isEmpty());
}
@Test(timeout = 10000)
public void testOnCompletedDoesntWaitIfNoEvents2() {
final PublishSubject<Integer> ps = PublishSubject.create();
TestSubscriber<Integer> o = new TestSubscriber<Integer>() {
@Override
public void onStart() {
request(0); // make sure it doesn't start in unlimited mode
}
@Override
public void onNext(Integer t) {
super.onNext(t);
ps.onCompleted(); // as if an async completion arrived while in the loop
}
};
ps.onBackpressureBlock(2).unsafeSubscribe(o);
ps.onNext(1);
o.requestMore(1);
o.assertNoErrors();
o.assertTerminalEvent();
o.assertReceivedOnNext(Arrays.asList(1));
}
@Test(timeout = 10000)
public void testOnCompletedDoesntWaitIfNoEvents3() {
final PublishSubject<Integer> ps = PublishSubject.create();
TestSubscriber<Integer> o = new TestSubscriber<Integer>() {
boolean once = true;
@Override
public void onStart() {
request(0); // make sure it doesn't start in unlimited mode
}
@Override
public void onNext(Integer t) {
super.onNext(t);
if (once) {
once = false;
ps.onNext(2);
ps.onCompleted(); // as if an async completion arrived while in the loop
requestMore(1);
}
}
};
ps.onBackpressureBlock(3).unsafeSubscribe(o);
ps.onNext(1);
o.requestMore(1);
o.assertNoErrors();
o.assertTerminalEvent();
o.assertReceivedOnNext(Arrays.asList(1, 2));
}
}