/** * 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.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.util.*; import java.util.concurrent.atomic.*; import org.junit.Test; import org.mockito.InOrder; import org.reactivestreams.*; import io.reactivex.*; import io.reactivex.Flowable; import io.reactivex.functions.*; import io.reactivex.plugins.RxJavaPlugins; import io.reactivex.subscribers.DefaultSubscriber; public class FlowableSingleTest { @Test public void testSingleFlowable() { Flowable<Integer> observable = Flowable.just(1).singleElement().toFlowable(); Subscriber<Integer> observer = TestHelper.mockSubscriber(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onNext(1); inOrder.verify(observer, times(1)).onComplete(); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleWithTooManyElementsFlowable() { Flowable<Integer> observable = Flowable.just(1, 2).singleElement().toFlowable(); Subscriber<Integer> observer = TestHelper.mockSubscriber(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onError( isA(IllegalArgumentException.class)); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleWithEmptyFlowable() { Flowable<Integer> observable = Flowable.<Integer> empty().singleElement().toFlowable(); Subscriber<Integer> observer = TestHelper.mockSubscriber(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer).onComplete(); inOrder.verify(observer, never()).onError(any(Throwable.class)); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleDoesNotRequestMoreThanItNeedsIf1Then2RequestedFlowable() { final List<Long> requests = new ArrayList<Long>(); Flowable.just(1) // .doOnRequest(new LongConsumer() { @Override public void accept(long n) { requests.add(n); } }) // .singleElement() // .toFlowable() .subscribe(new DefaultSubscriber<Integer>() { @Override public void onStart() { request(1); } @Override public void onComplete() { } @Override public void onError(Throwable e) { } @Override public void onNext(Integer t) { request(2); } }); // FIXME single now triggers fast-path assertEquals(Arrays.asList(Long.MAX_VALUE), requests); } @Test public void testSingleDoesNotRequestMoreThanItNeedsIf3RequestedFlowable() { final List<Long> requests = new ArrayList<Long>(); Flowable.just(1) // .doOnRequest(new LongConsumer() { @Override public void accept(long n) { requests.add(n); } }) // .singleElement() // .toFlowable() .subscribe(new DefaultSubscriber<Integer>() { @Override public void onStart() { request(3); } @Override public void onComplete() { } @Override public void onError(Throwable e) { } @Override public void onNext(Integer t) { } }); // FIXME single now triggers fast-path assertEquals(Arrays.asList(Long.MAX_VALUE), requests); } @Test public void testSingleRequestsExactlyWhatItNeedsIf1RequestedFlowable() { final List<Long> requests = new ArrayList<Long>(); Flowable.just(1) // .doOnRequest(new LongConsumer() { @Override public void accept(long n) { requests.add(n); } }) // .singleElement() // .toFlowable() .subscribe(new DefaultSubscriber<Integer>() { @Override public void onStart() { request(1); } @Override public void onComplete() { } @Override public void onError(Throwable e) { } @Override public void onNext(Integer t) { } }); // FIXME single now triggers fast-path assertEquals(Arrays.asList(Long.MAX_VALUE), requests); } @Test public void testSingleWithPredicateFlowable() { Flowable<Integer> observable = Flowable.just(1, 2) .filter( new Predicate<Integer>() { @Override public boolean test(Integer t1) { return t1 % 2 == 0; } }) .singleElement().toFlowable(); Subscriber<Integer> observer = TestHelper.mockSubscriber(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onNext(2); inOrder.verify(observer, times(1)).onComplete(); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleWithPredicateAndTooManyElementsFlowable() { Flowable<Integer> observable = Flowable.just(1, 2, 3, 4) .filter( new Predicate<Integer>() { @Override public boolean test(Integer t1) { return t1 % 2 == 0; } }) .singleElement().toFlowable(); Subscriber<Integer> observer = TestHelper.mockSubscriber(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onError( isA(IllegalArgumentException.class)); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleWithPredicateAndEmptyFlowable() { Flowable<Integer> observable = Flowable.just(1) .filter( new Predicate<Integer>() { @Override public boolean test(Integer t1) { return t1 % 2 == 0; } }) .singleElement().toFlowable(); Subscriber<Integer> observer = TestHelper.mockSubscriber(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer).onComplete(); inOrder.verify(observer, never()).onError(any(Throwable.class)); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleOrDefaultFlowable() { Flowable<Integer> observable = Flowable.just(1).single(2).toFlowable(); Subscriber<Integer> observer = TestHelper.mockSubscriber(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onNext(1); inOrder.verify(observer, times(1)).onComplete(); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleOrDefaultWithTooManyElementsFlowable() { Flowable<Integer> observable = Flowable.just(1, 2).single(3).toFlowable(); Subscriber<Integer> observer = TestHelper.mockSubscriber(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onError( isA(IllegalArgumentException.class)); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleOrDefaultWithEmptyFlowable() { Flowable<Integer> observable = Flowable.<Integer> empty() .single(1).toFlowable(); Subscriber<Integer> observer = TestHelper.mockSubscriber(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onNext(1); inOrder.verify(observer, times(1)).onComplete(); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleOrDefaultWithPredicateFlowable() { Flowable<Integer> observable = Flowable.just(1, 2) .filter(new Predicate<Integer>() { @Override public boolean test(Integer t1) { return t1 % 2 == 0; } }) .single(4).toFlowable(); Subscriber<Integer> observer = TestHelper.mockSubscriber(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onNext(2); inOrder.verify(observer, times(1)).onComplete(); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleOrDefaultWithPredicateAndTooManyElementsFlowable() { Flowable<Integer> observable = Flowable.just(1, 2, 3, 4) .filter(new Predicate<Integer>() { @Override public boolean test(Integer t1) { return t1 % 2 == 0; } }) .single(6).toFlowable(); Subscriber<Integer> observer = TestHelper.mockSubscriber(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onError( isA(IllegalArgumentException.class)); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleOrDefaultWithPredicateAndEmptyFlowable() { Flowable<Integer> observable = Flowable.just(1) .filter(new Predicate<Integer>() { @Override public boolean test(Integer t1) { return t1 % 2 == 0; } }) .single(2).toFlowable(); Subscriber<Integer> observer = TestHelper.mockSubscriber(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onNext(2); inOrder.verify(observer, times(1)).onComplete(); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleWithBackpressureFlowable() { Flowable<Integer> observable = Flowable.just(1, 2).singleElement().toFlowable(); Subscriber<Integer> subscriber = spy(new DefaultSubscriber<Integer>() { @Override public void onStart() { request(1); } @Override public void onComplete() { } @Override public void onError(Throwable e) { } @Override public void onNext(Integer integer) { request(1); } }); observable.subscribe(subscriber); InOrder inOrder = inOrder(subscriber); inOrder.verify(subscriber, times(1)).onError(isA(IllegalArgumentException.class)); inOrder.verifyNoMoreInteractions(); } @Test public void testSingle() { Maybe<Integer> observable = Flowable.just(1).singleElement(); MaybeObserver<Integer> observer = TestHelper.mockMaybeObserver(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onSuccess(1); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleWithTooManyElements() { Maybe<Integer> observable = Flowable.just(1, 2).singleElement(); MaybeObserver<Integer> observer = TestHelper.mockMaybeObserver(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onError( isA(IllegalArgumentException.class)); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleWithEmpty() { Maybe<Integer> observable = Flowable.<Integer> empty().singleElement(); MaybeObserver<Integer> observer = TestHelper.mockMaybeObserver(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer).onComplete(); inOrder.verify(observer, never()).onError(any(Throwable.class)); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleDoesNotRequestMoreThanItNeedsToEmitItem() { final AtomicLong request = new AtomicLong(); Flowable.just(1).doOnRequest(new LongConsumer() { @Override public void accept(long n) { request.addAndGet(n); } }).blockingSingle(); // FIXME single now triggers fast-path assertEquals(Long.MAX_VALUE, request.get()); } @Test public void testSingleDoesNotRequestMoreThanItNeedsToEmitErrorFromEmpty() { final AtomicLong request = new AtomicLong(); try { Flowable.empty().doOnRequest(new LongConsumer() { @Override public void accept(long n) { request.addAndGet(n); } }).blockingSingle(); } catch (NoSuchElementException e) { // FIXME single now triggers fast-path assertEquals(Long.MAX_VALUE, request.get()); } } @Test public void testSingleDoesNotRequestMoreThanItNeedsToEmitErrorFromMoreThanOne() { final AtomicLong request = new AtomicLong(); try { Flowable.just(1, 2).doOnRequest(new LongConsumer() { @Override public void accept(long n) { request.addAndGet(n); } }).blockingSingle(); } catch (IllegalArgumentException e) { // FIXME single now triggers fast-path assertEquals(Long.MAX_VALUE, request.get()); } } @Test public void testSingleWithPredicate() { Maybe<Integer> observable = Flowable.just(1, 2) .filter( new Predicate<Integer>() { @Override public boolean test(Integer t1) { return t1 % 2 == 0; } }) .singleElement(); MaybeObserver<Integer> observer = TestHelper.mockMaybeObserver(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onSuccess(2); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleWithPredicateAndTooManyElements() { Maybe<Integer> observable = Flowable.just(1, 2, 3, 4) .filter( new Predicate<Integer>() { @Override public boolean test(Integer t1) { return t1 % 2 == 0; } }) .singleElement(); MaybeObserver<Integer> observer = TestHelper.mockMaybeObserver(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onError( isA(IllegalArgumentException.class)); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleWithPredicateAndEmpty() { Maybe<Integer> observable = Flowable.just(1) .filter( new Predicate<Integer>() { @Override public boolean test(Integer t1) { return t1 % 2 == 0; } }) .singleElement(); MaybeObserver<Integer> observer = TestHelper.mockMaybeObserver(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer).onComplete(); inOrder.verify(observer, never()).onError(any(Throwable.class)); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleOrDefault() { Single<Integer> observable = Flowable.just(1).single(2); SingleObserver<Integer> observer = TestHelper.mockSingleObserver(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onSuccess(1); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleOrDefaultWithTooManyElements() { Single<Integer> observable = Flowable.just(1, 2).single(3); SingleObserver<Integer> observer = TestHelper.mockSingleObserver(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onError( isA(IllegalArgumentException.class)); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleOrDefaultWithEmpty() { Single<Integer> observable = Flowable.<Integer> empty() .single(1); SingleObserver<Integer> observer = TestHelper.mockSingleObserver(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onSuccess(1); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleOrDefaultWithPredicate() { Single<Integer> observable = Flowable.just(1, 2) .filter(new Predicate<Integer>() { @Override public boolean test(Integer t1) { return t1 % 2 == 0; } }) .single(4); SingleObserver<Integer> observer = TestHelper.mockSingleObserver(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onSuccess(2); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleOrDefaultWithPredicateAndTooManyElements() { Single<Integer> observable = Flowable.just(1, 2, 3, 4) .filter(new Predicate<Integer>() { @Override public boolean test(Integer t1) { return t1 % 2 == 0; } }) .single(6); SingleObserver<Integer> observer = TestHelper.mockSingleObserver(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onError( isA(IllegalArgumentException.class)); inOrder.verifyNoMoreInteractions(); } @Test public void testSingleOrDefaultWithPredicateAndEmpty() { Single<Integer> observable = Flowable.just(1) .filter(new Predicate<Integer>() { @Override public boolean test(Integer t1) { return t1 % 2 == 0; } }) .single(2); SingleObserver<Integer> observer = TestHelper.mockSingleObserver(); observable.subscribe(observer); InOrder inOrder = inOrder(observer); inOrder.verify(observer, times(1)).onSuccess(2); inOrder.verifyNoMoreInteractions(); } @Test(timeout = 30000) public void testIssue1527() throws InterruptedException { //https://github.com/ReactiveX/RxJava/pull/1527 Flowable<Integer> source = Flowable.just(1, 2, 3, 4, 5, 6); Maybe<Integer> reduced = source.reduce(new BiFunction<Integer, Integer, Integer>() { @Override public Integer apply(Integer i1, Integer i2) { return i1 + i2; } }); Integer r = reduced.blockingGet(); assertEquals(21, r.intValue()); } @Test public void singleOrErrorNoElement() { Flowable.empty() .singleOrError() .test() .assertNoValues() .assertError(NoSuchElementException.class); } @Test public void singleOrErrorOneElement() { Flowable.just(1) .singleOrError() .test() .assertNoErrors() .assertValue(1); } @Test public void singleOrErrorMultipleElements() { Flowable.just(1, 2, 3) .singleOrError() .test() .assertNoValues() .assertError(IllegalArgumentException.class); } @Test public void singleOrErrorError() { Flowable.error(new RuntimeException("error")) .singleOrError() .test() .assertNoValues() .assertErrorMessage("error") .assertError(RuntimeException.class); } @Test(timeout = 30000) public void testIssue1527Flowable() throws InterruptedException { //https://github.com/ReactiveX/RxJava/pull/1527 Flowable<Integer> source = Flowable.just(1, 2, 3, 4, 5, 6); Flowable<Integer> reduced = source.reduce(new BiFunction<Integer, Integer, Integer>() { @Override public Integer apply(Integer i1, Integer i2) { return i1 + i2; } }).toFlowable(); Integer r = reduced.blockingFirst(); assertEquals(21, r.intValue()); } @Test public void singleElementOperatorDoNotSwallowExceptionWhenDone() { final Throwable exception = new RuntimeException("some error"); final AtomicReference<Throwable> error = new AtomicReference<Throwable>(); try { RxJavaPlugins.setErrorHandler(new Consumer<Throwable>() { @Override public void accept(final Throwable throwable) throws Exception { error.set(throwable); } }); Flowable.unsafeCreate(new Publisher<Integer>() { @Override public void subscribe(final Subscriber<? super Integer> observer) { observer.onComplete(); observer.onError(exception); } }).singleElement().test().assertComplete(); assertSame(exception, error.get().getCause()); } finally { RxJavaPlugins.reset(); } } @Test public void badSource() { TestHelper.checkBadSourceFlowable(new Function<Flowable<Object>, Object>() { @Override public Object apply(Flowable<Object> o) throws Exception { return o.singleOrError(); } }, false, 1, 1, 1); TestHelper.checkBadSourceFlowable(new Function<Flowable<Object>, Object>() { @Override public Object apply(Flowable<Object> o) throws Exception { return o.singleElement(); } }, false, 1, 1, 1); TestHelper.checkBadSourceFlowable(new Function<Flowable<Object>, Object>() { @Override public Object apply(Flowable<Object> o) throws Exception { return o.singleOrError().toFlowable(); } }, false, 1, 1, 1); } @Test public void doubleOnSubscribe() { TestHelper.checkDoubleOnSubscribeFlowableToSingle(new Function<Flowable<Object>, SingleSource<Object>>() { @Override public SingleSource<Object> apply(Flowable<Object> o) throws Exception { return o.singleOrError(); } }); TestHelper.checkDoubleOnSubscribeFlowableToMaybe(new Function<Flowable<Object>, MaybeSource<Object>>() { @Override public MaybeSource<Object> apply(Flowable<Object> o) throws Exception { return o.singleElement(); } }); } }