| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.base.supplier; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotSame; |
| import static org.junit.Assert.assertTrue; |
| |
| import android.os.Handler; |
| |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.robolectric.annotation.Config; |
| |
| import org.chromium.base.Callback; |
| import org.chromium.base.test.BaseRobolectricTestRunner; |
| |
| /** Unit tests for {@link ObservableSupplierImpl}. */ |
| @RunWith(BaseRobolectricTestRunner.class) |
| @Config(manifest = Config.NONE) |
| public class ObservableSupplierImplTest { |
| private static final String TEST_STRING_1 = "Test"; |
| private static final String TEST_STRING_2 = "Test2"; |
| |
| private int mCallCount; |
| private String mLastSuppliedString; |
| private ObservableSupplierImpl<String> mSupplier = new ObservableSupplierImpl<>(); |
| |
| @Test |
| public void testObserverNotification_SetMultiple() { |
| Callback<String> supplierObserver = |
| result -> { |
| mCallCount++; |
| mLastSuppliedString = result; |
| }; |
| |
| mSupplier.addObserver(supplierObserver); |
| checkState(0, null, null, "before setting first string."); |
| |
| mSupplier.set(TEST_STRING_1); |
| checkState(1, TEST_STRING_1, TEST_STRING_1, "after setting first string."); |
| |
| mSupplier.set(TEST_STRING_2); |
| checkState(2, TEST_STRING_2, TEST_STRING_2, "after setting second string."); |
| |
| mSupplier.set(null); |
| checkState(3, null, null, "after setting third string."); |
| } |
| |
| @Test |
| public void testObserverNotification_SetSame() { |
| Callback<String> supplierObserver = |
| result -> { |
| mCallCount++; |
| mLastSuppliedString = result; |
| }; |
| |
| mSupplier.addObserver(supplierObserver); |
| checkState(0, null, null, "before setting first string."); |
| |
| mSupplier.set(TEST_STRING_1); |
| checkState(1, TEST_STRING_1, TEST_STRING_1, "after setting first string."); |
| |
| mSupplier.set(TEST_STRING_1); |
| checkState(1, TEST_STRING_1, TEST_STRING_1, "after resetting first string."); |
| |
| // Need to trick Java to not intern our new string. |
| String anotherTestString1 = new String(new char[] {'T', 'e', 's', 't'}); |
| assertNotSame(TEST_STRING_1, anotherTestString1); |
| mSupplier.set(anotherTestString1); |
| // Don't use checkState, as the string arguments do not really make sense. |
| assertEquals( |
| "Incorrect call count after setting a different but equal string.", 1, mCallCount); |
| } |
| |
| @Test |
| public void testObserverNotification_RemoveObserver() { |
| Callback<String> supplierObserver = |
| result -> { |
| mCallCount++; |
| mLastSuppliedString = result; |
| }; |
| |
| mSupplier.addObserver(supplierObserver); |
| checkState(0, null, null, "before setting first string."); |
| |
| mSupplier.set(TEST_STRING_1); |
| checkState(1, TEST_STRING_1, TEST_STRING_1, "after setting first string."); |
| |
| mSupplier.removeObserver(supplierObserver); |
| |
| mSupplier.set(TEST_STRING_2); |
| checkState(1, TEST_STRING_1, TEST_STRING_2, "after setting second string."); |
| } |
| |
| @Test |
| public void testObserverNotification_RegisterObserverAfterSet() { |
| Handler handler = new Handler(); |
| handler.post( |
| () -> { |
| mSupplier.set(TEST_STRING_1); |
| checkState(0, null, TEST_STRING_1, "after setting first string."); |
| |
| Callback<String> supplierObserver = |
| (String result) -> { |
| mCallCount++; |
| mLastSuppliedString = result; |
| }; |
| |
| mSupplier.addObserver(supplierObserver); |
| |
| checkState(0, null, TEST_STRING_1, "after setting observer."); |
| }); |
| |
| handler.post(() -> checkState(1, TEST_STRING_1, TEST_STRING_1, "in second message loop.")); |
| } |
| |
| @Test |
| public void testObserverNotification_RegisterObserverAfterSetThenSetAgain() { |
| Handler handler = new Handler(); |
| handler.post( |
| () -> { |
| mSupplier.set(TEST_STRING_1); |
| checkState(0, null, TEST_STRING_1, "after setting first string."); |
| |
| Callback<String> supplierObserver = |
| (String result) -> { |
| mCallCount++; |
| mLastSuppliedString = result; |
| }; |
| |
| mSupplier.addObserver(supplierObserver); |
| |
| checkState(0, null, TEST_STRING_1, "after setting observer."); |
| |
| mSupplier.set(TEST_STRING_2); |
| checkState(1, TEST_STRING_2, TEST_STRING_2, "after setting second string."); |
| }); |
| |
| handler.post(() -> checkState(1, TEST_STRING_2, TEST_STRING_2, "in second message loop.")); |
| } |
| |
| @Test |
| public void testObserverNotification_RegisterObserverAfterSetThenRemove() { |
| Handler handler = new Handler(); |
| handler.post( |
| () -> { |
| mSupplier.set(TEST_STRING_1); |
| checkState(0, null, TEST_STRING_1, "after setting first string."); |
| |
| Callback<String> supplierObserver = |
| (String result) -> { |
| mCallCount++; |
| mLastSuppliedString = result; |
| }; |
| |
| mSupplier.addObserver(supplierObserver); |
| |
| checkState(0, null, TEST_STRING_1, "after setting observer."); |
| |
| mSupplier.removeObserver(supplierObserver); |
| }); |
| |
| handler.post(() -> checkState(0, null, TEST_STRING_1, "in second message loop.")); |
| } |
| |
| @Test |
| public void testObserverNotification_RemoveObserverInsideCallback() { |
| Callback<String> supplierObserver = |
| new Callback<>() { |
| @Override |
| public void onResult(String result) { |
| mCallCount++; |
| mLastSuppliedString = result; |
| mSupplier.removeObserver(this); |
| } |
| }; |
| |
| mSupplier.addObserver(supplierObserver); |
| checkState(0, null, null, "before setting first string."); |
| |
| mSupplier.set(TEST_STRING_1); |
| checkState(1, TEST_STRING_1, TEST_STRING_1, "after setting first string."); |
| |
| mSupplier.set(TEST_STRING_2); |
| checkState(1, TEST_STRING_1, TEST_STRING_2, "after setting second string."); |
| } |
| |
| @Test |
| public void testHasObservers() { |
| Callback<String> observer1 = (ignored) -> {}; |
| Callback<String> observer2 = (ignored) -> {}; |
| |
| assertFalse("No observers yet", mSupplier.hasObservers()); |
| |
| mSupplier.addObserver(observer1); |
| assertTrue("Should have observer1", mSupplier.hasObservers()); |
| |
| mSupplier.addObserver(observer1); |
| assertTrue("Adding observer1 twice shouldn't break anything", mSupplier.hasObservers()); |
| |
| mSupplier.removeObserver(observer1); |
| assertFalse( |
| "observer1 should be entirely removed with one remove", mSupplier.hasObservers()); |
| |
| mSupplier.addObserver(observer1); |
| mSupplier.addObserver(observer2); |
| assertTrue("Should have multiple observers", mSupplier.hasObservers()); |
| |
| mSupplier.removeObserver(observer1); |
| assertTrue("Should still have observer2", mSupplier.hasObservers()); |
| |
| mSupplier.removeObserver(observer1); |
| assertTrue("Removing observer1 twice shouldn't break anything", mSupplier.hasObservers()); |
| |
| mSupplier.removeObserver(observer2); |
| assertFalse("Both observers should be gone", mSupplier.hasObservers()); |
| } |
| |
| private void checkState( |
| int expectedCallCount, |
| String expectedLastSuppliedString, |
| String expectedStringFromGet, |
| String assertDescription) { |
| assertEquals("Incorrect call count " + assertDescription, expectedCallCount, mCallCount); |
| assertEquals( |
| "Incorrect last supplied string " + assertDescription, |
| expectedLastSuppliedString, |
| mLastSuppliedString); |
| assertEquals( |
| "Incorrect #get() " + assertDescription, expectedStringFromGet, mSupplier.get()); |
| } |
| } |