RecyclerViewAnimationsTest.java revision a75ca911e26945e0b326e7b70dd12378ace80270
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
930ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar    public void testDetachBeforeAnimations() throws Throwable {
940ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        setupBasic(10, 0, 5);
950ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        final RecyclerView rv = mRecyclerView;
960ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        waitForAnimations(2);
970ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        final DefaultItemAnimator animator = new DefaultItemAnimator() {
980ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar            @Override
990ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar            public void runPendingAnimations() {
1000ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar                super.runPendingAnimations();
1010ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar            }
1020ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        };
1030ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        rv.setItemAnimator(animator);
1040ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        mLayoutManager.expectLayouts(2);
1050ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        mTestAdapter.deleteAndNotify(3, 4);
1060ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        mLayoutManager.waitForLayout(2);
1070ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        removeRecyclerView();
1080ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        assertNull("test sanity check RV should be removed", rv.getParent());
1090ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        assertEquals("no views should be hidden", 0, rv.mChildHelper.mHiddenViews.size());
1100ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar        assertFalse("there should not be any animations running", animator.isRunning());
1110ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar    }
1120ec96558f829719283bb1a49ff218c3ce1430626Yigit Boyar
11342c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar    public void testPreLayoutPositionCleanup() throws Throwable {
11442c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar        setupBasic(4, 0, 4);
11542c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar        mLayoutManager.expectLayouts(2);
11642c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar        mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
11742c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar            @Override
11842c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar            void beforePreLayout(RecyclerView.Recycler recycler,
11942c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar                    AnimationLayoutManager lm, RecyclerView.State state) {
12042c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar                mLayoutMin = 0;
12142c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar                mLayoutItemCount = 3;
12242c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar            }
12342c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar
12442c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar            @Override
12542c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar            void beforePostLayout(RecyclerView.Recycler recycler,
12642c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar                    AnimationLayoutManager layoutManager,
12742c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar                    RecyclerView.State state) {
12842c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar                mLayoutMin = 0;
12942c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar                mLayoutItemCount = 4;
13042c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar            }
13142c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar        };
13242c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar        mTestAdapter.addAndNotify(0, 1);
13342c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar        mLayoutManager.waitForLayout(2);
13442c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar
13542c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar
13642c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar
13742c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar    }
13842c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar
13993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar    public void testAddRemoveSamePass() throws Throwable {
14093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        final List<RecyclerView.ViewHolder> mRecycledViews
14193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                = new ArrayList<RecyclerView.ViewHolder>();
14293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        TestAdapter adapter = new TestAdapter(50) {
14393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            @Override
14493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            public void onViewRecycled(TestViewHolder holder) {
14593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                super.onViewRecycled(holder);
14693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                mRecycledViews.add(holder);
14793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            }
14893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        };
14993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        adapter.setHasStableIds(true);
15093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        setupBasic(50, 3, 5, adapter);
15193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        mRecyclerView.setItemViewCacheSize(0);
15293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        final ArrayList<RecyclerView.ViewHolder> addVH
15393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                = new ArrayList<RecyclerView.ViewHolder>();
15493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        final ArrayList<RecyclerView.ViewHolder> removeVH
15593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                = new ArrayList<RecyclerView.ViewHolder>();
15693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar
15793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        final ArrayList<RecyclerView.ViewHolder> moveVH
15893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                = new ArrayList<RecyclerView.ViewHolder>();
15993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar
16093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        final View[] testView = new View[1];
16193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        mRecyclerView.setItemAnimator(new DefaultItemAnimator() {
16293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            @Override
16393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            public boolean animateAdd(RecyclerView.ViewHolder holder) {
16493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                addVH.add(holder);
16593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                return true;
16693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            }
16793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar
16893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            @Override
16993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            public boolean animateRemove(RecyclerView.ViewHolder holder) {
17093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                removeVH.add(holder);
17193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                return true;
17293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            }
17393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar
17493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            @Override
17593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            public boolean animateMove(RecyclerView.ViewHolder holder, int fromX, int fromY,
17693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                    int toX, int toY) {
17793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                moveVH.add(holder);
17893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                return true;
17993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            }
18093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        });
18193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
18293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            @Override
18393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            void afterPreLayout(RecyclerView.Recycler recycler,
18493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                    AnimationLayoutManager layoutManager,
18593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                    RecyclerView.State state) {
18693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                super.afterPreLayout(recycler, layoutManager, state);
18793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                testView[0] = recycler.getViewForPosition(45);
18893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                testView[0].measure(View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.AT_MOST),
18993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                        View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.AT_MOST));
19093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                testView[0].layout(10, 10, 10 + testView[0].getMeasuredWidth(),
19193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                        10 + testView[0].getMeasuredHeight());
19293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                layoutManager.addView(testView[0], 4);
19393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            }
19493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar
19593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            @Override
19693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            void afterPostLayout(RecyclerView.Recycler recycler,
19793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                    AnimationLayoutManager layoutManager,
19893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                    RecyclerView.State state) {
19993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                super.afterPostLayout(recycler, layoutManager, state);
20093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                testView[0].layout(50, 50, 50 + testView[0].getMeasuredWidth(),
20193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                        50 + testView[0].getMeasuredHeight());
20293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar                layoutManager.addDisappearingView(testView[0], 4);
20393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            }
20493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        };
20593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 3;
20693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 5;
20793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        mRecycledViews.clear();
20893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        mLayoutManager.expectLayouts(2);
20993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        mTestAdapter.deleteAndNotify(3, 1);
21093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        mLayoutManager.waitForLayout(2);
21193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar
21293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        for (RecyclerView.ViewHolder vh : addVH) {
21393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            assertNotSame("add-remove item should not animate add", testView[0], vh.itemView);
21493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        }
21593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        for (RecyclerView.ViewHolder vh : moveVH) {
21693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            assertNotSame("add-remove item should not animate move", testView[0], vh.itemView);
21793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        }
21893d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        for (RecyclerView.ViewHolder vh : removeVH) {
21993d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            assertNotSame("add-remove item should not animate remove", testView[0], vh.itemView);
22093d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        }
22193d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        boolean found = false;
22293d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        for (RecyclerView.ViewHolder vh : mRecycledViews) {
22393d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar            found |= vh.itemView == testView[0];
22493d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        }
22593d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar        assertTrue("added-removed view should be recycled", found);
22693d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar    }
22793d88283955af6643f2ea0f3cc6cbc34ea49f181Yigit Boyar
2287c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen    public void testChangeAnimations()  throws Throwable {
2297c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        final boolean[] booleans = {true, false};
2307c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        for (boolean supportsChange : booleans) {
2317c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            for (boolean changeType : booleans) {
2327c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                for (boolean hasStableIds : booleans) {
2337c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    for (boolean deleteSomeItems : booleans) {
2347c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                        changeAnimTest(supportsChange, changeType, hasStableIds, deleteSomeItems);
2357c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    }
2367c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    removeRecyclerView();
2377c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                }
2387c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            }
2397c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        }
2407c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen    }
2417c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen    public void changeAnimTest(final boolean supportsChangeAnim, final boolean changeType,
2427c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            final boolean hasStableIds, final boolean deleteSomeItems)  throws Throwable {
2437c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        final int changedIndex = 3;
2447c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        final int defaultType = 1;
2457c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        final AtomicInteger changedIndexNewType = new AtomicInteger(defaultType);
2467c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        final String logPrefix = "supportsChangeAnim:" + supportsChangeAnim +
2477c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                ", change view type:" + changeType +
2487c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                ", has stable ids:" + hasStableIds +
2497c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                ", force predictive:" + deleteSomeItems;
2507c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        TestAdapter testAdapter = new TestAdapter(10) {
2517c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            @Override
2527c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            public int getItemViewType(int position) {
2537c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                return position == changedIndex ? changedIndexNewType.get() : defaultType;
2547c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            }
2557c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen
2567c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            @Override
2577c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            public TestViewHolder onCreateViewHolder(ViewGroup parent,
2587c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    int viewType) {
2597c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                TestViewHolder vh = super.onCreateViewHolder(parent, viewType);
2607c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                if (DEBUG) {
2617c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    Log.d(TAG, logPrefix + " onCreateVH" + vh.toString());
2627c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                }
2637c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                return vh;
2647c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            }
2657c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen
2667c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            @Override
2677c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            public void onBindViewHolder(TestViewHolder holder,
2687c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    int position) {
2697c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                super.onBindViewHolder(holder, position);
2707c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                if (DEBUG) {
2717c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    Log.d(TAG, logPrefix + " onBind to " + position + "" + holder.toString());
2727c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                }
2737c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            }
2747c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        };
2757c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        testAdapter.setHasStableIds(hasStableIds);
2767c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        setupBasic(testAdapter.getItemCount(), 0, 10, testAdapter);
2777c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        mRecyclerView.getItemAnimator().setSupportsChangeAnimations(supportsChangeAnim);
2787c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen
2797c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        final RecyclerView.ViewHolder toBeChangedVH =
280115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                mRecyclerView.findViewHolderForLayoutPosition(changedIndex);
2817c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
2827c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            @Override
2837c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            void afterPreLayout(RecyclerView.Recycler recycler,
2847c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    AnimationLayoutManager layoutManager,
2857c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    RecyclerView.State state) {
286115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForLayoutPosition(
287115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        changedIndex);
2887c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                if (supportsChangeAnim) {
2897c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    assertTrue(logPrefix + " changed view holder should have correct flag"
2907c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                            , vh.isChanged());
2917c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                } else {
2927c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    assertFalse(logPrefix + " changed view holder should have correct flag"
2937c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                            , vh.isChanged());
2947c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                }
2957c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            }
2967c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen
2977c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            @Override
2987c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            void afterPostLayout(RecyclerView.Recycler recycler,
2997c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    AnimationLayoutManager layoutManager, RecyclerView.State state) {
300115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                RecyclerView.ViewHolder vh = mRecyclerView.findViewHolderForLayoutPosition(
301115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        changedIndex);
3027c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                assertFalse(logPrefix + "VH should not be marked as changed", vh.isChanged());
3037c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                if (supportsChangeAnim) {
3047c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    assertNotSame(logPrefix + "a new VH should be given if change is supported",
3057c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                            toBeChangedVH, vh);
3067c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                } else if (!changeType && hasStableIds) {
3077c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    assertSame(logPrefix + "if change animations are not supported but we have "
3087c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                            + "stable ids, same view holder should be returned", toBeChangedVH, vh);
3097c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                }
3107c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                super.beforePostLayout(recycler, layoutManager, state);
3117c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            }
3127c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        };
3137c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        mLayoutManager.expectLayouts(1);
3147c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        if (changeType) {
3157c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            changedIndexNewType.set(defaultType + 1);
3167c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        }
3177c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        if (deleteSomeItems) {
3187c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            runTestOnUiThread(new Runnable() {
3197c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                @Override
3207c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                public void run() {
3217c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    try {
3227c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                        mTestAdapter.deleteAndNotify(changedIndex + 2, 1);
3237c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                        mTestAdapter.notifyItemChanged(3);
3247c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    } catch (Throwable throwable) {
3257c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                        throwable.printStackTrace();
3267c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                    }
3277c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen
3287c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen                }
3297c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            });
3307c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        } else {
3317c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen            mTestAdapter.notifyItemChanged(3);
3327c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        }
3337c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen
3347c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen        mLayoutManager.waitForLayout(2);
3357c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen    }
3367c7fba8365684e1ccfc4f39f286df4d100c6c81fJustin Klaassen
337504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar    public void testRecycleDuringAnimations() throws Throwable {
338504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        final AtomicInteger childCount = new AtomicInteger(0);
339504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        final TestAdapter adapter = new TestAdapter(1000) {
340504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
341504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public TestViewHolder onCreateViewHolder(ViewGroup parent,
342504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    int viewType) {
343504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                childCount.incrementAndGet();
344504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                return super.onCreateViewHolder(parent, viewType);
345504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
346504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        };
347504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        setupBasic(1000, 10, 20, adapter);
348504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 10;
349504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 20;
350504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
351504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        mRecyclerView.setRecycledViewPool(new RecyclerView.RecycledViewPool() {
352504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
353504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public void putRecycledView(RecyclerView.ViewHolder scrap) {
354504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                super.putRecycledView(scrap);
355504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                childCount.decrementAndGet();
356504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
357504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
358504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            @Override
359504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            public RecyclerView.ViewHolder getRecycledView(int viewType) {
360504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                final RecyclerView.ViewHolder recycledView = super.getRecycledView(viewType);
361504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                if (recycledView != null) {
362504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                    childCount.incrementAndGet();
363504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                }
364504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                return recycledView;
365504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            }
366504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        });
367504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
368504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        // now keep adding children to trigger more children being created etc.
369504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        for (int i = 0; i < 100; i ++) {
370504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            adapter.addAndNotify(15, 1);
371504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar            Thread.sleep(50);
372504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        }
373504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        getInstrumentation().waitForIdleSync();
374504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        waitForAnimations(2);
375504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar        assertEquals("Children count should add up", childCount.get(),
376504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar                mRecyclerView.getChildCount() + mRecyclerView.mRecycler.mCachedViews.size());
377504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar    }
378504c54ea52c1b2aae6f8f4ae128f1dcaac7e3f6aYigit Boyar
379b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar    public void testNotifyDataSetChanged() throws Throwable {
380b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        setupBasic(10, 3, 4);
381b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        int layoutCount = mLayoutManager.mTotalLayoutCount;
382b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        mLayoutManager.expectLayouts(1);
383b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        runTestOnUiThread(new Runnable() {
384b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar            @Override
385b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar            public void run() {
386b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar                try {
387b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar                    mTestAdapter.deleteAndNotify(4, 1);
388cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                    mTestAdapter.dispatchDataSetChanged();
389b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar                } catch (Throwable throwable) {
390b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar                    throwable.printStackTrace();
391b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar                }
392b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar
393b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar            }
394b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        });
395b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        mLayoutManager.waitForLayout(2);
396b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        getInstrumentation().waitForIdleSync();
397b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        assertEquals("on notify data set changed, predictive animations should not run",
398b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar                layoutCount + 1, mLayoutManager.mTotalLayoutCount);
399b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        mLayoutManager.expectLayouts(2);
400b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        mTestAdapter.addAndNotify(4, 2);
401b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        // make sure animations recover
402b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar        mLayoutManager.waitForLayout(2);
403b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar    }
404b7b44e5454dc409934bcc5609b81d1715c984d40Yigit Boyar
405e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar    public void testStableIdNotifyDataSetChanged() throws Throwable {
406e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        final int itemCount = 20;
407e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        List<Item> initialSet = new ArrayList<Item>();
408e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        final TestAdapter adapter = new TestAdapter(itemCount) {
409e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            @Override
410e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            public long getItemId(int position) {
411e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                return mItems.get(position).mId;
412e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            }
413e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        };
414e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        adapter.setHasStableIds(true);
415e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        initialSet.addAll(adapter.mItems);
416e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        positionStatesTest(itemCount, 5, 5, adapter, new AdapterOps() {
417e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            @Override
418e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            void onRun(TestAdapter testAdapter) throws Throwable {
419e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                Item item5 = adapter.mItems.get(5);
420e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                Item item6 = adapter.mItems.get(6);
421e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                item5.mAdapterIndex = 6;
422e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                item6.mAdapterIndex = 5;
423e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                adapter.mItems.remove(5);
424e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                adapter.mItems.add(6, item5);
425cadd54707a7aaa4cfa9c8b8051c6185e8717f22eChet Haase                adapter.dispatchDataSetChanged();
426e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                //hacky, we support only 1 layout pass
427e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                mLayoutManager.layoutLatch.countDown();
428e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            }
429e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        }, PositionConstraint.scrap(6, -1, 5), PositionConstraint.scrap(5, -1, 6),
430e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                PositionConstraint.scrap(7, -1, 7), PositionConstraint.scrap(8, -1, 8),
431e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                PositionConstraint.scrap(9, -1, 9));
432e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        // now mix items.
433e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar    }
434e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar
435d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar
4368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void testGetItemForDeletedView() throws Throwable {
4378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        getItemForDeletedViewTest(false);
4388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        getItemForDeletedViewTest(true);
43911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar    }
44011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar
4418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void getItemForDeletedViewTest(boolean stableIds) throws Throwable {
44211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        final Set<Integer> itemViewTypeQueries = new HashSet<Integer>();
44311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        final Set<Integer> itemIdQueries = new HashSet<Integer>();
44411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        TestAdapter adapter = new TestAdapter(10) {
44511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            @Override
44611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            public int getItemViewType(int position) {
44711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                itemViewTypeQueries.add(position);
44811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                return super.getItemViewType(position);
44911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            }
45011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar
45111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            @Override
45211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            public long getItemId(int position) {
45311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                itemIdQueries.add(position);
45411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                return mItems.get(position).mId;
45511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            }
45611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        };
45711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        adapter.setHasStableIds(stableIds);
45811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        setupBasic(10, 0, 10, adapter);
45911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        assertEquals("getItemViewType for all items should be called", 10,
46011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                itemViewTypeQueries.size());
46111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        if (adapter.hasStableIds()) {
46211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            assertEquals("getItemId should be called when adapter has stable ids", 10,
46311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                    itemIdQueries.size());
46411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        } else {
46511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            assertEquals("getItemId should not be called when adapter does not have stable ids", 0,
46611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                    itemIdQueries.size());
46711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        }
46811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        itemViewTypeQueries.clear();
46911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        itemIdQueries.clear();
47011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        mLayoutManager.expectLayouts(2);
47111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        // delete last two
47211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        final int deleteStart = 8;
47311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        final int deleteCount = adapter.getItemCount() - deleteStart;
47411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        adapter.deleteAndNotify(deleteStart, deleteCount);
47511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        mLayoutManager.waitForLayout(2);
47611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        for (int i = 0; i < deleteStart; i++) {
47711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            assertTrue("getItemViewType for existing item " + i + " should be called",
47811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                    itemViewTypeQueries.contains(i));
47911b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            if (adapter.hasStableIds()) {
48011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                assertTrue("getItemId for existing item " + i
48111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                        + " should be called when adapter has stable ids",
48211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                        itemIdQueries.contains(i));
48311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            }
48411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        }
48511b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        for (int i = deleteStart; i < deleteStart + deleteCount; i++) {
48611b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            assertFalse("getItemViewType for deleted item " + i + " SHOULD NOT be called",
48711b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                    itemViewTypeQueries.contains(i));
48811b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            if (adapter.hasStableIds()) {
4898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertFalse("getItemId for deleted item " + i + " SHOULD NOT be called",
49011b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar                        itemIdQueries.contains(i));
49111b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar            }
49211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar        }
49311b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar    }
49411b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar
4958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void testDeleteInvisibleMultiStep() throws Throwable {
4968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        setupBasic(1000, 1, 7);
4978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 1;
4988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 7;
4998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.expectLayouts(1);
5008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // try to trigger race conditions
5018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int targetItemCount = mTestAdapter.getItemCount();
5028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        for (int i = 0; i < 100; i++) {
5038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            mTestAdapter.deleteAndNotify(new int[]{0, 1}, new int[]{7, 1});
50490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            checkForMainThreadException();
5058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            targetItemCount -= 2;
5068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
5078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // wait until main thread runnables are consumed
5088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        while (targetItemCount != mTestAdapter.getItemCount()) {
5098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            Thread.sleep(100);
5108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
5118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.waitForLayout(2);
5128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
5138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
5148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void testAddManyMultiStep() throws Throwable {
5158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        setupBasic(10, 1, 7);
5168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 1;
5178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 7;
5188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.expectLayouts(1);
5198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // try to trigger race conditions
5208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int targetItemCount = mTestAdapter.getItemCount();
5218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        for (int i = 0; i < 100; i++) {
5228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            mTestAdapter.addAndNotify(0, 1);
5238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            mTestAdapter.addAndNotify(7, 1);
5248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            targetItemCount += 2;
5258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
5268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // wait until main thread runnables are consumed
5278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        while (targetItemCount != mTestAdapter.getItemCount()) {
5288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            Thread.sleep(100);
5298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
5308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.waitForLayout(2);
5318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
5328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
5338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void testBasicDelete() throws Throwable {
5348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        setupBasic(10);
5358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        final OnLayoutCallbacks callbacks = new OnLayoutCallbacks() {
5368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            @Override
5378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            public void postDispatchLayout() {
5388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                // verify this only in first layout
5398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertEquals("deleted views should still be children of RV",
5408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        mLayoutManager.getChildCount() + mDeletedViewCount
5418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        , mRecyclerView.getChildCount());
5428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
5438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
5448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            @Override
5458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            void afterPreLayout(RecyclerView.Recycler recycler,
5468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    AnimationLayoutManager layoutManager,
5478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    RecyclerView.State state) {
5488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                super.afterPreLayout(recycler, layoutManager, state);
5498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                mLayoutItemCount = 3;
5508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                mLayoutMin = 0;
5518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
5528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        };
5538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        callbacks.mLayoutItemCount = 10;
5548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        callbacks.setExpectedItemCounts(10, 3);
5558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.setOnLayoutCallbacks(callbacks);
5568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
5578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.expectLayouts(2);
5588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mTestAdapter.deleteAndNotify(0, 7);
5598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.waitForLayout(2);
5608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        callbacks.reset();// when animations end another layout will happen
5618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
5628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
5638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
564d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar    public void testAdapterChangeDuringScrolling() throws Throwable {
565d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        setupBasic(10);
566d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        final AtomicInteger onLayoutItemCount = new AtomicInteger(0);
567d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        final AtomicInteger onScrollItemCount = new AtomicInteger(0);
568d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar
569d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        mLayoutManager.setOnLayoutCallbacks(new OnLayoutCallbacks() {
570d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            @Override
571d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            void onLayoutChildren(RecyclerView.Recycler recycler,
572d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                    AnimationLayoutManager lm, RecyclerView.State state) {
573d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                onLayoutItemCount.set(state.getItemCount());
574d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                super.onLayoutChildren(recycler, lm, state);
575d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            }
576d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar
577d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            @Override
578d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            public void onScroll(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
579d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                onScrollItemCount.set(state.getItemCount());
580d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                super.onScroll(dx, recycler, state);
581d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            }
582d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        });
583d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        runTestOnUiThread(new Runnable() {
584d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            @Override
585d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            public void run() {
586d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                mTestAdapter.mItems.remove(5);
587d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                mTestAdapter.notifyItemRangeRemoved(5, 1);
588d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                mRecyclerView.scrollBy(0, 100);
589d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                assertTrue("scrolling while there are pending adapter updates should "
590d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                        + "trigger a layout", mLayoutManager.mOnLayoutCallbacks.mLayoutCount > 0);
591d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                assertEquals("scroll by should be called w/ updated adapter count",
592d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                        mTestAdapter.mItems.size(), onScrollItemCount.get());
593d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar
594d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            }
595d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        });
596d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar    }
597d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar
59817d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar    public void testNotifyDataSetChangedDuringScroll() throws Throwable {
59917d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar        setupBasic(10);
60017d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar        final AtomicInteger onLayoutItemCount = new AtomicInteger(0);
60117d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar        final AtomicInteger onScrollItemCount = new AtomicInteger(0);
60217d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar
60317d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar        mLayoutManager.setOnLayoutCallbacks(new OnLayoutCallbacks() {
60417d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar            @Override
60517d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar            void onLayoutChildren(RecyclerView.Recycler recycler,
60617d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                    AnimationLayoutManager lm, RecyclerView.State state) {
60717d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                onLayoutItemCount.set(state.getItemCount());
60817d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                super.onLayoutChildren(recycler, lm, state);
60917d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar            }
61017d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar
61117d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar            @Override
61217d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar            public void onScroll(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
61317d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                onScrollItemCount.set(state.getItemCount());
61417d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                super.onScroll(dx, recycler, state);
61517d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar            }
61617d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar        });
61717d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar        runTestOnUiThread(new Runnable() {
61817d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar            @Override
61917d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar            public void run() {
62017d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                mTestAdapter.mItems.remove(5);
62117d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                mTestAdapter.notifyDataSetChanged();
62217d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                mRecyclerView.scrollBy(0, 100);
62317d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                assertTrue("scrolling while there are pending adapter updates should "
62417d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                        + "trigger a layout", mLayoutManager.mOnLayoutCallbacks.mLayoutCount > 0);
62517d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                assertEquals("scroll by should be called w/ updated adapter count",
62617d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar                        mTestAdapter.mItems.size(), onScrollItemCount.get());
62717d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar
62817d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar            }
62917d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar        });
63017d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar    }
63117d560c4a247dbfb795a2ea838c819cda77e5fa7Yigit Boyar
632b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    public void testAddInvisibleAndVisible() throws Throwable {
633b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        setupBasic(10, 1, 7);
634b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.expectLayouts(2);
635b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(10, 12);
6362d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar        mTestAdapter.addAndNotify(new int[]{0, 1}, new int[]{7, 1});// add a new item 0 // invisible
637b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.waitForLayout(2);
638b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
639b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
640b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    public void testAddInvisible() throws Throwable {
641b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        setupBasic(10, 1, 7);
642b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.expectLayouts(1);
643b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(10, 12);
6442d2e8d88103866b631eb0f3805da137ebcfb0275Yigit Boyar        mTestAdapter.addAndNotify(new int[]{0, 1}, new int[]{8, 1});// add a new item 0
645b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.waitForLayout(2);
646b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
647b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
648b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    public void testBasicAdd() throws Throwable {
649b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        setupBasic(10);
650b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.expectLayouts(2);
651b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        setExpectedItemCounts(10, 13);
652b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mTestAdapter.addAndNotify(2, 3);
653b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.waitForLayout(2);
654b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
655b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
656a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu    public void testAppCancelAnimationInDetach() throws Throwable {
657a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        final View[] addedView = new View[2];
658a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        TestAdapter adapter = new TestAdapter(1) {
659a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu            @Override
660a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu            public void onViewDetachedFromWindow(TestViewHolder holder) {
661a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                if ((addedView[0] == holder.itemView || addedView[1] == holder.itemView)
662a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                        && holder.itemView.hasTransientState()) {
663a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                    holder.itemView.animate().cancel();
664a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                }
665a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                super.onViewDetachedFromWindow(holder);
666a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu            }
667a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        };
668a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        // original 1 item
669a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        setupBasic(1, 0, 1, adapter);
670a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        mRecyclerView.getItemAnimator().setAddDuration(3000);
671a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        mLayoutManager.expectLayouts(2);
672a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu
673a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        // add 2 items
674a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        setExpectedItemCounts(1, 3);
675a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        mTestAdapter.addAndNotify(0, 2);
676a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        mLayoutManager.waitForLayout(2);
677a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu
678a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        // wait till "add animation" starts
679a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        while (addedView[0] == null || addedView[1] == null) {
680a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu            Thread.sleep(20);
681a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu            runTestOnUiThread(new Runnable() {
682a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                @Override
683a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                public void run() {
684a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                    if (mRecyclerView.getChildCount() == 3) {
685a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                        View view = mRecyclerView.getChildAt(0);
686a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                        if (view.hasTransientState()) {
687a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                            addedView[0] = view;
688a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                        }
689a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                        view = mRecyclerView.getChildAt(1);
690a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                        if (view.hasTransientState()) {
691a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                            addedView[1] = view;
692a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                        }
693a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                    }
694a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                }
695a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu            });
696a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        }
697a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu
698a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        // Layout from item2, exclude the current adding items
699a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        mLayoutManager.expectLayouts(1);
700a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
701a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu            @Override
702a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu            void beforePostLayout(RecyclerView.Recycler recycler,
703a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                    AnimationLayoutManager layoutManager,
704a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                    RecyclerView.State state) {
705a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                mLayoutMin = 2;
706a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu                mLayoutItemCount = 1;
707a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu            }
708a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        };
709a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        requestLayoutOnUIThread(mRecyclerView);
710a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu        mLayoutManager.waitForLayout(2);
711a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu    }
712a75ca911e26945e0b326e7b70dd12378ace80270Dake Gu
713888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar    public TestRecyclerView getTestRecyclerView() {
714888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        return (TestRecyclerView) mRecyclerView;
715888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar    }
716888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar
717888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar    public void testRemoveScrapInvalidate() throws Throwable {
718888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        setupBasic(10);
719888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        TestRecyclerView testRecyclerView = getTestRecyclerView();
720888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        mLayoutManager.expectLayouts(1);
721888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        testRecyclerView.expectDraw(1);
722888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        runTestOnUiThread(new Runnable() {
723888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar            @Override
724888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar            public void run() {
725888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar                mTestAdapter.mItems.clear();
726888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar                mTestAdapter.notifyDataSetChanged();
727888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar            }
728888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        });
729888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        mLayoutManager.waitForLayout(2);
730888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        testRecyclerView.waitForDraw(2);
731888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar    }
732888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar
733b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    public void testDeleteVisibleAndInvisible() throws Throwable {
734b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        setupBasic(11, 3, 5); //layout items  3 4 5 6 7
735b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.expectLayouts(2);
7368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        setLayoutRange(3, 5); //layout previously invisible child 10 from end of the list
737b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        setExpectedItemCounts(9, 8);
738b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mTestAdapter.deleteAndNotify(new int[]{4, 1}, new int[]{7, 2});// delete items 4, 8, 9
739b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.waitForLayout(2);
740b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
741b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
7428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void testFindPositionOffset() throws Throwable {
7438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        setupBasic(10);
74490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
74590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            @Override
74690ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            void beforePreLayout(RecyclerView.Recycler recycler,
74790ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                    AnimationLayoutManager lm, RecyclerView.State state) {
74890ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                super.beforePreLayout(recycler, lm, state);
74990ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                // [0,2,4]
75090ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                assertEquals("offset check", 0, mAdapterHelper.findPositionOffset(0));
75190ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                assertEquals("offset check", 1, mAdapterHelper.findPositionOffset(2));
75290ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar                assertEquals("offset check", 2, mAdapterHelper.findPositionOffset(4));
75390ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar            }
75490ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        };
7558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        runTestOnUiThread(new Runnable() {
7568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            @Override
7578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            public void run() {
7588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                // [0,1,2,3,4]
7598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                // delete 1
7608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                mTestAdapter.notifyItemRangeRemoved(1, 1);
7618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                // delete 3
7628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                mTestAdapter.notifyItemRangeRemoved(2, 1);
7638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
7648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        });
76590ca3086dbf66ad6bb8840e46ec8524a705e1c18Yigit Boyar        mLayoutManager.waitForLayout(2);
7668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
7678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
768b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    private void setLayoutRange(int start, int count) {
769b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutMin = start;
770b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = count;
771b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
772b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
773b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    private void setExpectedItemCounts(int preLayout, int postLayout) {
774b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(preLayout, postLayout);
775b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
776b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
777b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    public void testDeleteInvisible() throws Throwable {
778b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        setupBasic(10, 1, 7);
7798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutMin = 1;
7808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.mLayoutItemCount = 7;
781b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.expectLayouts(1);
782b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.mOnLayoutCallbacks.setExpectedItemCounts(8, 8);
7838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mTestAdapter.deleteAndNotify(new int[]{0, 1}, new int[]{7, 1});// delete item id 0,8
784b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.waitForLayout(2);
785b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
786b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
787e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar    private CollectPositionResult findByPos(RecyclerView recyclerView,
788e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            RecyclerView.Recycler recycler, RecyclerView.State state, int position) {
789e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        View view = recycler.getViewForPosition(position, true);
790e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(view);
791e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        if (vh.wasReturnedFromScrap()) {
792e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            vh.clearReturnedFromScrapFlag(); //keep data consistent.
793e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            return CollectPositionResult.fromScrap(vh);
794e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        } else {
795e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            return CollectPositionResult.fromAdapter(vh);
7968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
7978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
7988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
799e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar    public Map<Integer, CollectPositionResult> collectPositions(RecyclerView recyclerView,
800e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            RecyclerView.Recycler recycler, RecyclerView.State state, int... positions) {
8018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        Map<Integer, CollectPositionResult> positionToAdapterMapping
8028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                = new HashMap<Integer, CollectPositionResult>();
8038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        for (int position : positions) {
8048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (position < 0) {
8058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                continue;
8068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
807e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            positionToAdapterMapping.put(position,
808e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                    findByPos(recyclerView, recycler, state, position));
8098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
8108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        return positionToAdapterMapping;
8118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
8128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
8138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void testAddDelete2() throws Throwable {
8148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        positionStatesTest(5, 0, 5, new AdapterOps() {
8158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // 0 1 2 3 4
8168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // 0 1 2 a b 3 4
8178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // 0 1 b 3 4
8188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // pre: 0 1 2 3 4
8198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // pre w/ adap: 0 1 2 b 3 4
820d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            @Override
8218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            void onRun(TestAdapter adapter) throws Throwable {
8228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                adapter.addDeleteAndNotify(new int[]{3, 2}, new int[]{2, -2});
823b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            }
8248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }, PositionConstraint.scrap(2, 2, -1), PositionConstraint.scrap(1, 1, 1),
8258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(3, 3, 3)
8268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        );
8278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
828d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
8298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void testAddDelete1() throws Throwable {
8308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        positionStatesTest(5, 0, 5, new AdapterOps() {
8318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // 0 1 2 3 4
8328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // 0 1 2 a b 3 4
8338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // 0 2 a b 3 4
8348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // 0 c d 2 a b 3 4
8358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // 0 c d 2 a 4
8368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // c d 2 a 4
8378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            // pre: 0 1 2 3 4
838b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            @Override
8398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            void onRun(TestAdapter adapter) throws Throwable {
8408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                adapter.addDeleteAndNotify(new int[]{3, 2}, new int[]{1, -1},
8418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        new int[]{1, 2}, new int[]{5, -2}, new int[]{0, -1});
842d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            }
8438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }, PositionConstraint.scrap(0, 0, -1), PositionConstraint.scrap(1, 1, -1),
8448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(2, 2, 2), PositionConstraint.scrap(3, 3, -1),
8458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(4, 4, 4), PositionConstraint.adapter(0),
8468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.adapter(1), PositionConstraint.adapter(3)
8478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        );
8488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
8498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
8508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void testAddSameIndexTwice() throws Throwable {
8518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        positionStatesTest(12, 2, 7, new AdapterOps() {
8528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            @Override
8538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            void onRun(TestAdapter adapter) throws Throwable {
8548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                adapter.addAndNotify(new int[]{1, 2}, new int[]{5, 1}, new int[]{5, 1},
8558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        new int[]{11, 1});
8568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
8578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }, PositionConstraint.adapterScrap(0, 0), PositionConstraint.adapterScrap(1, 3),
8588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(2, 2, 4), PositionConstraint.scrap(3, 3, 7),
8598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(4, 4, 8), PositionConstraint.scrap(7, 7, 12),
8608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(8, 8, 13)
8618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        );
8628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
8638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
8648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void testDeleteTwice() throws Throwable {
8658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        positionStatesTest(12, 2, 7, new AdapterOps() {
8668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            @Override
8678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            void onRun(TestAdapter adapter) throws Throwable {
8688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                adapter.deleteAndNotify(new int[]{0, 1}, new int[]{1, 1}, new int[]{7, 1},
8698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        new int[]{0, 1});// delete item ids 0,2,9,1
8708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
8718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }, PositionConstraint.scrap(2, 0, -1), PositionConstraint.scrap(3, 1, 0),
8728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(4, 2, 1), PositionConstraint.scrap(5, 3, 2),
8738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.scrap(6, 4, 3), PositionConstraint.scrap(8, 6, 5),
8748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                PositionConstraint.adapterScrap(7, 6), PositionConstraint.adapterScrap(8, 7)
8758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        );
8768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
8778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
878b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
8798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    public void positionStatesTest(int itemCount, int firstLayoutStartIndex,
880e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            int firstLayoutItemCount, AdapterOps adapterChanges,
881e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            final PositionConstraint... constraints) throws Throwable {
882e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        positionStatesTest(itemCount, firstLayoutStartIndex, firstLayoutItemCount, null,
883e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                adapterChanges,  constraints);
884e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar    }
885e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar    public void positionStatesTest(int itemCount, int firstLayoutStartIndex,
886e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            int firstLayoutItemCount,TestAdapter adapter, AdapterOps adapterChanges,
887e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar            final PositionConstraint... constraints) throws Throwable {
888e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        setupBasic(itemCount, firstLayoutStartIndex, firstLayoutItemCount, adapter);
889b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.expectLayouts(2);
8908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        mLayoutManager.mOnLayoutCallbacks = new OnLayoutCallbacks() {
8918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            @Override
8928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            void beforePreLayout(RecyclerView.Recycler recycler, AnimationLayoutManager lm,
8938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    RecyclerView.State state) {
8948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                super.beforePreLayout(recycler, lm, state);
8958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                //harmless
8968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                lm.detachAndScrapAttachedViews(recycler);
8978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                final int[] ids = new int[constraints.length];
8988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                for (int i = 0; i < constraints.length; i++) {
8998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    ids[i] = constraints[i].mPreLayoutPos;
9008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
9018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                Map<Integer, CollectPositionResult> positions
902e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        = collectPositions(lm.mRecyclerView, recycler, state, ids);
9038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                for (PositionConstraint constraint : constraints) {
9048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    if (constraint.mPreLayoutPos != -1) {
9058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        constraint.validate(state, positions.get(constraint.mPreLayoutPos),
9068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                                lm.getLog());
9078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    }
9088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
9098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
9108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
9118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            @Override
9128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            void beforePostLayout(RecyclerView.Recycler recycler, AnimationLayoutManager lm,
9138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    RecyclerView.State state) {
9148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                super.beforePostLayout(recycler, lm, state);
9158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                lm.detachAndScrapAttachedViews(recycler);
9168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                final int[] ids = new int[constraints.length];
9178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                for (int i = 0; i < constraints.length; i++) {
9188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    ids[i] = constraints[i].mPostLayoutPos;
9198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
9208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                Map<Integer, CollectPositionResult> positions
921e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        = collectPositions(lm.mRecyclerView, recycler, state, ids);
9228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                for (PositionConstraint constraint : constraints) {
9238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    if (constraint.mPostLayoutPos >= 0) {
9248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        constraint.validate(state, positions.get(constraint.mPostLayoutPos),
9258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                                lm.getLog());
9268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    }
9278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
9288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
9298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        };
9308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        adapterChanges.run(mTestAdapter);
931b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mLayoutManager.waitForLayout(2);
932e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar        checkForMainThreadException();
9338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        for (PositionConstraint constraint : constraints) {
9348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.assertValidate();
9358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
936b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
937b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
938b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    class AnimationLayoutManager extends TestLayoutManager {
939b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
9408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        private int mTotalLayoutCount = 0;
9418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        private String log;
9428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
943b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        OnLayoutCallbacks mOnLayoutCallbacks = new OnLayoutCallbacks() {
944b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        };
945b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
9468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
9478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
948b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        @Override
949c35968d173f900d8024bdf38174e2225c9a7f311Chet Haase        public boolean supportsPredictiveItemAnimations() {
950b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            return true;
951b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
952b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
9538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public String getLog() {
9548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return log;
9558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
9568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
957668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        private String prepareLog(RecyclerView.Recycler recycler, RecyclerView.State state, boolean done) {
9588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            StringBuilder builder = new StringBuilder();
959668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            builder.append("is pre layout:").append(state.isPreLayout()).append(", done:").append(done);
960668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            builder.append("\nViewHolders:\n");
9618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            for (RecyclerView.ViewHolder vh : ((TestRecyclerView)mRecyclerView).collectViewHolders()) {
9628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                builder.append(vh).append("\n");
9638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
9648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            builder.append("scrap:\n");
9658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            for (RecyclerView.ViewHolder vh : recycler.getScrapList()) {
9668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                builder.append(vh).append("\n");
9678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
968668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
969668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            if (state.isPreLayout() && !done) {
9708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                log = "\n" + builder.toString();
9718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            } else {
9728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                log += "\n" + builder.toString();
9738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
9748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return log;
9758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
9768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
977b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        @Override
978b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void expectLayouts(int count) {
979b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            super.expectLayouts(count);
980b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            mOnLayoutCallbacks.mLayoutCount = 0;
981b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
982b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
983b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void setOnLayoutCallbacks(OnLayoutCallbacks onLayoutCallbacks) {
984b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            mOnLayoutCallbacks = onLayoutCallbacks;
985b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
986b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
987b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        @Override
988b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public final void onLayoutChildren(RecyclerView.Recycler recycler,
989b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                RecyclerView.State state) {
990b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            try {
9918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                mTotalLayoutCount++;
992668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                prepareLog(recycler, state, false);
993668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                if (state.isPreLayout()) {
994668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    validateOldPositions(recycler, state);
995668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                } else {
996668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    validateClearedOldPositions(recycler, state);
997668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                }
998b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                mOnLayoutCallbacks.onLayoutChildren(recycler, this, state);
999668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                prepareLog(recycler, state, true);
1000b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            } finally {
1001b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                layoutLatch.countDown();
1002d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            }
1003b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
1004b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1005668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        private void validateClearedOldPositions(RecyclerView.Recycler recycler,
1006668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                RecyclerView.State state) {
1007668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            if (getTestRecyclerView() == null) {
1008668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                return;
1009668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
1010668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            for (RecyclerView.ViewHolder viewHolder : getTestRecyclerView().collectViewHolders()) {
1011668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                assertEquals("there should NOT be an old position in post layout",
1012668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                        RecyclerView.NO_POSITION, viewHolder.mOldPosition);
1013668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                assertEquals("there should NOT be a pre layout position in post layout",
1014668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                        RecyclerView.NO_POSITION, viewHolder.mPreLayoutPosition);
1015668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
1016668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
1017668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
1018668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        private void validateOldPositions(RecyclerView.Recycler recycler,
1019668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                RecyclerView.State state) {
1020668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            if (getTestRecyclerView() == null) {
1021668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                return;
1022668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
1023668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            for (RecyclerView.ViewHolder viewHolder : getTestRecyclerView().collectViewHolders()) {
1024668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                if (!viewHolder.isRemoved() && !viewHolder.isInvalid()) {
1025668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    assertTrue("there should be an old position in pre-layout",
1026668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                            viewHolder.mOldPosition != RecyclerView.NO_POSITION);
1027668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                }
1028668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
1029668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar        }
1030668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar
10318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public int getTotalLayoutCount() {
10328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return mTotalLayoutCount;
10338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
10348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
1035d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        @Override
1036d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        public boolean canScrollVertically() {
1037d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            return true;
1038d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        }
1039d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar
1040d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        @Override
1041d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
1042d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar                RecyclerView.State state) {
1043d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            mOnLayoutCallbacks.onScroll(dy, recycler, state);
1044d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar            return super.scrollVerticallyBy(dy, recycler, state);
1045d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        }
1046b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1047b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void onPostDispatchLayout() {
1048b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            mOnLayoutCallbacks.postDispatchLayout();
1049b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
1050b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1051b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        @Override
1052b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void waitForLayout(long timeout, TimeUnit timeUnit) throws Throwable {
1053b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            super.waitForLayout(timeout, timeUnit);
1054b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            checkForMainThreadException();
1055b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
1056d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    }
1057d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
1058b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    abstract class OnLayoutCallbacks {
1059b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1060b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        int mLayoutMin = Integer.MIN_VALUE;
1061b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1062b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        int mLayoutItemCount = Integer.MAX_VALUE;
1063b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1064b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        int expectedPreLayoutItemCount = -1;
1065b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1066b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        int expectedPostLayoutItemCount = -1;
1067b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1068b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        int mDeletedViewCount;
1069b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
10708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int mLayoutCount = 0;
10718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
1072b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        void setExpectedItemCounts(int preLayout, int postLayout) {
1073b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            expectedPreLayoutItemCount = preLayout;
1074b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            expectedPostLayoutItemCount = postLayout;
1075b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
1076b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1077b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        void reset() {
1078b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            mLayoutMin = Integer.MIN_VALUE;
1079b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            mLayoutItemCount = Integer.MAX_VALUE;
1080b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            expectedPreLayoutItemCount = -1;
1081b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            expectedPostLayoutItemCount = -1;
10828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            mLayoutCount = 0;
1083b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
1084b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1085b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        void beforePreLayout(RecyclerView.Recycler recycler,
1086b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                AnimationLayoutManager lm, RecyclerView.State state) {
1087b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            mDeletedViewCount = 0;
1088b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            for (int i = 0; i < lm.getChildCount(); i++) {
1089b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                View v = lm.getChildAt(i);
1090b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                if (lm.getLp(v).isItemRemoved()) {
1091b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                    mDeletedViewCount++;
1092b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                }
1093b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            }
1094b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
1095b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1096b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        void doLayout(RecyclerView.Recycler recycler, AnimationLayoutManager lm,
1097b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                RecyclerView.State state) {
1098b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            if (DEBUG) {
1099b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                Log.d(TAG, "item count " + state.getItemCount());
1100b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            }
1101b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            lm.detachAndScrapAttachedViews(recycler);
1102b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            final int start = mLayoutMin == Integer.MIN_VALUE ? 0 : mLayoutMin;
1103b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            final int count = mLayoutItemCount
1104b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                    == Integer.MAX_VALUE ? state.getItemCount() : mLayoutItemCount;
1105c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar            lm.layoutRange(recycler, start, start + count);
1106b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            assertEquals("correct # of children should be laid out",
1107c8b0ff5869e17a656a3a1d7fb306efa5b99fa07fYigit Boyar                    count, lm.getChildCount());
11088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            lm.assertVisibleItemPositions();
1109b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
1110b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
111142c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar        private void assertNoPreLayoutPosition(RecyclerView.Recycler recycler) {
111242c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar            for (RecyclerView.ViewHolder vh : recycler.mAttachedScrap) {
111342c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar                assertPreLayoutPosition(vh);
111442c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar            }
111542c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar        }
111642c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar
111742c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar        private void assertNoPreLayoutPosition(RecyclerView.LayoutManager lm) {
111842c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar            for (int i = 0; i < lm.getChildCount(); i ++) {
111942c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar                final RecyclerView.ViewHolder vh = mRecyclerView
112042c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar                        .getChildViewHolder(lm.getChildAt(i));
112142c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar                assertPreLayoutPosition(vh);
112242c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar            }
112342c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar        }
112442c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar
112542c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar        private void assertPreLayoutPosition(RecyclerView.ViewHolder vh) {
112642c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar            assertEquals("in post layout, there should not be a view holder w/ a pre "
112742c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar                    + "layout position", RecyclerView.NO_POSITION, vh.mPreLayoutPosition);
112842c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar            assertEquals("in post layout, there should not be a view holder w/ an old "
112942c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar                    + "layout position", RecyclerView.NO_POSITION, vh.mOldPosition);
113042c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar        }
113142c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar
1132b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        void onLayoutChildren(RecyclerView.Recycler recycler, AnimationLayoutManager lm,
1133b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                RecyclerView.State state) {
1134b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
11358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (state.isPreLayout()) {
1136b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                if (expectedPreLayoutItemCount != -1) {
1137b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                    assertEquals("on pre layout, state should return abstracted adapter size",
1138b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                            expectedPreLayoutItemCount, state.getItemCount());
1139b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                }
1140b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                beforePreLayout(recycler, lm, state);
11418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            } else {
1142b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                if (expectedPostLayoutItemCount != -1) {
1143b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                    assertEquals("on post layout, state should return real adapter size",
1144b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                            expectedPostLayoutItemCount, state.getItemCount());
1145b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                }
1146b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                beforePostLayout(recycler, lm, state);
1147b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            }
114842c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar            if (!state.isPreLayout()) {
114942c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar                assertNoPreLayoutPosition(recycler);
115042c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar            }
1151b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            doLayout(recycler, lm, state);
11528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (state.isPreLayout()) {
1153b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                afterPreLayout(recycler, lm, state);
11548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            } else {
1155b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                afterPostLayout(recycler, lm, state);
115642c4af4ff383daf001263ae18a9b00b9fbf87487Yigit Boyar                assertNoPreLayoutPosition(lm);
1157b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            }
1158b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            mLayoutCount++;
1159b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
1160b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1161b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        void afterPreLayout(RecyclerView.Recycler recycler, AnimationLayoutManager layoutManager,
1162b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                RecyclerView.State state) {
1163b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
1164b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1165b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        void beforePostLayout(RecyclerView.Recycler recycler, AnimationLayoutManager layoutManager,
1166b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                RecyclerView.State state) {
1167b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
1168b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1169b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        void afterPostLayout(RecyclerView.Recycler recycler, AnimationLayoutManager layoutManager,
1170b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                RecyclerView.State state) {
1171b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
1172b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1173b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        void postDispatchLayout() {
1174b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
1175b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1176d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        public void onScroll(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
1177d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar
1178d3f1c99a290de13631f9635e06444f75344967d2Yigit Boyar        }
1179b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
1180b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1181b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    class TestRecyclerView extends RecyclerView {
118211b7cfea15306c0d6e4ed7da09f7698df01695dfYigit Boyar
1183888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        CountDownLatch drawLatch;
1184b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1185b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public TestRecyclerView(Context context) {
1186b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            super(context);
1187b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
1188b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1189b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public TestRecyclerView(Context context, AttributeSet attrs) {
1190b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            super(context, attrs);
1191b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
1192b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1193b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public TestRecyclerView(Context context, AttributeSet attrs, int defStyle) {
1194b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            super(context, attrs, defStyle);
1195b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
1196b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
11978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        @Override
11988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        void initAdapterManager() {
11998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            super.initAdapterManager();
12008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            mAdapterHelper.mOnItemProcessedCallback = new Runnable() {
12018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                @Override
12028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                public void run() {
12038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    validatePostUpdateOp();
12048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
12058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            };
12068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
12078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
1208888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        public void expectDraw(int count) {
1209888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar            drawLatch = new CountDownLatch(count);
1210888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        }
1211888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar
1212888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        public void waitForDraw(long timeout) throws Throwable {
1213888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar            drawLatch.await(timeout * (DEBUG ? 100 : 1), TimeUnit.SECONDS);
1214888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar            assertEquals("all expected draws should happen at the expected time frame",
1215888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar                    0, drawLatch.getCount());
1216888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        }
1217888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar
12188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        List<ViewHolder> collectViewHolders() {
12198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            List<ViewHolder> holders = new ArrayList<ViewHolder>();
12208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            final int childCount = getChildCount();
12218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            for (int i = 0; i < childCount; i++) {
12228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                ViewHolder holder = getChildViewHolderInt(getChildAt(i));
12238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                if (holder != null) {
12248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    holders.add(holder);
12258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
12268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
12278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return holders;
12288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
12298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
12308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
12318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        private void validateViewHolderPositions() {
12328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            final Set<Integer> existingOffsets = new HashSet<Integer>();
1233668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            int childCount = getChildCount();
1234668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            StringBuilder log = new StringBuilder();
12358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            for (int i = 0; i < childCount; i++) {
12368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                ViewHolder vh = getChildViewHolderInt(getChildAt(i));
1237668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                TestViewHolder tvh = (TestViewHolder) vh;
1238115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                log.append(tvh.mBoundItem).append(vh)
1239668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                        .append(" hidden:")
1240668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                        .append(mChildHelper.mHiddenViews.contains(vh.itemView))
1241668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                        .append("\n");
1242668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            }
1243668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar            for (int i = 0; i < childCount; i++) {
1244668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                ViewHolder vh = getChildViewHolderInt(getChildAt(i));
1245668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                if (vh.isInvalid()) {
1246668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    continue;
1247668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                }
1248115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                if (vh.getLayoutPosition() < 0) {
1249668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    LayoutManager lm = getLayoutManager();
1250668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                    for (int j = 0; j < lm.getChildCount(); j ++) {
1251668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                        assertNotSame("removed view holder should not be in LM's child list",
1252668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                                vh.itemView, lm.getChildAt(j));
12538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    }
1254668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                } else if (!mChildHelper.mHiddenViews.contains(vh.itemView)) {
1255115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                    if (!existingOffsets.add(vh.getLayoutPosition())) {
1256668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                        throw new IllegalStateException("view holder position conflict for "
1257668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                                + "existing views " + vh + "\n" + log);
12588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    }
12598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
12608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
12618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
12628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
12638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        void validatePostUpdateOp() {
12648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            try {
12658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                validateViewHolderPositions();
12668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                if (super.mState.isPreLayout()) {
12678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    validatePreLayoutSequence((AnimationLayoutManager) getLayoutManager());
12688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
12698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                validateAdapterPosition((AnimationLayoutManager) getLayoutManager());
12708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            } catch (Throwable t) {
12718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                postExceptionToInstrumentation(t);
12728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
12738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
12748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
12758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
12768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
12778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        private void validateAdapterPosition(AnimationLayoutManager lm) {
12788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            for (ViewHolder vh : collectViewHolders()) {
12798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                if (!vh.isRemoved() && vh.mPreLayoutPosition >= 0) {
12808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    assertEquals("adapter position calculations should match view holder "
1281668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                            + "pre layout:" + mState.isPreLayout()
1282668e774379c036a5d53d07ec69ed9ebee13a1fd9Yigit Boyar                            + " positions\n" + vh + "\n" + lm.getLog(),
12838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                            mAdapterHelper.findPositionOffset(vh.mPreLayoutPosition), vh.mPosition);
12848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
12858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
12868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
12878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
12888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // ensures pre layout positions are continuous block. This is not necessarily a case
12898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // but valid in test RV
12908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        private void validatePreLayoutSequence(AnimationLayoutManager lm) {
12918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            Set<Integer> preLayoutPositions = new HashSet<Integer>();
12928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            for (ViewHolder vh : collectViewHolders()) {
12938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertTrue("pre layout positions should be distinct " + lm.getLog(),
12948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        preLayoutPositions.add(vh.mPreLayoutPosition));
12958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
12968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            int minPos = Integer.MAX_VALUE;
12978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            for (Integer pos : preLayoutPositions) {
12988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                if (pos < minPos) {
12998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    minPos = pos;
13008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                }
13018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
13028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            for (int i = 1; i < preLayoutPositions.size(); i++) {
13038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertNotNull("next position should exist " + lm.getLog(),
13048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        preLayoutPositions.contains(minPos + i));
13058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
13068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
13078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
1308888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        @Override
1309888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        protected void dispatchDraw(Canvas canvas) {
1310888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar            super.dispatchDraw(canvas);
1311888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar            if (drawLatch != null) {
1312888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar                drawLatch.countDown();
1313888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar            }
1314888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar        }
1315888c70abaa77e15458c366b8d27835d176dcc546Yigit Boyar
1316b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        @Override
1317b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        void dispatchLayout() {
1318b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            try {
1319b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                super.dispatchLayout();
1320b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                if (getLayoutManager() instanceof AnimationLayoutManager) {
1321b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                    ((AnimationLayoutManager) getLayoutManager()).onPostDispatchLayout();
1322b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                }
1323b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            } catch (Throwable t) {
1324b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                postExceptionToInstrumentation(t);
1325b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            }
1326b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
1327b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
1328b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
13298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
13308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
13318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
13328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    abstract class AdapterOps {
13338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
13348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        final public void run(TestAdapter adapter) throws Throwable {
13358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            onRun(adapter);
13368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
13378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
13388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        abstract void onRun(TestAdapter testAdapter) throws Throwable;
13398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
13408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
13418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    static class CollectPositionResult {
13428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
13438ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        // true if found in scrap
13448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public RecyclerView.ViewHolder scrapResult;
13458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
13468ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public RecyclerView.ViewHolder adapterResult;
13478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
13488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        static CollectPositionResult fromScrap(RecyclerView.ViewHolder viewHolder) {
13498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            CollectPositionResult cpr = new CollectPositionResult();
13508ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            cpr.scrapResult = viewHolder;
13518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return cpr;
13528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
13538ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
13548ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        static CollectPositionResult fromAdapter(RecyclerView.ViewHolder viewHolder) {
13558ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            CollectPositionResult cpr = new CollectPositionResult();
13568ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            cpr.adapterResult = viewHolder;
13578ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return cpr;
13588ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
13598ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    }
13608ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
13618ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar    static class PositionConstraint {
13628ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
13638ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public static enum Type {
13648ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            scrap,
13658ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            adapter,
13668ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            adapterScrap /*first pass adapter, second pass scrap*/
13678ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
13688ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
13698ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        Type mType;
13708ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
13718ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int mOldPos; // if VH
13728ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
13738ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int mPreLayoutPos;
13748ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
13758ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int mPostLayoutPos;
13768ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
13778ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        int mValidateCount = 0;
13788ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
13798ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public static PositionConstraint scrap(int oldPos, int preLayoutPos, int postLayoutPos) {
13808ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            PositionConstraint constraint = new PositionConstraint();
13818ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mType = Type.scrap;
13828ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mOldPos = oldPos;
13838ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mPreLayoutPos = preLayoutPos;
13848ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mPostLayoutPos = postLayoutPos;
13858ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return constraint;
13868ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
13878ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
13888ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public static PositionConstraint adapterScrap(int preLayoutPos, int position) {
13898ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            PositionConstraint constraint = new PositionConstraint();
13908ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mType = Type.adapterScrap;
13918ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mOldPos = RecyclerView.NO_POSITION;
13928ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mPreLayoutPos = preLayoutPos;
13938ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mPostLayoutPos = position;// adapter pos does not change
13948ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return constraint;
13958ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
13968ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
13978ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public static PositionConstraint adapter(int position) {
13988ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            PositionConstraint constraint = new PositionConstraint();
13998ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mType = Type.adapter;
14008ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mPreLayoutPos = RecyclerView.NO_POSITION;
14018ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mOldPos = RecyclerView.NO_POSITION;
14028ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            constraint.mPostLayoutPos = position;// adapter pos does not change
14038ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return constraint;
14048ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
14058ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
14068ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public void assertValidate() {
14078ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            int expectedValidate = 0;
14088ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (mPreLayoutPos >= 0) {
14098ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                expectedValidate ++;
14108ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
14118ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (mPostLayoutPos >= 0) {
14128ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                expectedValidate ++;
14138ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
14148ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            assertEquals("should run all validates", expectedValidate, mValidateCount);
14158ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
14168ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
14178ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        @Override
14188ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public String toString() {
14198ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            return "Cons{" +
14208ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    "t=" + mType.name() +
14218ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    ", old=" + mOldPos +
14228ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    ", pre=" + mPreLayoutPos +
14238ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    ", post=" + mPostLayoutPos +
14248ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    '}';
14258ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        }
14268ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar
14278ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar        public void validate(RecyclerView.State state, CollectPositionResult result, String log) {
14288ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            mValidateCount ++;
14298ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            assertNotNull(this + ": result should not be null\n" + log, result);
14308ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            RecyclerView.ViewHolder viewHolder;
14318ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (mType == Type.scrap || (mType == Type.adapterScrap && !state.isPreLayout())) {
14328ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertNotNull(this + ": result should come from scrap\n" + log, result.scrapResult);
14338ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                viewHolder = result.scrapResult;
14348ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            } else {
14358ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertNotNull(this + ": result should come from adapter\n"  + log,
14368ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        result.adapterResult);
14378ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertEquals(this + ": old position should be none when it came from adapter\n" + log,
14388ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        RecyclerView.NO_POSITION, result.adapterResult.getOldPosition());
14398ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                viewHolder = result.adapterResult;
14408ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            }
14418ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            if (state.isPreLayout()) {
14428ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertEquals(this + ": pre-layout position should match\n" + log, mPreLayoutPos,
1443e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                        viewHolder.mPreLayoutPosition == -1 ? viewHolder.mPosition :
14448ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                        viewHolder.mPreLayoutPosition);
14458ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                assertEquals(this + ": pre-layout getPosition should match\n" + log, mPreLayoutPos,
1446115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        viewHolder.getLayoutPosition());
14478ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                if (mType == Type.scrap) {
14488ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    assertEquals(this + ": old position should match\n" + log, mOldPos,
14498ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                            result.scrapResult.getOldPosition());
1450b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                }
14518ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar            } else if (mType == Type.adapter || mType == Type.adapterScrap || !result.scrapResult
14528ae76f91527ce850f155ce960fb9068bcd5d49f9Yigit Boyar                    .isRemoved()) {
1453e0c347f627f8a78d3e5e3e5eaac9c3ae26208689Yigit Boyar                assertEquals(this + ": post-layout position should match\n" + log + "\n\n"
1454115ba0c7b2a14aa4cd0273952195e1d8f6468f87Yigit Boyar                        + viewHolder, mPostLayoutPos, viewHolder.getLayoutPosition());
1455b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            }
1456b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
1457b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
1458d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase}
1459