1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.base;
6 
7 import android.support.test.filters.SmallTest;
8 
9 import org.junit.Assert;
10 import org.junit.Test;
11 import org.junit.runner.RunWith;
12 
13 import org.chromium.base.test.BaseJUnit4ClassRunner;
14 import org.chromium.base.test.util.Feature;
15 
16 import java.util.Collection;
17 import java.util.Iterator;
18 import java.util.NoSuchElementException;
19 
20 /**
21  * Tests for (@link ObserverList}.
22  */
23 @RunWith(BaseJUnit4ClassRunner.class)
24 public class ObserverListTest {
25     interface Observer {
observe(int x)26         void observe(int x);
27     }
28 
29     private static class Foo implements Observer {
30         private final int mScalar;
31         private int mTotal = 0;
32 
Foo(int scalar)33         Foo(int scalar) {
34             mScalar = scalar;
35         }
36 
37         @Override
observe(int x)38         public void observe(int x) {
39             mTotal += x * mScalar;
40         }
41     }
42 
43     /**
44      * An observer which add a given Observer object to the list when observe is called.
45      */
46     private static class FooAdder implements Observer {
47         private final ObserverList<Observer> mList;
48         private final Observer mLucky;
49 
FooAdder(ObserverList<Observer> list, Observer oblivious)50         FooAdder(ObserverList<Observer> list, Observer oblivious) {
51             mList = list;
52             mLucky = oblivious;
53         }
54 
55         @Override
observe(int x)56         public void observe(int x) {
57             mList.addObserver(mLucky);
58         }
59     }
60 
61     /**
62      * An observer which removes a given Observer object from the list when observe is called.
63      */
64     private static class FooRemover implements Observer {
65         private final ObserverList<Observer> mList;
66         private final Observer mDoomed;
67 
FooRemover(ObserverList<Observer> list, Observer innocent)68         FooRemover(ObserverList<Observer> list, Observer innocent) {
69             mList = list;
70             mDoomed = innocent;
71         }
72 
73         @Override
observe(int x)74         public void observe(int x) {
75             mList.removeObserver(mDoomed);
76         }
77     }
78 
getSizeOfIterable(Iterable<T> iterable)79     private static <T> int getSizeOfIterable(Iterable<T> iterable) {
80         if (iterable instanceof Collection<?>) return ((Collection<?>) iterable).size();
81         int num = 0;
82         for (T el : iterable) num++;
83         return num;
84     }
85 
86     @Test
87     @SmallTest
88     @Feature({"Android-AppBase"})
testRemoveWhileIteration()89     public void testRemoveWhileIteration() {
90         ObserverList<Observer> observerList = new ObserverList<Observer>();
91         Foo a = new Foo(1);
92         Foo b = new Foo(-1);
93         Foo c = new Foo(1);
94         Foo d = new Foo(-1);
95         Foo e = new Foo(-1);
96         FooRemover evil = new FooRemover(observerList, c);
97 
98         observerList.addObserver(a);
99         observerList.addObserver(b);
100 
101         for (Observer obs : observerList) obs.observe(10);
102 
103         // Removing an observer not in the list should do nothing.
104         observerList.removeObserver(e);
105 
106         observerList.addObserver(evil);
107         observerList.addObserver(c);
108         observerList.addObserver(d);
109 
110         for (Observer obs : observerList) obs.observe(10);
111 
112         // observe should be called twice on a.
113         Assert.assertEquals(20, a.mTotal);
114         // observe should be called twice on b.
115         Assert.assertEquals(-20, b.mTotal);
116         // evil removed c from the observerList before it got any callbacks.
117         Assert.assertEquals(0, c.mTotal);
118         // observe should be called once on d.
119         Assert.assertEquals(-10, d.mTotal);
120         // e was never added to the list, observe should not be called.
121         Assert.assertEquals(0, e.mTotal);
122     }
123 
124     @Test
125     @SmallTest
126     @Feature({"Android-AppBase"})
testAddWhileIteration()127     public void testAddWhileIteration() {
128         ObserverList<Observer> observerList = new ObserverList<Observer>();
129         Foo a = new Foo(1);
130         Foo b = new Foo(-1);
131         Foo c = new Foo(1);
132         FooAdder evil = new FooAdder(observerList, c);
133 
134         observerList.addObserver(evil);
135         observerList.addObserver(a);
136         observerList.addObserver(b);
137 
138         for (Observer obs : observerList) obs.observe(10);
139 
140         Assert.assertTrue(observerList.hasObserver(c));
141         Assert.assertEquals(10, a.mTotal);
142         Assert.assertEquals(-10, b.mTotal);
143         Assert.assertEquals(0, c.mTotal);
144     }
145 
146     @Test
147     @SmallTest
148     @Feature({"Android-AppBase"})
testIterator()149     public void testIterator() {
150         ObserverList<Integer> observerList = new ObserverList<Integer>();
151         observerList.addObserver(5);
152         observerList.addObserver(10);
153         observerList.addObserver(15);
154         Assert.assertEquals(3, getSizeOfIterable(observerList));
155 
156         observerList.removeObserver(10);
157         Assert.assertEquals(2, getSizeOfIterable(observerList));
158 
159         Iterator<Integer> it = observerList.iterator();
160         Assert.assertTrue(it.hasNext());
161         Assert.assertTrue(5 == it.next());
162         Assert.assertTrue(it.hasNext());
163         Assert.assertTrue(15 == it.next());
164         Assert.assertFalse(it.hasNext());
165 
166         boolean removeExceptionThrown = false;
167         try {
168             it.remove();
169             Assert.fail("Expecting UnsupportedOperationException to be thrown here.");
170         } catch (UnsupportedOperationException e) {
171             removeExceptionThrown = true;
172         }
173         Assert.assertTrue(removeExceptionThrown);
174         Assert.assertEquals(2, getSizeOfIterable(observerList));
175 
176         boolean noElementExceptionThrown = false;
177         try {
178             it.next();
179             Assert.fail("Expecting NoSuchElementException to be thrown here.");
180         } catch (NoSuchElementException e) {
181             noElementExceptionThrown = true;
182         }
183         Assert.assertTrue(noElementExceptionThrown);
184     }
185 
186     @Test
187     @SmallTest
188     @Feature({"Android-AppBase"})
testRewindableIterator()189     public void testRewindableIterator() {
190         ObserverList<Integer> observerList = new ObserverList<Integer>();
191         observerList.addObserver(5);
192         observerList.addObserver(10);
193         observerList.addObserver(15);
194         Assert.assertEquals(3, getSizeOfIterable(observerList));
195 
196         ObserverList.RewindableIterator<Integer> it = observerList.rewindableIterator();
197         Assert.assertTrue(it.hasNext());
198         Assert.assertTrue(5 == it.next());
199         Assert.assertTrue(it.hasNext());
200         Assert.assertTrue(10 == it.next());
201         Assert.assertTrue(it.hasNext());
202         Assert.assertTrue(15 == it.next());
203         Assert.assertFalse(it.hasNext());
204 
205         it.rewind();
206 
207         Assert.assertTrue(it.hasNext());
208         Assert.assertTrue(5 == it.next());
209         Assert.assertTrue(it.hasNext());
210         Assert.assertTrue(10 == it.next());
211         Assert.assertTrue(it.hasNext());
212         Assert.assertTrue(15 == it.next());
213         Assert.assertEquals(5, (int) observerList.mObservers.get(0));
214         observerList.removeObserver(5);
215         Assert.assertEquals(null, observerList.mObservers.get(0));
216 
217         it.rewind();
218 
219         Assert.assertEquals(10, (int) observerList.mObservers.get(0));
220         Assert.assertTrue(it.hasNext());
221         Assert.assertTrue(10 == it.next());
222         Assert.assertTrue(it.hasNext());
223         Assert.assertTrue(15 == it.next());
224     }
225 
226     @Test
227     @SmallTest
228     @Feature({"Android-AppBase"})
testAddObserverReturnValue()229     public void testAddObserverReturnValue() {
230         ObserverList<Object> observerList = new ObserverList<Object>();
231 
232         Object a = new Object();
233         Assert.assertTrue(observerList.addObserver(a));
234         Assert.assertFalse(observerList.addObserver(a));
235 
236         Object b = new Object();
237         Assert.assertTrue(observerList.addObserver(b));
238         Assert.assertFalse(observerList.addObserver(null));
239     }
240 
241     @Test
242     @SmallTest
243     @Feature({"Android-AppBase"})
testRemoveObserverReturnValue()244     public void testRemoveObserverReturnValue() {
245         ObserverList<Object> observerList = new ObserverList<Object>();
246 
247         Object a = new Object();
248         Object b = new Object();
249         observerList.addObserver(a);
250         observerList.addObserver(b);
251 
252         Assert.assertTrue(observerList.removeObserver(a));
253         Assert.assertFalse(observerList.removeObserver(a));
254         Assert.assertFalse(observerList.removeObserver(new Object()));
255         Assert.assertTrue(observerList.removeObserver(b));
256         Assert.assertFalse(observerList.removeObserver(null));
257 
258         // If we remove an object while iterating, it will be replaced by 'null'.
259         observerList.addObserver(a);
260         Assert.assertTrue(observerList.removeObserver(a));
261         Assert.assertFalse(observerList.removeObserver(null));
262     }
263 
264     @Test
265     @SmallTest
266     @Feature({"Android-AppBase"})
testSize()267     public void testSize() {
268         ObserverList<Object> observerList = new ObserverList<Object>();
269 
270         Assert.assertEquals(0, observerList.size());
271         Assert.assertTrue(observerList.isEmpty());
272 
273         observerList.addObserver(null);
274         Assert.assertEquals(0, observerList.size());
275         Assert.assertTrue(observerList.isEmpty());
276 
277         Object a = new Object();
278         observerList.addObserver(a);
279         Assert.assertEquals(1, observerList.size());
280         Assert.assertFalse(observerList.isEmpty());
281 
282         observerList.addObserver(a);
283         Assert.assertEquals(1, observerList.size());
284         Assert.assertFalse(observerList.isEmpty());
285 
286         observerList.addObserver(null);
287         Assert.assertEquals(1, observerList.size());
288         Assert.assertFalse(observerList.isEmpty());
289 
290         Object b = new Object();
291         observerList.addObserver(b);
292         Assert.assertEquals(2, observerList.size());
293         Assert.assertFalse(observerList.isEmpty());
294 
295         observerList.removeObserver(null);
296         Assert.assertEquals(2, observerList.size());
297         Assert.assertFalse(observerList.isEmpty());
298 
299         observerList.removeObserver(new Object());
300         Assert.assertEquals(2, observerList.size());
301         Assert.assertFalse(observerList.isEmpty());
302 
303         observerList.removeObserver(b);
304         Assert.assertEquals(1, observerList.size());
305         Assert.assertFalse(observerList.isEmpty());
306 
307         observerList.removeObserver(b);
308         Assert.assertEquals(1, observerList.size());
309         Assert.assertFalse(observerList.isEmpty());
310 
311         observerList.removeObserver(a);
312         Assert.assertEquals(0, observerList.size());
313         Assert.assertTrue(observerList.isEmpty());
314 
315         observerList.removeObserver(a);
316         observerList.removeObserver(b);
317         observerList.removeObserver(null);
318         observerList.removeObserver(new Object());
319         Assert.assertEquals(0, observerList.size());
320         Assert.assertTrue(observerList.isEmpty());
321 
322         observerList.addObserver(new Object());
323         observerList.addObserver(new Object());
324         observerList.addObserver(new Object());
325         observerList.addObserver(a);
326         Assert.assertEquals(4, observerList.size());
327         Assert.assertFalse(observerList.isEmpty());
328 
329         observerList.clear();
330         Assert.assertEquals(0, observerList.size());
331         Assert.assertTrue(observerList.isEmpty());
332 
333         observerList.removeObserver(a);
334         observerList.removeObserver(b);
335         observerList.removeObserver(null);
336         observerList.removeObserver(new Object());
337         Assert.assertEquals(0, observerList.size());
338         Assert.assertTrue(observerList.isEmpty());
339     }
340 }
341