/* * Copyright 2017 Realm Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.realm.internal; import android.annotation.SuppressLint; import android.support.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; /** * We are testing characteristic of the {@link ObserverPairList} here, such as: * Ownership of the listeners, equality of the pair and all public APIs for the class. */ @RunWith(AndroidJUnit4.class) public class ObserverPairListTests { private static class TestListener<Integer> { void onChange(Integer integer) { } } private static class TestObserverPair extends ObserverPairList.ObserverPair<Integer, TestListener> { TestObserverPair(Integer observer, TestListener listener) { super(observer, listener); } } private ObserverPairList<TestObserverPair> observerPairs; private TestListener testListener = new TestListener<Integer>(); private static final Integer ONE = 1; private static final Integer TWO = 2; private static final Integer THREE = 3; @Before public void setUp() { observerPairs = new ObserverPairList<TestObserverPair>(); } @After public void tearDown() { observerPairs = null; } @Test public void add() { TestObserverPair pair = new TestObserverPair(ONE, testListener); observerPairs.add(pair); assertEquals(1, observerPairs.size()); // Same observer object, different listener. pair = new TestObserverPair(ONE, new TestListener<Integer>()); observerPairs.add(pair); assertEquals(2, observerPairs.size()); // Different observer object, different listener. pair = new TestObserverPair(TWO, new TestListener<Integer>()); observerPairs.add(pair); assertEquals(3, observerPairs.size()); // Different observer object, same listener. pair = new TestObserverPair(TWO, testListener); observerPairs.add(pair); assertEquals(4, observerPairs.size()); } @Test // The Observer pair is treated as the same when the observer is the same object and the listener is the same too. public void add_noDuplicate() { TestObserverPair pair = new TestObserverPair(ONE, testListener); observerPairs.add(pair); assertEquals(1, observerPairs.size()); pair = new TestObserverPair(ONE, testListener); observerPairs.add(pair); assertEquals(1, observerPairs.size()); } // 1. add 2. clear 3. add 4. Check if the last listener can still be called. @Test public void add_worksAfterClears() { final AtomicBoolean foreachCalled = new AtomicBoolean(false); TestObserverPair pair = new TestObserverPair(ONE, testListener); observerPairs.add(pair); assertEquals(1, observerPairs.size()); observerPairs.clear(); observerPairs.add(pair); assertEquals(1, observerPairs.size()); observerPairs.foreach(new ObserverPairList.Callback<TestObserverPair>() { @Override public void onCalled(TestObserverPair pair, Object observer) { assertEquals(ONE, observer); foreachCalled.set(true); } }); assertTrue(foreachCalled.get()); } @SuppressLint({"UseValueOf", "BoxedPrimitiveConstructor"}) @Test public void remove() { TestObserverPair pair = new TestObserverPair(ONE, testListener); observerPairs.add(pair); assertEquals(1, observerPairs.size()); // Create a new Integer 1 to see if the equality is checked by the same object. //noinspection UnnecessaryBoxing observerPairs.remove(new Integer(1), testListener); assertEquals(1, observerPairs.size()); // Different listener observerPairs.remove(ONE, new TestListener()); assertEquals(1, observerPairs.size()); // Should remove now observerPairs.remove(ONE, testListener); assertEquals(0, observerPairs.size()); } @Test public void removeByObserver() { TestObserverPair pair = new TestObserverPair(ONE, testListener); observerPairs.add(pair); pair = new TestObserverPair(ONE, new TestListener()); observerPairs.add(pair); assertEquals(2, observerPairs.size()); // An different observer //noinspection UnnecessaryBoxing pair = new TestObserverPair(TWO, testListener); observerPairs.add(pair); assertEquals(3, observerPairs.size()); observerPairs.removeByObserver(ONE); assertEquals(1, observerPairs.size()); observerPairs.removeByObserver(TWO); assertEquals(0, observerPairs.size()); } @Test public void clear() { TestObserverPair pair = new TestObserverPair(ONE, new TestListener()); observerPairs.add(pair); assertEquals(1, observerPairs.size()); observerPairs.clear(); assertEquals(0, observerPairs.size()); } @Test public void isEmpty() { assertTrue(observerPairs.isEmpty()); TestObserverPair pair = new TestObserverPair(ONE, new TestListener()); observerPairs.add(pair); assertFalse(observerPairs.isEmpty()); observerPairs.clear(); assertTrue(observerPairs.isEmpty()); } @Test public void foreach() { final boolean[] onChangesCalled = {false, false}; TestListener<Integer> listener = new TestListener<Integer>() { @Override void onChange(Integer i) { onChangesCalled[i-1] = true; } }; TestObserverPair pair = new TestObserverPair(ONE, listener); observerPairs.add(pair); pair = new TestObserverPair(TWO, listener); observerPairs.add(pair); observerPairs.foreach(new ObserverPairList.Callback<TestObserverPair>() { @Override public void onCalled(TestObserverPair pair, Object observer) { //noinspection unchecked pair.listener.onChange(observer); } }); assertTrue(onChangesCalled[0] && onChangesCalled[1]); } // Test if the observer is GCed, the relevant listener should be removed when foreach called. @Test public void foreach_shouldRemoveWeakRefs() { TestObserverPair pair = new TestObserverPair(ONE, new TestListener()); observerPairs.add(pair); assertEquals(1, observerPairs.size()); observerPairs.foreach(new ObserverPairList.Callback<TestObserverPair>() { @Override public void onCalled(TestObserverPair pair, Object observer) { // There is no guaranteed way to release the WeakReference, // just clear it. pair.observerRef.clear(); } }); assertEquals(1, observerPairs.size()); observerPairs.foreach(new ObserverPairList.Callback<TestObserverPair>() { @Override public void onCalled(TestObserverPair pair, Object observer) { fail(); } }); assertEquals(0, observerPairs.size()); } @Test public void foreach_canRemove() { final AtomicInteger count = new AtomicInteger(0); final TestObserverPair pair1 = new TestObserverPair(ONE, new TestListener()); final TestListener listener2 = new TestListener(); final TestObserverPair pair2 = new TestObserverPair(TWO, listener2); final TestObserverPair pair3 = new TestObserverPair(THREE, new TestListener()); observerPairs.add(pair1); observerPairs.add(pair2); observerPairs.add(pair3); assertEquals(3, observerPairs.size()); observerPairs.foreach(new ObserverPairList.Callback<TestObserverPair>() { @Override public void onCalled(TestObserverPair pair, Object observer) { assertFalse(((Integer) observer) == 2); observerPairs.remove(TWO, listener2); count.getAndIncrement(); } }); assertEquals(2, observerPairs.size()); assertEquals(2, count.get()); } @Test public void foreach_canClear() { final AtomicInteger count = new AtomicInteger(0); final TestObserverPair pair1 = new TestObserverPair(ONE, new TestListener()); final TestObserverPair pair2 = new TestObserverPair(TWO, new TestListener()); final TestObserverPair pair3 = new TestObserverPair(THREE, new TestListener()); observerPairs.add(pair1); observerPairs.add(pair2); observerPairs.add(pair3); assertEquals(3, observerPairs.size()); observerPairs.foreach(new ObserverPairList.Callback<TestObserverPair>() { @Override public void onCalled(TestObserverPair pair, Object observer) { assertFalse(((Integer) observer) == 2); assertFalse(((Integer) observer) == 3); observerPairs.clear(); count.getAndIncrement(); } }); assertEquals(0, observerPairs.size()); assertEquals(1, count.get()); observerPairs.add(pair1); assertEquals(1, observerPairs.size()); observerPairs.foreach(new ObserverPairList.Callback<TestObserverPair>() { @Override public void onCalled(TestObserverPair pair, Object observer) { assertTrue(((Integer) observer) == 1); } }); } @Test public void foreach_canAdd() { final AtomicInteger count = new AtomicInteger(0); final TestObserverPair pair1 = new TestObserverPair(ONE, new TestListener()); final TestObserverPair pair2 = new TestObserverPair(TWO, new TestListener()); observerPairs.add(pair1); assertEquals(1, observerPairs.size()); observerPairs.foreach(new ObserverPairList.Callback<TestObserverPair>() { @Override public void onCalled(TestObserverPair pair, Object observer) { observerPairs.add(pair2); count.getAndIncrement(); } }); assertEquals(2, observerPairs.size()); assertEquals(1, count.get()); count.set(0); assertEquals(2, observerPairs.size()); observerPairs.foreach(new ObserverPairList.Callback<TestObserverPair>() { @Override public void onCalled(TestObserverPair pair, Object observer) { count.getAndIncrement(); } }); assertEquals(2, count.get()); } }