RecyclerViewBasicTest.java revision d7d27e9ebe5c7325e67e1a8af265378bd2056cad
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 17package android.support.v7.widget; 18 19import android.os.Parcel; 20import android.os.Parcelable; 21import android.test.AndroidTestCase; 22import android.view.View; 23import android.view.ViewGroup; 24import android.widget.TextView; 25 26import java.util.UUID; 27 28public class RecyclerViewBasicTest extends AndroidTestCase { 29 30 RecyclerView mRecyclerView; 31 32 @Override 33 protected void setUp() throws Exception { 34 super.setUp(); 35 mRecyclerView = new RecyclerView(mContext); 36 } 37 38 public void testMeasureWithoutLayoutManager() { 39 Throwable measureThrowable = null; 40 try { 41 measure(); 42 } catch (Throwable throwable) { 43 measureThrowable = throwable; 44 } 45 assertTrue("Calling measure without a layout manager should throw exception" 46 , measureThrowable instanceof NullPointerException); 47 } 48 49 private void measure() { 50 mRecyclerView.measure(View.MeasureSpec.AT_MOST | 320, View.MeasureSpec.AT_MOST | 240); 51 } 52 53 private void layout() { 54 mRecyclerView.layout(0, 0, 320, 320); 55 } 56 57 private void safeLayout() { 58 try { 59 layout(); 60 } catch (Throwable t) { 61 62 } 63 } 64 65 public void testLayoutWithoutLayoutManager() throws InterruptedException { 66 MockLayoutManager layoutManager = new MockLayoutManager(); 67 mRecyclerView.setLayoutManager(layoutManager); 68 safeLayout(); 69 assertEquals("layout manager should not be called if there is no adapter attached", 70 0, layoutManager.mLayoutCount); 71 } 72 73 public void testLayout() throws InterruptedException { 74 MockLayoutManager layoutManager = new MockLayoutManager(); 75 mRecyclerView.setLayoutManager(layoutManager); 76 mRecyclerView.setAdapter(new MockAdapter(3)); 77 layout(); 78 assertEquals("when both layout manager and activity is set, recycler view should call" 79 + " layout manager's layout method", 1, layoutManager.mLayoutCount); 80 } 81 82 public void testObservingAdapters() { 83 MockAdapter adapterOld = new MockAdapter(1); 84 mRecyclerView.setAdapter(adapterOld); 85 assertTrue("attached adapter should have observables", adapterOld.hasObservers()); 86 87 MockAdapter adapterNew = new MockAdapter(2); 88 mRecyclerView.setAdapter(adapterNew); 89 assertFalse("detached adapter should lose observable", adapterOld.hasObservers()); 90 assertTrue("new adapter should have observers", adapterNew.hasObservers()); 91 92 mRecyclerView.setAdapter(null); 93 assertNull("adapter should be removed successfully", mRecyclerView.getAdapter()); 94 assertFalse("when adapter is removed, observables should be removed too", 95 adapterNew.hasObservers()); 96 } 97 98 public void testAdapterChangeCallbacks() { 99 MockLayoutManager layoutManager = new MockLayoutManager(); 100 mRecyclerView.setLayoutManager(layoutManager); 101 MockAdapter adapterOld = new MockAdapter(1); 102 mRecyclerView.setAdapter(adapterOld); 103 layoutManager.assertPrevNextAdapters(null, adapterOld); 104 105 MockAdapter adapterNew = new MockAdapter(2); 106 mRecyclerView.setAdapter(adapterNew); 107 layoutManager.assertPrevNextAdapters("switching adapters should trigger correct callbacks" 108 , adapterOld, adapterNew); 109 110 mRecyclerView.setAdapter(null); 111 layoutManager.assertPrevNextAdapters( 112 "Setting adapter null should trigger correct callbacks", 113 adapterNew, null); 114 } 115 116 public void testSavedStateWithStatelessLayoutManager() throws InterruptedException { 117 mRecyclerView.setLayoutManager(new MockLayoutManager() { 118 @Override 119 public Parcelable onSaveInstanceState() { 120 return null; 121 } 122 }); 123 mRecyclerView.setAdapter(new MockAdapter(3)); 124 Parcel parcel = Parcel.obtain(); 125 String parcelSuffix = UUID.randomUUID().toString(); 126 Parcelable savedState = mRecyclerView.onSaveInstanceState(); 127 savedState.writeToParcel(parcel, 0); 128 parcel.writeString(parcelSuffix); 129 130 // reset position for reading 131 parcel.setDataPosition(0); 132 RecyclerView restored = new RecyclerView(mContext); 133 restored.setLayoutManager(new MockLayoutManager()); 134 mRecyclerView.setAdapter(new MockAdapter(3)); 135 // restore 136 savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel); 137 restored.onRestoreInstanceState(savedState); 138 139 assertEquals("Parcel reading should not go out of bounds", parcelSuffix, 140 parcel.readString()); 141 assertEquals("When unmarshalling, all of the parcel should be read", 0, parcel.dataAvail()); 142 143 } 144 145 public void testSavedState() throws InterruptedException { 146 MockLayoutManager mlm = new MockLayoutManager(); 147 mRecyclerView.setLayoutManager(mlm); 148 mRecyclerView.setAdapter(new MockAdapter(3)); 149 layout(); 150 Parcelable savedState = mRecyclerView.onSaveInstanceState(); 151 // we append a suffix to the parcelable to test out of bounds 152 String parcelSuffix = UUID.randomUUID().toString(); 153 Parcel parcel = Parcel.obtain(); 154 savedState.writeToParcel(parcel, 0); 155 parcel.writeString(parcelSuffix); 156 157 // reset for reading 158 parcel.setDataPosition(0); 159 // re-create 160 savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel); 161 162 RecyclerView restored = new RecyclerView(mContext); 163 MockLayoutManager mlmRestored = new MockLayoutManager(); 164 restored.setLayoutManager(mlmRestored); 165 restored.setAdapter(new MockAdapter(3)); 166 restored.onRestoreInstanceState(savedState); 167 168 assertEquals("Parcel reading should not go out of bounds", parcelSuffix, 169 parcel.readString()); 170 assertEquals("When unmarshalling, all of the parcel should be read", 0, parcel.dataAvail()); 171 assertEquals("uuid in layout manager should be preserved properly", mlm.mUuid, 172 mlmRestored.mUuid); 173 assertNotSame("stateless parameter should not be preserved", mlm.mLayoutCount, 174 mlmRestored.mLayoutCount); 175 layout(); 176 177 178 } 179 180 static class MockLayoutManager extends RecyclerView.LayoutManager { 181 182 int mLayoutCount = 0; 183 184 int mAdapterChangedCount = 0; 185 186 RecyclerView.Adapter mPrevAdapter; 187 188 RecyclerView.Adapter mNextAdapter; 189 190 String mUuid = UUID.randomUUID().toString(); 191 192 @Override 193 public void onAdapterChanged(RecyclerView.Adapter oldAdapter, 194 RecyclerView.Adapter newAdapter) { 195 super.onAdapterChanged(oldAdapter, newAdapter); 196 mPrevAdapter = oldAdapter; 197 mNextAdapter = newAdapter; 198 mAdapterChangedCount++; 199 } 200 201 @Override 202 public void onLayoutChildren(RecyclerView.Adapter adapter, RecyclerView.Recycler recycler, 203 boolean structureChanged, RecyclerView.State state) { 204 mLayoutCount += 1; 205 } 206 207 @Override 208 public Parcelable onSaveInstanceState() { 209 LayoutManagerSavedState lss = new LayoutManagerSavedState(); 210 lss.mUuid = mUuid; 211 return lss; 212 } 213 214 @Override 215 public void onRestoreInstanceState(Parcelable state) { 216 super.onRestoreInstanceState(state); 217 if (state instanceof LayoutManagerSavedState) { 218 mUuid = ((LayoutManagerSavedState) state).mUuid; 219 } 220 } 221 222 @Override 223 public RecyclerView.LayoutParams generateDefaultLayoutParams() { 224 return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 225 ViewGroup.LayoutParams.WRAP_CONTENT); 226 } 227 228 public void assertPrevNextAdapters(String message, RecyclerView.Adapter prevAdapter, 229 RecyclerView.Adapter nextAdapter) { 230 assertSame(message, prevAdapter, mPrevAdapter); 231 assertSame(message, nextAdapter, mNextAdapter); 232 } 233 234 public void assertPrevNextAdapters(RecyclerView.Adapter prevAdapter, 235 RecyclerView.Adapter nextAdapter) { 236 assertPrevNextAdapters("Adapters from onAdapterChanged callback should match", 237 prevAdapter, nextAdapter); 238 } 239 } 240 241 static class LayoutManagerSavedState implements Parcelable { 242 243 String mUuid; 244 245 public LayoutManagerSavedState(Parcel in) { 246 mUuid = in.readString(); 247 } 248 249 public LayoutManagerSavedState() { 250 251 } 252 253 @Override 254 public int describeContents() { 255 return 0; 256 } 257 258 @Override 259 public void writeToParcel(Parcel dest, int flags) { 260 dest.writeString(mUuid); 261 } 262 263 public static final Parcelable.Creator<LayoutManagerSavedState> CREATOR 264 = new Parcelable.Creator<LayoutManagerSavedState>() { 265 @Override 266 public LayoutManagerSavedState createFromParcel(Parcel in) { 267 return new LayoutManagerSavedState(in); 268 } 269 270 @Override 271 public LayoutManagerSavedState[] newArray(int size) { 272 return new LayoutManagerSavedState[size]; 273 } 274 }; 275 } 276 277 static class MockAdapter extends RecyclerView.Adapter { 278 279 private int mCount = 0; 280 281 282 MockAdapter(int count) { 283 this.mCount = count; 284 } 285 286 @Override 287 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 288 return new MockViewHolder(new TextView(parent.getContext())); 289 } 290 291 @Override 292 public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 293 294 } 295 296 @Override 297 public int getItemCount() { 298 return mCount; 299 } 300 301 void removeItems(int start, int count) { 302 mCount -= count; 303 notifyItemRangeRemoved(start, count); 304 } 305 306 void addItems(int start, int count) { 307 mCount += count; 308 notifyItemRangeInserted(start, count); 309 } 310 } 311 312 static class MockViewHolder extends RecyclerView.ViewHolder { 313 314 public MockViewHolder(View itemView) { 315 super(itemView); 316 } 317 } 318 319} 320 321