package com.mopub.mobileads; import android.content.Context; import android.location.Location; import com.mopub.common.AdReport; import com.mopub.common.DataKeys; import com.mopub.common.test.support.SdkTestRunner; import com.mopub.mobileads.factories.CustomEventInterstitialFactory; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.robolectric.Robolectric; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; import static com.mopub.mobileads.CustomEventInterstitial.CustomEventInterstitialListener; import static com.mopub.mobileads.MoPubErrorCode.ADAPTER_CONFIGURATION_ERROR; import static com.mopub.mobileads.MoPubErrorCode.ADAPTER_NOT_FOUND; import static com.mopub.mobileads.MoPubErrorCode.NETWORK_TIMEOUT; import static com.mopub.mobileads.MoPubErrorCode.UNSPECIFIED; import static org.fest.assertions.api.Assertions.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.stub; import static org.mockito.Mockito.verify; @RunWith(SdkTestRunner.class) public class CustomEventInterstitialAdapterTest { private static long BROADCAST_IDENTIFER = 123; private CustomEventInterstitialAdapter subject; @Mock private MoPubInterstitial mockMoPubInterstitial; private CustomEventInterstitial interstitial; private Map<String, Object> expectedLocalExtras; private HashMap<String, String> expectedServerExtras; @Mock private AdViewController mockAdViewController; @Mock private AdReport mockAdReport; private MoPubInterstitial.MoPubInterstitialView moPubInterstitialView; private static final String CLASS_NAME = "arbitrary_interstitial_adapter_class_name"; private Map<String, String> serverExtras; private CustomEventInterstitialAdapter.CustomEventInterstitialAdapterListener interstitialAdapterListener; @Before public void setUp() throws Exception { stub(mockMoPubInterstitial.getAdTimeoutDelay()).toReturn(null); moPubInterstitialView = mock(MoPubInterstitial.MoPubInterstitialView.class); stub(moPubInterstitialView.getAdViewController()).toReturn(mockAdViewController); stub(mockAdViewController.getAdReport()).toReturn(mockAdReport); stub(mockMoPubInterstitial.getMoPubInterstitialView()).toReturn(moPubInterstitialView); serverExtras = new HashMap<String, String>(); serverExtras.put("key", "value"); subject = new CustomEventInterstitialAdapter(mockMoPubInterstitial, CLASS_NAME, serverExtras, BROADCAST_IDENTIFER, mockAdViewController.getAdReport()); expectedLocalExtras = new HashMap<String, Object>(); expectedServerExtras = new HashMap<String, String>(); interstitial = CustomEventInterstitialFactory.create(CLASS_NAME); interstitialAdapterListener = mock(CustomEventInterstitialAdapter.CustomEventInterstitialAdapterListener.class); subject.setAdapterListener(interstitialAdapterListener); } @Test public void constructor_withInvalidClassName_shouldCallOnCustomEventInterstitialFailed() throws Exception { // Remove testing mock and use the real thing CustomEventInterstitialFactory.setInstance(new CustomEventInterstitialFactory()); new CustomEventInterstitialAdapter(mockMoPubInterstitial, "bad_class_name_11i234jb", new TreeMap<String, String>(), BROADCAST_IDENTIFER, mockAdViewController.getAdReport()); verify(mockMoPubInterstitial).onCustomEventInterstitialFailed(ADAPTER_NOT_FOUND); } @Test public void timeout_shouldSignalFailureAndInvalidateWithDefaultDelay() throws Exception { subject.loadInterstitial(); Robolectric.idleMainLooper(CustomEventInterstitialAdapter.DEFAULT_INTERSTITIAL_TIMEOUT_DELAY - 1); verify(interstitialAdapterListener, never()).onCustomEventInterstitialFailed(eq(NETWORK_TIMEOUT)); assertThat(subject.isInvalidated()).isFalse(); Robolectric.idleMainLooper(1); verify(interstitialAdapterListener).onCustomEventInterstitialFailed(eq(NETWORK_TIMEOUT)); assertThat(subject.isInvalidated()).isTrue(); } @Test public void timeout_withNegativeAdTimeoutDelay_shouldSignalFailureAndInvalidateWithDefaultDelay() throws Exception { stub(mockMoPubInterstitial.getAdTimeoutDelay()).toReturn(-1); subject.loadInterstitial(); Robolectric.idleMainLooper(CustomEventInterstitialAdapter.DEFAULT_INTERSTITIAL_TIMEOUT_DELAY - 1); verify(interstitialAdapterListener, never()).onCustomEventInterstitialFailed(eq(NETWORK_TIMEOUT)); assertThat(subject.isInvalidated()).isFalse(); Robolectric.idleMainLooper(1); verify(interstitialAdapterListener).onCustomEventInterstitialFailed(eq(NETWORK_TIMEOUT)); assertThat(subject.isInvalidated()).isTrue(); } @Test public void timeout_withNonNullAdTimeoutDelay_shouldSignalFailureAndInvalidateWithCustomDelay() throws Exception { stub(mockMoPubInterstitial.getAdTimeoutDelay()).toReturn(77); subject.loadInterstitial(); Robolectric.idleMainLooper(77000 - 1); verify(interstitialAdapterListener, never()).onCustomEventInterstitialFailed(eq(NETWORK_TIMEOUT)); assertThat(subject.isInvalidated()).isFalse(); Robolectric.idleMainLooper(1); verify(interstitialAdapterListener).onCustomEventInterstitialFailed(eq(NETWORK_TIMEOUT)); assertThat(subject.isInvalidated()).isTrue(); } @Test public void loadInterstitial_shouldPropagateLocationInLocalExtras() throws Exception { Location expectedLocation = new Location(""); expectedLocation.setLongitude(10.0); expectedLocation.setLongitude(20.1); stub(mockMoPubInterstitial.getLocation()).toReturn(expectedLocation); subject = new CustomEventInterstitialAdapter(mockMoPubInterstitial, CLASS_NAME, new HashMap<String, String>(), BROADCAST_IDENTIFER, mockAdViewController.getAdReport()); subject.loadInterstitial(); expectedLocalExtras.put("broadcastIdentifier", BROADCAST_IDENTIFER); expectedLocalExtras.put(DataKeys.AD_REPORT_KEY, mockAdReport); expectedLocalExtras.put("location", mockMoPubInterstitial.getLocation()); verify(interstitial).loadInterstitial( any(Context.class), eq(subject), eq(expectedLocalExtras), eq(expectedServerExtras) ); } @Test public void loadInterstitial_shouldPropagateServerExtrasToInterstitial() throws Exception { subject.loadInterstitial(); expectedLocalExtras.put("broadcastIdentifier", BROADCAST_IDENTIFER); expectedLocalExtras.put(DataKeys.AD_REPORT_KEY, mockAdReport); expectedServerExtras.put("key", "value"); verify(interstitial).loadInterstitial( any(Context.class), eq(subject), eq(expectedLocalExtras), eq(expectedServerExtras) ); } @Test public void loadInterstitial_shouldScheduleTimeout_interstitialLoadedAndFailed_shouldCancelTimeout() throws Exception { Robolectric.pauseMainLooper(); assertThat(Robolectric.getUiThreadScheduler().enqueuedTaskCount()).isEqualTo(0); subject.loadInterstitial(); assertThat(Robolectric.getUiThreadScheduler().enqueuedTaskCount()).isEqualTo(1); subject.onInterstitialLoaded(); assertThat(Robolectric.getUiThreadScheduler().enqueuedTaskCount()).isEqualTo(0); subject.loadInterstitial(); assertThat(Robolectric.getUiThreadScheduler().enqueuedTaskCount()).isEqualTo(1); subject.onInterstitialFailed(null); assertThat(Robolectric.getUiThreadScheduler().enqueuedTaskCount()).isEqualTo(0); } @Test public void loadInterstitial_shouldScheduleTimeoutRunnableBeforeCallingLoadInterstitial() throws Exception { Robolectric.pauseMainLooper(); assertThat(Robolectric.getUiThreadScheduler().enqueuedTaskCount()).isEqualTo(0); Answer assertTimeoutRunnableHasStarted = new Answer() { @Override public Object answer(InvocationOnMock invocationOnMock) throws Throwable { assertThat(Robolectric.getUiThreadScheduler().enqueuedTaskCount()).isEqualTo(1); return null; } }; doAnswer(assertTimeoutRunnableHasStarted) .when(interstitial) .loadInterstitial( any(Context.class), any(CustomEventInterstitialListener.class), any(Map.class), any(Map.class) ); subject.loadInterstitial(); } @Test public void loadInterstitial_whenCallingOnInterstitialFailed_shouldCancelExistingTimeoutRunnable() throws Exception { Robolectric.pauseMainLooper(); Answer justCallOnInterstitialFailed = new Answer() { @Override public Object answer(InvocationOnMock invocationOnMock) throws Throwable { assertThat(Robolectric.getUiThreadScheduler().enqueuedTaskCount()).isEqualTo(1); subject.onInterstitialFailed(null); assertThat(Robolectric.getUiThreadScheduler().enqueuedTaskCount()).isEqualTo(0); return null; } }; doAnswer(justCallOnInterstitialFailed) .when(interstitial) .loadInterstitial( any(Context.class), any(CustomEventInterstitialListener.class), any(Map.class), any(Map.class) ); assertThat(Robolectric.getUiThreadScheduler().enqueuedTaskCount()).isEqualTo(0); subject.loadInterstitial(); assertThat(Robolectric.getUiThreadScheduler().enqueuedTaskCount()).isEqualTo(0); } @Test public void showInterstitial_shouldCallCustomEventInterstitialShowInterstitial() throws Exception { subject.showInterstitial(); verify(interstitial).showInterstitial(); } @Test public void onInterstitialLoaded_shouldSignalAdapterListener() throws Exception { subject.onInterstitialLoaded(); verify(interstitialAdapterListener).onCustomEventInterstitialLoaded(); } @Test public void onInterstitialFailed_shouldLoadFailUrl() throws Exception { subject.onInterstitialFailed(ADAPTER_CONFIGURATION_ERROR); verify(interstitialAdapterListener).onCustomEventInterstitialFailed(eq(ADAPTER_CONFIGURATION_ERROR)); } @Test public void onInterstitialFailed_whenErrorCodeIsNull_shouldPassUnspecifiedError() throws Exception { subject.onInterstitialFailed(null); verify(interstitialAdapterListener).onCustomEventInterstitialFailed(eq(UNSPECIFIED)); } @Test public void onInterstitialShown_shouldSignalAdapterListener() throws Exception { subject.onInterstitialShown(); verify(interstitialAdapterListener).onCustomEventInterstitialShown(); } @Test public void onInterstitialClicked_shouldSignalAdapterListener() throws Exception { subject.onInterstitialClicked(); verify(interstitialAdapterListener).onCustomEventInterstitialClicked(); } @Test public void onLeaveApplication_shouldSignalAdapterListener() throws Exception { subject.onLeaveApplication(); verify(interstitialAdapterListener).onCustomEventInterstitialClicked(); } @Test public void onInterstitialDismissed_shouldSignalAdapterListener() throws Exception { subject.onInterstitialDismissed(); verify(interstitialAdapterListener).onCustomEventInterstitialDismissed(); } @Test public void invalidate_shouldCauseLoadInterstitialToDoNothing() throws Exception { subject.invalidate(); subject.loadInterstitial(); verify(interstitial, never()).loadInterstitial( any(Context.class), any(CustomEventInterstitialListener.class), any(Map.class), any(Map.class) ); } @Test public void invalidate_shouldCauseShowInterstitialToDoNothing() throws Exception { subject.invalidate(); subject.showInterstitial(); verify(interstitial, never()).showInterstitial(); } @Test public void invalidate_shouldCauseInterstitialListenerMethodsToDoNothing() throws Exception { subject.invalidate(); subject.onInterstitialLoaded(); subject.onInterstitialFailed(null); subject.onInterstitialShown(); subject.onInterstitialClicked(); subject.onLeaveApplication(); subject.onInterstitialDismissed(); verify(interstitialAdapterListener, never()).onCustomEventInterstitialLoaded(); verify(interstitialAdapterListener, never()).onCustomEventInterstitialFailed(any(MoPubErrorCode.class)); verify(interstitialAdapterListener, never()).onCustomEventInterstitialShown(); verify(interstitialAdapterListener, never()).onCustomEventInterstitialClicked(); verify(interstitialAdapterListener, never()).onCustomEventInterstitialDismissed(); } }