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
19754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport static org.junit.Assert.assertEquals;
20754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport static org.junit.Assert.assertTrue;
21754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikas
22754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport android.support.annotation.UiThread;
23754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport android.support.test.filters.MediumTest;
24e310cf31070baf35a7e9eedf9528d3c2927db6c7Aurimas Liutikasimport android.support.test.filters.Suppress;
2542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikasimport android.support.test.rule.ActivityTestRule;
2642e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikasimport android.support.v7.widget.TestActivity;
27754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport android.util.SparseBooleanArray;
28754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikas
290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport org.junit.After;
300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport org.junit.Before;
3142e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikasimport org.junit.Rule;
320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport org.junit.Test;
330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport org.junit.runner.RunWith;
340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport org.junit.runners.JUnit4;
350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
36a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheevimport java.util.concurrent.CountDownLatch;
37a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheevimport java.util.concurrent.TimeUnit;
38a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
395752abaccbb000adf2e4369ceff6e747da12e2f5Aurimas Liutikas// This test is disabled as it is flaky.
405752abaccbb000adf2e4369ceff6e747da12e2f5Aurimas Liutikas@Suppress
41f1b288ec2104488f4a92e911b0ab80c8f0f3e9d1Yigit Boyar@MediumTest
420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar@RunWith(JUnit4.class)
4342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikaspublic class AsyncListUtilTest {
4442e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas    @Rule
4542e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas    public ActivityTestRule<TestActivity> mActivityRule =
4642e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas            new ActivityTestRule<>(TestActivity.class);
47a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
48a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    private static final int TILE_SIZE = 10;
49a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
50a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    private TestDataCallback mDataCallback;
51a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    private TestViewCallback mViewCallback;
52a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
53a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    AsyncListUtil<String> mAsyncListUtil;
54a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Before
5642e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas    public final void setup() throws Throwable {
57a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        mDataCallback = new TestDataCallback();
58a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        mViewCallback = new TestViewCallback();
59a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        mDataCallback.expectTiles(0, 10, 20);
60180bb52faf312fa55eb0c2ec22c6d9cff977a323Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
61180bb52faf312fa55eb0c2ec22c6d9cff977a323Aurimas Liutikas            @Override
62180bb52faf312fa55eb0c2ec22c6d9cff977a323Aurimas Liutikas            public void run() {
63180bb52faf312fa55eb0c2ec22c6d9cff977a323Aurimas Liutikas                mAsyncListUtil = new AsyncListUtil<>(
64180bb52faf312fa55eb0c2ec22c6d9cff977a323Aurimas Liutikas                        String.class, TILE_SIZE, mDataCallback, mViewCallback);
65180bb52faf312fa55eb0c2ec22c6d9cff977a323Aurimas Liutikas            }
66180bb52faf312fa55eb0c2ec22c6d9cff977a323Aurimas Liutikas        });
6742e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mDataCallback.waitForTiles("initial load");
68de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev    }
69de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev
700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @After
71a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    public void tearDown() throws Exception {
72a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        /// Wait a little extra to catch spurious messages.
73a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        new CountDownLatch(1).await(500, TimeUnit.MILLISECONDS);
74a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    }
75a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void withNoPreload() throws Throwable {
78a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        scrollAndExpectTiles(10, "scroll to 10", 30);
79a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        scrollAndExpectTiles(25, "scroll to 25", 40);
80a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        scrollAndExpectTiles(45, "scroll to 45", 50, 60);
81a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        scrollAndExpectTiles(70, "scroll to 70", 70, 80, 90);
82a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    }
83a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void withPreload() throws Throwable {
86a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        mViewCallback.mStartPreload = 5;
87a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        mViewCallback.mEndPreload = 15;
88a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        scrollAndExpectTiles(50, "scroll down a lot", 40, 50, 60, 70, 80);
89a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
90a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        mViewCallback.mStartPreload = 0;
91a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        mViewCallback.mEndPreload = 0;
92a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        scrollAndExpectTiles(60, "scroll down a little, no new tiles loaded");
93a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        scrollAndExpectTiles(40, "scroll up a little, no new tiles loaded");
94a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    }
95a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
96e310cf31070baf35a7e9eedf9528d3c2927db6c7Aurimas Liutikas    @Test
970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void tileCaching() throws Throwable {
98a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        scrollAndExpectTiles(25, "next screen", 30, 40);
99a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
100a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        scrollAndExpectTiles(0, "back at top, no new page loads");
101a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        scrollAndExpectTiles(25, "next screen again, no new page loads");
102a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
103a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        mDataCallback.mCacheSize = 3;
104a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        scrollAndExpectTiles(50, "scroll down more, all pages should load", 50, 60, 70);
105a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        scrollAndExpectTiles(0, "scroll back to top, all pages should reload", 0, 10, 20);
106a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    }
107a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
1080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
1090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void dataRefresh() throws Throwable {
110a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        mViewCallback.expectDataSetChanged(40);
111a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        mDataCallback.expectTiles(0, 10, 20);
112de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev        refreshOnUiThread();
113a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        mViewCallback.waitForDataSetChanged("increasing item count");
114a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        mDataCallback.waitForTiles("increasing item count");
115a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
116a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        mViewCallback.expectDataSetChanged(15);
117a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        mDataCallback.expectTiles(0, 10);
118de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev        refreshOnUiThread();
119a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        mViewCallback.waitForDataSetChanged("decreasing item count");
120a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        mDataCallback.waitForTiles("decreasing item count");
121a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    }
122a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
1230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
1240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void itemChanged() throws Throwable {
125a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        final int position = 30;
126a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        final int count = 20;
127a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
128de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev        assertLoadedItemsOnUiThread("no new items should be loaded", 0, position, count);
129a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
130a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        mViewCallback.expectItemRangeChanged(position, count);
131a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        scrollAndExpectTiles(20, "scrolling to missing items", 30, 40);
132a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        mViewCallback.waitForItems();
133a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
134de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev        assertLoadedItemsOnUiThread("all new items should be loaded", count, position, count);
135a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    }
136a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
137de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev    @UiThread
138a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    private int getLoadedItemCount(int startPosition, int itemCount) {
139a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        int loaded = 0;
140a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        for (int i = 0; i < itemCount; i++) {
141a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            if (mAsyncListUtil.getItem(startPosition + i) != null) {
142a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev                loaded++;
143a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            }
144a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        }
145a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        return loaded;
146a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    }
147a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
148a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    private void scrollAndExpectTiles(int position, String context, int... positions)
149de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev            throws Throwable {
150a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        mDataCallback.expectTiles(positions);
151de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev        scrollOnUiThread(position);
152a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        mDataCallback.waitForTiles(context);
153a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    }
154a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
155a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    private static void waitForLatch(String context, CountDownLatch latch)
156a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            throws InterruptedException {
157a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        assertTrue("timed out waiting for " + context, latch.await(1, TimeUnit.SECONDS));
158a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    }
159a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
160de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev    private void refreshOnUiThread() throws Throwable {
16142e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
162de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev            @Override
163de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev            public void run() {
164de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev                mAsyncListUtil.refresh();
165de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev            }
166de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev        });
167de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev    }
168de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev
169de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev    private void assertLoadedItemsOnUiThread(final String message,
170de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev                                             final int expectedCount,
171de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev                                             final int position,
172de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev                                             final int count) throws Throwable {
17342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
174de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev            @Override
175de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev            public void run() {
176de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev                assertEquals(message, expectedCount, getLoadedItemCount(position, count));
177de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev            }
178de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev        });
179de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev    }
180de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev
181de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev    private void scrollOnUiThread(final int position) throws Throwable {
18242e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mActivityRule.runOnUiThread(new Runnable() {
183de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev            @Override
184de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev            public void run() {
185de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev                mViewCallback.scrollTo(position);
186de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev            }
187de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev        });
188de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev    }
189de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev
190a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    private class TestDataCallback extends AsyncListUtil.DataCallback<String> {
191a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        private int mCacheSize = 10;
192a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
193a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        int mDataItemCount = 100;
194a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
195a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        final PositionSetLatch mTilesFilledLatch = new PositionSetLatch("filled");
196a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
197a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        @Override
198a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        public void fillData(String[] data, int startPosition, int itemCount) {
199a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            synchronized (mTilesFilledLatch) {
200a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev                assertEquals(Math.min(TILE_SIZE, mDataItemCount - startPosition), itemCount);
201a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev                mTilesFilledLatch.countDown(startPosition);
202a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            }
203a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            for (int i = 0; i < itemCount; i++) {
204a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev                data[i] = "item #" + startPosition;
205a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            }
206a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        }
207a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
208a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        @Override
209a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        public int refreshData() {
210a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            return mDataItemCount;
211a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        }
212a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
213e2104f4b5c8e3ad63570306a25e61502dfe4c418Aurimas Liutikas        @Override
214a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        public int getMaxCachedTiles() {
215a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            return mCacheSize;
216a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        }
217a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
218a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        public void expectTiles(int... positions) {
219a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            synchronized (mTilesFilledLatch) {
220a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev                mTilesFilledLatch.expect(positions);
221a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            }
222a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        }
223a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
224a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        private void waitForTiles(String context) throws InterruptedException {
225a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            waitForLatch("filled tiles (" + context + ")", mTilesFilledLatch.mLatch);
226a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        }
227a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    }
228a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
229a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    private class TestViewCallback extends AsyncListUtil.ViewCallback {
230a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        public static final int VIEWPORT_SIZE = 25;
231a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        private int mStartPreload;
232a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        private int mEndPreload;
233a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
234a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        int mFirstVisibleItem;
235a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        int mLastVisibleItem = VIEWPORT_SIZE - 1;
236a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
237a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        private int mExpectedItemCount;
238a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        CountDownLatch mDataRefreshLatch;
239a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
240a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        PositionSetLatch mItemsChangedLatch = new PositionSetLatch("item changed");
241a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
242a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        @Override
243a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        public void getItemRangeInto(int[] outRange) {
244a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            outRange[0] = mFirstVisibleItem;
245a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            outRange[1] = mLastVisibleItem;
246a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        }
247a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
248a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        @Override
249a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        public void extendRangeInto(int[] range, int[] outRange, int scrollHint) {
250a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            outRange[0] = range[0] - mStartPreload;
251a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            outRange[1] = range[1] + mEndPreload;
252a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        }
253a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
254a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        @Override
255de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev        @UiThread
256a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        public void onDataRefresh() {
257a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            if (mDataRefreshLatch == null) {
258a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev                return;
259a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            }
260a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            assertTrue("unexpected onDataRefresh notification", mDataRefreshLatch.getCount() == 1);
261a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            assertEquals(mExpectedItemCount, mAsyncListUtil.getItemCount());
262a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            mDataRefreshLatch.countDown();
263a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            updateViewport();
264a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        }
265a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
266a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        @Override
267a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        public void onItemLoaded(int position) {
268a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            mItemsChangedLatch.countDown(position);
269a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        }
270a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
271a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        public void expectDataSetChanged(int expectedItemCount) {
272a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            mDataCallback.mDataItemCount = expectedItemCount;
273a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            mExpectedItemCount = expectedItemCount;
274a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            mDataRefreshLatch = new CountDownLatch(1);
275a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        }
276a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
277a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        public void waitForDataSetChanged(String context) throws InterruptedException {
278a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            waitForLatch("timed out waiting for data set change (" + context + ")",
279a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev                    mDataRefreshLatch);
280a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        }
281a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
282a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        public void expectItemRangeChanged(int startPosition, int itemCount) {
283a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            mItemsChangedLatch.expectRange(startPosition, itemCount);
284a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        }
285a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
286a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        public void waitForItems() throws InterruptedException {
287a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            waitForLatch("onItemChanged", mItemsChangedLatch.mLatch);
288a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        }
289a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
290de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev        @UiThread
291a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        public void scrollTo(int position) {
292a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            mLastVisibleItem += position - mFirstVisibleItem;
293a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            mFirstVisibleItem = position;
294a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            mAsyncListUtil.onRangeChanged();
295a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        }
296a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
297de8e2baf9504defe12972fbf60935a1148f1098fVladislav Kaznacheev        @UiThread
298a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        private void updateViewport() {
299a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            int itemCount = mAsyncListUtil.getItemCount();
300a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            if (mLastVisibleItem < itemCount) {
301a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev                return;
302a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            }
303a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            mLastVisibleItem = itemCount - 1;
304a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            mFirstVisibleItem = Math.max(0, mLastVisibleItem - VIEWPORT_SIZE + 1);
305a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        }
306a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    }
307a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
308a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    private static class PositionSetLatch {
309a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        public CountDownLatch mLatch = new CountDownLatch(0);
310a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
311a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        final private SparseBooleanArray mExpectedPositions = new SparseBooleanArray();
312a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        final private String mKind;
313a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
314a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        PositionSetLatch(String kind) {
315a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            this.mKind = kind;
316a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        }
317a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
318a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        void expect(int ... positions) {
319a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            mExpectedPositions.clear();
320a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            for (int position : positions) {
321a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev                mExpectedPositions.put(position, true);
322a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            }
323a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            createLatch();
324a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        }
325a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
326a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        void expectRange(int position, int count) {
327a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            mExpectedPositions.clear();
328a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            for (int i = 0; i < count; i++) {
329a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev                mExpectedPositions.put(position + i, true);
330a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            }
331a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            createLatch();
332a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        }
333a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
334a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        void countDown(int position) {
335a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            if (mLatch == null) {
336a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev                return;
337a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            }
338a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            assertTrue("unexpected " + mKind + " @" + position, mExpectedPositions.get(position));
339a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            mExpectedPositions.delete(position);
340a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            if (mExpectedPositions.size() == 0) {
341a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev                mLatch.countDown();
342a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            }
343a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        }
344a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev
345a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        private void createLatch() {
346a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            mLatch = new CountDownLatch(1);
347a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            if (mExpectedPositions.size() == 0) {
348a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev                mLatch.countDown();
349a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev            }
350a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev        }
351a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev    }
352a1470623b0f7c52c9e3985012bf9daeb692d7bccVladislav Kaznacheev}
353