1a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev/* 2a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev * Copyright (C) 2015 The Android Open Source Project 3a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev * 4a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev * Licensed under the Apache License, Version 2.0 (the "License"); 5a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev * you may not use this file except in compliance with the License. 6a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev * You may obtain a copy of the License at 7a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev * 8a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev * http://www.apache.org/licenses/LICENSE-2.0 9a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev * 10a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev * Unless required by applicable law or agreed to in writing, software 11a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev * distributed under the License is distributed on an "AS IS" BASIS, 12a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev * See the License for the specific language governing permissions and 14a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev * limitations under the License. 15a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev */ 16a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 17a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheevpackage android.support.v7.util; 18a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport org.junit.After; 200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport org.junit.Before; 210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport org.junit.Test; 220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport org.junit.runner.RunWith; 230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport org.junit.runners.JUnit4; 240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 25de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheevimport android.support.annotation.UiThread; 26f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyarimport android.test.suitebuilder.annotation.MediumTest; 27a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheevimport android.util.SparseBooleanArray; 28a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 29a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheevimport java.util.concurrent.CountDownLatch; 30a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheevimport java.util.concurrent.TimeUnit; 310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static org.junit.Assert.*; 32a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 33f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar@MediumTest 340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar@RunWith(JUnit4.class) 35de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheevpublic class AsyncListUtilTest extends BaseThreadedTest { 36a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 37a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private static final int TILE_SIZE = 10; 38a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 39a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private TestDataCallback mDataCallback; 40a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private TestViewCallback mViewCallback; 41a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 42a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev AsyncListUtil<String> mAsyncListUtil; 43a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Before 450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public final void setupCallbacks() throws Exception { 46a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mDataCallback = new TestDataCallback(); 47a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mViewCallback = new TestViewCallback(); 48a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mDataCallback.expectTiles(0, 10, 20); 49de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev super.setUp(); 50a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mDataCallback.waitForTiles("initial load"); 51a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 52a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 53de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev @Override 54de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev @UiThread 55de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev protected void setUpUi() { 56de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev mAsyncListUtil = new AsyncListUtil<String>( 57de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev String.class, TILE_SIZE, mDataCallback, mViewCallback); 58de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev } 59de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev 600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @After 61a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void tearDown() throws Exception { 62a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev /// Wait a little extra to catch spurious messages. 63a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev new CountDownLatch(1).await(500, TimeUnit.MILLISECONDS); 64a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 65a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void withNoPreload() throws Throwable { 68a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev scrollAndExpectTiles(10, "scroll to 10", 30); 69a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev scrollAndExpectTiles(25, "scroll to 25", 40); 70a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev scrollAndExpectTiles(45, "scroll to 45", 50, 60); 71a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev scrollAndExpectTiles(70, "scroll to 70", 70, 80, 90); 72a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 73a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void withPreload() throws Throwable { 76a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mViewCallback.mStartPreload = 5; 77a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mViewCallback.mEndPreload = 15; 78a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev scrollAndExpectTiles(50, "scroll down a lot", 40, 50, 60, 70, 80); 79a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 80a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mViewCallback.mStartPreload = 0; 81a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mViewCallback.mEndPreload = 0; 82a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev scrollAndExpectTiles(60, "scroll down a little, no new tiles loaded"); 83a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev scrollAndExpectTiles(40, "scroll up a little, no new tiles loaded"); 84a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 85a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void tileCaching() throws Throwable { 88a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev scrollAndExpectTiles(25, "next screen", 30, 40); 89a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 90a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev scrollAndExpectTiles(0, "back at top, no new page loads"); 91a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev scrollAndExpectTiles(25, "next screen again, no new page loads"); 92a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 93a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mDataCallback.mCacheSize = 3; 94a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev scrollAndExpectTiles(50, "scroll down more, all pages should load", 50, 60, 70); 95a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev scrollAndExpectTiles(0, "scroll back to top, all pages should reload", 0, 10, 20); 96a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 97a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void dataRefresh() throws Throwable { 100a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mViewCallback.expectDataSetChanged(40); 101a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mDataCallback.expectTiles(0, 10, 20); 102de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev refreshOnUiThread(); 103a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mViewCallback.waitForDataSetChanged("increasing item count"); 104a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mDataCallback.waitForTiles("increasing item count"); 105a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 106a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mViewCallback.expectDataSetChanged(15); 107a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mDataCallback.expectTiles(0, 10); 108de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev refreshOnUiThread(); 109a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mViewCallback.waitForDataSetChanged("decreasing item count"); 110a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mDataCallback.waitForTiles("decreasing item count"); 111a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 112a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 1130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 1140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void itemChanged() throws Throwable { 115a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev final int position = 30; 116a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev final int count = 20; 117a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 118de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev assertLoadedItemsOnUiThread("no new items should be loaded", 0, position, count); 119a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 120a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mViewCallback.expectItemRangeChanged(position, count); 121a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev scrollAndExpectTiles(20, "scrolling to missing items", 30, 40); 122a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mViewCallback.waitForItems(); 123a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 124de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev assertLoadedItemsOnUiThread("all new items should be loaded", count, position, count); 125a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 126a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 127de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev @UiThread 128a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private int getLoadedItemCount(int startPosition, int itemCount) { 129a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev int loaded = 0; 130a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev for (int i = 0; i < itemCount; i++) { 131a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev if (mAsyncListUtil.getItem(startPosition + i) != null) { 132a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev loaded++; 133a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 134a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 135a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev return loaded; 136a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 137a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 138a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private void scrollAndExpectTiles(int position, String context, int... positions) 139de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev throws Throwable { 140a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mDataCallback.expectTiles(positions); 141de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev scrollOnUiThread(position); 142a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mDataCallback.waitForTiles(context); 143a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 144a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 145a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private static void waitForLatch(String context, CountDownLatch latch) 146a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev throws InterruptedException { 147a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev assertTrue("timed out waiting for " + context, latch.await(1, TimeUnit.SECONDS)); 148a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 149a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 150de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev private void refreshOnUiThread() throws Throwable { 151de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev runTestOnUiThread(new Runnable() { 152de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev @Override 153de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev public void run() { 154de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev mAsyncListUtil.refresh(); 155de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev } 156de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev }); 157de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev } 158de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev 159de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev private void assertLoadedItemsOnUiThread(final String message, 160de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev final int expectedCount, 161de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev final int position, 162de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev final int count) throws Throwable { 163de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev runTestOnUiThread(new Runnable() { 164de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev @Override 165de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev public void run() { 166de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev assertEquals(message, expectedCount, getLoadedItemCount(position, count)); 167de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev } 168de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev }); 169de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev } 170de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev 171de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev private void scrollOnUiThread(final int position) throws Throwable { 172de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev runTestOnUiThread(new Runnable() { 173de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev @Override 174de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev public void run() { 175de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev mViewCallback.scrollTo(position); 176de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev } 177de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev }); 178de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev } 179de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev 180a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private class TestDataCallback extends AsyncListUtil.DataCallback<String> { 181a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private int mCacheSize = 10; 182a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 183a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev int mDataItemCount = 100; 184a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 185a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev final PositionSetLatch mTilesFilledLatch = new PositionSetLatch("filled"); 186a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 187a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev @Override 188a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void fillData(String[] data, int startPosition, int itemCount) { 189a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev synchronized (mTilesFilledLatch) { 190a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev assertEquals(Math.min(TILE_SIZE, mDataItemCount - startPosition), itemCount); 191a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mTilesFilledLatch.countDown(startPosition); 192a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 193a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev for (int i = 0; i < itemCount; i++) { 194a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev data[i] = "item #" + startPosition; 195a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 196a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 197a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 198a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev @Override 199a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public int refreshData() { 200a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev return mDataItemCount; 201a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 202a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 203a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public int getMaxCachedTiles() { 204a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev return mCacheSize; 205a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 206a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 207a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void expectTiles(int... positions) { 208a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev synchronized (mTilesFilledLatch) { 209a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mTilesFilledLatch.expect(positions); 210a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 211a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 212a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 213a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private void waitForTiles(String context) throws InterruptedException { 214a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev waitForLatch("filled tiles (" + context + ")", mTilesFilledLatch.mLatch); 215a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 216a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 217a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 218a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private class TestViewCallback extends AsyncListUtil.ViewCallback { 219a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public static final int VIEWPORT_SIZE = 25; 220a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private int mStartPreload; 221a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private int mEndPreload; 222a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 223a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev int mFirstVisibleItem; 224a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev int mLastVisibleItem = VIEWPORT_SIZE - 1; 225a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 226a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private int mExpectedItemCount; 227a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev CountDownLatch mDataRefreshLatch; 228a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 229a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev PositionSetLatch mItemsChangedLatch = new PositionSetLatch("item changed"); 230a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 231a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev @Override 232a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void getItemRangeInto(int[] outRange) { 233a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev outRange[0] = mFirstVisibleItem; 234a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev outRange[1] = mLastVisibleItem; 235a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 236a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 237a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev @Override 238a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void extendRangeInto(int[] range, int[] outRange, int scrollHint) { 239a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev outRange[0] = range[0] - mStartPreload; 240a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev outRange[1] = range[1] + mEndPreload; 241a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 242a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 243a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev @Override 244de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev @UiThread 245a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void onDataRefresh() { 246a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev if (mDataRefreshLatch == null) { 247a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev return; 248a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 249a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev assertTrue("unexpected onDataRefresh notification", mDataRefreshLatch.getCount() == 1); 250a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev assertEquals(mExpectedItemCount, mAsyncListUtil.getItemCount()); 251a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mDataRefreshLatch.countDown(); 252a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev updateViewport(); 253a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 254a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 255a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev @Override 256a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void onItemLoaded(int position) { 257a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mItemsChangedLatch.countDown(position); 258a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 259a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 260a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void expectDataSetChanged(int expectedItemCount) { 261a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mDataCallback.mDataItemCount = expectedItemCount; 262a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mExpectedItemCount = expectedItemCount; 263a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mDataRefreshLatch = new CountDownLatch(1); 264a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 265a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 266a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void waitForDataSetChanged(String context) throws InterruptedException { 267a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev waitForLatch("timed out waiting for data set change (" + context + ")", 268a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mDataRefreshLatch); 269a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 270a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 271a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void expectItemRangeChanged(int startPosition, int itemCount) { 272a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mItemsChangedLatch.expectRange(startPosition, itemCount); 273a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 274a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 275a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void waitForItems() throws InterruptedException { 276a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev waitForLatch("onItemChanged", mItemsChangedLatch.mLatch); 277a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 278a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 279de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev @UiThread 280a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void scrollTo(int position) { 281a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mLastVisibleItem += position - mFirstVisibleItem; 282a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mFirstVisibleItem = position; 283a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mAsyncListUtil.onRangeChanged(); 284a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 285a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 286de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev @UiThread 287a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private void updateViewport() { 288a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev int itemCount = mAsyncListUtil.getItemCount(); 289a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev if (mLastVisibleItem < itemCount) { 290a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev return; 291a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 292a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mLastVisibleItem = itemCount - 1; 293a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mFirstVisibleItem = Math.max(0, mLastVisibleItem - VIEWPORT_SIZE + 1); 294a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 295a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 296a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 297a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private static class PositionSetLatch { 298a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public CountDownLatch mLatch = new CountDownLatch(0); 299a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 300a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev final private SparseBooleanArray mExpectedPositions = new SparseBooleanArray(); 301a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev final private String mKind; 302a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 303a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev PositionSetLatch(String kind) { 304a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev this.mKind = kind; 305a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 306a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 307a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev void expect(int ... positions) { 308a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mExpectedPositions.clear(); 309a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev for (int position : positions) { 310a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mExpectedPositions.put(position, true); 311a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 312a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev createLatch(); 313a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 314a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 315a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev void expectRange(int position, int count) { 316a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mExpectedPositions.clear(); 317a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev for (int i = 0; i < count; i++) { 318a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mExpectedPositions.put(position + i, true); 319a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 320a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev createLatch(); 321a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 322a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 323a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev void countDown(int position) { 324a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev if (mLatch == null) { 325a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev return; 326a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 327a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev assertTrue("unexpected " + mKind + " @" + position, mExpectedPositions.get(position)); 328a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mExpectedPositions.delete(position); 329a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev if (mExpectedPositions.size() == 0) { 330a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mLatch.countDown(); 331a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 332a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 333a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 334a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private void createLatch() { 335a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mLatch = new CountDownLatch(1); 336a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev if (mExpectedPositions.size() == 0) { 337a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mLatch.countDown(); 338a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 339a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 340a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 341a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev} 342