/**
* 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.observables;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import org.junit.Test;
import org.mockito.InOrder;
import rx.*;
import rx.Observable;
import rx.Observer;
import rx.exceptions.TestException;
import rx.functions.*;
import rx.observables.AbstractOnSubscribe.SubscriptionState;
import rx.observers.TestSubscriber;
import rx.schedulers.Schedulers;
/**
* Test if AbstractOnSubscribe adheres to the usual unsubscription and backpressure contracts.
*/
public class AbstractOnSubscribeTest {
@Test
public void testJust() {
AbstractOnSubscribe<Integer, Void> aos = new AbstractOnSubscribe<Integer, Void>() {
@Override
protected void next(SubscriptionState<Integer, Void> state) {
state.onNext(1);
state.onCompleted();
}
};
TestSubscriber<Integer> ts = new TestSubscriber<Integer>();
aos.toObservable().subscribe(ts);
ts.assertNoErrors();
ts.assertTerminalEvent();
ts.assertReceivedOnNext(Arrays.asList(1));
}
@Test
public void testJustMisbehaving() {
AbstractOnSubscribe<Integer, Void> aos = new AbstractOnSubscribe<Integer, Void>() {
@Override
protected void next(SubscriptionState<Integer, Void> state) {
state.onNext(1);
state.onNext(2);
state.onCompleted();
}
};
@SuppressWarnings("unchecked")
Observer<Object> o = mock(Observer.class);
aos.toObservable().subscribe(o);
verify(o, never()).onNext(any(Integer.class));
verify(o, never()).onCompleted();
verify(o).onError(any(IllegalStateException.class));
}
@Test
public void testJustMisbehavingOnCompleted() {
AbstractOnSubscribe<Integer, Void> aos = new AbstractOnSubscribe<Integer, Void>() {
@Override
protected void next(SubscriptionState<Integer, Void> state) {
state.onNext(1);
state.onCompleted();
state.onCompleted();
}
};
@SuppressWarnings("unchecked")
Observer<Object> o = mock(Observer.class);
aos.toObservable().subscribe(o);
verify(o, never()).onNext(any(Integer.class));
verify(o, never()).onCompleted();
verify(o).onError(any(IllegalStateException.class));
}
@Test
public void testJustMisbehavingOnError() {
AbstractOnSubscribe<Integer, Void> aos = new AbstractOnSubscribe<Integer, Void>() {
@Override
protected void next(SubscriptionState<Integer, Void> state) {
state.onNext(1);
state.onError(new TestException("Forced failure 1"));
state.onError(new TestException("Forced failure 2"));
}
};
@SuppressWarnings("unchecked")
Observer<Object> o = mock(Observer.class);
aos.toObservable().subscribe(o);
verify(o, never()).onNext(any(Integer.class));
verify(o, never()).onCompleted();
verify(o).onError(any(IllegalStateException.class));
}
@Test
public void testEmpty() {
AbstractOnSubscribe<Integer, Void> aos = new AbstractOnSubscribe<Integer, Void>() {
@Override
protected void next(SubscriptionState<Integer, Void> state) {
state.onCompleted();
}
};
@SuppressWarnings("unchecked")
Observer<Object> o = mock(Observer.class);
aos.toObservable().subscribe(o);
verify(o, never()).onNext(any(Integer.class));
verify(o, never()).onError(any(Throwable.class));
verify(o).onCompleted();
}
@Test
public void testNever() {
AbstractOnSubscribe<Integer, Void> aos = new AbstractOnSubscribe<Integer, Void>() {
@Override
protected void next(SubscriptionState<Integer, Void> state) {
state.stop();
}
};
@SuppressWarnings("unchecked")
Observer<Object> o = mock(Observer.class);
aos.toObservable().subscribe(o);
verify(o, never()).onNext(any(Integer.class));
verify(o, never()).onError(any(Throwable.class));
verify(o, never()).onCompleted();
}
@Test
public void testThrows() {
AbstractOnSubscribe<Integer, Void> aos = new AbstractOnSubscribe<Integer, Void>() {
@Override
protected void next(SubscriptionState<Integer, Void> state) {
throw new TestException("Forced failure");
}
};
@SuppressWarnings("unchecked")
Observer<Object> o = mock(Observer.class);
aos.toObservable().subscribe(o);
verify(o, never()).onNext(any(Integer.class));
verify(o, never()).onCompleted();
verify(o).onError(any(TestException.class));
}
@Test
public void testError() {
AbstractOnSubscribe<Integer, Void> aos = new AbstractOnSubscribe<Integer, Void>() {
@Override
protected void next(SubscriptionState<Integer, Void> state) {
state.onError(new TestException("Forced failure"));
}
};
@SuppressWarnings("unchecked")
Observer<Object> o = mock(Observer.class);
aos.toObservable().subscribe(o);
verify(o, never()).onNext(any(Integer.class));
verify(o).onError(any(TestException.class));
verify(o, never()).onCompleted();
}
@Test
public void testRange() {
final int start = 1;
final int count = 100;
AbstractOnSubscribe<Integer, Void> aos = new AbstractOnSubscribe<Integer, Void>() {
@Override
protected void next(SubscriptionState<Integer, Void> state) {
long calls = state.calls();
if (calls <= count) {
state.onNext((int)calls + start);
if (calls == count) {
state.onCompleted();
}
}
}
};
@SuppressWarnings("unchecked")
Observer<Object> o = mock(Observer.class);
InOrder inOrder = inOrder(o);
aos.toObservable().subscribe(o);
verify(o, never()).onError(any(TestException.class));
for (int i = start; i < start + count; i++) {
inOrder.verify(o).onNext(i);
}
inOrder.verify(o).onCompleted();
inOrder.verifyNoMoreInteractions();
}
@Test
public void testFromIterable() {
int n = 100;
final List<Integer> source = new ArrayList<Integer>();
for (int i = 0; i < n; i++) {
source.add(i);
}
AbstractOnSubscribe<Integer, Iterator<Integer>> aos = new AbstractOnSubscribe<Integer, Iterator<Integer>>() {
@Override
protected Iterator<Integer> onSubscribe(
Subscriber<? super Integer> subscriber) {
return source.iterator();
}
@Override
protected void next(SubscriptionState<Integer, Iterator<Integer>> state) {
Iterator<Integer> it = state.state();
if (it.hasNext()) {
state.onNext(it.next());
}
if (!it.hasNext()) {
state.onCompleted();
}
}
};
@SuppressWarnings("unchecked")
Observer<Object> o = mock(Observer.class);
InOrder inOrder = inOrder(o);
aos.toObservable().subscribe(o);
verify(o, never()).onError(any(TestException.class));
for (int i = 0; i < n; i++) {
inOrder.verify(o).onNext(i);
}
inOrder.verify(o).onCompleted();
inOrder.verifyNoMoreInteractions();
}
@Test
public void testPhased() {
final int count = 100;
AbstractOnSubscribe<String, Void> aos = new AbstractOnSubscribe<String, Void>() {
@Override
protected void next(SubscriptionState<String, Void> state) {
long c = state.calls();
switch (state.phase()) {
case 0:
if (c < count) {
state.onNext("Beginning");
if (c == count - 1) {
state.advancePhase();
}
}
break;
case 1:
state.onNext("Beginning");
state.advancePhase();
break;
case 2:
state.onNext("Finally");
state.onCompleted();
state.advancePhase();
break;
default:
throw new IllegalStateException("Wrong phase: " + state.phase());
}
}
};
@SuppressWarnings("unchecked")
Observer<Object> o = mock(Observer.class);
InOrder inOrder = inOrder(o);
aos.toObservable().subscribe(o);
verify(o, never()).onError(any(Throwable.class));
inOrder.verify(o, times(count + 1)).onNext("Beginning");
inOrder.verify(o).onNext("Finally");
inOrder.verify(o).onCompleted();
inOrder.verifyNoMoreInteractions();
}
@Test
public void testPhasedRetry() {
final int count = 100;
AbstractOnSubscribe<String, Void> aos = new AbstractOnSubscribe<String, Void>() {
int calls;
int phase;
@Override
protected void next(SubscriptionState<String, Void> state) {
switch (phase) {
case 0:
if (calls++ < count) {
state.onNext("Beginning");
state.onError(new TestException());
} else {
phase++;
}
break;
case 1:
state.onNext("Beginning");
phase++;
break;
case 2:
state.onNext("Finally");
state.onCompleted();
phase++;
break;
default:
throw new IllegalStateException("Wrong phase: " + state.phase());
}
}
};
@SuppressWarnings("unchecked")
Observer<Object> o = mock(Observer.class);
InOrder inOrder = inOrder(o);
aos.toObservable().retry(2 * count).subscribe(o);
verify(o, never()).onError(any(Throwable.class));
inOrder.verify(o, times(count + 1)).onNext("Beginning");
inOrder.verify(o).onNext("Finally");
inOrder.verify(o).onCompleted();
inOrder.verifyNoMoreInteractions();
}
@Test
public void testInfiniteTake() {
int count = 100;
AbstractOnSubscribe<Integer, Void> aos = new AbstractOnSubscribe<Integer, Void>() {
@Override
protected void next(SubscriptionState<Integer, Void> state) {
state.onNext((int)state.calls());
}
};
@SuppressWarnings("unchecked")
Observer<Object> o = mock(Observer.class);
InOrder inOrder = inOrder(o);
aos.toObservable().take(count).subscribe(o);
verify(o, never()).onError(any(Throwable.class));
for (int i = 0; i < 100; i++) {
inOrder.verify(o).onNext(i);
}
inOrder.verify(o).onCompleted();
inOrder.verifyNoMoreInteractions();
}
@Test
public void testInfiniteRequestSome() {
int count = 100;
AbstractOnSubscribe<Integer, Void> aos = new AbstractOnSubscribe<Integer, Void>() {
@Override
protected void next(SubscriptionState<Integer, Void> state) {
state.onNext((int)state.calls());
}
};
@SuppressWarnings("unchecked")
Observer<Object> o = mock(Observer.class);
InOrder inOrder = inOrder(o);
TestSubscriber<Object> ts = new TestSubscriber<Object>(o) {
@Override
public void onStart() {
requestMore(0); // don't start right away
}
};
aos.toObservable().subscribe(ts);
ts.requestMore(count);
verify(o, never()).onError(any(Throwable.class));
verify(o, never()).onCompleted();
for (int i = 0; i < count; i++) {
inOrder.verify(o).onNext(i);
}
inOrder.verifyNoMoreInteractions();
}
@Test
public void testIndependentStates() {
int count = 100;
final ConcurrentHashMap<Object, Object> states = new ConcurrentHashMap<Object, Object>();
AbstractOnSubscribe<Integer, Void> aos = new AbstractOnSubscribe<Integer, Void>() {
@Override
protected void next(SubscriptionState<Integer, Void> state) {
states.put(state, state);
state.stop();
}
};
Observable<Integer> source = aos.toObservable();
for (int i = 0; i < count; i++) {
source.subscribe();
}
assertEquals(count, states.size());
}
@Test(timeout = 3000)
public void testSubscribeOn() {
final int start = 1;
final int count = 100;
AbstractOnSubscribe<Integer, Void> aos = new AbstractOnSubscribe<Integer, Void>() {
@Override
protected void next(SubscriptionState<Integer, Void> state) {
long calls = state.calls();
if (calls <= count) {
state.onNext((int)calls + start);
if (calls == count) {
state.onCompleted();
}
}
}
};
@SuppressWarnings("unchecked")
Observer<Object> o = mock(Observer.class);
InOrder inOrder = inOrder(o);
TestSubscriber<Object> ts = new TestSubscriber<Object>(o);
aos.toObservable().subscribeOn(Schedulers.newThread()).subscribe(ts);
ts.awaitTerminalEvent();
verify(o, never()).onError(any(Throwable.class));
for (int i = 1; i <= count; i++) {
inOrder.verify(o).onNext(i);
}
inOrder.verify(o).onCompleted();
inOrder.verifyNoMoreInteractions();
}
@Test(timeout = 10000)
public void testObserveOn() {
final int start = 1;
final int count = 1000;
AbstractOnSubscribe<Integer, Void> aos = new AbstractOnSubscribe<Integer, Void>() {
@Override
protected void next(SubscriptionState<Integer, Void> state) {
long calls = state.calls();
if (calls <= count) {
state.onNext((int)calls + start);
if (calls == count) {
state.onCompleted();
}
}
}
};
@SuppressWarnings("unchecked")
Observer<Object> o = mock(Observer.class);
TestSubscriber<Object> ts = new TestSubscriber<Object>(o);
aos.toObservable().observeOn(Schedulers.newThread()).subscribe(ts);
ts.awaitTerminalEvent();
verify(o, never()).onError(any(Throwable.class));
verify(o, times(count + 1)).onNext(any(Integer.class));
verify(o).onCompleted();
for (int i = 0; i < ts.getOnNextEvents().size(); i++) {
Object object = ts.getOnNextEvents().get(i);
assertEquals(i + 1, object);
}
}
@Test
public void testMissingEmission() {
@SuppressWarnings("unchecked")
Observer<Object> o = mock(Observer.class);
Action1<SubscriptionState<Object, Void>> empty = Actions.empty();
AbstractOnSubscribe.create(empty).toObservable().subscribe(o);
verify(o, never()).onCompleted();
verify(o, never()).onNext(any(Object.class));
verify(o).onError(any(IllegalStateException.class));
}
}