1 // Copyright 2023 The Chromium Authors 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.supplier; 6 7 import static org.junit.Assert.assertEquals; 8 import static org.junit.Assert.assertFalse; 9 import static org.junit.Assert.assertNull; 10 import static org.junit.Assert.assertTrue; 11 import static org.mockito.ArgumentMatchers.eq; 12 import static org.mockito.Mockito.never; 13 import static org.mockito.Mockito.verify; 14 import static org.mockito.Mockito.verifyNoInteractions; 15 16 import org.junit.Rule; 17 import org.junit.Test; 18 import org.junit.runner.RunWith; 19 import org.mockito.Mock; 20 import org.mockito.junit.MockitoJUnit; 21 import org.mockito.junit.MockitoRule; 22 import org.robolectric.shadows.ShadowLooper; 23 24 import org.chromium.base.Callback; 25 import org.chromium.base.test.BaseRobolectricTestRunner; 26 27 import java.util.function.Function; 28 29 /** Unit tests for {@link TransitiveObservableSupplier}. */ 30 @RunWith(BaseRobolectricTestRunner.class) 31 public class TransitiveObservableSupplierTest { 32 // Shared singleton lambda to satisfy all callers. Type erasure makes it all equivalent at 33 // runtime and we still get compile time type safety. 34 private static final Function<?, ?> SHARED_TRAMPOLINE = arg -> arg; 35 36 public @Rule MockitoRule mMockitoRule = MockitoJUnit.rule(); 37 38 private @Mock Callback<Object> mOnChangeCallback; 39 // While nothing is verified on these, mocks have good toString methods by default. 40 private @Mock Object mObject1; 41 private @Mock Object mObject2; 42 private @Mock Object mObject3; 43 private @Mock Object mObject4; 44 45 /** 46 * Convenience helper when the parent value needs no unwrapping. These methods should be moved 47 * to the implementation file if any client needs it. 48 */ make( ObservableSupplier<ObservableSupplier<Z>> parentSupplier)49 private static <Z> TransitiveObservableSupplier<ObservableSupplier<Z>, Z> make( 50 ObservableSupplier<ObservableSupplier<Z>> parentSupplier) { 51 return new TransitiveObservableSupplier(parentSupplier, trampoline()); 52 } 53 trampoline()54 private static <T> Function<T, T> trampoline() { 55 return (Function<T, T>) SHARED_TRAMPOLINE; 56 } 57 58 @Test testGetWithoutObservers()59 public void testGetWithoutObservers() { 60 ObservableSupplierImpl<ObservableSupplier<Object>> parentSupplier = 61 new ObservableSupplierImpl<>(); 62 ObservableSupplierImpl<Object> targetSupplier1 = new ObservableSupplierImpl<>(); 63 64 ObservableSupplier<Object> transitiveSupplier = make(parentSupplier); 65 assertNull(transitiveSupplier.get()); 66 67 parentSupplier.set(targetSupplier1); 68 assertNull(transitiveSupplier.get()); 69 70 targetSupplier1.set(mObject1); 71 assertEquals(mObject1, transitiveSupplier.get()); 72 73 targetSupplier1.set(null); 74 assertNull(transitiveSupplier.get()); 75 76 targetSupplier1.set(mObject2); 77 assertEquals(mObject2, transitiveSupplier.get()); 78 79 parentSupplier.set(null); 80 assertNull(transitiveSupplier.get()); 81 82 targetSupplier1.set(mObject3); 83 assertNull(transitiveSupplier.get()); 84 85 parentSupplier.set(targetSupplier1); 86 assertEquals(mObject3, transitiveSupplier.get()); 87 } 88 89 @Test testGetWithObserver()90 public void testGetWithObserver() { 91 ObservableSupplierImpl<ObservableSupplier<Object>> parentSupplier = 92 new ObservableSupplierImpl<>(); 93 ObservableSupplierImpl<Object> targetSupplier1 = new ObservableSupplierImpl<>(); 94 ObservableSupplierImpl<Object> targetSupplier2 = new ObservableSupplierImpl<>(); 95 96 ObservableSupplier<Object> transitiveSupplier = make(parentSupplier); 97 assertNull(transitiveSupplier.get()); 98 99 assertNull(transitiveSupplier.addObserver(mOnChangeCallback)); 100 verifyNoInteractions(mOnChangeCallback); 101 102 parentSupplier.set(targetSupplier1); 103 assertNull(transitiveSupplier.get()); 104 verifyNoInteractions(mOnChangeCallback); 105 106 targetSupplier1.set(mObject1); 107 assertEquals(mObject1, transitiveSupplier.get()); 108 verify(mOnChangeCallback).onResult(eq(mObject1)); 109 110 targetSupplier1.set(mObject2); 111 assertEquals(mObject2, transitiveSupplier.get()); 112 verify(mOnChangeCallback).onResult(eq(mObject2)); 113 114 targetSupplier1.set(null); 115 assertEquals(null, transitiveSupplier.get()); 116 verify(mOnChangeCallback).onResult(eq(null)); 117 118 targetSupplier2.set(mObject3); 119 parentSupplier.set(targetSupplier2); 120 assertEquals(mObject3, transitiveSupplier.get()); 121 verify(mOnChangeCallback).onResult(eq(mObject3)); 122 123 transitiveSupplier.removeObserver(mOnChangeCallback); 124 targetSupplier2.set(mObject4); 125 assertEquals(mObject4, transitiveSupplier.get()); 126 verify(mOnChangeCallback, never()).onResult(eq(mObject4)); 127 } 128 129 @Test testSameObserver()130 public void testSameObserver() { 131 ObservableSupplierImpl<ObservableSupplier<Object>> parentSupplier = 132 new ObservableSupplierImpl<>(); 133 ObservableSupplierImpl<Object> targetSupplier = new ObservableSupplierImpl<>(); 134 parentSupplier.set(targetSupplier); 135 136 ObservableSupplier<Object> transitiveSupplier = make(parentSupplier); 137 assertEquals(null, transitiveSupplier.addObserver(mOnChangeCallback)); 138 assertTrue(parentSupplier.hasObservers()); 139 assertTrue(targetSupplier.hasObservers()); 140 141 targetSupplier.set(mObject1); 142 assertEquals(mObject1, transitiveSupplier.get()); 143 verify(mOnChangeCallback).onResult(eq(mObject1)); 144 145 assertEquals(mObject1, transitiveSupplier.addObserver(mOnChangeCallback)); 146 transitiveSupplier.removeObserver(mOnChangeCallback); 147 assertFalse(parentSupplier.hasObservers()); 148 assertFalse(targetSupplier.hasObservers()); 149 } 150 151 @Test testAlreadyHasValueWhenObserverAdded()152 public void testAlreadyHasValueWhenObserverAdded() { 153 ObservableSupplierImpl<ObservableSupplier<Object>> parentSupplier = 154 new ObservableSupplierImpl<>(); 155 ObservableSupplierImpl<Object> targetSupplier = new ObservableSupplierImpl<>(); 156 parentSupplier.set(targetSupplier); 157 targetSupplier.set(mObject1); 158 159 ObservableSupplier<Object> transitiveSupplier = make(parentSupplier); 160 assertEquals(mObject1, transitiveSupplier.get()); 161 162 assertEquals(mObject1, transitiveSupplier.addObserver(mOnChangeCallback)); 163 assertEquals(mObject1, transitiveSupplier.get()); 164 ShadowLooper.idleMainLooper(); 165 verify(mOnChangeCallback).onResult(eq(mObject1)); 166 } 167 } 168