@Test public void testAwaitTerminalEventWithDuration() { TestSubscriber<Object> ts = new TestSubscriber<Object>(); Observable.just(1).subscribe(ts); ts.awaitTerminalEvent(1, TimeUnit.SECONDS); ts.assertTerminalEvent(); }
@Test(timeout = 2000) public void testFirehoseFailsAsExpected() { AtomicInteger c = new AtomicInteger(); TestSubscriber<Integer> ts = new TestSubscriber<>(); firehose(c) .observeOn(Schedulers.computation()) .map( v -> { try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } return v; }) .subscribe(ts); ts.awaitTerminalEvent(); System.out.println( "testFirehoseFailsAsExpected => Received: " + ts.valueCount() + " Emitted: " + c.get()); // FIXME it is possible slow is not slow enough or the main gets delayed and thus more than one // source value is emitted. int vc = ts.valueCount(); assertTrue("10 < " + vc, vc <= 10); ts.assertError(MissingBackpressureException.class); }
@Test public void testObserveOnWithSlowConsumer() { int NUM = (int) (Observable.bufferSize() * 0.2); AtomicInteger c = new AtomicInteger(); TestSubscriber<Integer> ts = new TestSubscriber<>(); incrementingIntegers(c) .observeOn(Schedulers.computation()) .map( i -> { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return i; }) .take(NUM) .subscribe(ts); ts.awaitTerminalEvent(); ts.assertNoErrors(); System.out.println( "testObserveOnWithSlowConsumer => Received: " + ts.valueCount() + " Emitted: " + c.get()); assertEquals(NUM, ts.valueCount()); assertTrue(c.get() < Observable.bufferSize() * 2); }
@Test(timeout = 10000) public void testOnBackpressureDropSynchronous() { for (int i = 0; i < 100; i++) { int NUM = (int) (Observable.bufferSize() * 1.1); // > 1 so that take doesn't prevent buffer overflow AtomicInteger c = new AtomicInteger(); TestSubscriber<Integer> ts = new TestSubscriber<>(); firehose(c).onBackpressureDrop().map(SLOW_PASS_THRU).take(NUM).subscribe(ts); ts.awaitTerminalEvent(); ts.assertNoErrors(); List<Integer> onNextEvents = ts.values(); assertEquals(NUM, onNextEvents.size()); Integer lastEvent = onNextEvents.get(NUM - 1); System.out.println( "testOnBackpressureDrop => Received: " + onNextEvents.size() + " Emitted: " + c.get() + " Last value: " + lastEvent); // it drop, so we should get some number far higher than what would have sequentially // incremented assertTrue(NUM - 1 <= lastEvent.intValue()); } }
@Test public void testInterruptTerminalEventAwaitTimed() { TestSubscriber<Integer> ts = TestSubscriber.create(); final Thread t0 = Thread.currentThread(); Worker w = Schedulers.computation().createWorker(); try { w.schedule( new Action0() { @Override public void call() { t0.interrupt(); } }, 200, TimeUnit.MILLISECONDS); try { ts.awaitTerminalEvent(5, TimeUnit.SECONDS); fail("Did not interrupt wait!"); } catch (RuntimeException ex) { if (!(ex.getCause() instanceof InterruptedException)) { fail("The cause is not InterruptedException! " + ex.getCause()); } } } finally { w.unsubscribe(); } }
@Test public void testZipAsync() { int NUM = (int) (Observable.bufferSize() * 2.1); AtomicInteger c1 = new AtomicInteger(); AtomicInteger c2 = new AtomicInteger(); TestSubscriber<Integer> ts = new TestSubscriber<>(); Observable<Integer> zipped = Observable.zip( incrementingIntegers(c1).subscribeOn(Schedulers.computation()), incrementingIntegers(c2).subscribeOn(Schedulers.computation()), (t1, t2) -> t1 + t2); zipped.take(NUM).subscribe(ts); ts.awaitTerminalEvent(); ts.assertNoErrors(); System.out.println( "testZipAsync => Received: " + ts.valueCount() + " Emitted: " + c1.get() + " / " + c2.get()); assertEquals(NUM, ts.valueCount()); int max = Observable.bufferSize() * 5; assertTrue("" + c1.get() + " >= " + max, c1.get() < max); assertTrue("" + c2.get() + " >= " + max, c2.get() < max); }
@Test public void testAssertError() { RuntimeException e = new RuntimeException("Oops"); TestSubscriber<Object> subscriber = new TestSubscriber<Object>(); Observable.error(e).subscribe(subscriber); subscriber.assertError(e); }
@Test public void testTakeFilterSkipChainAsync() { int NUM = (int) (Observable.bufferSize() * 2.1); AtomicInteger c = new AtomicInteger(); TestSubscriber<Integer> ts = new TestSubscriber<>(); incrementingIntegers(c) .observeOn(Schedulers.computation()) .skip(10000) .filter(i -> i > 11000) .take(NUM) .subscribe(ts); ts.awaitTerminalEvent(); ts.assertNoErrors(); // emit 10000 that are skipped // emit next 1000 that are filtered out // take NUM // so emitted is at least 10000+1000+NUM + extra for buffer size/threshold int expected = 10000 + 1000 + Observable.bufferSize() * 3 + Observable.bufferSize() / 2; System.out.println( "testTakeFilterSkipChain => Received: " + ts.valueCount() + " Emitted: " + c.get() + " Expected: " + expected); assertEquals(NUM, ts.valueCount()); assertTrue(c.get() < expected); }
@Test public void testMergeAsync() { int NUM = (int) (Observable.bufferSize() * 4.1); AtomicInteger c1 = new AtomicInteger(); AtomicInteger c2 = new AtomicInteger(); TestSubscriber<Integer> ts = new TestSubscriber<>(); Observable<Integer> merged = Observable.merge( incrementingIntegers(c1).subscribeOn(Schedulers.computation()), incrementingIntegers(c2).subscribeOn(Schedulers.computation())); merged.take(NUM).subscribe(ts); ts.awaitTerminalEvent(); ts.assertNoErrors(); System.out.println( "testMergeAsync => Received: " + ts.valueCount() + " Emitted: " + c1.get() + " / " + c2.get()); assertEquals(NUM, ts.valueCount()); // either one can starve the other, but neither should be capable of doing more than 5 batches // (taking 4.1) // TODO is it possible to make this deterministic rather than one possibly starving the other? // benjchristensen => In general I'd say it's not worth trying to make it so, as "fair" // algoritms generally take a performance hit int max = Observable.bufferSize() * 7; assertTrue("" + c1.get() + " >= " + max, c1.get() < max); assertTrue("" + c2.get() + " >= " + max, c2.get() < max); }
@Test public void testMergeAsyncThenObserveOn() { int NUM = (int) (Observable.bufferSize() * 4.1); AtomicInteger c1 = new AtomicInteger(); AtomicInteger c2 = new AtomicInteger(); TestSubscriber<Integer> ts = new TestSubscriber<>(); Observable<Integer> merged = Observable.merge( incrementingIntegers(c1).subscribeOn(Schedulers.computation()), incrementingIntegers(c2).subscribeOn(Schedulers.computation())); merged.observeOn(Schedulers.newThread()).take(NUM).subscribe(ts); ts.awaitTerminalEvent(); ts.assertNoErrors(); System.out.println( "testMergeAsyncThenObserveOn => Received: " + ts.valueCount() + " Emitted: " + c1.get() + " / " + c2.get()); assertEquals(NUM, ts.valueCount()); // either one can starve the other, but neither should be capable of doing more than 5 batches // (taking 4.1) // TODO is it possible to make this deterministic rather than one possibly starving the other? // benjchristensen => In general I'd say it's not worth trying to make it so, as "fair" // algoritms generally take a performance hit // akarnokd => run this in a loop over 10k times and never saw values get as high as 7*SIZE, but // since observeOn delays the unsubscription non-deterministically, the test will remain // unreliable assertTrue(c1.get() < Observable.bufferSize() * 7); assertTrue(c2.get() < Observable.bufferSize() * 7); }
@Test @Ignore // the test is non-deterministic and can't be made deterministic public void testFlatMapAsync() { int NUM = (int) (Observable.bufferSize() * 2.1); AtomicInteger c = new AtomicInteger(); TestSubscriber<Integer> ts = new TestSubscriber<>(); incrementingIntegers(c) .subscribeOn(Schedulers.computation()) .flatMap( i -> incrementingIntegers(new AtomicInteger()) .take(10) .subscribeOn(Schedulers.computation())) .take(NUM) .subscribe(ts); ts.awaitTerminalEvent(); ts.assertNoErrors(); System.out.println( "testFlatMapAsync => Received: " + ts.valueCount() + " Emitted: " + c.get() + " Size: " + Observable.bufferSize()); assertEquals(NUM, ts.valueCount()); // even though we only need 10, it will request at least Observable.bufferSize(), and then as it // drains keep requesting more // and then it will be non-deterministic when the take() causes the unsubscribe as it is // scheduled on 10 different schedulers (threads) // normally this number is ~250 but can get up to ~1200 when Observable.bufferSize() == 1024 assertTrue(c.get() <= Observable.bufferSize() * 2); }
@Test public void testInterruptTerminalEventAwaitAndUnsubscribe() { TestSubscriber<Integer> ts = TestSubscriber.create(); final Thread t0 = Thread.currentThread(); Worker w = Schedulers.computation().createWorker(); try { w.schedule( new Action0() { @Override public void call() { t0.interrupt(); } }, 200, TimeUnit.MILLISECONDS); ts.awaitTerminalEventAndUnsubscribeOnTimeout(5, TimeUnit.SECONDS); if (!ts.isUnsubscribed()) { fail("Did not unsubscribe!"); } } finally { w.unsubscribe(); } }
@Test public void testDelegate1() { TestObserver<Integer> to = new TestObserver<Integer>(); TestSubscriber<Integer> ts = TestSubscriber.create(to); ts.onCompleted(); to.assertTerminalEvent(); }
@Test public void testAssert() { Observable<Integer> oi = Observable.from(Arrays.asList(1, 2)); TestSubscriber<Integer> o = new TestSubscriber<Integer>(); oi.subscribe(o); o.assertReceivedOnNext(Arrays.asList(1, 2)); assertEquals(2, o.getOnNextEvents().size()); o.assertTerminalEvent(); }
@Test public void testNotCompleted() { TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); try { ts.assertCompleted(); } catch (AssertionError ex) { // expected return; } fail("Not completed and no assertion error!"); }
@Test public void testUnsubscribed() { TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); try { ts.assertUnsubscribed(); } catch (AssertionError ex) { // expected return; } fail("Not unsubscribed but not reported!"); }
@Test public void testNoError2() { TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); try { ts.assertError(new TestException()); } catch (AssertionError ex) { // expected return; } fail("No present but no assertion error!"); }
@Test public void testObserveOn() { int NUM = (int) (Observable.bufferSize() * 2.1); AtomicInteger c = new AtomicInteger(); TestSubscriber<Integer> ts = new TestSubscriber<>(); incrementingIntegers(c).observeOn(Schedulers.computation()).take(NUM).subscribe(ts); ts.awaitTerminalEvent(); ts.assertNoErrors(); System.out.println("testObserveOn => Received: " + ts.valueCount() + " Emitted: " + c.get()); assertEquals(NUM, ts.valueCount()); assertTrue(c.get() < Observable.bufferSize() * 4); }
@Test public void testDifferentError3() { TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); ts.onError(new RuntimeException()); try { ts.assertError(TestException.class); } catch (AssertionError ex) { // expected return; } fail("Different Error present but no assertion error!"); }
@Test public void testNoValues() { TestSubscriber<Integer> ts = TestSubscriber.create(); ts.onNext(1); try { ts.assertNoValues(); fail("Failed to report there were values!"); } catch (AssertionError ex) { // expected } }
@Test public void testMultipleCompletions2() { TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); ts.onCompleted(); ts.onCompleted(); try { ts.assertNotCompleted(); } catch (AssertionError ex) { // expected return; } fail("Multiple completions and no assertion error!"); }
@Test public void testAssertNotMatchValue() { Observable<Integer> oi = Observable.from(Arrays.asList(1, 2)); TestSubscriber<Integer> o = new TestSubscriber<Integer>(); oi.subscribe(o); thrown.expect(AssertionError.class); thrown.expectMessage("Value at index: 1 expected to be [3] (Integer) but was: [2] (Integer)"); o.assertReceivedOnNext(Arrays.asList(1, 3)); assertEquals(2, o.getOnNextEvents().size()); o.assertTerminalEvent(); }
@Test public void testAssertNotMatchCount() { Observable<Integer> oi = Observable.from(Arrays.asList(1, 2)); TestSubscriber<Integer> o = new TestSubscriber<Integer>(); oi.subscribe(o); thrown.expect(AssertionError.class); thrown.expectMessage("Number of items does not match. Provided: 1 Actual: 2"); o.assertReceivedOnNext(Arrays.asList(1)); assertEquals(2, o.getOnNextEvents().size()); o.assertTerminalEvent(); }
@Test public void testNoTerminalEventBut1Error() { TestSubscriber<Integer> ts = TestSubscriber.create(); ts.onError(new TestException()); try { ts.assertNoTerminalEvent(); fail("Failed to report there were terminal event(s)!"); } catch (AssertionError ex) { // expected } }
@Test public void testMultipleErrors3() { TestSubscriber<Integer> ts = new TestSubscriber<Integer>(); ts.onError(new TestException()); ts.onError(new TestException()); try { ts.assertError(new TestException()); } catch (AssertionError ex) { if (!(ex.getCause() instanceof CompositeException)) { fail("Multiple Error present but the reported error doesn't have a composite cause!"); } // expected return; } fail("Multiple Error present but no assertion error!"); }
@Test public void testAssertTerminalEventNotReceived() { PublishSubject<Integer> p = PublishSubject.create(); TestSubscriber<Integer> o = new TestSubscriber<Integer>(); p.subscribe(o); p.onNext(1); p.onNext(2); thrown.expect(AssertionError.class); thrown.expectMessage("No terminal events received."); o.assertReceivedOnNext(Arrays.asList(1, 2)); assertEquals(2, o.getOnNextEvents().size()); o.assertTerminalEvent(); }
@Test public void testNoTerminalEventBut2Errors() { TestSubscriber<Integer> ts = TestSubscriber.create(); ts.onError(new TestException()); ts.onError(new TestException()); try { ts.assertNoTerminalEvent(); fail("Failed to report there were terminal event(s)!"); } catch (AssertionError ex) { // expected if (!(ex.getCause() instanceof CompositeException)) { fail("Did not report a composite exception cause: " + ex.getCause()); } } }
/** Should request -1 for infinite */ @Test public void testRequestFromFinalSubscribeWithoutRequestValue() { TestSubscriber<String> s = new TestSubscriber<>(); final AtomicLong r = new AtomicLong(); s.onSubscribe( new Subscription() { @Override public void request(long n) { r.set(n); } @Override public void cancel() {} }); assertEquals(Long.MAX_VALUE, r.get()); }
@Test(timeout = 20000) public void testOnBackpressureDropWithAction() { for (int i = 0; i < 100; i++) { final AtomicInteger emitCount = new AtomicInteger(); final AtomicInteger dropCount = new AtomicInteger(); final AtomicInteger passCount = new AtomicInteger(); final int NUM = Observable.bufferSize() * 3; // > 1 so that take doesn't prevent buffer overflow TestSubscriber<Integer> ts = new TestSubscriber<>(); firehose(emitCount) .onBackpressureDrop(v -> dropCount.incrementAndGet()) .doOnNext(v -> passCount.incrementAndGet()) .observeOn(Schedulers.computation()) .map(SLOW_PASS_THRU) .take(NUM) .subscribe(ts); ts.awaitTerminalEvent(); ts.assertNoErrors(); List<Integer> onNextEvents = ts.values(); Integer lastEvent = onNextEvents.get(NUM - 1); System.out.println( testName.getMethodName() + " => Received: " + onNextEvents.size() + " Passed: " + passCount.get() + " Dropped: " + dropCount.get() + " Emitted: " + emitCount.get() + " Last value: " + lastEvent); assertEquals(NUM, onNextEvents.size()); // in reality, NUM < passCount assertTrue(NUM <= passCount.get()); // it drop, so we should get some number far higher than what would have sequentially // incremented assertTrue(NUM - 1 <= lastEvent.intValue()); assertTrue(0 < dropCount.get()); assertEquals(emitCount.get(), passCount.get() + dropCount.get()); } }
@Test public void testFlatMapSync() { int NUM = (int) (Observable.bufferSize() * 2.1); AtomicInteger c = new AtomicInteger(); TestSubscriber<Integer> ts = new TestSubscriber<>(); incrementingIntegers(c) .flatMap(i -> incrementingIntegers(new AtomicInteger()).take(10)) .take(NUM) .subscribe(ts); ts.awaitTerminalEvent(); ts.assertNoErrors(); System.out.println("testFlatMapSync => Received: " + ts.valueCount() + " Emitted: " + c.get()); assertEquals(NUM, ts.valueCount()); // expect less than 1 buffer since the flatMap is emitting 10 each time, so it is NUM/10 that // will be taken. assertTrue(c.get() < Observable.bufferSize()); }