package com.jakewharton.rxbinding2;

import android.util.Log;
import java.util.NoSuchElementException;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;

import static com.google.common.truth.Truth.assertThat;

public final class RecordingObserver<T> implements Observer<T>, Disposable {
  private static final String TAG = "RecordingObserver";

  private final BlockingDeque<Object> events = new LinkedBlockingDeque<>();
  private Disposable disposable;

  @Override
  public void onSubscribe(Disposable disposable) {
    this.disposable = disposable;
  }

  @Override public void onComplete() {
    Log.v(TAG, "onCompleted");
    events.addLast(new OnCompleted());
  }

  @Override public void onError(Throwable e) {
    Log.v(TAG, "onError", e);
    events.addLast(new OnError(e));
  }

  @Override public void onNext(T t) {
    Log.v(TAG, "onNext " + t);
    events.addLast(new OnNext(t));
  }

  @Override
  public void dispose() {
    disposable.dispose();
  }

  @Override
  public boolean isDisposed() {
    return disposable.isDisposed();
  }

  private <E> E takeEvent(Class<E> wanted) {
    Object event;
    try {
      event = events.pollFirst(1, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }
    if (event == null) {
      throw new NoSuchElementException(
          "No event found while waiting for " + wanted.getSimpleName());
    }
    assertThat(event).isInstanceOf(wanted);
    return wanted.cast(event);
  }

  public T takeNext() {
    OnNext event = takeEvent(OnNext.class);
    return event.value;
  }

  public Throwable takeError() {
    return takeEvent(OnError.class).throwable;
  }

  public void assertOnCompleted() {
    takeEvent(OnCompleted.class);
  }

  public void assertNoMoreEvents() {
    try {
      Object event = takeEvent(Object.class);
      throw new IllegalStateException("Expected no more events but got " + event);
    } catch (NoSuchElementException ignored) {
    }
  }

  private final class OnNext {
    final T value;

    private OnNext(T value) {
      this.value = value;
    }

    @Override public String toString() {
      return "OnNext[" + value + "]";
    }
  }

  private final class OnCompleted {
    @Override public String toString() {
      return "OnCompleted";
    }
  }

  private final class OnError {
    private final Throwable throwable;

    private OnError(Throwable throwable) {
      this.throwable = throwable;
    }

    @Override public String toString() {
      return "OnError[" + throwable + "]";
    }
  }
}

@@ -250,48 +250,16 @@
 vex.
-O
+o
 bserver
-;%0Aimport io.reactivex.disposable
 s.Di
@@ -266,16 +266,24 @@
 sposable
+Observer
 ;%0A%0Aimpor
@@ -378,19 +378,26 @@
 %3CT%3E 
-implement
+extend
 s 
+Disposable
 Obse
@@ -407,20 +407,8 @@
 r%3CT%3E
-, Disposable
  %7B%0A 
@@ -543,143 +543,8 @@
 %3E();
-%0A  private Disposable disposable;%0A%0A  @Override%0A  public void onSubscribe(Disposable disposable) %7B%0A    this.disposable = disposable;%0A  %7D
 %0A%0A  
@@ -838,24 +838,24 @@
 ext %22 + t);%0A
+
     events.a
@@ -886,162 +886,8 @@
  %7D%0A%0A
-  @Override%0A  public void dispose() %7B%0A    disposable.dispose();%0A  %7D%0A%0A  @Override%0A  public boolean isDisposed() %7B%0A    return disposable.isDisposed();%0A  %7D%0A%0A
   pr

Use DisposableObserver in RecordingObserver