RecyclerViewAnimationsTest.java revision 7c7fba8365684e1ccfc4f39f286df4d100c6c81f
1d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase/*
2d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * Copyright (C) 2014 The Android Open Source Project
3d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase *
4d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * Licensed under the Apache License, Version 2.0 (the "License");
5d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * you may not use this file except in compliance with the License.
6d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * You may obtain a copy of the License at
7d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase *
8d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase *      http://www.apache.org/licenses/LICENSE-2.0
9d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase *
10d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * Unless required by applicable law or agreed to in writing, software
11d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * distributed under the License is distributed on an "AS IS" BASIS,
12d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * See the License for the specific language governing permissions and
14d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase * limitations under the License.
15d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase */
16d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
17d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haasepackage android.support.v7.widget;
18d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
19b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyarimport android.content.Context;
20888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyarimport android.graphics.Canvas;
21b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyarimport android.util.AttributeSet;
22b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyarimport android.util.Log;
23b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyarimport android.view.View;
24504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyarimport android.view.ViewGroup;
25b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
2611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.ArrayList;
278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyarimport java.util.HashMap;
2811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.HashSet;
2911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.List;
3011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.Map;
3111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyarimport java.util.Set;
32888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyarimport java.util.concurrent.CountDownLatch;
33d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.TimeUnit;
34d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyarimport java.util.concurrent.atomic.AtomicInteger;
35d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
36d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haasepublic class RecyclerViewAnimationsTest extends BaseRecyclerViewInstrumentationTest {
37d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
38668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar    private static final boolean DEBUG = false;
39b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
40b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    private static final String TAG = "RecyclerViewAnimationsTest";
41b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
42b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    AnimationLayoutManager mLayoutManager;
43b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
44b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    TestAdapter mTestAdapter;
45b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
46b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    public RecyclerViewAnimationsTest() {
47b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        super(DEBUG);
48b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
49b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
50b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    @Override
51b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    protected void setUp() throws Exception {
52b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        super.setUp();
53b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
54b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
55b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    RecyclerView setupBasic(int itemCount) throws Throwable {
56b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        return setupBasic(itemCount, 0, itemCount);
57b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
58b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
59b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    RecyclerView setupBasic(int itemCount, int firstLayoutStartIndex, int firstLayoutItemCount)
60b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            throws Throwable {
6111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        return setupBasic(itemCount, firstLayoutStartIndex, firstLayoutItemCount, null);
6211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar    }
6311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar
6411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar    RecyclerView setupBasic(int itemCount, int firstLayoutStartIndex, int firstLayoutItemCount,
6511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            TestAdapter testAdapter)
6611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            throws Throwable {
67888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        final TestRecyclerView recyclerView = new TestRecyclerView(getActivity());
68b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        recyclerView.setHasFixedSize(true);
6911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        if (testAdapter == null) {
7011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            mTestAdapter = new TestAdapter(itemCount);
7111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        } else {
7211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            mTestAdapter = testAdapter;
7311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        }
74b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        recyclerView.setAdapter(mTestAdapter);
75b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager = new AnimationLayoutManager();
76b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        recyclerView.setLayoutManager(mLayoutManager);
77b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutMin = firstLayoutStartIndex;
78b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = firstLayoutItemCount;
79b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
80b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.expectLayouts(1);
81888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        recyclerView.expectDraw(1);
82b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        setRecyclerView(recyclerView);
83b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.waitForLayout(2);
84888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        recyclerView.waitForDraw(1);
85888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.reset();
868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        getInstrumentation().waitForIdleSync();
87b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        assertEquals("extra layouts should not happen", 1, mLayoutManager.getTotalLayoutCount());
888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        assertEquals("all expected children should be laid out", firstLayoutItemCount,
898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                mLayoutManager.getChildCount());
90b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        return recyclerView;
91b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
92b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
937c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen    public void testChangeAnimations()  throws Throwable {
947c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        final boolean[] booleans = {true, false};
957c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        for (boolean supportsChange : booleans) {
967c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            for (boolean changeType : booleans) {
977c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                for (boolean hasStableIds : booleans) {
987c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    for (boolean deleteSomeItems : booleans) {
997c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                        changeAnimTest(supportsChange, changeType, hasStableIds, deleteSomeItems);
1007c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    }
1017c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    removeRecyclerView();
1027c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                }
1037c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            }
1047c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        }
1057c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen    }
1067c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen    public void changeAnimTest(final boolean supportsChangeAnim, final boolean changeType,
1077c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            final boolean hasStableIds, final boolean deleteSomeItems)  throws Throwable {
1087c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        final int changedIndex = 3;
1097c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        final int defaultType = 1;
1107c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        final AtomicInteger changedIndexNewType = new AtomicInteger(defaultType);
1117c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        final String logPrefix = "supportsChangeAnim:" + supportsChangeAnim +
1127c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                ", change view type:" + changeType +
1137c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                ", has stable ids:" + hasStableIds +
1147c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                ", force predictive:" + deleteSomeItems;
1157c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        TestAdapter testAdapter = new TestAdapter(10) {
1167c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            @Override
1177c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            public int getItemViewType(int position) {
1187c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                return position == changedIndex ? changedIndexNewType.get() : defaultType;
1197c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            }
1207c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen
1217c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            @Override
1227c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            public TestViewHolder onCreateViewHolder(ViewGroup parent,
1237c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    int viewType) {
1247c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                TestViewHolder vh = super.onCreateViewHolder(parent, viewType);
1257c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                if (DEBUG) {
1267c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    Log.d(TAG, logPrefix + " onCreateVH" + vh.toString());
1277c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                }
1287c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                return vh;
1297c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            }
1307c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen
1317c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            @Override
1327c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            public void onBindViewHolder(TestViewHolder holder,
1337c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    int position) {
1347c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                super.onBindViewHolder(holder, position);
1357c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                if (DEBUG) {
1367c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    Log.d(TAG, logPrefix + " onBind to " + position + "" + holder.toString());
1377c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                }
1387c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            }
1397c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        };
1407c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        testAdapter.setHasStableIds(hasStableIds);
1417c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        setupBasic(testAdapter.getItemCount(), 0, 10, testAdapter);
1427c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        mRecyclerView.getItemAnimator().setSupportsChangeAnimations(supportsChangeAnim);
1437c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen
1447c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        final RecyclerView.ViewHolder toBeChangedVH =
1457c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                mRecyclerView.findViewHolderForPosition(changedIndex);
1467c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
1477c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            @Override
1487c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            void afterPreLayout(RecyclerView.Recycler recycler,
1497c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    AnimationLayoutManager layoutManager,
1507c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    RecyclerView.State state) {
1517c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForPosition(changedIndex);
1527c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                if (supportsChangeAnim) {
1537c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    assertTrue(logPrefix + " changed view holder should have correct flag"
1547c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                            , vh.isChanged());
1557c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                } else {
1567c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    assertFalse(logPrefix + " changed view holder should have correct flag"
1577c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                            , vh.isChanged());
1587c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                }
1597c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            }
1607c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen
1617c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            @Override
1627c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            void afterPostLayout(RecyclerView.Recycler recycler,
1637c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    AnimationLayoutManager layoutManager, RecyclerView.State state) {
1647c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForPosition(changedIndex);
1657c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                assertFalse(logPrefix + "VH should not be marked as changed", vh.isChanged());
1667c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                if (supportsChangeAnim) {
1677c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    assertNotSame(logPrefix + "a new VH should be given if change is supported",
1687c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                            toBeChangedVH, vh);
1697c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                } else if (!changeType && hasStableIds) {
1707c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    assertSame(logPrefix + "if change animations are not supported but we have "
1717c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                            + "stable ids, same view holder should be returned", toBeChangedVH, vh);
1727c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                }
1737c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                super.beforePostLayout(recycler, layoutManager, state);
1747c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            }
1757c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        };
1767c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        mLayoutManager.expectLayouts(1);
1777c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        if (changeType) {
1787c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            changedIndexNewType.set(defaultType + 1);
1797c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        }
1807c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        if (deleteSomeItems) {
1817c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            runTestOnUiThread(new Runnable() {
1827c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                @Override
1837c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                public void run() {
1847c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    try {
1857c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                        mTestAdapter.deleteAndNotify(changedIndex + 2, 1);
1867c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                        mTestAdapter.notifyItemChanged(3);
1877c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    } catch (Throwable throwable) {
1887c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                        throwable.printStackTrace();
1897c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    }
1907c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen
1917c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                }
1927c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            });
1937c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        } else {
1947c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            mTestAdapter.notifyItemChanged(3);
1957c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        }
1967c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen
1977c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        mLayoutManager.waitForLayout(2);
1987c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen    }
1997c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen
200504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar    public void testRecycleDuringAnimations() throws Throwable {
201504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        final AtomicInteger childCount = new AtomicInteger(0);
202504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        final TestAdapter adapter = new TestAdapter(1000) {
203504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
204504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public TestViewHolder onCreateViewHolder(ViewGroup parent,
205504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    int viewType) {
206504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                childCount.incrementAndGet();
207504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                return super.onCreateViewHolder(parent, viewType);
208504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
209504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        };
210504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        setupBasic(1000, 10, 20, adapter);
211504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 10;
212504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 20;
213504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
214504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        mRecyclerView.setRecycledViewPool(new RecyclerView.RecycledViewPool() {
215504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
216504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public void putRecycledView(RecyclerView.ViewHolder scrap) {
217504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                super.putRecycledView(scrap);
218504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                childCount.decrementAndGet();
219504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
220504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
221504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
222504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public RecyclerView.ViewHolder getRecycledView(int viewType) {
223504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                final RecyclerView.ViewHolder recycledView = super.getRecycledView(viewType);
224504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                if (recycledView != null) {
225504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    childCount.incrementAndGet();
226504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                }
227504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                return recycledView;
228504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
229504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        });
230504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
231504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        // now keep adding children to trigger more children being created etc.
232504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        for (int i = 0; i < 100; i ++) {
233504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            adapter.addAndNotify(15, 1);
234504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            Thread.sleep(50);
235504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
236504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        getInstrumentation().waitForIdleSync();
237504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        waitForAnimations(2);
238504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        assertEquals("Children count should add up", childCount.get(),
239504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size());
240504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar    }
241504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
242b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar    public void testNotifyDataSetChanged() throws Throwable {
243b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        setupBasic(10, 3, 4);
244b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        int layoutCount = mLayoutManager.mTotalLayoutCount;
245b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        mLayoutManager.expectLayouts(1);
246b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        runTestOnUiThread(new Runnable() {
247b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar            @Override
248b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar            public void run() {
249b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar                try {
250b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar                    mTestAdapter.deleteAndNotify(4, 1);
251cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                    mTestAdapter.dispatchDataSetChanged();
252b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar                } catch (Throwable throwable) {
253b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar                    throwable.printStackTrace();
254b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar                }
255b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar
256b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar            }
257b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        });
258b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        mLayoutManager.waitForLayout(2);
259b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        getInstrumentation().waitForIdleSync();
260b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        assertEquals("on notify data set changed, predictive animations should not run",
261b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar                layoutCount + 1, mLayoutManager.mTotalLayoutCount);
262b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        mLayoutManager.expectLayouts(2);
263b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        mTestAdapter.addAndNotify(4, 2);
264b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        // make sure animations recover
265b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        mLayoutManager.waitForLayout(2);
266b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar    }
267b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar
268e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar    public void testStableIdNotifyDataSetChanged() throws Throwable {
269e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        final int itemCount = 20;
270e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        List<Item> initialSet = new ArrayList<Item>();
271e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        final TestAdapter adapter = new TestAdapter(itemCount) {
272e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            @Override
273e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            public long getItemId(int position) {
274e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                return mItems.get(position).mId;
275e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            }
276e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        };
277e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        adapter.setHasStableIds(true);
278e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        initialSet.addAll(adapter.mItems);
279e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        positionStatesTest(itemCount, 5, 5, adapter, new AdapterOps() {
280e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            @Override
281e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            void onRun(TestAdapter testAdapter) throws Throwable {
282e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                Item item5 = adapter.mItems.get(5);
283e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                Item item6 = adapter.mItems.get(6);
284e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                item5.mAdapterIndex = 6;
285e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                item6.mAdapterIndex = 5;
286e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                adapter.mItems.remove(5);
287e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                adapter.mItems.add(6, item5);
288cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                adapter.dispatchDataSetChanged();
289e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                //hacky, we support only 1 layout pass
290e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                mLayoutManager.layoutLatch.countDown();
291e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            }
292e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        }, PositionConstraint.scrap(6, -1, 5), PositionConstraint.scrap(5, -1, 6),
293e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                PositionConstraint.scrap(7, -1, 7), PositionConstraint.scrap(8, -1, 8),
294e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                PositionConstraint.scrap(9, -1, 9));
295e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        // now mix items.
296e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar    }
297e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar
298d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar
2998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void testGetItemForDeletedView() throws Throwable {
3008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        getItemForDeletedViewTest(false);
3018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        getItemForDeletedViewTest(true);
30211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar    }
30311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar
3048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void getItemForDeletedViewTest(boolean stableIds) throws Throwable {
30511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        final Set<Integer> itemViewTypeQueries = new HashSet<Integer>();
30611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        final Set<Integer> itemIdQueries = new HashSet<Integer>();
30711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        TestAdapter adapter = new TestAdapter(10) {
30811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            @Override
30911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            public int getItemViewType(int position) {
31011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                itemViewTypeQueries.add(position);
31111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                return super.getItemViewType(position);
31211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            }
31311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar
31411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            @Override
31511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            public long getItemId(int position) {
31611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                itemIdQueries.add(position);
31711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                return mItems.get(position).mId;
31811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            }
31911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        };
32011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        adapter.setHasStableIds(stableIds);
32111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        setupBasic(10, 0, 10, adapter);
32211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        assertEquals("getItemViewType for all items should be called", 10,
32311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                itemViewTypeQueries.size());
32411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        if (adapter.hasStableIds()) {
32511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            assertEquals("getItemId should be called when adapter has stable ids", 10,
32611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                    itemIdQueries.size());
32711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        } else {
32811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            assertEquals("getItemId should not be called when adapter does not have stable ids", 0,
32911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                    itemIdQueries.size());
33011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        }
33111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        itemViewTypeQueries.clear();
33211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        itemIdQueries.clear();
33311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        mLayoutManager.expectLayouts(2);
33411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        // delete last two
33511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        final int deleteStart = 8;
33611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        final int deleteCount = adapter.getItemCount() - deleteStart;
33711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        adapter.deleteAndNotify(deleteStart, deleteCount);
33811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        mLayoutManager.waitForLayout(2);
33911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        for (int i = 0; i < deleteStart; i++) {
34011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            assertTrue("getItemViewType for existing item " + i + " should be called",
34111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                    itemViewTypeQueries.contains(i));
34211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            if (adapter.hasStableIds()) {
34311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                assertTrue("getItemId for existing item " + i
34411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                        + " should be called when adapter has stable ids",
34511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                        itemIdQueries.contains(i));
34611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            }
34711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        }
34811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        for (int i = deleteStart; i < deleteStart + deleteCount; i++) {
34911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            assertFalse("getItemViewType for deleted item " + i + " SHOULD NOT be called",
35011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                    itemViewTypeQueries.contains(i));
35111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            if (adapter.hasStableIds()) {
3528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertFalse("getItemId for deleted item " + i + " SHOULD NOT be called",
35311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                        itemIdQueries.contains(i));
35411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            }
35511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        }
35611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar    }
35711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar
3588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void testDeleteInvisibleMultiStep() throws Throwable {
3598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        setupBasic(1000, 1, 7);
3608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 1;
3618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 7;
3628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.expectLayouts(1);
3638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // try to trigger race conditions
3648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int targetItemCount = mTestAdapter.getItemCount();
3658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        for (int i = 0; i < 100; i++) {
3668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            mTestAdapter.deleteAndNotify(new int[]{0, 1}, new int[]{7, 1});
3678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            targetItemCount -= 2;
3688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
3698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // wait until main thread runnables are consumed
3708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        while (targetItemCount != mTestAdapter.getItemCount()) {
3718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            Thread.sleep(100);
3728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
3738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.waitForLayout(2);
3748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
3758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
3768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void testAddManyMultiStep() throws Throwable {
3778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        setupBasic(10, 1, 7);
3788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 1;
3798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 7;
3808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.expectLayouts(1);
3818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // try to trigger race conditions
3828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int targetItemCount = mTestAdapter.getItemCount();
3838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        for (int i = 0; i < 100; i++) {
3848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            mTestAdapter.addAndNotify(0, 1);
3858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            mTestAdapter.addAndNotify(7, 1);
3868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            targetItemCount += 2;
3878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
3888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // wait until main thread runnables are consumed
3898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        while (targetItemCount != mTestAdapter.getItemCount()) {
3908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            Thread.sleep(100);
3918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
3928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.waitForLayout(2);
3938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
3948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
3958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void testBasicDelete() throws Throwable {
3968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        setupBasic(10);
3978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        final OnLayoutCallbacks callbacks = new OnLayoutCallbacks() {
3988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            @Override
3998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            public void postDispatchLayout() {
4008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                // verify this only in first layout
4018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertEquals("deleted views should still be children of RV",
4028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        mLayoutManager.getChildCount() + mDeletedViewCount
4038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        , mRecyclerView.getChildCount());
4048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
4058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
4068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            @Override
4078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            void afterPreLayout(RecyclerView.Recycler recycler,
4088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    AnimationLayoutManager layoutManager,
4098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    RecyclerView.State state) {
4108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                super.afterPreLayout(recycler, layoutManager, state);
4118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                mLayoutItemCount = 3;
4128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                mLayoutMin = 0;
4138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
4148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        };
4158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        callbacks.mLayoutItemCount = 10;
4168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        callbacks.setExpectedItemCounts(10, 3);
4178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.setOnLayoutCallbacks(callbacks);
4188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
4198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.expectLayouts(2);
4208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mTestAdapter.deleteAndNotify(0, 7);
4218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.waitForLayout(2);
4228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        callbacks.reset();// when animations end another layout will happen
4238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
4248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
4258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
426d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar    public void testAdapterChangeDuringScrolling() throws Throwable {
427d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        setupBasic(10);
428d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        final AtomicInteger onLayoutItemCount = new AtomicInteger(0);
429d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        final AtomicInteger onScrollItemCount = new AtomicInteger(0);
430d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar
431d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        mLayoutManager.setOnLayoutCallbacks(new OnLayoutCallbacks() {
432d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            @Override
433d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            void onLayoutChildren(RecyclerView.Recycler recycler,
434d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                    AnimationLayoutManager lm, RecyclerView.State state) {
435d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                onLayoutItemCount.set(state.getItemCount());
436d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                super.onLayoutChildren(recycler, lm, state);
437d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            }
438d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar
439d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            @Override
440d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            public void onScroll(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
441d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                onScrollItemCount.set(state.getItemCount());
442d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                super.onScroll(dx, recycler, state);
443d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            }
444d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        });
445d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        runTestOnUiThread(new Runnable() {
446d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            @Override
447d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            public void run() {
448d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                mTestAdapter.mItems.remove(5);
449d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                mTestAdapter.notifyItemRangeRemoved(5, 1);
450d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                mRecyclerView.scrollBy(0, 100);
451d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                assertTrue("scrolling while there are pending adapter updates should "
452d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                        + "trigger a layout", mLayoutManager.mOnLayoutCallbacks.mLayoutCount > 0);
453d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                assertEquals("scroll by should be called w/ updated adapter count",
454d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                        mTestAdapter.mItems.size(), onScrollItemCount.get());
455d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar
456d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            }
457d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        });
458d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar    }
459d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar
460b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    public void testAddInvisibleAndVisible() throws Throwable {
461b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        setupBasic(10, 1, 7);
462b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.expectLayouts(2);
463b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(10, 12);
4642d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar        mTestAdapter.addAndNotify(new int[]{0, 1}, new int[]{7, 1});// add a new item 0 // invisible
465b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.waitForLayout(2);
466b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
467b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
468b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    public void testAddInvisible() throws Throwable {
469b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        setupBasic(10, 1, 7);
470b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.expectLayouts(1);
471b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(10, 12);
4722d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar        mTestAdapter.addAndNotify(new int[]{0, 1}, new int[]{8, 1});// add a new item 0
473b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.waitForLayout(2);
474b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
475b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
476b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    public void testBasicAdd() throws Throwable {
477b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        setupBasic(10);
478b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.expectLayouts(2);
479b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        setExpectedItemCounts(10, 13);
480b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mTestAdapter.addAndNotify(2, 3);
481b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.waitForLayout(2);
482b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
483b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
484888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar    public TestRecyclerView getTestRecyclerView() {
485888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        return (TestRecyclerView) mRecyclerView;
486888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar    }
487888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar
488888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar    public void testRemoveScrapInvalidate() throws Throwable {
489888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        setupBasic(10);
490888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        TestRecyclerView testRecyclerView = getTestRecyclerView();
491888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        mLayoutManager.expectLayouts(1);
492888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        testRecyclerView.expectDraw(1);
493888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        runTestOnUiThread(new Runnable() {
494888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar            @Override
495888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar            public void run() {
496888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar                mTestAdapter.mItems.clear();
497888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar                mTestAdapter.notifyDataSetChanged();
498888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar            }
499888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        });
500888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        mLayoutManager.waitForLayout(2);
501888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        testRecyclerView.waitForDraw(2);
502888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar    }
503888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar
504b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    public void testDeleteVisibleAndInvisible() throws Throwable {
505b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        setupBasic(11, 3, 5); //layout items  3 4 5 6 7
506b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.expectLayouts(2);
5078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        setLayoutRange(3, 5); //layout previously invisible child 10 from end of the list
508b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        setExpectedItemCounts(9, 8);
509b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mTestAdapter.deleteAndNotify(new int[]{4, 1}, new int[]{7, 2});// delete items 4, 8, 9
510b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.waitForLayout(2);
511b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
512b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
5138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void testFindPositionOffset() throws Throwable {
5148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        setupBasic(10);
5158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        runTestOnUiThread(new Runnable() {
5168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            @Override
5178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            public void run() {
5188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                // [0,1,2,3,4]
5198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                // delete 1
5208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                mTestAdapter.notifyItemRangeRemoved(1, 1);
5218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                // delete 3
5228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                mTestAdapter.notifyItemRangeRemoved(2, 1);
5238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                mAdapterHelper.preProcess();
5248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                // [0,2,4]
5258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertEquals("offset check", 0, mAdapterHelper.findPositionOffset(0));
5268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertEquals("offset check", 1, mAdapterHelper.findPositionOffset(2));
5278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertEquals("offset check", 2, mAdapterHelper.findPositionOffset(4));
5288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
5298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
5308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        });
5318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
5328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
533b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    private void setLayoutRange(int start, int count) {
534b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutMin = start;
535b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = count;
536b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
537b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
538b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    private void setExpectedItemCounts(int preLayout, int postLayout) {
539b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(preLayout, postLayout);
540b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
541b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
542b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    public void testDeleteInvisible() throws Throwable {
543b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        setupBasic(10, 1, 7);
5448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 1;
5458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 7;
546b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.expectLayouts(1);
547b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(8, 8);
5488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mTestAdapter.deleteAndNotify(new int[]{0, 1}, new int[]{7, 1});// delete item id 0,8
549b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.waitForLayout(2);
550b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
551b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
552e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar    private CollectPositionResult findByPos(RecyclerView recyclerView,
553e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            RecyclerView.Recycler recycler, RecyclerView.State state, int position) {
554e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        View view = recycler.getViewForPosition(position, true);
555e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(view);
556e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        if (vh.wasReturnedFromScrap()) {
557e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            vh.clearReturnedFromScrapFlag(); //keep data consistent.
558e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            return CollectPositionResult.fromScrap(vh);
559e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        } else {
560e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            return CollectPositionResult.fromAdapter(vh);
5618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
5628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
5638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
564e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar    public Map<Integer, CollectPositionResult> collectPositions(RecyclerView recyclerView,
565e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            RecyclerView.Recycler recycler, RecyclerView.State state, int... positions) {
5668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        Map<Integer, CollectPositionResult> positionToAdapterMapping
5678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                = new HashMap<Integer, CollectPositionResult>();
5688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        for (int position : positions) {
5698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (position < 0) {
5708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                continue;
5718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
572e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            positionToAdapterMapping.put(position,
573e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    findByPos(recyclerView, recycler, state, position));
5748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
5758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        return positionToAdapterMapping;
5768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
5778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
5788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void testAddDelete2() throws Throwable {
5798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        positionStatesTest(5, 0, 5, new AdapterOps() {
5808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // 0 1 2 3 4
5818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // 0 1 2 a b 3 4
5828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // 0 1 b 3 4
5838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // pre: 0 1 2 3 4
5848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // pre w/ adap: 0 1 2 b 3 4
585d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            @Override
5868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            void onRun(TestAdapter adapter) throws Throwable {
5878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                adapter.addDeleteAndNotify(new int[]{3, 2}, new int[]{2, -2});
588b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            }
5898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }, PositionConstraint.scrap(2, 2, -1), PositionConstraint.scrap(1, 1, 1),
5908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(3, 3, 3)
5918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        );
5928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
593d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
5948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void testAddDelete1() throws Throwable {
5958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        positionStatesTest(5, 0, 5, new AdapterOps() {
5968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // 0 1 2 3 4
5978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // 0 1 2 a b 3 4
5988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // 0 2 a b 3 4
5998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // 0 c d 2 a b 3 4
6008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // 0 c d 2 a 4
6018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // c d 2 a 4
6028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // pre: 0 1 2 3 4
603b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            @Override
6048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            void onRun(TestAdapter adapter) throws Throwable {
6058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                adapter.addDeleteAndNotify(new int[]{3, 2}, new int[]{1, -1},
6068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        new int[]{1, 2}, new int[]{5, -2}, new int[]{0, -1});
607d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            }
6088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }, PositionConstraint.scrap(0, 0, -1), PositionConstraint.scrap(1, 1, -1),
6098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(2, 2, 2), PositionConstraint.scrap(3, 3, -1),
6108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(4, 4, 4), PositionConstraint.adapter(0),
6118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.adapter(1), PositionConstraint.adapter(3)
6128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        );
6138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
6148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
6158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void testAddSameIndexTwice() throws Throwable {
6168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        positionStatesTest(12, 2, 7, new AdapterOps() {
6178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            @Override
6188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            void onRun(TestAdapter adapter) throws Throwable {
6198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                adapter.addAndNotify(new int[]{1, 2}, new int[]{5, 1}, new int[]{5, 1},
6208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        new int[]{11, 1});
6218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
6228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }, PositionConstraint.adapterScrap(0, 0), PositionConstraint.adapterScrap(1, 3),
6238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(2, 2, 4), PositionConstraint.scrap(3, 3, 7),
6248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(4, 4, 8), PositionConstraint.scrap(7, 7, 12),
6258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(8, 8, 13)
6268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        );
6278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
6288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
6298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void testDeleteTwice() throws Throwable {
6308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        positionStatesTest(12, 2, 7, new AdapterOps() {
6318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            @Override
6328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            void onRun(TestAdapter adapter) throws Throwable {
6338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                adapter.deleteAndNotify(new int[]{0, 1}, new int[]{1, 1}, new int[]{7, 1},
6348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        new int[]{0, 1});// delete item ids 0,2,9,1
6358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
6368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }, PositionConstraint.scrap(2, 0, -1), PositionConstraint.scrap(3, 1, 0),
6378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(4, 2, 1), PositionConstraint.scrap(5, 3, 2),
6388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(6, 4, 3), PositionConstraint.scrap(8, 6, 5),
6398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.adapterScrap(7, 6), PositionConstraint.adapterScrap(8, 7)
6408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        );
6418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
6428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
643b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
6448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void positionStatesTest(int itemCount, int firstLayoutStartIndex,
645e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            int firstLayoutItemCount, AdapterOps adapterChanges,
646e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            final PositionConstraint... constraints) throws Throwable {
647e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        positionStatesTest(itemCount, firstLayoutStartIndex, firstLayoutItemCount, null,
648e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                adapterChanges,  constraints);
649e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar    }
650e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar    public void positionStatesTest(int itemCount, int firstLayoutStartIndex,
651e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            int firstLayoutItemCount,TestAdapter adapter, AdapterOps adapterChanges,
652e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            final PositionConstraint... constraints) throws Throwable {
653e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        setupBasic(itemCount, firstLayoutStartIndex, firstLayoutItemCount, adapter);
654b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.expectLayouts(2);
6558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
6568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            @Override
6578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            void beforePreLayout(RecyclerView.Recycler recycler, AnimationLayoutManager lm,
6588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    RecyclerView.State state) {
6598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                super.beforePreLayout(recycler, lm, state);
6608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                //harmless
6618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                lm.detachAndScrapAttachedViews(recycler);
6628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                final int[] ids = new int[constraints.length];
6638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                for (int i = 0; i < constraints.length; i++) {
6648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    ids[i] = constraints[i].mPreLayoutPos;
6658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
6668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                Map<Integer, CollectPositionResult> positions
667e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        = collectPositions(lm.mRecyclerView, recycler, state, ids);
6688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                for (PositionConstraint constraint : constraints) {
6698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    if (constraint.mPreLayoutPos != -1) {
6708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        constraint.validate(state, positions.get(constraint.mPreLayoutPos),
6718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                                lm.getLog());
6728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    }
6738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
6748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
6758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
6768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            @Override
6778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            void beforePostLayout(RecyclerView.Recycler recycler, AnimationLayoutManager lm,
6788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    RecyclerView.State state) {
6798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                super.beforePostLayout(recycler, lm, state);
6808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                lm.detachAndScrapAttachedViews(recycler);
6818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                final int[] ids = new int[constraints.length];
6828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                for (int i = 0; i < constraints.length; i++) {
6838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    ids[i] = constraints[i].mPostLayoutPos;
6848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
6858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                Map<Integer, CollectPositionResult> positions
686e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        = collectPositions(lm.mRecyclerView, recycler, state, ids);
6878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                for (PositionConstraint constraint : constraints) {
6888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    if (constraint.mPostLayoutPos >= 0) {
6898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        constraint.validate(state, positions.get(constraint.mPostLayoutPos),
6908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                                lm.getLog());
6918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    }
6928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
6938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
6948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        };
6958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        adapterChanges.run(mTestAdapter);
696b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.waitForLayout(2);
697e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        checkForMainThreadException();
6988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        for (PositionConstraint constraint : constraints) {
6998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.assertValidate();
7008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
701b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
702b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
703b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    class AnimationLayoutManager extends TestLayoutManager {
704b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
7058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        private int mTotalLayoutCount = 0;
7068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        private String log;
7078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
708b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        OnLayoutCallbacks mOnLayoutCallbacks = new OnLayoutCallbacks() {
709b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        };
710b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
7118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
7128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
713b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        @Override
714c35968d173f900d8024bdf38174e2225c9a7f311Chet Haase        public boolean supportsPredictiveItemAnimations() {
715b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            return true;
716b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
717b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
7188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public String getLog() {
7198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return log;
7208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
7218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
722668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        private String prepareLog(RecyclerView.Recycler recycler, RecyclerView.State state, boolean done) {
7238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            StringBuilder builder = new StringBuilder();
724668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            builder.append("is pre layout:").append(state.isPreLayout()).append(", done:").append(done);
725668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            builder.append("\nViewHolders:\n");
7268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            for (RecyclerView.ViewHolder vh : ((TestRecyclerView)mRecyclerView).collectViewHolders()) {
7278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                builder.append(vh).append("\n");
7288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
7298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            builder.append("scrap:\n");
7308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            for (RecyclerView.ViewHolder vh : recycler.getScrapList()) {
7318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                builder.append(vh).append("\n");
7328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
733668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
734668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            if (state.isPreLayout() && !done) {
7358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                log = "\n" + builder.toString();
7368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            } else {
7378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                log += "\n" + builder.toString();
7388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
7398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return log;
7408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
7418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
742b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        @Override
743b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void expectLayouts(int count) {
744b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            super.expectLayouts(count);
745b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            mOnLayoutCallbacks.mLayoutCount = 0;
746b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
747b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
748b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void setOnLayoutCallbacks(OnLayoutCallbacks onLayoutCallbacks) {
749b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            mOnLayoutCallbacks = onLayoutCallbacks;
750b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
751b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
752b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        @Override
753b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public final void onLayoutChildren(RecyclerView.Recycler recycler,
754b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                RecyclerView.State state) {
755b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            try {
7568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                mTotalLayoutCount++;
757668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                prepareLog(recycler, state, false);
758668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                if (state.isPreLayout()) {
759668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    validateOldPositions(recycler, state);
760668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                } else {
761668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    validateClearedOldPositions(recycler, state);
762668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                }
763b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                mOnLayoutCallbacks.onLayoutChildren(recycler, this, state);
764668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                prepareLog(recycler, state, true);
765b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            } finally {
766b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                layoutLatch.countDown();
767d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            }
768b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
769b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
770668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        private void validateClearedOldPositions(RecyclerView.Recycler recycler,
771668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                RecyclerView.State state) {
772668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            if (getTestRecyclerView() == null) {
773668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                return;
774668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
775668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            for (RecyclerView.ViewHolder viewHolder : getTestRecyclerView().collectViewHolders()) {
776668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                assertEquals("there should NOT be an old position in post layout",
777668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                        RecyclerView.NO_POSITION, viewHolder.mOldPosition);
778668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                assertEquals("there should NOT be a pre layout position in post layout",
779668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                        RecyclerView.NO_POSITION, viewHolder.mPreLayoutPosition);
780668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
781668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
782668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
783668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        private void validateOldPositions(RecyclerView.Recycler recycler,
784668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                RecyclerView.State state) {
785668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            if (getTestRecyclerView() == null) {
786668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                return;
787668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
788668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            for (RecyclerView.ViewHolder viewHolder : getTestRecyclerView().collectViewHolders()) {
789668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                if (!viewHolder.isRemoved() && !viewHolder.isInvalid()) {
790668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    assertTrue("there should be an old position in pre-layout",
791668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                            viewHolder.mOldPosition != RecyclerView.NO_POSITION);
792668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                }
793668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
794668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
795668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
7968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public int getTotalLayoutCount() {
7978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return mTotalLayoutCount;
7988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
7998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
800d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        @Override
801d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        public boolean canScrollVertically() {
802d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            return true;
803d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        }
804d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar
805d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        @Override
806d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
807d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                RecyclerView.State state) {
808d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            mOnLayoutCallbacks.onScroll(dy, recycler, state);
809d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            return super.scrollVerticallyBy(dy, recycler, state);
810d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        }
811b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
812b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void onPostDispatchLayout() {
813b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            mOnLayoutCallbacks.postDispatchLayout();
814b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
815b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
816b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        @Override
817b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void waitForLayout(long timeout, TimeUnit timeUnit) throws Throwable {
818b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            super.waitForLayout(timeout, timeUnit);
819b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            checkForMainThreadException();
820b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
821d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    }
822d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
823b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    abstract class OnLayoutCallbacks {
824b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
825b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        int mLayoutMin = Integer.MIN_VALUE;
826b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
827b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        int mLayoutItemCount = Integer.MAX_VALUE;
828b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
829b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        int expectedPreLayoutItemCount = -1;
830b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
831b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        int expectedPostLayoutItemCount = -1;
832b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
833b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        int mDeletedViewCount;
834b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
8358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int mLayoutCount = 0;
8368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
837b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        void setExpectedItemCounts(int preLayout, int postLayout) {
838b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            expectedPreLayoutItemCount = preLayout;
839b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            expectedPostLayoutItemCount = postLayout;
840b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
841b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
842b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        void reset() {
843b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            mLayoutMin = Integer.MIN_VALUE;
844b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            mLayoutItemCount = Integer.MAX_VALUE;
845b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            expectedPreLayoutItemCount = -1;
846b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            expectedPostLayoutItemCount = -1;
8478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            mLayoutCount = 0;
848b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
849b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
850b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        void beforePreLayout(RecyclerView.Recycler recycler,
851b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                AnimationLayoutManager lm, RecyclerView.State state) {
852b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            mDeletedViewCount = 0;
853b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            for (int i = 0; i < lm.getChildCount(); i++) {
854b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                View v = lm.getChildAt(i);
855b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                if (lm.getLp(v).isItemRemoved()) {
856b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                    mDeletedViewCount++;
857b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                }
858b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            }
859b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
860b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
861b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        void doLayout(RecyclerView.Recycler recycler, AnimationLayoutManager lm,
862b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                RecyclerView.State state) {
863b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            if (DEBUG) {
864b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                Log.d(TAG, "item count " + state.getItemCount());
865b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            }
866b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            lm.detachAndScrapAttachedViews(recycler);
867b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            final int start = mLayoutMin == Integer.MIN_VALUE ? 0 : mLayoutMin;
868b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            final int count = mLayoutItemCount
869b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                    == Integer.MAX_VALUE ? state.getItemCount() : mLayoutItemCount;
870c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar            lm.layoutRange(recycler, start, start + count);
871b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            assertEquals("correct # of children should be laid out",
872c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                    count, lm.getChildCount());
8738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            lm.assertVisibleItemPositions();
874b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
875b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
876b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        void onLayoutChildren(RecyclerView.Recycler recycler, AnimationLayoutManager lm,
877b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                RecyclerView.State state) {
878b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
8798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (state.isPreLayout()) {
880b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                if (expectedPreLayoutItemCount != -1) {
881b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                    assertEquals("on pre layout, state should return abstracted adapter size",
882b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                            expectedPreLayoutItemCount, state.getItemCount());
883b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                }
884b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                beforePreLayout(recycler, lm, state);
8858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            } else {
886b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                if (expectedPostLayoutItemCount != -1) {
887b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                    assertEquals("on post layout, state should return real adapter size",
888b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                            expectedPostLayoutItemCount, state.getItemCount());
889b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                }
890b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                beforePostLayout(recycler, lm, state);
891b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            }
892b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            doLayout(recycler, lm, state);
8938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (state.isPreLayout()) {
894b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                afterPreLayout(recycler, lm, state);
8958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            } else {
896b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                afterPostLayout(recycler, lm, state);
897b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            }
898b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            mLayoutCount++;
899b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
900b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
901b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        void afterPreLayout(RecyclerView.Recycler recycler, AnimationLayoutManager layoutManager,
902b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                RecyclerView.State state) {
903b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
904b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
905b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        void beforePostLayout(RecyclerView.Recycler recycler, AnimationLayoutManager layoutManager,
906b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                RecyclerView.State state) {
907b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
908b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
909b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        void afterPostLayout(RecyclerView.Recycler recycler, AnimationLayoutManager layoutManager,
910b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                RecyclerView.State state) {
911b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
912b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
913b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        void postDispatchLayout() {
914b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
915b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
916d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        public void onScroll(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
917d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar
918d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        }
919b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
920b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
921b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    class TestRecyclerView extends RecyclerView {
92211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar
923888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        CountDownLatch drawLatch;
924b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
925b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public TestRecyclerView(Context context) {
926b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            super(context);
927b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
928b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
929b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public TestRecyclerView(Context context, AttributeSet attrs) {
930b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            super(context, attrs);
931b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
932b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
933b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public TestRecyclerView(Context context, AttributeSet attrs, int defStyle) {
934b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            super(context, attrs, defStyle);
935b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
936b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
9378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        @Override
9388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        void initAdapterManager() {
9398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            super.initAdapterManager();
9408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            mAdapterHelper.mOnItemProcessedCallback = new Runnable() {
9418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                @Override
9428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                public void run() {
9438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    validatePostUpdateOp();
9448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
9458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            };
9468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
9478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
948888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        public void expectDraw(int count) {
949888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar            drawLatch = new CountDownLatch(count);
950888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        }
951888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar
952888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        public void waitForDraw(long timeout) throws Throwable {
953888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar            drawLatch.await(timeout * (DEBUG ? 100 : 1), TimeUnit.SECONDS);
954888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar            assertEquals("all expected draws should happen at the expected time frame",
955888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar                    0, drawLatch.getCount());
956888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        }
957888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar
9588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        List<ViewHolder> collectViewHolders() {
9598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            List<ViewHolder> holders = new ArrayList<ViewHolder>();
9608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            final int childCount = getChildCount();
9618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            for (int i = 0; i < childCount; i++) {
9628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                ViewHolder holder = getChildViewHolderInt(getChildAt(i));
9638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                if (holder != null) {
9648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    holders.add(holder);
9658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
9668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
9678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return holders;
9688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
9698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
9708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
9718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        private void validateViewHolderPositions() {
9728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            final Set<Integer> existingOffsets = new HashSet<Integer>();
973668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            int childCount = getChildCount();
974668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            StringBuilder log = new StringBuilder();
9758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            for (int i = 0; i < childCount; i++) {
9768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                ViewHolder vh = getChildViewHolderInt(getChildAt(i));
977668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                TestViewHolder tvh = (TestViewHolder) vh;
978668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                log.append(tvh.mBindedItem).append(vh)
979668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                        .append(" hidden:")
980668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                        .append(mChildHelper.mHiddenViews.contains(vh.itemView))
981668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                        .append("\n");
982668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
983668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            for (int i = 0; i < childCount; i++) {
984668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                ViewHolder vh = getChildViewHolderInt(getChildAt(i));
985668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                if (vh.isInvalid()) {
986668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    continue;
987668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                }
988668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                if (vh.getPosition() < 0) {
989668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    LayoutManager lm = getLayoutManager();
990668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    for (int j = 0; j < lm.getChildCount(); j ++) {
991668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                        assertNotSame("removed view holder should not be in LM's child list",
992668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                                vh.itemView, lm.getChildAt(j));
9938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    }
994668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                } else if (!mChildHelper.mHiddenViews.contains(vh.itemView)) {
9958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    if (!existingOffsets.add(vh.getPosition())) {
996668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                        throw new IllegalStateException("view holder position conflict for "
997668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                                + "existing views " + vh + "\n" + log);
9988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    }
9998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
10008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
10018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
10028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
10038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        void validatePostUpdateOp() {
10048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            try {
10058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                validateViewHolderPositions();
10068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                if (super.mState.isPreLayout()) {
10078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    validatePreLayoutSequence((AnimationLayoutManager) getLayoutManager());
10088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
10098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                validateAdapterPosition((AnimationLayoutManager) getLayoutManager());
10108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            } catch (Throwable t) {
10118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                postExceptionToInstrumentation(t);
10128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
10138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
10148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
10158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
10168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
10178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        private void validateAdapterPosition(AnimationLayoutManager lm) {
10188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            for (ViewHolder vh : collectViewHolders()) {
10198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                if (!vh.isRemoved() && vh.mPreLayoutPosition >= 0) {
10208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    assertEquals("adapter position calculations should match view holder "
1021668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                            + "pre layout:" + mState.isPreLayout()
1022668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                            + " positions\n" + vh + "\n" + lm.getLog(),
10238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                            mAdapterHelper.findPositionOffset(vh.mPreLayoutPosition), vh.mPosition);
10248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
10258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
10268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
10278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
10288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // ensures pre layout positions are continuous block. This is not necessarily a case
10298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // but valid in test RV
10308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        private void validatePreLayoutSequence(AnimationLayoutManager lm) {
10318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            Set<Integer> preLayoutPositions = new HashSet<Integer>();
10328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            for (ViewHolder vh : collectViewHolders()) {
10338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertTrue("pre layout positions should be distinct " + lm.getLog(),
10348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        preLayoutPositions.add(vh.mPreLayoutPosition));
10358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
10368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            int minPos = Integer.MAX_VALUE;
10378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            for (Integer pos : preLayoutPositions) {
10388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                if (pos < minPos) {
10398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    minPos = pos;
10408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
10418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
10428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            for (int i = 1; i < preLayoutPositions.size(); i++) {
10438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertNotNull("next position should exist " + lm.getLog(),
10448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        preLayoutPositions.contains(minPos + i));
10458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
10468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
10478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
1048888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        @Override
1049888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        protected void dispatchDraw(Canvas canvas) {
1050888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar            super.dispatchDraw(canvas);
1051888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar            if (drawLatch != null) {
1052888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar                drawLatch.countDown();
1053888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar            }
1054888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        }
1055888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar
1056b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        @Override
1057b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        void dispatchLayout() {
1058b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            try {
1059b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                super.dispatchLayout();
1060b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                if (getLayoutManager() instanceof AnimationLayoutManager) {
1061b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                    ((AnimationLayoutManager) getLayoutManager()).onPostDispatchLayout();
1062b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                }
1063b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            } catch (Throwable t) {
1064b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                postExceptionToInstrumentation(t);
1065b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            }
1066b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1067b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
1068b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
10698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
10708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
10718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
10728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    abstract class AdapterOps {
10738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
10748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        final public void run(TestAdapter adapter) throws Throwable {
10758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            onRun(adapter);
10768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
10778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
10788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        abstract void onRun(TestAdapter testAdapter) throws Throwable;
10798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
10808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
10818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    static class CollectPositionResult {
10828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
10838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // true if found in scrap
10848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public RecyclerView.ViewHolder scrapResult;
10858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
10868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public RecyclerView.ViewHolder adapterResult;
10878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
10888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        static CollectPositionResult fromScrap(RecyclerView.ViewHolder viewHolder) {
10898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            CollectPositionResult cpr = new CollectPositionResult();
10908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            cpr.scrapResult = viewHolder;
10918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return cpr;
10928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
10938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
10948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        static CollectPositionResult fromAdapter(RecyclerView.ViewHolder viewHolder) {
10958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            CollectPositionResult cpr = new CollectPositionResult();
10968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            cpr.adapterResult = viewHolder;
10978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return cpr;
10988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
10998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
11008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
11018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    static class PositionConstraint {
11028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
11038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public static enum Type {
11048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            scrap,
11058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            adapter,
11068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            adapterScrap /*first pass adapter, second pass scrap*/
11078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
11088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
11098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        Type mType;
11108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
11118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int mOldPos; // if VH
11128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
11138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int mPreLayoutPos;
11148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
11158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int mPostLayoutPos;
11168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
11178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int mValidateCount = 0;
11188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
11198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public static PositionConstraint scrap(int oldPos, int preLayoutPos, int postLayoutPos) {
11208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            PositionConstraint constraint = new PositionConstraint();
11218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mType = Type.scrap;
11228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mOldPos = oldPos;
11238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mPreLayoutPos = preLayoutPos;
11248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mPostLayoutPos = postLayoutPos;
11258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return constraint;
11268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
11278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
11288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public static PositionConstraint adapterScrap(int preLayoutPos, int position) {
11298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            PositionConstraint constraint = new PositionConstraint();
11308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mType = Type.adapterScrap;
11318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mOldPos = RecyclerView.NO_POSITION;
11328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mPreLayoutPos = preLayoutPos;
11338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mPostLayoutPos = position;// adapter pos does not change
11348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return constraint;
11358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
11368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
11378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public static PositionConstraint adapter(int position) {
11388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            PositionConstraint constraint = new PositionConstraint();
11398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mType = Type.adapter;
11408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mPreLayoutPos = RecyclerView.NO_POSITION;
11418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mOldPos = RecyclerView.NO_POSITION;
11428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mPostLayoutPos = position;// adapter pos does not change
11438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return constraint;
11448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
11458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
11468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public void assertValidate() {
11478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            int expectedValidate = 0;
11488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (mPreLayoutPos >= 0) {
11498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                expectedValidate ++;
11508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
11518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (mPostLayoutPos >= 0) {
11528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                expectedValidate ++;
11538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
11548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            assertEquals("should run all validates", expectedValidate, mValidateCount);
11558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
11568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
11578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        @Override
11588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public String toString() {
11598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return "Cons{" +
11608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    "t=" + mType.name() +
11618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    ", old=" + mOldPos +
11628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    ", pre=" + mPreLayoutPos +
11638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    ", post=" + mPostLayoutPos +
11648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    '}';
11658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
11668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
11678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public void validate(RecyclerView.State state, CollectPositionResult result, String log) {
11688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            mValidateCount ++;
11698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            assertNotNull(this + ": result should not be null\n" + log, result);
11708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            RecyclerView.ViewHolder viewHolder;
11718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (mType == Type.scrap || (mType == Type.adapterScrap && !state.isPreLayout())) {
11728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertNotNull(this + ": result should come from scrap\n" + log, result.scrapResult);
11738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                viewHolder = result.scrapResult;
11748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            } else {
11758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertNotNull(this + ": result should come from adapter\n"  + log,
11768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        result.adapterResult);
11778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertEquals(this + ": old position should be none when it came from adapter\n" + log,
11788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        RecyclerView.NO_POSITION, result.adapterResult.getOldPosition());
11798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                viewHolder = result.adapterResult;
11808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
11818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (state.isPreLayout()) {
11828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertEquals(this + ": pre-layout position should match\n" + log, mPreLayoutPos,
1183e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        viewHolder.mPreLayoutPosition == -1 ? viewHolder.mPosition :
11848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        viewHolder.mPreLayoutPosition);
11858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertEquals(this + ": pre-layout getPosition should match\n" + log, mPreLayoutPos,
11868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        viewHolder.getPosition());
11878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                if (mType == Type.scrap) {
11888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    assertEquals(this + ": old position should match\n" + log, mOldPos,
11898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                            result.scrapResult.getOldPosition());
1190b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                }
11918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            } else if (mType == Type.adapter || mType == Type.adapterScrap || !result.scrapResult
11928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    .isRemoved()) {
1193e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                assertEquals(this + ": post-layout position should match\n" + log + "\n\n"
1194e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        + viewHolder, mPostLayoutPos, viewHolder.getPosition());
1195b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            }
1196b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
1197b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
1198d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase}
1199