package com.mopub.nativeads; import android.app.Activity; import android.graphics.Bitmap; import com.mopub.common.test.support.SdkTestRunner; import com.mopub.nativeads.test.support.MoPubShadowBitmap; import com.mopub.nativeads.test.support.MoPubShadowDisplay; import com.mopub.network.MaxWidthImageLoader; import com.mopub.network.Networking; import com.mopub.volley.RequestQueue; import com.mopub.volley.VolleyError; import com.mopub.volley.toolbox.ImageLoader; import org.json.JSONArray; import org.json.JSONObject; 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.annotation.Config; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import static com.mopub.nativeads.NativeResponse.Parameter; import static com.mopub.nativeads.NativeResponse.Parameter.requiredKeys; import static com.mopub.volley.toolbox.ImageLoader.ImageListener; import static org.fest.assertions.api.Assertions.assertThat; import static org.fest.assertions.api.Assertions.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.stub; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(SdkTestRunner.class) @Config(shadows={MoPubShadowBitmap.class, MoPubShadowDisplay.class}) public class MoPubForwardingNativeAdTest { private JSONObject fakeJsonObject; private MoPubCustomEventNative.MoPubForwardingNativeAd subject; private Activity context; @Mock private CustomEventNative.CustomEventNativeListener mockCustomEventNativeListener; @Mock private RequestQueue mockRequestQueue; @Mock private MaxWidthImageLoader mockImageLoader; @Mock private ImageLoader.ImageContainer mockImageContainer; @Before public void setUp() throws Exception { context = new Activity(); fakeJsonObject = new JSONObject(); fakeJsonObject.put("imptracker", new JSONArray("[\"url1\", \"url2\"]")); fakeJsonObject.put("clktracker", "expected clicktracker"); Networking.setRequestQueueForTesting(mockRequestQueue); Networking.setImageLoaderForTesting(mockImageLoader); stub(mockImageContainer.getBitmap()).toReturn(mock(Bitmap.class)); } @Test public void parameter_requiredKeys_shouldOnlyContainTheRequiredKeys() throws Exception { final HashSet<String> expectedKeys = new HashSet<String>(); expectedKeys.add("imptracker"); expectedKeys.add("clktracker"); assertThat(requiredKeys).isEqualTo(expectedKeys); } @Test public void parameter_fromString_shouldReturnParameterOnMatch() throws Exception { final Parameter parameter = Parameter.from("title"); assertThat(parameter).isEqualTo(Parameter.TITLE); } @Test public void parameter_fromString_shouldReturnNullOnIllegalKey() throws Exception { final Parameter parameter = Parameter.from("random gibberish"); assertThat(parameter).isNull(); } @Test public void loadAd_whenMissingRequiredKeys_shouldThrowIllegalArgumentException() throws Exception { fakeJsonObject.remove("imptracker"); try { subject = new MoPubCustomEventNative.MoPubForwardingNativeAd(context, fakeJsonObject, mockCustomEventNativeListener); subject.loadAd(); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException e) { // pass } } @Test public void loadAd_whenRequiredKeyOfWrongType_shouldThrowIllegalArgumentException() throws Exception { fakeJsonObject.put("imptracker", 12345); try { subject = new MoPubCustomEventNative.MoPubForwardingNativeAd(context, fakeJsonObject, mockCustomEventNativeListener); subject.loadAd(); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException e) { // pass } } @Test public void loadAd_shouldSetRequiredExpectedFields() throws Exception { subject = new MoPubCustomEventNative.MoPubForwardingNativeAd(context, fakeJsonObject, mockCustomEventNativeListener); subject.loadAd(); assertThat(subject.getImpressionTrackers()).containsOnly("url1", "url2"); } @Test public void loadAd_shouldSetOptionalExpectedFields() throws Exception { fakeJsonObject.put("title", "expected title"); fakeJsonObject.put("text", "expected text"); fakeJsonObject.put("mainimage", "expected mainimage"); fakeJsonObject.put("iconimage", "expected iconimage"); fakeJsonObject.put("clk", "expected clk"); fakeJsonObject.put("fallback", "expected fallback"); fakeJsonObject.put("ctatext", "expected ctatext"); fakeJsonObject.put("starrating", 5.0); subject = new MoPubCustomEventNative.MoPubForwardingNativeAd(context, fakeJsonObject, mockCustomEventNativeListener); subject.loadAd(); assertThat(subject.getTitle()).isEqualTo("expected title"); assertThat(subject.getText()).isEqualTo("expected text"); assertThat(subject.getMainImageUrl()).isEqualTo("expected mainimage"); assertThat(subject.getIconImageUrl()).isEqualTo("expected iconimage"); assertThat(subject.getClickDestinationUrl()).isEqualTo("expected clk"); // assertThat(subject.getFallback()).isEqualTo("expected fallback"); assertThat(subject.getImpressionTrackers()).containsOnly("url1", "url2"); assertThat(subject.getCallToAction()).isEqualTo("expected ctatext"); assertThat(subject.getStarRating()).isEqualTo(5.0); } @Test public void loadAd_withIntegerStarRating_shouldSetStarRating() throws Exception { fakeJsonObject.put("starrating", 3); subject = new MoPubCustomEventNative.MoPubForwardingNativeAd(context, fakeJsonObject, mockCustomEventNativeListener); subject.loadAd(); assertThat(subject.getStarRating()).isEqualTo(3.0); } @Test public void loadAd_withStringStarRating_shouldSetStarRating() throws Exception { fakeJsonObject.put("starrating", "2.3"); subject = new MoPubCustomEventNative.MoPubForwardingNativeAd(context, fakeJsonObject, mockCustomEventNativeListener); subject.loadAd(); assertThat(subject.getStarRating()).isEqualTo(2.3); } @Test public void loadAd_withInvalidStringStarRating_shouldNotSetStarRating() throws Exception { fakeJsonObject.put("starrating", "this is not a number"); subject = new MoPubCustomEventNative.MoPubForwardingNativeAd(context, fakeJsonObject, mockCustomEventNativeListener); subject.loadAd(); assertThat(subject.getStarRating()).isNull(); } @Test public void loadAd_withInvalidlyTypedStarRating_shouldNotSetStarRating() throws Exception { fakeJsonObject.put("starrating", new Activity()); subject = new MoPubCustomEventNative.MoPubForwardingNativeAd(context, fakeJsonObject, mockCustomEventNativeListener); subject.loadAd(); assertThat(subject.getStarRating()).isNull(); } @Test public void loadAd_whenImpressionTrackersContainsNonStrings_willCoerceToString() throws Exception { /** * At this level of abstraction, we don't actually care that these Strings resolve to valid * URLs. We just want to ensure that loadAd does not throw an exception. */ final JSONArray impressionTrackers = new JSONArray(); impressionTrackers.put("url1"); impressionTrackers.put(JSONObject.NULL); impressionTrackers.put(2.12); fakeJsonObject.put("imptracker", impressionTrackers); subject = new MoPubCustomEventNative.MoPubForwardingNativeAd(context, fakeJsonObject, mockCustomEventNativeListener); subject.loadAd(); assertThat(subject.getImpressionTrackers()).containsOnly("url1", "null", "2.12"); } @Test public void loadAd_shouldSetExtraFields() throws Exception { List<Object> array = new ArrayList<Object>(); array.add("index1"); array.add(-10); Map<String, Object> map = new HashMap<String, Object>(); map.put("one", "a"); map.put("two", "b"); fakeJsonObject.put("key1", "yay json"); fakeJsonObject.put("key2", 5); fakeJsonObject.put("key3", new JSONArray(array)); fakeJsonObject.put("key4", new JSONObject(map)); subject = new MoPubCustomEventNative.MoPubForwardingNativeAd(context, fakeJsonObject, mockCustomEventNativeListener); subject.loadAd(); assertThat(subject.getExtra("key1")).isEqualTo("yay json"); assertThat(subject.getExtra("key2")).isEqualTo(5); assertThat((JSONArray) subject.getExtra("key3")).isEqualsToByComparingFields(new JSONArray(array)); assertThat((JSONObject) subject.getExtra("key4")).isEqualsToByComparingFields(new JSONObject(map)); } @Test public void loadAd_shouldDownloadImages_shouldNotifyListenerOfOnNativeAdLoaded() throws Exception { fakeJsonObject.put("mainimage", "mainimageurl"); fakeJsonObject.put("iconimage", "iconimageurl"); fakeJsonObject.put("extraimage", "extraimageurl"); when(mockImageLoader.get(anyString(), any(ImageListener.class))) .then(new Answer<Void>() { @Override public Void answer(final InvocationOnMock invocationOnMock) throws Throwable { ImageListener listener = ((ImageListener) invocationOnMock.getArguments()[1]); listener.onResponse(mockImageContainer, false); return null; } }); subject = new MoPubCustomEventNative.MoPubForwardingNativeAd(context, fakeJsonObject, mockCustomEventNativeListener); subject.loadAd(); verify(mockImageLoader).get(eq("mainimageurl"), any(ImageListener.class)); verify(mockImageLoader).get(eq("iconimageurl"), any(ImageListener.class)); verify(mockImageLoader).get(eq("extraimageurl"), any(ImageListener.class)); verify(mockCustomEventNativeListener).onNativeAdLoaded(subject); verify(mockCustomEventNativeListener, never()).onNativeAdFailed(any(NativeErrorCode.class)); } @Test public void loadAd_withFailedImageDownload_shouldNotDownloadImages_shouldNotifyListenerOfOnNativeAdFailed() throws Exception { fakeJsonObject.put("mainimage", "mainimageurl"); fakeJsonObject.put("iconimage", "iconimageurl"); fakeJsonObject.put("extraimage", "extraimageurl"); when(mockImageLoader.get(anyString(), any(ImageListener.class))) .then(new Answer<Void>() { @Override public Void answer(final InvocationOnMock invocationOnMock) throws Throwable { ImageListener listener = ((ImageListener) invocationOnMock.getArguments()[1]); listener.onErrorResponse(new VolleyError()); return null; } }); subject = new MoPubCustomEventNative.MoPubForwardingNativeAd(context, fakeJsonObject, mockCustomEventNativeListener); subject.loadAd(); verify(mockCustomEventNativeListener, never()).onNativeAdLoaded(subject); verify(mockCustomEventNativeListener).onNativeAdFailed(NativeErrorCode.IMAGE_DOWNLOAD_FAILURE); } @Test public void getExtrasImageUrls_whenExtrasContainsImages_shouldReturnImageUrls() throws Exception { // getExtrasImageUrls requires the key to end with a case-insensitive "image" to be counted as an image fakeJsonObject.put("test_image", "image_url_1"); fakeJsonObject.put("filler", "ignored"); fakeJsonObject.put("otherIMAGE", "image_url_2"); fakeJsonObject.put("more filler", "ignored"); fakeJsonObject.put("lastimage", "image_url_3"); subject = new MoPubCustomEventNative.MoPubForwardingNativeAd(context, fakeJsonObject, mockCustomEventNativeListener); subject.loadAd(); assertThat(subject.getExtrasImageUrls()).containsOnly("image_url_1", "image_url_2", "image_url_3"); } @Test public void getExtrasImageUrls_whenExtrasDoesNotContainImageKeys_shouldReturnEmptyList() throws Exception { // getExtrasImageUrls requires the key to end with a case-insensitive "image" to be counted as an image fakeJsonObject.put("imageAtFront", "ignored"); fakeJsonObject.put("middle_image_in_key", "ignored"); fakeJsonObject.put("other", "ignored"); subject = new MoPubCustomEventNative.MoPubForwardingNativeAd(context, fakeJsonObject, mockCustomEventNativeListener); subject.loadAd(); assertThat(subject.getExtrasImageUrls()).isEmpty(); } @Test public void getAllImageUrls_withExtraImagesAndMainAndIconImages_shouldReturnAllUrls() throws Exception { fakeJsonObject.put("mainimage", "mainImageUrl"); fakeJsonObject.put("iconimage", "iconImageUrl"); fakeJsonObject.put("extraimage", "extraImageUrl"); subject = new MoPubCustomEventNative.MoPubForwardingNativeAd(context, fakeJsonObject, mockCustomEventNativeListener); subject.loadAd(); assertThat(subject.getAllImageUrls()).containsOnly( "mainImageUrl", "iconImageUrl", "extraImageUrl" ); } @Test public void getAllImageUrls_withOnlyExtrasImages_shouldNotIncludeMainOrIconImages() throws Exception { fakeJsonObject.put("extra1_image", "expected extra1_image"); fakeJsonObject.put("extra2_image", "expected extra2_image"); subject = new MoPubCustomEventNative.MoPubForwardingNativeAd(context, fakeJsonObject, mockCustomEventNativeListener); subject.loadAd(); assertThat(subject.getAllImageUrls()).containsOnly("expected extra1_image", "expected extra2_image"); } }