@Ignore("Non-positive requests are relayed to the plugin and is a no-op otherwise")
  @Test
  public void testNegativeRequestThrowsIllegalArgumentException() throws InterruptedException {
    final CountDownLatch latch = new CountDownLatch(1);
    final AtomicReference<Throwable> exception = new AtomicReference<>();
    Observable.just(1, 2, 3, 4)
        .subscribe(
            new Observer<Integer>() {

              @Override
              public void onStart() {
                request(1);
              }

              @Override
              public void onComplete() {}

              @Override
              public void onError(Throwable e) {
                exception.set(e);
                latch.countDown();
              }

              @Override
              public void onNext(Integer t) {
                request(-1);
                request(1);
              }
            });

    Assert.assertTrue(latch.await(10, TimeUnit.SECONDS));
    Assert.assertTrue(exception.get() instanceof IllegalArgumentException);
  }
  @Test
  public void testOnStartRequestsAreAdditiveAndOverflowBecomesMaxValue() {
    final List<Integer> list = new ArrayList<>();
    Observable.just(1, 2, 3, 4, 5)
        .subscribe(
            new Observer<Integer>() {
              @Override
              public void onStart() {
                request(2);
                request(Long.MAX_VALUE - 1);
              }

              @Override
              public void onComplete() {}

              @Override
              public void onError(Throwable e) {}

              @Override
              public void onNext(Integer t) {
                list.add(t);
              }
            });
    assertEquals(Arrays.asList(1, 2, 3, 4, 5), list);
  }
  @Test
  public void testOnStartCalledOnceViaUnsafeSubscribe() {
    final AtomicInteger c = new AtomicInteger();
    Observable.just(1, 2, 3, 4)
        .take(2)
        .unsafeSubscribe(
            new Observer<Integer>() {

              @Override
              public void onStart() {
                c.incrementAndGet();
                request(1);
              }

              @Override
              public void onComplete() {}

              @Override
              public void onError(Throwable e) {}

              @Override
              public void onNext(Integer t) {
                request(1);
              }
            });

    assertEquals(1, c.get());
  }
  @Test
  public void testRequestToObservable() {
    TestSubscriber<Integer> ts = new TestSubscriber<>();
    ts.request(3);
    final AtomicLong requested = new AtomicLong();
    Observable.<Integer>create(
            s ->
                s.onSubscribe(
                    new Subscription() {

                      @Override
                      public void request(long n) {
                        requested.set(n);
                      }

                      @Override
                      public void cancel() {}
                    }))
        .subscribe(ts);
    assertEquals(3, requested.get());
  }
  @Test
  public void testOnStartCalledOnceViaLift() {
    final AtomicInteger c = new AtomicInteger();
    Observable.just(1, 2, 3, 4)
        .lift(
            new Operator<Integer, Integer>() {

              @Override
              public Subscriber<? super Integer> apply(final Subscriber<? super Integer> child) {
                return new Observer<Integer>() {

                  @Override
                  public void onStart() {
                    c.incrementAndGet();
                    request(1);
                  }

                  @Override
                  public void onComplete() {
                    child.onComplete();
                  }

                  @Override
                  public void onError(Throwable e) {
                    child.onError(e);
                  }

                  @Override
                  public void onNext(Integer t) {
                    child.onNext(t);
                    request(1);
                  }
                };
              }
            })
        .subscribe();

    assertEquals(1, c.get());
  }
  @Test
  public void testRequestThroughTakeWhereRequestIsSmallerThanTake() {
    TestSubscriber<Integer> ts = new TestSubscriber<>((Long) null);
    ts.request(3);
    final AtomicLong requested = new AtomicLong();
    Observable.<Integer>create(
            s ->
                s.onSubscribe(
                    new Subscription() {

                      @Override
                      public void request(long n) {
                        requested.set(n);
                      }

                      @Override
                      public void cancel() {}
                    }))
        .take(10)
        .subscribe(ts);
    assertEquals(3, requested.get());
  }
  @Test
  public void testRequestThroughTakeThatReducesRequest() {
    TestSubscriber<Integer> ts = new TestSubscriber<>((Long) null);
    ts.request(3);
    final AtomicLong requested = new AtomicLong();
    Observable.<Integer>create(
            s ->
                s.onSubscribe(
                    new Subscription() {

                      @Override
                      public void request(long n) {
                        requested.set(n);
                      }

                      @Override
                      public void cancel() {}
                    }))
        .take(2)
        .subscribe(ts);

    // FIXME the take now requests Long.MAX_PATH if downstream requests at least the limit
    assertEquals(Long.MAX_VALUE, requested.get());
  }