Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(390)

Unified Diff: chrome/android/junit/src/org/chromium/chrome/browser/suggestions/TileGroupTest.java

Issue 2722243002: 📰 Add tests for Tile and TileGroup (Closed)
Patch Set: Revert default Rule effect Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/junit/src/org/chromium/chrome/browser/suggestions/TileGroupTest.java
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/TileGroupTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/TileGroupTest.java
index fc193a783bc4aff78d0d5a57c500662d7fffbe7b..045a9a02162397a75537910ec170d774815dace3 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/TileGroupTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/TileGroupTest.java
@@ -4,35 +4,54 @@
package org.chromium.chrome.browser.suggestions;
-import static junit.framework.TestCase.assertNotNull;
-
-import static org.mockito.Mockito.inOrder;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Color;
import android.support.annotation.ColorRes;
import android.support.annotation.DimenRes;
+import android.support.annotation.LayoutRes;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import org.hamcrest.CoreMatchers;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.InOrder;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.RealObject;
import org.robolectric.shadows.ShadowResources;
import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.EnableFeatures;
+import org.chromium.chrome.browser.favicon.LargeIconBridge.LargeIconCallback;
import org.chromium.chrome.browser.ntp.ContextMenuManager;
-import org.chromium.chrome.browser.ntp.cards.NodeParent;
-import org.chromium.chrome.browser.ntp.cards.SuggestionsSection;
+import org.chromium.chrome.browser.ntp.NTPTileSource;
import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
import org.chromium.testing.local.LocalRobolectricTestRunner;
@@ -40,7 +59,10 @@
* Unit tests for {@link TileGroup}.
*/
@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {TileGroupTest.TileShadowResources.class})
+@Config(manifest = Config.NONE,
+ shadows = {TileGroupTest.TileShadowResources.class,
+ TileGroupTest.ShadowLayoutInflater.class})
+@EnableFeatures({})
public class TileGroupTest {
private static final int MAX_TILES_TO_FETCH = 4;
private static final int TILE_TITLE_LINES = 1;
@@ -50,15 +72,7 @@
public EnableFeatures.Processor mEnableFeatureProcessor = new EnableFeatures.Processor();
@Mock
- private SuggestionsSection.Delegate mDelegate;
- @Mock
- private NodeParent mParent;
- @Mock
- private SuggestionsUiDelegate mUiDelegate;
- @Mock
private TileGroup.Observer mTileGroupObserver;
- @Mock
- private OfflinePageBridge mOfflinePageBridge;
private FakeTileGroupDelegate mTileGroupDelegate;
@@ -70,36 +84,243 @@ public void setUp() {
}
@Test
- @EnableFeatures({ChromeFeatureList.NTP_OFFLINE_PAGES_FEATURE_NAME})
public void testInitialiseWithEmptyTileList() {
- TileGroup tileGroup = new TileGroup(RuntimeEnvironment.application, mUiDelegate,
- mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver,
- mOfflinePageBridge, TILE_TITLE_LINES);
+ TileGroup tileGroup =
+ new TileGroup(RuntimeEnvironment.application, mock(SuggestionsUiDelegate.class),
+ mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver,
+ mock(OfflinePageBridge.class), TILE_TITLE_LINES);
tileGroup.startObserving(MAX_TILES_TO_FETCH);
notifyTileUrlsAvailable();
+ // The TileGroup.Observer methods should be called even though no tiles are added, which is
+ // an initialisation but not real state change.
verify(mTileGroupObserver).onTileCountChanged();
verify(mTileGroupObserver).onLoadTaskCompleted();
verify(mTileGroupObserver).onTileDataChanged();
}
@Test
- @EnableFeatures({ChromeFeatureList.NTP_OFFLINE_PAGES_FEATURE_NAME})
public void testInitialiseWithTileList() {
- TileGroup tileGroup = new TileGroup(RuntimeEnvironment.application, mUiDelegate,
- mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver,
- mOfflinePageBridge, TILE_TITLE_LINES);
+ TileGroup tileGroup =
+ new TileGroup(RuntimeEnvironment.application, mock(SuggestionsUiDelegate.class),
+ mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver,
+ mock(OfflinePageBridge.class), TILE_TITLE_LINES);
+ tileGroup.startObserving(MAX_TILES_TO_FETCH);
+
+ notifyTileUrlsAvailable(URLS);
+
+ verify(mTileGroupObserver).onTileCountChanged();
+ verify(mTileGroupObserver).onLoadTaskCompleted();
+ verify(mTileGroupObserver).onTileDataChanged();
+ }
+
+ @Test
+ public void testReceiveNewTilesWithoutChanges() {
+ TileGroup tileGroup =
+ new TileGroup(RuntimeEnvironment.application, mock(SuggestionsUiDelegate.class),
+ mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver,
+ mock(OfflinePageBridge.class), TILE_TITLE_LINES);
+ tileGroup.startObserving(MAX_TILES_TO_FETCH);
+
+ // First initialisation
+ notifyTileUrlsAvailable(URLS);
+ reset(mTileGroupObserver);
+
+ // Notify the same thing. No changes so|mTileGroupObserver| should not be notified.
+ notifyTileUrlsAvailable(URLS);
+ verifyNoMoreInteractions(mTileGroupObserver);
+ }
+
+ @Test
+ public void testReceiveNewTilesWithDataChanges() {
+ TileGroup tileGroup =
+ new TileGroup(RuntimeEnvironment.application, mock(SuggestionsUiDelegate.class),
+ mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver,
+ mock(OfflinePageBridge.class), TILE_TITLE_LINES);
+ tileGroup.startObserving(MAX_TILES_TO_FETCH);
+
+ // First initialisation
+ notifyTileUrlsAvailable(URLS);
+ reset(mTileGroupObserver);
+
+ // Notify the about different URLs, but the same number. #onTileCountChanged() should not be
+ // called.
+ notifyTileUrlsAvailable("foo", "bar");
+ verify(mTileGroupObserver, never()).onTileCountChanged(); // Tile count is still 2
+ verify(mTileGroupObserver, never()).onLoadTaskCompleted(); // No load task the second time.
+ verify(mTileGroupObserver).onTileDataChanged(); // Data DID change.
+ }
+
+ @Test
+ public void testReceiveNewTilesWithCountChanges() {
+ TileGroup tileGroup =
+ new TileGroup(RuntimeEnvironment.application, mock(SuggestionsUiDelegate.class),
+ mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver,
+ mock(OfflinePageBridge.class), TILE_TITLE_LINES);
+ tileGroup.startObserving(MAX_TILES_TO_FETCH);
+
+ // First initialisation
+ notifyTileUrlsAvailable(URLS);
+ reset(mTileGroupObserver);
+
+ notifyTileUrlsAvailable(URLS[0]);
+ verify(mTileGroupObserver).onTileCountChanged(); // Tile count DID change.
+ verify(mTileGroupObserver, never()).onLoadTaskCompleted(); // No load task the second time.
+ verify(mTileGroupObserver).onTileDataChanged(); // Data DID change.
+ }
+
+ @Test
+ public void testRenderTileView() {
+ TileGroup tileGroup =
+ new TileGroup(RuntimeEnvironment.application, mock(SuggestionsUiDelegate.class),
+ mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver,
+ mock(OfflinePageBridge.class), TILE_TITLE_LINES);
+ tileGroup.startObserving(MAX_TILES_TO_FETCH);
+ ViewGroup layout = new FrameLayout(RuntimeEnvironment.application, null);
+
+ // Initialise the internal list of tiles
+ notifyTileUrlsAvailable(URLS);
+
+ // Render them to the layout.
+ tileGroup.renderTileViews(layout, false, false);
+ assertThat(layout.getChildCount(), is(2));
+ assertThat(((TileView) layout.getChildAt(0)).getUrl(), is(URLS[0]));
+ assertThat(((TileView) layout.getChildAt(1)).getUrl(), is(URLS[1]));
+ }
+
+ @Test
+ public void testRenderTileViewReplacing() {
+ TileGroup tileGroup =
+ new TileGroup(RuntimeEnvironment.application, mock(SuggestionsUiDelegate.class),
+ mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver,
+ mock(OfflinePageBridge.class), TILE_TITLE_LINES);
tileGroup.startObserving(MAX_TILES_TO_FETCH);
+ notifyTileUrlsAvailable(URLS);
+
+ // Initialise the layout with views whose URLs don't match the ones of the new tiles.
+ ViewGroup layout = new FrameLayout(RuntimeEnvironment.application, null);
+ TileView view1 = mock(TileView.class);
+ layout.addView(view1);
+ TileView view2 = mock(TileView.class);
+ layout.addView(view2);
+
+ // The tiles should be updated, the old ones removed.
+ tileGroup.renderTileViews(layout, false, false);
+ assertThat(layout.getChildCount(), is(2));
+ assertThat(layout.indexOfChild(view1), is(-1));
+ assertThat(layout.indexOfChild(view2), is(-1));
+ verify(view1, never()).updateIfDataChanged(tileGroup.getTiles()[0]);
+ verify(view2, never()).updateIfDataChanged(tileGroup.getTiles()[1]);
+ }
+
+ @Test
+ public void testRenderTileViewRecycling() {
+ TileGroup tileGroup =
+ new TileGroup(RuntimeEnvironment.application, mock(SuggestionsUiDelegate.class),
+ mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver,
+ mock(OfflinePageBridge.class), TILE_TITLE_LINES);
+ tileGroup.startObserving(MAX_TILES_TO_FETCH);
notifyTileUrlsAvailable(URLS);
- InOrder inOrder = inOrder(mTileGroupObserver);
- inOrder.verify(mTileGroupObserver).onTileCountChanged();
- inOrder.verify(mTileGroupObserver).onLoadTaskCompleted();
- inOrder.verify(mTileGroupObserver).onTileDataChanged();
- inOrder.verifyNoMoreInteractions();
+ // Initialise the layout with views whose URLs match the ones of the new tiles.
+ ViewGroup layout = new FrameLayout(RuntimeEnvironment.application, null);
+ TileView view1 = mock(TileView.class);
+ when(view1.getUrl()).thenReturn(URLS[0]);
+ layout.addView(view1);
+
+ TileView view2 = mock(TileView.class);
+ when(view2.getUrl()).thenReturn(URLS[1]);
+ layout.addView(view2);
+
+ // The tiles should be updated, the old ones reused.
+ tileGroup.renderTileViews(layout, false, false);
+ assertThat(layout.getChildCount(), is(2));
+ assertThat(layout.getChildAt(0), CoreMatchers.<View>is(view1));
+ assertThat(layout.getChildAt(1), CoreMatchers.<View>is(view2));
+ verify(view1).updateIfDataChanged(tileGroup.getTiles()[0]);
+ verify(view2).updateIfDataChanged(tileGroup.getTiles()[1]);
+ }
+
+ @Test
+ public void testIconLoading() {
+ SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class);
+ TileGroup tileGroup = new TileGroup(RuntimeEnvironment.application, uiDelegate,
+ mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver,
+ mock(OfflinePageBridge.class), TILE_TITLE_LINES);
+ tileGroup.startObserving(MAX_TILES_TO_FETCH);
+ notifyTileUrlsAvailable(URLS); // Initialise the internal state to include the test tile.
+ reset(mTileGroupObserver);
+ Tile tile = tileGroup.getTiles()[0];
+
+ ViewGroup layout = new FrameLayout(RuntimeEnvironment.application, null);
+ tileGroup.buildTileView(tile, layout, /* trackLoadTask = */ true, /* condensed = */ false);
+
+ verify(mTileGroupObserver).onLoadTaskAdded();
+
+ ArgumentCaptor<LargeIconCallback> captor = ArgumentCaptor.forClass(LargeIconCallback.class);
+ verify(uiDelegate).getLargeIconForUrl(any(String.class), anyInt(), captor.capture());
+ for (LargeIconCallback cb : captor.getAllValues()) {
+ cb.onLargeIconAvailable(mock(Bitmap.class), Color.BLACK, /* isColorDefault = */ false);
+ }
+
+ verify(mTileGroupObserver).onLoadTaskCompleted();
+ verify(mTileGroupObserver).onTileIconChanged(tile);
+ }
+
+ @Test
+ public void testIconLoadingNoTask() {
+ SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class);
+ TileGroup tileGroup = new TileGroup(RuntimeEnvironment.application, uiDelegate,
+ mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver,
+ mock(OfflinePageBridge.class), TILE_TITLE_LINES);
+ tileGroup.startObserving(MAX_TILES_TO_FETCH);
+ notifyTileUrlsAvailable(URLS); // Initialise the internal state to include the test tile.
+ reset(mTileGroupObserver);
+ Tile tile = tileGroup.getTiles()[0];
+
+ ViewGroup layout = new FrameLayout(RuntimeEnvironment.application, null);
+ tileGroup.buildTileView(tile, layout, /* trackLoadTask = */ false, /* condensed = */ false);
+
+ verify(mTileGroupObserver, never()).onLoadTaskAdded();
+
+ ArgumentCaptor<LargeIconCallback> captor = ArgumentCaptor.forClass(LargeIconCallback.class);
+ verify(uiDelegate).getLargeIconForUrl(any(String.class), anyInt(), captor.capture());
+ for (LargeIconCallback cb : captor.getAllValues()) {
+ cb.onLargeIconAvailable(mock(Bitmap.class), Color.BLACK, /* isColorDefault = */ false);
+ }
+
+ verify(mTileGroupObserver, never()).onLoadTaskCompleted();
+ verify(mTileGroupObserver).onTileIconChanged(tile);
+ }
+
+ @Test
+ public void testIconLoadingWhenTileNotRegistered() {
+ SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class);
+ TileGroup tileGroup = new TileGroup(RuntimeEnvironment.application, uiDelegate,
+ mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupObserver,
+ mock(OfflinePageBridge.class), TILE_TITLE_LINES);
+ tileGroup.startObserving(MAX_TILES_TO_FETCH);
+ reset(mTileGroupObserver);
+ Tile tile = new Tile("title", URLS[0], "", 0, NTPTileSource.POPULAR);
+
+ ViewGroup layout = new FrameLayout(RuntimeEnvironment.application, null);
+ tileGroup.buildTileView(tile, layout, /* trackLoadTask = */ true, /* condensed = */ false);
+ verify(mTileGroupObserver).onLoadTaskAdded();
+
+ ArgumentCaptor<LargeIconCallback> captor = ArgumentCaptor.forClass(LargeIconCallback.class);
+ verify(uiDelegate).getLargeIconForUrl(any(String.class), anyInt(), captor.capture());
+ captor.getValue().onLargeIconAvailable(mock(Bitmap.class), Color.BLACK, false);
+
+ verify(mTileGroupObserver).onLoadTaskCompleted();
+ verify(mTileGroupObserver, never()).onTileIconChanged(tile);
}
+ /**
+ * Notifies the tile group of new tiles created from the provided URLs. Requires
+ * {@link TileGroup#startObserving(int)} to have been called on the tile group under test.
+ * @see TileGroup#onMostVisitedURLsAvailable(String[], String[], String[], int[])
+ */
private void notifyTileUrlsAvailable(String... urls) {
assertNotNull("MostVisitedObserver has not been registered.", mTileGroupDelegate.mObserver);
@@ -111,6 +332,25 @@ private void notifyTileUrlsAvailable(String... urls) {
titles, urls, whitelistIconPaths, sources);
}
+ /**
+ * Creates a mocked {@link TileView} that will return the URL of the tile it has been
+ * initialised with.
+ */
+ private static TileView createMockTileView() {
+ final TileView tileView = mock(TileView.class);
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Tile tile = invocation.getArgument(0);
+ when(tileView.getUrl()).thenReturn(tile.getUrl());
+ return null;
+ }
+ })
+ .when(tileView)
+ .initialize(any(Tile.class), anyInt(), anyBoolean());
+ return tileView;
+ }
+
private class FakeTileGroupDelegate implements TileGroup.Delegate {
public MostVisitedSites.Observer mObserver;
@@ -140,17 +380,38 @@ public void destroy() {}
*/
@Implements(Resources.class)
public static class TileShadowResources extends ShadowResources {
- @RealObject
- private Resources mRealResources;
+ @Implementation
public int getDimensionPixelSize(@DimenRes int id) {
- if (id == R.dimen.tile_view_icon_size) return 2;
- return mRealResources.getDimensionPixelSize(id);
+ if (id == R.dimen.tile_view_icon_size) return 48;
+
+ throw new IllegalArgumentException();
}
- @SuppressWarnings("deprecation")
+ @Implementation
public int getColor(@ColorRes int id) {
- if (id == R.color.default_favicon_background_color) return 2;
- return mRealResources.getColor(id);
+ if (id == R.color.default_favicon_background_color) return Color.BLACK;
+
+ throw new IllegalArgumentException();
+ }
+ }
+
+ /** Intercepts calls to inflate views to replace them with mocks. */
+ @Implements(LayoutInflater.class)
+ public static class ShadowLayoutInflater {
+ @Implementation
+ public static LayoutInflater from(Context context) {
+ LayoutInflater layoutInflater = mock(LayoutInflater.class);
+ when(layoutInflater.inflate(anyInt(), any(ViewGroup.class), anyBoolean()))
+ .thenAnswer(new Answer<View>() {
+ @Override
+ public View answer(InvocationOnMock invocation) throws Throwable {
+ @LayoutRes
+ int layoutId = invocation.getArgument(0);
+ if (layoutId != R.layout.tile_view) fail("Unexpected resource id.");
+ return createMockTileView();
+ }
+ });
+ return layoutInflater;
}
}
}
« no previous file with comments | « chrome/android/java_sources.gni ('k') | chrome/android/junit/src/org/chromium/chrome/browser/suggestions/TileTest.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698