@@ -19,10 +19,7 @@ package com.google.firebase.sessions.settings
19
19
import androidx.test.ext.junit.runners.AndroidJUnit4
20
20
import com.google.common.truth.Truth.assertThat
21
21
import com.google.firebase.FirebaseApp
22
- import com.google.firebase.installations.FirebaseInstallationsApi
23
- import com.google.firebase.sessions.ApplicationInfo
24
22
import com.google.firebase.sessions.SessionEvents
25
- import com.google.firebase.sessions.TimeProvider
26
23
import com.google.firebase.sessions.testing.FakeFirebaseApp
27
24
import com.google.firebase.sessions.testing.FakeFirebaseInstallations
28
25
import com.google.firebase.sessions.testing.FakeRemoteConfigFetcher
@@ -31,55 +28,46 @@ import com.google.firebase.sessions.testing.FakeTimeProvider
31
28
import kotlin.time.Duration.Companion.minutes
32
29
import kotlin.time.Duration.Companion.seconds
33
30
import kotlinx.coroutines.Dispatchers
34
- import kotlinx.coroutines.ExperimentalCoroutinesApi
35
31
import kotlinx.coroutines.delay
36
32
import kotlinx.coroutines.launch
37
- import kotlinx.coroutines.test.UnconfinedTestDispatcher
38
- import kotlinx.coroutines.test.runCurrent
39
33
import kotlinx.coroutines.test.runTest
40
34
import kotlinx.coroutines.withTimeout
41
35
import org.json.JSONObject
42
36
import org.junit.After
43
37
import org.junit.Test
44
38
import org.junit.runner.RunWith
45
39
46
- @OptIn(ExperimentalCoroutinesApi ::class )
47
40
@RunWith(AndroidJUnit4 ::class )
48
41
class RemoteSettingsTest {
49
42
50
43
@Test
51
- fun remoteSettings_successfulFetchCachesValues () =
52
- runTest(UnconfinedTestDispatcher ()) {
53
- val firebaseApp = FakeFirebaseApp ().firebaseApp
54
- val firebaseInstallations = FakeFirebaseInstallations (" FaKeFiD" )
55
- val fakeFetcher = FakeRemoteConfigFetcher ()
56
-
57
- val remoteSettings =
58
- buildRemoteSettings(
59
- FakeTimeProvider (),
60
- firebaseInstallations,
61
- SessionEvents .getApplicationInfo(firebaseApp),
62
- fakeFetcher,
63
- FakeSettingsCache (),
64
- )
65
-
66
- runCurrent()
44
+ fun remoteSettings_successfulFetchCachesValues () = runTest {
45
+ val firebaseApp = FakeFirebaseApp ().firebaseApp
46
+ val firebaseInstallations = FakeFirebaseInstallations (" FaKeFiD" )
47
+ val fakeFetcher = FakeRemoteConfigFetcher ()
67
48
68
- assertThat(remoteSettings.sessionEnabled).isNull()
69
- assertThat(remoteSettings.samplingRate).isNull()
70
- assertThat(remoteSettings.sessionRestartTimeout).isNull()
49
+ val remoteSettings =
50
+ RemoteSettings (
51
+ FakeTimeProvider (),
52
+ firebaseInstallations,
53
+ SessionEvents .getApplicationInfo(firebaseApp),
54
+ fakeFetcher,
55
+ FakeSettingsCache (),
56
+ )
71
57
72
- fakeFetcher.responseJSONObject = JSONObject (VALID_RESPONSE )
73
- remoteSettings.updateSettings()
58
+ assertThat(remoteSettings.sessionEnabled).isNull()
59
+ assertThat(remoteSettings.samplingRate).isNull()
60
+ assertThat(remoteSettings.sessionRestartTimeout).isNull()
74
61
75
- runCurrent()
62
+ fakeFetcher.responseJSONObject = JSONObject (VALID_RESPONSE )
63
+ remoteSettings.updateSettings()
76
64
77
- assertThat(remoteSettings.sessionEnabled).isFalse()
78
- assertThat(remoteSettings.samplingRate).isEqualTo(0.75 )
79
- assertThat(remoteSettings.sessionRestartTimeout).isEqualTo(40 .minutes)
65
+ assertThat(remoteSettings.sessionEnabled).isFalse()
66
+ assertThat(remoteSettings.samplingRate).isEqualTo(0.75 )
67
+ assertThat(remoteSettings.sessionRestartTimeout).isEqualTo(40 .minutes)
80
68
81
- remoteSettings.clearCachedSettings()
82
- }
69
+ remoteSettings.clearCachedSettings()
70
+ }
83
71
84
72
@Test
85
73
fun remoteSettings_successfulFetchWithLessConfigsCachesOnlyReceivedValues () = runTest {
@@ -88,16 +76,14 @@ class RemoteSettingsTest {
88
76
val fakeFetcher = FakeRemoteConfigFetcher ()
89
77
90
78
val remoteSettings =
91
- buildRemoteSettings (
79
+ RemoteSettings (
92
80
FakeTimeProvider (),
93
81
firebaseInstallations,
94
82
SessionEvents .getApplicationInfo(firebaseApp),
95
83
fakeFetcher,
96
84
FakeSettingsCache (),
97
85
)
98
86
99
- runCurrent()
100
-
101
87
assertThat(remoteSettings.sessionEnabled).isNull()
102
88
assertThat(remoteSettings.samplingRate).isNull()
103
89
assertThat(remoteSettings.sessionRestartTimeout).isNull()
@@ -107,8 +93,6 @@ class RemoteSettingsTest {
107
93
fakeFetcher.responseJSONObject = fetchedResponse
108
94
remoteSettings.updateSettings()
109
95
110
- runCurrent()
111
-
112
96
assertThat(remoteSettings.sessionEnabled).isNull()
113
97
assertThat(remoteSettings.samplingRate).isEqualTo(0.75 )
114
98
assertThat(remoteSettings.sessionRestartTimeout).isEqualTo(40 .minutes)
@@ -124,7 +108,7 @@ class RemoteSettingsTest {
124
108
val fakeTimeProvider = FakeTimeProvider ()
125
109
126
110
val remoteSettings =
127
- buildRemoteSettings (
111
+ RemoteSettings (
128
112
fakeTimeProvider,
129
113
firebaseInstallations,
130
114
SessionEvents .getApplicationInfo(firebaseApp),
@@ -137,8 +121,6 @@ class RemoteSettingsTest {
137
121
fakeFetcher.responseJSONObject = fetchedResponse
138
122
remoteSettings.updateSettings()
139
123
140
- runCurrent()
141
-
142
124
assertThat(remoteSettings.sessionEnabled).isFalse()
143
125
assertThat(remoteSettings.samplingRate).isEqualTo(0.75 )
144
126
assertThat(remoteSettings.sessionRestartTimeout).isEqualTo(40 .minutes)
@@ -152,8 +134,6 @@ class RemoteSettingsTest {
152
134
fakeFetcher.responseJSONObject = fetchedResponse
153
135
remoteSettings.updateSettings()
154
136
155
- runCurrent()
156
-
157
137
assertThat(remoteSettings.sessionEnabled).isTrue()
158
138
assertThat(remoteSettings.samplingRate).isEqualTo(0.25 )
159
139
assertThat(remoteSettings.sessionRestartTimeout).isEqualTo(20 .minutes)
@@ -162,110 +142,99 @@ class RemoteSettingsTest {
162
142
}
163
143
164
144
@Test
165
- fun remoteSettings_successfulFetchWithEmptyConfigRetainsOldConfigs () =
166
- runTest(UnconfinedTestDispatcher ()) {
167
- val firebaseApp = FakeFirebaseApp ().firebaseApp
168
- val firebaseInstallations = FakeFirebaseInstallations (" FaKeFiD" )
169
- val fakeFetcher = FakeRemoteConfigFetcher ()
170
- val fakeTimeProvider = FakeTimeProvider ()
171
-
172
- val remoteSettings =
173
- buildRemoteSettings(
174
- fakeTimeProvider,
175
- firebaseInstallations,
176
- SessionEvents .getApplicationInfo(firebaseApp),
177
- fakeFetcher,
178
- FakeSettingsCache (),
179
- )
180
-
181
- val fetchedResponse = JSONObject (VALID_RESPONSE )
182
- fetchedResponse.getJSONObject(" app_quality" ).put(" cache_duration" , 1 )
183
- fakeFetcher.responseJSONObject = fetchedResponse
184
- remoteSettings.updateSettings()
185
-
186
- runCurrent()
187
- fakeTimeProvider.addInterval(31 .seconds)
188
-
189
- assertThat(remoteSettings.sessionEnabled).isFalse()
190
- assertThat(remoteSettings.samplingRate).isEqualTo(0.75 )
191
- assertThat(remoteSettings.sessionRestartTimeout).isEqualTo(40 .minutes)
192
-
193
- fetchedResponse.remove(" app_quality" )
194
-
195
- // Sleep for a second before updating configs
196
- Thread .sleep(2000 )
197
-
198
- fakeFetcher.responseJSONObject = fetchedResponse
199
- remoteSettings.updateSettings()
200
-
201
- runCurrent()
202
- Thread .sleep(30 )
203
-
204
- assertThat(remoteSettings.sessionEnabled).isFalse()
205
- assertThat(remoteSettings.samplingRate).isEqualTo(0.75 )
206
- assertThat(remoteSettings.sessionRestartTimeout).isEqualTo(40 .minutes)
207
-
208
- remoteSettings.clearCachedSettings()
209
- }
145
+ fun remoteSettings_successfulFetchWithEmptyConfigRetainsOldConfigs () = runTest {
146
+ val firebaseApp = FakeFirebaseApp ().firebaseApp
147
+ val firebaseInstallations = FakeFirebaseInstallations (" FaKeFiD" )
148
+ val fakeFetcher = FakeRemoteConfigFetcher ()
149
+ val fakeTimeProvider = FakeTimeProvider ()
150
+
151
+ val remoteSettings =
152
+ RemoteSettings (
153
+ fakeTimeProvider,
154
+ firebaseInstallations,
155
+ SessionEvents .getApplicationInfo(firebaseApp),
156
+ fakeFetcher,
157
+ FakeSettingsCache (),
158
+ )
159
+
160
+ val fetchedResponse = JSONObject (VALID_RESPONSE )
161
+ fetchedResponse.getJSONObject(" app_quality" ).put(" cache_duration" , 1 )
162
+ fakeFetcher.responseJSONObject = fetchedResponse
163
+ remoteSettings.updateSettings()
164
+
165
+ fakeTimeProvider.addInterval(31 .seconds)
166
+
167
+ assertThat(remoteSettings.sessionEnabled).isFalse()
168
+ assertThat(remoteSettings.samplingRate).isEqualTo(0.75 )
169
+ assertThat(remoteSettings.sessionRestartTimeout).isEqualTo(40 .minutes)
170
+
171
+ fetchedResponse.remove(" app_quality" )
172
+
173
+ fakeFetcher.responseJSONObject = fetchedResponse
174
+ remoteSettings.updateSettings()
175
+
176
+ assertThat(remoteSettings.sessionEnabled).isFalse()
177
+ assertThat(remoteSettings.samplingRate).isEqualTo(0.75 )
178
+ assertThat(remoteSettings.sessionRestartTimeout).isEqualTo(40 .minutes)
179
+
180
+ remoteSettings.clearCachedSettings()
181
+ }
210
182
211
183
@Test
212
- fun remoteSettings_fetchWhileFetchInProgress () =
213
- runTest(UnconfinedTestDispatcher ()) {
214
- // This test does:
215
- // 1. Do a fetch with a fake fetcher that will block for 3 seconds.
216
- // 2. While that is happening, do a second fetch.
217
- // - First fetch is still fetching, so second fetch should fall through to the mutex.
218
- // - Second fetch will be blocked until first completes.
219
- // - First fetch returns, should unblock the second fetch.
220
- // - Second fetch should go into mutex, sees cache is valid in "double check," exist early.
221
- // 3. After a fetch completes, do a third fetch.
222
- // - First fetch should have have updated the cache.
223
- // - Third fetch should exit even earlier, never having gone into the mutex.
224
-
225
- val firebaseApp = FakeFirebaseApp ().firebaseApp
226
- val firebaseInstallations = FakeFirebaseInstallations (" FaKeFiD" )
227
- val fakeFetcherWithDelay =
228
- FakeRemoteConfigFetcher (JSONObject (VALID_RESPONSE ), networkDelay = 3 .seconds)
229
-
230
- fakeFetcherWithDelay.responseJSONObject
231
- .getJSONObject(" app_quality" )
232
- .put(" sampling_rate" , 0.125 )
233
-
234
- val remoteSettingsWithDelay =
235
- buildRemoteSettings(
236
- FakeTimeProvider (),
237
- firebaseInstallations,
238
- SessionEvents .getApplicationInfo(firebaseApp),
239
- configsFetcher = fakeFetcherWithDelay,
240
- FakeSettingsCache (),
241
- )
242
-
243
- // Do the first fetch. This one should fetched the configsFetcher.
244
- val firstFetch = launch(Dispatchers .Default ) { remoteSettingsWithDelay.updateSettings() }
245
-
246
- // Wait a second, and then do the second fetch while first is still running.
247
- // This one should block until the first fetch completes, but then exit early.
248
- launch(Dispatchers .Default ) {
249
- delay(1 .seconds)
250
- remoteSettingsWithDelay.updateSettings()
251
- }
184
+ fun remoteSettings_fetchWhileFetchInProgress () = runTest {
185
+ // This test does:
186
+ // 1. Do a fetch with a fake fetcher that will block for 3 seconds.
187
+ // 2. While that is happening, do a second fetch.
188
+ // - First fetch is still fetching, so second fetch should fall through to the mutex.
189
+ // - Second fetch will be blocked until first completes.
190
+ // - First fetch returns, should unblock the second fetch.
191
+ // - Second fetch should go into mutex, sees cache is valid in "double check," exist early.
192
+ // 3. After a fetch completes, do a third fetch.
193
+ // - First fetch should have have updated the cache.
194
+ // - Third fetch should exit even earlier, never having gone into the mutex.
195
+
196
+ val firebaseApp = FakeFirebaseApp ().firebaseApp
197
+ val firebaseInstallations = FakeFirebaseInstallations (" FaKeFiD" )
198
+ val fakeFetcherWithDelay =
199
+ FakeRemoteConfigFetcher (JSONObject (VALID_RESPONSE ), networkDelay = 3 .seconds)
200
+
201
+ fakeFetcherWithDelay.responseJSONObject.getJSONObject(" app_quality" ).put(" sampling_rate" , 0.125 )
202
+
203
+ val remoteSettingsWithDelay =
204
+ RemoteSettings (
205
+ FakeTimeProvider (),
206
+ firebaseInstallations,
207
+ SessionEvents .getApplicationInfo(firebaseApp),
208
+ configsFetcher = fakeFetcherWithDelay,
209
+ FakeSettingsCache (),
210
+ )
252
211
253
- // Wait until the first fetch is done, then do a third fetch.
254
- // This one should not even block, and exit early.
255
- firstFetch.join()
256
- withTimeout(1 .seconds) { remoteSettingsWithDelay.updateSettings() }
212
+ // Do the first fetch. This one should fetched the configsFetcher.
213
+ val firstFetch = launch(Dispatchers .Default ) { remoteSettingsWithDelay.updateSettings() }
257
214
258
- // Assert that the configsFetcher was fetched exactly once.
259
- assertThat(fakeFetcherWithDelay.timesCalled).isEqualTo(1 )
260
- assertThat(remoteSettingsWithDelay.samplingRate).isEqualTo(0.125 )
215
+ // Wait a second, and then do the second fetch while first is still running.
216
+ // This one should block until the first fetch completes, but then exit early.
217
+ launch(Dispatchers .Default ) {
218
+ delay(1 .seconds)
219
+ remoteSettingsWithDelay.updateSettings()
261
220
}
262
221
222
+ // Wait until the first fetch is done, then do a third fetch.
223
+ // This one should not even block, and exit early.
224
+ firstFetch.join()
225
+ withTimeout(1 .seconds) { remoteSettingsWithDelay.updateSettings() }
226
+
227
+ // Assert that the configsFetcher was fetched exactly once.
228
+ assertThat(fakeFetcherWithDelay.timesCalled).isEqualTo(1 )
229
+ assertThat(remoteSettingsWithDelay.samplingRate).isEqualTo(0.125 )
230
+ }
231
+
263
232
@After
264
233
fun cleanUp () {
265
234
FirebaseApp .clearInstancesForTest()
266
235
}
267
236
268
- internal companion object {
237
+ private companion object {
269
238
const val VALID_RESPONSE =
270
239
"""
271
240
{
@@ -284,30 +253,5 @@ class RemoteSettingsTest {
284
253
}
285
254
}
286
255
"""
287
-
288
- /* *
289
- * Build an instance of [RemoteSettings] using the Dagger factory.
290
- *
291
- * This is needed because the SDK vendors Dagger to a difference namespace, but it does not for
292
- * these unit tests. The [RemoteSettings.lazySettingsCache] has type [dagger.Lazy] in these
293
- * tests, but type `com.google.firebase.sessions.dagger.Lazy` in the SDK. This method to build
294
- * the instance is the easiest I could find that does not need any reference to [dagger.Lazy] in
295
- * the test code.
296
- */
297
- fun buildRemoteSettings (
298
- timeProvider : TimeProvider ,
299
- firebaseInstallationsApi : FirebaseInstallationsApi ,
300
- appInfo : ApplicationInfo ,
301
- configsFetcher : CrashlyticsSettingsFetcher ,
302
- settingsCache : SettingsCache ,
303
- ): RemoteSettings =
304
- RemoteSettings_Factory .create(
305
- { timeProvider },
306
- { firebaseInstallationsApi },
307
- { appInfo },
308
- { configsFetcher },
309
- { settingsCache },
310
- )
311
- .get()
312
256
}
313
257
}
0 commit comments