1/* 2 * Copyright (C) 2015 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 static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR; 20import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_DISAPPEARED; 21import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_POST; 22import static android.support.v7.widget.ViewInfoStore.InfoRecord.FLAG_PRE; 23 24import android.support.annotation.NonNull; 25import android.support.annotation.Nullable; 26import android.support.test.filters.SmallTest; 27import android.support.v4.util.Pair; 28import android.support.v7.widget.RecyclerView.ItemAnimator.ItemHolderInfo; 29import android.support.v7.widget.RecyclerView.ViewHolder; 30import android.view.View; 31 32import junit.framework.TestCase; 33 34import org.junit.Before; 35import org.junit.Test; 36import org.junit.runner.RunWith; 37import org.junit.runners.JUnit4; 38 39import java.util.ArrayList; 40import java.util.HashMap; 41import java.util.List; 42import java.util.Map; 43 44@SuppressWarnings("ConstantConditions") 45@RunWith(JUnit4.class) 46@SmallTest 47public class ViewInfoStoreTest extends TestCase { 48 ViewInfoStore mStore; 49 LoggingProcessCallback mCallback; 50 @Before 51 public void prepare() { 52 mStore = new ViewInfoStore(); 53 mCallback = new LoggingProcessCallback(); 54 } 55 56 @Test 57 public void addOverridePre() { 58 RecyclerView.ViewHolder vh = new MockViewHolder(); 59 MockInfo info = new MockInfo(); 60 mStore.addToPreLayout(vh, info); 61 MockInfo info2 = new MockInfo(); 62 mStore.addToPreLayout(vh, info2); 63 assertSame(info2, find(vh, FLAG_PRE)); 64 } 65 66 @Test 67 public void addOverridePost() { 68 RecyclerView.ViewHolder vh = new MockViewHolder(); 69 MockInfo info = new MockInfo(); 70 mStore.addToPostLayout(vh, info); 71 MockInfo info2 = new MockInfo(); 72 mStore.addToPostLayout(vh, info2); 73 assertSame(info2, find(vh, FLAG_POST)); 74 } 75 76 @Test 77 public void addRemoveAndReAdd() { 78 RecyclerView.ViewHolder vh = new MockViewHolder(); 79 MockInfo pre = new MockInfo(); 80 mStore.addToPreLayout(vh, pre); 81 MockInfo post1 = new MockInfo(); 82 mStore.addToPostLayout(vh, post1); 83 mStore.onViewDetached(vh); 84 mStore.addToDisappearedInLayout(vh); 85 } 86 87 @Test 88 public void addToPreLayout() { 89 RecyclerView.ViewHolder vh = new MockViewHolder(); 90 MockInfo info = new MockInfo(); 91 mStore.addToPreLayout(vh, info); 92 assertSame(info, find(vh, FLAG_PRE)); 93 assertTrue(mStore.isInPreLayout(vh)); 94 mStore.removeViewHolder(vh); 95 assertFalse(mStore.isInPreLayout(vh)); 96 } 97 98 @Test 99 public void addToPostLayout() { 100 RecyclerView.ViewHolder vh = new MockViewHolder(); 101 MockInfo info = new MockInfo(); 102 mStore.addToPostLayout(vh, info); 103 assertSame(info, find(vh, FLAG_POST)); 104 mStore.removeViewHolder(vh); 105 assertNull(find(vh, FLAG_POST)); 106 } 107 108 @Test 109 public void popFromPreLayout() { 110 assertEquals(0, sizeOf(FLAG_PRE)); 111 RecyclerView.ViewHolder vh = new MockViewHolder(); 112 MockInfo info = new MockInfo(); 113 mStore.addToPreLayout(vh, info); 114 assertSame(info, mStore.popFromPreLayout(vh)); 115 assertNull(mStore.popFromPreLayout(vh)); 116 } 117 118 @Test 119 public void addToOldChangeHolders() { 120 RecyclerView.ViewHolder vh = new MockViewHolder(); 121 mStore.addToOldChangeHolders(1, vh); 122 assertSame(vh, mStore.getFromOldChangeHolders(1)); 123 mStore.removeViewHolder(vh); 124 assertNull(mStore.getFromOldChangeHolders(1)); 125 } 126 127 @Test 128 public void appearListTests() { 129 RecyclerView.ViewHolder vh = new MockViewHolder(); 130 RecyclerView.ItemAnimator.ItemHolderInfo info = new MockInfo(); 131 mStore.addToAppearedInPreLayoutHolders(vh, info); 132 assertEquals(1, sizeOf(FLAG_APPEAR)); 133 RecyclerView.ViewHolder vh2 = new MockViewHolder(); 134 mStore.addToAppearedInPreLayoutHolders(vh2, info); 135 assertEquals(2, sizeOf(FLAG_APPEAR)); 136 mStore.removeViewHolder(vh2); 137 assertEquals(1, sizeOf(FLAG_APPEAR)); 138 } 139 140 @Test 141 public void disappearListTest() { 142 RecyclerView.ViewHolder vh = new MockViewHolder(); 143 mStore.addToDisappearedInLayout(vh); 144 assertEquals(1, sizeOf(FLAG_DISAPPEARED)); 145 mStore.addToDisappearedInLayout(vh); 146 assertEquals(1, sizeOf(FLAG_DISAPPEARED)); 147 RecyclerView.ViewHolder vh2 = new MockViewHolder(); 148 mStore.addToDisappearedInLayout(vh2); 149 assertEquals(2, sizeOf(FLAG_DISAPPEARED)); 150 mStore.removeViewHolder(vh2); 151 assertEquals(1, sizeOf(FLAG_DISAPPEARED)); 152 mStore.removeFromDisappearedInLayout(vh); 153 assertEquals(0, sizeOf(FLAG_DISAPPEARED)); 154 } 155 156 @Test 157 public void processAppear() { 158 ViewHolder vh = new MockViewHolder(); 159 MockInfo info = new MockInfo(); 160 mStore.addToPostLayout(vh, info); 161 mStore.process(mCallback); 162 assertEquals(new Pair<>(null, info), mCallback.appeared.get(vh)); 163 assertTrue(mCallback.disappeared.isEmpty()); 164 assertTrue(mCallback.unused.isEmpty()); 165 assertTrue(mCallback.persistent.isEmpty()); 166 } 167 168 @Test 169 public void processDisappearNormal() { 170 ViewHolder vh = new MockViewHolder(); 171 MockInfo info = new MockInfo(); 172 mStore.addToPreLayout(vh, info); 173 mStore.process(mCallback); 174 assertEquals(new Pair<>(info, null), mCallback.disappeared.get(vh)); 175 assertTrue(mCallback.appeared.isEmpty()); 176 assertTrue(mCallback.unused.isEmpty()); 177 assertTrue(mCallback.persistent.isEmpty()); 178 } 179 180 @Test 181 public void processDisappearMissingLayout() { 182 ViewHolder vh = new MockViewHolder(); 183 MockInfo info = new MockInfo(); 184 mStore.addToPreLayout(vh, info); 185 mStore.addToDisappearedInLayout(vh); 186 mStore.process(mCallback); 187 assertEquals(new Pair<>(info, null), mCallback.disappeared.get(vh)); 188 assertTrue(mCallback.appeared.isEmpty()); 189 assertTrue(mCallback.unused.isEmpty()); 190 assertTrue(mCallback.persistent.isEmpty()); 191 } 192 193 @Test 194 public void processDisappearMoveOut() { 195 ViewHolder vh = new MockViewHolder(); 196 MockInfo pre = new MockInfo(); 197 MockInfo post = new MockInfo(); 198 mStore.addToPreLayout(vh, pre); 199 mStore.addToDisappearedInLayout(vh); 200 mStore.addToPostLayout(vh, post); 201 mStore.process(mCallback); 202 assertEquals(new Pair<>(pre, post), mCallback.disappeared.get(vh)); 203 assertTrue(mCallback.appeared.isEmpty()); 204 assertTrue(mCallback.unused.isEmpty()); 205 assertTrue(mCallback.persistent.isEmpty()); 206 } 207 208 @Test 209 public void processDisappearAppear() { 210 ViewHolder vh = new MockViewHolder(); 211 MockInfo pre = new MockInfo(); 212 MockInfo post = new MockInfo(); 213 mStore.addToPreLayout(vh, pre); 214 mStore.addToDisappearedInLayout(vh); 215 mStore.addToPostLayout(vh, post); 216 mStore.removeFromDisappearedInLayout(vh); 217 mStore.process(mCallback); 218 assertTrue(mCallback.disappeared.isEmpty()); 219 assertTrue(mCallback.appeared.isEmpty()); 220 assertTrue(mCallback.unused.isEmpty()); 221 assertEquals(mCallback.persistent.get(vh), new Pair<>(pre, post)); 222 } 223 224 @Test 225 public void processAppearAndDisappearInPostLayout() { 226 ViewHolder vh = new MockViewHolder(); 227 MockInfo info1 = new MockInfo(); 228 mStore.addToPostLayout(vh, info1); 229 mStore.addToDisappearedInLayout(vh); 230 mStore.process(mCallback); 231 assertTrue(mCallback.disappeared.isEmpty()); 232 assertTrue(mCallback.appeared.isEmpty()); 233 assertTrue(mCallback.persistent.isEmpty()); 234 assertSame(mCallback.unused.get(0), vh); 235 } 236 237 static class MockViewHolder extends RecyclerView.ViewHolder { 238 public MockViewHolder() { 239 super(new View(null)); 240 } 241 } 242 243 static class MockInfo extends RecyclerView.ItemAnimator.ItemHolderInfo { 244 245 } 246 247 private int sizeOf(int flags) { 248 int cnt = 0; 249 final int size = mStore.mLayoutHolderMap.size(); 250 for (int i = 0; i < size; i ++) { 251 ViewInfoStore.InfoRecord record = mStore.mLayoutHolderMap.valueAt(i); 252 if ((record.flags & flags) != 0) { 253 cnt ++; 254 } 255 } 256 return cnt; 257 } 258 259 private RecyclerView.ItemAnimator.ItemHolderInfo find(RecyclerView.ViewHolder viewHolder, 260 int flags) { 261 final int size = mStore.mLayoutHolderMap.size(); 262 for (int i = 0; i < size; i ++) { 263 ViewInfoStore.InfoRecord record = mStore.mLayoutHolderMap.valueAt(i); 264 RecyclerView.ViewHolder holder = mStore.mLayoutHolderMap.keyAt(i); 265 if ((record.flags & flags) != 0 && holder == viewHolder) { 266 if (flags == FLAG_PRE || flags == FLAG_APPEAR) { 267 return record.preInfo; 268 } else if (flags == FLAG_POST) { 269 return record.postInfo; 270 } 271 throw new UnsupportedOperationException("don't know this flag"); 272 } 273 } 274 return null; 275 } 276 277 private static class LoggingProcessCallback implements ViewInfoStore.ProcessCallback { 278 final Map<ViewHolder, Pair<ItemHolderInfo, ItemHolderInfo>> disappeared = new HashMap<>(); 279 final Map<ViewHolder, Pair<ItemHolderInfo, ItemHolderInfo>> appeared = new HashMap<>(); 280 final Map<ViewHolder, Pair<ItemHolderInfo, ItemHolderInfo>> persistent = new HashMap<>(); 281 final List<ViewHolder> unused = new ArrayList<>(); 282 @Override 283 public void processDisappeared(ViewHolder viewHolder, 284 ItemHolderInfo preInfo, 285 @Nullable ItemHolderInfo postInfo) { 286 assertNotNull(preInfo); 287 assertFalse(disappeared.containsKey(viewHolder)); 288 disappeared.put(viewHolder, new Pair<>(preInfo, postInfo)); 289 } 290 291 @Override 292 public void processAppeared(ViewHolder viewHolder, 293 @Nullable ItemHolderInfo preInfo, @NonNull ItemHolderInfo info) { 294 assertNotNull(info); 295 assertFalse(appeared.containsKey(viewHolder)); 296 appeared.put(viewHolder, new Pair<>(preInfo, info)); 297 } 298 299 @Override 300 public void processPersistent(ViewHolder viewHolder, 301 @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) { 302 assertFalse(persistent.containsKey(viewHolder)); 303 assertNotNull(preInfo); 304 assertNotNull(postInfo); 305 persistent.put(viewHolder, new Pair<>(preInfo, postInfo)); 306 } 307 308 @Override 309 public void unused(ViewHolder holder) { 310 unused.add(holder); 311 } 312 } 313 314} 315