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