package com.mopub.common;
import android.app.Activity;
import android.content.Context;
import android.os.Looper;
import com.mopub.common.factories.MethodBuilderFactory;
import com.mopub.common.test.support.SdkTestRunner;
import com.mopub.common.util.test.support.TestMethodBuilderFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import java.util.concurrent.Semaphore;
import static com.mopub.common.util.Reflection.MethodBuilder;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(SdkTestRunner.class)
public class GpsHelperTest {
private MethodBuilder methodBuilder;
private Activity context;
private TestAdInfo adInfo;
private Semaphore semaphore;
private GpsHelper.GpsHelperListener semaphoreGpsHelperListener;
private Looper gpsHelperListenerCallbackLooper;
// This class emulates the AdInfo class returned from the Google Play Services
// AdvertisingIdClient.getAdvertisingIdInfo method; need to implement getters for reflection calls
public static final class TestAdInfo {
public static final String ADVERTISING_ID = "38400000-8cf0-11bd-b23e-10b96e40000d";
public static final boolean LIMIT_AD_TRACKING_ENABLED = true;
public String mAdId = ADVERTISING_ID;
public boolean mLimitAdTrackingEnabled = LIMIT_AD_TRACKING_ENABLED;
public String getId() {
return mAdId;
}
public boolean isLimitAdTrackingEnabled() {
return mLimitAdTrackingEnabled;
}
}
@Before
public void setup() {
context = new Activity();
adInfo = new TestAdInfo();
methodBuilder = TestMethodBuilderFactory.getSingletonMock();
when(methodBuilder.setStatic(any(Class.class))).thenReturn(methodBuilder);
when(methodBuilder.addParam(any(Class.class), any())).thenReturn(methodBuilder);
semaphore = new Semaphore(0);
semaphoreGpsHelperListener = new GpsHelper.GpsHelperListener() {
@Override
public void onFetchAdInfoCompleted() {
gpsHelperListenerCallbackLooper = Looper.myLooper();
semaphore.release();
}
};
}
@After
public void tearDown() throws Exception {
reset(methodBuilder);
}
@Test
public void isPlayServicesAvailable_whenGooglePlayServicesIsLinked_shouldReturnTrue() throws Exception {
GpsHelper.setClassNamesForTesting();
when(methodBuilder.execute()).thenReturn(GpsHelper.GOOGLE_PLAY_SUCCESS_CODE);
assertThat(GpsHelper.isPlayServicesAvailable(context)).isTrue();
}
@Test
public void isPlayServicesAvailable_whenGooglePlayServicesReturnsNonSuccessCode_shouldReturnFalse() throws Exception {
GpsHelper.setClassNamesForTesting();
when(methodBuilder.execute()).thenReturn(GpsHelper.GOOGLE_PLAY_SUCCESS_CODE + 1);
assertThat(GpsHelper.isPlayServicesAvailable(context)).isFalse();
}
@Test
public void isPlayServicesAvailable_whenGooglePlayServicesReturnsNull_shouldReturnFalse() throws Exception {
GpsHelper.setClassNamesForTesting();
when(methodBuilder.execute()).thenReturn(null);
assertThat(GpsHelper.isPlayServicesAvailable(context)).isFalse();
}
@Test
public void isPlayServicesAvailable_whenGooglePlayServicesIsNotLinked_shouldReturnFalse() throws Exception {
assertThat(GpsHelper.isPlayServicesAvailable(context)).isFalse();
}
@Test
public void fetchAdvertisingInfoAsync_whenGooglePlayServicesIsLinked_shouldInvokeCallbackOnMainLooper() throws Exception {
GpsHelper.setClassNamesForTesting();
when(methodBuilder.execute()).thenReturn(
adInfo,
adInfo.mAdId,
adInfo.mLimitAdTrackingEnabled
);
GpsHelper.fetchAdvertisingInfoAsync(context, semaphoreGpsHelperListener);
safeAcquireSemaphore();
assertThat(gpsHelperListenerCallbackLooper).isEqualTo(Looper.getMainLooper());
}
@Test
public void fetchAdvertisingInfoAsync_whenGooglePlayServicesIsLinked_shouldPopulateClientMetadata() throws Exception {
verifyCleanClientMetadata(context);
GpsHelper.setClassNamesForTesting();
when(methodBuilder.execute()).thenReturn(
GpsHelper.GOOGLE_PLAY_SUCCESS_CODE,
adInfo,
adInfo.mAdId,
adInfo.mLimitAdTrackingEnabled
);
GpsHelper.fetchAdvertisingInfoAsync(context, semaphoreGpsHelperListener);
safeAcquireSemaphore();
verifyClientMetadata(context, adInfo);
}
@Test
public void fetchAdvertisingInfoAsync_whenReflectedMethodCallThrows_shouldNotPopulateClientMetadata() throws Exception {
verifyCleanClientMetadata(context);
GpsHelper.setClassNamesForTesting();
when(methodBuilder.execute()).thenThrow(new Exception());
GpsHelper.fetchAdvertisingInfoAsync(context, semaphoreGpsHelperListener);
safeAcquireSemaphore();
verifyCleanClientMetadata(context);
}
@Test
public void fetchAdvertisingInfoAsync_whenReflectedMethodCallReturnsNull_shouldNotPopulateClientMetadata() throws Exception {
verifyCleanClientMetadata(context);
GpsHelper.setClassNamesForTesting();
when(methodBuilder.execute()).thenReturn(null);
GpsHelper.fetchAdvertisingInfoAsync(context, semaphoreGpsHelperListener);
safeAcquireSemaphore();
verifyCleanClientMetadata(context);
}
@Test
public void fetchAdvertisingInfoAsync_whenGooglePlayServicesIsLinkedAndClientMetadataIsClean_shouldPopulateClientMetadata() throws Exception {
verifyCleanClientMetadata(context);
GpsHelper.setClassNamesForTesting();
when(methodBuilder.execute()).thenReturn(
GpsHelper.GOOGLE_PLAY_SUCCESS_CODE,
adInfo,
adInfo.mAdId,
adInfo.mLimitAdTrackingEnabled
);
GpsHelper.fetchAdvertisingInfoAsync(context, semaphoreGpsHelperListener);
safeAcquireSemaphore();
verifyClientMetadata(context, adInfo);
}
@Test
public void fetchAdvertisingInfoAsync_whenGooglePlayServicesLinkedAndClientMetadataIsPopulated_shouldRePopulateClientMetadata() throws Exception {
verifyCleanClientMetadata(context);
populateAndVerifyClientMetadata(context, adInfo);
adInfo.mLimitAdTrackingEnabled = false;
GpsHelper.setClassNamesForTesting();
when(methodBuilder.execute()).thenReturn(
GpsHelper.GOOGLE_PLAY_SUCCESS_CODE,
adInfo,
adInfo.mAdId,
adInfo.mLimitAdTrackingEnabled
);
GpsHelper.fetchAdvertisingInfoAsync(context, semaphoreGpsHelperListener);
safeAcquireSemaphore();
verifyClientMetadata(context, adInfo);
}
@Test
public void fetchAdvertisingInfoAsync_whenGooglePlayServicesIsNotLinked_shouldNotPopulateClientMetadata() throws Exception {
verifyCleanClientMetadata(context);
GpsHelper.setClassNamesForTesting();
when(methodBuilder.execute()).thenReturn(
GpsHelper.GOOGLE_PLAY_SUCCESS_CODE + 1
);
GpsHelper.fetchAdvertisingInfoAsync(context, semaphoreGpsHelperListener);
safeAcquireSemaphore();
verifyCleanClientMetadata(context);
}
@Test
public void isClientMetadataPopulated_whenContainsAdvertisingIdKeyAndIsLimitAdTrackingEnabledKey_shouldReturnTrue() throws Exception {
verifyCleanClientMetadata(context);
populateAndVerifyClientMetadata(context, adInfo);
assertThat(GpsHelper.isClientMetadataPopulated(context)).isTrue();
}
@Test
public void isClientMetadataPopulated_whenClean_shouldReturnFalse() throws Exception {
verifyCleanClientMetadata(context);
assertThat(GpsHelper.isClientMetadataPopulated(context)).isFalse();
}
@Test
public void updateClientMetadata_whenPassingInValidAdInfoObject_shouldUpdateClientMetadata() throws Exception {
// Use the real MethodBuilderFactory for this test, not the mock one
// Most mocks are set by default in SdkTestRunner setup
MethodBuilderFactory.setInstance(new MethodBuilderFactory());
verifyCleanClientMetadata(context);
GpsHelper.updateClientMetadata(context, adInfo);
verifyClientMetadata(context, adInfo);
}
@Test
public void reflectedGetIsLimitAdTrackingEnabled_whenIsLimitAdTrackingEnabledIsSet_shouldReturnIsLimitAdTrackingEnabled() throws Exception {
MethodBuilderFactory.setInstance(new MethodBuilderFactory());
assertThat(GpsHelper.reflectedIsLimitAdTrackingEnabled(adInfo, false)).isEqualTo(adInfo.LIMIT_AD_TRACKING_ENABLED);
}
@Test
public void reflectedGetIsLimitAdTrackingEnabled_whenReflectedMethodCallThrows_shouldReturnDefaultValue() throws Exception {
when(methodBuilder.execute()).thenThrow(new Exception());
assertThat(GpsHelper.reflectedIsLimitAdTrackingEnabled(new Object(), false)).isFalse();
verify(methodBuilder).execute();
assertThat(GpsHelper.reflectedIsLimitAdTrackingEnabled(new Object(), true)).isTrue();
verify(methodBuilder, times(2)).execute();
}
@Test
public void reflectedGetIsLimitAdTrackingEnabled_whenReflectedMethodCallReturnsNull_shouldReturnDefaultValue() throws Exception {
when(methodBuilder.execute()).thenReturn(null);
assertThat(GpsHelper.reflectedIsLimitAdTrackingEnabled(new Object(), false)).isFalse();
verify(methodBuilder).execute();
assertThat(GpsHelper.reflectedIsLimitAdTrackingEnabled(new Object(), true)).isTrue();
verify(methodBuilder, times(2)).execute();
}
@Test
public void reflectedGetAdvertisingId_whenAdvertisingIdIsSet_shouldReturnAdvertisingId() throws Exception {
MethodBuilderFactory.setInstance(new MethodBuilderFactory());
assertThat(GpsHelper.reflectedGetAdvertisingId(adInfo, null)).isEqualTo(adInfo.ADVERTISING_ID);
}
@Test
public void reflectedGetAdvertisingId_whenReflectedMethodCallThrows_shouldReturnDefaultValue() throws Exception {
when(methodBuilder.execute()).thenThrow(new Exception());
assertThat(GpsHelper.reflectedGetAdvertisingId(new Object(), null)).isNull();
verify(methodBuilder).execute();
String defaultAdId = "TEST_DEFAULT";
assertThat(GpsHelper.reflectedGetAdvertisingId(new Object(), defaultAdId)).isEqualTo(defaultAdId);
verify(methodBuilder, times(2)).execute();
}
@Test
public void isLimitAdTrackingEnabled_whenGooglePlayServicesIsLinkedAndLimitAdTrackingIsCached_shouldReturnLimitAdTracking() throws Exception {
GpsHelper.setClassNamesForTesting();
when(methodBuilder.execute()).thenReturn(GpsHelper.GOOGLE_PLAY_SUCCESS_CODE);
SharedPreferencesHelper.getSharedPreferences(context)
.edit()
.putBoolean(GpsHelper.IS_LIMIT_AD_TRACKING_ENABLED_KEY, adInfo.LIMIT_AD_TRACKING_ENABLED)
.commit();
assertThat(GpsHelper.isLimitAdTrackingEnabled(context)).isEqualTo(adInfo.LIMIT_AD_TRACKING_ENABLED);
}
@Test
public void isLimitAdTrackingEnabled_whenGooglePlayServicesIsLinkedAndAdInfoIsNotCached_shouldReturnFalse() throws Exception {
GpsHelper.setClassNamesForTesting();
when(methodBuilder.execute()).thenReturn(GpsHelper.GOOGLE_PLAY_SUCCESS_CODE);
assertThat(GpsHelper.isLimitAdTrackingEnabled(context)).isFalse();
}
@Test
public void isLimitAdTrackingEnabled_whenGooglePlayServicesIsNotLinked_shouldReturnFalse() throws Exception {
assertThat(GpsHelper.isLimitAdTrackingEnabled(context)).isFalse();
}
static public void populateAndVerifyClientMetadata(Context context, TestAdInfo adInfo) {
ClientMetadata clientMetadata = ClientMetadata.getInstance(context);
clientMetadata.setAdvertisingInfo(adInfo.getId(), adInfo.isLimitAdTrackingEnabled());
verifyClientMetadata(context, adInfo);
}
private void safeAcquireSemaphore() throws Exception {
Robolectric.runBackgroundTasks();
Robolectric.runUiThreadTasks();
semaphore.acquire();
}
static public void verifyClientMetadata(Context context, TestAdInfo adInfo) {
ClientMetadata clientMetadata = ClientMetadata.getInstance(context);
assertThat(clientMetadata.getDeviceId()).isEqualTo("ifa:" + adInfo.getId());
assertThat(clientMetadata.isDoNotTrackSet()).isEqualTo(adInfo.isLimitAdTrackingEnabled());
}
static public void verifyCleanClientMetadata(Context context) {
ClientMetadata clientMetadata = ClientMetadata.getInstance(context);
assertThat(clientMetadata.isAdvertisingInfoSet()).isFalse();
}
}