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}