@Test
  public void testNormal() {
    @SuppressWarnings("unchecked")
    Observer<Object> o = mock(Observer.class);

    final List<Integer> list = Arrays.asList(1, 2, 3);

    Func1<Integer, List<Integer>> func =
        new Func1<Integer, List<Integer>>() {
          @Override
          public List<Integer> call(Integer t1) {
            return list;
          }
        };
    Func2<Integer, Integer, Integer> resFunc =
        new Func2<Integer, Integer, Integer>() {

          @Override
          public Integer call(Integer t1, Integer t2) {
            return t1 | t2;
          }
        };

    List<Integer> source = Arrays.asList(16, 32, 64);

    Observable.from(source).flatMapIterable(func, resFunc).subscribe(o);

    for (Integer s : source) {
      for (Integer v : list) {
        verify(o).onNext(s | v);
      }
    }
    verify(o).onCompleted();
    verify(o, never()).onError(any(Throwable.class));
  }
  @Test
  public void testResultFunctionThrows() {
    @SuppressWarnings("unchecked")
    Observer<Object> o = mock(Observer.class);

    final List<Integer> list = Arrays.asList(1, 2, 3);

    Func1<Integer, List<Integer>> func =
        new Func1<Integer, List<Integer>>() {
          @Override
          public List<Integer> call(Integer t1) {
            return list;
          }
        };
    Func2<Integer, Integer, Integer> resFunc =
        new Func2<Integer, Integer, Integer>() {

          @Override
          public Integer call(Integer t1, Integer t2) {
            throw new TestException();
          }
        };

    List<Integer> source = Arrays.asList(16, 32, 64);

    Observable.from(source).flatMapIterable(func, resFunc).subscribe(o);

    verify(o, never()).onCompleted();
    verify(o, never()).onNext(any());
    verify(o).onError(any(TestException.class));
  }
  @Test
  public void testFlatMapTransformsException() {
    Observable<Integer> onNext = Observable.from(Arrays.asList(1, 2, 3));
    Observable<Integer> onCompleted = Observable.from(Arrays.asList(4));
    Observable<Integer> onError = Observable.from(Arrays.asList(5));

    Observable<Integer> source =
        Observable.concat(
            Observable.from(Arrays.asList(10, 20, 30)),
            Observable.<Integer>error(new RuntimeException("Forced failure!")));

    @SuppressWarnings("unchecked")
    Observer<Object> o = mock(Observer.class);

    source.flatMap(just(onNext), just(onError), just0(onCompleted)).subscribe(o);

    verify(o, times(3)).onNext(1);
    verify(o, times(3)).onNext(2);
    verify(o, times(3)).onNext(3);
    verify(o).onNext(5);
    verify(o).onCompleted();
    verify(o, never()).onNext(4);

    verify(o, never()).onError(any(Throwable.class));
  }
  @Test
  public void testFlatMapMaxConcurrent() {
    final int m = 4;
    final AtomicInteger subscriptionCount = new AtomicInteger();
    Observable<Integer> source =
        Observable.range(1, 10)
            .flatMap(
                new Func1<Integer, Observable<Integer>>() {
                  @Override
                  public Observable<Integer> call(Integer t1) {
                    return compose(Observable.range(t1 * 10, 2), subscriptionCount, m)
                        .subscribeOn(Schedulers.computation());
                  }
                },
                m);

    TestSubscriber<Integer> ts = new TestSubscriber<Integer>();

    source.subscribe(ts);

    ts.awaitTerminalEvent();
    ts.assertNoErrors();
    Set<Integer> expected =
        new HashSet<Integer>(
            Arrays.asList(
                10, 11, 20, 21, 30, 31, 40, 41, 50, 51, 60, 61, 70, 71, 80, 81, 90, 91, 100, 101));
    Assert.assertEquals(expected.size(), ts.getOnNextEvents().size());
    Assert.assertTrue(expected.containsAll(ts.getOnNextEvents()));
  }
  @Test
  public void testFlatMapTransformsOnNextFuncThrows() {
    Observable<Integer> onCompleted = Observable.from(Arrays.asList(4));
    Observable<Integer> onError = Observable.from(Arrays.asList(5));

    Observable<Integer> source = Observable.from(Arrays.asList(10, 20, 30));

    @SuppressWarnings("unchecked")
    Observer<Object> o = mock(Observer.class);

    source.flatMap(funcThrow(1, onError), just(onError), just0(onCompleted)).subscribe(o);

    verify(o).onError(any(TestException.class));
    verify(o, never()).onNext(any());
    verify(o, never()).onCompleted();
  }
  @Test
  public void testMergeError() {
    @SuppressWarnings("unchecked")
    Observer<Object> o = mock(Observer.class);

    Func1<Integer, Observable<Integer>> func =
        new Func1<Integer, Observable<Integer>>() {
          @Override
          public Observable<Integer> call(Integer t1) {
            return Observable.error(new TestException());
          }
        };
    Func2<Integer, Integer, Integer> resFunc =
        new Func2<Integer, Integer, Integer>() {

          @Override
          public Integer call(Integer t1, Integer t2) {
            return t1 | t2;
          }
        };

    List<Integer> source = Arrays.asList(16, 32, 64);

    Observable.from(source).flatMap(func, resFunc).subscribe(o);

    verify(o, never()).onCompleted();
    verify(o, never()).onNext(any());
    verify(o).onError(any(TestException.class));
  }
  @Test
  public void testFlatMapTransformsMaxConcurrentNormal() {
    final int m = 2;
    final AtomicInteger subscriptionCount = new AtomicInteger();
    Observable<Integer> onNext =
        compose(
                Observable.from(Arrays.asList(1, 2, 3)).observeOn(Schedulers.computation()),
                subscriptionCount,
                m)
            .subscribeOn(Schedulers.computation());
    Observable<Integer> onCompleted =
        compose(Observable.from(Arrays.asList(4)), subscriptionCount, m)
            .subscribeOn(Schedulers.computation());
    Observable<Integer> onError = Observable.from(Arrays.asList(5));

    Observable<Integer> source = Observable.from(Arrays.asList(10, 20, 30));

    @SuppressWarnings("unchecked")
    Observer<Object> o = mock(Observer.class);
    TestSubscriber<Object> ts = new TestSubscriber<Object>(o);

    source.flatMap(just(onNext), just(onError), just0(onCompleted), m).subscribe(ts);

    ts.awaitTerminalEvent(1, TimeUnit.SECONDS);
    ts.assertNoErrors();
    ts.assertTerminalEvent();

    verify(o, times(3)).onNext(1);
    verify(o, times(3)).onNext(2);
    verify(o, times(3)).onNext(3);
    verify(o).onNext(4);
    verify(o).onCompleted();

    verify(o, never()).onNext(5);
    verify(o, never()).onError(any(Throwable.class));
  }