1/*
2 * Copyright (C) 2014 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 org.junit.Test;
20import org.junit.runner.RunWith;
21import org.junit.runners.Parameterized;
22
23import android.util.Log;
24import android.view.View;
25
26import java.util.Arrays;
27import java.util.BitSet;
28import java.util.List;
29
30import static org.junit.Assert.assertEquals;
31import static org.junit.Assert.assertSame;
32import static org.junit.Assert.assertTrue;
33
34@RunWith(Parameterized.class)
35public class GridLayoutManagerBaseConfigSetTest extends BaseGridLayoutManagerTest {
36    @Parameterized.Parameters(name = "{0}")
37    public static List<Config> params() {
38        return createBaseVariations();
39    }
40
41    private final Config mConfig;
42
43    public GridLayoutManagerBaseConfigSetTest(Config config) {
44        mConfig = config;
45    }
46
47    @Test
48    public void scrollBackAndPreservePositions() throws Throwable {
49        Config config = (Config) mConfig.clone();
50        config.mItemCount = 150;
51        scrollBackAndPreservePositionsTest(config);
52    }
53
54    public void scrollBackAndPreservePositionsTest(final Config config) throws Throwable {
55        final RecyclerView rv = setupBasic(config);
56        for (int i = 1; i < mAdapter.getItemCount(); i += config.mSpanCount + 2) {
57            mAdapter.setFullSpan(i);
58        }
59        waitForFirstLayout(rv);
60        final int[] globalPositions = new int[mAdapter.getItemCount()];
61        Arrays.fill(globalPositions, Integer.MIN_VALUE);
62        final int scrollStep = (mGlm.mOrientationHelper.getTotalSpace() / 20)
63                * (config.mReverseLayout ? -1 : 1);
64        final String logPrefix = config.toString();
65        final int[] globalPos = new int[1];
66        runTestOnUiThread(new Runnable() {
67            @Override
68            public void run() {
69                assertSame("test sanity", mRecyclerView, rv);
70                int globalScrollPosition = 0;
71                int visited = 0;
72                while (visited < mAdapter.getItemCount()) {
73                    for (int i = 0; i < mRecyclerView.getChildCount(); i++) {
74                        View child = mRecyclerView.getChildAt(i);
75                        final int pos = mRecyclerView.getChildLayoutPosition(child);
76                        if (globalPositions[pos] != Integer.MIN_VALUE) {
77                            continue;
78                        }
79                        visited++;
80                        GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams)
81                                child.getLayoutParams();
82                        if (config.mReverseLayout) {
83                            globalPositions[pos] = globalScrollPosition +
84                                    mGlm.mOrientationHelper.getDecoratedEnd(child);
85                        } else {
86                            globalPositions[pos] = globalScrollPosition +
87                                    mGlm.mOrientationHelper.getDecoratedStart(child);
88                        }
89                        assertEquals(logPrefix + " span index should match",
90                                mGlm.getSpanSizeLookup().getSpanIndex(pos, mGlm.getSpanCount()),
91                                lp.getSpanIndex());
92                    }
93                    int scrolled = mGlm.scrollBy(scrollStep,
94                            mRecyclerView.mRecycler, mRecyclerView.mState);
95                    globalScrollPosition += scrolled;
96                    if (scrolled == 0) {
97                        assertEquals(
98                                logPrefix + " If scroll is complete, all views should be visited",
99                                visited, mAdapter.getItemCount());
100                    }
101                }
102                if (DEBUG) {
103                    Log.d(TAG, "done recording positions " + Arrays.toString(globalPositions));
104                }
105                globalPos[0] = globalScrollPosition;
106            }
107        });
108        checkForMainThreadException();
109        // test sanity, ensure scroll happened
110        runTestOnUiThread(new Runnable() {
111            @Override
112            public void run() {
113                final int childCount = mGlm.getChildCount();
114                final BitSet expectedPositions = new BitSet();
115                for (int i = 0; i < childCount; i ++) {
116                    expectedPositions.set(mAdapter.getItemCount() - i - 1);
117                }
118                for (int i = 0; i <childCount; i ++) {
119                    final View view = mGlm.getChildAt(i);
120                    int position = mGlm.getPosition(view);
121                    assertTrue("child position should be in last page", expectedPositions.get(position));
122                }
123            }
124        });
125        getInstrumentation().waitForIdleSync();
126        runTestOnUiThread(new Runnable() {
127            @Override
128            public void run() {
129                int globalScrollPosition = globalPos[0];
130                // now scroll back and make sure global positions match
131                BitSet shouldTest = new BitSet(mAdapter.getItemCount());
132                shouldTest.set(0, mAdapter.getItemCount() - 1, true);
133                String assertPrefix = config
134                        + " global pos must match when scrolling in reverse for position ";
135                int scrollAmount = Integer.MAX_VALUE;
136                while (!shouldTest.isEmpty() && scrollAmount != 0) {
137                    for (int i = 0; i < mRecyclerView.getChildCount(); i++) {
138                        View child = mRecyclerView.getChildAt(i);
139                        int pos = mRecyclerView.getChildLayoutPosition(child);
140                        if (!shouldTest.get(pos)) {
141                            continue;
142                        }
143                        GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams)
144                                child.getLayoutParams();
145                        shouldTest.clear(pos);
146                        int globalPos;
147                        if (config.mReverseLayout) {
148                            globalPos = globalScrollPosition +
149                                    mGlm.mOrientationHelper.getDecoratedEnd(child);
150                        } else {
151                            globalPos = globalScrollPosition +
152                                    mGlm.mOrientationHelper.getDecoratedStart(child);
153                        }
154                        assertEquals(assertPrefix + pos,
155                                globalPositions[pos], globalPos);
156                        assertEquals("span index should match",
157                                mGlm.getSpanSizeLookup().getSpanIndex(pos, mGlm.getSpanCount()),
158                                lp.getSpanIndex());
159                    }
160                    scrollAmount = mGlm.scrollBy(-scrollStep,
161                            mRecyclerView.mRecycler, mRecyclerView.mState);
162                    globalScrollPosition += scrollAmount;
163                }
164                assertTrue("all views should be seen", shouldTest.isEmpty());
165            }
166        });
167        checkForMainThreadException();
168    }
169}
170