/**
* 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.parallel;
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.*;
import org.junit.*;
import org.reactivestreams.*;
import io.reactivex.*;
import io.reactivex.exceptions.*;
import io.reactivex.functions.*;
import io.reactivex.internal.functions.Functions;
import io.reactivex.internal.util.*;
import io.reactivex.plugins.RxJavaPlugins;
import io.reactivex.processors.UnicastProcessor;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.subscribers.TestSubscriber;
public class ParallelFlowableTest {
@Test
public void sequentialMode() {
Flowable<Integer> source = Flowable.range(1, 1000000).hide();
for (int i = 1; i < 33; i++) {
Flowable<Integer> result = ParallelFlowable.from(source, i)
.map(new Function<Integer, Integer>() {
@Override
public Integer apply(Integer v) throws Exception {
return v + 1;
}
})
.sequential()
;
TestSubscriber<Integer> ts = new TestSubscriber<Integer>();
result.subscribe(ts);
ts
.assertSubscribed()
.assertValueCount(1000000)
.assertComplete()
.assertNoErrors()
;
}
}
@Test
public void sequentialModeFused() {
Flowable<Integer> source = Flowable.range(1, 1000000);
for (int i = 1; i < 33; i++) {
Flowable<Integer> result = ParallelFlowable.from(source, i)
.map(new Function<Integer, Integer>() {
@Override
public Integer apply(Integer v) throws Exception {
return v + 1;
}
})
.sequential()
;
TestSubscriber<Integer> ts = new TestSubscriber<Integer>();
result.subscribe(ts);
ts
.assertSubscribed()
.assertValueCount(1000000)
.assertComplete()
.assertNoErrors()
;
}
}
@Test
public void parallelMode() {
Flowable<Integer> source = Flowable.range(1, 1000000).hide();
int ncpu = Math.max(8, Runtime.getRuntime().availableProcessors());
for (int i = 1; i < ncpu + 1; i++) {
ExecutorService exec = Executors.newFixedThreadPool(i);
Scheduler scheduler = Schedulers.from(exec);
try {
Flowable<Integer> result = ParallelFlowable.from(source, i)
.runOn(scheduler)
.map(new Function<Integer, Integer>() {
@Override
public Integer apply(Integer v) throws Exception {
return v + 1;
}
})
.sequential()
;
TestSubscriber<Integer> ts = new TestSubscriber<Integer>();
result.subscribe(ts);
ts.awaitDone(10, TimeUnit.SECONDS);
ts
.assertSubscribed()
.assertValueCount(1000000)
.assertComplete()
.assertNoErrors()
;
} finally {
exec.shutdown();
}
}
}
@Test
public void parallelModeFused() {
Flowable<Integer> source = Flowable.range(1, 1000000);
int ncpu = Math.max(8, Runtime.getRuntime().availableProcessors());
for (int i = 1; i < ncpu + 1; i++) {
ExecutorService exec = Executors.newFixedThreadPool(i);
Scheduler scheduler = Schedulers.from(exec);
try {
Flowable<Integer> result = ParallelFlowable.from(source, i)
.runOn(scheduler)
.map(new Function<Integer, Integer>() {
@Override
public Integer apply(Integer v) throws Exception {
return v + 1;
}
})
.sequential()
;
TestSubscriber<Integer> ts = new TestSubscriber<Integer>();
result.subscribe(ts);
ts.awaitDone(10, TimeUnit.SECONDS);
ts
.assertSubscribed()
.assertValueCount(1000000)
.assertComplete()
.assertNoErrors()
;
} finally {
exec.shutdown();
}
}
}
@Test
public void reduceFull() {
for (int i = 1; i <= Runtime.getRuntime().availableProcessors() * 2; i++) {
TestSubscriber<Integer> ts = new TestSubscriber<Integer>();
Flowable.range(1, 10)
.parallel(i)
.reduce(new BiFunction<Integer, Integer, Integer>() {
@Override
public Integer apply(Integer a, Integer b) throws Exception {
return a + b;
}
})
.subscribe(ts);
ts.assertResult(55);
}
}
@Test
public void parallelReduceFull() {
int m = 100000;
for (int n = 1; n <= m; n *= 10) {
// System.out.println(n);
for (int i = 1; i <= Runtime.getRuntime().availableProcessors(); i++) {
// System.out.println(" " + i);
ExecutorService exec = Executors.newFixedThreadPool(i);
Scheduler scheduler = Schedulers.from(exec);
try {
TestSubscriber<Long> ts = new TestSubscriber<Long>();
Flowable.range(1, n)
.map(new Function<Integer, Long>() {
@Override
public Long apply(Integer v) throws Exception {
return (long)v;
}
})
.parallel(i)
.runOn(scheduler)
.reduce(new BiFunction<Long, Long, Long>() {
@Override
public Long apply(Long a, Long b) throws Exception {
return a + b;
}
})
.subscribe(ts);
ts.awaitDone(500, TimeUnit.SECONDS);
long e = ((long)n) * (1 + n) / 2;
ts.assertResult(e);
} finally {
exec.shutdown();
}
}
}
}
@SuppressWarnings("unchecked")
@Test
public void toSortedList() {
TestSubscriber<List<Integer>> ts = new TestSubscriber<List<Integer>>();
Flowable.fromArray(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
.parallel()
.toSortedList(Functions.naturalComparator())
.subscribe(ts);
ts.assertResult(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
}
@Test
public void sorted() {
TestSubscriber<Integer> ts = new TestSubscriber<Integer>(0);
Flowable.fromArray(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
.parallel()
.sorted(Functions.naturalComparator())
.subscribe(ts);
ts.assertNoValues();
ts.request(2);
ts.assertValues(1, 2);
ts.request(5);
ts.assertValues(1, 2, 3, 4, 5, 6, 7);
ts.request(3);
ts.assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
}
@Test
public void collect() {
Callable<List<Integer>> as = new Callable<List<Integer>>() {
@Override
public List<Integer> call() throws Exception {
return new ArrayList<Integer>();
}
};
TestSubscriber<Integer> ts = new TestSubscriber<Integer>();
Flowable.range(1, 10)
.parallel()
.collect(as, new BiConsumer<List<Integer>, Integer>() {
@Override
public void accept(List<Integer> a, Integer b) throws Exception {
a.add(b);
}
})
.sequential()
.flatMapIterable(new Function<List<Integer>, Iterable<Integer>>() {
@Override
public Iterable<Integer> apply(List<Integer> v) throws Exception {
return v;
}
})
.subscribe(ts);
ts.assertValueSet(new HashSet<Integer>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)))
.assertNoErrors()
.assertComplete();
}
@SuppressWarnings("unchecked")
@Test
public void from() {
TestSubscriber<Integer> ts = new TestSubscriber<Integer>();
ParallelFlowable.fromArray(Flowable.range(1, 5), Flowable.range(6, 5))
.sequential()
.subscribe(ts);
ts.assertValueSet(new HashSet<Integer>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)))
.assertNoErrors()
.assertComplete();
}
@Test
public void concatMapUnordered() {
TestSubscriber<Integer> ts = new TestSubscriber<Integer>();
Flowable.range(1, 5)
.parallel()
.concatMap(new Function<Integer, Publisher<Integer>>() {
@Override
public Publisher<Integer> apply(Integer v) throws Exception {
return Flowable.range(v * 10 + 1, 3);
}
})
.sequential()
.subscribe(ts);
ts.assertValueSet(new HashSet<Integer>(Arrays.asList(11, 12, 13, 21, 22, 23, 31, 32, 33, 41, 42, 43, 51, 52, 53)))
.assertNoErrors()
.assertComplete();
}
@Test
public void flatMapUnordered() {
TestSubscriber<Integer> ts = new TestSubscriber<Integer>();
Flowable.range(1, 5)
.parallel()
.flatMap(new Function<Integer, Publisher<Integer>>() {
@Override
public Publisher<Integer> apply(Integer v) throws Exception {
return Flowable.range(v * 10 + 1, 3);
}
})
.sequential()
.subscribe(ts);
ts.assertValueSet(new HashSet<Integer>(Arrays.asList(11, 12, 13, 21, 22, 23, 31, 32, 33, 41, 42, 43, 51, 52, 53)))
.assertNoErrors()
.assertComplete();
}
@Test
public void collectAsyncFused() {
ExecutorService exec = Executors.newFixedThreadPool(3);
Scheduler s = Schedulers.from(exec);
try {
Callable<List<Integer>> as = new Callable<List<Integer>>() {
@Override
public List<Integer> call() throws Exception {
return new ArrayList<Integer>();
}
};
TestSubscriber<List<Integer>> ts = new TestSubscriber<List<Integer>>();
Flowable.range(1, 100000)
.parallel(3)
.runOn(s)
.collect(as, new BiConsumer<List<Integer>, Integer>() {
@Override
public void accept(List<Integer> a, Integer b) throws Exception {
a.add(b);
}
})
.doOnNext(new Consumer<List<Integer>>() {
@Override
public void accept(List<Integer> v) throws Exception {
System.out.println(v.size());
}
})
.sequential()
.subscribe(ts);
ts.awaitDone(5, TimeUnit.SECONDS);
ts.assertValueCount(3)
.assertNoErrors()
.assertComplete()
;
List<List<Integer>> list = ts.values();
Assert.assertEquals(100000, list.get(0).size() + list.get(1).size() + list.get(2).size());
} finally {
exec.shutdown();
}
}
@Test
public void collectAsync() {
ExecutorService exec = Executors.newFixedThreadPool(3);
Scheduler s = Schedulers.from(exec);
try {
Callable<List<Integer>> as = new Callable<List<Integer>>() {
@Override
public List<Integer> call() throws Exception {
return new ArrayList<Integer>();
}
};
TestSubscriber<List<Integer>> ts = new TestSubscriber<List<Integer>>();
Flowable.range(1, 100000).hide()
.parallel(3)
.runOn(s)
.collect(as, new BiConsumer<List<Integer>, Integer>() {
@Override
public void accept(List<Integer> a, Integer b) throws Exception {
a.add(b);
}
})
.doOnNext(new Consumer<List<Integer>>() {
@Override
public void accept(List<Integer> v) throws Exception {
System.out.println(v.size());
}
})
.sequential()
.subscribe(ts);
ts.awaitDone(5, TimeUnit.SECONDS);
ts.assertValueCount(3)
.assertNoErrors()
.assertComplete()
;
List<List<Integer>> list = ts.values();
Assert.assertEquals(100000, list.get(0).size() + list.get(1).size() + list.get(2).size());
} finally {
exec.shutdown();
}
}
@Test
public void collectAsync2() {
ExecutorService exec = Executors.newFixedThreadPool(3);
Scheduler s = Schedulers.from(exec);
try {
Callable<List<Integer>> as = new Callable<List<Integer>>() {
@Override
public List<Integer> call() throws Exception {
return new ArrayList<Integer>();
}
};
TestSubscriber<List<Integer>> ts = new TestSubscriber<List<Integer>>();
Flowable.range(1, 100000).hide()
.observeOn(s)
.parallel(3)
.runOn(s)
.collect(as, new BiConsumer<List<Integer>, Integer>() {
@Override
public void accept(List<Integer> a, Integer b) throws Exception {
a.add(b);
}
})
.doOnNext(new Consumer<List<Integer>>() {
@Override
public void accept(List<Integer> v) throws Exception {
System.out.println(v.size());
}
})
.sequential()
.subscribe(ts);
ts.awaitDone(5, TimeUnit.SECONDS);
ts.assertValueCount(3)
.assertNoErrors()
.assertComplete()
;
List<List<Integer>> list = ts.values();
Assert.assertEquals(100000, list.get(0).size() + list.get(1).size() + list.get(2).size());
} finally {
exec.shutdown();
}
}
@Test
public void collectAsync3() {
ExecutorService exec = Executors.newFixedThreadPool(3);
Scheduler s = Schedulers.from(exec);
try {
Callable<List<Integer>> as = new Callable<List<Integer>>() {
@Override
public List<Integer> call() throws Exception {
return new ArrayList<Integer>();
}
};
TestSubscriber<List<Integer>> ts = new TestSubscriber<List<Integer>>();
Flowable.range(1, 100000).hide()
.observeOn(s)
.parallel(3)
.runOn(s)
.collect(as, new BiConsumer<List<Integer>, Integer>() {
@Override
public void accept(List<Integer> a, Integer b) throws Exception {
a.add(b);
}
})
.doOnNext(new Consumer<List<Integer>>() {
@Override
public void accept(List<Integer> v) throws Exception {
System.out.println(v.size());
}
})
.sequential()
.subscribe(ts);
ts.awaitDone(5, TimeUnit.SECONDS);
ts.assertValueCount(3)
.assertNoErrors()
.assertComplete()
;
List<List<Integer>> list = ts.values();
Assert.assertEquals(100000, list.get(0).size() + list.get(1).size() + list.get(2).size());
} finally {
exec.shutdown();
}
}
@Test
public void collectAsync3Fused() {
ExecutorService exec = Executors.newFixedThreadPool(3);
Scheduler s = Schedulers.from(exec);
try {
Callable<List<Integer>> as = new Callable<List<Integer>>() {
@Override
public List<Integer> call() throws Exception {
return new ArrayList<Integer>();
}
};
TestSubscriber<List<Integer>> ts = new TestSubscriber<List<Integer>>();
Flowable.range(1, 100000)
.observeOn(s)
.parallel(3)
.runOn(s)
.collect(as, new BiConsumer<List<Integer>, Integer>() {
@Override
public void accept(List<Integer> a, Integer b) throws Exception {
a.add(b);
}
})
.doOnNext(new Consumer<List<Integer>>() {
@Override
public void accept(List<Integer> v) throws Exception {
System.out.println(v.size());
}
})
.sequential()
.subscribe(ts);
ts.awaitDone(5, TimeUnit.SECONDS);
ts.assertValueCount(3)
.assertNoErrors()
.assertComplete()
;
List<List<Integer>> list = ts.values();
Assert.assertEquals(100000, list.get(0).size() + list.get(1).size() + list.get(2).size());
} finally {
exec.shutdown();
}
}
@Test
public void collectAsync3Take() {
ExecutorService exec = Executors.newFixedThreadPool(4);
Scheduler s = Schedulers.from(exec);
try {
Callable<List<Integer>> as = new Callable<List<Integer>>() {
@Override
public List<Integer> call() throws Exception {
return new ArrayList<Integer>();
}
};
TestSubscriber<List<Integer>> ts = new TestSubscriber<List<Integer>>();
Flowable.range(1, 100000)
.take(1000)
.observeOn(s)
.parallel(3)
.runOn(s)
.collect(as, new BiConsumer<List<Integer>, Integer>() {
@Override
public void accept(List<Integer> a, Integer b) throws Exception {
a.add(b);
}
})
.doOnNext(new Consumer<List<Integer>>() {
@Override
public void accept(List<Integer> v) throws Exception {
System.out.println(v.size());
}
})
.sequential()
.subscribe(ts);
ts.awaitDone(5, TimeUnit.SECONDS);
ts.assertValueCount(3)
.assertNoErrors()
.assertComplete()
;
List<List<Integer>> list = ts.values();
Assert.assertEquals(1000, list.get(0).size() + list.get(1).size() + list.get(2).size());
} finally {
exec.shutdown();
}
}
@Test
public void collectAsync4Take() {
ExecutorService exec = Executors.newFixedThreadPool(3);
Scheduler s = Schedulers.from(exec);
try {
Callable<List<Integer>> as = new Callable<List<Integer>>() {
@Override
public List<Integer> call() throws Exception {
return new ArrayList<Integer>();
}
};
TestSubscriber<List<Integer>> ts = new TestSubscriber<List<Integer>>();
UnicastProcessor<Integer> up = UnicastProcessor.create();
for (int i = 0; i < 1000; i++) {
up.onNext(i);
}
up
.take(1000)
.observeOn(s)
.parallel(3)
.runOn(s)
.collect(as, new BiConsumer<List<Integer>, Integer>() {
@Override
public void accept(List<Integer> a, Integer b) throws Exception {
a.add(b);
}
})
.doOnNext(new Consumer<List<Integer>>() {
@Override
public void accept(List<Integer> v) throws Exception {
System.out.println(v.size());
}
})
.sequential()
.subscribe(ts);
ts.awaitDone(5, TimeUnit.SECONDS);
ts.assertValueCount(3)
.assertNoErrors()
.assertComplete()
;
List<List<Integer>> list = ts.values();
Assert.assertEquals(1000, list.get(0).size() + list.get(1).size() + list.get(2).size());
} finally {
exec.shutdown();
}
}
@Test
public void emptySourceZeroRequest() {
TestSubscriber<Object> ts = new TestSubscriber<Object>(0);
Flowable.range(1, 3).parallel(3).sequential().subscribe(ts);
ts.request(1);
ts.assertValue(1);
}
@Test
public void parallelismAndPrefetch() {
for (int parallelism = 1; parallelism <= 8; parallelism++) {
for (int prefetch = 1; prefetch <= 1024; prefetch *= 2) {
Flowable.range(1, 1024 * 1024)
.parallel(parallelism, prefetch)
.map(Functions.<Integer>identity())
.sequential()
.test()
.assertSubscribed()
.assertValueCount(1024 * 1024)
.assertNoErrors()
.assertComplete();
}
}
}
@Test
public void parallelismAndPrefetchAsync() {
for (int parallelism = 1; parallelism <= 8; parallelism *= 2) {
for (int prefetch = 1; prefetch <= 1024; prefetch *= 2) {
System.out.println("parallelismAndPrefetchAsync >> " + parallelism + ", " + prefetch);
Flowable.range(1, 1024 * 1024)
.parallel(parallelism, prefetch)
.runOn(Schedulers.computation())
.map(Functions.<Integer>identity())
.sequential(prefetch)
.test()
.withTag("parallelism = " + parallelism + ", prefetch = " + prefetch)
.awaitDone(30, TimeUnit.SECONDS)
.assertSubscribed()
.assertValueCount(1024 * 1024)
.assertNoErrors()
.assertComplete();
}
}
}
@SuppressWarnings("unchecked")
@Test
public void badParallelismStage() {
TestSubscriber<Integer> ts = new TestSubscriber<Integer>();
Flowable.range(1, 10)
.parallel(2)
.subscribe(new Subscriber[] { ts });
ts.assertFailure(IllegalArgumentException.class);
}
@SuppressWarnings("unchecked")
@Test
public void badParallelismStage2() {
TestSubscriber<Integer> ts1 = new TestSubscriber<Integer>();
TestSubscriber<Integer> ts2 = new TestSubscriber<Integer>();
TestSubscriber<Integer> ts3 = new TestSubscriber<Integer>();
Flowable.range(1, 10)
.parallel(2)
.subscribe(new Subscriber[] { ts1, ts2, ts3 });
ts1.assertFailure(IllegalArgumentException.class);
ts2.assertFailure(IllegalArgumentException.class);
ts3.assertFailure(IllegalArgumentException.class);
}
@Test
public void filter() {
Flowable.range(1, 20)
.parallel()
.runOn(Schedulers.computation())
.filter(new Predicate<Integer>() {
@Override
public boolean test(Integer v) throws Exception {
return v % 2 == 0;
}
})
.sequential()
.test()
.awaitDone(5, TimeUnit.SECONDS)
.assertValueSet(Arrays.asList(2, 4, 6, 8, 10, 12, 14, 16, 18, 20))
.assertNoErrors()
.assertComplete();
}
@Test
public void filterThrows() throws Exception {
final boolean[] cancelled = { false };
Flowable.range(1, 20).concatWith(Flowable.<Integer>never())
.doOnCancel(new Action() {
@Override
public void run() throws Exception {
cancelled[0] = true;
}
})
.parallel()
.runOn(Schedulers.computation())
.filter(new Predicate<Integer>() {
@Override
public boolean test(Integer v) throws Exception {
if (v == 10) {
throw new TestException();
}
return v % 2 == 0;
}
})
.sequential()
.test()
.awaitDone(5, TimeUnit.SECONDS)
.assertError(TestException.class)
.assertNotComplete();
Thread.sleep(100);
assertTrue(cancelled[0]);
}
@Test
public void doAfterNext() {
final int[] count = { 0 };
Flowable.range(1, 5)
.parallel()
.doAfterNext(new Consumer<Integer>() {
@Override
public void accept(Integer v) throws Exception {
count[0]++;
}
})
.sequential()
.test()
.assertResult(1, 2, 3, 4, 5);
}
@Test
public void doOnNextThrows() {
final int[] count = { 0 };
Flowable.range(1, 5)
.parallel()
.doOnNext(new Consumer<Integer>() {
@Override
public void accept(Integer v) throws Exception {
if (v == 3) {
throw new TestException();
} else {
count[0]++;
}
}
})
.sequential()
.test()
.assertError(TestException.class)
.assertNotComplete();
assertTrue("" + count[0], count[0] < 5);
}
@Test
public void doAfterNextThrows() {
final int[] count = { 0 };
Flowable.range(1, 5)
.parallel()
.doAfterNext(new Consumer<Integer>() {
@Override
public void accept(Integer v) throws Exception {
if (v == 3) {
throw new TestException();
} else {
count[0]++;
}
}
})
.sequential()
.test()
.assertError(TestException.class)
.assertNotComplete();
assertTrue("" + count[0], count[0] < 5);
}
@Test
public void errorNotRepeating() throws Exception {
List<Throwable> errors = TestHelper.trackPluginErrors();
try {
Flowable.error(new TestException())
.parallel()
.runOn(Schedulers.computation())
.sequential()
.test()
.awaitDone(5, TimeUnit.SECONDS)
.assertFailure(TestException.class)
;
Thread.sleep(300);
for (Throwable ex : errors) {
ex.printStackTrace();
}
assertTrue(errors.toString(), errors.isEmpty());
} finally {
RxJavaPlugins.reset();
}
}
@Test
public void doOnError() {
final int[] count = { 0 };
Flowable.range(1, 5)
.parallel(2)
.map(new Function<Integer, Integer>() {
@Override
public Integer apply(Integer v) throws Exception {
if (v == 3) {
throw new TestException();
}
return v;
}
})
.doOnError(new Consumer<Throwable>() {
@Override
public void accept(Throwable e) throws Exception {
if (e instanceof TestException) {
count[0]++;
}
}
})
.sequential()
.test()
.assertError(TestException.class)
.assertNotComplete();
assertEquals(1, count[0]);
}
@Test
public void doOnErrorThrows() {
TestSubscriber<Integer> ts = Flowable.range(1, 5)
.parallel(2)
.map(new Function<Integer, Integer>() {
@Override
public Integer apply(Integer v) throws Exception {
if (v == 3) {
throw new TestException();
}
return v;
}
})
.doOnError(new Consumer<Throwable>() {
@Override
public void accept(Throwable e) throws Exception {
if (e instanceof TestException) {
throw new IOException();
}
}
})
.sequential()
.test()
.assertError(CompositeException.class)
.assertNotComplete();
List<Throwable> errors = TestHelper.errorList(ts);
TestHelper.assertError(errors, 0, TestException.class);
TestHelper.assertError(errors, 1, IOException.class);
}
@Test
public void doOnComplete() {
final int[] count = { 0 };
Flowable.range(1, 5)
.parallel(2)
.doOnComplete(new Action() {
@Override
public void run() throws Exception {
count[0]++;
}
})
.sequential()
.test()
.assertResult(1, 2, 3, 4, 5);
assertEquals(2, count[0]);
}
@Test
public void doAfterTerminate() {
final int[] count = { 0 };
Flowable.range(1, 5)
.parallel(2)
.doAfterTerminated(new Action() {
@Override
public void run() throws Exception {
count[0]++;
}
})
.sequential()
.test()
.assertResult(1, 2, 3, 4, 5);
assertEquals(2, count[0]);
}
@Test
public void doOnSubscribe() {
final int[] count = { 0 };
Flowable.range(1, 5)
.parallel(2)
.doOnSubscribe(new Consumer<Subscription>() {
@Override
public void accept(Subscription s) throws Exception {
count[0]++;
}
})
.sequential()
.test()
.assertResult(1, 2, 3, 4, 5);
assertEquals(2, count[0]);
}
@Test
public void doOnRequest() {
final int[] count = { 0 };
Flowable.range(1, 5)
.parallel(2)
.doOnRequest(new LongConsumer() {
@Override
public void accept(long s) throws Exception {
count[0]++;
}
})
.sequential()
.test()
.assertResult(1, 2, 3, 4, 5);
assertEquals(2, count[0]);
}
@Test
public void doOnCancel() {
final int[] count = { 0 };
Flowable.range(1, 5)
.parallel(2)
.doOnCancel(new Action() {
@Override
public void run() throws Exception {
count[0]++;
}
})
.sequential()
.take(2)
.test()
.assertResult(1, 2);
assertEquals(2, count[0]);
}
@SuppressWarnings("unchecked")
@Test(expected = IllegalArgumentException.class)
public void fromPublishers() {
ParallelFlowable.fromArray(new Publisher[0]);
}
@Test
public void to() {
Flowable.range(1, 5)
.parallel()
.to(new Function<ParallelFlowable<Integer>, Flowable<Integer>>() {
@Override
public Flowable<Integer> apply(ParallelFlowable<Integer> pf) throws Exception {
return pf.sequential();
}
})
.test()
.assertResult(1, 2, 3, 4, 5);
}
@Test(expected = TestException.class)
public void toThrows() {
Flowable.range(1, 5)
.parallel()
.to(new Function<ParallelFlowable<Integer>, Flowable<Integer>>() {
@Override
public Flowable<Integer> apply(ParallelFlowable<Integer> pf) throws Exception {
throw new TestException();
}
});
}
@Test
public void compose() {
Flowable.range(1, 5)
.parallel()
.compose(new ParallelTransformer<Integer, Integer>() {
@Override
public ParallelFlowable<Integer> apply(ParallelFlowable<Integer> pf) {
return pf.map(new Function<Integer, Integer>() {
@Override
public Integer apply(Integer v) throws Exception {
return v + 1;
}
});
}
})
.sequential()
.test()
.assertResult(2, 3, 4, 5, 6);
}
@Test
public void flatMapDelayError() {
final int[] count = { 0 };
Flowable.range(1, 5)
.parallel(2)
.flatMap(new Function<Integer, Flowable<Integer>>() {
@Override
public Flowable<Integer> apply(Integer v) throws Exception {
if (v == 3) {
return Flowable.error(new TestException());
}
return Flowable.just(v);
}
}, true)
.doOnError(new Consumer<Throwable>() {
@Override
public void accept(Throwable e) throws Exception {
if (e instanceof TestException) {
count[0]++;
}
}
})
.sequential()
.test()
.assertValues(1, 2, 4, 5)
.assertError(TestException.class)
.assertNotComplete();
assertEquals(1, count[0]);
}
@Test
public void flatMapDelayErrorMaxConcurrency() {
final int[] count = { 0 };
Flowable.range(1, 5)
.parallel(2)
.flatMap(new Function<Integer, Flowable<Integer>>() {
@Override
public Flowable<Integer> apply(Integer v) throws Exception {
if (v == 3) {
return Flowable.error(new TestException());
}
return Flowable.just(v);
}
}, true, 1)
.doOnError(new Consumer<Throwable>() {
@Override
public void accept(Throwable e) throws Exception {
if (e instanceof TestException) {
count[0]++;
}
}
})
.sequential()
.test()
.assertValues(1, 2, 4, 5)
.assertError(TestException.class)
.assertNotComplete();
assertEquals(1, count[0]);
}
@Test
public void concatMapDelayError() {
final int[] count = { 0 };
Flowable.range(1, 5)
.parallel(2)
.concatMapDelayError(new Function<Integer, Flowable<Integer>>() {
@Override
public Flowable<Integer> apply(Integer v) throws Exception {
if (v == 3) {
return Flowable.error(new TestException());
}
return Flowable.just(v);
}
}, true)
.doOnError(new Consumer<Throwable>() {
@Override
public void accept(Throwable e) throws Exception {
if (e instanceof TestException) {
count[0]++;
}
}
})
.sequential()
.test()
.assertValues(1, 2, 4, 5)
.assertError(TestException.class)
.assertNotComplete();
assertEquals(1, count[0]);
}
@Test
public void concatMapDelayErrorPrefetch() {
final int[] count = { 0 };
Flowable.range(1, 5)
.parallel(2)
.concatMapDelayError(new Function<Integer, Flowable<Integer>>() {
@Override
public Flowable<Integer> apply(Integer v) throws Exception {
if (v == 3) {
return Flowable.error(new TestException());
}
return Flowable.just(v);
}
}, 1, true)
.doOnError(new Consumer<Throwable>() {
@Override
public void accept(Throwable e) throws Exception {
if (e instanceof TestException) {
count[0]++;
}
}
})
.sequential()
.test()
.assertValues(1, 2, 4, 5)
.assertError(TestException.class)
.assertNotComplete();
assertEquals(1, count[0]);
}
@Test
public void concatMapDelayErrorBoundary() {
final int[] count = { 0 };
Flowable.range(1, 5)
.parallel(2)
.concatMapDelayError(new Function<Integer, Flowable<Integer>>() {
@Override
public Flowable<Integer> apply(Integer v) throws Exception {
if (v == 3) {
return Flowable.error(new TestException());
}
return Flowable.just(v);
}
}, false)
.doOnError(new Consumer<Throwable>() {
@Override
public void accept(Throwable e) throws Exception {
if (e instanceof TestException) {
count[0]++;
}
}
})
.sequential()
.test()
.assertValues(1, 2)
.assertError(TestException.class)
.assertNotComplete();
assertEquals(1, count[0]);
}
public static void checkSubscriberCount(ParallelFlowable<?> source) {
int n = source.parallelism();
@SuppressWarnings("unchecked")
TestSubscriber<Object>[] consumers = new TestSubscriber[n + 1];
for (int i = 0; i <= n; i++) {
consumers[i] = new TestSubscriber<Object>();
}
source.subscribe(consumers);
for (int i = 0; i <= n; i++) {
consumers[i].awaitDone(5, TimeUnit.SECONDS)
.assertFailure(IllegalArgumentException.class);
}
}
@Test
public void checkAddBiConsumer() {
TestHelper.checkEnum(ListAddBiConsumer.class);
}
@Test
public void mergeBiFunction() throws Exception {
MergerBiFunction<Integer> f = new MergerBiFunction<Integer>(Functions.<Integer>naturalComparator());
assertEquals(0, f.apply(Collections.<Integer>emptyList(), Collections.<Integer>emptyList()).size());
assertEquals(Arrays.asList(1, 2), f.apply(Collections.<Integer>emptyList(), Arrays.asList(1, 2)));
for (int i = 0; i < 4; i++) {
int k = 0;
List<Integer> list1 = new ArrayList<Integer>();
for (int j = 0; j < i; j++) {
list1.add(k++);
}
List<Integer> list2 = new ArrayList<Integer>();
for (int j = i; j < 4; j++) {
list2.add(k++);
}
assertEquals(Arrays.asList(0, 1, 2, 3), f.apply(list1, list2));
}
}
@Test
public void concatMapSubscriberCount() {
ParallelFlowableTest.checkSubscriberCount(Flowable.range(1, 5).parallel()
.concatMap(Functions.justFunction(Flowable.just(1))));
}
@Test
public void flatMapSubscriberCount() {
ParallelFlowableTest.checkSubscriberCount(Flowable.range(1, 5).parallel()
.flatMap(Functions.justFunction(Flowable.just(1))));
}
@SuppressWarnings("unchecked")
@Test
public void fromArraySubscriberCount() {
ParallelFlowableTest.checkSubscriberCount(ParallelFlowable.fromArray(new Publisher[] { Flowable.just(1) }));
}
}