14
14
15
15
package com .google .firebase .remoteconfig ;
16
16
17
+ import static android .os .AsyncTask .THREAD_POOL_EXECUTOR ;
17
18
import static androidx .test .ext .truth .os .BundleSubject .assertThat ;
18
19
import static com .google .common .truth .Truth .assertThat ;
19
20
import static com .google .common .truth .Truth .assertWithMessage ;
26
27
import static com .google .firebase .remoteconfig .FirebaseRemoteConfig .toExperimentInfoMaps ;
27
28
import static com .google .firebase .remoteconfig .internal .Personalization .EXTERNAL_ARM_VALUE_PARAM ;
28
29
import static com .google .firebase .remoteconfig .internal .Personalization .EXTERNAL_PERSONALIZATION_ID_PARAM ;
30
+ import static com .google .firebase .remoteconfig .testutil .Assert .assertTrue ;
29
31
import static org .mockito .ArgumentMatchers .anyString ;
30
32
import static org .mockito .ArgumentMatchers .eq ;
31
33
import static org .mockito .Matchers .any ;
42
44
43
45
import android .content .Context ;
44
46
import android .content .res .Resources ;
47
+ import android .os .AsyncTask ;
45
48
import android .os .Bundle ;
46
49
import androidx .annotation .NonNull ;
47
50
import androidx .test .core .app .ApplicationProvider ;
83
86
import java .util .List ;
84
87
import java .util .Map ;
85
88
import java .util .Set ;
89
+ import java .util .concurrent .Callable ;
90
+ import java .util .concurrent .CountDownLatch ;
86
91
import java .util .concurrent .Executor ;
92
+ import java .util .concurrent .ExecutorService ;
93
+ import java .util .concurrent .Executors ;
94
+ import java .util .concurrent .ScheduledExecutorService ;
95
+ import java .util .concurrent .TimeUnit ;
96
+
87
97
import org .json .JSONArray ;
88
98
import org .json .JSONException ;
89
99
import org .json .JSONObject ;
93
103
import org .mockito .ArgumentCaptor ;
94
104
import org .mockito .Mock ;
95
105
import org .mockito .MockitoAnnotations ;
106
+ import org .robolectric .Robolectric ;
96
107
import org .robolectric .RobolectricTestRunner ;
108
+ import org .robolectric .android .util .concurrent .InlineExecutorService ;
97
109
import org .robolectric .annotation .Config ;
98
110
import org .robolectric .annotation .LooperMode ;
99
111
import org .skyscreamer .jsonassert .JSONAssert ;
@@ -179,9 +191,10 @@ public final class FirebaseRemoteConfigTest {
179
191
private ConfigContainer realtimeFetchedContainer ;
180
192
private ConfigAutoFetch configAutoFetch ;
181
193
private ConfigRealtimeHttpClient configRealtimeHttpClient ;
182
-
183
194
private FetchResponse firstFetchedContainerResponse ;
184
195
196
+ private final ScheduledExecutorService executorService = Executors .newSingleThreadScheduledExecutor ();
197
+
185
198
@ Before
186
199
public void setUp () throws Exception {
187
200
DEFAULTS_MAP .put ("first_default_key" , "first_default_value" );
@@ -324,7 +337,8 @@ public void onError(@NonNull FirebaseRemoteConfigException error) {
324
337
mockFetchHandler ,
325
338
mockActivatedCache ,
326
339
listeners ,
327
- mockRetryListener );
340
+ mockRetryListener ,
341
+ executorService );
328
342
configRealtimeHttpClient =
329
343
new ConfigRealtimeHttpClient (
330
344
firebaseApp ,
@@ -333,7 +347,8 @@ public void onError(@NonNull FirebaseRemoteConfigException error) {
333
347
mockActivatedCache ,
334
348
context ,
335
349
"firebase" ,
336
- listeners );
350
+ listeners ,
351
+ executorService );
337
352
}
338
353
339
354
@ Test
@@ -1300,51 +1315,42 @@ public void realtime_stream_listen_get_inputstream_fail() throws Exception {
1300
1315
1301
1316
@ Test
1302
1317
public void realtime_stream_autofetch_success () throws Exception {
1318
+ // Setup activated configs with keys "string_param", "long_param"
1319
+ loadCacheWithConfig (mockActivatedCache , firstFetchedContainer );
1303
1320
when (mockFetchHandler .getTemplateVersionNumber ()).thenReturn (1L );
1304
1321
when (mockFetchHandler .fetch (0L )).thenReturn (Tasks .forResult (realtimeFetchedContainerResponse ));
1305
1322
1306
- // Setup activated configs with keys "string_param", "long_param"
1307
- loadCacheWithConfig (mockActivatedCache , firstFetchedContainer );
1308
- Set <String > updatedParams = Sets .newHashSet ("realtime_param" );
1323
+ configAutoFetch .fetchLatestConfig (1 , 1 );
1324
+ flushTasks ();
1309
1325
1310
- configAutoFetch
1311
- .fetchLatestConfig (1 , 1 )
1312
- .addOnCompleteListener (
1313
- unused -> {
1314
- verify (mockOnUpdateListener ).onUpdate (updatedParams );
1315
- });
1326
+ Set <String > updatedParams = Sets .newHashSet ("realtime_param" );
1327
+ verify (mockOnUpdateListener ).onUpdate (updatedParams );
1316
1328
}
1317
1329
1318
1330
@ Test
1319
- public void realtime_autofetchBeforeActivate_callsOnUpdateWithAllFetchedParams () {
1331
+ public void realtime_autofetchBeforeActivate_callsOnUpdateWithAllFetchedParams () throws Exception {
1332
+ // The first call to get() returns null while the cache is loading.
1333
+ loadCacheWithConfig (mockActivatedCache , null );
1320
1334
when (mockFetchHandler .getTemplateVersionNumber ()).thenReturn (1L );
1321
1335
when (mockFetchHandler .fetch (0 )).thenReturn (Tasks .forResult (realtimeFetchedContainerResponse ));
1322
1336
1323
- // The first call to get() returns null while the cache is loading.
1324
- loadCacheWithConfig ( mockActivatedCache , null );
1337
+ configAutoFetch . fetchLatestConfig ( 1 , 1 );
1338
+ flushTasks ( );
1325
1339
1326
1340
Set <String > updatedParams = Sets .newHashSet ("string_param" , "long_param" , "realtime_param" );
1327
-
1328
- configAutoFetch
1329
- .fetchLatestConfig (1 , 1 )
1330
- .addOnCompleteListener (
1331
- unused -> {
1332
- verify (mockOnUpdateListener ).onUpdate (updatedParams );
1333
- });
1341
+ verify (mockOnUpdateListener ).onUpdate (updatedParams );
1334
1342
}
1335
1343
1336
1344
@ Test
1337
- public void realtime_stream_autofetch_failure () {
1345
+ public void realtime_stream_autofetch_failure () throws Exception {
1346
+ loadCacheWithConfig (mockActivatedCache , firstFetchedContainer );
1338
1347
when (mockFetchHandler .getTemplateVersionNumber ()).thenReturn (1L );
1339
1348
when (mockFetchHandler .fetch (0 )).thenReturn (Tasks .forResult (realtimeFetchedContainerResponse ));
1340
1349
1341
- configAutoFetch
1342
- .fetchLatestConfig (1 , 1000 )
1343
- .addOnCompleteListener (
1344
- unused -> {
1345
- verify (mockNotFetchedEventListener )
1346
- .onError (any (FirebaseRemoteConfigServerException .class ));
1347
- });
1350
+ configAutoFetch .fetchLatestConfig (1 , 1000 );
1351
+ flushTasks ();
1352
+
1353
+ verify (mockNotFetchedEventListener ).onError (any (FirebaseRemoteConfigServerException .class ));
1348
1354
}
1349
1355
1350
1356
private static void loadCacheWithConfig (
@@ -1424,6 +1430,17 @@ private static FirebaseApp initializeFirebaseApp(Context context) {
1424
1430
.build ());
1425
1431
}
1426
1432
1433
+ /**
1434
+ * Flush tasks on the {@code executorService}'s thread.
1435
+ *
1436
+ * @throws InterruptedException
1437
+ */
1438
+ private void flushTasks () throws InterruptedException {
1439
+ CountDownLatch latch = new CountDownLatch (1 );
1440
+ executorService .execute (latch ::countDown );
1441
+ assertTrue ("Task didn't finish" , latch .await (10 , TimeUnit .MILLISECONDS ));
1442
+ }
1443
+
1427
1444
private ConfigUpdateListener generateEmptyRealtimeListener () {
1428
1445
return new ConfigUpdateListener () {
1429
1446
@ Override
0 commit comments