1 | // Copyright 2013 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.test.InstrumentationTestCase; |
8 | import android.test.suitebuilder.annotation.SmallTest; |
9 | |
10 | import org.chromium.base.test.util.Feature; |
11 | |
12 | import java.lang.Iterable; |
13 | import java.util.Iterator; |
14 | import java.util.NoSuchElementException; |
15 | |
16 | /** |
17 | * Tests for (@link ObserverList}. |
18 | */ |
19 | public class ObserverListTest extends InstrumentationTestCase { |
20 | interface Observer { |
21 | void observe(int x); |
22 | } |
23 | |
24 | private static class Foo implements Observer { |
25 | private final int mScalar; |
26 | private int mTotal = 0; |
27 | |
28 | Foo(int scalar) { |
29 | mScalar = scalar; |
30 | } |
31 | |
32 | @Override |
33 | public void observe(int x) { |
34 | mTotal += x * mScalar; |
35 | } |
36 | } |
37 | |
38 | /** |
39 | * An observer which add a given Observer object to the list when observe is called. |
40 | */ |
41 | private static class FooAdder implements Observer { |
42 | private final ObserverList<Observer> mList; |
43 | private final Observer mLucky; |
44 | |
45 | FooAdder(ObserverList<Observer> list, Observer oblivious) { |
46 | mList = list; |
47 | mLucky = oblivious; |
48 | } |
49 | |
50 | @Override |
51 | public void observe(int x) { |
52 | mList.addObserver(mLucky); |
53 | } |
54 | } |
55 | |
56 | /** |
57 | * An observer which removes a given Observer object from the list when observe is called. |
58 | */ |
59 | private static class FooRemover implements Observer { |
60 | private final ObserverList<Observer> mList; |
61 | private final Observer mDoomed; |
62 | |
63 | FooRemover(ObserverList<Observer> list, Observer innocent) { |
64 | mList = list; |
65 | mDoomed = innocent; |
66 | } |
67 | |
68 | @Override |
69 | public void observe(int x) { |
70 | mList.removeObserver(mDoomed); |
71 | } |
72 | } |
73 | |
74 | private static <T> int getSizeOfIterable(Iterable<T> iterable) { |
75 | int num = 0; |
76 | for (T el : iterable) |
77 | num++; |
78 | return num; |
79 | } |
80 | |
81 | @SmallTest |
82 | @Feature({"Android-AppBase"}) |
83 | public void testRemoveWhileIteration() { |
84 | ObserverList<Observer> observerList = new ObserverList<Observer>(); |
85 | Foo a = new Foo(1); |
86 | Foo b = new Foo(-1); |
87 | Foo c = new Foo(1); |
88 | Foo d = new Foo(-1); |
89 | Foo e = new Foo(-1); |
90 | FooRemover evil = new FooRemover(observerList, c); |
91 | |
92 | observerList.addObserver(a); |
93 | observerList.addObserver(b); |
94 | |
95 | for (Observer obs : observerList) |
96 | obs.observe(10); |
97 | |
98 | // Removing an observer not in the list should do nothing. |
99 | observerList.removeObserver(e); |
100 | |
101 | observerList.addObserver(evil); |
102 | observerList.addObserver(c); |
103 | observerList.addObserver(d); |
104 | |
105 | for (Observer obs : observerList) |
106 | obs.observe(10); |
107 | |
108 | // observe should be called twice on a. |
109 | assertEquals(20, a.mTotal); |
110 | // observe should be called twice on b. |
111 | assertEquals(-20, b.mTotal); |
112 | // evil removed c from the observerList before it got any callbacks. |
113 | assertEquals(0, c.mTotal); |
114 | // observe should be called once on d. |
115 | assertEquals(-10, d.mTotal); |
116 | // e was never added to the list, observe should not be called. |
117 | assertEquals(0, e.mTotal); |
118 | } |
119 | |
120 | @SmallTest |
121 | @Feature({"Android-AppBase"}) |
122 | public void testAddWhileIteration() { |
123 | ObserverList<Observer> observerList = new ObserverList<Observer>(); |
124 | Foo a = new Foo(1); |
125 | Foo b = new Foo(-1); |
126 | Foo c = new Foo(1); |
127 | FooAdder evil = new FooAdder(observerList, c); |
128 | |
129 | observerList.addObserver(evil); |
130 | observerList.addObserver(a); |
131 | observerList.addObserver(b); |
132 | |
133 | for (Observer obs : observerList) |
134 | obs.observe(10); |
135 | |
136 | assertTrue(observerList.hasObserver(c)); |
137 | assertEquals(10, a.mTotal); |
138 | assertEquals(-10, b.mTotal); |
139 | assertEquals(0, c.mTotal); |
140 | } |
141 | |
142 | @SmallTest |
143 | @Feature({"Android-AppBase"}) |
144 | public void testIterator() { |
145 | ObserverList<Integer> observerList = new ObserverList<Integer>(); |
146 | observerList.addObserver(5); |
147 | observerList.addObserver(10); |
148 | observerList.addObserver(15); |
149 | assertEquals(3, getSizeOfIterable(observerList)); |
150 | |
151 | observerList.removeObserver(10); |
152 | assertEquals(2, getSizeOfIterable(observerList)); |
153 | |
154 | Iterator<Integer> it = observerList.iterator(); |
155 | assertTrue(it.hasNext()); |
156 | assertTrue(5 == it.next()); |
157 | assertTrue(it.hasNext()); |
158 | assertTrue(15 == it.next()); |
159 | assertFalse(it.hasNext()); |
160 | |
161 | boolean removeExceptionThrown = false; |
162 | try { |
163 | it.remove(); |
164 | fail("Expecting UnsupportedOperationException to be thrown here."); |
165 | } catch (UnsupportedOperationException e) { |
166 | removeExceptionThrown = true; |
167 | } |
168 | assertTrue(removeExceptionThrown); |
169 | assertEquals(2, getSizeOfIterable(observerList)); |
170 | |
171 | boolean noElementExceptionThrown = false; |
172 | try { |
173 | it.next(); |
174 | fail("Expecting NoSuchElementException to be thrown here."); |
175 | } catch (NoSuchElementException e) { |
176 | noElementExceptionThrown = true; |
177 | } |
178 | assertTrue(noElementExceptionThrown); |
179 | } |
180 | } |