From 22233acb8e3d92ceede872252bb48036c5ec45c8 Mon Sep 17 00:00:00 2001 From: Jeffrey Yu Date: Mon, 13 Jan 2014 22:40:27 -0800 Subject: [PATCH] Separated Android test code from source. --- .../observables/AndroidObservable.java | 58 ----- .../schedulers/HandlerThreadScheduler.java | 53 ----- .../OperationObserveFromAndroidComponent.java | 164 -------------- .../observables/AndroidObservableTest.java | 85 +++++++ ...rationObserveFromAndroidComponentTest.java | 213 ++++++++++++++++++ .../HandlerThreadSchedulerTest.java | 81 +++++++ 6 files changed, 379 insertions(+), 275 deletions(-) create mode 100644 rxjava-contrib/rxjava-android/src/test/java/rx/android/observables/AndroidObservableTest.java create mode 100644 rxjava-contrib/rxjava-android/src/test/java/rx/android/operators/OperationObserveFromAndroidComponentTest.java create mode 100644 rxjava-contrib/rxjava-android/src/test/java/rx/android/schedulers/HandlerThreadSchedulerTest.java diff --git a/rxjava-contrib/rxjava-android/src/main/java/rx/android/observables/AndroidObservable.java b/rxjava-contrib/rxjava-android/src/main/java/rx/android/observables/AndroidObservable.java index 5ae8a11a74..be471b6291 100644 --- a/rxjava-contrib/rxjava-android/src/main/java/rx/android/observables/AndroidObservable.java +++ b/rxjava-contrib/rxjava-android/src/main/java/rx/android/observables/AndroidObservable.java @@ -15,16 +15,6 @@ */ package rx.android.observables; -import static org.mockito.Mockito.verify; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.Robolectric; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; import rx.Observable; import rx.Observer; import rx.operators.OperationObserveFromAndroidComponent; @@ -108,52 +98,4 @@ public static Observable fromFragment(Object fragment, Observable sour throw new IllegalArgumentException("Target fragment is neither a native nor support library Fragment"); } } - - @RunWith(RobolectricTestRunner.class) - @Config(manifest = Config.NONE) - public static final class AndroidObservableTest { - - // support library fragments - private FragmentActivity fragmentActivity; - private android.support.v4.app.Fragment supportFragment; - - // native fragments - private Activity activity; - private Fragment fragment; - - @Mock - private Observer observer; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - supportFragment = new android.support.v4.app.Fragment(); - fragmentActivity = Robolectric.buildActivity(FragmentActivity.class).create().get(); - fragmentActivity.getSupportFragmentManager().beginTransaction().add(supportFragment, null).commit(); - - fragment = new Fragment(); - activity = Robolectric.buildActivity(Activity.class).create().get(); - activity.getFragmentManager().beginTransaction().add(fragment, null).commit(); - } - - @Test - public void itSupportsFragmentsFromTheSupportV4Library() { - fromFragment(supportFragment, Observable.just("success")).subscribe(observer); - verify(observer).onNext("success"); - verify(observer).onCompleted(); - } - - @Test - public void itSupportsNativeFragments() { - fromFragment(fragment, Observable.just("success")).subscribe(observer); - verify(observer).onNext("success"); - verify(observer).onCompleted(); - } - - @Test(expected = IllegalArgumentException.class) - public void itThrowsIfObjectPassedIsNotAFragment() { - fromFragment("not a fragment", Observable.never()); - } - } - } diff --git a/rxjava-contrib/rxjava-android/src/main/java/rx/android/schedulers/HandlerThreadScheduler.java b/rxjava-contrib/rxjava-android/src/main/java/rx/android/schedulers/HandlerThreadScheduler.java index 8535dda295..5ce5856d86 100644 --- a/rxjava-contrib/rxjava-android/src/main/java/rx/android/schedulers/HandlerThreadScheduler.java +++ b/rxjava-contrib/rxjava-android/src/main/java/rx/android/schedulers/HandlerThreadScheduler.java @@ -17,12 +17,6 @@ import android.os.Handler; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - import rx.Scheduler; import rx.Subscription; import rx.operators.SafeObservableSubscription; @@ -30,10 +24,6 @@ import java.util.concurrent.TimeUnit; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - /** * Schedules actions to run on an Android Handler thread. */ @@ -84,49 +74,6 @@ public void run() { }, unit.toMillis(delayTime)); return subscription; } - - @RunWith(RobolectricTestRunner.class) - @Config(manifest=Config.NONE) - public static final class UnitTest { - - @Test - public void shouldScheduleImmediateActionOnHandlerThread() { - final Handler handler = mock(Handler.class); - final Object state = new Object(); - @SuppressWarnings("unchecked") - final Func2 action = mock(Func2.class); - - Scheduler scheduler = new HandlerThreadScheduler(handler); - scheduler.schedule(state, action); - - // verify that we post to the given Handler - ArgumentCaptor runnable = ArgumentCaptor.forClass(Runnable.class); - verify(handler).postDelayed(runnable.capture(), eq(0L)); - - // verify that the given handler delegates to our action - runnable.getValue().run(); - verify(action).call(scheduler, state); - } - - @Test - public void shouldScheduleDelayedActionOnHandlerThread() { - final Handler handler = mock(Handler.class); - final Object state = new Object(); - @SuppressWarnings("unchecked") - final Func2 action = mock(Func2.class); - - Scheduler scheduler = new HandlerThreadScheduler(handler); - scheduler.schedule(state, action, 1L, TimeUnit.SECONDS); - - // verify that we post to the given Handler - ArgumentCaptor runnable = ArgumentCaptor.forClass(Runnable.class); - verify(handler).postDelayed(runnable.capture(), eq(1000L)); - - // verify that the given handler delegates to our action - runnable.getValue().run(); - verify(action).call(scheduler, state); - } - } } diff --git a/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java b/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java index 06b22dae5c..4ebf6b117a 100644 --- a/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java +++ b/rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperationObserveFromAndroidComponent.java @@ -15,23 +15,6 @@ */ package rx.operators; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - import rx.Observable; import rx.Observer; import rx.Subscription; @@ -42,7 +25,6 @@ import android.os.Looper; import android.util.Log; -import java.lang.reflect.Field; import java.util.concurrent.Callable; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -164,150 +146,4 @@ protected boolean isComponentValid(android.support.v4.app.Fragment fragment) { return fragment.isAdded(); } } - - @RunWith(RobolectricTestRunner.class) - @Config(manifest = Config.NONE) - public static final class UnitTest { - - @Mock - private Observer mockObserver; - - @Mock - private Fragment mockFragment; - - @Mock - private Activity mockActivity; - - @Mock - private Observable mockObservable; - - @Before - public void setupMocks() { - MockitoAnnotations.initMocks(this); - when(mockFragment.isAdded()).thenReturn(true); - } - - @Test - public void itThrowsIfObserverSubscribesFromBackgroundThread() throws Exception { - final Future future = Executors.newSingleThreadExecutor().submit(new Callable() { - @Override - public Object call() throws Exception { - OperationObserveFromAndroidComponent.observeFromAndroidComponent( - mockObservable, mockFragment).subscribe(mockObserver); - return null; - } - }); - future.get(1, TimeUnit.SECONDS); - verify(mockObserver).onError(any(IllegalStateException.class)); - verifyNoMoreInteractions(mockObserver); - } - - @Test - public void itObservesTheSourceSequenceOnTheMainUIThread() { - OperationObserveFromAndroidComponent.observeFromAndroidComponent(mockObservable, mockFragment).subscribe(mockObserver); - verify(mockObservable).observeOn(AndroidSchedulers.mainThread()); - } - - @Test - public void itForwardsOnNextOnCompletedSequenceToTargetObserver() { - Observable source = Observable.from(1, 2, 3); - OperationObserveFromAndroidComponent.observeFromAndroidComponent(source, mockFragment).subscribe(mockObserver); - verify(mockObserver, times(3)).onNext(anyInt()); - verify(mockObserver).onCompleted(); - verify(mockObserver, never()).onError(any(Exception.class)); - } - - @Test - public void itForwardsOnErrorToTargetObserver() { - final Exception exception = new Exception(); - Observable source = Observable.error(exception); - OperationObserveFromAndroidComponent.observeFromAndroidComponent(source, mockFragment).subscribe(mockObserver); - verify(mockObserver).onError(exception); - verify(mockObserver, never()).onNext(anyInt()); - verify(mockObserver, never()).onCompleted(); - } - - @Test - public void itDropsOnNextOnCompletedSequenceIfTargetComponentIsGone() throws Throwable { - PublishSubject source = PublishSubject.create(); - - final OnSubscribeFragment operator = new OnSubscribeFragment(source, mockFragment); - operator.onSubscribe(mockObserver); - - source.onNext(1); - releaseComponentRef(operator); - - source.onNext(2); - source.onNext(3); - source.onCompleted(); - - verify(mockObserver).onNext(1); - verifyNoMoreInteractions(mockObserver); - } - - @Test - public void itDropsOnErrorIfTargetComponentIsGone() throws Throwable { - PublishSubject source = PublishSubject.create(); - - final OnSubscribeFragment operator = new OnSubscribeFragment(source, mockFragment); - operator.onSubscribe(mockObserver); - - source.onNext(1); - releaseComponentRef(operator); - - source.onError(new Exception()); - - verify(mockObserver).onNext(1); - verifyNoMoreInteractions(mockObserver); - } - - private void releaseComponentRef(OnSubscribeFragment operator) throws NoSuchFieldException, IllegalAccessException { - final Field componentRef = operator.getClass().getSuperclass().getDeclaredField("componentRef"); - componentRef.setAccessible(true); - componentRef.set(operator, null); - } - - @Test - public void itDoesNotForwardOnNextOnCompletedSequenceIfFragmentIsDetached() { - PublishSubject source = PublishSubject.create(); - OperationObserveFromAndroidComponent.observeFromAndroidComponent(source, mockFragment).subscribe(mockObserver); - - source.onNext(1); - - when(mockFragment.isAdded()).thenReturn(false); - source.onNext(2); - source.onNext(3); - source.onCompleted(); - - verify(mockObserver).onNext(1); - verify(mockObserver, never()).onCompleted(); - } - - @Test - public void itDoesNotForwardOnErrorIfFragmentIsDetached() { - PublishSubject source = PublishSubject.create(); - OperationObserveFromAndroidComponent.observeFromAndroidComponent(source, mockFragment).subscribe(mockObserver); - - source.onNext(1); - - when(mockFragment.isAdded()).thenReturn(false); - source.onError(new Exception()); - - verify(mockObserver).onNext(1); - verify(mockObserver, never()).onError(any(Exception.class)); - } - - @Test - public void itUnsubscribesFromTheSourceSequence() { - Subscription underlying = mock(Subscription.class); - when(mockObservable.observeOn(AndroidSchedulers.mainThread())).thenReturn(mockObservable); - when(mockObservable.subscribe(any(Observer.class))).thenReturn(underlying); - - Subscription sub = OperationObserveFromAndroidComponent.observeFromAndroidComponent( - mockObservable, mockActivity).subscribe(mockObserver); - sub.unsubscribe(); - - verify(underlying).unsubscribe(); - } - } } diff --git a/rxjava-contrib/rxjava-android/src/test/java/rx/android/observables/AndroidObservableTest.java b/rxjava-contrib/rxjava-android/src/test/java/rx/android/observables/AndroidObservableTest.java new file mode 100644 index 0000000000..0f8f81e5e8 --- /dev/null +++ b/rxjava-contrib/rxjava-android/src/test/java/rx/android/observables/AndroidObservableTest.java @@ -0,0 +1,85 @@ +/** + * Copyright 2013 Netflix, Inc. + * + * 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 rx.android.observables; + +import static org.mockito.Mockito.verify; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import rx.Observable; +import rx.Observer; +import rx.operators.OperationObserveFromAndroidComponent; + +import android.app.Activity; +import android.app.Fragment; +import android.os.Build; +import android.support.v4.app.FragmentActivity; + +import rx.android.observables.AndroidObservable; + + +@RunWith(RobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class AndroidObservableTest { + + // support library fragments + private FragmentActivity fragmentActivity; + private android.support.v4.app.Fragment supportFragment; + + // native fragments + private Activity activity; + private Fragment fragment; + + @Mock + private Observer observer; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + supportFragment = new android.support.v4.app.Fragment(); + fragmentActivity = Robolectric.buildActivity(FragmentActivity.class).create().get(); + fragmentActivity.getSupportFragmentManager().beginTransaction().add(supportFragment, null).commit(); + + fragment = new Fragment(); + activity = Robolectric.buildActivity(Activity.class).create().get(); + activity.getFragmentManager().beginTransaction().add(fragment, null).commit(); + } + + @Test + public void itSupportsFragmentsFromTheSupportV4Library() { + AndroidObservable.fromFragment(supportFragment, Observable.just("success")).subscribe(observer); + verify(observer).onNext("success"); + verify(observer).onCompleted(); + } + + @Test + public void itSupportsNativeFragments() { + AndroidObservable.fromFragment(fragment, Observable.just("success")).subscribe(observer); + verify(observer).onNext("success"); + verify(observer).onCompleted(); + } + + @Test(expected = IllegalArgumentException.class) + public void itThrowsIfObjectPassedIsNotAFragment() { + AndroidObservable.fromFragment("not a fragment", Observable.never()); + } +} diff --git a/rxjava-contrib/rxjava-android/src/test/java/rx/android/operators/OperationObserveFromAndroidComponentTest.java b/rxjava-contrib/rxjava-android/src/test/java/rx/android/operators/OperationObserveFromAndroidComponentTest.java new file mode 100644 index 0000000000..14ef2d2790 --- /dev/null +++ b/rxjava-contrib/rxjava-android/src/test/java/rx/android/operators/OperationObserveFromAndroidComponentTest.java @@ -0,0 +1,213 @@ +/** + * Copyright 2013 Netflix, Inc. + * + * 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 rx.operators; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.junit.Assert.*; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.subjects.PublishSubject; +import android.app.Activity; +import android.app.Fragment; +import android.os.Looper; +import android.util.Log; + +import java.lang.reflect.Field; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.Callable; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +@RunWith(RobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class OperationObserveFromAndroidComponentTest { + + @Mock + private Observer mockObserver; + + @Mock + private Fragment mockFragment; + + @Mock + private Activity mockActivity; + + @Mock + private Observable mockObservable; + + @Before + public void setupMocks() { + MockitoAnnotations.initMocks(this); + when(mockFragment.isAdded()).thenReturn(true); + } + + @Test + public void itThrowsIfObserverSubscribesFromBackgroundThread() throws Exception { + final Future future = Executors.newSingleThreadExecutor().submit(new Callable() { + @Override + public Object call() throws Exception { + OperationObserveFromAndroidComponent.observeFromAndroidComponent( + mockObservable, mockFragment).subscribe(mockObserver); + return null; + } + }); + future.get(1, TimeUnit.SECONDS); + verify(mockObserver).onError(any(IllegalStateException.class)); + verifyNoMoreInteractions(mockObserver); + } + + @Test + public void itObservesTheSourceSequenceOnTheMainUIThread() { + OperationObserveFromAndroidComponent.observeFromAndroidComponent(mockObservable, mockFragment).subscribe(mockObserver); + verify(mockObservable).observeOn(AndroidSchedulers.mainThread()); + } + + @Test + public void itForwardsOnNextOnCompletedSequenceToTargetObserver() { + Observable source = Observable.from(1, 2, 3); + OperationObserveFromAndroidComponent.observeFromAndroidComponent(source, mockFragment).subscribe(mockObserver); + verify(mockObserver, times(3)).onNext(anyInt()); + verify(mockObserver).onCompleted(); + verify(mockObserver, never()).onError(any(Exception.class)); + } + + @Test + public void itForwardsOnErrorToTargetObserver() { + final Exception exception = new Exception(); + Observable source = Observable.error(exception); + OperationObserveFromAndroidComponent.observeFromAndroidComponent(source, mockFragment).subscribe(mockObserver); + verify(mockObserver).onError(exception); + verify(mockObserver, never()).onNext(anyInt()); + verify(mockObserver, never()).onCompleted(); + } + + @Test + public void itDropsOnNextOnCompletedSequenceIfTargetComponentIsGone() throws Throwable { + PublishSubject source = PublishSubject.create(); + + final Observable.OnSubscribeFunc operator = newOnSubscribeFragmentInstance(source, mockFragment); + operator.onSubscribe(mockObserver); + + source.onNext(1); + releaseComponentRef(operator); + + source.onNext(2); + source.onNext(3); + source.onCompleted(); + + verify(mockObserver).onNext(1); + verifyNoMoreInteractions(mockObserver); + } + + @Test + public void itDropsOnErrorIfTargetComponentIsGone() throws Throwable { + PublishSubject source = PublishSubject.create(); + + final Observable.OnSubscribeFunc operator = newOnSubscribeFragmentInstance(source, mockFragment); + operator.onSubscribe(mockObserver); + + source.onNext(1); + releaseComponentRef(operator); + + source.onError(new Exception()); + + verify(mockObserver).onNext(1); + verifyNoMoreInteractions(mockObserver); + } + + private Observable.OnSubscribeFunc newOnSubscribeFragmentInstance(Observable source, Fragment fragment) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { + final Class[] klasses = OperationObserveFromAndroidComponent.class.getDeclaredClasses(); + Class onSubscribeFragmentClass = null; + for (Class klass : klasses) { + if ("rx.operators.OperationObserveFromAndroidComponent$OnSubscribeFragment".equals(klass.getName())) { + onSubscribeFragmentClass = klass; + break; + } + } + Constructor constructor = onSubscribeFragmentClass.getDeclaredConstructor(Observable.class, Fragment.class); + constructor.setAccessible(true); + Object object = constructor.newInstance(source, fragment); + return (Observable.OnSubscribeFunc) object; + } + + private void releaseComponentRef(Observable.OnSubscribeFunc operator) throws NoSuchFieldException, IllegalAccessException { + final Field componentRef = operator.getClass().getSuperclass().getDeclaredField("componentRef"); + componentRef.setAccessible(true); + componentRef.set(operator, null); + } + + @Test + public void itDoesNotForwardOnNextOnCompletedSequenceIfFragmentIsDetached() { + PublishSubject source = PublishSubject.create(); + OperationObserveFromAndroidComponent.observeFromAndroidComponent(source, mockFragment).subscribe(mockObserver); + + source.onNext(1); + + when(mockFragment.isAdded()).thenReturn(false); + source.onNext(2); + source.onNext(3); + source.onCompleted(); + + verify(mockObserver).onNext(1); + verify(mockObserver, never()).onCompleted(); + } + + @Test + public void itDoesNotForwardOnErrorIfFragmentIsDetached() { + PublishSubject source = PublishSubject.create(); + OperationObserveFromAndroidComponent.observeFromAndroidComponent(source, mockFragment).subscribe(mockObserver); + + source.onNext(1); + + when(mockFragment.isAdded()).thenReturn(false); + source.onError(new Exception()); + + verify(mockObserver).onNext(1); + verify(mockObserver, never()).onError(any(Exception.class)); + } + + @Test + public void itUnsubscribesFromTheSourceSequence() { + Subscription underlying = mock(Subscription.class); + when(mockObservable.observeOn(AndroidSchedulers.mainThread())).thenReturn(mockObservable); + when(mockObservable.subscribe(any(Observer.class))).thenReturn(underlying); + + Subscription sub = OperationObserveFromAndroidComponent.observeFromAndroidComponent( + mockObservable, mockActivity).subscribe(mockObserver); + sub.unsubscribe(); + + verify(underlying).unsubscribe(); + } +} diff --git a/rxjava-contrib/rxjava-android/src/test/java/rx/android/schedulers/HandlerThreadSchedulerTest.java b/rxjava-contrib/rxjava-android/src/test/java/rx/android/schedulers/HandlerThreadSchedulerTest.java new file mode 100644 index 0000000000..936ff51dbf --- /dev/null +++ b/rxjava-contrib/rxjava-android/src/test/java/rx/android/schedulers/HandlerThreadSchedulerTest.java @@ -0,0 +1,81 @@ +/** + * Copyright 2013 Netflix, Inc. + * + * 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 rx.android.schedulers; + +import android.os.Handler; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import rx.Scheduler; +import rx.Subscription; +import rx.operators.SafeObservableSubscription; +import rx.util.functions.Func2; + +import java.util.concurrent.TimeUnit; + +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +@RunWith(RobolectricTestRunner.class) +@Config(manifest=Config.NONE) +public class HandlerThreadSchedulerTest { + + @Test + public void shouldScheduleImmediateActionOnHandlerThread() { + final Handler handler = mock(Handler.class); + final Object state = new Object(); + @SuppressWarnings("unchecked") + final Func2 action = mock(Func2.class); + + Scheduler scheduler = new HandlerThreadScheduler(handler); + scheduler.schedule(state, action); + + // verify that we post to the given Handler + ArgumentCaptor runnable = ArgumentCaptor.forClass(Runnable.class); + verify(handler).postDelayed(runnable.capture(), eq(0L)); + + // verify that the given handler delegates to our action + runnable.getValue().run(); + verify(action).call(scheduler, state); + } + + @Test + public void shouldScheduleDelayedActionOnHandlerThread() { + final Handler handler = mock(Handler.class); + final Object state = new Object(); + @SuppressWarnings("unchecked") + final Func2 action = mock(Func2.class); + + Scheduler scheduler = new HandlerThreadScheduler(handler); + scheduler.schedule(state, action, 1L, TimeUnit.SECONDS); + + // verify that we post to the given Handler + ArgumentCaptor runnable = ArgumentCaptor.forClass(Runnable.class); + verify(handler).postDelayed(runnable.capture(), eq(1000L)); + + // verify that the given handler delegates to our action + runnable.getValue().run(); + verify(action).call(scheduler, state); + } +}