package com.mopub.nativeads;
import android.app.Activity;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import com.mopub.common.test.support.SdkTestRunner;
import com.mopub.nativeads.MoPubNativeAdPositioning.MoPubClientPositioning;
import com.mopub.nativeads.PositioningSource.PositioningListener;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.robolectric.Robolectric;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
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.when;
@RunWith(SdkTestRunner.class)
public class MoPubStreamAdPlacerTest {
private Context context;
MoPubClientPositioning positioning;
@Mock
PositioningSource mockPositioningSource;
@Mock
NativeAdSource mockAdSource;
@Mock
MoPubNativeAdRenderer mockAdRenderer;
@Mock
MoPubNativeAdLoadedListener mockAdLoadedListener;
@Mock
ImpressionTracker mockImpressionTracker;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
NativeResponse stubNativeResponse;
private MoPubStreamAdPlacer subject;
@Before
public void setup() {
context = new Activity();
positioning = MoPubNativeAdPositioning.clientPositioning()
.enableRepeatingPositions(2);
subject = new MoPubStreamAdPlacer(
context, mockAdSource, mockImpressionTracker, mockPositioningSource);
subject.registerAdRenderer(mockAdRenderer);
subject.setAdLoadedListener(mockAdLoadedListener);
}
@Test
public void isAd_initialState_hasNoAds() {
checkAdPositions();
}
@Test
public void isAd_loadPositions_withoutLoadingAds_hasNoAds() {
subject.handlePositioningLoad(positioning);
checkAdPositions();
}
@Test
public void isAd_loadAds_withoutLoadingPositions_hasNoAds() {
when(mockAdSource.dequeueAd()).thenReturn(stubNativeResponse);
subject.handleAdsAvailable();
subject.setItemCount(4);
checkAdPositions();
}
@Test
public void isAd_loadAds_thenLoadPositions_hasAds() {
when(mockAdSource.dequeueAd()).thenReturn(stubNativeResponse);
subject.registerAdRenderer(mockAdRenderer);
subject.loadAds("test-ad-unit-id");
subject.handleAdsAvailable();
subject.handlePositioningLoad(positioning);
subject.setItemCount(4);
checkAdPositions(1, 3, 5);
}
@Test
public void isAd_loadPositions_thenLoadAds_hasAds() {
when(mockAdSource.dequeueAd()).thenReturn(stubNativeResponse);
subject.registerAdRenderer(mockAdRenderer);
subject.loadAds("test-ad-unit-id");
subject.handlePositioningLoad(positioning);
subject.handleAdsAvailable();
subject.setItemCount(4);
checkAdPositions(1, 3, 5);
}
@Test
public void getOriginalPosition_adjustsPositions() {
assertThat(subject.getOriginalPosition(0)).isEqualTo(0);
assertThat(subject.getOriginalPosition(1)).isEqualTo(1);
assertThat(subject.getOriginalPosition(2)).isEqualTo(2);
assertThat(subject.getOriginalPosition(3)).isEqualTo(3);
assertThat(subject.getOriginalPosition(4)).isEqualTo(4);
when(mockAdSource.dequeueAd()).thenReturn(stubNativeResponse);
subject.registerAdRenderer(mockAdRenderer);
subject.loadAds("test-ad-unit-id");
subject.handlePositioningLoad(positioning);
subject.handleAdsAvailable();
subject.setItemCount(4);
assertThat(subject.getOriginalPosition(0)).isEqualTo(0);
assertThat(subject.getOriginalPosition(1)).isEqualTo(PlacementData.NOT_FOUND);
assertThat(subject.getOriginalPosition(2)).isEqualTo(1);
assertThat(subject.getOriginalPosition(3)).isEqualTo(PlacementData.NOT_FOUND);
assertThat(subject.getOriginalPosition(4)).isEqualTo(2);
assertThat(subject.getOriginalPosition(5)).isEqualTo(PlacementData.NOT_FOUND);
assertThat(subject.getOriginalPosition(6)).isEqualTo(3);
assertThat(subject.getOriginalPosition(7)).isEqualTo(4);
assertThat(subject.getOriginalPosition(8)).isEqualTo(5);
assertThat(subject.getOriginalPosition(9)).isEqualTo(6);
}
@Test
public void getAdjustedPosition_adjustsPositions() {
assertThat(subject.getAdjustedPosition(0)).isEqualTo(0);
assertThat(subject.getAdjustedPosition(1)).isEqualTo(1);
assertThat(subject.getAdjustedPosition(2)).isEqualTo(2);
assertThat(subject.getAdjustedPosition(3)).isEqualTo(3);
assertThat(subject.getAdjustedPosition(4)).isEqualTo(4);
when(mockAdSource.dequeueAd()).thenReturn(stubNativeResponse);
subject.registerAdRenderer(mockAdRenderer);
subject.loadAds("test-ad-unit-id");
subject.handlePositioningLoad(positioning);
subject.handleAdsAvailable();
subject.setItemCount(4);
assertThat(subject.getAdjustedPosition(0)).isEqualTo(0);
assertThat(subject.getAdjustedPosition(1)).isEqualTo(2);
assertThat(subject.getAdjustedPosition(2)).isEqualTo(4);
assertThat(subject.getAdjustedPosition(3)).isEqualTo(6);
assertThat(subject.getAdjustedPosition(4)).isEqualTo(7);
assertThat(subject.getAdjustedPosition(5)).isEqualTo(8);
assertThat(subject.getAdjustedPosition(6)).isEqualTo(9);
assertThat(subject.getAdjustedPosition(7)).isEqualTo(10);
assertThat(subject.getAdjustedPosition(8)).isEqualTo(11);
assertThat(subject.getAdjustedPosition(9)).isEqualTo(12);
}
@Test
public void getOriginalCount_adjustsPositions() {
assertThat(subject.getOriginalCount(0)).isEqualTo(0);
assertThat(subject.getOriginalCount(1)).isEqualTo(1);
assertThat(subject.getOriginalCount(2)).isEqualTo(2);
assertThat(subject.getOriginalCount(3)).isEqualTo(3);
assertThat(subject.getOriginalCount(4)).isEqualTo(4);
when(mockAdSource.dequeueAd()).thenReturn(stubNativeResponse);
subject.registerAdRenderer(mockAdRenderer);
subject.loadAds("test-ad-unit-id");
subject.handlePositioningLoad(positioning);
subject.handleAdsAvailable();
subject.setItemCount(4);
assertThat(subject.getOriginalCount(0)).isEqualTo(0);
assertThat(subject.getOriginalCount(1)).isEqualTo(1);
assertThat(subject.getOriginalCount(2)).isEqualTo(PlacementData.NOT_FOUND);
assertThat(subject.getOriginalCount(3)).isEqualTo(2);
assertThat(subject.getOriginalCount(4)).isEqualTo(PlacementData.NOT_FOUND);
assertThat(subject.getOriginalCount(5)).isEqualTo(3);
assertThat(subject.getOriginalCount(6)).isEqualTo(PlacementData.NOT_FOUND);
assertThat(subject.getOriginalCount(7)).isEqualTo(4);
assertThat(subject.getOriginalCount(8)).isEqualTo(5);
assertThat(subject.getOriginalCount(9)).isEqualTo(6);
}
@Test
public void getAdjustedCount_adjustsPositions() {
assertThat(subject.getAdjustedCount(0)).isEqualTo(0);
assertThat(subject.getAdjustedCount(1)).isEqualTo(1);
assertThat(subject.getAdjustedCount(2)).isEqualTo(2);
assertThat(subject.getAdjustedCount(3)).isEqualTo(3);
assertThat(subject.getAdjustedCount(4)).isEqualTo(4);
when(mockAdSource.dequeueAd()).thenReturn(stubNativeResponse);
subject.registerAdRenderer(mockAdRenderer);
subject.loadAds("test-ad-unit-id");
subject.handlePositioningLoad(positioning);
subject.handleAdsAvailable();
subject.setItemCount(4);
assertThat(subject.getAdjustedCount(0)).isEqualTo(0);
assertThat(subject.getAdjustedCount(1)).isEqualTo(1);
assertThat(subject.getAdjustedCount(2)).isEqualTo(3);
assertThat(subject.getAdjustedCount(3)).isEqualTo(5);
assertThat(subject.getAdjustedCount(4)).isEqualTo(7);
assertThat(subject.getAdjustedCount(5)).isEqualTo(8);
assertThat(subject.getAdjustedCount(6)).isEqualTo(9);
assertThat(subject.getAdjustedCount(7)).isEqualTo(10);
assertThat(subject.getAdjustedCount(8)).isEqualTo(11);
assertThat(subject.getAdjustedCount(9)).isEqualTo(12);
}
@Test
public void placeAds_shouldCallListener() {
when(mockAdSource.dequeueAd()).thenReturn(stubNativeResponse);
subject.registerAdRenderer(mockAdRenderer);
subject.loadAds("test-ad-unit-id");
subject.handlePositioningLoad(positioning);
subject.handleAdsAvailable();
subject.setItemCount(4);
verify(mockAdLoadedListener, never()).onAdLoaded(0);
verify(mockAdLoadedListener).onAdLoaded(1);
verify(mockAdLoadedListener, never()).onAdLoaded(2);
verify(mockAdLoadedListener).onAdLoaded(3);
verify(mockAdLoadedListener, never()).onAdLoaded(4);
verify(mockAdLoadedListener).onAdLoaded(5);
verify(mockAdLoadedListener, never()).onAdLoaded(6);
verify(mockAdLoadedListener, never()).onAdLoaded(7);
}
@Test
public void placeAdsInRange_shouldPlaceAfter() {
when(mockAdSource.dequeueAd()).thenReturn(stubNativeResponse);
subject.registerAdRenderer(mockAdRenderer);
subject.loadAds("test-ad-unit-id");
subject.handlePositioningLoad(positioning);
subject.handleAdsAvailable();
Robolectric.getUiThreadScheduler().pause();
subject.setItemCount(100);
subject.placeAdsInRange(50, 50);
Robolectric.getUiThreadScheduler().advanceToLastPostedRunnable();
assertThat(subject.isAd(48)).isFalse();
assertThat(subject.isAd(49)).isFalse();
assertThat(subject.isAd(50)).isTrue();
assertThat(subject.isAd(51)).isFalse();
assertThat(subject.isAd(52)).isTrue();
assertThat(subject.isAd(53)).isFalse();
assertThat(subject.isAd(54)).isTrue();
}
@Test
public void placeAdsInRange_shouldCallListener() {
when(mockAdSource.dequeueAd()).thenReturn(stubNativeResponse);
subject.registerAdRenderer(mockAdRenderer);
subject.loadAds("test-ad-unit-id");
subject.handlePositioningLoad(positioning);
subject.handleAdsAvailable();
Robolectric.getUiThreadScheduler().pause();
subject.setItemCount(100);
subject.placeAdsInRange(50, 54);
Robolectric.getUiThreadScheduler().advanceToLastPostedRunnable();
verify(mockAdLoadedListener).onAdLoaded(50);
verify(mockAdLoadedListener, never()).onAdLoaded(51);
verify(mockAdLoadedListener).onAdLoaded(52);
verify(mockAdLoadedListener, never()).onAdLoaded(53);
verify(mockAdLoadedListener).onAdLoaded(54);
verify(mockAdLoadedListener, never()).onAdLoaded(55);
verify(mockAdLoadedListener).onAdLoaded(56);
}
@Test
public void placeAdsInRange_aboveItemCount_shouldNotInsert() {
when(mockAdSource.dequeueAd()).thenReturn(stubNativeResponse);
subject.registerAdRenderer(mockAdRenderer);
subject.loadAds("test-ad-unit-id");
subject.handlePositioningLoad(positioning);
subject.handleAdsAvailable();
Robolectric.getUiThreadScheduler().pause();
subject.setItemCount(0);
subject.placeAdsInRange(50, 54);
Robolectric.getUiThreadScheduler().advanceToLastPostedRunnable();
verify(mockAdLoadedListener, never()).onAdLoaded(50);
}
@Test
public void getAdView_withNoAds_returnsNull() {
assertThat(subject.getAdView(1, null, null)).isNull();
}
@Test
public void loadAds_shouldClearAds_afterFirstAdLoads() {
when(mockAdSource.dequeueAd()).thenReturn(stubNativeResponse);
subject.registerAdRenderer(mockAdRenderer);
subject.loadAds("test-ad-unit-id");
subject.handlePositioningLoad(positioning);
subject.handleAdsAvailable();
subject.setItemCount(2);
subject.placeAdsInRange(0, 1);
subject.loadAds("test-ad-unit-id");
// Ad should still exist until a new ad is available
checkAdPositions(1);
verify(mockAdLoadedListener, never()).onAdRemoved(anyInt());
// Once an ad is available, it should be immediately removed and replaced
subject.handleAdsAvailable();
subject.handlePositioningLoad(positioning);
verify(mockAdLoadedListener).onAdRemoved(1);
verify(mockAdLoadedListener, times(2)).onAdLoaded(1);
checkAdPositions(1);
}
@Test
public void loadAds_withFailingPositioningSource_shouldNotLoadAds() {
ArgumentCaptor<PositioningListener> listenerCaptor =
ArgumentCaptor.forClass(PositioningListener.class);
subject.registerAdRenderer(mockAdRenderer);
subject.loadAds("test-ad-unit-id");
verify(mockPositioningSource).loadPositions(
eq("test-ad-unit-id"), listenerCaptor.capture());
listenerCaptor.getValue().onFailed();
verify(mockAdLoadedListener, never()).onAdLoaded(anyInt());
}
@Test
public void destroy_shouldClearAdSource_shouldDestroyImpressionTracker_shouldDestroyResponse() {
when(mockAdSource.dequeueAd()).thenReturn(stubNativeResponse);
subject = new MoPubStreamAdPlacer(context,
mockAdSource, mockImpressionTracker, mockPositioningSource);
subject.registerAdRenderer(mockAdRenderer);
subject.loadAds("test-ad-unit-id");
subject.handlePositioningLoad(positioning);
subject.handleAdsAvailable();
subject.setItemCount(2); // Places 1 ad
subject.destroy();
verify(mockAdSource).clear();
verify(mockImpressionTracker).destroy();
verify(stubNativeResponse).destroy();
}
@Test
public void getAdView_withNullConvertView_callsRenderer_addsToImpressionTracker() {
View view = new View(context);
when(mockAdRenderer.createAdView(any(Context.class), any(ViewGroup.class)))
.thenReturn(view);
when(mockAdSource.dequeueAd()).thenReturn(stubNativeResponse);
subject.registerAdRenderer(mockAdRenderer);
subject.loadAds("test-ad-unit-id");
subject.handlePositioningLoad(positioning);
subject.handleAdsAvailable();
subject.setItemCount(100);
assertThat(subject.getAdView(1, null, null)).isEqualTo(view);
verify(mockAdRenderer).createAdView(any(Context.class), any(ViewGroup.class));
verify(mockAdRenderer).renderAdView(view, stubNativeResponse);
verify(mockImpressionTracker).addView(view, stubNativeResponse);
}
@Test
public void getAdView_withConvertView_callsRenderer_addsToImpressionTracker() {
View convertView = new View(context);
when(mockAdSource.dequeueAd()).thenReturn(stubNativeResponse);
subject.registerAdRenderer(mockAdRenderer);
subject.loadAds("test-ad-unit-id");
subject.handlePositioningLoad(positioning);
subject.handleAdsAvailable();
subject.setItemCount(4);
assertThat(subject.getAdView(1, convertView, null)).isEqualTo(convertView);
verify(mockAdRenderer, never()).createAdView(any(Context.class), any(ViewGroup.class));
verify(mockAdRenderer).renderAdView(convertView, stubNativeResponse);
verify(mockImpressionTracker).addView(convertView, stubNativeResponse);
}
@Test
public void getAdView_shouldRemoveViewFromImpressionTracker_shouldClearPreviousNativeResponse() throws Exception {
NativeResponse mockNativeResponse = mock(NativeResponse.class);
View mockView = mock(View.class);
when(mockAdSource.dequeueAd()).thenReturn(stubNativeResponse, mockNativeResponse, stubNativeResponse);
subject.registerAdRenderer(mockAdRenderer);
subject.loadAds("test-ad-unit-id");
subject.handlePositioningLoad(positioning);
subject.handleAdsAvailable();
subject.setItemCount(100);
subject.getAdView(1, mockView, null);
verify(mockImpressionTracker).removeView(mockView);
// Second call should clear the first NativeResponse
subject.getAdView(3, mockView, null);
verify(mockImpressionTracker, times(2)).removeView(mockView);
verify(stubNativeResponse).clear(mockView);
// Third call should clear the second NativeResponse
subject.getAdView(5, mockView, null);
verify(mockImpressionTracker, times(3)).removeView(mockView);
verify(mockNativeResponse).clear(mockView);
}
@Test
public void getAdView_withNetworkImpressionTracker_shouldNotAddViewToImpressionTracker_shouldPrepareNativeResponse() throws Exception {
View mockView = mock(View.class);
when(mockAdSource.dequeueAd()).thenReturn(stubNativeResponse);
subject.registerAdRenderer(mockAdRenderer);
subject.loadAds("test-ad-unit-id");
subject.handlePositioningLoad(positioning);
subject.handleAdsAvailable();
subject.setItemCount(100);
when(stubNativeResponse.isOverridingImpressionTracker()).thenReturn(true);
subject.getAdView(1, mockView, null);
verify(mockImpressionTracker, never()).addView(any(View.class), any(NativeResponse.class));
verify(stubNativeResponse).prepare(mockView);
}
@Test
public void getAdView_withoutNetworkImpressionTracker_shouldAddViewToImpressionTracker_shouldPrepareNativeResponse() throws Exception {
View mockView = mock(View.class);
when(mockAdSource.dequeueAd()).thenReturn(stubNativeResponse);
subject.registerAdRenderer(mockAdRenderer);
subject.loadAds("test-ad-unit-id");
subject.handlePositioningLoad(positioning);
subject.handleAdsAvailable();
subject.setItemCount(100);
when(stubNativeResponse.isOverridingImpressionTracker()).thenReturn(false);
subject.getAdView(1, mockView, null);
verify(mockImpressionTracker).addView(mockView, stubNativeResponse);
verify(stubNativeResponse).prepare(mockView);
}
@Test
public void destroy_shouldClearAdSource_shouldDestroyImpressionTracker_shouldResetPlacementData() {
when(mockAdSource.dequeueAd()).thenReturn(stubNativeResponse);
subject = new MoPubStreamAdPlacer(context,
mockAdSource, mockImpressionTracker, mockPositioningSource);
subject.registerAdRenderer(mockAdRenderer);
subject.loadAds("test-ad-unit-id");
subject.handlePositioningLoad(positioning);
subject.handleAdsAvailable();
subject.setItemCount(2); // Places 1 ad
subject.destroy();
verify(mockAdSource).clear();
verify(mockImpressionTracker).destroy();
verify(stubNativeResponse).destroy();
}
@Test
public void modifyClientPositioning_afterConstructingAdPlacer_shouldNotModifyAdPositions() {
when(mockAdSource.dequeueAd()).thenReturn(stubNativeResponse);
subject.registerAdRenderer(mockAdRenderer);
subject.loadAds("test-ad-unit-id");
subject.handlePositioningLoad(positioning);
positioning.enableRepeatingPositions(5);
subject.handleAdsAvailable();
subject.setItemCount(4);
checkAdPositions(1, 3, 5);
}
void checkAdPositions(Integer... positions) {
List<Integer> expected = Arrays.asList(positions);
List<Integer> actual = new ArrayList<Integer>();
for (int i = 0; i < 20; i++) {
if (subject.isAd(i)) {
actual.add(i);
assertThat(subject.getAdData(i)).isNotNull();
} else {
assertThat(subject.getAdData(i)).isNull();
}
}
assertThat(actual).isEqualTo(expected);
}
}