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