Skip to content

Commit

Permalink
Add unit tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
patloew committed Nov 1, 2016
1 parent f5ee5d8 commit f10af67
Show file tree
Hide file tree
Showing 16 changed files with 1,709 additions and 28 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Reactive Location API Library for Android

[![Build Status](https://travis-ci.org/patloew/RxLocation.svg?branch=master)](https://travis-ci.org/patloew/RxLocation) [![API](https://img.shields.io/badge/API-9%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=9)
[![Build Status](https://travis-ci.org/patloew/RxLocation.svg?branch=master)](https://travis-ci.org/patloew/RxLocation) [![codecov](https://codecov.io/gh/patloew/RxLocation/branch/master/graph/badge.svg)](https://codecov.io/gh/patloew/RxLocation) [![API](https://img.shields.io/badge/API-9%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=9)

This library wraps the Location APIs in [RxJava 2](https://github.com/ReactiveX/RxJava/tree/2.x) Observables, Singles and Maybes. No more managing GoogleApiClients! Also, the resolution of the location settings check is optionally handled by the lib.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class LocationSettingsActivity extends Activity {
protected static final String ARG_STATUS = "status";
protected static final String ARG_ID = "id";

private static final int REQUEST_CODE_RESOLUTION = 123;
static final int REQUEST_CODE_RESOLUTION = 123;

@Override
protected void onCreate(Bundle savedInstanceState) {
Expand All @@ -42,7 +42,7 @@ protected void onNewIntent(Intent intent) {
handleIntent();
}

private void handleIntent() {
void handleIntent() {
Status status = getIntent().getParcelableExtra(ARG_STATUS);

try {
Expand All @@ -62,7 +62,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
}
}

private void setResolutionResultAndFinish(int resultCode) {
void setResolutionResultAndFinish(int resultCode) {
SettingsCheckHandleSingle.onResolutionResult(getIntent().getStringExtra(ARG_ID), resultCode);
finish();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class LocationUpdatesFlowable extends BaseFlowable<Location> {

final LocationRequest locationRequest;
final Looper looper;
private LocationListener locationListener;
LocationListener locationListener;

protected LocationUpdatesFlowable(@NonNull RxLocation rxLocation, LocationRequest locationRequest, Looper looper, Long timeout, TimeUnit timeUnit) {
super(rxLocation, timeout, timeUnit);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ protected void onGoogleApiClientReady(GoogleApiClient apiClient, SingleEmitter<B

emitter.onSuccess(false);
break;

default:
emitter.onError(new StatusException(status));
break;
}
}
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package com.patloew.rxlocation;

import android.app.PendingIntent;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.LocationServices;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareOnlyThisForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import io.reactivex.Single;

import static org.mockito.Mockito.when;

@SuppressWarnings("MissingPermission")
@RunWith(PowerMockRunner.class)
@PrepareOnlyThisForTest({ LocationServices.class, com.google.android.gms.location.ActivityRecognition.class, Status.class, ConnectionResult.class, BaseRx.class })
public class ActivityOnSubscribeTest extends BaseOnSubscribeTest {

@Mock PendingIntent pendingIntent;

@Override
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
super.setup();
}

// ActivityRequestUpdatesSingle

@Test
public void ActivityRequestUpdatesSingle_Success() {
ActivityRequestUpdatesSingle single = PowerMockito.spy(new ActivityRequestUpdatesSingle(rxLocation, 1L, pendingIntent, null, null));

setPendingResultValue(status);
when(status.isSuccess()).thenReturn(true);
when(activityRecognitionApi.requestActivityUpdates(apiClient, 1L, pendingIntent)).thenReturn(pendingResult);

setupBaseSingleSuccess(single);

assertSingleValue(Single.create(single).test(), status);
}

@Test
public void ActivityRequestUpdatesSingle_StatusException() {
ActivityRequestUpdatesSingle single = PowerMockito.spy(new ActivityRequestUpdatesSingle(rxLocation, 1L, pendingIntent, null, null));

setPendingResultValue(status);
when(status.isSuccess()).thenReturn(false);
when(activityRecognitionApi.requestActivityUpdates(apiClient, 1L, pendingIntent)).thenReturn(pendingResult);

setupBaseSingleSuccess(single);

assertError(Single.create(single).test(), StatusException.class);
}

// ActivityRemoveUpdatesSingle

@Test
public void ActivityRemoveUpdatesSingle_Success() {
ActivityRemoveUpdatesSingle single = PowerMockito.spy(new ActivityRemoveUpdatesSingle(rxLocation, pendingIntent, null, null));

setPendingResultValue(status);
when(status.isSuccess()).thenReturn(true);
when(activityRecognitionApi.removeActivityUpdates(apiClient, pendingIntent)).thenReturn(pendingResult);

setupBaseSingleSuccess(single);

assertSingleValue(Single.create(single).test(), status);
}

@Test
public void ActivityRemoveUpdatesSingle_StatusException() {
ActivityRemoveUpdatesSingle single = PowerMockito.spy(new ActivityRemoveUpdatesSingle(rxLocation, pendingIntent, null, null));

setPendingResultValue(status);
when(status.isSuccess()).thenReturn(false);
when(activityRecognitionApi.removeActivityUpdates(apiClient, pendingIntent)).thenReturn(pendingResult);

setupBaseSingleSuccess(single);

assertError(Single.create(single).test(), StatusException.class);
}
}
85 changes: 85 additions & 0 deletions library/src/test/java/com/patloew/rxlocation/ActivityTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.patloew.rxlocation;

import android.app.PendingIntent;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.LocationServices;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareOnlyThisForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import io.reactivex.Single;

import static junit.framework.Assert.assertEquals;
import static org.mockito.Mockito.times;

@SuppressWarnings("MissingPermission")
@RunWith(PowerMockRunner.class)
@PrepareOnlyThisForTest({ Single.class, LocationServices.class, com.google.android.gms.location.ActivityRecognition.class, Status.class, ConnectionResult.class })
public class ActivityTest extends BaseTest {

@Mock PendingIntent pendingIntent;

@Override
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
PowerMockito.spy(Single.class);
super.setup();
}

// Request Updates

@Test
public void Activity_RequestUpdates() throws Exception {
ArgumentCaptor<ActivityRequestUpdatesSingle> captor = ArgumentCaptor.forClass(ActivityRequestUpdatesSingle.class);

long detectionIntervalMillis = 123L;
rxLocation.activity().requestUpdates(detectionIntervalMillis,pendingIntent);
rxLocation.activity().requestUpdates(detectionIntervalMillis,pendingIntent, TIMEOUT_TIME, TIMEOUT_TIMEUNIT);

PowerMockito.verifyStatic(times(2));
Single.create(captor.capture());

ActivityRequestUpdatesSingle single = captor.getAllValues().get(0);
assertEquals(detectionIntervalMillis, single.detectionIntervalMillis);
assertEquals(pendingIntent, single.pendingIntent);
assertNoTimeoutSet(single);

single = captor.getAllValues().get(1);
assertEquals(detectionIntervalMillis, single.detectionIntervalMillis);
assertEquals(pendingIntent, single.pendingIntent);
assertTimeoutSet(single);
}

// Remove Updates

@Test
public void Activity_RemoveUpdates() throws Exception {
ArgumentCaptor<ActivityRemoveUpdatesSingle> captor = ArgumentCaptor.forClass(ActivityRemoveUpdatesSingle.class);

rxLocation.activity().removeUpdates(pendingIntent);
rxLocation.activity().removeUpdates(pendingIntent, TIMEOUT_TIME, TIMEOUT_TIMEUNIT);

PowerMockito.verifyStatic(times(2));
Single.create(captor.capture());

ActivityRemoveUpdatesSingle single = captor.getAllValues().get(0);
assertEquals(pendingIntent, single.pendingIntent);
assertNoTimeoutSet(single);

single = captor.getAllValues().get(1);
assertEquals(pendingIntent, single.pendingIntent);
assertTimeoutSet(single);
}


}
109 changes: 109 additions & 0 deletions library/src/test/java/com/patloew/rxlocation/BaseMaybeTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package com.patloew.rxlocation;

import android.support.v4.content.ContextCompat;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.Api;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.ActivityRecognition;
import com.google.android.gms.location.LocationServices;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.mockito.MockitoAnnotations;
import org.powermock.core.classloader.annotations.PrepareOnlyThisForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import io.reactivex.Maybe;
import io.reactivex.MaybeEmitter;
import io.reactivex.SingleEmitter;
import io.reactivex.observers.TestObserver;

import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;

@RunWith(PowerMockRunner.class)
@PrepareOnlyThisForTest({ ContextCompat.class, Status.class, LocationServices.class, ActivityRecognition.class, ConnectionResult.class, SingleEmitter.class })
public class BaseMaybeTest extends BaseOnSubscribeTest {

@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
super.setup();
}

@Test
public void BaseObservable_ApiClient_Connected() {
final Object object = new Object();
BaseMaybe<Object> single = spy(new BaseMaybe<Object>(ctx, new Api[] {}, null) {
@Override
protected void onGoogleApiClientReady(GoogleApiClient apiClient, MaybeEmitter<? super Object> emitter) {
emitter.onSuccess(object);
}
});

doAnswer(invocation -> {
BaseRx.ApiClientConnectionCallbacks callbacks = invocation.getArgumentAt(0, BaseRx.ApiClientConnectionCallbacks.class);
callbacks.setClient(apiClient);
callbacks.onConnected(null);
return apiClient;
}).when(single).createApiClient(Matchers.any(BaseRx.ApiClientConnectionCallbacks.class));

TestObserver<Object> sub = Maybe.create(single).test();

sub.assertValue(object);
sub.assertComplete();
}

@Test
public void BaseObservable_ApiClient_ConnectionSuspended() {
final Object object = new Object();
BaseMaybe<Object> single = spy(new BaseMaybe<Object>(ctx, new Api[] {}, null) {
@Override
protected void onGoogleApiClientReady(GoogleApiClient apiClient, MaybeEmitter<? super Object> emitter) {
emitter.onSuccess(object);
}
});

doAnswer(invocation -> {
BaseRx.ApiClientConnectionCallbacks callbacks = invocation.getArgumentAt(0, BaseRx.ApiClientConnectionCallbacks.class);
callbacks.setClient(apiClient);
callbacks.onConnectionSuspended(0);
return apiClient;
}).when(single).createApiClient(Matchers.any(BaseRx.ApiClientConnectionCallbacks.class));

TestObserver<Object> sub = Maybe.create(single).test();

sub.assertNoValues();
sub.assertError(GoogleAPIConnectionSuspendedException.class);
}

@Test
public void BaseObservable_ApiClient_ConnectionFailed() {
final Object object = new Object();
BaseMaybe<Object> single = spy(new BaseMaybe<Object>(ctx, new Api[] {}, null) {
@Override
protected void onGoogleApiClientReady(GoogleApiClient apiClient, MaybeEmitter<? super Object> emitter) {
emitter.onSuccess(object);
}
});

doReturn(false).when(connectionResult).hasResolution();

doAnswer(invocation -> {
BaseRx.ApiClientConnectionCallbacks callbacks = invocation.getArgumentAt(0, BaseRx.ApiClientConnectionCallbacks.class);
callbacks.setClient(apiClient);
callbacks.onConnectionFailed(connectionResult);
return apiClient;
}).when(single).createApiClient(Matchers.any(BaseRx.ApiClientConnectionCallbacks.class));

TestObserver<Object> sub = Maybe.create(single).test();

sub.assertNoValues();
sub.assertError(GoogleAPIConnectionException.class);
}
}
Loading

0 comments on commit f10af67

Please sign in to comment.