BaseRecyclerViewInstrumentationTest.java revision b5f8b4fecb531aee6b359b4968409410aba90817
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
19d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.test.ActivityInstrumentationTestCase2;
20d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.util.Log;
21d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.view.View;
22d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.view.ViewGroup;
23d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport android.widget.TextView;
24d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
25d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.ArrayList;
26d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.List;
27d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.CountDownLatch;
28d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseimport java.util.concurrent.TimeUnit;
29d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
30d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haaseabstract public class BaseRecyclerViewInstrumentationTest extends
31d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        ActivityInstrumentationTestCase2<TestActivity> {
32d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
33d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    private static final String TAG = "RecyclerViewTest";
34d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
35b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    private boolean mDebug;
36b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
37b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    protected RecyclerView mRecyclerView;
38d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
39d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    public BaseRecyclerViewInstrumentationTest() {
40b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        this(false);
41b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
42b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
43b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    public BaseRecyclerViewInstrumentationTest(boolean debug) {
44d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        super("android.support.v7.widget", TestActivity.class);
45b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mDebug = debug;
46b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
47b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
48b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    public void removeRecyclerView() throws Throwable {
49b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mRecyclerView = null;
50b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        runTestOnUiThread(new Runnable() {
51b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            @Override
52b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            public void run() {
53b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                getActivity().mContainer.removeAllViews();
54b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            }
55b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        });
56b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
57b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
58b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    public void setRecyclerView(final RecyclerView recyclerView) throws Throwable {
59b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        mRecyclerView = recyclerView;
60b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        runTestOnUiThread(new Runnable() {
61b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            @Override
62b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            public void run() {
63b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                getActivity().mContainer.addView(recyclerView);
64b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            }
65b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        });
66b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
67b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
68b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    public void requestLayoutOnUIThread(final View view) throws Throwable {
69b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        runTestOnUiThread(new Runnable() {
70b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            @Override
71b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            public void run() {
72b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                view.requestLayout();
73b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            }
74b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        });
75b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    }
76b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
77b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar    public void scrollBy(final int dt) throws Throwable {
78b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        runTestOnUiThread(new Runnable() {
79b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            @Override
80b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            public void run() {
81b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                if (mRecyclerView.getLayoutManager().canScrollHorizontally()) {
82b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                    mRecyclerView.scrollBy(dt, 0);
83b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                } else {
84b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                    mRecyclerView.scrollBy(0, dt);
85b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                }
86b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
87b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            }
88b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        });
89d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    }
90d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
91d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    class TestViewHolder extends RecyclerView.ViewHolder {
92d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
93d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        Item mBindedItem;
94d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
95d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        public TestViewHolder(View itemView) {
96d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            super(itemView);
97d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
98d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    }
99d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
100d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    class TestLayoutManager extends RecyclerView.LayoutManager {
101d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
102d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        CountDownLatch layoutLatch;
103d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
104d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        public void expectLayouts(int count) {
105d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            layoutLatch = new CountDownLatch(count);
106d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
107d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
108b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void waitForLayout(long timeout, TimeUnit timeUnit) throws Throwable {
109b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            layoutLatch.await(timeout * (mDebug ? 100 : 1), timeUnit);
110d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            assertEquals("all expected layouts should be executed at the expected time",
111d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                    0, layoutLatch.getCount());
112d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
113d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
114b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void assertLayoutCount(int count, String msg, long timeout) throws Throwable {
115b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            layoutLatch.await(timeout, TimeUnit.SECONDS);
116b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            assertEquals(msg, count, layoutLatch.getCount());
117b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
118b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
119b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void assertNoLayout(String msg, long timeout) throws Throwable {
120b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            layoutLatch.await(timeout, TimeUnit.SECONDS);
121b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            assertFalse(msg, layoutLatch.getCount() == 0);
122b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
123b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
124b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void waitForLayout(long timeout) throws Throwable {
125b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            waitForLayout(timeout, TimeUnit.SECONDS);
126b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
127b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
128d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        @Override
129d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        public RecyclerView.LayoutParams generateDefaultLayoutParams() {
130d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
131d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                    ViewGroup.LayoutParams.WRAP_CONTENT);
132d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
133d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
134d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        void assertVisibleItemPositions() {
135d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            int i = getChildCount();
136d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            TestAdapter testAdapter = (TestAdapter) mRecyclerView.getAdapter();
137d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            while (i-- > 0) {
138d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                View view = getChildAt(i);
139d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams();
140d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                Item item = ((TestViewHolder) lp.mViewHolder).mBindedItem;
141b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                if (mDebug) {
142d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                    Log.d(TAG, "testing item " + i);
143d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                }
144d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                assertSame("item position in LP should match adapter value",
145d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                        testAdapter.mItems.get(lp.getViewPosition()), item);
146d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            }
147d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
148d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
149b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        RecyclerView.LayoutParams getLp(View v) {
150b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            return (RecyclerView.LayoutParams) v.getLayoutParams();
151b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
152b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
153d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        void layoutRange(RecyclerView.Recycler recycler, int start,
154d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                int end) {
155b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            if (mDebug) {
156d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                Log.d(TAG, "will layout items from " + start + " to " + end);
157d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            }
158d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            for (int i = start; i < end; i++) {
159b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                if (mDebug) {
160d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                    Log.d(TAG, "laying out item " + i);
161d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                }
162d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                View view = recycler.getViewForPosition(i);
163b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                assertNotNull("view should not be null for valid position. "
164b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                        + "got null view at position " + i, view);
165b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                if (!getLp(view).isItemRemoved()) {
166b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                    addView(view);
167b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                }
168b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
169d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                measureChildWithMargins(view, 0, 0);
170d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                layoutDecorated(view, 0, (i - start) * 10, getDecoratedMeasuredWidth(view)
171d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                        , getDecoratedMeasuredHeight(view));
172d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            }
173d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
174d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    }
175d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
176d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    static class Item {
177d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
178d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        int originalIndex;
179d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
180d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        final String text;
181d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
182d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        Item(int originalIndex, String text) {
183d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            this.originalIndex = originalIndex;
184d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            this.text = text;
185d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
186d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    }
187d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
188d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    class TestAdapter extends RecyclerView.Adapter<TestViewHolder> {
189d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
190d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        List<Item> mItems;
191d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
192d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        TestAdapter(int count) {
193d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            mItems = new ArrayList<Item>(count);
194d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            for (int i = 0; i < count; i++) {
195d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                mItems.add(new Item(i, "Item " + i));
196d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            }
197d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
198d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
199d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        @Override
200d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        public TestViewHolder onCreateViewHolder(ViewGroup parent,
201d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                int viewType) {
202d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            return new TestViewHolder(new TextView(parent.getContext()));
203d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
204d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
205d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        @Override
206d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        public void onBindViewHolder(TestViewHolder holder, int position) {
207d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            final Item item = mItems.get(position);
208d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            ((TextView) (holder.itemView)).setText(item.text);
209d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            holder.mBindedItem = item;
210d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
211d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
212b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void deleteAndNotify(final int start, final int count) throws Throwable {
213b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            deleteAndNotify(new int[]{start, count});
214b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
215b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
216b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        /**
217b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar         * Deletes items in the given ranges.
218b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar         * <p>
219b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar         * Note that each operation affects the one after so you should offset them properly.
220b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar         * <p>
221b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar         * For example, if adapter has 5 items (A,B,C,D,E), and then you call this method with
222b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar         * <code>[1, 2],[2, 1]</code>, it will first delete items B,C and the new adapter will be
223b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar         * A D E. Then it will delete 2,1 which means it will delete E.
224b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar         */
225b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void deleteAndNotify(final int[]... startCountTuples) throws Throwable {
226d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            runTestOnUiThread(new Runnable() {
227d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                @Override
228d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                public void run() {
229b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                    for (int t = 0; t < startCountTuples.length; t++) {
230b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                        int[] tuple = startCountTuples[t];
231b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                        for (int i = 0; i < tuple[1]; i++) {
232b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                            mItems.remove(tuple[0]);
233b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                        }
234b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                        notifyItemRangeRemoved(tuple[0], tuple[1]);
235d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                    }
236b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
237d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                }
238d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            });
239d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
240d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
241b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void addAndNotify(final int start, final int count) throws Throwable {
242b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar            addAndNotify(new int[]{start, count});
243b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        }
244b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
245b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar        public void addAndNotify(final int[]... startCountTuples) throws Throwable {
246d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            runTestOnUiThread(new Runnable() {
247d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                @Override
248d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                public void run() {
249b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                    for (int t = 0; t < startCountTuples.length; t++) {
250b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                        int[] tuple = startCountTuples[t];
251b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                        for (int i = 0; i < tuple[1]; i++) {
252b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                            mItems.add(tuple[0], new Item(i, "new item " + i));
253b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                        }
254b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                        // offset others
255b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                        for (int i = tuple[0] + tuple[1]; i < mItems.size(); i++) {
256b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                            mItems.get(i).originalIndex += tuple[1];
257b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                        }
258b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar                        notifyItemRangeInserted(tuple[0], tuple[1]);
259d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                    }
260b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar
261d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase                }
262d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            });
263d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
264d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase
265d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        @Override
266d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        public int getItemCount() {
267d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase            return mItems.size();
268d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase        }
269d7d27e9ebe5c7325e67e1a8af265378bd2056cadChet Haase    }
270b5f8b4fecb531aee6b359b4968409410aba90817Yigit Boyar}