• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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