Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.chrome.browser.suggestions; | 5 package org.chromium.chrome.browser.suggestions; |
| 6 | 6 |
| 7 import static junit.framework.TestCase.assertNotNull; | 7 import static org.hamcrest.CoreMatchers.is; |
| 8 import static org.junit.Assert.assertFalse; | |
| 9 import static org.junit.Assert.assertNotNull; | |
| 10 import static org.junit.Assert.assertThat; | |
| 11 import static org.junit.Assert.fail; | |
| 12 import static org.mockito.ArgumentMatchers.any; | |
| 13 import static org.mockito.ArgumentMatchers.anyBoolean; | |
| 14 import static org.mockito.ArgumentMatchers.anyInt; | |
| 15 import static org.mockito.Mockito.doAnswer; | |
| 16 import static org.mockito.Mockito.mock; | |
| 17 import static org.mockito.Mockito.never; | |
| 18 import static org.mockito.Mockito.reset; | |
| 19 import static org.mockito.Mockito.verify; | |
| 20 import static org.mockito.Mockito.verifyNoMoreInteractions; | |
| 21 import static org.mockito.Mockito.when; | |
| 8 | 22 |
| 9 import static org.mockito.Mockito.inOrder; | 23 import android.content.Context; |
| 10 import static org.mockito.Mockito.mock; | |
| 11 import static org.mockito.Mockito.verify; | |
| 12 | |
| 13 import android.content.res.Resources; | 24 import android.content.res.Resources; |
| 25 import android.graphics.Bitmap; | |
| 26 import android.graphics.Color; | |
| 14 import android.support.annotation.ColorRes; | 27 import android.support.annotation.ColorRes; |
| 15 import android.support.annotation.DimenRes; | 28 import android.support.annotation.DimenRes; |
| 29 import android.support.annotation.LayoutRes; | |
| 30 import android.view.LayoutInflater; | |
| 31 import android.view.View; | |
| 32 import android.view.ViewGroup; | |
| 16 | 33 |
| 34 import org.hamcrest.CoreMatchers; | |
| 17 import org.junit.Before; | 35 import org.junit.Before; |
| 18 import org.junit.Rule; | 36 import org.junit.Rule; |
| 19 import org.junit.Test; | 37 import org.junit.Test; |
| 20 import org.junit.runner.RunWith; | 38 import org.junit.runner.RunWith; |
| 21 import org.mockito.InOrder; | 39 import org.mockito.ArgumentCaptor; |
| 22 import org.mockito.Mock; | 40 import org.mockito.Mock; |
| 23 import org.mockito.MockitoAnnotations; | 41 import org.mockito.MockitoAnnotations; |
| 42 import org.mockito.invocation.InvocationOnMock; | |
| 43 import org.mockito.stubbing.Answer; | |
| 24 import org.robolectric.RuntimeEnvironment; | 44 import org.robolectric.RuntimeEnvironment; |
| 25 import org.robolectric.annotation.Config; | 45 import org.robolectric.annotation.Config; |
| 46 import org.robolectric.annotation.Implementation; | |
| 26 import org.robolectric.annotation.Implements; | 47 import org.robolectric.annotation.Implements; |
| 27 import org.robolectric.annotation.RealObject; | 48 import org.robolectric.annotation.RealObject; |
| 28 import org.robolectric.shadows.ShadowResources; | 49 import org.robolectric.shadows.ShadowResources; |
| 29 | 50 |
| 30 import org.chromium.chrome.R; | 51 import org.chromium.chrome.R; |
| 31 import org.chromium.chrome.browser.ChromeFeatureList; | |
| 32 import org.chromium.chrome.browser.EnableFeatures; | 52 import org.chromium.chrome.browser.EnableFeatures; |
| 53 import org.chromium.chrome.browser.favicon.LargeIconBridge.LargeIconCallback; | |
| 33 import org.chromium.chrome.browser.ntp.ContextMenuManager; | 54 import org.chromium.chrome.browser.ntp.ContextMenuManager; |
| 34 import org.chromium.chrome.browser.ntp.cards.NodeParent; | 55 import org.chromium.chrome.browser.ntp.NTPTileSource; |
| 35 import org.chromium.chrome.browser.ntp.cards.SuggestionsSection; | |
| 36 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; | 56 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; |
| 37 import org.chromium.testing.local.LocalRobolectricTestRunner; | 57 import org.chromium.testing.local.LocalRobolectricTestRunner; |
| 38 | 58 |
| 59 import java.util.ArrayList; | |
| 60 | |
| 39 /** | 61 /** |
| 40 * Unit tests for {@link TileGroup}. | 62 * Unit tests for {@link TileGroup}. |
| 63 * TODO(dgn): Split some of the tests out to TileTest.java | |
| 64 * TODO(dgn): renderTileView tests don't work. Need to mock out TileView creatio n. | |
| 41 */ | 65 */ |
| 42 @RunWith(LocalRobolectricTestRunner.class) | 66 @RunWith(LocalRobolectricTestRunner.class) |
| 43 @Config(manifest = Config.NONE, shadows = {TileGroupTest.TileShadowResources.cla ss}) | 67 @Config(manifest = Config.NONE, |
| 68 shadows = {TileGroupTest.TileShadowResources.class, | |
| 69 TileGroupTest.ShadowLayoutInflater.class}) | |
| 44 public class TileGroupTest { | 70 public class TileGroupTest { |
| 45 private static final int MAX_TILES_TO_FETCH = 4; | 71 private static final int MAX_TILES_TO_FETCH = 4; |
| 46 private static final int TILE_TITLE_LINES = 1; | 72 private static final int TILE_TITLE_LINES = 1; |
| 47 private static final String[] URLS = {"https://www.google.com", "https://tel lmedadjokes.com"}; | 73 private static final String[] URLS = {"https://www.google.com", "https://tel lmedadjokes.com"}; |
| 48 | 74 |
| 49 @Rule | 75 @Rule |
| 50 public EnableFeatures.Processor mEnableFeatureProcessor = new EnableFeatures .Processor(); | 76 public EnableFeatures.Processor mEnableFeatureProcessor = new EnableFeatures .Processor(); |
| 51 | 77 |
| 52 @Mock | 78 @Mock |
| 53 private SuggestionsSection.Delegate mDelegate; | |
| 54 @Mock | |
| 55 private NodeParent mParent; | |
| 56 @Mock | |
| 57 private SuggestionsUiDelegate mUiDelegate; | |
| 58 @Mock | |
| 59 private TileGroup.Observer mTileGroupObserver; | 79 private TileGroup.Observer mTileGroupObserver; |
| 60 @Mock | |
| 61 private OfflinePageBridge mOfflinePageBridge; | |
| 62 | 80 |
| 63 private FakeTileGroupDelegate mTileGroupDelegate; | 81 private FakeTileGroupDelegate mTileGroupDelegate; |
| 64 | 82 |
| 65 @Before | 83 @Before |
| 66 public void setUp() { | 84 public void setUp() { |
| 67 MockitoAnnotations.initMocks(this); | 85 MockitoAnnotations.initMocks(this); |
| 68 | 86 |
| 69 mTileGroupDelegate = new FakeTileGroupDelegate(); | 87 mTileGroupDelegate = new FakeTileGroupDelegate(); |
| 70 } | 88 } |
| 71 | 89 |
| 72 @Test | 90 @Test |
| 73 @EnableFeatures({ChromeFeatureList.NTP_OFFLINE_PAGES_FEATURE_NAME}) | |
| 74 public void testInitialiseWithEmptyTileList() { | 91 public void testInitialiseWithEmptyTileList() { |
| 75 TileGroup tileGroup = new TileGroup(RuntimeEnvironment.application, mUiD elegate, | 92 TileGroup tileGroup = |
| 93 new TileGroup(RuntimeEnvironment.application, mock(SuggestionsUi Delegate.class), | |
| 94 mock(ContextMenuManager.class), mTileGroupDelegate, mTil eGroupObserver, | |
| 95 mock(OfflinePageBridge.class), TILE_TITLE_LINES); | |
| 96 tileGroup.startObserving(MAX_TILES_TO_FETCH); | |
| 97 notifyTileUrlsAvailable(); | |
| 98 | |
| 99 // The TileGroup.Observer methods should be called even though no tiles are added, which is | |
| 100 // an initialisation but not real state change. | |
| 101 verify(mTileGroupObserver).onTileCountChanged(); | |
| 102 verify(mTileGroupObserver).onLoadTaskCompleted(); | |
| 103 verify(mTileGroupObserver).onTileDataChanged(); | |
| 104 } | |
| 105 | |
| 106 @Test | |
| 107 public void testInitialiseWithTileList() { | |
| 108 TileGroup tileGroup = | |
| 109 new TileGroup(RuntimeEnvironment.application, mock(SuggestionsUi Delegate.class), | |
| 110 mock(ContextMenuManager.class), mTileGroupDelegate, mTil eGroupObserver, | |
| 111 mock(OfflinePageBridge.class), TILE_TITLE_LINES); | |
| 112 tileGroup.startObserving(MAX_TILES_TO_FETCH); | |
| 113 | |
| 114 notifyTileUrlsAvailable(URLS); | |
| 115 | |
| 116 verify(mTileGroupObserver).onTileCountChanged(); | |
| 117 verify(mTileGroupObserver).onLoadTaskCompleted(); | |
| 118 verify(mTileGroupObserver).onTileDataChanged(); | |
| 119 } | |
| 120 | |
| 121 @Test | |
| 122 public void testReceiveNewTilesWithoutChanges() { | |
| 123 TileGroup tileGroup = | |
| 124 new TileGroup(RuntimeEnvironment.application, mock(SuggestionsUi Delegate.class), | |
| 125 mock(ContextMenuManager.class), mTileGroupDelegate, mTil eGroupObserver, | |
| 126 mock(OfflinePageBridge.class), TILE_TITLE_LINES); | |
| 127 tileGroup.startObserving(MAX_TILES_TO_FETCH); | |
| 128 | |
| 129 // First initialisation | |
| 130 notifyTileUrlsAvailable(URLS); | |
| 131 reset(mTileGroupObserver); | |
| 132 | |
| 133 // Notify the same thing. No changes so|mTileGroupObserver| should not b e notified. | |
| 134 notifyTileUrlsAvailable(URLS); | |
| 135 verifyNoMoreInteractions(mTileGroupObserver); | |
| 136 } | |
| 137 | |
| 138 @Test | |
| 139 public void testReceiveNewTilesWithDataChanges() { | |
| 140 TileGroup tileGroup = | |
| 141 new TileGroup(RuntimeEnvironment.application, mock(SuggestionsUi Delegate.class), | |
| 142 mock(ContextMenuManager.class), mTileGroupDelegate, mTil eGroupObserver, | |
| 143 mock(OfflinePageBridge.class), TILE_TITLE_LINES); | |
| 144 tileGroup.startObserving(MAX_TILES_TO_FETCH); | |
| 145 | |
| 146 // First initialisation | |
| 147 notifyTileUrlsAvailable(URLS); | |
| 148 reset(mTileGroupObserver); | |
| 149 | |
| 150 // Notify the about different URLs, but the same number. #onTileCountCha nged() should not be | |
| 151 // called. | |
| 152 notifyTileUrlsAvailable("foo", "bar"); | |
| 153 verify(mTileGroupObserver, never()).onTileCountChanged(); // Tile count is still 2 | |
| 154 verify(mTileGroupObserver, never()).onLoadTaskCompleted(); // No load ta sk the second time. | |
| 155 verify(mTileGroupObserver).onTileDataChanged(); // Data DID change. | |
| 156 } | |
| 157 | |
| 158 @Test | |
| 159 public void testReceiveNewTilesWithCountChanges() { | |
| 160 TileGroup tileGroup = | |
| 161 new TileGroup(RuntimeEnvironment.application, mock(SuggestionsUi Delegate.class), | |
| 162 mock(ContextMenuManager.class), mTileGroupDelegate, mTil eGroupObserver, | |
| 163 mock(OfflinePageBridge.class), TILE_TITLE_LINES); | |
| 164 tileGroup.startObserving(MAX_TILES_TO_FETCH); | |
| 165 | |
| 166 // First initialisation | |
| 167 notifyTileUrlsAvailable(URLS); | |
| 168 reset(mTileGroupObserver); | |
| 169 | |
| 170 notifyTileUrlsAvailable(URLS[0]); | |
| 171 verify(mTileGroupObserver).onTileCountChanged(); // Tile count DID chang e. | |
| 172 verify(mTileGroupObserver, never()).onLoadTaskCompleted(); // No load ta sk the second time. | |
| 173 verify(mTileGroupObserver).onTileDataChanged(); // Data DID change. | |
| 174 } | |
| 175 | |
| 176 @Test | |
| 177 public void testRenderTileView() { | |
| 178 TileGroup tileGroup = | |
| 179 new TileGroup(RuntimeEnvironment.application, mock(SuggestionsUi Delegate.class), | |
| 180 mock(ContextMenuManager.class), mTileGroupDelegate, mTil eGroupObserver, | |
| 181 mock(OfflinePageBridge.class), TILE_TITLE_LINES); | |
| 182 tileGroup.startObserving(MAX_TILES_TO_FETCH); | |
| 183 TileGridLayoutWrapper layoutWrapper = new TileGridLayoutWrapper(); | |
| 184 | |
| 185 // Initialise the internal list of tiles | |
| 186 notifyTileUrlsAvailable(URLS); | |
| 187 | |
| 188 // Render them to the layout. | |
| 189 tileGroup.renderTileViews(layoutWrapper.getLayout(), false, false); | |
| 190 assertThat(layoutWrapper.size(), is(2)); | |
| 191 assertThat(((TileView) layoutWrapper.get(0)).getUrl(), is(URLS[0])); | |
| 192 assertThat(((TileView) layoutWrapper.get(1)).getUrl(), is(URLS[1])); | |
| 193 } | |
| 194 | |
| 195 @Test | |
| 196 public void testRenderTileViewReplacing() { | |
| 197 TileGroup tileGroup = | |
| 198 new TileGroup(RuntimeEnvironment.application, mock(SuggestionsUi Delegate.class), | |
| 199 mock(ContextMenuManager.class), mTileGroupDelegate, mTil eGroupObserver, | |
| 200 mock(OfflinePageBridge.class), TILE_TITLE_LINES); | |
| 201 tileGroup.startObserving(MAX_TILES_TO_FETCH); | |
| 202 notifyTileUrlsAvailable(URLS); | |
| 203 | |
| 204 // Initialise the layout with views whose URLs don't match the ones of t he new tiles. | |
| 205 TileGridLayoutWrapper layoutWrapper = new TileGridLayoutWrapper(); | |
| 206 TileView view1 = mock(TileView.class); | |
| 207 layoutWrapper.add(view1); | |
| 208 | |
| 209 TileView view2 = mock(TileView.class); | |
| 210 layoutWrapper.add(view2); | |
| 211 | |
| 212 // The tiles should be updated, the old ones removed. | |
| 213 tileGroup.renderTileViews(layoutWrapper.getLayout(), false, false); | |
| 214 assertThat(layoutWrapper.size(), is(2)); | |
| 215 assertFalse(layoutWrapper.contains(view1)); | |
| 216 assertFalse(layoutWrapper.contains(view2)); | |
| 217 verify(view1, never()).updateIfDataChanged(tileGroup.getTiles()[0]); | |
| 218 verify(view2, never()).updateIfDataChanged(tileGroup.getTiles()[1]); | |
| 219 } | |
| 220 | |
| 221 @Test | |
| 222 public void testRenderTileViewRecycling() { | |
| 223 TileGroup tileGroup = | |
| 224 new TileGroup(RuntimeEnvironment.application, mock(SuggestionsUi Delegate.class), | |
| 225 mock(ContextMenuManager.class), mTileGroupDelegate, mTil eGroupObserver, | |
| 226 mock(OfflinePageBridge.class), TILE_TITLE_LINES); | |
| 227 tileGroup.startObserving(MAX_TILES_TO_FETCH); | |
| 228 notifyTileUrlsAvailable(URLS); | |
| 229 | |
| 230 // Initialise the layout with views whose URLs match the ones of the new tiles. | |
| 231 TileGridLayoutWrapper layoutWrapper = new TileGridLayoutWrapper(); | |
| 232 TileView view1 = mock(TileView.class); | |
| 233 when(view1.getUrl()).thenReturn(URLS[0]); | |
| 234 layoutWrapper.add(view1); | |
| 235 | |
| 236 TileView view2 = mock(TileView.class); | |
| 237 when(view2.getUrl()).thenReturn(URLS[1]); | |
| 238 layoutWrapper.add(view2); | |
| 239 | |
| 240 // The tiles should be updated, the old ones reused. | |
| 241 tileGroup.renderTileViews(layoutWrapper.getLayout(), false, false); | |
| 242 assertThat(layoutWrapper.size(), is(2)); | |
| 243 assertThat(layoutWrapper.get(0), CoreMatchers.<View>is(view1)); | |
| 244 assertThat(layoutWrapper.get(1), CoreMatchers.<View>is(view2)); | |
| 245 verify(view1).updateIfDataChanged(tileGroup.getTiles()[0]); | |
| 246 verify(view2).updateIfDataChanged(tileGroup.getTiles()[1]); | |
| 247 } | |
| 248 | |
| 249 @Test | |
| 250 public void testIconLoading() { | |
| 251 SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class); | |
| 252 TileGroup tileGroup = new TileGroup(RuntimeEnvironment.application, uiDe legate, | |
| 76 mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupOb server, | 253 mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupOb server, |
| 77 mOfflinePageBridge, TILE_TITLE_LINES); | 254 mock(OfflinePageBridge.class), TILE_TITLE_LINES); |
| 78 tileGroup.startObserving(MAX_TILES_TO_FETCH); | 255 tileGroup.startObserving(MAX_TILES_TO_FETCH); |
| 79 notifyTileUrlsAvailable(); | 256 notifyTileUrlsAvailable(URLS); // Initialise the internal state to inclu de the test tile. |
| 80 | 257 reset(mTileGroupObserver); |
| 81 verify(mTileGroupObserver).onTileCountChanged(); | 258 Tile tile = tileGroup.getTiles()[0]; |
| 82 verify(mTileGroupObserver).onLoadTaskCompleted(); | 259 |
| 83 verify(mTileGroupObserver).onTileDataChanged(); | 260 TileGridLayoutWrapper layoutWrapper = new TileGridLayoutWrapper(); |
| 84 } | 261 tileGroup.buildTileView(tile, layoutWrapper.getLayout(), /* trackLoadTas k = */ true, |
| 85 | 262 /* condensed = */ false); |
| 86 @Test | 263 |
| 87 @EnableFeatures({ChromeFeatureList.NTP_OFFLINE_PAGES_FEATURE_NAME}) | 264 verify(mTileGroupObserver).onLoadTaskAdded(); |
| 88 public void testInitialiseWithTileList() { | 265 |
| 89 TileGroup tileGroup = new TileGroup(RuntimeEnvironment.application, mUiD elegate, | 266 ArgumentCaptor<LargeIconCallback> captor = ArgumentCaptor.forClass(Large IconCallback.class); |
| 267 verify(uiDelegate).getLargeIconForUrl(any(String.class), anyInt(), capto r.capture()); | |
| 268 for (LargeIconCallback cb : captor.getAllValues()) { | |
| 269 cb.onLargeIconAvailable(mock(Bitmap.class), Color.BLACK, /* isColorD efault = */ false); | |
| 270 } | |
| 271 | |
| 272 verify(mTileGroupObserver).onLoadTaskCompleted(); | |
| 273 verify(mTileGroupObserver).onTileIconChanged(tile); | |
| 274 } | |
| 275 | |
| 276 @Test | |
| 277 public void testIconLoadingNoTask() { | |
| 278 SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class); | |
| 279 TileGroup tileGroup = new TileGroup(RuntimeEnvironment.application, uiDe legate, | |
| 90 mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupOb server, | 280 mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupOb server, |
| 91 mOfflinePageBridge, TILE_TITLE_LINES); | 281 mock(OfflinePageBridge.class), TILE_TITLE_LINES); |
| 92 tileGroup.startObserving(MAX_TILES_TO_FETCH); | 282 tileGroup.startObserving(MAX_TILES_TO_FETCH); |
| 93 | 283 notifyTileUrlsAvailable(URLS); // Initialise the internal state to inclu de the test tile. |
| 94 notifyTileUrlsAvailable(URLS); | 284 reset(mTileGroupObserver); |
| 95 | 285 Tile tile = tileGroup.getTiles()[0]; |
| 96 InOrder inOrder = inOrder(mTileGroupObserver); | 286 |
| 97 inOrder.verify(mTileGroupObserver).onTileCountChanged(); | 287 TileGridLayoutWrapper layoutWrapper = new TileGridLayoutWrapper(); |
| 98 inOrder.verify(mTileGroupObserver).onLoadTaskCompleted(); | 288 tileGroup.buildTileView(tile, layoutWrapper.getLayout(), /* trackLoadTas k = */ false, |
| 99 inOrder.verify(mTileGroupObserver).onTileDataChanged(); | 289 /* condensed = */ false); |
| 100 inOrder.verifyNoMoreInteractions(); | 290 |
| 101 } | 291 verify(mTileGroupObserver, never()).onLoadTaskAdded(); |
| 102 | 292 |
| 293 ArgumentCaptor<LargeIconCallback> captor = ArgumentCaptor.forClass(Large IconCallback.class); | |
| 294 verify(uiDelegate).getLargeIconForUrl(any(String.class), anyInt(), capto r.capture()); | |
| 295 for (LargeIconCallback cb : captor.getAllValues()) { | |
| 296 cb.onLargeIconAvailable(mock(Bitmap.class), Color.BLACK, /* isColorD efault = */ false); | |
| 297 } | |
| 298 | |
| 299 verify(mTileGroupObserver, never()).onLoadTaskCompleted(); | |
| 300 verify(mTileGroupObserver).onTileIconChanged(tile); | |
| 301 } | |
| 302 | |
| 303 @Test | |
| 304 public void testIconLoadingWhenTileNotRegistered() { | |
| 305 SuggestionsUiDelegate uiDelegate = mock(SuggestionsUiDelegate.class); | |
| 306 TileGroup tileGroup = new TileGroup(RuntimeEnvironment.application, uiDe legate, | |
| 307 mock(ContextMenuManager.class), mTileGroupDelegate, mTileGroupOb server, | |
| 308 mock(OfflinePageBridge.class), TILE_TITLE_LINES); | |
| 309 tileGroup.startObserving(MAX_TILES_TO_FETCH); | |
| 310 reset(mTileGroupObserver); | |
| 311 Tile tile = new Tile("title", URLS[0], "", 0, NTPTileSource.POPULAR); | |
| 312 | |
| 313 TileGridLayoutWrapper layoutWrapper = new TileGridLayoutWrapper(); | |
| 314 tileGroup.buildTileView(tile, layoutWrapper.getLayout(), /* trackLoadTas k = */ true, | |
| 315 /* condensed = */ false); | |
| 316 verify(mTileGroupObserver).onLoadTaskAdded(); | |
| 317 | |
| 318 ArgumentCaptor<LargeIconCallback> captor = ArgumentCaptor.forClass(Large IconCallback.class); | |
| 319 verify(uiDelegate).getLargeIconForUrl(any(String.class), anyInt(), capto r.capture()); | |
| 320 captor.getValue().onLargeIconAvailable(mock(Bitmap.class), Color.BLACK, false); | |
| 321 | |
| 322 verify(mTileGroupObserver).onLoadTaskCompleted(); | |
| 323 verify(mTileGroupObserver, never()).onTileIconChanged(tile); | |
| 324 } | |
| 325 | |
| 326 /** | |
| 327 * Notifies the tile group of new tiles created from the provided URLs. Requ ires | |
| 328 * {@link TileGroup#startObserving(int)} to have been called on the tile gro up under test. | |
| 329 * @see TileGroup#onMostVisitedURLsAvailable(String[], String[], String[], i nt[]) | |
| 330 */ | |
| 103 private void notifyTileUrlsAvailable(String... urls) { | 331 private void notifyTileUrlsAvailable(String... urls) { |
| 104 assertNotNull("MostVisitedObserver has not been registered.", mTileGroup Delegate.mObserver); | 332 assertNotNull("MostVisitedObserver has not been registered.", mTileGroup Delegate.mObserver); |
| 105 | 333 |
| 106 String[] titles = urls.clone(); | 334 String[] titles = urls.clone(); |
| 107 String[] whitelistIconPaths = new String[urls.length]; | 335 String[] whitelistIconPaths = new String[urls.length]; |
| 108 int[] sources = new int[urls.length]; | 336 int[] sources = new int[urls.length]; |
| 109 for (int i = 0; i < urls.length; ++i) whitelistIconPaths[i] = ""; | 337 for (int i = 0; i < urls.length; ++i) whitelistIconPaths[i] = ""; |
| 110 mTileGroupDelegate.mObserver.onMostVisitedURLsAvailable( | 338 mTileGroupDelegate.mObserver.onMostVisitedURLsAvailable( |
| 111 titles, urls, whitelistIconPaths, sources); | 339 titles, urls, whitelistIconPaths, sources); |
| 112 } | 340 } |
| 113 | 341 |
| 342 /** | |
| 343 * Creates a mocked {@link TileView} that will return the URL of the tile it has been | |
| 344 * initialised with. | |
| 345 */ | |
| 346 private static TileView createMockTileView() { | |
| 347 final TileView tileView = mock(TileView.class); | |
| 348 doAnswer(new Answer<Void>() { | |
| 349 @Override | |
| 350 public Void answer(InvocationOnMock invocation) throws Throwable { | |
| 351 Tile tile = invocation.getArgument(0); | |
| 352 when(tileView.getUrl()).thenReturn(tile.getUrl()); | |
| 353 return null; | |
| 354 } | |
| 355 }) | |
| 356 .when(tileView) | |
| 357 .initialize(any(Tile.class), anyInt(), anyBoolean()); | |
| 358 return tileView; | |
| 359 } | |
| 360 | |
| 114 private class FakeTileGroupDelegate implements TileGroup.Delegate { | 361 private class FakeTileGroupDelegate implements TileGroup.Delegate { |
| 115 public MostVisitedSites.Observer mObserver; | 362 public MostVisitedSites.Observer mObserver; |
| 116 | 363 |
| 117 @Override | 364 @Override |
| 118 public void removeMostVisitedItem(Tile tile) {} | 365 public void removeMostVisitedItem(Tile tile) {} |
| 119 | 366 |
| 120 @Override | 367 @Override |
| 121 public void openMostVisitedItem(int windowDisposition, Tile tile) {} | 368 public void openMostVisitedItem(int windowDisposition, Tile tile) {} |
| 122 | 369 |
| 123 @Override | 370 @Override |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 146 if (id == R.dimen.tile_view_icon_size) return 2; | 393 if (id == R.dimen.tile_view_icon_size) return 2; |
| 147 return mRealResources.getDimensionPixelSize(id); | 394 return mRealResources.getDimensionPixelSize(id); |
| 148 } | 395 } |
| 149 | 396 |
| 150 @SuppressWarnings("deprecation") | 397 @SuppressWarnings("deprecation") |
| 151 public int getColor(@ColorRes int id) { | 398 public int getColor(@ColorRes int id) { |
| 152 if (id == R.color.default_favicon_background_color) return 2; | 399 if (id == R.color.default_favicon_background_color) return 2; |
| 153 return mRealResources.getColor(id); | 400 return mRealResources.getColor(id); |
| 154 } | 401 } |
| 155 } | 402 } |
| 403 | |
| 404 /** Intercepts calls to inflate views to replace them with mocks. */ | |
| 405 @Implements(LayoutInflater.class) | |
| 406 public static class ShadowLayoutInflater { | |
| 407 @Implementation | |
| 408 public static LayoutInflater from(Context context) { | |
| 409 LayoutInflater layoutInflater = mock(LayoutInflater.class); | |
| 410 when(layoutInflater.inflate(anyInt(), any(ViewGroup.class), anyBoole an())) | |
| 411 .thenAnswer(new Answer<View>() { | |
| 412 @Override | |
| 413 public View answer(InvocationOnMock invocation) throws T hrowable { | |
| 414 @LayoutRes | |
| 415 int layoutId = invocation.getArgument(0); | |
| 416 if (layoutId != R.layout.tile_view) fail("Unexpected resource id."); | |
| 417 return createMockTileView(); | |
| 418 } | |
| 419 }); | |
| 420 return layoutInflater; | |
| 421 } | |
| 422 } | |
| 423 | |
| 424 /** Allows substituting the layout object with a mock. */ | |
| 425 private static class TileGridLayoutWrapper extends ArrayList<View> { | |
| 426 private TileGridLayout mLayout; | |
| 427 | |
| 428 public TileGridLayoutWrapper() { | |
| 429 mLayout = mock(TileGridLayout.class); | |
|
Bernhard Bauer
2017/03/03 11:54:38
Can you use a custom subclass instead?
dgn
2017/03/03 13:48:30
Used FrameLayout instead.
| |
| 430 when(mLayout.getChildAt(anyInt())).thenAnswer(new Answer<View>() { | |
| 431 @Override | |
| 432 public View answer(InvocationOnMock invocation) throws Throwable { | |
| 433 int childIndex = invocation.getArgument(0); | |
| 434 return get(childIndex); | |
| 435 } | |
| 436 }); | |
| 437 when(mLayout.getChildCount()).thenAnswer(new Answer<Integer>() { | |
| 438 @Override | |
| 439 public Integer answer(InvocationOnMock invocation) throws Throwa ble { | |
| 440 return size(); | |
| 441 } | |
| 442 }); | |
| 443 doAnswer(new Answer<Void>() { | |
| 444 @Override | |
| 445 public Void answer(InvocationOnMock invocation) throws Throwable { | |
| 446 clear(); | |
| 447 return null; | |
| 448 } | |
| 449 }) | |
| 450 .when(mLayout) | |
| 451 .removeAllViews(); | |
| 452 doAnswer(new Answer<Void>() { | |
| 453 @Override | |
| 454 public Void answer(InvocationOnMock invocation) throws Throwable { | |
| 455 add(invocation.<View>getArgument(0)); | |
| 456 return null; | |
| 457 } | |
| 458 }) | |
| 459 .when(mLayout) | |
| 460 .addView(any(View.class)); | |
| 461 } | |
| 462 | |
| 463 public TileGridLayout getLayout() { | |
| 464 return mLayout; | |
| 465 } | |
| 466 } | |
| 156 } | 467 } |
| OLD | NEW |