RecyclerViewLayoutTest.java revision 5ced882cabdcefbb469e332f6f73983999aad0e5
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17
18package android.support.v7.widget;
19
20import android.view.View;
21
22import java.util.concurrent.TimeUnit;
23import java.util.concurrent.atomic.AtomicBoolean;
24import java.util.concurrent.atomic.AtomicInteger;
25
26public class RecyclerViewLayoutTest extends BaseRecyclerViewInstrumentationTest {
27
28    private static final boolean DEBUG = false;
29
30    public RecyclerViewLayoutTest() {
31        super(DEBUG);
32    }
33
34    public void testFindViewById() throws Throwable {
35        findViewByIdTest(false);
36        removeRecyclerView();
37        findViewByIdTest(true);
38    }
39
40    public void findViewByIdTest(final boolean supportPredictive) throws Throwable {
41        final RecyclerView recyclerView = new RecyclerView(getActivity());
42        final int initialAdapterSize = 20;
43        final TestAdapter adapter = new TestAdapter(initialAdapterSize);
44        final int deleteStart = 6;
45        final int deleteCount = 5;
46        recyclerView.setAdapter(adapter);
47        final AtomicBoolean assertPositions = new AtomicBoolean(false);
48        TestLayoutManager lm = new TestLayoutManager() {
49            @Override
50            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
51                super.onLayoutChildren(recycler, state);
52                if (assertPositions.get()) {
53                    if (state.isPreLayout()) {
54                        for (int i = 0; i < deleteStart; i ++) {
55                            View view = findViewByPosition(i);
56                            assertNotNull("find view by position for existing items should work "
57                                    + "fine", view);
58                            assertFalse("view should not be marked as removed",
59                                    ((RecyclerView.LayoutParams) view.getLayoutParams())
60                                            .isItemRemoved());
61                        }
62                        for (int i = 0;  i < deleteCount; i++) {
63                            View view = findViewByPosition(i + deleteStart);
64                            assertNotNull("find view by position should work fine for removed "
65                                    + "views in pre-layout", view);
66                            assertTrue("view should be marked as removed",
67                                    ((RecyclerView.LayoutParams) view.getLayoutParams())
68                                            .isItemRemoved());
69                        }
70                        for (int i = deleteStart + deleteCount; i < 20; i++) {
71                            View view = findViewByPosition(i);
72                            assertNotNull(view);
73                            assertFalse("view should not be marked as removed",
74                                    ((RecyclerView.LayoutParams) view.getLayoutParams())
75                                            .isItemRemoved());
76                        }
77                    } else {
78                        for (int i = 0; i < initialAdapterSize - deleteCount; i ++) {
79                            View view = findViewByPosition(i);
80                            assertNotNull("find view by position for existing items should work "
81                                    + "fine", view);
82                            TestViewHolder viewHolder =
83                                    (TestViewHolder) mRecyclerView.getChildViewHolder(view);
84                            assertSame("should be the correct item " + viewHolder
85                                    ,viewHolder.mBindedItem,
86                                    adapter.mItems.get(viewHolder.mPosition));
87                            assertFalse("view should not be marked as removed",
88                                    ((RecyclerView.LayoutParams) view.getLayoutParams())
89                                            .isItemRemoved());
90                        }
91                    }
92
93                }
94                detachAndScrapAttachedViews(recycler);
95                layoutRange(recycler, state.getItemCount() - 1, -1);
96                layoutLatch.countDown();
97            }
98
99            @Override
100            public boolean supportsPredictiveItemAnimations() {
101                return supportPredictive;
102            }
103        };
104        recyclerView.setLayoutManager(lm);
105        lm.expectLayouts(1);
106        setRecyclerView(recyclerView);
107        lm.waitForLayout(2);
108        getInstrumentation().waitForIdleSync();
109
110        assertPositions.set(true);
111        lm.expectLayouts(supportPredictive ? 2 : 1);
112        adapter.deleteAndNotify(new int[]{deleteStart, deleteCount - 1}, new int[]{deleteStart, 1});
113        lm.waitForLayout(2);
114    }
115
116    public void testTypeForCache() throws Throwable {
117        final AtomicInteger viewType = new AtomicInteger(1);
118        final TestAdapter adapter = new TestAdapter(100) {
119            @Override
120            public int getItemViewType(int position) {
121                return viewType.get();
122            }
123
124            @Override
125            public long getItemId(int position) {
126                return mItems.get(position).mId;
127            }
128        };
129        adapter.setHasStableIds(true);
130        final AtomicInteger layoutStart = new AtomicInteger(2);
131        final int childCount = 10;
132        final TestLayoutManager lm = new TestLayoutManager() {
133            @Override
134            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
135                super.onLayoutChildren(recycler, state);
136                detachAndScrapAttachedViews(recycler);
137                layoutRange(recycler, layoutStart.get(), layoutStart.get() + childCount);
138                layoutLatch.countDown();
139            }
140        };
141        final RecyclerView recyclerView = new RecyclerView(getActivity());
142        recyclerView.setAdapter(adapter);
143        recyclerView.setLayoutManager(lm);
144        recyclerView.setItemViewCacheSize(10);
145        lm.expectLayouts(1);
146        setRecyclerView(recyclerView);
147        lm.waitForLayout(2);
148        getInstrumentation().waitForIdleSync();
149        layoutStart.set(4); // trigger a cache for 3,4
150        lm.expectLayouts(1);
151        requestLayoutOnUIThread(recyclerView);
152        lm.waitForLayout(2);
153        //
154        viewType.incrementAndGet();
155        layoutStart.set(2); // go back to bring views from cache
156        lm.expectLayouts(1);
157        adapter.mItems.remove(1);
158        adapter.notifyChange();
159        lm.waitForLayout(2);
160        runTestOnUiThread(new Runnable() {
161            @Override
162            public void run() {
163                for (int i = 2; i < 4; i++) {
164                    RecyclerView.ViewHolder vh = recyclerView.findViewHolderForPosition(2);
165                    assertEquals("View holder's type should match latest type", viewType.get(),
166                            vh.getItemViewType());
167                }
168            }
169        });
170    }
171
172    public void testState() throws Throwable {
173        final TestAdapter adapter = new TestAdapter(10);
174        final RecyclerView recyclerView = new RecyclerView(getActivity());
175        recyclerView.setAdapter(adapter);
176        recyclerView.setItemAnimator(null);
177        final AtomicInteger itemCount = new AtomicInteger();
178        final AtomicBoolean structureChanged = new AtomicBoolean();
179        TestLayoutManager testLayoutManager = new TestLayoutManager() {
180            @Override
181            public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
182                detachAndScrapAttachedViews(recycler);
183                layoutRange(recycler, 0, state.getItemCount());
184                itemCount.set(state.getItemCount());
185                structureChanged.set(state.didStructureChange());
186                layoutLatch.countDown();
187            }
188        };
189        recyclerView.setLayoutManager(testLayoutManager);
190        testLayoutManager.expectLayouts(1);
191        runTestOnUiThread(new Runnable() {
192            @Override
193            public void run() {
194                getActivity().mContainer.addView(recyclerView);
195            }
196        });
197        testLayoutManager.waitForLayout(2, TimeUnit.SECONDS);
198
199        assertEquals("item count in state should be correct", adapter.getItemCount()
200                , itemCount.get());
201        assertEquals("structure changed should be true for first layout", true,
202                structureChanged.get());
203        Thread.sleep(1000); //wait for other layouts.
204        testLayoutManager.expectLayouts(1);
205        runTestOnUiThread(new Runnable() {
206            @Override
207            public void run() {
208                recyclerView.requestLayout();
209            }
210        });
211        testLayoutManager.waitForLayout(2);
212        assertEquals("in second layout,structure changed should be false", false,
213                structureChanged.get());
214        testLayoutManager.expectLayouts(1); //
215        adapter.deleteAndNotify(3, 2);
216        testLayoutManager.waitForLayout(2);
217        assertEquals("when items are removed, item count in state should be updated",
218                adapter.getItemCount(),
219                itemCount.get());
220        assertEquals("structure changed should be true when items are removed", true,
221                structureChanged.get());
222        testLayoutManager.expectLayouts(1);
223        adapter.addAndNotify(2, 5);
224        testLayoutManager.waitForLayout(2);
225
226        assertEquals("when items are added, item count in state should be updated",
227                adapter.getItemCount(),
228                itemCount.get());
229        assertEquals("structure changed should be true when items are removed", true,
230                structureChanged.get());
231
232    }
233
234}