From 0e96065c2f8b0414ade7e33240eec772d531cec2 Mon Sep 17 00:00:00 2001 From: Felipe Lima Date: Sat, 27 Aug 2016 20:49:41 -0700 Subject: [PATCH] Bump buildTools, sdkVersion and dependencies Test changes were required since RxJava 1.1.6 changed the way ReplaySubject in a way that broke reusing the same `TestSubscriber` more than once. (See comments here: https://github.com/ReactiveX/RxJava/pull/4225#issuecomment-242952697) Fixed tests by using a `TestObserver` that can be reused multiple times. --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 +- rxgroups-android/build.gradle | 10 +- rxgroups/build.gradle | 2 +- .../airbnb/rxgroups/ObservableGroupTest.java | 168 +++++------ .../rxgroups/SubscriptionProxyTest.java | 111 ++++--- .../com/airbnb/rxgroups/TestObserver.java | 280 ++++++++++++++++++ 7 files changed, 429 insertions(+), 148 deletions(-) create mode 100644 rxgroups/src/test/java/com/airbnb/rxgroups/TestObserver.java diff --git a/build.gradle b/build.gradle index c020406..23cf6e6 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { classpath 'com.bmuschko:gradle-nexus-plugin:2.3.1' - classpath 'com.android.tools.build:gradle:2.1.0' + classpath 'com.android.tools.build:gradle:2.1.3' classpath 'me.tatarka.retrolambda.projectlombok:lombok.ast:0.2.3.a2' } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ae6aaf3..b81f9e0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Sep 15 12:05:54 PDT 2015 +#Sat Aug 27 18:56:48 PDT 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/rxgroups-android/build.gradle b/rxgroups-android/build.gradle index 46c5a66..fddfc45 100644 --- a/rxgroups-android/build.gradle +++ b/rxgroups-android/build.gradle @@ -2,18 +2,18 @@ apply plugin: 'com.android.library' apply from: 'gradle-maven-push.gradle' android { - compileSdkVersion 23 - buildToolsVersion "23.0.2" + compileSdkVersion 24 + buildToolsVersion "24.0.2" defaultConfig { minSdkVersion 16 - targetSdkVersion 23 + targetSdkVersion 24 } } dependencies { compile project(':rxgroups') - compile 'io.reactivex:rxandroid:1.2.0' + compile 'io.reactivex:rxandroid:1.2.1' testCompile "junit:junit:4.12" testCompile 'org.mockito:mockito-core:1.10.19' @@ -21,7 +21,7 @@ dependencies { testCompile "org.hamcrest:hamcrest-core:1.3" testCompile "org.hamcrest:hamcrest-library:1.3" testCompile 'org.assertj:assertj-core:1.7.0' - testCompile("org.robolectric:robolectric:3.0") { + testCompile("org.robolectric:robolectric:3.1.2") { exclude module: 'classworlds' exclude module: 'commons-logging' exclude module: 'httpclient' diff --git a/rxgroups/build.gradle b/rxgroups/build.gradle index c32abf4..199bcba 100644 --- a/rxgroups/build.gradle +++ b/rxgroups/build.gradle @@ -15,7 +15,7 @@ project.group = GROUP project.version = VERSION_NAME dependencies { - compile 'io.reactivex:rxjava:1.1.5' + compile 'io.reactivex:rxjava:1.1.9' compile 'com.google.code.findbugs:jsr305:3.0.0' testCompile 'junit:junit:4.12' diff --git a/rxgroups/src/test/java/com/airbnb/rxgroups/ObservableGroupTest.java b/rxgroups/src/test/java/com/airbnb/rxgroups/ObservableGroupTest.java index 870e56b..aa6bcec 100644 --- a/rxgroups/src/test/java/com/airbnb/rxgroups/ObservableGroupTest.java +++ b/rxgroups/src/test/java/com/airbnb/rxgroups/ObservableGroupTest.java @@ -19,17 +19,17 @@ import org.junit.Test; import java.io.IOException; +import java.util.Collections; import rx.Observable; import rx.Observer; -import rx.observers.TestSubscriber; import rx.subjects.PublishSubject; import static junit.framework.TestCase.fail; import static org.assertj.core.api.Assertions.assertThat; public class ObservableGroupTest { - ObservableManager observableManager = new ObservableManager(); + private final ObservableManager observableManager = new ObservableManager(); @Before public void setUp() throws IOException { System.setProperty("rxjava.plugin.RxJavaSchedulersHook.implementation", @@ -46,7 +46,7 @@ public class ObservableGroupTest { ObservableGroup group2 = observableManager.newGroup(); Observable observable = Observable.never(); - group.add("foo", observable, new TestSubscriber<>()); + group.add("foo", observable, new TestObserver<>()); assertThat(group.hasObservable("foo")).isEqualTo(true); assertThat(group2.hasObservable("foo")).isEqualTo(false); @@ -55,21 +55,21 @@ public class ObservableGroupTest { @Test public void shouldNotBeCompleted() { ObservableGroup group = observableManager.newGroup(); - TestSubscriber subscriber = new TestSubscriber<>(); + TestObserver subscriber = new TestObserver<>(); group.add("foo", Observable.never(), subscriber); subscriber.assertNotCompleted(); } @Test public void shouldBeSubscribed() { ObservableGroup group = observableManager.newGroup(); - group.add("foo", Observable.never(), new TestSubscriber<>()); + group.add("foo", Observable.never(), new TestObserver<>()); assertThat(group.subscription("foo").isCancelled()).isEqualTo(false); } @Test public void shouldDeliverSuccessfulEvent() throws Exception { ObservableGroup group = observableManager.newGroup(); PublishSubject subject = PublishSubject.create(); - TestSubscriber subscriber = new TestSubscriber<>(); + TestObserver subscriber = new TestObserver<>(); group.add("foo", subject, subscriber); subscriber.assertNotCompleted(); @@ -83,11 +83,11 @@ public class ObservableGroupTest { @Test public void shouldDeliverError() throws Exception { ObservableGroup group = observableManager.newGroup(); - TestSubscriber testSubscriber = new TestSubscriber<>(); + TestObserver testObserver = new TestObserver<>(); Observable observable = Observable.error(new RuntimeException("boom")); - group.add("foo", observable, testSubscriber); + group.add("foo", observable, testObserver); - testSubscriber.assertError(RuntimeException.class); + testObserver.assertError(RuntimeException.class); } @Test public void shouldSeparateObservablesByGroupId() { @@ -95,8 +95,8 @@ public class ObservableGroupTest { ObservableGroup group2 = observableManager.newGroup(); Observable observable1 = Observable.never(); Observable observable2 = Observable.never(); - TestSubscriber subscriber1 = new TestSubscriber<>(); - TestSubscriber subscriber2 = new TestSubscriber<>(); + TestObserver subscriber1 = new TestObserver<>(); + TestObserver subscriber2 = new TestObserver<>(); group.add("tag", observable1, subscriber1); assertThat(group.hasObservable("tag")).isEqualTo(true); @@ -116,7 +116,7 @@ public class ObservableGroupTest { ObservableGroup group2 = observableManager.newGroup(); Observable observable1 = Observable.never(); Observable observable2 = Observable.never(); - TestSubscriber subscriber1 = new TestSubscriber<>(); + TestObserver subscriber1 = new TestObserver<>(); group.add("foo", observable1, subscriber1); group2.add("foo", observable2, subscriber1); @@ -139,8 +139,8 @@ public class ObservableGroupTest { ObservableGroup group = observableManager.newGroup(); Observable observable1 = Observable.never(); Observable observable2 = Observable.never(); - TestSubscriber subscriber1 = new TestSubscriber<>(); - TestSubscriber subscriber2 = new TestSubscriber<>(); + TestObserver subscriber1 = new TestObserver<>(); + TestObserver subscriber2 = new TestObserver<>(); group.add("foo", observable1, subscriber1); group.add("bar", observable2, subscriber2); @@ -155,7 +155,7 @@ public class ObservableGroupTest { @Test public void shouldClearQueuedResults() throws Exception { ObservableGroup group = observableManager.newGroup(); PublishSubject subject = PublishSubject.create(); - TestSubscriber subscriber1 = new TestSubscriber<>(); + TestObserver subscriber1 = new TestObserver<>(); group.add("foo", subject, subscriber1); group.unsubscribe(); @@ -169,7 +169,7 @@ public class ObservableGroupTest { @Test public void shouldRemoveObservablesAfterTermination() throws Exception { ObservableGroup group = observableManager.newGroup(); PublishSubject subject = PublishSubject.create(); - TestSubscriber subscriber = new TestSubscriber<>(); + TestObserver subscriber = new TestObserver<>(); group.add("foo", subject, subscriber); subject.onNext("Roberto Gomez Bolanos is king"); @@ -181,93 +181,95 @@ public class ObservableGroupTest { @Test public void shouldRemoveResponseAfterErrorDelivery() throws InterruptedException { ObservableGroup group = observableManager.newGroup(); - TestSubscriber testSubscriber = new TestSubscriber<>(); + TestObserver testObserver = new TestObserver<>(); PublishSubject subject = PublishSubject.create(); - group.add("foo", subject, testSubscriber); + group.add("foo", subject, testObserver); subject.onError(new RuntimeException("BOOM!")); - testSubscriber.assertError(Exception.class); + testObserver.assertError(Exception.class); assertThat(group.hasObservable("foo")).isEqualTo(false); } @Test public void shouldNotDeliverResultWhileUnsubscribed() throws Exception { ObservableGroup group = observableManager.newGroup(); - TestSubscriber testSubscriber = new TestSubscriber<>(); + TestObserver testObserver = new TestObserver<>(); PublishSubject subject = PublishSubject.create(); - group.add("foo", subject, testSubscriber); + group.add("foo", subject, testObserver); group.unsubscribe(); subject.onNext("Roberto Gomez Bolanos"); subject.onCompleted(); - testSubscriber.assertNotCompleted(); + testObserver.assertNotCompleted(); assertThat(group.hasObservable("foo")).isEqualTo(true); } @Test public void shouldDeliverQueuedEventsWhenResubscribed() throws Exception { ObservableGroup group = observableManager.newGroup(); - TestSubscriber testSubscriber = new TestSubscriber<>(); + TestObserver testObserver = new TestObserver<>(); PublishSubject subject = PublishSubject.create(); - group.add("foo", subject, testSubscriber); + group.add("foo", subject, testObserver); group.unsubscribe(); subject.onNext("Hello World"); subject.onCompleted(); - testSubscriber.assertNotCompleted(); - testSubscriber.assertNoValues(); + assertThat(testObserver.getOnCompletedEvents()).isEmpty(); + assertThat(testObserver.getOnNextEvents()).isEmpty(); - group.observable("foo").subscribe(testSubscriber); + group.observable("foo").subscribe(testObserver); - testSubscriber.assertCompleted(); - testSubscriber.assertNoErrors(); - testSubscriber.assertValue("Hello World"); + testObserver.assertCompleted(); + assertThat(testObserver.getOnNextEvents()) + .isEqualTo(Collections.singletonList("Hello World")); assertThat(group.hasObservable("foo")).isEqualTo(false); } @Test public void shouldDeliverQueuedErrorWhenResubscribed() throws Exception { ObservableGroup group = observableManager.newGroup(); - TestSubscriber testSubscriber = new TestSubscriber<>(); + TestObserver testObserver = new TestObserver<>(); PublishSubject subject = PublishSubject.create(); - group.add("foo", subject, testSubscriber); + group.add("foo", subject, testObserver); group.unsubscribe(); subject.onError(new Exception("Exploded")); - testSubscriber.assertNotCompleted(); - testSubscriber.assertNoValues(); + testObserver.assertNotCompleted(); + testObserver.assertNoValues(); - group.observable("foo").subscribe(testSubscriber); + testObserver = new TestObserver<>(); + group.observable("foo").subscribe(testObserver); - testSubscriber.assertError(Exception.class); + testObserver.assertError(Exception.class); assertThat(group.hasObservable("foo")).isEqualTo(false); } @Test public void shouldNotDeliverEventsWhenResubscribedIfLocked() { ObservableGroup group = observableManager.newGroup(); - TestSubscriber testSubscriber = new TestSubscriber<>(); + TestObserver testObserver = new TestObserver<>(); PublishSubject subject = PublishSubject.create(); - group.add("foo", subject, testSubscriber); + group.add("foo", subject, testObserver); group.unsubscribe(); subject.onNext("Hello World"); subject.onCompleted(); group.lock(); - group.observable("foo").subscribe(testSubscriber); + testObserver = new TestObserver<>(); + group.observable("foo").subscribe(testObserver); - testSubscriber.assertNotCompleted(); - testSubscriber.assertNoValues(); + testObserver.assertNotCompleted(); + testObserver.assertNoValues(); group.unlock(); - testSubscriber.assertCompleted(); - testSubscriber.assertNoErrors(); - testSubscriber.assertValue("Hello World"); + testObserver.assertCompleted(); + testObserver.assertNoErrors(); + testObserver.assertValue("Hello World"); assertThat(group.hasObservable("foo")).isEqualTo(false); } @@ -275,17 +277,17 @@ public class ObservableGroupTest { ObservableGroup group = observableManager.newGroup(); ObservableGroup group2 = observableManager.newGroup(); PublishSubject subject = PublishSubject.create(); - TestSubscriber testSubscriber = new TestSubscriber<>(); + TestObserver testObserver = new TestObserver<>(); - group2.add("foo", subject, testSubscriber); + group2.add("foo", subject, testObserver); group.unsubscribe(); subject.onNext("Gremio Foot-ball Porto Alegrense"); subject.onCompleted(); - testSubscriber.assertCompleted(); - testSubscriber.assertNoErrors(); - testSubscriber.assertValue("Gremio Foot-ball Porto Alegrense"); + testObserver.assertCompleted(); + testObserver.assertNoErrors(); + testObserver.assertValue("Gremio Foot-ball Porto Alegrense"); assertThat(group2.hasObservable("foo")).isEqualTo(false); } @@ -293,15 +295,15 @@ public class ObservableGroupTest { @Test public void shouldNotDeliverEventsAfterCancelled() throws Exception { ObservableGroup group = observableManager.newGroup(); PublishSubject subject = PublishSubject.create(); - TestSubscriber testSubscriber = new TestSubscriber<>(); + TestObserver testObserver = new TestObserver<>(); - group.add("foo", subject, testSubscriber); + group.add("foo", subject, testObserver); observableManager.destroy(group); subject.onNext("Gremio Foot-ball Porto Alegrense"); subject.onCompleted(); - testSubscriber.assertNotCompleted(); + testObserver.assertNotCompleted(); assertThat(group.hasObservable("foo")).isEqualTo(false); } @@ -309,9 +311,9 @@ public class ObservableGroupTest { ObservableGroup group = observableManager.newGroup(); ObservableGroup group2 = observableManager.newGroup(); PublishSubject subject1 = PublishSubject.create(); - TestSubscriber testSubscriber1 = new TestSubscriber<>(); + TestObserver testSubscriber1 = new TestObserver<>(); PublishSubject subject2 = PublishSubject.create(); - TestSubscriber testSubscriber2 = new TestSubscriber<>(); + TestObserver testSubscriber2 = new TestObserver<>(); group.add("foo", subject1, testSubscriber1); group2.add("bar", subject2, testSubscriber2); @@ -330,8 +332,8 @@ public class ObservableGroupTest { @Test public void shouldOverrideExistingSubscriber() throws Exception { ObservableGroup group = observableManager.newGroup(); PublishSubject subject = PublishSubject.create(); - TestSubscriber testSubscriber1 = new TestSubscriber<>(); - TestSubscriber testSubscriber2 = new TestSubscriber<>(); + TestObserver testSubscriber1 = new TestObserver<>(); + TestObserver testSubscriber2 = new TestObserver<>(); group.add("tag", subject, testSubscriber1); group.observable("tag").subscribe(testSubscriber2); @@ -348,9 +350,9 @@ public class ObservableGroupTest { @Test public void shouldQueueMultipleRequests() throws Exception { ObservableGroup group = observableManager.newGroup(); PublishSubject subject1 = PublishSubject.create(); - TestSubscriber testSubscriber1 = new TestSubscriber<>(); + TestObserver testSubscriber1 = new TestObserver<>(); PublishSubject subject2 = PublishSubject.create(); - TestSubscriber testSubscriber2 = new TestSubscriber<>(); + TestObserver testSubscriber2 = new TestObserver<>(); group.add("foo", subject1, testSubscriber1); group.add("bar", subject2, testSubscriber2); @@ -369,45 +371,45 @@ public class ObservableGroupTest { @Test public void shouldNotDeliverResultWhileLocked() throws Exception { ObservableGroup group = observableManager.newGroup(); - TestSubscriber testSubscriber = new TestSubscriber<>(); + TestObserver testObserver = new TestObserver<>(); PublishSubject subject = PublishSubject.create(); group.lock(); - group.add("tag", subject, testSubscriber); + group.add("tag", subject, testObserver); subject.onNext("Chespirito"); subject.onCompleted(); - testSubscriber.assertNotCompleted(); - testSubscriber.assertNoValues(); + testObserver.assertNotCompleted(); + testObserver.assertNoValues(); assertThat(group.hasObservable("tag")).isEqualTo(true); } @Test public void shouldAutoResubscribeAfterUnlock() throws InterruptedException { ObservableGroup group = observableManager.newGroup(); - TestSubscriber testSubscriber = new TestSubscriber<>(); + TestObserver testObserver = new TestObserver<>(); PublishSubject subject = PublishSubject.create(); group.lock(); - group.add("tag", subject, testSubscriber); + group.add("tag", subject, testObserver); subject.onNext("Chespirito"); subject.onCompleted(); group.unlock(); - testSubscriber.assertCompleted(); - testSubscriber.assertNoErrors(); - testSubscriber.assertValue("Chespirito"); + testObserver.assertCompleted(); + testObserver.assertNoErrors(); + testObserver.assertValue("Chespirito"); assertThat(group.hasObservable("tag")).isEqualTo(false); } @Test public void shouldAutoResubscribeAfterLockAndUnlock() { ObservableGroup group = observableManager.newGroup(); - TestSubscriber testSubscriber = new TestSubscriber<>(); + TestObserver testObserver = new TestObserver<>(); PublishSubject subject = PublishSubject.create(); - group.add("tag", subject, testSubscriber); + group.add("tag", subject, testObserver); group.lock(); subject.onNext("Chespirito"); @@ -415,18 +417,18 @@ public class ObservableGroupTest { group.unlock(); - testSubscriber.assertCompleted(); - testSubscriber.assertNoErrors(); - testSubscriber.assertValue("Chespirito"); + testObserver.assertTerminalEvent(); + testObserver.assertNoErrors(); + testObserver.assertReceivedOnNext(Collections.singletonList("Chespirito")); assertThat(group.hasObservable("tag")).isEqualTo(false); } @Test public void testUnsubscribeWhenLocked() { ObservableGroup group = observableManager.newGroup(); - TestSubscriber testSubscriber = new TestSubscriber<>(); + TestObserver testObserver = new TestObserver<>(); PublishSubject subject = PublishSubject.create(); - group.add("tag", subject, testSubscriber); + group.add("tag", subject, testObserver); group.lock(); group.unsubscribe(); @@ -435,8 +437,8 @@ public class ObservableGroupTest { group.unlock(); - testSubscriber.assertNotCompleted(); - testSubscriber.assertNoValues(); + testObserver.assertNotCompleted(); + testObserver.assertNoValues(); assertThat(group.hasObservable("tag")).isEqualTo(true); } @@ -444,7 +446,7 @@ public class ObservableGroupTest { ObservableGroup group = observableManager.newGroup(); group.destroy(); try { - group.add("tag", PublishSubject.create(), new TestSubscriber<>()); + group.add("tag", PublishSubject.create(), new TestObserver<>()); fail(); } catch (IllegalStateException ignored) { } @@ -453,10 +455,10 @@ public class ObservableGroupTest { @Test public void testResubscribeThrowsAfterDestroyed() { ObservableGroup group = observableManager.newGroup(); try { - group.add("tag", PublishSubject.create(), new TestSubscriber<>()); + group.add("tag", PublishSubject.create(), new TestObserver<>()); group.unsubscribe(); group.destroy(); - group.observable("tag").subscribe(new TestSubscriber<>()); + group.observable("tag").subscribe(new TestObserver<>()); fail(); } catch (IllegalStateException ignored) { } @@ -466,8 +468,8 @@ public class ObservableGroupTest { ObservableGroup group = observableManager.newGroup(); Observable observable1 = Observable.never(); PublishSubject observable2 = PublishSubject.create(); - TestSubscriber observer1 = new TestSubscriber<>(); - TestSubscriber observer2 = new TestSubscriber<>(); + TestObserver observer1 = new TestObserver<>(); + TestObserver observer2 = new TestObserver<>(); group.add("foo", observable1, observer1); group.add("foo", observable2, observer2); @@ -483,12 +485,12 @@ public class ObservableGroupTest { @Test public void testCancelAndReAddSubscription() { ObservableGroup group = observableManager.newGroup(); - group.add("tag", PublishSubject.create(), new TestSubscriber<>()); + group.add("tag", PublishSubject.create(), new TestObserver<>()); group.cancelAndRemove("tag"); assertThat(group.subscription("tag")).isNull(); Observable observable = PublishSubject.create(); - Observer observer = new TestSubscriber<>(); + Observer observer = new TestObserver<>(); group.add("tag", observable, observer); diff --git a/rxgroups/src/test/java/com/airbnb/rxgroups/SubscriptionProxyTest.java b/rxgroups/src/test/java/com/airbnb/rxgroups/SubscriptionProxyTest.java index bb009f3..c9b3503 100644 --- a/rxgroups/src/test/java/com/airbnb/rxgroups/SubscriptionProxyTest.java +++ b/rxgroups/src/test/java/com/airbnb/rxgroups/SubscriptionProxyTest.java @@ -22,7 +22,6 @@ import rx.Observable; import rx.Subscriber; import rx.functions.Action0; -import rx.observers.TestSubscriber; import rx.subjects.PublishSubject; import rx.subjects.ReplaySubject; import rx.subscriptions.Subscriptions; @@ -33,9 +32,9 @@ public class SubscriptionProxyTest { @Test public void testInitialState() { Observable observable = Observable.create(new Observable.OnSubscribe() { @Override - public void call(Subscriber subscriber) { - subscriber.onNext(1234); - subscriber.onCompleted(); + public void call(Subscriber observer) { + observer.onNext(1234); + observer.onCompleted(); } }); SubscriptionProxy proxy = SubscriptionProxy.create(observable); @@ -44,11 +43,11 @@ public void call(Subscriber subscriber) { } @Test public void testSubscriptionState() { - TestSubscriber subscriber = new TestSubscriber<>(); + TestObserver observer = new TestObserver<>(); PublishSubject subject = PublishSubject.create(); SubscriptionProxy proxy = SubscriptionProxy.create(subject); - proxy.subscribe(subscriber); + proxy.subscribe(observer); assertThat(proxy.isUnsubscribed()).isEqualTo(false); assertThat(proxy.isCancelled()).isEqualTo(false); @@ -64,40 +63,40 @@ public void call(Subscriber subscriber) { } @Test public void testSubscribe() { - TestSubscriber subscriber = new TestSubscriber<>(); + TestObserver observer = new TestObserver<>(); Observable observable = Observable.create(new Observable.OnSubscribe() { @Override - public void call(Subscriber subscriber) { - subscriber.onNext(1234); - subscriber.onCompleted(); + public void call(Subscriber observer) { + observer.onNext(1234); + observer.onCompleted(); } }); SubscriptionProxy proxy = SubscriptionProxy.create(observable); - proxy.subscribe(subscriber); + proxy.subscribe(observer); - subscriber.assertCompleted(); - subscriber.assertValue(1234); + observer.assertCompleted(); + observer.assertValue(1234); } @Test public void testUnsubscribe() { - TestSubscriber subscriber = new TestSubscriber<>(); + TestObserver observer = new TestObserver<>(); PublishSubject subject = PublishSubject.create(); SubscriptionProxy proxy = SubscriptionProxy.create(subject); - proxy.subscribe(subscriber); + proxy.subscribe(observer); proxy.unsubscribe(); subject.onNext("Avanti!"); subject.onCompleted(); assertThat(proxy.isUnsubscribed()).isEqualTo(true); - subscriber.awaitTerminalEvent(10, TimeUnit.MILLISECONDS); - subscriber.assertNotCompleted(); - subscriber.assertNoValues(); + observer.awaitTerminalEvent(10, TimeUnit.MILLISECONDS); + observer.assertNotCompleted(); + observer.assertNoValues(); } - static class TestOnUnsubscribe implements Action0 { + private static class TestOnUnsubscribe implements Action0 { boolean called = false; @Override public void call() { @@ -106,16 +105,16 @@ static class TestOnUnsubscribe implements Action0 { } @Test public void testCancelShouldUnsubscribeFromSourceObservable() { - TestSubscriber subscriber = new TestSubscriber<>(); + TestObserver observer = new TestObserver<>(); final TestOnUnsubscribe onUnsubscribe = new TestOnUnsubscribe(); Observable observable = Observable.create(new Observable.OnSubscribe() { - @Override public void call(final Subscriber subscriber) { - subscriber.add(Subscriptions.create(onUnsubscribe)); + @Override public void call(final Subscriber observer) { + observer.add(Subscriptions.create(onUnsubscribe)); } }); SubscriptionProxy proxy = SubscriptionProxy.create(observable); - proxy.subscribe(subscriber); + proxy.subscribe(observer); proxy.cancel(); assertThat(proxy.isUnsubscribed()).isEqualTo(true); @@ -124,16 +123,16 @@ static class TestOnUnsubscribe implements Action0 { } @Test public void testUnsubscribeShouldNotUnsubscribeFromSourceObservable() { - TestSubscriber subscriber = new TestSubscriber<>(); + TestObserver observer = new TestObserver<>(); final TestOnUnsubscribe onUnsubscribe = new TestOnUnsubscribe(); Observable observable = Observable.create(new Observable.OnSubscribe() { - @Override public void call(final Subscriber subscriber) { - subscriber.add(Subscriptions.create(onUnsubscribe)); + @Override public void call(final Subscriber observer) { + observer.add(Subscriptions.create(onUnsubscribe)); } }).share(); SubscriptionProxy proxy = SubscriptionProxy.create(observable); - proxy.subscribe(subscriber); + proxy.subscribe(observer); proxy.unsubscribe(); assertThat(proxy.isUnsubscribed()).isEqualTo(true); @@ -142,41 +141,41 @@ static class TestOnUnsubscribe implements Action0 { } @Test public void testUnsubscribeBeforeEmit() { - TestSubscriber subscriber = new TestSubscriber<>(); + TestObserver observer = new TestObserver<>(); ReplaySubject subject = ReplaySubject.create(); SubscriptionProxy proxy = SubscriptionProxy.create(subject); - proxy.subscribe(subscriber); + proxy.subscribe(observer); proxy.unsubscribe(); - subscriber.assertNotCompleted(); - subscriber.assertNoValues(); + observer.assertNotCompleted(); + observer.assertNoValues(); subject.onNext("Avanti!"); subject.onCompleted(); - proxy.subscribe(subscriber); - subscriber.assertCompleted(); - subscriber.assertValue("Avanti!"); + proxy.subscribe(observer); + observer.assertCompleted(); + observer.assertValue("Avanti!"); } @Test public void shouldCacheResultsWhileUnsubscribedAndDeliverAfterResubscription() { - TestSubscriber subscriber = new TestSubscriber<>(); + TestObserver observer = new TestObserver<>(); ReplaySubject subject = ReplaySubject.create(); SubscriptionProxy proxy = SubscriptionProxy.create(subject); - proxy.subscribe(subscriber); + proxy.subscribe(observer); proxy.unsubscribe(); - subscriber.assertNoValues(); + observer.assertNoValues(); subject.onNext("Avanti!"); subject.onCompleted(); - proxy.subscribe(subscriber); + proxy.subscribe(observer); - subscriber.awaitTerminalEvent(3, TimeUnit.SECONDS); - subscriber.assertValue("Avanti!"); + observer.awaitTerminalEvent(3, TimeUnit.SECONDS); + observer.assertValue("Avanti!"); } @Test public void shouldRedeliverSameResultsToDifferentSubscriber() { @@ -184,59 +183,59 @@ static class TestOnUnsubscribe implements Action0 { // Observable to a new Observer, which is a member of the new activity instance. In this // case, we may want to redeliver any previous results (if the request is still being // managed by ObservableManager). - TestSubscriber subscriber = new TestSubscriber<>(); + TestObserver observer = new TestObserver<>(); ReplaySubject subject = ReplaySubject.create(); SubscriptionProxy proxy = SubscriptionProxy.create(subject); - proxy.subscribe(subscriber); + proxy.subscribe(observer); subject.onNext("Avanti!"); subject.onCompleted(); proxy.unsubscribe(); - TestSubscriber newSubscriber = new TestSubscriber<>(); + TestObserver newSubscriber = new TestObserver<>(); proxy.subscribe(newSubscriber); newSubscriber.awaitTerminalEvent(3, TimeUnit.SECONDS); newSubscriber.assertCompleted(); newSubscriber.assertValue("Avanti!"); - subscriber.assertCompleted(); - subscriber.assertValue("Avanti!"); + observer.assertCompleted(); + observer.assertValue("Avanti!"); } @Test public void multipleSubscribesForSameObserverShouldBeIgnored() { - TestSubscriber subscriber = new TestSubscriber<>(); + TestObserver observer = new TestObserver<>(); PublishSubject subject = PublishSubject.create(); SubscriptionProxy proxy = SubscriptionProxy.create(subject); - proxy.subscribe(subscriber); - proxy.subscribe(subscriber); + proxy.subscribe(observer); + proxy.subscribe(observer); proxy.unsubscribe(); subject.onNext("Avanti!"); subject.onCompleted(); assertThat(proxy.isUnsubscribed()).isEqualTo(true); - subscriber.awaitTerminalEvent(10, TimeUnit.MILLISECONDS); - subscriber.assertNotCompleted(); - subscriber.assertNoValues(); + observer.awaitTerminalEvent(10, TimeUnit.MILLISECONDS); + observer.assertNotCompleted(); + observer.assertNoValues(); } @Test public void shouldKeepDeliveringEventsAfterResubscribed() { - TestSubscriber subscriber = new TestSubscriber<>(); + TestObserver observer = new TestObserver<>(); ReplaySubject subject = ReplaySubject.create(); SubscriptionProxy proxy = SubscriptionProxy.create(subject); - proxy.subscribe(subscriber); + proxy.subscribe(observer); subject.onNext("Avanti 1"); proxy.unsubscribe(); - subscriber = new TestSubscriber<>(); - proxy.subscribe(subscriber); + observer = new TestObserver<>(); + proxy.subscribe(observer); subject.onNext("Avanti!"); - subscriber.assertValues("Avanti 1", "Avanti!"); + observer.assertValues("Avanti 1", "Avanti!"); } } diff --git a/rxgroups/src/test/java/com/airbnb/rxgroups/TestObserver.java b/rxgroups/src/test/java/com/airbnb/rxgroups/TestObserver.java new file mode 100644 index 0000000..dbea4a0 --- /dev/null +++ b/rxgroups/src/test/java/com/airbnb/rxgroups/TestObserver.java @@ -0,0 +1,280 @@ +package com.airbnb.rxgroups; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import rx.Notification; +import rx.Observer; +import rx.exceptions.CompositeException; + +class TestObserver implements Observer { + private final Observer delegate; + private final List onNextEvents = new ArrayList(); + private final List onErrorEvents = new ArrayList(); + private final List> onCompletedEvents = new ArrayList<>(); + private final CountDownLatch latch = new CountDownLatch(1); + + TestObserver(Observer delegate) { + this.delegate = delegate; + } + + @SuppressWarnings("unchecked") TestObserver() { + this.delegate = (Observer) INERT; + } + + @Override public void onCompleted() { + try { + onCompletedEvents.add(Notification.createOnCompleted()); + delegate.onCompleted(); + } finally { + latch.countDown(); + } + } + + /** + * Get the {@link Notification}s representing each time this observer was notified of sequence + * completion via {@link #onCompleted}, as a {@link List}. + * + * @return a list of Notifications representing calls to this observer's {@link #onCompleted} + * method + */ + List> getOnCompletedEvents() { + return Collections.unmodifiableList(onCompletedEvents); + } + + @Override public void onError(Throwable e) { + try { + onErrorEvents.add(e); + delegate.onError(e); + } finally { + latch.countDown(); + } + } + + /** + * Get the {@link Throwable}s this observer was notified of via {@link #onError} as a {@link + * List}. + * + * @return a list of Throwables passed to this observer's {@link #onError} method + */ + private List getOnErrorEvents() { + return Collections.unmodifiableList(onErrorEvents); + } + + @Override public void onNext(T t) { + onNextEvents.add(t); + delegate.onNext(t); + } + + /** + * Get the sequence of items observed by this observer, as an ordered {@link List}. + * + * @return a list of items observed by this observer, in the order in which they were observed + */ + List getOnNextEvents() { + return Collections.unmodifiableList(onNextEvents); + } + + /** + * Get a list containing all of the items and notifications received by this observer, where the + * items will be given as-is, any error notifications will be represented by their {@code + * Throwable}s, and any sequence-complete notifications will be represented by their {@code + * Notification} objects. + * + * @return a {@link List} containing one item for each item or notification received by this + * observer, in the order in which they were observed or received + */ + List getEvents() { + ArrayList events = new ArrayList<>(); + events.add(onNextEvents); + events.add(onErrorEvents); + events.add(onCompletedEvents); + return Collections.unmodifiableList(events); + } + + /** + * Assert that a particular sequence of items was received in order. + * + * @param items the sequence of items expected to have been observed + * @throws AssertionError if the sequence of items observed does not exactly match {@code items} + */ + void assertReceivedOnNext(List items) { + if (onNextEvents.size() != items.size()) { + assertionError("Number of items does not match. Provided: " + items.size() + " Actual: " + + onNextEvents.size() + + ".\n" + + "Provided values: " + items + + "\n" + + "Actual values: " + onNextEvents + + "\n"); + } + + for (int i = 0; i < items.size(); i++) { + T expected = items.get(i); + T actual = onNextEvents.get(i); + if (expected == null) { + // check for null equality + if (actual != null) { + assertionError("Value at index: " + i + " expected to be [null] but was: [" + actual + + "]\n"); + } + } else if (!expected.equals(actual)) { + assertionError("Value at index: " + i + + " expected to be [" + expected + "] (" + expected.getClass().getSimpleName() + + ") but was: [" + actual + "] (" + + (actual != null ? actual.getClass().getSimpleName() : "null") + ")\n"); + + } + } + + } + + /** + * Assert that a single terminal event occurred, either {@link #onCompleted} or {@link #onError}. + * + * @throws AssertionError if not exactly one terminal event notification was received + */ + void assertTerminalEvent() { + if (onErrorEvents.size() > 1) { + assertionError("Too many onError events: " + onErrorEvents.size()); + } + + if (onCompletedEvents.size() > 1) { + assertionError("Too many onCompleted events: " + onCompletedEvents.size()); + } + + if (onCompletedEvents.size() == 1 && onErrorEvents.size() == 1) { + assertionError("Received both an onError and onCompleted. Should be one or the other."); + } + + if (onCompletedEvents.isEmpty() && onErrorEvents.isEmpty()) { + assertionError("No terminal events received."); + } + } + + void assertCompleted() { + int s = onCompletedEvents.size(); + if (s == 0) { + assertionError("Not completed!"); + } else if (s > 1) { + assertionError("Completed multiple times: " + s); + } + } + + void assertNoErrors() { + List onErrorEvents = getOnErrorEvents(); + if (!onErrorEvents.isEmpty()) { + assertionError("Unexpected onError events"); + } + } + + void assertNotCompleted() { + int s = onCompletedEvents.size(); + if (s == 1) { + assertionError("Completed!"); + } else if (s > 1) { + assertionError("Completed multiple times: " + s); + } + } + + void assertValues(T... values) { + assertReceivedOnNext(Arrays.asList(values)); + } + + void assertValue(T value) { + assertReceivedOnNext(Collections.singletonList(value)); + } + + void assertNoValues() { + int s = getOnNextEvents().size(); + if (s != 0) { + assertionError("No onNext events expected yet some received: " + s); + } + } + + void awaitTerminalEvent(long timeout, TimeUnit unit) { + try { + latch.await(timeout, unit); + } catch (InterruptedException e) { + throw new IllegalStateException("Interrupted", e); + } + } + + void assertError(Class clazz) { + List err = getOnErrorEvents(); + if (err.isEmpty()) { + assertionError("No errors"); + } else if (err.size() > 1) { + AssertionError ae = new AssertionError("Multiple errors: " + err.size()); + ae.initCause(new CompositeException(err)); + throw ae; + } else if (!clazz.isInstance(err.get(0))) { + AssertionError ae = new AssertionError("Exceptions differ; expected: " + clazz + ", actual: " + + err.get(0)); + ae.initCause(err.get(0)); + throw ae; + } + } + + /** + * Combines an assertion error message with the current completion and error state of this + * TestSubscriber, giving more information when some assertXXX check fails. + * + * @param message the message to use for the error + */ + private void assertionError(String message) { + StringBuilder b = new StringBuilder(message.length() + 32); + + b.append(message) + .append(" ("); + + int c = onCompletedEvents.size(); + b.append(c) + .append(" completion"); + if (c != 1) { + b.append('s'); + } + b.append(')'); + + if (!onErrorEvents.isEmpty()) { + int size = onErrorEvents.size(); + b.append(" (+") + .append(size) + .append(" error"); + if (size != 1) { + b.append('s'); + } + b.append(')'); + } + + AssertionError ae = new AssertionError(b.toString()); + if (!onErrorEvents.isEmpty()) { + if (onErrorEvents.size() == 1) { + ae.initCause(onErrorEvents.get(0)); + } else { + ae.initCause(new CompositeException(onErrorEvents)); + } + } + throw ae; + } + + // do nothing ... including swallowing errors + private static final Observer INERT = new Observer() { + @Override public void onCompleted() { + // deliberately ignored + } + + @Override public void onError(Throwable e) { + // deliberately ignored + } + + @Override public void onNext(Object t) { + // deliberately ignored + } + + }; +}