/**
* 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.subjects;
import static org.junit.Assert.*;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Test;
import io.reactivex.*;
import io.reactivex.disposables.*;
import io.reactivex.exceptions.TestException;
import io.reactivex.internal.fuseable.*;
import io.reactivex.observers.*;
import io.reactivex.plugins.RxJavaPlugins;
import io.reactivex.schedulers.Schedulers;
import static org.mockito.Mockito.mock;
public class UnicastSubjectTest {
@Test
public void fusionLive() {
UnicastSubject<Integer> ap = UnicastSubject.create();
TestObserver<Integer> ts = ObserverFusion.newTest(QueueDisposable.ANY);
ap.subscribe(ts);
ts
.assertOf(ObserverFusion.<Integer>assertFuseable())
.assertOf(ObserverFusion.<Integer>assertFusionMode(QueueDisposable.ASYNC));
ts.assertNoValues().assertNoErrors().assertNotComplete();
ap.onNext(1);
ts.assertValue(1).assertNoErrors().assertNotComplete();
ap.onComplete();
ts.assertResult(1);
}
@Test
public void fusionOfflie() {
UnicastSubject<Integer> ap = UnicastSubject.create();
ap.onNext(1);
ap.onComplete();
TestObserver<Integer> ts = ObserverFusion.newTest(QueueDisposable.ANY);
ap.subscribe(ts);
ts
.assertOf(ObserverFusion.<Integer>assertFuseable())
.assertOf(ObserverFusion.<Integer>assertFusionMode(QueueDisposable.ASYNC))
.assertResult(1);
}
@Test
public void failFast() {
UnicastSubject<Integer> ap = UnicastSubject.create(false);
ap.onNext(1);
ap.onError(new RuntimeException());
TestObserver<Integer> ts = TestObserver.create();
ap.subscribe(ts);
ts
.assertValueCount(0)
.assertError(RuntimeException.class);
}
@Test
public void threeArgsFactoryFailFast() {
Runnable noop = mock(Runnable.class);
UnicastSubject<Integer> ap = UnicastSubject.create(16, noop, false);
ap.onNext(1);
ap.onError(new RuntimeException());
TestObserver<Integer> ts = TestObserver.create();
ap.subscribe(ts);
ts
.assertValueCount(0)
.assertError(RuntimeException.class);
}
@Test
public void threeArgsFactoryDelayError() {
Runnable noop = mock(Runnable.class);
UnicastSubject<Integer> ap = UnicastSubject.create(16, noop, true);
ap.onNext(1);
ap.onError(new RuntimeException());
TestObserver<Integer> ts = TestObserver.create();
ap.subscribe(ts);
ts
.assertValueCount(1)
.assertError(RuntimeException.class);
}
@Test
public void fusionOfflineFailFast() {
UnicastSubject<Integer> ap = UnicastSubject.create(false);
ap.onNext(1);
ap.onError(new RuntimeException());
TestObserver<Integer> ts = ObserverFusion.newTest(QueueDisposable.ANY);
ap.subscribe(ts);
ts
.assertValueCount(0)
.assertError(RuntimeException.class);
}
@Test
public void fusionOfflineFailFastMultipleEvents() {
UnicastSubject<Integer> ap = UnicastSubject.create(false);
ap.onNext(1);
ap.onNext(2);
ap.onNext(3);
ap.onComplete();
TestObserver<Integer> ts = ObserverFusion.newTest(QueueDisposable.ANY);
ap.subscribe(ts);
ts
.assertValueCount(3)
.assertComplete();
}
@Test
public void failFastMultipleEvents() {
UnicastSubject<Integer> ap = UnicastSubject.create(false);
ap.onNext(1);
ap.onNext(2);
ap.onNext(3);
ap.onComplete();
TestObserver<Integer> ts = TestObserver.create();
ap.subscribe(ts);
ts
.assertValueCount(3)
.assertComplete();
}
@Test
public void onTerminateCalledWhenOnError() {
final AtomicBoolean didRunOnTerminate = new AtomicBoolean();
UnicastSubject<Integer> us = UnicastSubject.create(Observable.bufferSize(), new Runnable() {
@Override public void run() {
didRunOnTerminate.set(true);
}
});
assertEquals(false, didRunOnTerminate.get());
us.onError(new RuntimeException("some error"));
assertEquals(true, didRunOnTerminate.get());
}
@Test
public void onTerminateCalledWhenOnComplete() {
final AtomicBoolean didRunOnTerminate = new AtomicBoolean();
UnicastSubject<Integer> us = UnicastSubject.create(Observable.bufferSize(), new Runnable() {
@Override public void run() {
didRunOnTerminate.set(true);
}
});
assertEquals(false, didRunOnTerminate.get());
us.onComplete();
assertEquals(true, didRunOnTerminate.get());
}
@Test
public void onTerminateCalledWhenCanceled() {
final AtomicBoolean didRunOnTerminate = new AtomicBoolean();
UnicastSubject<Integer> us = UnicastSubject.create(Observable.bufferSize(), new Runnable() {
@Override public void run() {
didRunOnTerminate.set(true);
}
});
final Disposable subscribe = us.subscribe();
assertEquals(false, didRunOnTerminate.get());
subscribe.dispose();
assertEquals(true, didRunOnTerminate.get());
}
@Test(expected = NullPointerException.class)
public void nullOnTerminate() {
UnicastSubject.create(5, null);
}
@Test(expected = IllegalArgumentException.class)
public void negativeCapacityHint() {
UnicastSubject.create(-1);
}
@Test(expected = IllegalArgumentException.class)
public void zeroCapacityHint() {
UnicastSubject.create(0);
}
@Test
public void onNextNull() {
final UnicastSubject<Object> s = UnicastSubject.create();
s.onNext(null);
s.test()
.assertNoValues()
.assertError(NullPointerException.class)
.assertErrorMessage("onNext called with null. Null values are generally not allowed in 2.x operators and sources.");
}
@Test
public void onErrorNull() {
final UnicastSubject<Object> s = UnicastSubject.create();
s.onError(null);
s.test()
.assertNoValues()
.assertError(NullPointerException.class)
.assertErrorMessage("onError called with null. Null values are generally not allowed in 2.x operators and sources.");
}
@Test
public void onNextNullDelayed() {
final UnicastSubject<Object> p = UnicastSubject.create();
TestObserver<Object> ts = p.test();
p.onNext(null);
ts
.assertNoValues()
.assertError(NullPointerException.class)
.assertErrorMessage("onNext called with null. Null values are generally not allowed in 2.x operators and sources.");
}
@Test
public void onErrorNullDelayed() {
final UnicastSubject<Object> p = UnicastSubject.create();
assertFalse(p.hasObservers());
TestObserver<Object> ts = p.test();
assertTrue(p.hasObservers());
p.onError(null);
assertFalse(p.hasObservers());
ts
.assertNoValues()
.assertError(NullPointerException.class)
.assertErrorMessage("onError called with null. Null values are generally not allowed in 2.x operators and sources.");
}
@Test
public void completeCancelRace() {
for (int i = 0; i < 500; i++) {
final int[] calls = { 0 };
final UnicastSubject<Object> up = UnicastSubject.create(100, new Runnable() {
@Override
public void run() {
calls[0]++;
}
});
final TestObserver<Object> ts = up.test();
Runnable r1 = new Runnable() {
@Override
public void run() {
ts.cancel();
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
up.onComplete();
}
};
TestHelper.race(r1, r2, Schedulers.single());
assertEquals(1, calls[0]);
}
}
@Test
public void afterDone() {
UnicastSubject<Object> p = UnicastSubject.create();
p.onComplete();
Disposable bs = Disposables.empty();
p.onSubscribe(bs);
p.onNext(1);
List<Throwable> errors = TestHelper.trackPluginErrors();
try {
p.onError(new TestException());
TestHelper.assertUndeliverable(errors, 0, TestException.class);
} finally {
RxJavaPlugins.reset();
}
p.onComplete();
p.test().assertResult();
assertNull(p.getThrowable());
assertTrue(p.hasComplete());
assertFalse(p.hasThrowable());
}
@Test
public void onErrorStatePeeking() {
UnicastSubject<Object> p = UnicastSubject.create();
assertFalse(p.hasComplete());
assertFalse(p.hasThrowable());
assertNull(p.getThrowable());
TestException ex = new TestException();
p.onError(ex);
assertFalse(p.hasComplete());
assertTrue(p.hasThrowable());
assertSame(ex, p.getThrowable());
}
@Test
public void rejectSyncFusion() {
UnicastSubject<Object> p = UnicastSubject.create();
TestObserver<Object> ts = ObserverFusion.newTest(QueueDisposable.SYNC);
p.subscribe(ts);
ObserverFusion.assertFusion(ts, QueueDisposable.NONE);
}
@Test
public void cancelOnArrival() {
UnicastSubject.create()
.test(true)
.assertEmpty();
}
@Test
public void multiSubscriber() {
UnicastSubject<Object> p = UnicastSubject.create();
TestObserver<Object> ts = p.test();
p.test()
.assertFailure(IllegalStateException.class);
p.onNext(1);
p.onComplete();
ts.assertResult(1);
}
@Test
public void fusedDrainCancel() {
for (int i = 0; i < 500; i++) {
final UnicastSubject<Object> p = UnicastSubject.create();
final TestObserver<Object> ts = ObserverFusion.newTest(QueueSubscription.ANY);
p.subscribe(ts);
Runnable r1 = new Runnable() {
@Override
public void run() {
p.onNext(1);
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
ts.cancel();
}
};
TestHelper.race(r1, r2, Schedulers.single());
}
}
@Test
public void dispose() {
final int[] calls = { 0 };
UnicastSubject<Integer> us = new UnicastSubject<Integer>(128, new Runnable() {
@Override
public void run() { calls[0]++; }
});
TestHelper.checkDisposed(us);
assertEquals(1, calls[0]);
List<Throwable> errors = TestHelper.trackPluginErrors();
try {
us.onError(new TestException());
TestHelper.assertUndeliverable(errors, 0, TestException.class);
} finally {
RxJavaPlugins.reset();
}
Disposable d = Disposables.empty();
us.onSubscribe(d);
assertTrue(d.isDisposed());
}
}