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