// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package org.chromium.base; import android.test.InstrumentationTestCase; import android.test.suitebuilder.annotation.SmallTest; import org.chromium.base.test.util.Feature; import java.util.Collection; import java.util.Iterator; import java.util.NoSuchElementException; /** * Tests for (@link ObserverList}. */ public class ObserverListTest extends InstrumentationTestCase { interface Observer { void observe(int x); } private static class Foo implements Observer { private final int mScalar; private int mTotal = 0; Foo(int scalar) { mScalar = scalar; } @Override public void observe(int x) { mTotal += x * mScalar; } } /** * An observer which add a given Observer object to the list when observe is called. */ private static class FooAdder implements Observer { private final ObserverList mList; private final Observer mLucky; FooAdder(ObserverList list, Observer oblivious) { mList = list; mLucky = oblivious; } @Override public void observe(int x) { mList.addObserver(mLucky); } } /** * An observer which removes a given Observer object from the list when observe is called. */ private static class FooRemover implements Observer { private final ObserverList mList; private final Observer mDoomed; FooRemover(ObserverList list, Observer innocent) { mList = list; mDoomed = innocent; } @Override public void observe(int x) { mList.removeObserver(mDoomed); } } @SuppressWarnings("ElementsCountedInLoop") private static int getSizeOfIterable(Iterable iterable) { if (iterable instanceof Collection) return ((Collection) iterable).size(); int num = 0; for (T el : iterable) num++; return num; } @SmallTest @Feature({"Android-AppBase"}) public void testRemoveWhileIteration() { ObserverList observerList = new ObserverList(); Foo a = new Foo(1); Foo b = new Foo(-1); Foo c = new Foo(1); Foo d = new Foo(-1); Foo e = new Foo(-1); FooRemover evil = new FooRemover(observerList, c); observerList.addObserver(a); observerList.addObserver(b); for (Observer obs : observerList) obs.observe(10); // Removing an observer not in the list should do nothing. observerList.removeObserver(e); observerList.addObserver(evil); observerList.addObserver(c); observerList.addObserver(d); for (Observer obs : observerList) obs.observe(10); // observe should be called twice on a. assertEquals(20, a.mTotal); // observe should be called twice on b. assertEquals(-20, b.mTotal); // evil removed c from the observerList before it got any callbacks. assertEquals(0, c.mTotal); // observe should be called once on d. assertEquals(-10, d.mTotal); // e was never added to the list, observe should not be called. assertEquals(0, e.mTotal); } @SmallTest @Feature({"Android-AppBase"}) public void testAddWhileIteration() { ObserverList observerList = new ObserverList(); Foo a = new Foo(1); Foo b = new Foo(-1); Foo c = new Foo(1); FooAdder evil = new FooAdder(observerList, c); observerList.addObserver(evil); observerList.addObserver(a); observerList.addObserver(b); for (Observer obs : observerList) obs.observe(10); assertTrue(observerList.hasObserver(c)); assertEquals(10, a.mTotal); assertEquals(-10, b.mTotal); assertEquals(0, c.mTotal); } @SmallTest @Feature({"Android-AppBase"}) public void testIterator() { ObserverList observerList = new ObserverList(); observerList.addObserver(5); observerList.addObserver(10); observerList.addObserver(15); assertEquals(3, getSizeOfIterable(observerList)); observerList.removeObserver(10); assertEquals(2, getSizeOfIterable(observerList)); Iterator it = observerList.iterator(); assertTrue(it.hasNext()); assertTrue(5 == it.next()); assertTrue(it.hasNext()); assertTrue(15 == it.next()); assertFalse(it.hasNext()); boolean removeExceptionThrown = false; try { it.remove(); fail("Expecting UnsupportedOperationException to be thrown here."); } catch (UnsupportedOperationException e) { removeExceptionThrown = true; } assertTrue(removeExceptionThrown); assertEquals(2, getSizeOfIterable(observerList)); boolean noElementExceptionThrown = false; try { it.next(); fail("Expecting NoSuchElementException to be thrown here."); } catch (NoSuchElementException e) { noElementExceptionThrown = true; } assertTrue(noElementExceptionThrown); } @SmallTest @Feature({"Android-AppBase"}) public void testRewindableIterator() { ObserverList observerList = new ObserverList(); observerList.addObserver(5); observerList.addObserver(10); observerList.addObserver(15); assertEquals(3, getSizeOfIterable(observerList)); ObserverList.RewindableIterator it = observerList.rewindableIterator(); assertTrue(it.hasNext()); assertTrue(5 == it.next()); assertTrue(it.hasNext()); assertTrue(10 == it.next()); assertTrue(it.hasNext()); assertTrue(15 == it.next()); assertFalse(it.hasNext()); it.rewind(); assertTrue(it.hasNext()); assertTrue(5 == it.next()); assertTrue(it.hasNext()); assertTrue(10 == it.next()); assertTrue(it.hasNext()); assertTrue(15 == it.next()); assertEquals(5, (int) observerList.mObservers.get(0)); observerList.removeObserver(5); assertEquals(null, observerList.mObservers.get(0)); it.rewind(); assertEquals(10, (int) observerList.mObservers.get(0)); assertTrue(it.hasNext()); assertTrue(10 == it.next()); assertTrue(it.hasNext()); assertTrue(15 == it.next()); } @SmallTest @Feature({"Android-AppBase"}) public void testAddObserverReturnValue() { ObserverList observerList = new ObserverList(); Object a = new Object(); assertTrue(observerList.addObserver(a)); assertFalse(observerList.addObserver(a)); Object b = new Object(); assertTrue(observerList.addObserver(b)); assertFalse(observerList.addObserver(null)); } @SmallTest @Feature({"Android-AppBase"}) public void testRemoveObserverReturnValue() { ObserverList observerList = new ObserverList(); Object a = new Object(); Object b = new Object(); observerList.addObserver(a); observerList.addObserver(b); assertTrue(observerList.removeObserver(a)); assertFalse(observerList.removeObserver(a)); assertFalse(observerList.removeObserver(new Object())); assertTrue(observerList.removeObserver(b)); assertFalse(observerList.removeObserver(null)); // If we remove an object while iterating, it will be replaced by 'null'. observerList.addObserver(a); assertTrue(observerList.removeObserver(a)); assertFalse(observerList.removeObserver(null)); } @SmallTest @Feature({"Android-AppBase"}) public void testSize() { ObserverList observerList = new ObserverList(); assertEquals(0, observerList.size()); assertTrue(observerList.isEmpty()); observerList.addObserver(null); assertEquals(0, observerList.size()); assertTrue(observerList.isEmpty()); Object a = new Object(); observerList.addObserver(a); assertEquals(1, observerList.size()); assertFalse(observerList.isEmpty()); observerList.addObserver(a); assertEquals(1, observerList.size()); assertFalse(observerList.isEmpty()); observerList.addObserver(null); assertEquals(1, observerList.size()); assertFalse(observerList.isEmpty()); Object b = new Object(); observerList.addObserver(b); assertEquals(2, observerList.size()); assertFalse(observerList.isEmpty()); observerList.removeObserver(null); assertEquals(2, observerList.size()); assertFalse(observerList.isEmpty()); observerList.removeObserver(new Object()); assertEquals(2, observerList.size()); assertFalse(observerList.isEmpty()); observerList.removeObserver(b); assertEquals(1, observerList.size()); assertFalse(observerList.isEmpty()); observerList.removeObserver(b); assertEquals(1, observerList.size()); assertFalse(observerList.isEmpty()); observerList.removeObserver(a); assertEquals(0, observerList.size()); assertTrue(observerList.isEmpty()); observerList.removeObserver(a); observerList.removeObserver(b); observerList.removeObserver(null); observerList.removeObserver(new Object()); assertEquals(0, observerList.size()); assertTrue(observerList.isEmpty()); observerList.addObserver(new Object()); observerList.addObserver(new Object()); observerList.addObserver(new Object()); observerList.addObserver(a); assertEquals(4, observerList.size()); assertFalse(observerList.isEmpty()); observerList.clear(); assertEquals(0, observerList.size()); assertTrue(observerList.isEmpty()); observerList.removeObserver(a); observerList.removeObserver(b); observerList.removeObserver(null); observerList.removeObserver(new Object()); assertEquals(0, observerList.size()); assertTrue(observerList.isEmpty()); } }