AsyncListUtilLayoutTest.java revision 0a017072206f93474ccd2706e7983c2ff778b904
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.widget; 18a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport org.junit.Test; 200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport org.junit.runner.RunWith; 210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar 22a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheevimport android.content.Context; 230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.support.test.runner.AndroidJUnit4; 24a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheevimport android.support.v7.util.AsyncListUtil; 25f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyarimport android.test.suitebuilder.annotation.MediumTest; 26a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheevimport android.view.View; 27a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheevimport android.view.ViewGroup; 28a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheevimport android.widget.TextView; 29a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 30a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheevimport java.util.BitSet; 31a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheevimport java.util.concurrent.CountDownLatch; 32a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheevimport java.util.concurrent.TimeUnit; 330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static org.junit.Assert.*; 34a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 35f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar@MediumTest 360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar@RunWith(AndroidJUnit4.class) 37a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheevpublic class AsyncListUtilLayoutTest extends BaseRecyclerViewInstrumentationTest { 38a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 39a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private static final boolean DEBUG = false; 40a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 41a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private static final String TAG = "AsyncListUtilLayoutTest"; 42a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 43a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private static final int ITEM_COUNT = 1000; 44a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private static final int TILE_SIZE = 5; 45a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 46a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev AsyncTestAdapter mAdapter; 47a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 48a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev WrappedLinearLayoutManager mLayoutManager; 49a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 50a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private TestDataCallback mDataCallback; 51a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private TestViewCallback mViewCallback; 52a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private AsyncListUtil<String> mAsyncListUtil; 53a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 54a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public int mStartPrefetch = 0; 55a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public int mEndPrefetch = 0; 56a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar @Test 580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar public void asyncListUtil() throws Throwable { 59a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mRecyclerView = inflateWrappedRV(); 60a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mRecyclerView.setHasFixedSize(true); 61a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 62a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mAdapter = new AsyncTestAdapter(); 63a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mRecyclerView.setAdapter(mAdapter); 64a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 65a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mLayoutManager = new WrappedLinearLayoutManager( 66a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev getActivity(), LinearLayoutManager.VERTICAL, false); 67a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mRecyclerView.setLayoutManager(mLayoutManager); 68a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 69a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mLayoutManager.expectLayouts(1); 70a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev setRecyclerView(mRecyclerView); 71a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mLayoutManager.waitForLayout(2); 72a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 73a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev int rangeStart = 0; 74a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev assertEquals(rangeStart, mLayoutManager.findFirstVisibleItemPosition()); 75a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 76a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev final int rangeSize = mLayoutManager.findLastVisibleItemPosition() + 1; 77a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev assertTrue("No visible items", rangeSize > 0); 78a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 79a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev assertEquals("All visible items must be empty at first", 80a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev rangeSize, getEmptyVisibleChildCount()); 81a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 82a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mDataCallback = new TestDataCallback(); 83a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mViewCallback = new TestViewCallback(); 84a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 85a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mDataCallback.expectTilesInRange(rangeStart, rangeSize); 86a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mAdapter.expectItemsInRange(rangeStart, rangeSize); 87a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 88de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev runTestOnUiThread(new Runnable() { 89de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev @Override 90de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev public void run() { 91de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev mAsyncListUtil = new AsyncListUtil<String>( 92de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev String.class, TILE_SIZE, mDataCallback, mViewCallback); 93de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev } 94de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev }); 95a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 96a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { 97a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev @Override 98a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 99a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mAsyncListUtil.onRangeChanged(); 100a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 101a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev }); 102a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev assertAllLoaded("First load"); 103a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 104a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev rangeStart = roundUp(rangeSize); 105a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev scrollAndAssert("Scroll with no prefetch", rangeStart, rangeSize); 106a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 107a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev rangeStart = roundUp(rangeStart + rangeSize); 108a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mEndPrefetch = TILE_SIZE * 2; 109a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev scrollAndAssert("Scroll with prefetch", rangeStart, rangeSize); 110a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 111a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev rangeStart += mEndPrefetch; 112a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mEndPrefetch = 0; 113a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev scrollAndAssert("Scroll a little down, no prefetch", rangeStart, 0); 114a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 115a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev rangeStart = ITEM_COUNT / 2; 116a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mStartPrefetch = TILE_SIZE * 2; 117a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mEndPrefetch = TILE_SIZE * 3; 118a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev scrollAndAssert("Scroll to middle, prefetch", rangeStart, rangeSize); 119a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 120a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev rangeStart -= mStartPrefetch; 121a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mStartPrefetch = 0; 122a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mEndPrefetch = 0; 123a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev scrollAndAssert("Scroll a little up, no prefetch", rangeStart, 0); 124a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 125a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev Thread.sleep(500); // Wait for possible spurious messages. 126a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 127a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 128a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private void assertAllLoaded(String context) 129a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev throws InterruptedException { 130a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev assertTrue(context + ", timed out while waiting for items", mAdapter.waitForItems(2)); 131a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev assertTrue(context + ", timed out while waiting for tiles", mDataCallback.waitForTiles(2)); 132a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev assertEquals(context + ", empty child found", 0, getEmptyVisibleChildCount()); 133a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 134a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 135a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private void scrollAndAssert(String context, int rangeStart, int rangeSize) throws Throwable { 136a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev if (rangeSize > 0) { 137a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mDataCallback.expectTilesInRange(rangeStart, rangeSize); 138a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } else { 139a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mDataCallback.expectNoNewTilesLoaded(); 140a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 141a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mAdapter.expectItemsInRange(rangeStart, rangeSize); 142a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mLayoutManager.expectLayouts(1); 143a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev scrollToPositionWithOffset(rangeStart, 0); 144a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mLayoutManager.waitForLayout(1); 145a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev assertAllLoaded(context); 146a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 147a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 148a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev void scrollToPositionWithOffset(final int position, final int offset) throws Throwable { 149a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev runTestOnUiThread(new Runnable() { 150a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev @Override 151a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void run() { 152a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mLayoutManager.scrollToPositionWithOffset(position, offset); 153a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 154a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev }); 155a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 156a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 157a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private int roundUp(int value) { 158a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev return value - value % TILE_SIZE + TILE_SIZE; 159a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 160a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 161a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private int getTileCount(int start, int size) { 162a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev return ((start + size - 1) / TILE_SIZE) - (start / TILE_SIZE) + 1; 163a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 164a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 165a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private int getEmptyVisibleChildCount() { 166a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev int emptyChildCount = 0; 167a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev int firstVisible = mLayoutManager.findFirstVisibleItemPosition(); 168a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev int lastVisible = mLayoutManager.findLastVisibleItemPosition(); 169a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev for (int i = firstVisible; i <= lastVisible; i++) { 170a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev View child = mLayoutManager.findViewByPosition(i); 171a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev assertTrue(child instanceof TextView); 172a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev if (((TextView) child).getText() == "") { 173a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev emptyChildCount++; 174a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 175a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 176a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev return emptyChildCount; 177a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 178a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 179a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private class TestDataCallback extends AsyncListUtil.DataCallback<String> { 180a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 181a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private CountDownLatch mTilesLatch; 182a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 183a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev @Override 184a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void fillData(String[] data, int startPosition, int itemCount) { 185a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev assertTrue("Unexpected tile load @" + startPosition, mTilesLatch.getCount() > 0); 186a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev try { 187a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev Thread.sleep(100); 188a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } catch (InterruptedException e) { 189a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 190a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev for (int i = 0; i < itemCount; i++) { 191a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev data[i] = "Item #" + (startPosition + i); 192a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 193a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mTilesLatch.countDown(); 194a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 195a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 196a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev @Override 197a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public int refreshData() { 198a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev return ITEM_COUNT; 199a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 200a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 201a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private void expectTiles(int count) { 202a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mTilesLatch = new CountDownLatch(count); 203a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 204a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 205a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void expectTilesInRange(int rangeStart, int rangeSize) { 206a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev expectTiles(getTileCount(rangeStart - mStartPrefetch, 207a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev rangeSize + mStartPrefetch + mEndPrefetch)); 208a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 209a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 210a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void expectNoNewTilesLoaded() { 211a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev expectTiles(0); 212a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 213a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 214a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public boolean waitForTiles(long timeoutInSeconds) throws InterruptedException { 215a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev return mTilesLatch.await(timeoutInSeconds, TimeUnit.SECONDS); 216a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 217a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 218a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 219a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private class TestViewCallback extends AsyncListUtil.ViewCallback { 220a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev @Override 221a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void getItemRangeInto(int[] outRange) { 222a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev outRange[0] = mLayoutManager.findFirstVisibleItemPosition(); 223a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev outRange[1] = mLayoutManager.findLastVisibleItemPosition(); 224a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 225a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 226a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev @Override 227a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void extendRangeInto(int[] range, int[] outRange, int scrollHint) { 228a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev outRange[0] = range[0] - mStartPrefetch; 229a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev outRange[1] = range[1] + mEndPrefetch; 230a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 231a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 232a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev @Override 233a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void onDataRefresh() { 234a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mRecyclerView.getAdapter().notifyDataSetChanged(); 235a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 236a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 237a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev @Override 238a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void onItemLoaded(int position) { 239a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mRecyclerView.getAdapter().notifyItemChanged(position); 240a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 241a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 242a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 243a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private static class SimpleViewHolder extends RecyclerView.ViewHolder { 244a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 245a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public SimpleViewHolder(Context context) { 246a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev super(new TextView(context)); 247a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 248a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 249a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 250a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private class AsyncTestAdapter extends RecyclerView.Adapter<SimpleViewHolder> { 251a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 252a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private BitSet mLoadedPositions; 253a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private BitSet mExpectedPositions; 254a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 255a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private CountDownLatch mItemsLatch; 256a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public AsyncTestAdapter() { 257a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mLoadedPositions = new BitSet(ITEM_COUNT); 258a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 259a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 260a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev @Override 261a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public SimpleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 262a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev return new SimpleViewHolder(parent.getContext()); 263a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 264a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 265a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev @Override 266a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void onBindViewHolder(SimpleViewHolder holder, int position) { 267a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev final String item = mAsyncListUtil == null ? null : mAsyncListUtil.getItem(position); 268a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev ((TextView) (holder.itemView)).setText(item == null ? "" : item); 269a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 270a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev if (item != null) { 271a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mLoadedPositions.set(position); 272a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev if (mExpectedPositions.get(position)) { 273a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mExpectedPositions.clear(position); 274a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev if (mExpectedPositions.cardinality() == 0) { 275a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mItemsLatch.countDown(); 276a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 277a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 278a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 279a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 280a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 281a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev @Override 282a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public int getItemCount() { 283a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev return ITEM_COUNT; 284a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 285a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 286a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev private void expectItemsInRange(int rangeStart, int rangeSize) { 287a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mExpectedPositions = new BitSet(rangeStart + rangeSize); 288a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev for (int i = 0; i < rangeSize; i++) { 289a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev if (!mLoadedPositions.get(rangeStart + i)) { 290a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mExpectedPositions.set(rangeStart + i); 291a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 292a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 293a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mItemsLatch = new CountDownLatch(1); 294a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev if (mExpectedPositions.cardinality() == 0) { 295a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mItemsLatch.countDown(); 296a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 297a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 298a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 299a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public boolean waitForItems(long timeoutInSeconds) throws InterruptedException { 300a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev return mItemsLatch.await(timeoutInSeconds, TimeUnit.SECONDS); 301a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 302a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 303a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 304a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev class WrappedLinearLayoutManager extends LinearLayoutManager { 305a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 306a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev CountDownLatch mLayoutLatch; 307a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 308a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public WrappedLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { 309a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev super(context, orientation, reverseLayout); 310a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 311a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 312a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void expectLayouts(int count) { 313a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mLayoutLatch = new CountDownLatch(count); 314a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 315a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 316a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void waitForLayout(long timeout) throws InterruptedException { 317a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mLayoutLatch.await(timeout * (DEBUG ? 100 : 1), TimeUnit.SECONDS); 318a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev assertEquals("all expected layouts should be executed at the expected time", 319a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 0, mLayoutLatch.getCount()); 320a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev getInstrumentation().waitForIdleSync(); 321a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 322a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev 323a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev @Override 324a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 325a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev try { 326a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev super.onLayoutChildren(recycler, state); 327a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } catch (Throwable t) { 328a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev postExceptionToInstrumentation(t); 329a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 330a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev mLayoutLatch.countDown(); 331a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 332a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev } 333a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev} 334