SortedListTest.java revision a3d5bb01bc01733999d84c452a27012c57ab369c
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.util; 18 19import junit.framework.TestCase; 20 21import android.support.v7.util.SortedList; 22 23import java.util.ArrayList; 24import java.util.Collections; 25import java.util.Comparator; 26import java.util.List; 27import java.util.Random; 28 29public class SortedListTest extends TestCase { 30 31 SortedList<Item> mList; 32 List<Pair> mAdditions = new ArrayList<Pair>(); 33 List<Pair> mRemovals = new ArrayList<Pair>(); 34 List<Pair> mMoves = new ArrayList<Pair>(); 35 List<Pair> mUpdates = new ArrayList<Pair>(); 36 private SortedList.Callback<Item> mCallback; 37 38 private Comparator<? super Item> sItemComparator = new Comparator<Item>() { 39 @Override 40 public int compare(Item o1, Item o2) { 41 return mCallback.compare(o1, o2); 42 } 43 }; 44 45 @Override 46 public void setUp() throws Exception { 47 super.setUp(); 48 mCallback = new SortedList.Callback<Item>() { 49 @Override 50 public int compare(Item o1, Item o2) { 51 return o1.cmpField < o2.cmpField ? -1 : (o1.cmpField == o2.cmpField ? 0 : 1); 52 } 53 54 @Override 55 public void onInserted(int position, int count) { 56 mAdditions.add(new Pair(position, count)); 57 } 58 59 @Override 60 public void onRemoved(int position, int count) { 61 mRemovals.add(new Pair(position, count)); 62 } 63 64 @Override 65 public void onMoved(int fromPosition, int toPosition) { 66 mMoves.add(new Pair(fromPosition, toPosition)); 67 } 68 69 @Override 70 public void onChanged(int position, int count) { 71 mUpdates.add(new Pair(position, count)); 72 } 73 74 @Override 75 public boolean areContentsTheSame(Item oldItem, Item newItem) { 76 return oldItem.cmpField == newItem.cmpField && oldItem.data == newItem.data; 77 } 78 79 @Override 80 public boolean areItemsTheSame(Item item1, Item item2) { 81 return item1.id == item2.id; 82 } 83 }; 84 mList = new SortedList<Item>(Item.class, mCallback); 85 } 86 87 public void testEmpty() { 88 assertEquals("empty", mList.size(), 0); 89 } 90 91 public void testAdd() { 92 Item item = new Item(); 93 assertEquals(insert(item), 0); 94 assertEquals(size(), 1); 95 assertTrue(mAdditions.contains(new Pair(0, 1))); 96 Item item2 = new Item(); 97 item2.cmpField = item.cmpField + 1; 98 assertEquals(insert(item2), 1); 99 assertEquals(size(), 2); 100 assertTrue(mAdditions.contains(new Pair(1, 1))); 101 Item item3 = new Item(); 102 item3.cmpField = item.cmpField - 1; 103 mAdditions.clear(); 104 assertEquals(insert(item3), 0); 105 assertEquals(size(), 3); 106 assertTrue(mAdditions.contains(new Pair(0, 1))); 107 } 108 109 public void testAddDuplicate() { 110 Item item = new Item(); 111 Item item2 = new Item(item.id, item.cmpField); 112 item2.data = item.data; 113 insert(item); 114 assertEquals(0, insert(item2)); 115 assertEquals(1, size()); 116 assertEquals(1, mAdditions.size()); 117 assertEquals(0, mUpdates.size()); 118 } 119 120 public void testRemove() { 121 Item item = new Item(); 122 assertFalse(remove(item)); 123 assertEquals(0, mRemovals.size()); 124 insert(item); 125 assertTrue(remove(item)); 126 assertEquals(1, mRemovals.size()); 127 assertTrue(mRemovals.contains(new Pair(0, 1))); 128 assertEquals(0, size()); 129 assertFalse(remove(item)); 130 assertEquals(1, mRemovals.size()); 131 } 132 133 public void testRemove2() { 134 Item item = new Item(); 135 Item item2 = new Item(item.cmpField); 136 insert(item); 137 assertFalse(remove(item2)); 138 assertEquals(0, mRemovals.size()); 139 } 140 141 public void testBatch() { 142 mList.beginBatchedUpdates(); 143 for (int i = 0; i < 5; i ++) { 144 mList.add(new Item(i)); 145 } 146 assertEquals(0, mAdditions.size()); 147 mList.endBatchedUpdates(); 148 assertTrue(mAdditions.contains(new Pair(0, 5))); 149 } 150 151 public void testRandom() throws Throwable { 152 Random random = new Random(System.nanoTime()); 153 List<Item> copy = new ArrayList<Item>(); 154 StringBuilder log = new StringBuilder(); 155 try { 156 for (int i = 0; i < 10000; i++) { 157 switch (random.nextInt(3)) { 158 case 0://ADD 159 Item item = new Item(); 160 copy.add(item); 161 insert(item); 162 log.append("add " + item).append("\n"); 163 break; 164 case 1://REMOVE 165 if (copy.size() > 0) { 166 int index = random.nextInt(mList.size()); 167 item = mList.get(index); 168 log.append("remove " + item).append("\n"); 169 assertTrue(copy.remove(item)); 170 assertTrue(mList.remove(item)); 171 } 172 break; 173 case 2://UPDATE 174 if (copy.size() > 0) { 175 int index = random.nextInt(mList.size()); 176 item = mList.get(index); 177 // TODO this cannot work 178 Item newItem = new Item(item.id, item.cmpField); 179 log.append("update " + item + " to " + newItem).append("\n"); 180 while (newItem.data == item.data) { 181 newItem.data = random.nextInt(1000); 182 } 183 int itemIndex = mList.add(newItem); 184 copy.remove(item); 185 copy.add(newItem); 186 assertSame(mList.get(itemIndex), newItem); 187 assertNotSame(mList.get(index), item); 188 } 189 break; 190 case 3:// UPDATE AT 191 if (copy.size() > 0) { 192 int index = random.nextInt(mList.size()); 193 item = mList.get(index); 194 Item newItem = new Item(item.id, random.nextInt()); 195 mList.updateItemAt(index, newItem); 196 copy.remove(item); 197 copy.add(newItem); 198 } 199 } 200 int lastCmp = Integer.MIN_VALUE; 201 for (int index = 0; index < copy.size(); index ++) { 202 assertFalse(mList.indexOf(copy.get(index)) == SortedList.INVALID_POSITION); 203 assertTrue(mList.get(index).cmpField >= lastCmp); 204 lastCmp = mList.get(index).cmpField; 205 assertTrue(copy.contains(mList.get(index))); 206 } 207 208 for (int index = 0; index < mList.size(); index ++) { 209 assertNotNull(mList.mData[index]); 210 } 211 for (int index = mList.size(); index < mList.mData.length; index ++) { 212 assertNull(mList.mData[index]); 213 } 214 215 } 216 } catch (Throwable t) { 217 Collections.sort(copy, sItemComparator); 218 log.append("Items:\n"); 219 for (Item item : copy) { 220 log.append(item).append("\n"); 221 } 222 log.append("SortedList:\n"); 223 for (int i = 0; i < mList.size(); i ++) { 224 log.append(mList.get(i)).append("\n"); 225 } 226 227 throw new Throwable(" \nlog:\n" + log.toString(), t); 228 } 229 } 230 231 private int size() { 232 return mList.size(); 233 } 234 235 private int insert(Item item) { 236 return mList.add(item); 237 } 238 239 private boolean remove(Item item ) { 240 return mList.remove(item); 241 } 242 243 static class Item { 244 static int idCounter = 0; 245 final int id; 246 247 int cmpField; 248 249 int data = (int) (Math.random() * 1000);//used for comparison 250 251 public Item() { 252 id = idCounter ++;; 253 cmpField = (int) (Math.random() * 1000); 254 } 255 256 public Item(int cmpField) { 257 id = idCounter ++;; 258 this.cmpField = cmpField; 259 } 260 261 public Item(int id, int cmpField) { 262 this.id = id; 263 this.cmpField = cmpField; 264 } 265 266 @Override 267 public boolean equals(Object o) { 268 if (this == o) { 269 return true; 270 } 271 if (o == null || getClass() != o.getClass()) { 272 return false; 273 } 274 275 Item item = (Item) o; 276 277 if (cmpField != item.cmpField) { 278 return false; 279 } 280 if (id != item.id) { 281 return false; 282 } 283 284 return true; 285 } 286 287 @Override 288 public int hashCode() { 289 int result = id; 290 result = 31 * result + cmpField; 291 return result; 292 } 293 294 @Override 295 public String toString() { 296 return "Item{" + 297 "id=" + id + 298 ", cmpField=" + cmpField + 299 ", data=" + data + 300 '}'; 301 } 302 } 303 304 private static final class Pair { 305 final int first, second; 306 307 public Pair(int first) { 308 this.first = first; 309 this.second = Integer.MIN_VALUE; 310 } 311 312 public Pair(int first, int second) { 313 this.first = first; 314 this.second = second; 315 } 316 317 @Override 318 public boolean equals(Object o) { 319 if (this == o) { 320 return true; 321 } 322 if (o == null || getClass() != o.getClass()) { 323 return false; 324 } 325 326 Pair pair = (Pair) o; 327 328 if (first != pair.first) { 329 return false; 330 } 331 if (second != pair.second) { 332 return false; 333 } 334 335 return true; 336 } 337 338 @Override 339 public int hashCode() { 340 int result = first; 341 result = 31 * result + second; 342 return result; 343 } 344 } 345}