Skip to content

Commit 4e8dceb

Browse files
committed
RUM-10064: Emit available feature contexts when FeatureSdkCore.setContextUpdateReceiver is called
1 parent 8893ced commit 4e8dceb

File tree

5 files changed

+95
-5
lines changed

5 files changed

+95
-5
lines changed

dd-sdk-android-core/src/main/kotlin/com/datadog/android/api/feature/FeatureSdkCore.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ interface FeatureSdkCore : SdkCore {
7070
fun setEventReceiver(featureName: String, receiver: FeatureEventReceiver)
7171

7272
/**
73-
* Sets context update receiver for the given feature.
73+
* Sets context update receiver for the given feature. Once subscribed, current context will be emitted
74+
* immdediately if it exists.
7475
*
7576
* @param featureName Feature name.
7677
* @param listener Listener to remove.

dd-sdk-android-core/src/main/kotlin/com/datadog/android/core/internal/DatadogCore.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,12 @@ internal class DatadogCore(
260260
)
261261
} else {
262262
feature.setContextUpdateListener(listener)
263+
features.keys.forEach {
264+
val currentContext = contextProvider?.getFeatureContext(it)
265+
if (!currentContext.isNullOrEmpty()) {
266+
listener.onContextUpdate(it, currentContext)
267+
}
268+
}
263269
}
264270
}
265271

dd-sdk-android-core/src/test/kotlin/com/datadog/android/core/DatadogCoreTest.kt

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import com.datadog.tools.unit.annotations.TestConfigurationsProvider
3838
import com.datadog.tools.unit.extensions.TestConfigurationExtension
3939
import com.datadog.tools.unit.extensions.config.TestConfiguration
4040
import com.datadog.tools.unit.forge.aThrowable
41+
import com.datadog.tools.unit.forge.exhaustiveAttributes
4142
import com.google.gson.JsonObject
4243
import fr.xgouchet.elmyr.Forge
4344
import fr.xgouchet.elmyr.annotation.AdvancedForgery
@@ -445,7 +446,7 @@ internal class DatadogCoreTest {
445446
) {
446447
// Given
447448
val mockFeature = mock<SdkFeature>()
448-
val mockContextUpdateListener: FeatureContextUpdateReceiver = mock()
449+
val mockContextUpdateListener = mock<FeatureContextUpdateReceiver>()
449450
testedCore.features[feature] = mockFeature
450451

451452
// When
@@ -455,12 +456,68 @@ internal class DatadogCoreTest {
455456
verify(mockFeature).setContextUpdateListener(mockContextUpdateListener)
456457
}
457458

459+
@Test
460+
fun `M not invoke listener W setContextUpdateListener() { other features have no context yet }`(
461+
@StringForgery feature: String,
462+
forge: Forge
463+
) {
464+
// Given
465+
val mockFeature = mock<SdkFeature>()
466+
val mockContextUpdateListener = mock<FeatureContextUpdateReceiver>()
467+
testedCore.features[feature] = mockFeature
468+
val mockContextProvider = mock<ContextProvider>()
469+
testedCore.coreFeature.contextProvider = mockContextProvider
470+
whenever(mockContextProvider.getFeatureContext(any())) doAnswer {
471+
forge.anElementFrom(null, emptyMap<String, Any?>())
472+
}
473+
repeat(forge.aTinyInt()) {
474+
testedCore.features += forge.aString() to mock<SdkFeature>()
475+
}
476+
477+
// When
478+
testedCore.setContextUpdateReceiver(feature, mockContextUpdateListener)
479+
480+
// Then
481+
verify(mockFeature).setContextUpdateListener(mockContextUpdateListener)
482+
verifyNoInteractions(mockContextUpdateListener)
483+
}
484+
485+
@Test
486+
fun `M invoke listener W setContextUpdateListener() { other features have context }`(
487+
@StringForgery feature: String,
488+
forge: Forge
489+
) {
490+
// Given
491+
val mockFeature = mock<SdkFeature>()
492+
val mockContextUpdateListener = mock<FeatureContextUpdateReceiver>()
493+
testedCore.features[feature] = mockFeature
494+
val mockContextProvider = mock<ContextProvider>()
495+
testedCore.coreFeature.contextProvider = mockContextProvider
496+
val otherFeatures = forge.aList {
497+
forge.aString() to forge.exhaustiveAttributes()
498+
}
499+
otherFeatures.forEach {
500+
testedCore.features += it.first to mock<SdkFeature>()
501+
whenever(mockContextProvider.getFeatureContext(it.first)) doReturn it.second
502+
}
503+
504+
// When
505+
testedCore.setContextUpdateReceiver(feature, mockContextUpdateListener)
506+
507+
// Then
508+
verify(mockFeature).setContextUpdateListener(mockContextUpdateListener)
509+
otherFeatures.forEach {
510+
verify(mockContextUpdateListener).onContextUpdate(it.first, it.second)
511+
}
512+
verifyNoMoreInteractions(mockContextUpdateListener)
513+
}
514+
458515
@Test
459516
fun `M notify no feature registered W setContextUpdateListener() { feature is not registered }`(
460517
@StringForgery feature: String
461518
) {
462519
// Given
463-
val mockContextUpdateListener: FeatureContextUpdateReceiver = mock()
520+
val mockContextUpdateListener = mock<FeatureContextUpdateReceiver>()
464521

465522
// When
466523
testedCore.setContextUpdateReceiver(feature, mockContextUpdateListener)

features/dd-sdk-android-webview/src/main/kotlin/com/datadog/android/webview/internal/rum/WebViewRumFeature.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ internal class WebViewRumFeature(
4141
sdkCore.setContextUpdateReceiver(WEB_RUM_FEATURE_NAME, this)
4242
dataWriter = createDataWriter(sdkCore.internalLogger)
4343
initialized.set(true)
44-
val currentRumContext = sdkCore.getFeatureContext(Feature.RUM_FEATURE_NAME)
45-
nativeRumViewsCache.addToCache(currentRumContext)
4644
}
4745

4846
override fun onContextUpdate(featureName: String, event: Map<String, Any?>) {

reliability/core-it/src/androidTest/kotlin/com/datadog/android/core/integration/tests/FeatureSdkCoreTest.kt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,34 @@ class FeatureSdkCoreTest : MockServerTest() {
391391
assertThat(receivedEvent.eventData).containsExactlyInAnyOrderEntriesOf(fakeKeyValues)
392392
}
393393

394+
@Test
395+
fun mustReceiveCurrentContextIfExists_when_contextUpdateReceiverRegistered() {
396+
// Given
397+
val stubContextUpdateReceiver = StubContextUpdateReceiver()
398+
val otherFeature = StubStorageBackedFeature(
399+
forge,
400+
forge.anAlphabeticalString(),
401+
getMockServerWrapper().getServerUrl()
402+
)
403+
val fakeKeyValues = forge.aMap { forge.anAlphabeticalString() to forge.anAlphabeticalString() }
404+
testedFeatureSdkCore.registerFeature(stubFeature)
405+
testedFeatureSdkCore.registerFeature(otherFeature)
406+
testedFeatureSdkCore.updateFeatureContext(stubFeature.name) {
407+
fakeKeyValues.forEach { (key, value) ->
408+
it[key] = value
409+
}
410+
}
411+
412+
// When
413+
testedFeatureSdkCore.setContextUpdateReceiver(otherFeature.name, stubContextUpdateReceiver)
414+
415+
// Then
416+
assertThat(stubContextUpdateReceiver.getReceivedEvents()).hasSize(1)
417+
val receivedEvent = stubContextUpdateReceiver.getReceivedEvents().first()
418+
assertThat(receivedEvent.featureName).isEqualTo(stubFeature.name)
419+
assertThat(receivedEvent.eventData).containsExactlyInAnyOrderEntriesOf(fakeKeyValues)
420+
}
421+
394422
@Test
395423
fun mustReceiveContextUpdate_when_contextUpdateReceiverRegistered_updateCalledFromDifferentThreads() {
396424
// Given

0 commit comments

Comments
 (0)