StaggeredGridLayoutManagerCacheTest.java revision 42e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.support.v7.widget;
18
19import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
20import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
21
22import static junit.framework.Assert.assertEquals;
23import static junit.framework.Assert.assertTrue;
24
25import android.os.Build;
26import android.support.test.filters.MediumTest;
27import android.support.test.filters.SdkSuppress;
28
29import org.junit.Test;
30import org.junit.runner.RunWith;
31import org.junit.runners.Parameterized;
32
33import java.util.ArrayList;
34import java.util.Arrays;
35import java.util.List;
36import java.util.concurrent.TimeUnit;
37
38@RunWith(Parameterized.class)
39@SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
40public class StaggeredGridLayoutManagerCacheTest extends BaseStaggeredGridLayoutManagerTest {
41
42    final Config mConfig;
43    final int mDx;
44    final int mDy;
45
46    public StaggeredGridLayoutManagerCacheTest(Config config, int dx, int dy) {
47        mConfig = config;
48        mDx = dx;
49        mDy = dy;
50    }
51
52    @Parameterized.Parameters(name = "config:{0},dx:{1},dy:{2}")
53    public static List<Object[]> getParams() {
54        List<Object[]> result = new ArrayList<>();
55        List<Config> configs = createBaseVariations();
56        for (Config config : configs) {
57            for (int dx : new int[] {-1, 0, 1}) {
58                for (int dy : new int[] {-1, 0, 1}) {
59                    result.add(new Object[]{config, dx, dy});
60                }
61            }
62        }
63        return result;
64    }
65
66    private ArrayList<RecyclerView.ViewHolder> cachedViews() {
67        return mRecyclerView.mRecycler.mCachedViews;
68    }
69
70    private boolean cachedViewsContains(int position) {
71        // Note: can't make assumptions about order here, so just check all cached views
72        for (int i = 0; i < cachedViews().size(); i++) {
73            if (cachedViews().get(i).getAdapterPosition() == position) return true;
74        }
75        return false;
76    }
77
78    public int findFirstVisibleItemPosition() {
79        int[] positions = new int[mConfig.mSpanCount];
80        mLayoutManager.findFirstVisibleItemPositions(positions);
81        Arrays.sort(positions);
82        return positions[0];
83    }
84
85    public int findLastVisibleItemPosition() {
86        int[] positions = new int[mConfig.mSpanCount];
87        mLayoutManager.findLastVisibleItemPositions(positions);
88        Arrays.sort(positions);
89        return positions[mConfig.mSpanCount - 1];
90    }
91
92    @MediumTest
93    @Test
94    public void cacheAndPrefetch() throws Throwable {
95        final Config config = (Config) mConfig.clone();
96        setupByConfig(config);
97        waitFirstLayout();
98
99        mActivityRule.runOnUiThread(new Runnable() {
100            @Override
101            public void run() {
102                // pretend to have an extra 5s before next frame so prefetch won't abort early
103                ((WrappedRecyclerView)mRecyclerView).setDrawingTimeOffset(5000);
104
105                // scroll to the middle, so we can move in either direction
106                mRecyclerView.scrollToPosition(mConfig.mItemCount / 2);
107            }
108        });
109
110        mRecyclerView.setItemViewCacheSize(0);
111        {
112            mLayoutManager.expectPrefetch(1);
113            mActivityRule.runOnUiThread(new Runnable() {
114                @Override
115                public void run() {
116                    mRecyclerView.mRecycler.recycleAndClearCachedViews();
117                    mRecyclerView.mGapWorker.postFromTraversal(mRecyclerView, mDx, mDy);
118
119                    // Lie about post time, so prefetch executes even if it is delayed
120                    mRecyclerView.mGapWorker.mPostTimeNs += TimeUnit.SECONDS.toNanos(5);
121                }
122            });
123            mLayoutManager.waitForPrefetch(1);
124        }
125
126        mActivityRule.runOnUiThread(new Runnable() {
127            @Override
128            public void run() {
129                // validate cache state on UI thread
130                if ((config.mOrientation == HORIZONTAL && mDx == 0)
131                        || (config.mOrientation == VERTICAL && mDy == 0)) {
132                    assertEquals(0, cachedViews().size());
133                } else {
134                    assertEquals(config.mSpanCount, cachedViews().size());
135
136                    boolean reverseScroll = config.mOrientation == HORIZONTAL ? mDx < 0 : mDy < 0;
137                    int lastVisibleItemPosition = findLastVisibleItemPosition();
138                    int firstVisibleItemPosition = findFirstVisibleItemPosition();
139
140                    for (int i = 0; i < config.mSpanCount; i++) {
141                        if (mConfig.mReverseLayout == reverseScroll) {
142                            // Pos scroll on pos layout, or reverse scroll on reverse layout
143                            // = toward last
144                            assertTrue(cachedViewsContains(lastVisibleItemPosition + 1 + i));
145                        } else {
146                            // Pos scroll on reverse layout, or reverse scroll on pos layout
147                            // = toward first
148                            assertTrue(cachedViewsContains(firstVisibleItemPosition - 1 - i));
149                        }
150                    }
151                }
152            }
153        });
154    }
155}
156