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