/** * 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 com.trello.rxlifecycle; import android.app.Activity; import android.view.View; import com.trello.rxlifecycle.android.ActivityEvent; import com.trello.rxlifecycle.android.FragmentEvent; import com.trello.rxlifecycle.android.RxLifecycleAndroid; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import rx.Observable; import rx.Subscription; import rx.observers.TestSubscriber; import rx.subjects.BehaviorSubject; import rx.subjects.PublishSubject; import java.util.concurrent.CopyOnWriteArrayList; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE) public class RxLifecycleTest { private Observable<Object> observable; @Before public void setup() { // Simulate an actual lifecycle (hot Observable that does not end) observable = PublishSubject.create().asObservable(); } @Test public void testBindUntilFragmentEvent() { BehaviorSubject<com.trello.rxlifecycle.android.FragmentEvent> lifecycle = BehaviorSubject.create(); TestSubscriber<Object> testSubscriber = new TestSubscriber<>(); observable.compose( RxLifecycle.bindUntilEvent(lifecycle, FragmentEvent.STOP)) .subscribe(testSubscriber); lifecycle.onNext(FragmentEvent.ATTACH); assertFalse(testSubscriber.isUnsubscribed()); lifecycle.onNext(FragmentEvent.CREATE); assertFalse(testSubscriber.isUnsubscribed()); lifecycle.onNext(FragmentEvent.CREATE_VIEW); assertFalse(testSubscriber.isUnsubscribed()); lifecycle.onNext(FragmentEvent.START); assertFalse(testSubscriber.isUnsubscribed()); lifecycle.onNext(FragmentEvent.RESUME); assertFalse(testSubscriber.isUnsubscribed()); lifecycle.onNext(FragmentEvent.PAUSE); assertFalse(testSubscriber.isUnsubscribed()); lifecycle.onNext(FragmentEvent.STOP); testSubscriber.assertCompleted(); testSubscriber.assertUnsubscribed(); } @Test public void testBindUntilActivityEvent() { BehaviorSubject<ActivityEvent> lifecycle = BehaviorSubject.create(); TestSubscriber<Object> testSubscriber = new TestSubscriber<>(); observable.compose( RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.STOP)) .subscribe(testSubscriber); lifecycle.onNext(ActivityEvent.CREATE); assertFalse(testSubscriber.isUnsubscribed()); lifecycle.onNext(ActivityEvent.START); assertFalse(testSubscriber.isUnsubscribed()); lifecycle.onNext(ActivityEvent.RESUME); assertFalse(testSubscriber.isUnsubscribed()); lifecycle.onNext(ActivityEvent.PAUSE); assertFalse(testSubscriber.isUnsubscribed()); lifecycle.onNext(ActivityEvent.STOP); testSubscriber.assertCompleted(); testSubscriber.assertUnsubscribed(); } @Test public void testBindActivityLifecycle() { BehaviorSubject<ActivityEvent> lifecycle = BehaviorSubject.create(); lifecycle.onNext(ActivityEvent.CREATE); TestSubscriber<Object> createTestSub = new TestSubscriber<>(); observable.compose(RxLifecycleAndroid.bindActivity(lifecycle)).subscribe(createTestSub); lifecycle.onNext(ActivityEvent.START); assertFalse(createTestSub.isUnsubscribed()); TestSubscriber<Object> startTestSub = new TestSubscriber<>(); observable.compose(RxLifecycleAndroid.bindActivity(lifecycle)).subscribe(startTestSub); lifecycle.onNext(ActivityEvent.RESUME); assertFalse(createTestSub.isUnsubscribed()); assertFalse(startTestSub.isUnsubscribed()); TestSubscriber<Object> resumeTestSub = new TestSubscriber<>(); observable.compose(RxLifecycleAndroid.bindActivity(lifecycle)).subscribe(resumeTestSub); lifecycle.onNext(ActivityEvent.PAUSE); assertFalse(createTestSub.isUnsubscribed()); assertFalse(startTestSub.isUnsubscribed()); resumeTestSub.assertCompleted(); resumeTestSub.assertUnsubscribed(); TestSubscriber<Object> pauseTestSub = new TestSubscriber<>(); observable.compose(RxLifecycleAndroid.bindActivity(lifecycle)).subscribe(pauseTestSub); lifecycle.onNext(ActivityEvent.STOP); assertFalse(createTestSub.isUnsubscribed()); startTestSub.assertCompleted(); startTestSub.assertUnsubscribed(); pauseTestSub.assertCompleted(); pauseTestSub.assertUnsubscribed(); TestSubscriber<Object> stopTestSub = new TestSubscriber<>(); observable.compose(RxLifecycleAndroid.bindActivity(lifecycle)).subscribe(stopTestSub); lifecycle.onNext(ActivityEvent.DESTROY); createTestSub.assertCompleted(); createTestSub.assertUnsubscribed(); stopTestSub.assertCompleted(); stopTestSub.assertUnsubscribed(); } @Test public void testEndsImmediatelyOutsideActivityLifecycle() { BehaviorSubject<ActivityEvent> lifecycle = BehaviorSubject.create(); lifecycle.onNext(ActivityEvent.DESTROY); TestSubscriber<Object> testSubscriber = new TestSubscriber<>(); observable.compose(RxLifecycleAndroid.bindActivity(lifecycle)).subscribe(testSubscriber); testSubscriber.assertCompleted(); testSubscriber.assertUnsubscribed(); } @Test public void testBindFragmentLifecycle() { BehaviorSubject<FragmentEvent> lifecycle = BehaviorSubject.create(); lifecycle.onNext(FragmentEvent.ATTACH); TestSubscriber<Object> attachTestSub = new TestSubscriber<>(); observable.compose(RxLifecycleAndroid.bindFragment(lifecycle)).subscribe(attachTestSub); lifecycle.onNext(FragmentEvent.CREATE); assertFalse(attachTestSub.isUnsubscribed()); TestSubscriber<Object> createTestSub = new TestSubscriber<>(); observable.compose(RxLifecycleAndroid.bindFragment(lifecycle)).subscribe(createTestSub); lifecycle.onNext(FragmentEvent.CREATE_VIEW); assertFalse(attachTestSub.isUnsubscribed()); assertFalse(createTestSub.isUnsubscribed()); TestSubscriber<Object> createViewTestSub = new TestSubscriber<>(); observable.compose(RxLifecycleAndroid.bindFragment(lifecycle)).subscribe(createViewTestSub); lifecycle.onNext(FragmentEvent.START); assertFalse(attachTestSub.isUnsubscribed()); assertFalse(createTestSub.isUnsubscribed()); assertFalse(createViewTestSub.isUnsubscribed()); TestSubscriber<Object> startTestSub = new TestSubscriber<>(); observable.compose(RxLifecycleAndroid.bindFragment(lifecycle)).subscribe(startTestSub); lifecycle.onNext(FragmentEvent.RESUME); assertFalse(attachTestSub.isUnsubscribed()); assertFalse(createTestSub.isUnsubscribed()); assertFalse(createViewTestSub.isUnsubscribed()); assertFalse(startTestSub.isUnsubscribed()); TestSubscriber<Object> resumeTestSub = new TestSubscriber<>(); observable.compose(RxLifecycleAndroid.bindFragment(lifecycle)).subscribe(resumeTestSub); lifecycle.onNext(FragmentEvent.PAUSE); assertFalse(attachTestSub.isUnsubscribed()); assertFalse(createTestSub.isUnsubscribed()); assertFalse(createViewTestSub.isUnsubscribed()); assertFalse(startTestSub.isUnsubscribed()); resumeTestSub.assertCompleted(); resumeTestSub.assertUnsubscribed(); TestSubscriber<Object> pauseTestSub = new TestSubscriber<>(); observable.compose(RxLifecycleAndroid.bindFragment(lifecycle)).subscribe(pauseTestSub); lifecycle.onNext(FragmentEvent.STOP); assertFalse(attachTestSub.isUnsubscribed()); assertFalse(createTestSub.isUnsubscribed()); assertFalse(createViewTestSub.isUnsubscribed()); startTestSub.assertCompleted(); startTestSub.assertUnsubscribed(); pauseTestSub.assertCompleted(); pauseTestSub.assertUnsubscribed(); TestSubscriber<Object> stopTestSub = new TestSubscriber<>(); observable.compose(RxLifecycleAndroid.bindFragment(lifecycle)).subscribe(stopTestSub); lifecycle.onNext(FragmentEvent.DESTROY_VIEW); assertFalse(attachTestSub.isUnsubscribed()); assertFalse(createTestSub.isUnsubscribed()); createViewTestSub.assertCompleted(); createViewTestSub.assertUnsubscribed(); stopTestSub.assertCompleted(); stopTestSub.assertUnsubscribed(); TestSubscriber<Object> desroyViewTestSub = new TestSubscriber<>(); observable.compose(RxLifecycleAndroid.bindFragment(lifecycle)).subscribe(desroyViewTestSub); lifecycle.onNext(FragmentEvent.DESTROY); assertFalse(attachTestSub.isUnsubscribed()); createTestSub.assertCompleted(); createTestSub.assertUnsubscribed(); desroyViewTestSub.assertCompleted(); desroyViewTestSub.assertUnsubscribed(); TestSubscriber<Object> destroyTestSub = new TestSubscriber<>(); observable.compose(RxLifecycleAndroid.bindFragment(lifecycle)).subscribe(destroyTestSub); lifecycle.onNext(FragmentEvent.DETACH); attachTestSub.assertCompleted(); attachTestSub.assertUnsubscribed(); destroyTestSub.assertCompleted(); destroyTestSub.assertUnsubscribed(); } @Test public void testEndsImmediatelyOutsideFragmentLifecycle() { BehaviorSubject<FragmentEvent> lifecycle = BehaviorSubject.create(); lifecycle.onNext(FragmentEvent.DETACH); TestSubscriber<Object> testSubscriber = new TestSubscriber<>(); observable.compose(RxLifecycleAndroid.bindFragment(lifecycle)).subscribe(testSubscriber); testSubscriber.assertCompleted(); testSubscriber.assertUnsubscribed(); } @Test public void testBindLifecycle() { BehaviorSubject<Object> lifecycle = BehaviorSubject.create(); Subscription attachSub = observable.compose(RxLifecycle.bind(lifecycle)).subscribe(); assertFalse(attachSub.isUnsubscribed()); lifecycle.onNext(new Object()); assertTrue(attachSub.isUnsubscribed()); } @Test public void testBindLifecycleOtherObject() { // Ensures it works with other types as well, and not just "Object" BehaviorSubject<String> lifecycle = BehaviorSubject.create(); Subscription attachSub = observable.compose(RxLifecycle.bind(lifecycle)).subscribe(); assertFalse(attachSub.isUnsubscribed()); lifecycle.onNext(""); assertTrue(attachSub.isUnsubscribed()); } @Test public void testBindView() { Activity activity = Robolectric.buildActivity(Activity.class).create().get(); View view = new View(activity); CopyOnWriteArrayList<View.OnAttachStateChangeListener> listeners = TestUtil.getAttachStateChangeListeners(view); // Do the attach notification if (listeners != null) { for (View.OnAttachStateChangeListener listener : listeners) { listener.onViewAttachedToWindow(view); } } // Subscribe Subscription viewAttachSub = observable.compose(RxLifecycleAndroid.bindView(view)).subscribe(); assertFalse(viewAttachSub.isUnsubscribed()); listeners = TestUtil.getAttachStateChangeListeners(view); assertNotNull(listeners); assertFalse(listeners.isEmpty()); // Now detach for (View.OnAttachStateChangeListener listener : listeners) { listener.onViewDetachedFromWindow(view); } assertTrue(viewAttachSub.isUnsubscribed()); } // Null checks @Test(expected=NullPointerException.class) public void testBindUntilFragmentEventThrowsOnNullLifecycle() { //noinspection ResourceType RxLifecycle.bindUntilEvent(null, FragmentEvent.CREATE); } @Test(expected=NullPointerException.class) public void testBindUntilFragmentEventThrowsOnNullEvent() { BehaviorSubject<FragmentEvent> lifecycle = BehaviorSubject.create(); //noinspection ResourceType RxLifecycle.bindUntilEvent(lifecycle, null); } @Test(expected=NullPointerException.class) public void testBindFragmentThrowsOnNull() { //noinspection ResourceType RxLifecycleAndroid.bindFragment(null); } @Test(expected=NullPointerException.class) public void testBindUntilActivityThrowsOnNullLifecycle() { //noinspection ResourceType RxLifecycle.bindUntilEvent(null, ActivityEvent.CREATE); } @Test(expected=NullPointerException.class) public void testBindUntilActivityEventThrowsOnNullEvent() { BehaviorSubject<ActivityEvent> lifecycle = BehaviorSubject.create(); //noinspection ResourceType RxLifecycle.bindUntilEvent(lifecycle, null); } @Test(expected=NullPointerException.class) public void testBindActivityThrowsOnNull() { //noinspection ResourceType RxLifecycleAndroid.bindActivity(null); } @Test(expected=NullPointerException.class) public void testBindViewThrowsOnNullView() { //noinspection ResourceType RxLifecycleAndroid.bindView((View) null); } @Test(expected=NullPointerException.class) public void testBindThrowsOnNullLifecycle() { //noinspection ResourceType RxLifecycle.bind((Observable) null); } }