package com.mopub.nativeads;
import android.app.Activity;
import android.database.DataSetObserver;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import com.mopub.common.test.support.SdkTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;
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.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@Config(manifest=Config.NONE)
@RunWith(SdkTestRunner.class)
public class MoPubAdAdapterTest {
private static final int AD_POSITION = 1;
@Mock
private MoPubStreamAdPlacer mockStreamAdPlacer;
@Mock
private NativeAdData mockNativeAdData;
@Mock
private View mockAdView;
@Mock
private VisibilityTracker mockVisibilityTracker;
@Mock
private MoPubNativeAdLoadedListener mockAdLoadedListener;
@Mock
private DataSetObserver mockDataSetObserver;
@Mock
private RequestParameters mockRequestParameters;
@Mock
private MoPubAdRenderer mockAdRenderer;
@Mock
private ListView mockListView;
@Mock
private OnItemClickListener mockOnItemClickListener;
@Mock
private OnItemLongClickListener mockOnItemLongClickListener;
@Mock
private OnItemSelectedListener mockOnItemSelectedListener;
@Mock
private View mockItemView;
private long originalItemId = 0;
private boolean originalHasStableIds = false;
private int originalItemViewType = 0;
private int originalViewTypeCount = 1;
private boolean originalItemsAreEnabled = false;
private ArrayAdapter<String> originalAdapter;
private MoPubAdAdapter subject;
@Before
public void setup() {
// Set up original adapter with 2 items
Activity activity = Robolectric.buildActivity(Activity.class).create().get();
originalAdapter = new ArrayAdapter<String>(activity, android.R.layout.simple_list_item_1) {
@Override
public boolean isEnabled(final int position) {
return originalItemsAreEnabled;
}
@Override
public long getItemId(final int position) {
return originalItemId;
}
@Override
public boolean hasStableIds() {
return originalHasStableIds;
}
@Override
public int getItemViewType(final int position) {
return originalItemViewType;
}
@Override
public int getViewTypeCount() {
return originalViewTypeCount;
}
};
originalAdapter.add("ITEM 1");
originalAdapter.add("ITEM 2");
subject = new MoPubAdAdapter(mockStreamAdPlacer, originalAdapter, mockVisibilityTracker);
// Reset because the constructor interacts with the stream ad placer, and we don't want
// to worry about verifying those changes in tests.
reset(mockStreamAdPlacer);
// Mock some simple adjustment behavior for tests. This is creating an ad placer that
// emulates a content item followed by an ad item, then another content item.
when(mockStreamAdPlacer.getAdData(AD_POSITION)).thenReturn(mockNativeAdData);
when(mockStreamAdPlacer.getAdView(eq(AD_POSITION), any(View.class), any(ViewGroup.class))).thenReturn(mockAdView);
when(mockStreamAdPlacer.isAd(anyInt())).thenAnswer(new Answer<Boolean>() {
@Override
public Boolean answer(final InvocationOnMock invocation) throws Throwable {
int position = (Integer)invocation.getArguments()[0];
return position == AD_POSITION;
}
});
when(mockStreamAdPlacer.getOriginalPosition(anyInt())).thenAnswer(new Answer<Integer>() {
@Override
public Integer answer(final InvocationOnMock invocation) throws Throwable {
int originalPosition = (Integer)invocation.getArguments()[0];
return originalPosition < AD_POSITION ? originalPosition : originalPosition - 1;
}
});
when(mockStreamAdPlacer.getAdViewType(anyInt())).thenAnswer(new Answer<Integer>() {
@Override
public Integer answer(final InvocationOnMock invocation) throws Throwable {
int originalPosition = (Integer)invocation.getArguments()[0];
return originalPosition == AD_POSITION ? 1 : MoPubStreamAdPlacer.CONTENT_VIEW_TYPE;
}
});
when(mockStreamAdPlacer.getAdjustedPosition(anyInt())).thenAnswer(new Answer<Integer>() {
@Override
public Integer answer(final InvocationOnMock invocation) throws Throwable {
int originalPosition = (Integer)invocation.getArguments()[0];
return originalPosition < AD_POSITION ? originalPosition : originalPosition + 1;
}
});
when(mockStreamAdPlacer.getAdjustedCount(anyInt())).thenAnswer(new Answer<Integer>() {
@Override
public Integer answer(final InvocationOnMock invocation) throws Throwable {
int originalCount = (Integer)invocation.getArguments()[0];
return originalCount < AD_POSITION ? originalCount : originalCount + 1;
}
});
}
@Test
public void originalAdapterChange_shouldNotifyDataSetChanged() {
subject.registerDataSetObserver(mockDataSetObserver);
originalAdapter.notifyDataSetChanged();
verify(mockDataSetObserver).onChanged();
}
@Test
public void originalAdapterInvalidated_shouldNotifyDataSetInvalidated() {
subject.registerDataSetObserver(mockDataSetObserver);
originalAdapter.notifyDataSetInvalidated();
verify(mockDataSetObserver).onInvalidated();
}
@Test
public void registerAdRenderer_shouldCallRegisterAdRendererOnAdPlacer() {
subject.registerAdRenderer(mockAdRenderer);
verify(mockStreamAdPlacer).registerAdRenderer(mockAdRenderer);
}
@Test
public void registerAdRenderer_withNull_shouldNotCallAdPlacer() {
subject.registerAdRenderer(null);
verify(mockStreamAdPlacer, never()).registerAdRenderer(any(MoPubAdRenderer.class));
}
@Test
public void setAdLoadedListener_handleAdLoaded_shouldCallCallback_shouldCallObserver() {
subject.setAdLoadedListener(mockAdLoadedListener);
subject.registerDataSetObserver(mockDataSetObserver);
subject.handleAdLoaded(8);
verify(mockAdLoadedListener).onAdLoaded(8);
verify(mockDataSetObserver).onChanged();
}
@Test
public void setAdLoadedListener_handleAdRemoved_shouldCallCallback_shouldCallObserver() {
subject.setAdLoadedListener(mockAdLoadedListener);
subject.registerDataSetObserver(mockDataSetObserver);
subject.handleAdRemoved(10);
verify(mockAdLoadedListener).onAdRemoved(10);
verify(mockDataSetObserver).onChanged();
}
@Test
public void loadAds_shouldCallLoadAdsOnAdPlacer() {
subject.loadAds("AD_UNIT_ID");
verify(mockStreamAdPlacer).loadAds("AD_UNIT_ID");
subject.loadAds("AD_UNIT_ID", mockRequestParameters);
verify(mockStreamAdPlacer).loadAds("AD_UNIT_ID", mockRequestParameters);
}
@Test
public void isAd_shouldCallIsAdOnAdPlacer() {
boolean isAd = subject.isAd(AD_POSITION);
assertThat(isAd).isTrue();
isAd = subject.isAd(AD_POSITION + 1);
assertThat(isAd).isFalse();
verify(mockStreamAdPlacer, times(2)).isAd(anyInt());
}
@Test
public void clearAds_shouldCallClearAdsOnAdPlacer() {
subject.clearAds();
verify(mockStreamAdPlacer).clearAds();
}
@Test
public void destroy_shouldDestroyStreamAdPlacer_shouldDestroyVisibilityTracker() {
subject.destroy();
verify(mockStreamAdPlacer).destroy();
verify(mockVisibilityTracker).destroy();
}
@Test
public void isEnabled_adPosition_shouldReturnTrue() {
boolean isEnabled = subject.isEnabled(AD_POSITION);
assertThat(isEnabled).isTrue();
}
@Test
public void isEnabled_withNonAdPosition_shouldUseOriginalAdapter() {
originalItemsAreEnabled = false;
boolean isEnabled = subject.isEnabled(AD_POSITION + 1);
assertThat(isEnabled).isFalse();
originalItemsAreEnabled = true;
isEnabled = subject.isEnabled(AD_POSITION + 1);
assertThat(isEnabled).isTrue();
}
@Test
public void getItem_withAdPosition_shouldReturnAd_shouldGetAdDataOnCallAdPlacer() {
Object actualItem = subject.getItem(AD_POSITION);
assertThat(actualItem).isEqualTo(mockNativeAdData);
verify(mockStreamAdPlacer).getAdData(AD_POSITION);
}
@Test
public void getItem_withNonAdPosition_shouldCallGetOriginalPositionOnAdPlacer() {
Object actualItem = subject.getItem(AD_POSITION + 1);
assertThat(actualItem).isNotEqualTo(mockNativeAdData);
verify(mockStreamAdPlacer).getOriginalPosition(AD_POSITION + 1);
}
@Test
public void getCount_shouldCallGetAdjustedCountOnAdPlacer() {
int actualCount = subject.getCount();
assertThat(actualCount).isEqualTo(3);
verify(mockStreamAdPlacer).getAdjustedCount(anyInt());
}
@Test
public void getItemId_withAdPosition_shouldBeNegative() {
long itemId = subject.getItemId(AD_POSITION);
assertThat(itemId).isLessThan(0);
}
@Test
public void getItemId_withNonAdPosition_shouldUseOriginalAdapterId() {
originalItemId = 42;
long itemId = subject.getItemId(AD_POSITION + 1);
assertThat(itemId).isEqualTo(42);
}
@Test
public void hasStableIds_shouldUseOriginalAdapterValue() {
originalHasStableIds = false;
boolean hasStableIds = subject.hasStableIds();
assertThat(hasStableIds).isFalse();
originalHasStableIds = true;
hasStableIds = subject.hasStableIds();
assertThat(hasStableIds).isTrue();
}
@Test
public void getView_withAdPosition_shouldReturnAdView_shouldTrackVisibility() {
View view = subject.getView(AD_POSITION, null, null);
assertThat(view).isEqualTo(mockAdView);
verify(mockVisibilityTracker).addView(eq(mockAdView), anyInt());
}
@Test
public void getView_withNonAdPosition_shouldOriginalAdapterView_shouldTrackVisibility() {
View view = subject.getView(AD_POSITION + 1, null, null);
assertThat(view).isNotEqualTo(mockAdView);
verify(mockVisibilityTracker).addView(any(View.class), anyInt());
}
@Test
public void getItemViewType_withAdPosition_shouldReturnOneGreaterThanViewType() {
originalItemViewType = 0;
int itemViewType = subject.getItemViewType(AD_POSITION);
assertThat(itemViewType).isEqualTo(originalItemViewType + 1);
}
@Test
public void getItemViewType_withNonAdPosition_shouldUseOriginalAdapterId() {
originalItemViewType = 0;
int itemViewType = subject.getItemViewType(AD_POSITION + 1);
assertThat(itemViewType).isEqualTo(originalItemViewType);
}
@Test
public void getViewTypeCount_shouldReturnOriginalViewTypeCountPlusOne() {
originalViewTypeCount = 1;
int viewTypeCount = subject.getViewTypeCount();
assertThat(viewTypeCount).isEqualTo(1);
originalViewTypeCount = 2;
viewTypeCount = subject.getViewTypeCount();
assertThat(viewTypeCount).isEqualTo(2);
}
@Test
public void isEmpty_shouldUseOriginalAdapterValue() {
boolean isEmpty = subject.isEmpty();
assertThat(isEmpty).isFalse();
originalAdapter.clear();
isEmpty = subject.isEmpty();
assertThat(isEmpty).isTrue();
}
@Test
public void getOriginalPosition_shouldCallStreamAdPlacer() {
subject.getOriginalPosition(5);
verify(mockStreamAdPlacer).getOriginalPosition(5);
}
@Test
public void getAdjustedPosition_shouldCallStreamAdPlacer() {
subject.getAdjustedPosition(5);
verify(mockStreamAdPlacer).getAdjustedPosition(5);
}
@Test
public void insertItem_shouldCallInsertItemOnStreamAdPlacer() {
subject.insertItem(5);
verify(mockStreamAdPlacer).insertItem(5);
}
@Test
public void removeItem_shouldCallRemoveItemOnStreamAdPlacer() {
subject.removeItem(5);
verify(mockStreamAdPlacer).removeItem(5);
}
@Test
public void setOnItemClickListener_withAdPosition_shouldNotCallListener() {
subject.setOnClickListener(mockListView, mockOnItemClickListener);
ArgumentCaptor<OnItemClickListener> listenerCaptor =
ArgumentCaptor.forClass(OnItemClickListener.class);
verify(mockListView).setOnItemClickListener(listenerCaptor.capture());
OnItemClickListener listener = listenerCaptor.getValue();
listener.onItemClick(mockListView, mockItemView, AD_POSITION, 0);
verify(mockOnItemClickListener, never()).onItemClick(
any(AdapterView.class), any(View.class), anyInt(), anyInt());
}
@Test
public void setOnItemClickListener_withNonAdPosition_shouldCallListener() {
subject.setOnClickListener(mockListView, mockOnItemClickListener);
ArgumentCaptor<OnItemClickListener> listenerCaptor =
ArgumentCaptor.forClass(OnItemClickListener.class);
verify(mockListView).setOnItemClickListener(listenerCaptor.capture());
OnItemClickListener listener = listenerCaptor.getValue();
listener.onItemClick(mockListView, mockItemView, AD_POSITION + 1, 0);
verify(mockOnItemClickListener).onItemClick(
mockListView, mockItemView, AD_POSITION, 0);
}
@Test
public void setOnItemLongClickListener_withAdPosition_shouldNotCallListener() {
subject.setOnItemLongClickListener(mockListView, mockOnItemLongClickListener);
ArgumentCaptor<OnItemLongClickListener> listenerCaptor =
ArgumentCaptor.forClass(OnItemLongClickListener.class);
verify(mockListView).setOnItemLongClickListener(listenerCaptor.capture());
OnItemLongClickListener listener = listenerCaptor.getValue();
listener.onItemLongClick(mockListView, mockItemView, AD_POSITION, 0);
verify(mockOnItemLongClickListener, never()).onItemLongClick(
any(AdapterView.class), any(View.class), anyInt(), anyInt());
}
@Test
public void setOnItemLongClickListener_withNonAdPosition_shouldCallListener() {
subject.setOnItemLongClickListener(mockListView, mockOnItemLongClickListener);
ArgumentCaptor<OnItemLongClickListener> listenerCaptor =
ArgumentCaptor.forClass(OnItemLongClickListener.class);
verify(mockListView).setOnItemLongClickListener(listenerCaptor.capture());
OnItemLongClickListener listener = listenerCaptor.getValue();
listener.onItemLongClick(mockListView, mockItemView, AD_POSITION + 1, 0);
verify(mockOnItemLongClickListener).onItemLongClick(
mockListView, mockItemView, AD_POSITION, 0);
}
@Test
public void setOnItemSelectedListener_withAdPosition_shouldNotCallListener() {
subject.setOnItemSelectedListener(mockListView, mockOnItemSelectedListener);
ArgumentCaptor<OnItemSelectedListener> listenerCaptor =
ArgumentCaptor.forClass(OnItemSelectedListener.class);
verify(mockListView).setOnItemSelectedListener(listenerCaptor.capture());
OnItemSelectedListener listener = listenerCaptor.getValue();
listener.onItemSelected(mockListView, mockItemView, AD_POSITION, 0);
verify(mockOnItemSelectedListener, never()).onItemSelected(
any(AdapterView.class), any(View.class), anyInt(), anyInt());
}
@Test
public void setOnItemSelectedListener_withNonAdPosition_shouldCallListener() {
subject.setOnItemSelectedListener(mockListView, mockOnItemSelectedListener);
ArgumentCaptor<OnItemSelectedListener> listenerCaptor =
ArgumentCaptor.forClass(OnItemSelectedListener.class);
verify(mockListView).setOnItemSelectedListener(listenerCaptor.capture());
OnItemSelectedListener listener = listenerCaptor.getValue();
listener.onItemSelected(mockListView, mockItemView, AD_POSITION + 1, 0);
verify(mockOnItemSelectedListener).onItemSelected(
mockListView, mockItemView, AD_POSITION, 0);
}
@Test
public void setSelection_shouldCallSetSelectionOnListView() {
subject.setSelection(mockListView, AD_POSITION);
// Since the original position is the ad position, the adjusted position is 1 higher
verify(mockListView).setSelection(AD_POSITION + 1);
}
@Test
public void smoothScrollToPosition_shouldCallSmooethScrollToPositionOnListView() {
subject.smoothScrollToPosition(mockListView, AD_POSITION);
// Since the original position is the ad position, the adjusted position is 1 higher
verify(mockListView).smoothScrollToPosition(AD_POSITION + 1);
}
@Test
public void refreshAds_shouldLoadAdsOnAdPlacer() {
when(mockListView.getAdapter()).thenReturn(subject);
subject.refreshAds(mockListView, "AD_UNIT_ID", mockRequestParameters);
verify(mockStreamAdPlacer).loadAds("AD_UNIT_ID", mockRequestParameters);
}
}