package com.mopub.nativeads;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.mopub.common.test.support.SdkTestRunner;
import com.mopub.common.util.Utils;
import com.mopub.network.MaxWidthImageLoader;
import com.mopub.network.Networking;
import com.mopub.volley.RequestQueue;
import com.mopub.volley.toolbox.ImageLoader;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.stub;
import static org.mockito.Mockito.verify;
@RunWith(SdkTestRunner.class)
public class NativeViewHolderTest {
private Context context;
private RelativeLayout relativeLayout;
private ViewGroup viewGroup;
private NativeResponse nativeResponse;
private ViewBinder viewBinder;
private MoPubNative.MoPubNativeListener mopubNativeListener;
private TextView titleView;
private TextView textView;
private TextView callToActionView;
private ImageView mainImageView;
private ImageView iconImageView;
private TextView extrasTextView;
private ImageView extrasImageView;
private ImageView extrasImageView2;
private String mainImageUrl;
private String iconImageUrl;
private String mainImageData;
private String iconImageData;
private Bitmap iconImage;
private Bitmap mainImage;
private String extrasImageData;
private String extrasImageData2;
private Bitmap extrasImage2;
private Bitmap extrasImage;
private static final String IMPRESSION_URL = "http://example.com";
private static final String CLICK_URL = "http://test.com";
private static final String AD_UNIT_ID = "http://blah.org";
@Mock
private RequestQueue mockRequestQueue;
@Mock
private MaxWidthImageLoader mockImageLoader;
@Mock
private ImageLoader.ImageContainer mockImageContainer;
@Mock
private Bitmap mockBitmap;
@Captor
private ArgumentCaptor<ImageLoader.ImageListener> mainImageCaptor;
@Captor
private ArgumentCaptor<ImageLoader.ImageListener> iconImageCaptor;
@Before
public void setUp() throws Exception {
Networking.setRequestQueueForTesting(mockRequestQueue);
Networking.setImageLoaderForTesting(mockImageLoader);
context = new Activity();
relativeLayout = new RelativeLayout(context);
relativeLayout.setId((int) Utils.generateUniqueId());
viewGroup = new LinearLayout(context);
// Fields in the web ui
titleView = new TextView(context);
titleView.setId((int) Utils.generateUniqueId());
textView = new TextView(context);
textView.setId((int) Utils.generateUniqueId());
callToActionView = new Button(context);
callToActionView.setId((int) Utils.generateUniqueId());
mainImageView = new ImageView(context);
mainImageView.setId((int) Utils.generateUniqueId());
iconImageView = new ImageView(context);
iconImageView.setId((int) Utils.generateUniqueId());
// Extras
extrasTextView = new TextView(context);
extrasTextView.setId((int) Utils.generateUniqueId());
extrasImageView = new ImageView(context);
extrasImageView.setId((int) Utils.generateUniqueId());
extrasImageView2 = new ImageView(context);
extrasImageView2.setId((int) Utils.generateUniqueId());
relativeLayout.addView(titleView);
relativeLayout.addView(textView);
relativeLayout.addView(callToActionView);
relativeLayout.addView(mainImageView);
relativeLayout.addView(iconImageView);
relativeLayout.addView(extrasTextView);
relativeLayout.addView(extrasImageView);
relativeLayout.addView(extrasImageView2);
mainImageUrl = "mainimageurl";
iconImageUrl = "iconimageurl";
mainImageData = "mainimagedata";
iconImageData = "iconimagedata";
extrasImageData = "extrasimagedata";
extrasImageData2 = "extrasimagedata2";
iconImage = BitmapFactory.decodeByteArray(iconImageData.getBytes(), 0, iconImageData.getBytes().length);
mainImage = BitmapFactory.decodeByteArray(mainImageData.getBytes(), 0, mainImageData.getBytes().length);
extrasImage = BitmapFactory.decodeByteArray(extrasImageData.getBytes(), 0, extrasImageData.getBytes().length);
extrasImage2 = BitmapFactory.decodeByteArray(extrasImageData2.getBytes(), 0, extrasImageData2.getBytes().length);
}
@Test
public void fromViewBinder_shouldPopulateClassFields() throws Exception {
viewBinder = new ViewBinder.Builder(relativeLayout.getId())
.titleId(titleView.getId())
.textId(textView.getId())
.callToActionId(callToActionView.getId())
.mainImageId(mainImageView.getId())
.iconImageId(iconImageView.getId())
.build();
NativeViewHolder nativeViewHolder =
NativeViewHolder.fromViewBinder(relativeLayout, viewBinder);
assertThat(nativeViewHolder.titleView).isEqualTo(titleView);
assertThat(nativeViewHolder.textView).isEqualTo(textView);
assertThat(nativeViewHolder.callToActionView).isEqualTo(callToActionView);
assertThat(nativeViewHolder.mainImageView).isEqualTo(mainImageView);
assertThat(nativeViewHolder.iconImageView).isEqualTo(iconImageView);
}
@Test
public void fromViewBinder_withSubsetOfFields_shouldLeaveOtherFieldsNull() throws Exception {
viewBinder = new ViewBinder.Builder(relativeLayout.getId())
.titleId(titleView.getId())
.iconImageId(iconImageView.getId())
.build();
NativeViewHolder nativeViewHolder =
NativeViewHolder.fromViewBinder(relativeLayout, viewBinder);
assertThat(nativeViewHolder.titleView).isEqualTo(titleView);
assertThat(nativeViewHolder.textView).isNull();
assertThat(nativeViewHolder.callToActionView).isNull();
assertThat(nativeViewHolder.mainImageView).isNull();
assertThat(nativeViewHolder.iconImageView).isEqualTo(iconImageView);
}
@Test
public void fromViewBinder_withNonExistantIds_shouldLeaveFieldsNull() throws Exception {
viewBinder = new ViewBinder.Builder(relativeLayout.getId())
.titleId((int) Utils.generateUniqueId())
.textId((int) Utils.generateUniqueId())
.callToActionId((int) Utils.generateUniqueId())
.mainImageId((int) Utils.generateUniqueId())
.iconImageId((int) Utils.generateUniqueId())
.build();
NativeViewHolder nativeViewHolder =
NativeViewHolder.fromViewBinder(relativeLayout, viewBinder);
assertThat(nativeViewHolder.titleView).isNull();
assertThat(nativeViewHolder.textView).isNull();
assertThat(nativeViewHolder.callToActionView).isNull();
assertThat(nativeViewHolder.mainImageView).isNull();
assertThat(nativeViewHolder.iconImageView).isNull();
}
@Test
public void update_shouldAddValuesToViews() throws Exception {
// Setup for cache state for image gets
BaseForwardingNativeAd nativeAd = new BaseForwardingNativeAd() {};
nativeAd.setTitle("titletext");
nativeAd.setText("texttext");
nativeAd.setMainImageUrl("mainimageurl");
nativeAd.setIconImageUrl("iconimageurl");
nativeAd.setCallToAction("cta");
nativeResponse = new NativeResponse(context,
IMPRESSION_URL, CLICK_URL, AD_UNIT_ID, nativeAd, null);
viewBinder = new ViewBinder.Builder(relativeLayout.getId())
.titleId(titleView.getId())
.textId(textView.getId())
.callToActionId(callToActionView.getId())
.mainImageId(mainImageView.getId())
.iconImageId(iconImageView.getId())
.build();
NativeViewHolder nativeViewHolder =
NativeViewHolder.fromViewBinder(relativeLayout, viewBinder);
nativeViewHolder.update(nativeResponse);
assertThat(titleView.getText()).isEqualTo("titletext");
assertThat(textView.getText()).isEqualTo("texttext");
assertThat(callToActionView.getText()).isEqualTo("cta");
verify(mockImageLoader).get(eq("mainimageurl"), mainImageCaptor.capture());
verify(mockImageLoader).get(eq("iconimageurl"), iconImageCaptor.capture());
stub(mockImageContainer.getBitmap()).toReturn(mockBitmap);
mainImageCaptor.getValue().onResponse(mockImageContainer, true);
iconImageCaptor.getValue().onResponse(mockImageContainer, true);
assertThat(((BitmapDrawable) mainImageView.getDrawable()).getBitmap()).isEqualTo(mockBitmap);
assertThat(((BitmapDrawable) iconImageView.getDrawable()).getBitmap()).isEqualTo(mockBitmap);
}
@Test
public void update_withMissingNativeResponseFields_shouldClearPreviousValues() throws Exception {
// Set previous values that should be cleared
titleView.setText("previoustitletext");
textView.setText("previoustexttext");
callToActionView.setText("previousctatext");
mainImageView.setImageBitmap(BitmapFactory.decodeByteArray("previousmainimagedata".getBytes(), 0, "previousmainimagedata".getBytes().length));
iconImageView.setImageBitmap(BitmapFactory.decodeByteArray("previousiconimagedata".getBytes(), 0, "previousiconimagedata".getBytes().length));
// Only required fields in native response
nativeResponse = new NativeResponse(context,
IMPRESSION_URL, CLICK_URL, AD_UNIT_ID, mock(BaseForwardingNativeAd.class), null);
viewBinder = new ViewBinder.Builder(relativeLayout.getId())
.titleId(titleView.getId())
.textId(textView.getId())
.callToActionId(callToActionView.getId())
.mainImageId(mainImageView.getId())
.iconImageId(iconImageView.getId())
.build();
NativeViewHolder nativeViewHolder =
NativeViewHolder.fromViewBinder(relativeLayout, viewBinder);
nativeViewHolder.update(nativeResponse);
assertThat(titleView.getText()).isEqualTo("");
assertThat(textView.getText()).isEqualTo("");
assertThat(callToActionView.getText()).isEqualTo("");
assertThat(mainImageView.getDrawable()).isNull();
assertThat(iconImageView.getDrawable()).isNull();
}
@Test
public void update_withDifferentViewBinder_shouldNotClearPreviousValues() throws Exception {
// Set previous values that should be cleared
titleView.setText("previoustitletext");
textView.setText("previoustexttext");
BaseForwardingNativeAd nativeAd = new BaseForwardingNativeAd() {};
nativeAd.setCallToAction("cta");
nativeResponse = new NativeResponse(context,
IMPRESSION_URL, CLICK_URL, AD_UNIT_ID, nativeAd, null);
viewBinder = new ViewBinder.Builder(relativeLayout.getId())
.callToActionId(callToActionView.getId())
.build();
NativeViewHolder nativeViewHolder =
NativeViewHolder.fromViewBinder(relativeLayout, viewBinder);
nativeViewHolder.update(nativeResponse);
assertThat(titleView.getText()).isEqualTo("previoustitletext");
assertThat(textView.getText()).isEqualTo("previoustexttext");
assertThat(callToActionView.getText()).isEqualTo("cta");
}
@Test
public void updateExtras_shouldAddValuesToViews() throws Exception {
// Setup for cache state for image gets
BaseForwardingNativeAd nativeAd = new BaseForwardingNativeAd() {};
nativeAd.addExtra("extrastext", "extrastexttext");
nativeAd.addExtra("extrasimage", "extrasimageurl");
nativeAd.addExtra("extrasimage2", "extrasimageurl2");
nativeResponse = new NativeResponse(context,
IMPRESSION_URL, CLICK_URL, AD_UNIT_ID, nativeAd, null);
viewBinder = new ViewBinder.Builder(relativeLayout.getId())
.addExtra("extrastext", extrasTextView.getId())
.addExtra("extrasimage", extrasImageView.getId())
.addExtra("extrasimage2", extrasImageView2.getId())
.build();
NativeViewHolder nativeViewHolder =
NativeViewHolder.fromViewBinder(relativeLayout, viewBinder);
nativeViewHolder.updateExtras(relativeLayout, nativeResponse, viewBinder);
assertThat(extrasTextView.getText()).isEqualTo("extrastexttext");
verify(mockImageLoader).get(eq("extrasimageurl"), mainImageCaptor.capture());
verify(mockImageLoader).get(eq("extrasimageurl2"), iconImageCaptor.capture());
stub(mockImageContainer.getBitmap()).toReturn(mockBitmap);
mainImageCaptor.getValue().onResponse(mockImageContainer, true);
iconImageCaptor.getValue().onResponse(mockImageContainer, true);
assertThat(((BitmapDrawable) extrasImageView.getDrawable()).getBitmap()).isEqualTo(mockBitmap);
assertThat(((BitmapDrawable) extrasImageView2.getDrawable()).getBitmap()).isEqualTo(mockBitmap);
}
@Test
public void updateExtras_withMissingExtrasValues_shouldClearPreviousValues() throws Exception {
extrasTextView.setText("previousextrastext");
extrasImageView.setImageBitmap(BitmapFactory.decodeByteArray("previousextrasimagedata".getBytes(), 0, "previousextrasimagedata".getBytes().length));
extrasImageView2.setImageBitmap(BitmapFactory.decodeByteArray("previousextrasimagedata2".getBytes(), 0, "previousextrasimagedata2".getBytes().length));
nativeResponse = new NativeResponse(context,
IMPRESSION_URL, CLICK_URL, AD_UNIT_ID, new BaseForwardingNativeAd(){}, null);
viewBinder = new ViewBinder.Builder(relativeLayout.getId())
.addExtra("extrastext", extrasTextView.getId())
.addExtra("extrasimage", extrasImageView.getId())
.addExtra("extrasimage2", extrasImageView2.getId())
.build();
NativeViewHolder nativeViewHolder =
NativeViewHolder.fromViewBinder(relativeLayout, viewBinder);
assertThat(extrasTextView.getText()).isEqualTo("previousextrastext");
nativeViewHolder.updateExtras(relativeLayout, nativeResponse, viewBinder);
assertThat(extrasTextView.getText()).isEqualTo("");
assertThat(extrasImageView.getDrawable()).isNull();
assertThat(extrasImageView2.getDrawable()).isNull();
}
@Test
public void updateExtras_withMismatchingViewTypes_shouldSetTextViewToImageUrlAndSetExtrasImageViewToNull() throws Exception {
BaseForwardingNativeAd nativeAd = new BaseForwardingNativeAd() {};
nativeAd.addExtra("extrastext", "extrastexttext");
nativeAd.addExtra("extrasimage", "extrasimageurl");
nativeResponse = new NativeResponse(context,
IMPRESSION_URL, CLICK_URL, AD_UNIT_ID, nativeAd, null);
viewBinder = new ViewBinder.Builder(relativeLayout.getId())
.addExtra("extrastext", extrasImageView.getId())
.addExtra("extrasimage", extrasTextView.getId())
.build();
NativeViewHolder nativeViewHolder =
NativeViewHolder.fromViewBinder(relativeLayout, viewBinder);
assertThat(extrasTextView.getText()).isEqualTo("");
assertThat(extrasImageView.getDrawable()).isNull();
nativeViewHolder.updateExtras(relativeLayout, nativeResponse, viewBinder);
// Volley's imageloader will set this to a bitmapdrawable with no bitmap
assertThat(extrasTextView.getText()).isEqualTo("extrasimageurl");
assertThat(extrasImageView.getDrawable()).isNull();
}
public void fromViewBinder_withMixedViewTypes_shouldReturnEmptyViewHolder() throws Exception {
viewBinder = new ViewBinder.Builder(relativeLayout.getId())
.titleId(mainImageView.getId())
.textId(textView.getId())
.build();
NativeViewHolder nativeViewHolder =
NativeViewHolder.fromViewBinder(relativeLayout, viewBinder);
assertThat(nativeViewHolder).isEqualTo(NativeViewHolder.EMPTY_VIEW_HOLDER);
}
}