Skip to content

Commit 694be09

Browse files
committed
Fake app lifecycle when faking a session during integration tests
1 parent b57caf9 commit 694be09

21 files changed

+337
-252
lines changed

embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/FlutterInternalInterfaceTest.kt

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import io.opentelemetry.api.logs.Severity
1515
import io.opentelemetry.semconv.ExceptionAttributes
1616
import org.junit.Assert.assertEquals
1717
import org.junit.Assert.assertNull
18+
import org.junit.Before
1819
import org.junit.Rule
1920
import org.junit.Test
2021
import org.junit.runner.RunWith
@@ -32,10 +33,17 @@ internal class FlutterInternalInterfaceTest {
3233
)
3334
)
3435

36+
private var sessionStartTimeMs: Long = 0L
37+
3538
@Rule
3639
@JvmField
3740
val testRule: SdkIntegrationTestRule = SdkIntegrationTestRule()
3841

42+
@Before
43+
fun before() {
44+
sessionStartTimeMs = 0L
45+
}
46+
3947
@Test
4048
fun `flutter without values should return defaults`() {
4149
testRule.runTest(
@@ -155,15 +163,15 @@ internal class FlutterInternalInterfaceTest {
155163
testRule.runTest(
156164
instrumentedConfig = instrumentedConfig,
157165
testCaseAction = {
158-
recordSession {
166+
sessionStartTimeMs = recordSession {
159167
EmbraceInternalApi.getInstance().flutterInternalInterface.logHandledDartException(
160168
expectedStacktrace,
161169
expectedName,
162170
expectedMessage,
163171
expectedContext,
164172
expectedLibrary,
165173
)
166-
}
174+
}.firstActionTimeMs
167175
},
168176
assertAction = {
169177
val log = getSingleLogEnvelope().getLogOfType(EmbType.System.FlutterException)
@@ -173,6 +181,7 @@ internal class FlutterInternalInterfaceTest {
173181
expectedMessage = "Dart error",
174182
expectedSeverityNumber = Severity.ERROR.severityNumber,
175183
expectedSeverityText = Severity.ERROR.name,
184+
expectedTimeMs = sessionStartTimeMs,
176185
expectedType = LogExceptionType.HANDLED.value,
177186
expectedExceptionName = expectedName,
178187
expectedExceptionMessage = expectedMessage,
@@ -201,15 +210,15 @@ internal class FlutterInternalInterfaceTest {
201210
testRule.runTest(
202211
instrumentedConfig = instrumentedConfig,
203212
testCaseAction = {
204-
recordSession {
213+
sessionStartTimeMs = recordSession {
205214
EmbraceInternalApi.getInstance().flutterInternalInterface.logUnhandledDartException(
206215
expectedStacktrace,
207216
expectedName,
208217
expectedMessage,
209218
expectedContext,
210219
expectedLibrary,
211220
)
212-
}
221+
}.firstActionTimeMs
213222
},
214223
assertAction = {
215224
val log = getSingleLogEnvelope().getLogOfType(EmbType.System.FlutterException)
@@ -219,6 +228,7 @@ internal class FlutterInternalInterfaceTest {
219228
expectedMessage = "Dart error",
220229
expectedSeverityNumber = Severity.ERROR.severityNumber,
221230
expectedSeverityText = Severity.ERROR.name,
231+
expectedTimeMs = sessionStartTimeMs,
222232
expectedType = LogExceptionType.UNHANDLED.value,
223233
expectedExceptionName = expectedName,
224234
expectedExceptionMessage = expectedMessage,

embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/ReactNativeInternalInterfaceTest.kt

Lines changed: 23 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
package io.embrace.android.embracesdk.testcases
22

33
import androidx.test.ext.junit.runners.AndroidJUnit4
4-
import io.embrace.android.embracesdk.assertions.findSpanOfType
5-
import io.embrace.android.embracesdk.assertions.findSpanSnapshotOfType
64
import io.embrace.android.embracesdk.assertions.findSpansByName
5+
import io.embrace.android.embracesdk.assertions.findSpansOfType
76
import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig
87
import io.embrace.android.embracesdk.fakes.config.FakeProjectConfig
98
import io.embrace.android.embracesdk.internal.EmbraceInternalApi
109
import io.embrace.android.embracesdk.internal.arch.schema.EmbType
1110
import io.embrace.android.embracesdk.internal.arch.schema.toSessionPropertyAttributeName
1211
import io.embrace.android.embracesdk.internal.clock.nanosToMillis
1312
import io.embrace.android.embracesdk.internal.payload.AppFramework
13+
import io.embrace.android.embracesdk.internal.spans.findAttributeValue
1414
import io.embrace.android.embracesdk.testframework.SdkIntegrationTestRule
1515
import io.embrace.android.embracesdk.testframework.assertions.assertMatches
1616
import org.junit.Assert.assertEquals
17+
import org.junit.Assert.assertNotNull
1718
import org.junit.Assert.assertNull
1819
import org.junit.Rule
1920
import org.junit.Test
@@ -25,10 +26,12 @@ import org.junit.runner.RunWith
2526
@RunWith(AndroidJUnit4::class)
2627
internal class ReactNativeInternalInterfaceTest {
2728

28-
private val instrumentedConfig = FakeInstrumentedConfig(project = FakeProjectConfig(
29-
appId = "abcde",
30-
appFramework = "react_native"
31-
))
29+
private val instrumentedConfig = FakeInstrumentedConfig(
30+
project = FakeProjectConfig(
31+
appId = "abcde",
32+
appFramework = "react_native"
33+
)
34+
)
3235

3336
@Rule
3437
@JvmField
@@ -152,21 +155,19 @@ internal class ReactNativeInternalInterfaceTest {
152155
assertEquals(1000L, span.startTimeNanos?.nanosToMillis())
153156
assertEquals(5000L, span.endTimeNanos?.nanosToMillis())
154157

155-
span.attributes?.assertMatches(mapOf(
156-
"emb.type" to "sys.rn_action",
157-
"name" to "MyAction",
158-
"outcome" to "SUCCESS",
159-
"payload_size" to "100",
160-
"key".toSessionPropertyAttributeName() to "value",
161-
))
158+
span.attributes?.assertMatches(
159+
mapOf(
160+
"emb.type" to "sys.rn_action",
161+
"name" to "MyAction",
162+
"outcome" to "SUCCESS",
163+
"payload_size" to "100",
164+
"key".toSessionPropertyAttributeName() to "value",
165+
)
166+
)
162167
}
163168
)
164169
}
165170

166-
/*
167-
* The first view is logged and stored as a span, because we know that it ends when logRnView is called again.
168-
* The second view is logged as a span snapshot, because we know that it ends when the session ends.
169-
* */
170171
@Test
171172
fun `react native log RN view`() {
172173
testRule.runTest(
@@ -179,21 +180,9 @@ internal class ReactNativeInternalInterfaceTest {
179180
}
180181
},
181182
assertAction = {
182-
val message = getSingleSessionEnvelope()
183-
val firstSpan = message.findSpanOfType(EmbType.Ux.View)
184-
val secondSpan = message.findSpanSnapshotOfType(EmbType.Ux.View)
185-
186-
assertEquals("emb-screen-view", firstSpan.name)
187-
firstSpan.attributes?.assertMatches(mapOf(
188-
"emb.type" to "ux.view",
189-
"view.name" to "HomeScreen",
190-
))
191-
192-
assertEquals("emb-screen-view", secondSpan.name)
193-
secondSpan.attributes?.assertMatches(mapOf(
194-
"emb.type" to "ux.view",
195-
"view.name" to "DetailsScreen",
196-
))
183+
val viewSpans = getSingleSessionEnvelope().findSpansOfType(EmbType.Ux.View)
184+
assertNotNull(viewSpans.single { it.attributes?.findAttributeValue("view.name") == "HomeScreen" })
185+
assertNotNull(viewSpans.single { it.attributes?.findAttributeValue("view.name") == "DetailsScreen" })
197186
}
198187
)
199188
}
@@ -214,20 +203,8 @@ internal class ReactNativeInternalInterfaceTest {
214203
}
215204
},
216205
assertAction = {
217-
val message = getSingleSessionEnvelope()
218-
val firstSpan = message.findSpanOfType(EmbType.Ux.View)
219-
assertEquals("emb-screen-view", firstSpan.name)
220-
firstSpan.attributes?.assertMatches(mapOf(
221-
"emb.type" to "ux.view",
222-
"view.name" to "HomeScreen",
223-
))
224-
225-
val secondSpan = message.findSpanSnapshotOfType(EmbType.Ux.View)
226-
assertEquals("emb-screen-view", secondSpan.name)
227-
secondSpan.attributes?.assertMatches(mapOf(
228-
"emb.type" to "ux.view",
229-
"view.name" to "HomeScreen",
230-
))
206+
val viewSpans = getSingleSessionEnvelope().findSpansOfType(EmbType.Ux.View)
207+
assertEquals(2, viewSpans.filter { it.attributes?.findAttributeValue("view.name") == "HomeScreen" }.size)
231208
}
232209
)
233210
}

embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/SessionApiTest.kt

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
44
import io.embrace.android.embracesdk.ResourceReader
55
import io.embrace.android.embracesdk.fakes.config.FakeEnabledFeatureConfig
66
import io.embrace.android.embracesdk.fakes.config.FakeInstrumentedConfig
7-
import io.embrace.android.embracesdk.internal.clock.millisToNanos
87
import io.embrace.android.embracesdk.internal.clock.nanosToMillis
98
import io.embrace.android.embracesdk.internal.opentelemetry.embFreeDiskBytes
109
import io.embrace.android.embracesdk.internal.spans.findAttributeValue
1110
import io.embrace.android.embracesdk.testframework.SdkIntegrationTestRule
12-
import io.embrace.android.embracesdk.testframework.assertions.assertMatches
1311
import io.embrace.android.embracesdk.testframework.assertions.toMap
1412
import io.opentelemetry.semconv.incubating.SessionIncubatingAttributes
1513
import org.junit.Assert.assertEquals
@@ -36,16 +34,15 @@ internal class SessionApiTest {
3634
testRule.runTest(
3735
instrumentedConfig = FakeInstrumentedConfig(enabledFeatures = FakeEnabledFeatureConfig(diskUsageCapture = false, bgActivityCapture = true)),
3836
testCaseAction = {
39-
startTime = clock.now()
40-
recordSession {
37+
startTime = recordSession {
4138
embrace.setUserIdentifier("some id")
4239
embrace.setUserEmail("user@email.com")
4340
embrace.setUsername("John Doe")
4441

4542
// add webview information
4643
val msg = ResourceReader.readResourceAsText("expected-webview-core-vital.json")
4744
embrace.trackWebViewPerformance("myWebView", msg)
48-
}
45+
}.firstForegroundTimeMs
4946
},
5047
assertAction = {
5148
val message = getSingleSessionEnvelope()
@@ -55,12 +52,8 @@ internal class SessionApiTest {
5552
val snapshots = checkNotNull(message.data.spanSnapshots)
5653
assertEquals(1, snapshots.size)
5754

58-
// validate network status span
59-
val networkStatusSpan = snapshots.single { it.name == "emb-network-status" }
60-
assertEquals(startTime, networkStatusSpan.startTimeNanos?.nanosToMillis())
61-
networkStatusSpan.attributes?.assertMatches(mapOf(
62-
"emb.type" to "sys.network_status",
63-
))
55+
// validate expected in-process spans
56+
checkNotNull(snapshots.single { it.name == "emb-network-status" })
6457

6558
// validate session span
6659
val spans = checkNotNull(message.data.spans)
@@ -85,14 +78,13 @@ internal class SessionApiTest {
8578
"emb.session_start_type" to "state",
8679
"emb.terminated" to "false",
8780
"emb.session_end_type" to "state",
88-
"emb.heartbeat_time_unix_nano" to "${startTime.millisToNanos()}",
8981
"emb.session_number" to "1",
9082
"emb.type" to "ux.session",
9183
"emb.error_log_count" to "0",
9284
"emb.usage.set_username" to "1",
9385
"emb.usage.set_user_email" to "1",
9486
"emb.usage.set_user_identifier" to "1",
95-
"emb.private.sequence_id" to "4",
87+
"emb.private.sequence_id" to "5",
9688
"emb.startup_duration" to "0"
9789
).toSortedMap()
9890

@@ -110,6 +102,7 @@ internal class SessionApiTest {
110102
"emb.process_identifier",
111103
"emb.is_emulator",
112104
"emb.okhttp3_on_classpath",
105+
"emb.heartbeat_time_unix_nano",
113106
)
114107

115108
// Attributes that are unstable that we should not try to verify

0 commit comments

Comments
 (0)