From af16658dde145a17733cc421551bd246dd671ae6 Mon Sep 17 00:00:00 2001 From: bidetofevil Date: Tue, 10 Sep 2024 21:20:42 -0700 Subject: [PATCH] Hook up Activity Open tracing component --- .../behavior/AutoDataCaptureBehavior.kt | 5 + .../behavior/AutoDataCaptureBehaviorImpl.kt | 1 + .../AutoDataCaptureBehaviorImplTest.kt | 1 + .../internal/spans/EmbraceSpanBuilderTest.kt | 4 +- .../capture/startup/StartupTracker.kt | 5 + .../injection/DataCaptureServiceModule.kt | 8 +- .../injection/DataCaptureServiceModuleImpl.kt | 23 ++ .../activity/UiLoadEventEmitterTest.kt | 123 +--------- .../capture/startup/StartupTrackerTest.kt | 2 + .../DataCaptureServiceModuleImplTest.kt | 20 ++ .../schema/EnabledFeatureConfig.kt | 7 + .../testcases/features/UiLoadTest.kt | 220 ++++++++++++++++++ .../actions/EmbraceActionInterface.kt | 56 +++++ .../injection/ModuleInitBootstrapper.kt | 3 +- .../injection/FakeDataCaptureServiceModule.kt | 11 +- .../embracesdk/fakes/FakeUiLoadEvents.kt | 119 ++++++++++ .../behavior/FakeAutoDataCaptureBehavior.kt | 2 + .../fakes/config/FakeEnabledFeatureConfig.kt | 2 + 18 files changed, 488 insertions(+), 124 deletions(-) create mode 100644 embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/UiLoadTest.kt create mode 100644 embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeUiLoadEvents.kt diff --git a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AutoDataCaptureBehavior.kt b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AutoDataCaptureBehavior.kt index 81641b26ff..116e1b7e08 100644 --- a/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AutoDataCaptureBehavior.kt +++ b/embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/config/behavior/AutoDataCaptureBehavior.kt @@ -65,6 +65,11 @@ interface AutoDataCaptureBehavior : ConfigBehavior private var startTimeMs: Long = 0L @@ -37,7 +38,7 @@ internal class UiLoadEventEmitterTest { clock = FakeClock() val initModule = FakeInitModule(clock = clock) clock.tick(100L) - openEvents = FakeOpenEvents() + openEvents = FakeUiLoadEvents() eventEmitter = UiLoadEventEmitter( uiLoadEvents = openEvents, clock = initModule.openTelemetryModule.openTelemetryClock, @@ -262,120 +263,4 @@ internal class UiLoadEventEmitterTest { activityName = activityName, timestampMs = timestampMs ) - - class FakeOpenEvents : UiLoadEvents { - val events = mutableListOf() - - override fun abandon(instanceId: Int, activityName: String, timestampMs: Long) { - events.add( - EventData( - stage = "abandon", - instanceId = instanceId, - activityName = activityName, - timestampMs = timestampMs - ) - ) - } - - override fun reset(instanceId: Int) { - events.add( - EventData( - stage = "reset", - instanceId = instanceId, - ) - ) - } - - override fun create(instanceId: Int, activityName: String, timestampMs: Long) { - events.add( - EventData( - stage = "create", - instanceId = instanceId, - activityName = activityName, - timestampMs = timestampMs - ) - ) - } - - override fun createEnd(instanceId: Int, timestampMs: Long) { - events.add( - EventData( - stage = "createEnd", - instanceId = instanceId, - activityName = null, - timestampMs = timestampMs - ) - ) - } - - override fun start(instanceId: Int, activityName: String, timestampMs: Long) { - events.add( - EventData( - stage = "start", - instanceId = instanceId, - activityName = activityName, - timestampMs = timestampMs - ) - ) - } - - override fun startEnd(instanceId: Int, timestampMs: Long) { - events.add( - EventData( - stage = "startEnd", - instanceId = instanceId, - timestampMs = timestampMs - ) - ) - } - - override fun resume(instanceId: Int, activityName: String, timestampMs: Long) { - events.add( - EventData( - stage = "resume", - instanceId = instanceId, - activityName = activityName, - timestampMs = timestampMs - ) - ) - } - - override fun resumeEnd(instanceId: Int, timestampMs: Long) { - events.add( - EventData( - stage = "resumeEnd", - instanceId = instanceId, - timestampMs = timestampMs - ) - ) - } - - override fun render(instanceId: Int, activityName: String, timestampMs: Long) { - events.add( - EventData( - stage = "render", - instanceId = instanceId, - activityName = activityName, - timestampMs = timestampMs - ) - ) - } - - override fun renderEnd(instanceId: Int, timestampMs: Long) { - events.add( - EventData( - stage = "renderEnd", - instanceId = instanceId, - timestampMs = timestampMs - ) - ) - } - - data class EventData( - val stage: String, - val instanceId: Int, - val activityName: String? = null, - val timestampMs: Long? = null, - ) - } } diff --git a/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/capture/startup/StartupTrackerTest.kt b/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/capture/startup/StartupTrackerTest.kt index 625139230f..2f797cf937 100644 --- a/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/capture/startup/StartupTrackerTest.kt +++ b/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/capture/startup/StartupTrackerTest.kt @@ -10,6 +10,7 @@ import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.FakeEmbLogger import io.embrace.android.embracesdk.fakes.FakeSplashScreenActivity import io.embrace.android.embracesdk.internal.logging.EmbLogger +import io.embrace.android.embracesdk.internal.session.lifecycle.ActivityLifecycleListener import io.embrace.android.embracesdk.internal.utils.BuildVersionChecker import org.junit.Assert.assertEquals import org.junit.Assert.assertNotEquals @@ -38,6 +39,7 @@ internal class StartupTrackerTest { dataCollector = FakeAppStartupDataCollector(clock = clock) startupTracker = StartupTracker( appStartupDataCollector = dataCollector, + uiLoadEventEmitter = object : ActivityLifecycleListener { }, logger = logger, versionChecker = BuildVersionChecker ) diff --git a/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/injection/DataCaptureServiceModuleImplTest.kt b/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/injection/DataCaptureServiceModuleImplTest.kt index f91745b5fe..7d7bb836c4 100644 --- a/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/injection/DataCaptureServiceModuleImplTest.kt +++ b/embrace-android-features/src/test/java/io/embrace/android/embracesdk/internal/injection/DataCaptureServiceModuleImplTest.kt @@ -3,9 +3,11 @@ package io.embrace.android.embracesdk.internal.injection import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.FakeFeatureModule import io.embrace.android.embracesdk.fakes.FakeVersionChecker +import io.embrace.android.embracesdk.fakes.behavior.FakeAutoDataCaptureBehavior import io.embrace.android.embracesdk.fakes.injection.FakeInitModule import io.embrace.android.embracesdk.fakes.injection.FakeWorkerThreadModule import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull import org.junit.Test internal class DataCaptureServiceModuleImplTest { @@ -29,5 +31,23 @@ internal class DataCaptureServiceModuleImplTest { assertNotNull(module.appStartupDataCollector) assertNotNull(module.pushNotificationService) assertNotNull(module.startupService) + assertNull(module.uiLoadEventEmitter) + } + + @Test + fun `enable ui load performance capture`() { + val module = DataCaptureServiceModuleImpl( + initModule, + openTelemetryModule, + FakeConfigService( + autoDataCaptureBehavior = FakeAutoDataCaptureBehavior(uiLoadPerfCaptureEnabled = true) + ), + FakeWorkerThreadModule(), + FakeVersionChecker(false), + FakeFeatureModule() + ) + + assertNotNull(module.uiLoadEvents) + assertNotNull(module.uiLoadEventEmitter) } } diff --git a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/EnabledFeatureConfig.kt b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/EnabledFeatureConfig.kt index 84587c1446..cb69611033 100644 --- a/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/EnabledFeatureConfig.kt +++ b/embrace-android-payload/src/main/kotlin/io/embrace/android/embracesdk/internal/config/instrumented/schema/EnabledFeatureConfig.kt @@ -145,4 +145,11 @@ interface EnabledFeatureConfig { * sdk_config.networking.enable_network_span_forwarding */ fun isNetworkSpanForwardingEnabled(): Boolean = false + + /** + * Gates whether the SDK will capture traces for the performance of the opening of Activities. + * + * sdk_config.automatic_data_capture.ui_load_perf_info + */ + fun isUiLoadPerfCaptureEnabled(): Boolean = false } diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/UiLoadTest.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/UiLoadTest.kt new file mode 100644 index 0000000000..0d2afa9ca7 --- /dev/null +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/features/UiLoadTest.kt @@ -0,0 +1,220 @@ +package io.embrace.android.embracesdk.testcases.features + +import android.app.Activity +import android.os.Build +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.embrace.android.embracesdk.annotation.ObservedActivity +import io.embrace.android.embracesdk.assertions.assertEmbraceSpanData +import io.embrace.android.embracesdk.assertions.findSpansByName +import io.embrace.android.embracesdk.assertions.findSpansOfType +import io.embrace.android.embracesdk.fakes.config.FakeEnabledFeatureConfig +import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig +import io.embrace.android.embracesdk.internal.arch.schema.EmbType +import io.embrace.android.embracesdk.internal.capture.activity.UiLoadTraceEmitter.LifecycleEvent +import io.embrace.android.embracesdk.internal.payload.ApplicationState +import io.embrace.android.embracesdk.testframework.IntegrationTestRule +import io.opentelemetry.api.trace.SpanId +import org.junit.Assert.assertEquals +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.annotation.Config + +@RunWith(AndroidJUnit4::class) +internal class UiLoadTest { + + @Rule + @JvmField + val testRule = IntegrationTestRule() + + @Config(sdk = [Build.VERSION_CODES.LOLLIPOP]) + @Test + fun `activity open does not create a trace if feature flag is disabled`() { + testRule.runTest( + instrumentedConfig = FakeInstrumentedConfig( + enabledFeatures = FakeEnabledFeatureConfig(uiLoadPerfCapture = false, bgActivityCapture = true) + ), + testCaseAction = { + simulateOpeningActivities( + addStartupActivity = false, + startInBackground = true, + activitiesAndActions = listOf( + Robolectric.buildActivity(Activity1::class.java) to {}, + Robolectric.buildActivity(Activity2::class.java) to {}, + ) + ) + }, + assertAction = { + val payload = getSingleSessionEnvelope() + assertEquals(0, payload.findSpansOfType(EmbType.Performance.UiLoad).size) + } + ) + } + + @Config(sdk = [Build.VERSION_CODES.LOLLIPOP]) + @Test + fun `opening first non-startup activity creates cold open trace in L`() { + var preLaunchTimeMs = 0L + testRule.runTest( + instrumentedConfig = FakeInstrumentedConfig( + enabledFeatures = FakeEnabledFeatureConfig(uiLoadPerfCapture = true, bgActivityCapture = true) + ), + setupAction = { + preLaunchTimeMs = overriddenClock.now() + }, + testCaseAction = { + simulateOpeningActivities( + addStartupActivity = false, + startInBackground = true, + activitiesAndActions = listOf( + Robolectric.buildActivity(Activity1::class.java) to {}, + Robolectric.buildActivity(Activity2::class.java) to {}, + ) + ) + }, + assertAction = { + val payload = getSingleSessionEnvelope() + val trace = payload.findSpansOfType(EmbType.Performance.UiLoad).single() + val rootSpanId = checkNotNull(trace.spanId) + assertEquals("emb-$ACTIVITY2_NAME-cold-time-to-initial-display", trace.name) + + val expectedTraceStartTime = preLaunchTimeMs + 20301 + assertEmbraceSpanData( + span = trace, + expectedStartTimeMs = expectedTraceStartTime, + expectedEndTimeMs = expectedTraceStartTime + 250, + expectedParentId = SpanId.getInvalid(), + key = true + ) + + assertEmbraceSpanData( + span = payload.findSpansByName("emb-${LifecycleEvent.CREATE.spanName(ACTIVITY2_NAME)}").single(), + expectedStartTimeMs = expectedTraceStartTime + 50, + expectedEndTimeMs = expectedTraceStartTime + 150, + expectedParentId = rootSpanId, + ) + + assertEmbraceSpanData( + span = payload.findSpansByName("emb-${LifecycleEvent.START.spanName(ACTIVITY2_NAME)}").single(), + expectedStartTimeMs = expectedTraceStartTime + 150, + expectedEndTimeMs = expectedTraceStartTime + 250, + expectedParentId = rootSpanId, + ) + } + ) + } + + @Config(sdk = [Build.VERSION_CODES.LOLLIPOP]) + @Test + fun `foregrounding and initializing new activity creates cold open trace in L`() { + var preLaunchTimeMs = 0L + testRule.runTest( + instrumentedConfig = FakeInstrumentedConfig( + enabledFeatures = FakeEnabledFeatureConfig(uiLoadPerfCapture = true, bgActivityCapture = true) + ), + setupAction = { + preLaunchTimeMs = overriddenClock.now() + }, + testCaseAction = { + simulateOpeningActivities( + addStartupActivity = true, + startInBackground = true, + activitiesAndActions = listOf( + Robolectric.buildActivity(Activity1::class.java) to {} + ) + ) + }, + assertAction = { + val payloads = getSessionEnvelopes(2) + val payload = payloads[1] + val trace = payload.findSpansOfType(EmbType.Performance.UiLoad).single() + assertEquals("emb-$ACTIVITY1_NAME-cold-time-to-initial-display", trace.name) + val rootSpanId = checkNotNull(trace.spanId) + + val expectedTraceStartTime = preLaunchTimeMs + 10000 + assertEmbraceSpanData( + span = trace, + expectedStartTimeMs = expectedTraceStartTime, + expectedEndTimeMs = expectedTraceStartTime + 200, + expectedParentId = SpanId.getInvalid(), + key = true + ) + + val lastBackgroundActivity = getSessionEnvelopes(2, ApplicationState.BACKGROUND)[1] + assertEmbraceSpanData( + span = lastBackgroundActivity + .findSpansByName("emb-${LifecycleEvent.CREATE.spanName(ACTIVITY1_NAME)}") + .single(), + expectedStartTimeMs = expectedTraceStartTime, + expectedEndTimeMs = expectedTraceStartTime + 100, + expectedParentId = rootSpanId, + ) + + assertEmbraceSpanData( + span = payload.findSpansByName("emb-${LifecycleEvent.START.spanName(ACTIVITY1_NAME)}").single(), + expectedStartTimeMs = expectedTraceStartTime + 100, + expectedEndTimeMs = expectedTraceStartTime + 200, + expectedParentId = rootSpanId, + ) + } + ) + } + + @Config(sdk = [Build.VERSION_CODES.LOLLIPOP]) + @Test + fun `foregrounding already-created activity creates hot open trace in L`() { + var preLaunchTimeMs = 0L + testRule.runTest( + instrumentedConfig = FakeInstrumentedConfig( + enabledFeatures = FakeEnabledFeatureConfig(uiLoadPerfCapture = true, bgActivityCapture = true) + ), + setupAction = { + preLaunchTimeMs = overriddenClock.now() + }, + testCaseAction = { + simulateOpeningActivities( + addStartupActivity = true, + startInBackground = true, + createFirstActivity = false, + activitiesAndActions = listOf( + Robolectric.buildActivity(Activity1::class.java) to {}, + ) + ) + }, + assertAction = { + val payload = getSessionEnvelopes(2)[1] + val trace = payload.findSpansOfType(EmbType.Performance.UiLoad).single() + assertEquals("emb-$ACTIVITY1_NAME-hot-time-to-initial-display", trace.name) + val rootSpanId = checkNotNull(trace.spanId) + + val expectedTraceStartTime = preLaunchTimeMs + 10000 + assertEmbraceSpanData( + span = trace, + expectedStartTimeMs = expectedTraceStartTime, + expectedEndTimeMs = expectedTraceStartTime + 100, + expectedParentId = SpanId.getInvalid(), + key = true + ) + + assertEmbraceSpanData( + span = payload.findSpansByName("emb-${LifecycleEvent.START.spanName(ACTIVITY1_NAME)}").single(), + expectedStartTimeMs = expectedTraceStartTime, + expectedEndTimeMs = expectedTraceStartTime + 100, + expectedParentId = rootSpanId, + ) + } + ) + } + + private companion object { + @ObservedActivity + class Activity1 : Activity() + + @ObservedActivity + class Activity2 : Activity() + + val ACTIVITY1_NAME = Robolectric.buildActivity(Activity1::class.java).get().localClassName + val ACTIVITY2_NAME = Robolectric.buildActivity(Activity2::class.java).get().localClassName + } +} diff --git a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceActionInterface.kt b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceActionInterface.kt index 58920fd1de..4228dccdd5 100644 --- a/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceActionInterface.kt +++ b/embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testframework/actions/EmbraceActionInterface.kt @@ -9,6 +9,7 @@ import io.embrace.android.embracesdk.fakes.FakeNetworkConnectivityService import io.embrace.android.embracesdk.internal.comms.delivery.NetworkStatus import io.embrace.android.embracesdk.internal.injection.ModuleInitBootstrapper import org.robolectric.Robolectric +import org.robolectric.android.controller.ActivityController /** * Interface for performing actions on the [Embrace] instance under test @@ -60,6 +61,61 @@ internal class EmbraceActionInterface( service.networkStatus = status } + internal fun simulateOpeningActivities( + addStartupActivity: Boolean = true, + startInBackground: Boolean = false, + createFirstActivity: Boolean = true, + activitiesAndActions: List, () -> Unit>> = listOf(), + lifecycleEventGap: Long = 100L, + postActionDwell: Long = 20000L, + activityGap: Long = 50L, + ) { + var lastActivity: ActivityController<*>? = if (addStartupActivity) { + Robolectric.buildActivity(Activity::class.java) + } else { + null + }?.apply { + create() + start() + onForeground() + resume() + pause() + if (startInBackground) { + stop() + onBackground() + setup.overriddenClock.tick(10000L) + } else { + setup.overriddenClock.tick(activityGap) + } + } + activitiesAndActions.forEachIndexed { index, (activityController, action) -> + if (index != 0 || createFirstActivity) { + activityController.create() + setup.overriddenClock.tick(lifecycleEventGap) + } + activityController.start() + setup.overriddenClock.tick(lifecycleEventGap) + if (index == 0 && startInBackground) { + onForeground() + } + activityController.resume() + setup.overriddenClock.tick(lifecycleEventGap) + lastActivity?.stop() + setup.overriddenClock.tick() + + action() + + setup.overriddenClock.tick(postActionDwell) + activityController.pause() + setup.overriddenClock.tick(activityGap) + lastActivity = activityController + } + lastActivity?.stop() + setup.overriddenClock.tick() + onBackground() + } + + fun simulateActivityLifecycle() { with(Robolectric.buildActivity(Activity::class.java)) { create() diff --git a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt index 0556a42b83..fe606615e4 100644 --- a/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt +++ b/embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/internal/injection/ModuleInitBootstrapper.kt @@ -272,7 +272,8 @@ internal class ModuleInitBootstrapper( serviceRegistry.registerServices( lazy { dataCaptureServiceModule.webviewService }, lazy { dataCaptureServiceModule.activityBreadcrumbTracker }, - lazy { dataCaptureServiceModule.pushNotificationService } + lazy { dataCaptureServiceModule.pushNotificationService }, + lazy { dataCaptureServiceModule.uiLoadEvents }, ) } diff --git a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/injection/FakeDataCaptureServiceModule.kt b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/injection/FakeDataCaptureServiceModule.kt index 9971624e9d..97ae7fe759 100644 --- a/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/injection/FakeDataCaptureServiceModule.kt +++ b/embrace-android-sdk/src/test/java/io/embrace/android/embracesdk/fakes/injection/FakeDataCaptureServiceModule.kt @@ -1,9 +1,14 @@ package io.embrace.android.embracesdk.fakes.injection +import io.embrace.android.embracesdk.fakes.FakeAppStartupDataCollector +import io.embrace.android.embracesdk.fakes.FakeClock import io.embrace.android.embracesdk.fakes.FakeConfigService import io.embrace.android.embracesdk.fakes.FakeFeatureModule import io.embrace.android.embracesdk.fakes.FakeStartupService +import io.embrace.android.embracesdk.fakes.FakeUiLoadEvents import io.embrace.android.embracesdk.fakes.FakeWebViewService +import io.embrace.android.embracesdk.internal.capture.activity.UiLoadEventEmitter +import io.embrace.android.embracesdk.internal.capture.activity.UiLoadEvents import io.embrace.android.embracesdk.internal.capture.crumbs.ActivityBreadcrumbTracker import io.embrace.android.embracesdk.internal.capture.crumbs.PushNotificationCaptureService import io.embrace.android.embracesdk.internal.capture.startup.AppStartupDataCollector @@ -24,7 +29,11 @@ internal class FakeDataCaptureServiceModule( override val startupService: StartupService = FakeStartupService() + override val appStartupDataCollector: AppStartupDataCollector = FakeAppStartupDataCollector(FakeClock()) + override val startupTracker: StartupTracker = mockk(relaxed = true) - override val appStartupDataCollector: AppStartupDataCollector = mockk(relaxed = true) + override val uiLoadEvents: UiLoadEvents = FakeUiLoadEvents() + + override val uiLoadEventEmitter: UiLoadEventEmitter = mockk(relaxed = true) } diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeUiLoadEvents.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeUiLoadEvents.kt new file mode 100644 index 0000000000..73cee5fd46 --- /dev/null +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/FakeUiLoadEvents.kt @@ -0,0 +1,119 @@ +package io.embrace.android.embracesdk.fakes + +import io.embrace.android.embracesdk.internal.capture.activity.UiLoadEvents + +class FakeUiLoadEvents : UiLoadEvents { + val events = mutableListOf() + + override fun abandon(instanceId: Int, activityName: String, timestampMs: Long) { + events.add( + EventData( + stage = "abandon", + instanceId = instanceId, + activityName = activityName, + timestampMs = timestampMs + ) + ) + } + + override fun reset(instanceId: Int) { + events.add( + EventData( + stage = "reset", + instanceId = instanceId, + ) + ) + } + + override fun create(instanceId: Int, activityName: String, timestampMs: Long) { + events.add( + EventData( + stage = "create", + instanceId = instanceId, + activityName = activityName, + timestampMs = timestampMs + ) + ) + } + + override fun createEnd(instanceId: Int, timestampMs: Long) { + events.add( + EventData( + stage = "createEnd", + instanceId = instanceId, + activityName = null, + timestampMs = timestampMs + ) + ) + } + + override fun start(instanceId: Int, activityName: String, timestampMs: Long) { + events.add( + EventData( + stage = "start", + instanceId = instanceId, + activityName = activityName, + timestampMs = timestampMs + ) + ) + } + + override fun startEnd(instanceId: Int, timestampMs: Long) { + events.add( + EventData( + stage = "startEnd", + instanceId = instanceId, + timestampMs = timestampMs + ) + ) + } + + override fun resume(instanceId: Int, activityName: String, timestampMs: Long) { + events.add( + EventData( + stage = "resume", + instanceId = instanceId, + activityName = activityName, + timestampMs = timestampMs + ) + ) + } + + override fun resumeEnd(instanceId: Int, timestampMs: Long) { + events.add( + EventData( + stage = "resumeEnd", + instanceId = instanceId, + timestampMs = timestampMs + ) + ) + } + + override fun render(instanceId: Int, activityName: String, timestampMs: Long) { + events.add( + EventData( + stage = "render", + instanceId = instanceId, + activityName = activityName, + timestampMs = timestampMs + ) + ) + } + + override fun renderEnd(instanceId: Int, timestampMs: Long) { + events.add( + EventData( + stage = "renderEnd", + instanceId = instanceId, + timestampMs = timestampMs + ) + ) + } + + data class EventData( + val stage: String, + val instanceId: Int, + val activityName: String? = null, + val timestampMs: Long? = null, + ) +} diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeAutoDataCaptureBehavior.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeAutoDataCaptureBehavior.kt index dce1e1830c..9197a18c0d 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeAutoDataCaptureBehavior.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/behavior/FakeAutoDataCaptureBehavior.kt @@ -15,6 +15,7 @@ class FakeAutoDataCaptureBehavior( private val sigHandlerDetectionEnabled: Boolean = true, private val ndkEnabled: Boolean = false, private val diskUsageReportingEnabled: Boolean = true, + private val uiLoadPerfCaptureEnabled: Boolean = false, private val v2StorageEnabled: Boolean = true, private val useOkhttp: Boolean = true, ) : AutoDataCaptureBehavior { @@ -34,6 +35,7 @@ class FakeAutoDataCaptureBehavior( override fun is3rdPartySigHandlerDetectionEnabled(): Boolean = sigHandlerDetectionEnabled override fun isNativeCrashCaptureEnabled(): Boolean = ndkEnabled override fun isDiskUsageCaptureEnabled(): Boolean = diskUsageReportingEnabled + override fun isUiLoadPerfCaptureEnabled(): Boolean = uiLoadPerfCaptureEnabled override fun isV2StorageEnabled(): Boolean = v2StorageEnabled override fun shouldUseOkHttp(): Boolean = useOkhttp } diff --git a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeEnabledFeatureConfig.kt b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeEnabledFeatureConfig.kt index e6e93d59c8..cdde184303 100644 --- a/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeEnabledFeatureConfig.kt +++ b/embrace-test-fakes/src/main/kotlin/io/embrace/android/embracesdk/fakes/config/FakeEnabledFeatureConfig.kt @@ -26,6 +26,7 @@ class FakeEnabledFeatureConfig( private val requestContentLengthCapture: Boolean = base.isRequestContentLengthCaptureEnabled(), private val httpUrlConnectionCapture: Boolean = base.isHttpUrlConnectionCaptureEnabled(), private val networkSpanForwarding: Boolean = base.isNetworkSpanForwardingEnabled(), + private val uiLoadPerfCapture: Boolean = base.isUiLoadPerfCaptureEnabled() ) : EnabledFeatureConfig { override fun isUnityAnrCaptureEnabled(): Boolean = unityAnrCapture @@ -50,4 +51,5 @@ class FakeEnabledFeatureConfig( override fun isRequestContentLengthCaptureEnabled(): Boolean = requestContentLengthCapture override fun isHttpUrlConnectionCaptureEnabled(): Boolean = httpUrlConnectionCapture override fun isNetworkSpanForwardingEnabled(): Boolean = networkSpanForwarding + override fun isUiLoadPerfCaptureEnabled(): Boolean = uiLoadPerfCapture }