/** * 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.Callable; import org.junit.*; import org.mockito.InOrder; import org.reactivestreams.*; import io.reactivex.*; import io.reactivex.exceptions.TestException; import io.reactivex.functions.Function; import io.reactivex.internal.functions.Functions; import io.reactivex.internal.fuseable.*; import io.reactivex.internal.subscriptions.BooleanSubscription; import io.reactivex.plugins.RxJavaPlugins; import io.reactivex.processors.UnicastProcessor; import io.reactivex.subscribers.*; public class FlowableDistinctTest { Subscriber<String> w; // nulls lead to exceptions final Function<String, String> TO_UPPER_WITH_EXCEPTION = new Function<String, String>() { @Override public String apply(String s) { if (s.equals("x")) { return "XX"; } return s.toUpperCase(); } }; @Before public void before() { w = TestHelper.mockSubscriber(); } @Test public void testDistinctOfNone() { Flowable<String> src = Flowable.empty(); src.distinct().subscribe(w); verify(w, never()).onNext(anyString()); verify(w, never()).onError(any(Throwable.class)); verify(w, times(1)).onComplete(); } @Test public void testDistinctOfNoneWithKeySelector() { Flowable<String> src = Flowable.empty(); src.distinct(TO_UPPER_WITH_EXCEPTION).subscribe(w); verify(w, never()).onNext(anyString()); verify(w, never()).onError(any(Throwable.class)); verify(w, times(1)).onComplete(); } @Test public void testDistinctOfNormalSource() { Flowable<String> src = Flowable.just("a", "b", "c", "c", "c", "b", "b", "a", "e"); src.distinct().subscribe(w); InOrder inOrder = inOrder(w); inOrder.verify(w, times(1)).onNext("a"); inOrder.verify(w, times(1)).onNext("b"); inOrder.verify(w, times(1)).onNext("c"); inOrder.verify(w, times(1)).onNext("e"); inOrder.verify(w, times(1)).onComplete(); inOrder.verify(w, never()).onNext(anyString()); verify(w, never()).onError(any(Throwable.class)); } @Test public void testDistinctOfNormalSourceWithKeySelector() { Flowable<String> src = Flowable.just("a", "B", "c", "C", "c", "B", "b", "a", "E"); src.distinct(TO_UPPER_WITH_EXCEPTION).subscribe(w); InOrder inOrder = inOrder(w); inOrder.verify(w, times(1)).onNext("a"); inOrder.verify(w, times(1)).onNext("B"); inOrder.verify(w, times(1)).onNext("c"); inOrder.verify(w, times(1)).onNext("E"); inOrder.verify(w, times(1)).onComplete(); inOrder.verify(w, never()).onNext(anyString()); verify(w, never()).onError(any(Throwable.class)); } @Test @Ignore("Null values no longer allowed") public void testDistinctOfSourceWithNulls() { Flowable<String> src = Flowable.just(null, "a", "a", null, null, "b", null); src.distinct().subscribe(w); InOrder inOrder = inOrder(w); inOrder.verify(w, times(1)).onNext(null); inOrder.verify(w, times(1)).onNext("a"); inOrder.verify(w, times(1)).onNext("b"); inOrder.verify(w, times(1)).onComplete(); inOrder.verify(w, never()).onNext(anyString()); verify(w, never()).onError(any(Throwable.class)); } @Test @Ignore("Null values no longer allowed") public void testDistinctOfSourceWithExceptionsFromKeySelector() { Flowable<String> src = Flowable.just("a", "b", null, "c"); src.distinct(TO_UPPER_WITH_EXCEPTION).subscribe(w); InOrder inOrder = inOrder(w); inOrder.verify(w, times(1)).onNext("a"); inOrder.verify(w, times(1)).onNext("b"); inOrder.verify(w, times(1)).onError(any(NullPointerException.class)); inOrder.verify(w, never()).onNext(anyString()); inOrder.verify(w, never()).onComplete(); } @Test public void error() { Flowable.error(new TestException()) .distinct() .test() .assertFailure(TestException.class); } @Test public void fusedSync() { TestSubscriber<Integer> to = SubscriberFusion.newTest(QueueDisposable.ANY); Flowable.just(1, 1, 2, 1, 3, 2, 4, 5, 4) .distinct() .subscribe(to); SubscriberFusion.assertFusion(to, QueueDisposable.SYNC) .assertResult(1, 2, 3, 4, 5); } @Test public void fusedAsync() { TestSubscriber<Integer> to = SubscriberFusion.newTest(QueueDisposable.ANY); UnicastProcessor<Integer> us = UnicastProcessor.create(); us .distinct() .subscribe(to); TestHelper.emit(us, 1, 1, 2, 1, 3, 2, 4, 5, 4); SubscriberFusion.assertFusion(to, QueueDisposable.ASYNC) .assertResult(1, 2, 3, 4, 5); } @Test public void fusedClear() { Flowable.just(1, 1, 2, 1, 3, 2, 4, 5, 4) .distinct() .subscribe(new FlowableSubscriber<Integer>() { @Override public void onSubscribe(Subscription d) { QueueSubscription<?> qd = (QueueSubscription<?>)d; assertFalse(qd.isEmpty()); qd.clear(); assertTrue(qd.isEmpty()); } @Override public void onNext(Integer value) { } @Override public void onError(Throwable e) { } @Override public void onComplete() { } }); } @Test public void collectionSupplierThrows() { Flowable.just(1) .distinct(Functions.identity(), new Callable<Collection<Object>>() { @Override public Collection<Object> call() throws Exception { throw new TestException(); } }) .test() .assertFailure(TestException.class); } @Test public void collectionSupplierIsNull() { Flowable.just(1) .distinct(Functions.identity(), new Callable<Collection<Object>>() { @Override public Collection<Object> call() throws Exception { return null; } }) .test() .assertFailure(NullPointerException.class) .assertErrorMessage("The collectionSupplier returned a null collection. Null values are generally not allowed in 2.x operators and sources."); } @Test public void badSource() { List<Throwable> errors = TestHelper.trackPluginErrors(); try { new Flowable<Integer>() { @Override protected void subscribeActual(Subscriber<? super Integer> observer) { observer.onSubscribe(new BooleanSubscription()); observer.onNext(1); observer.onComplete(); observer.onNext(2); observer.onError(new TestException()); observer.onComplete(); } } .distinct() .test() .assertResult(1); TestHelper.assertUndeliverable(errors, 0, TestException.class); } finally { RxJavaPlugins.reset(); } } }