10a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar/*
20a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * Copyright (C) 2015 The Android Open Source Project
30a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar *
40a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
50a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * you may not use this file except in compliance with the License.
60a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * You may obtain a copy of the License at
70a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar *
80a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
90a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar *
100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * Unless required by applicable law or agreed to in writing, software
110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * See the License for the specific language governing permissions and
140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar * limitations under the License.
150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar */
160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarpackage android.support.v7.widget;
180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport org.junit.Test;
210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport org.junit.runner.RunWith;
220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport org.junit.runners.Parameterized;
230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.graphics.Rect;
250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.os.Looper;
260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.os.Parcel;
270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.os.Parcelable;
284510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyarimport android.support.v4.view.ViewCompat;
290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.test.suitebuilder.annotation.MediumTest;
300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.util.Log;
310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport android.view.View;
324510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyarimport android.view.ViewParent;
330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.Arrays;
350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.BitSet;
360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.List;
370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.Map;
380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport java.util.UUID;
390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static android.support.v7.widget.LayoutState.LAYOUT_START;
410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static android.support.v7.widget.LinearLayoutManager.VERTICAL;
420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static android.support.v7.widget.StaggeredGridLayoutManager.HORIZONTAL;
434510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
444510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyarimport static org.hamcrest.CoreMatchers.hasItem;
454510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyarimport static org.hamcrest.CoreMatchers.is;
464510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyarimport static org.hamcrest.CoreMatchers.not;
474510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyarimport static org.hamcrest.CoreMatchers.sameInstance;
480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static org.junit.Assert.assertEquals;
490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static org.junit.Assert.assertNotNull;
504510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyarimport static org.junit.Assert.assertThat;
510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarimport static org.junit.Assert.assertTrue;
520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar@RunWith(Parameterized.class)
540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar@MediumTest
550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyarpublic class StaggeredGridLayoutManagerBaseConfigSetTest
560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        extends BaseStaggeredGridLayoutManagerTest {
570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Parameterized.Parameters(name = "{0}")
590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public static List<Config> getParams() {
600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        return createBaseVariations();
610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    private final Config mConfig;
640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public StaggeredGridLayoutManagerBaseConfigSetTest(Config config)
660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            throws CloneNotSupportedException {
670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mConfig = (Config) config.clone();
680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void rTL() throws Throwable {
724143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        rtlTest(false, false);
730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void rTLChangeAfter() throws Throwable {
774143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        rtlTest(true, false);
780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
804143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    @Test
814143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    public void rTLItemWrapContent() throws Throwable {
824143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        rtlTest(false, true);
834143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    }
844143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar
854143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    @Test
864143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    public void rTLChangeAfterItemWrapContent() throws Throwable {
874143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        rtlTest(true, true);
884143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    }
894143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar
904143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar    void rtlTest(boolean changeRtlAfter, final boolean wrapContent) throws Throwable {
910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        if (mConfig.mSpanCount == 1) {
920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mConfig.mSpanCount = 2;
930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        String logPrefix = mConfig + ", changeRtlAfterLayout:" + changeRtlAfter;
954143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar        setupByConfig(mConfig.itemCount(5),
964143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                new GridTestAdapter(mConfig.mItemCount, mConfig.mOrientation) {
974143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                    @Override
984143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                    public void onBindViewHolder(TestViewHolder holder,
994143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                            int position) {
1004143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                        super.onBindViewHolder(holder, position);
1014143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                        if (wrapContent) {
1024143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                            if (mOrientation == HORIZONTAL) {
1034143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                                holder.itemView.getLayoutParams().height
1044143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                                        = RecyclerView.LayoutParams.WRAP_CONTENT;
1054143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                            } else {
1064143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                                holder.itemView.getLayoutParams().width
1074143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                                        = RecyclerView.LayoutParams.MATCH_PARENT;
1084143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                            }
1094143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                        }
1104143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                    }
1114143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar                });
1120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        if (changeRtlAfter) {
1130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            waitFirstLayout();
1140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mLayoutManager.expectLayouts(1);
1150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mLayoutManager.setFakeRtl(true);
1160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mLayoutManager.waitForLayout(2);
1170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        } else {
1180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mLayoutManager.mFakeRTL = true;
1190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            waitFirstLayout();
1200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
1210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
1220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        assertEquals("view should become rtl", true, mLayoutManager.isLayoutRTL());
1230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        OrientationHelper helper = OrientationHelper.createHorizontalHelper(mLayoutManager);
1240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        View child0 = mLayoutManager.findViewByPosition(0);
1250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        View child1 = mLayoutManager.findViewByPosition(mConfig.mOrientation == VERTICAL ? 1
1260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                : mConfig.mSpanCount);
1270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        assertNotNull(logPrefix + " child position 0 should be laid out", child0);
1280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        assertNotNull(logPrefix + " child position 0 should be laid out", child1);
1290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        logPrefix += " child1 pos:" + mLayoutManager.getPosition(child1);
1300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        if (mConfig.mOrientation == VERTICAL || !mConfig.mReverseLayout) {
1310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            assertTrue(logPrefix + " second child should be to the left of first child",
1320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    helper.getDecoratedEnd(child0) > helper.getDecoratedEnd(child1));
1330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            assertEquals(logPrefix + " first child should be right aligned",
1340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    helper.getDecoratedEnd(child0), helper.getEndAfterPadding());
1350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        } else {
1360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            assertTrue(logPrefix + " first child should be to the left of second child",
1370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    helper.getDecoratedStart(child1) >= helper.getDecoratedStart(child0));
1380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            assertEquals(logPrefix + " first child should be left aligned",
1390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    helper.getDecoratedStart(child0), helper.getStartAfterPadding());
1400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
1410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        checkForMainThreadException();
1420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
1430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
1440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
1450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollBackAndPreservePositions() throws Throwable {
1460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        scrollBackAndPreservePositionsTest(false);
1470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
1480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
1490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
1500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollBackAndPreservePositionsWithRestore() throws Throwable {
1510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        scrollBackAndPreservePositionsTest(true);
1520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
1530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
1540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollBackAndPreservePositionsTest(final boolean saveRestoreInBetween)
1550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            throws Throwable {
1560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        setupByConfig(mConfig);
1570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mAdapter.mOnBindCallback = new OnBindCallback() {
1580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            @Override
1590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            public void onBoundItem(TestViewHolder vh, int position) {
1600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                StaggeredGridLayoutManager.LayoutParams
1610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        lp = (StaggeredGridLayoutManager.LayoutParams) vh.itemView
1620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        .getLayoutParams();
1630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                lp.setFullSpan((position * 7) % (mConfig.mSpanCount + 1) == 0);
1640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
1650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        };
1660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        waitFirstLayout();
1670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        final int[] globalPositions = new int[mAdapter.getItemCount()];
1680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Arrays.fill(globalPositions, Integer.MIN_VALUE);
1690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        final int scrollStep = (mLayoutManager.mPrimaryOrientation.getTotalSpace() / 10)
1700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                * (mConfig.mReverseLayout ? -1 : 1);
1710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
1720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        final int[] globalPos = new int[1];
1730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        runTestOnUiThread(new Runnable() {
1740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            @Override
1750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            public void run() {
1760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                int globalScrollPosition = 0;
1770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                while (globalPositions[mAdapter.getItemCount() - 1] == Integer.MIN_VALUE) {
1780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    for (int i = 0; i < mRecyclerView.getChildCount(); i++) {
1790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        View child = mRecyclerView.getChildAt(i);
1800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        final int pos = mRecyclerView.getChildLayoutPosition(child);
1810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        if (globalPositions[pos] != Integer.MIN_VALUE) {
1820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            continue;
1830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        }
1840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        if (mConfig.mReverseLayout) {
1850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            globalPositions[pos] = globalScrollPosition +
1860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                    mLayoutManager.mPrimaryOrientation.getDecoratedEnd(child);
1870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        } else {
1880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            globalPositions[pos] = globalScrollPosition +
1890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                    mLayoutManager.mPrimaryOrientation.getDecoratedStart(child);
1900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        }
1910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    }
1920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    globalScrollPosition += mLayoutManager.scrollBy(scrollStep,
1930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            mRecyclerView.mRecycler, mRecyclerView.mState);
1940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
1950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (DEBUG) {
1960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    Log.d(TAG, "done recording positions " + Arrays.toString(globalPositions));
1970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
1980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                globalPos[0] = globalScrollPosition;
1990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
2000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        });
2010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        checkForMainThreadException();
2020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        if (saveRestoreInBetween) {
2040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            saveRestore(mConfig);
2050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
2060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        checkForMainThreadException();
2080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        runTestOnUiThread(new Runnable() {
2090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            @Override
2100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            public void run() {
2110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                int globalScrollPosition = globalPos[0];
2120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                // now scroll back and make sure global positions match
2130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                BitSet shouldTest = new BitSet(mAdapter.getItemCount());
2140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                shouldTest.set(0, mAdapter.getItemCount() - 1, true);
2150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                String assertPrefix = mConfig + ", restored in between:" + saveRestoreInBetween
2160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        + " global pos must match when scrolling in reverse for position ";
2170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                int scrollAmount = Integer.MAX_VALUE;
2180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                while (!shouldTest.isEmpty() && scrollAmount != 0) {
2190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    for (int i = 0; i < mRecyclerView.getChildCount(); i++) {
2200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        View child = mRecyclerView.getChildAt(i);
2210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        int pos = mRecyclerView.getChildLayoutPosition(child);
2220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        if (!shouldTest.get(pos)) {
2230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            continue;
2240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        }
2250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        shouldTest.clear(pos);
2260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        int globalPos;
2270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        if (mConfig.mReverseLayout) {
2280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            globalPos = globalScrollPosition +
2290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                    mLayoutManager.mPrimaryOrientation.getDecoratedEnd(child);
2300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        } else {
2310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            globalPos = globalScrollPosition +
2320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                    mLayoutManager.mPrimaryOrientation.getDecoratedStart(child);
2330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        }
2340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        assertEquals(assertPrefix + pos,
2350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                globalPositions[pos], globalPos);
2360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    }
2370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    scrollAmount = mLayoutManager.scrollBy(-scrollStep,
2380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            mRecyclerView.mRecycler, mRecyclerView.mState);
2390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    globalScrollPosition += scrollAmount;
2400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
2410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                assertTrue("all views should be seen", shouldTest.isEmpty());
2420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
2430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        });
2440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        checkForMainThreadException();
2450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
2460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    private void saveRestore(final Config config) throws Throwable {
2480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        runTestOnUiThread(new Runnable() {
2490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            @Override
2500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            public void run() {
2510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                try {
2520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    Parcelable savedState = mRecyclerView.onSaveInstanceState();
2530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    // we append a suffix to the parcelable to test out of bounds
2540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    String parcelSuffix = UUID.randomUUID().toString();
2550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    Parcel parcel = Parcel.obtain();
2560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    savedState.writeToParcel(parcel, 0);
2570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    parcel.writeString(parcelSuffix);
2580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    removeRecyclerView();
2590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    // reset for reading
2600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    parcel.setDataPosition(0);
2610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    // re-create
2620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel);
2630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    RecyclerView restored = new RecyclerView(getActivity());
2640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    mLayoutManager = new WrappedLayoutManager(config.mSpanCount,
2650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            config.mOrientation);
2660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    mLayoutManager.setGapStrategy(config.mGapStrategy);
2670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    restored.setLayoutManager(mLayoutManager);
2680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    // use the same adapter for Rect matching
2690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    restored.setAdapter(mAdapter);
2700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    restored.onRestoreInstanceState(savedState);
2710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    if (Looper.myLooper() == Looper.getMainLooper()) {
2720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mLayoutManager.expectLayouts(1);
2730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        setRecyclerView(restored);
2740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    } else {
2750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mLayoutManager.expectLayouts(1);
2760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        setRecyclerView(restored);
2770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mLayoutManager.waitForLayout(2);
2780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    }
2790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                } catch (Throwable t) {
2800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    postExceptionToInstrumentation(t);
2810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
2820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
2830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        });
2840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        checkForMainThreadException();
2850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
2860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
2880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void getFirstLastChildrenTest() throws Throwable {
2890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        getFirstLastChildrenTest(false);
2900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
2910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
2930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void getFirstLastChildrenTestProvideArray() throws Throwable {
2940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        getFirstLastChildrenTest(true);
2950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
2960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
2970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void getFirstLastChildrenTest(final boolean provideArr) throws Throwable {
2980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        setupByConfig(mConfig);
2990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        waitFirstLayout();
3000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Runnable viewInBoundsTest = new Runnable() {
3010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            @Override
3020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            public void run() {
3030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                VisibleChildren visibleChildren = mLayoutManager.traverseAndFindVisibleChildren();
3040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final String boundsLog = mLayoutManager.getBoundsLog();
3050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                VisibleChildren queryResult = new VisibleChildren(mLayoutManager.getSpanCount());
3060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                queryResult.findFirstPartialVisibleClosestToStart = mLayoutManager
307e5874e666791a58d21eed482fb90e917445da873Chris Craik                        .findFirstVisibleItemClosestToStart(false);
3080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                queryResult.findFirstPartialVisibleClosestToEnd = mLayoutManager
309e5874e666791a58d21eed482fb90e917445da873Chris Craik                        .findFirstVisibleItemClosestToEnd(false);
3100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                queryResult.firstFullyVisiblePositions = mLayoutManager
3110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        .findFirstCompletelyVisibleItemPositions(
3120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                provideArr ? new int[mLayoutManager.getSpanCount()] : null);
3130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                queryResult.firstVisiblePositions = mLayoutManager
3140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        .findFirstVisibleItemPositions(
3150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                provideArr ? new int[mLayoutManager.getSpanCount()] : null);
3160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                queryResult.lastFullyVisiblePositions = mLayoutManager
3170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        .findLastCompletelyVisibleItemPositions(
3180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                provideArr ? new int[mLayoutManager.getSpanCount()] : null);
3190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                queryResult.lastVisiblePositions = mLayoutManager
3200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        .findLastVisibleItemPositions(
3210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                provideArr ? new int[mLayoutManager.getSpanCount()] : null);
3220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                assertEquals(mConfig + ":\nfirst visible child should match traversal result\n"
3230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        + "traversed:" + visibleChildren + "\n"
3240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        + "queried:" + queryResult + "\n"
3250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        + boundsLog, visibleChildren, queryResult
3260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                );
3270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        };
3290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        runTestOnUiThread(viewInBoundsTest);
3300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching
3310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // case
3320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        final int scrollPosition = mAdapter.getItemCount();
3330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        runTestOnUiThread(new Runnable() {
3340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            @Override
3350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            public void run() {
3360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                mRecyclerView.smoothScrollToPosition(scrollPosition);
3370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        });
3390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        while (mLayoutManager.isSmoothScrolling() ||
3400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
3410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            runTestOnUiThread(viewInBoundsTest);
3420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            checkForMainThreadException();
3430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            Thread.sleep(400);
3440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
3450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // delete all items
3460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mLayoutManager.expectLayouts(2);
3470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mAdapter.deleteAndNotify(0, mAdapter.getItemCount());
3480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mLayoutManager.waitForLayout(2);
3490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // test empty case
3500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        runTestOnUiThread(viewInBoundsTest);
3510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // set a new adapter with huge items to test full bounds check
3520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mLayoutManager.expectLayouts(1);
3530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        final int totalSpace = mLayoutManager.mPrimaryOrientation.getTotalSpace();
3540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        final TestAdapter newAdapter = new TestAdapter(100) {
3550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            @Override
3560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            public void onBindViewHolder(TestViewHolder holder,
3570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    int position) {
3580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                super.onBindViewHolder(holder, position);
3590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (mConfig.mOrientation == LinearLayoutManager.HORIZONTAL) {
3600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    holder.itemView.setMinimumWidth(totalSpace + 100);
3610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                } else {
3620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    holder.itemView.setMinimumHeight(totalSpace + 100);
3630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
3640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        };
3660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        runTestOnUiThread(new Runnable() {
3670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            @Override
3680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            public void run() {
3690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                mRecyclerView.setAdapter(newAdapter);
3700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        });
3720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mLayoutManager.waitForLayout(2);
3730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        runTestOnUiThread(viewInBoundsTest);
3740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        checkForMainThreadException();
3750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching
3770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // case
3780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        runTestOnUiThread(new Runnable() {
3790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            @Override
3800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            public void run() {
3810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final int diff;
3820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (mConfig.mReverseLayout) {
3830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    diff = -1;
3840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                } else {
3850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    diff = 1;
3860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
3870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final int distance = diff * 10;
3880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (mConfig.mOrientation == HORIZONTAL) {
3890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    mRecyclerView.scrollBy(distance, 0);
3900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                } else {
3910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    mRecyclerView.scrollBy(0, distance);
3920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
3930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
3940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        });
3950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        runTestOnUiThread(viewInBoundsTest);
3960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        checkForMainThreadException();
3970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
3980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
3990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
4000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void viewSnapTest() throws Throwable {
4010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        final Config config = ((Config) mConfig.clone()).itemCount(mConfig.mSpanCount + 1);
4020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        setupByConfig(config);
4030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mAdapter.mOnBindCallback = new OnBindCallback() {
4040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            @Override
4050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            void onBoundItem(TestViewHolder vh, int position) {
4060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                StaggeredGridLayoutManager.LayoutParams
4070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        lp = (StaggeredGridLayoutManager.LayoutParams) vh.itemView
4080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        .getLayoutParams();
4090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (config.mOrientation == HORIZONTAL) {
4100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    lp.width = mRecyclerView.getWidth() / 3;
4110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                } else {
4120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    lp.height = mRecyclerView.getHeight() / 3;
4130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
4140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
4150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            @Override
4170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            boolean assignRandomSize() {
4180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                return false;
4190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
4200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        };
4210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        waitFirstLayout();
4220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // run these tests twice. once initial layout, once after scroll
4230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        String logSuffix = "";
4240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        for (int i = 0; i < 2; i++) {
4250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            Map<Item, Rect> itemRectMap = mLayoutManager.collectChildCoordinates();
4260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            Rect recyclerViewBounds = getDecoratedRecyclerViewBounds();
4270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            // workaround for SGLM's span distribution issue. Right now, it may leave gaps so we
4280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            // avoid it by setting its layout params directly
4290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (config.mOrientation == HORIZONTAL) {
4300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                recyclerViewBounds.bottom -= recyclerViewBounds.height() % config.mSpanCount;
4310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } else {
4320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                recyclerViewBounds.right -= recyclerViewBounds.width() % config.mSpanCount;
4330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
4340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            Rect usedLayoutBounds = new Rect();
4360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            for (Rect rect : itemRectMap.values()) {
4370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                usedLayoutBounds.union(rect);
4380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
4390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (DEBUG) {
4410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                Log.d(TAG, "testing view snapping (" + logSuffix + ") for config " + config);
4420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
4430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (config.mOrientation == VERTICAL) {
4440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                assertEquals(config + " there should be no gap on left" + logSuffix,
4450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        usedLayoutBounds.left, recyclerViewBounds.left);
4460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                assertEquals(config + " there should be no gap on right" + logSuffix,
4470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        usedLayoutBounds.right, recyclerViewBounds.right);
4480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (config.mReverseLayout) {
4490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    assertEquals(config + " there should be no gap on bottom" + logSuffix,
4500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            usedLayoutBounds.bottom, recyclerViewBounds.bottom);
4510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    assertTrue(config + " there should be some gap on top" + logSuffix,
4520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            usedLayoutBounds.top > recyclerViewBounds.top);
4530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                } else {
4540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    assertEquals(config + " there should be no gap on top" + logSuffix,
4550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            usedLayoutBounds.top, recyclerViewBounds.top);
4560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    assertTrue(config + " there should be some gap at the bottom" + logSuffix,
4570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            usedLayoutBounds.bottom < recyclerViewBounds.bottom);
4580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
4590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } else {
4600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                assertEquals(config + " there should be no gap on top" + logSuffix,
4610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        usedLayoutBounds.top, recyclerViewBounds.top);
4620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                assertEquals(config + " there should be no gap at the bottom" + logSuffix,
4630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        usedLayoutBounds.bottom, recyclerViewBounds.bottom);
4640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (config.mReverseLayout) {
4650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    assertEquals(config + " there should be no on right" + logSuffix,
4660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            usedLayoutBounds.right, recyclerViewBounds.right);
4670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    assertTrue(config + " there should be some gap on left" + logSuffix,
4680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            usedLayoutBounds.left > recyclerViewBounds.left);
4690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                } else {
4700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    assertEquals(config + " there should be no gap on left" + logSuffix,
4710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            usedLayoutBounds.left, recyclerViewBounds.left);
4720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    assertTrue(config + " there should be some gap on right" + logSuffix,
4730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            usedLayoutBounds.right < recyclerViewBounds.right);
4740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
4750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
4760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final int scroll = config.mReverseLayout ? -500 : 500;
4770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            scrollBy(scroll);
4780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            logSuffix = " scrolled " + scroll;
4790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
4800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
4810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
4820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
4830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollToPositionWithOffsetTest() throws Throwable {
4840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        setupByConfig(mConfig);
4850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        waitFirstLayout();
4860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        OrientationHelper orientationHelper = OrientationHelper
4870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                .createOrientationHelper(mLayoutManager, mConfig.mOrientation);
4880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Rect layoutBounds = getDecoratedRecyclerViewBounds();
4890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // try scrolling towards head, should not affect anything
4900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Map<Item, Rect> before = mLayoutManager.collectChildCoordinates();
4910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        scrollToPositionWithOffset(0, 20);
4920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        assertRectSetsEqual(mConfig + " trying to over scroll with offset should be no-op",
4930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                before, mLayoutManager.collectChildCoordinates());
4940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // try offsetting some visible children
4950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int testCount = 10;
4960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        while (testCount-- > 0) {
4970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            // get middle child
4980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final View child = mLayoutManager.getChildAt(mLayoutManager.getChildCount() / 2);
4990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final int position = mRecyclerView.getChildLayoutPosition(child);
5000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final int startOffset = mConfig.mReverseLayout ?
5010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    orientationHelper.getEndAfterPadding() - orientationHelper
5020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            .getDecoratedEnd(child)
5030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    : orientationHelper.getDecoratedStart(child) - orientationHelper
5040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            .getStartAfterPadding();
5050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final int scrollOffset = startOffset / 2;
5060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mLayoutManager.expectLayouts(1);
5070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            scrollToPositionWithOffset(position, scrollOffset);
5080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mLayoutManager.waitForLayout(2);
5090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final int finalOffset = mConfig.mReverseLayout ?
5100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    orientationHelper.getEndAfterPadding() - orientationHelper
5110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            .getDecoratedEnd(child)
5120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    : orientationHelper.getDecoratedStart(child) - orientationHelper
5130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            .getStartAfterPadding();
5140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            assertEquals(mConfig + " scroll with offset on a visible child should work fine",
5150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    scrollOffset, finalOffset);
5160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
5170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
5180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // try scrolling to invisible children
5190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        testCount = 10;
5200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // we test above and below, one by one
5210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int offsetMultiplier = -1;
5220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        while (testCount-- > 0) {
5230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final TargetTuple target = findInvisibleTarget(mConfig);
5240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mLayoutManager.expectLayouts(1);
5250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final int offset = offsetMultiplier
5260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    * orientationHelper.getDecoratedMeasurement(mLayoutManager.getChildAt(0)) / 3;
5270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            scrollToPositionWithOffset(target.mPosition, offset);
5280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mLayoutManager.waitForLayout(2);
5290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final View child = mLayoutManager.findViewByPosition(target.mPosition);
5300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            assertNotNull(mConfig + " scrolling to a mPosition with offset " + offset
5310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    + " should layout it", child);
5320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final Rect bounds = mLayoutManager.getViewBounds(child);
5330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (DEBUG) {
5340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                Log.d(TAG, mConfig + " post scroll to invisible mPosition " + bounds + " in "
5350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        + layoutBounds + " with offset " + offset);
5360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
5370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
5380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (mConfig.mReverseLayout) {
5390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                assertEquals(mConfig + " when scrolling with offset to an invisible in reverse "
5400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                + "layout, its end should align with recycler view's end - offset",
5410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        orientationHelper.getEndAfterPadding() - offset,
5420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        orientationHelper.getDecoratedEnd(child)
5430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                );
5440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } else {
5450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                assertEquals(mConfig + " when scrolling with offset to an invisible child in normal"
5460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                + " layout its start should align with recycler view's start + "
5470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                + "offset",
5480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        orientationHelper.getStartAfterPadding() + offset,
5490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        orientationHelper.getDecoratedStart(child)
5500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                );
5510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
5520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            offsetMultiplier *= -1;
5530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
5540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
5550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
5560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
5570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scrollToPositionTest() throws Throwable {
5580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        setupByConfig(mConfig);
5590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        waitFirstLayout();
5600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        OrientationHelper orientationHelper = OrientationHelper
5610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                .createOrientationHelper(mLayoutManager, mConfig.mOrientation);
5620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Rect layoutBounds = getDecoratedRecyclerViewBounds();
5630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        for (int i = 0; i < mLayoutManager.getChildCount(); i++) {
5640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            View view = mLayoutManager.getChildAt(i);
5650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            Rect bounds = mLayoutManager.getViewBounds(view);
5660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (layoutBounds.contains(bounds)) {
5670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                Map<Item, Rect> initialBounds = mLayoutManager.collectChildCoordinates();
5680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final int position = mRecyclerView.getChildLayoutPosition(view);
5690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                StaggeredGridLayoutManager.LayoutParams layoutParams
5700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        = (StaggeredGridLayoutManager.LayoutParams) (view.getLayoutParams());
5710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                TestViewHolder vh = (TestViewHolder) layoutParams.mViewHolder;
5720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                assertEquals("recycler view mPosition should match adapter mPosition", position,
5730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        vh.mBoundItem.mAdapterIndex);
5740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (DEBUG) {
5750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    Log.d(TAG, "testing scroll to visible mPosition at " + position
5760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            + " " + bounds + " inside " + layoutBounds);
5770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
5780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                mLayoutManager.expectLayouts(1);
5790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                scrollToPosition(position);
5800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                mLayoutManager.waitForLayout(2);
5810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (DEBUG) {
5820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    view = mLayoutManager.findViewByPosition(position);
5830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    Rect newBounds = mLayoutManager.getViewBounds(view);
5840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    Log.d(TAG, "after scrolling to visible mPosition " +
5850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            bounds + " equals " + newBounds);
5860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
5870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
5880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                assertRectSetsEqual(
5890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mConfig + "scroll to mPosition on fully visible child should be no-op",
5900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        initialBounds, mLayoutManager.collectChildCoordinates());
5910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } else {
5920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                final int position = mRecyclerView.getChildLayoutPosition(view);
5930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (DEBUG) {
5940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    Log.d(TAG,
5950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            "child(" + position + ") not fully visible " + bounds + " not inside "
5960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                    + layoutBounds
5970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                    + mRecyclerView.getChildLayoutPosition(view)
5980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    );
5990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
6000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                mLayoutManager.expectLayouts(1);
6010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                runTestOnUiThread(new Runnable() {
6020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    @Override
6030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    public void run() {
6040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mLayoutManager.scrollToPosition(position);
6050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    }
6060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                });
6070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                mLayoutManager.waitForLayout(2);
6080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                view = mLayoutManager.findViewByPosition(position);
6090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                bounds = mLayoutManager.getViewBounds(view);
6100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (DEBUG) {
6110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    Log.d(TAG, "after scroll to partially visible child " + bounds + " in "
6120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                            + layoutBounds);
6130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
6140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                assertTrue(mConfig
6150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                + " after scrolling to a partially visible child, it should become fully "
6160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                + " visible. " + bounds + " not inside " + layoutBounds,
6170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        layoutBounds.contains(bounds)
6180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                );
6190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                assertTrue(
6200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mConfig + " when scrolling to a partially visible item, one of its edges "
6210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                + "should be on the boundaries",
6220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        orientationHelper.getStartAfterPadding() ==
6230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                orientationHelper.getDecoratedStart(view)
6240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                || orientationHelper.getEndAfterPadding() ==
6250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                orientationHelper.getDecoratedEnd(view));
6260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
6270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
6280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
6290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // try scrolling to invisible children
6300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int testCount = 10;
6310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        while (testCount-- > 0) {
6320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final TargetTuple target = findInvisibleTarget(mConfig);
6330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mLayoutManager.expectLayouts(1);
6340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            scrollToPosition(target.mPosition);
6350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mLayoutManager.waitForLayout(2);
6360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final View child = mLayoutManager.findViewByPosition(target.mPosition);
6370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            assertNotNull(mConfig + " scrolling to a mPosition should lay it out", child);
6380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            final Rect bounds = mLayoutManager.getViewBounds(child);
6390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (DEBUG) {
6400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                Log.d(TAG, mConfig + " post scroll to invisible mPosition " + bounds + " in "
6410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        + layoutBounds);
6420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
6430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            assertTrue(mConfig + " scrolling to a mPosition should make it fully visible",
6440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    layoutBounds.contains(bounds));
6450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (target.mLayoutDirection == LAYOUT_START) {
6460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                assertEquals(
6470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mConfig + " when scrolling to an invisible child above, its start should"
6480a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                + " align with recycler view's start",
6490a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        orientationHelper.getStartAfterPadding(),
6500a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        orientationHelper.getDecoratedStart(child)
6510a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                );
6520a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } else {
6530a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                assertEquals(mConfig + " when scrolling to an invisible child below, its end "
6540a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                + "should align with recycler view's end",
6550a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        orientationHelper.getEndAfterPadding(),
6560a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        orientationHelper.getDecoratedEnd(child)
6570a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                );
6580a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
6590a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
6600a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
6610a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
6620a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
6630a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void scollByTest() throws Throwable {
6640a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        setupByConfig(mConfig);
6650a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        waitFirstLayout();
6660a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // try invalid scroll. should not happen
6670a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        final View first = mLayoutManager.getChildAt(0);
6680a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        OrientationHelper primaryOrientation = OrientationHelper
6690a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                .createOrientationHelper(mLayoutManager, mConfig.mOrientation);
6700a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int scrollDist;
6710a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        if (mConfig.mReverseLayout) {
6720a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            scrollDist = primaryOrientation.getDecoratedMeasurement(first) / 2;
6730a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        } else {
6740a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            scrollDist = -primaryOrientation.getDecoratedMeasurement(first) / 2;
6750a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
6760a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Map<Item, Rect> before = mLayoutManager.collectChildCoordinates();
6770a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        scrollBy(scrollDist);
6780a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Map<Item, Rect> after = mLayoutManager.collectChildCoordinates();
6790a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        assertRectSetsEqual(
6800a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                mConfig + " if there are no more items, scroll should not happen (dt:" + scrollDist
6810a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        + ")",
6820a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                before, after
6830a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        );
6840a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
6850a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        scrollDist = -scrollDist * 3;
6860a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        before = mLayoutManager.collectChildCoordinates();
6870a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        scrollBy(scrollDist);
6880a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        after = mLayoutManager.collectChildCoordinates();
6890a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int layoutStart = primaryOrientation.getStartAfterPadding();
6900a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int layoutEnd = primaryOrientation.getEndAfterPadding();
6910a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        for (Map.Entry<Item, Rect> entry : before.entrySet()) {
6920a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            Rect afterRect = after.get(entry.getKey());
6930a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            // offset rect
6940a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (mConfig.mOrientation == VERTICAL) {
6950a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                entry.getValue().offset(0, -scrollDist);
6960a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } else {
6970a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                entry.getValue().offset(-scrollDist, 0);
6980a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
6990a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            if (afterRect == null || afterRect.isEmpty()) {
7000a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                // assert item is out of bounds
7010a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                int start, end;
7020a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                if (mConfig.mOrientation == VERTICAL) {
7030a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    start = entry.getValue().top;
7040a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    end = entry.getValue().bottom;
7050a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                } else {
7060a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    start = entry.getValue().left;
7070a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                    end = entry.getValue().right;
7080a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                }
7090a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                assertTrue(
7100a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        mConfig + " if item is missing after relayout, it should be out of bounds."
7110a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                + "item start: " + start + ", end:" + end + " layout start:"
7120a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                + layoutStart +
7130a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                ", layout end:" + layoutEnd,
7140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        start <= layoutStart && end <= layoutEnd ||
7150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                                start >= layoutEnd && end >= layoutEnd
7160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                );
7170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            } else {
7180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                assertEquals(mConfig + " Item should be laid out at the scroll offset coordinates",
7190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        entry.getValue(),
7200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                        afterRect);
7210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            }
7220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
7230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        assertViewPositions(mConfig);
7240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
7250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
7260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
7270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void layoutOrderTest() throws Throwable {
7280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        setupByConfig(mConfig);
7290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        assertViewPositions(mConfig);
7300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
7310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
7320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
7330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void consistentRelayout() throws Throwable {
7340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        consistentRelayoutTest(mConfig, false);
7350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
7360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
7370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    @Test
7380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void consistentRelayoutWithFullSpanFirstChild() throws Throwable {
7390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        consistentRelayoutTest(mConfig, true);
7400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
7410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar
7424510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    @Test
7434510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    public void dontRecycleViewsTranslatedOutOfBoundsFromStart() throws Throwable {
7444510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        final Config config = ((Config) mConfig.clone()).itemCount(1000);
7454510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        setupByConfig(config);
7464510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        waitFirstLayout();
7474510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        // pick position from child count so that it is not too far away
7484510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        int pos = mRecyclerView.getChildCount() * 2;
7494510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        smoothScrollToPosition(pos, true);
7504510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        final RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForAdapterPosition(pos);
7514510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        OrientationHelper helper = mLayoutManager.mPrimaryOrientation;
7524510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        int gap = helper.getDecoratedStart(vh.itemView);
7534510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        scrollBy(gap);
7544510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        gap = helper.getDecoratedStart(vh.itemView);
7554510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        assertThat("test sanity", gap, is(0));
7564510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
7574510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        final int size = helper.getDecoratedMeasurement(vh.itemView);
7584510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        AttachDetachCollector collector = new AttachDetachCollector(mRecyclerView);
7594510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        runTestOnUiThread(new Runnable() {
7604510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            @Override
7614510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            public void run() {
7624510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                if (mConfig.mOrientation == HORIZONTAL) {
7634510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                    ViewCompat.setTranslationX(vh.itemView, size * 2);
7644510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                } else {
7654510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                    ViewCompat.setTranslationY(vh.itemView, size * 2);
7664510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                }
7674510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            }
7684510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        });
7694510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        scrollBy(size * 2);
7704510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        assertThat(collector.getDetached(), not(hasItem(sameInstance(vh.itemView))));
7714510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        assertThat(vh.itemView.getParent(), is((ViewParent) mRecyclerView));
7724510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        assertThat(vh.getAdapterPosition(), is(pos));
7734510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        scrollBy(size * 2);
7744510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        assertThat(collector.getDetached(), hasItem(sameInstance(vh.itemView)));
7754510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    }
7764510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
7774510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    @Test
7784510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    public void dontRecycleViewsTranslatedOutOfBoundsFromEnd() throws Throwable {
7794510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        final Config config = ((Config) mConfig.clone()).itemCount(1000);
7804510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        setupByConfig(config);
7814510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        waitFirstLayout();
7824510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        // pick position from child count so that it is not too far away
7834510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        int pos = mRecyclerView.getChildCount() * 2;
7844510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        mLayoutManager.expectLayouts(1);
7854510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        scrollToPosition(pos);
7864510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        mLayoutManager.waitForLayout(2);
7874510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        final RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForAdapterPosition(pos);
7884510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        OrientationHelper helper = mLayoutManager.mPrimaryOrientation;
7894510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        int gap = helper.getEnd() - helper.getDecoratedEnd(vh.itemView);
7904510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        scrollBy(-gap);
7914510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        gap = helper.getEnd() - helper.getDecoratedEnd(vh.itemView);
7924510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        assertThat("test sanity", gap, is(0));
7934510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
7944510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        final int size = helper.getDecoratedMeasurement(vh.itemView);
7954510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        AttachDetachCollector collector = new AttachDetachCollector(mRecyclerView);
7964510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        runTestOnUiThread(new Runnable() {
7974510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            @Override
7984510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            public void run() {
7994510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                if (mConfig.mOrientation == HORIZONTAL) {
8004510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                    ViewCompat.setTranslationX(vh.itemView, -size * 2);
8014510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                } else {
8024510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                    ViewCompat.setTranslationY(vh.itemView, -size * 2);
8034510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar                }
8044510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar            }
8054510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        });
8064510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        scrollBy(-size * 2);
8074510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        assertThat(collector.getDetached(), not(hasItem(sameInstance(vh.itemView))));
8084510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        assertThat(vh.itemView.getParent(), is((ViewParent) mRecyclerView));
8094510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        assertThat(vh.getAdapterPosition(), is(pos));
8104510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        scrollBy(-size * 2);
8114510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar        assertThat(collector.getDetached(), hasItem(sameInstance(vh.itemView)));
8124510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar    }
8134510b5c24adad2b94df9b84c6b73f5534ffe9b57Yigit Boyar
8140a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    public void consistentRelayoutTest(Config config, boolean firstChildMultiSpan)
8150a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            throws Throwable {
8160a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        setupByConfig(config);
8170a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        if (firstChildMultiSpan) {
8180a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            mAdapter.mFullSpanItems.add(0);
8190a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
8200a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        waitFirstLayout();
8210a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // record all child positions
8220a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Map<Item, Rect> before = mLayoutManager.collectChildCoordinates();
8230a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        requestLayoutOnUIThread(mRecyclerView);
8240a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        Map<Item, Rect> after = mLayoutManager.collectChildCoordinates();
8250a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        assertRectSetsEqual(
8260a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                config + " simple re-layout, firstChildMultiSpan:" + firstChildMultiSpan, before,
8270a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                after);
8280a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        // scroll some to create inconsistency
8290a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        View firstChild = mLayoutManager.getChildAt(0);
8300a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        final int firstChildStartBeforeScroll = mLayoutManager.mPrimaryOrientation
8310a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                .getDecoratedStart(firstChild);
8320a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        int distance = mLayoutManager.mPrimaryOrientation.getDecoratedMeasurement(firstChild) / 2;
8330a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        if (config.mReverseLayout) {
8340a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar            distance *= -1;
8350a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        }
8360a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        scrollBy(distance);
8370a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        waitForMainThread(2);
8380a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        assertTrue("scroll by should move children", firstChildStartBeforeScroll !=
8390a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar                mLayoutManager.mPrimaryOrientation.getDecoratedStart(firstChild));
8400a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        before = mLayoutManager.collectChildCoordinates();
8410a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mLayoutManager.expectLayouts(1);
8420a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        requestLayoutOnUIThread(mRecyclerView);
8430a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        mLayoutManager.waitForLayout(2);
8440a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        after = mLayoutManager.collectChildCoordinates();
8450a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar        assertRectSetsEqual(config + " simple re-layout after scroll", before, after);
8460a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar    }
8470a017072206f93474ccd2706e7983c2ff778b904Yigit Boyar}
848