Skip to content

Commit

Permalink
Refactor the SessionOrchestrator tests to use a real PayloadFactory (#…
Browse files Browse the repository at this point in the history
…854)

## Goal

There was too much logic in PayloadFactoryImpl for us to faithfully test SessionOrchestratorImpl without a really complex FakePayloadFactory. As such, we'll use a real one, and do the faking at the `PayloadMessageCollator` level so we can validate the whole `SessionMessage` to ensure they match.
  • Loading branch information
bidetofevil committed May 17, 2024
2 parents 3f97297 + fb2dad9 commit a3ee76a
Show file tree
Hide file tree
Showing 11 changed files with 376 additions and 180 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import io.embrace.android.embracesdk.session.lifecycle.ProcessState
import io.embrace.android.embracesdk.session.orchestrator.SessionSnapshotType

internal class PayloadFactoryImpl(
private val v1payloadMessageCollator: V1PayloadMessageCollator,
private val v2payloadMessageCollator: V2PayloadMessageCollator,
private val v1payloadMessageCollator: PayloadMessageCollator,
private val v2payloadMessageCollator: PayloadMessageCollator,
private val configService: ConfigService,
private val logger: EmbLogger
) : PayloadFactory {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import io.embrace.android.embracesdk.internal.utils.Provider
import io.embrace.android.embracesdk.ndk.NativeCrashService
import io.embrace.android.embracesdk.payload.EventMessage
import io.embrace.android.embracesdk.payload.NetworkEvent
import io.embrace.android.embracesdk.payload.Session
import io.embrace.android.embracesdk.payload.SessionMessage
import io.embrace.android.embracesdk.session.id.SessionIdTracker
import io.embrace.android.embracesdk.session.orchestrator.SessionSnapshotType
Expand All @@ -27,13 +28,13 @@ internal open class FakeDeliveryService : DeliveryService {
var lastSavedCrash: EventMessage? = null
var lastSentCachedSession: String? = null
val sentSessionMessages: MutableList<Pair<SessionMessage, SessionSnapshotType>> = mutableListOf()
val lastSavedSessionMessages: MutableList<Pair<SessionMessage, SessionSnapshotType>> = mutableListOf()
val savedSessionMessages: MutableList<Pair<SessionMessage, SessionSnapshotType>> = mutableListOf()

override fun sendSession(sessionMessage: SessionMessage, snapshotType: SessionSnapshotType) {
if (snapshotType != SessionSnapshotType.PERIODIC_CACHE) {
sentSessionMessages.add(sessionMessage to snapshotType)
}
lastSavedSessionMessages.add(sessionMessage to snapshotType)
savedSessionMessages.add(sessionMessage to snapshotType)
}

override fun sendCachedSessions(
Expand Down Expand Up @@ -71,18 +72,38 @@ internal open class FakeDeliveryService : DeliveryService {
}

fun getSentSessions(): List<SessionMessage> {
return sentSessionMessages.map { it.first }.filter { it.session.appState == "foreground" }
return sentSessionMessages.filter { it.first.session.appState == Session.APPLICATION_STATE_FOREGROUND }.map { it.first }
}

fun getSentBackgroundActivities(): List<SessionMessage> {
return sentSessionMessages.map { it.first }.filter { it.session.appState == "background" }
return sentSessionMessages.filter { it.first.session.appState == Session.APPLICATION_STATE_BACKGROUND }.map { it.first }
}

fun getLastSavedSession(): SessionMessage? {
return lastSavedSessionMessages.map { it.first }.lastOrNull { it.session.appState == "foreground" }
fun getSavedSessions(): List<SessionMessage> {
return savedSessionMessages.filter {
it.first.session.appState == Session.APPLICATION_STATE_FOREGROUND
}.map { it.first }
}

fun getSavedBackgroundActivities(): List<SessionMessage> {
return savedSessionMessages.filter {
it.first.session.appState == Session.APPLICATION_STATE_BACKGROUND
}.map { it.first }
}

fun getLastSentSession(): SessionMessage? {
return getSentSessions().lastOrNull()
}

fun getLastSentBackgroundActivity(): SessionMessage? {
return getSentBackgroundActivities().lastOrNull()
}

fun getLastSavedSession(): SessionMessage? {
return getSavedSessions().lastOrNull()
}

fun getLastSavedBackgroundActivity(): SessionMessage? {
return getSavedBackgroundActivities().lastOrNull()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ internal class FakePayloadFactory : PayloadFactory {
initial: Session
): SessionMessage? {
return when (state) {
ProcessState.FOREGROUND -> snapshotSession(initial)
ProcessState.BACKGROUND -> snapshotBackgroundActivity(initial)
ProcessState.FOREGROUND -> snapshotSession()
ProcessState.BACKGROUND -> snapshotBackgroundActivity()
}
}

Expand All @@ -82,9 +82,9 @@ internal class FakePayloadFactory : PayloadFactory {
return fakeBackgroundActivityMessage()
}

private fun snapshotBackgroundActivity(initial: Session): SessionMessage {
private fun snapshotBackgroundActivity(): SessionMessage {
snapshotBaCount++
return fakeBackgroundActivityMessage().copy(session = initial)
return fakeBackgroundActivityMessage()
}

private fun startSessionWithState(timestamp: Long): Session {
Expand Down Expand Up @@ -119,8 +119,8 @@ internal class FakePayloadFactory : PayloadFactory {
return fakeV1SessionMessage()
}

private fun snapshotSession(initial: Session): SessionMessage {
private fun snapshotSession(): SessionMessage? {
snapshotSessionCount++
return fakeV1SessionMessage().copy(session = initial)
return null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import io.embrace.android.embracesdk.fakes.FakeNativeThreadSamplerService
import io.embrace.android.embracesdk.fakes.FakePersistableEmbraceSpan
import io.embrace.android.embracesdk.fakes.FakeSpanData
import io.embrace.android.embracesdk.internal.payload.SessionPayload
import io.embrace.android.embracesdk.internal.spans.EmbraceSpanData
import io.embrace.android.embracesdk.internal.spans.SpanRepository
import io.embrace.android.embracesdk.internal.spans.SpanSinkImpl
import io.embrace.android.embracesdk.logging.EmbLoggerImpl
import io.embrace.android.embracesdk.payload.LegacyExceptionError
import io.embrace.android.embracesdk.session.orchestrator.SessionSnapshotType
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Test

Expand All @@ -38,7 +38,7 @@ internal class SessionPayloadSourceImplTest {
storeCompletedSpans(listOf(cacheSpan))
}
currentSessionSpan = FakeCurrentSessionSpan().apply {
spanData = listOf(EmbraceSpanData(FakeSpanData("my-span")))
initializeService(1000L)
}
activeSpan = FakePersistableEmbraceSpan.started()
spanRepository = SpanRepository()
Expand All @@ -57,8 +57,7 @@ internal class SessionPayloadSourceImplTest {
fun `session crash`() {
val payload = impl.getSessionPayload(SessionSnapshotType.JVM_CRASH)
assertPayloadPopulated(payload)
val span = checkNotNull(payload.spans?.single())
assertEquals("my-span", span.name)
assertNotNull(payload.spans?.single())
}

@Test
Expand All @@ -73,8 +72,7 @@ internal class SessionPayloadSourceImplTest {
fun `session lifecycle change`() {
val payload = impl.getSessionPayload(SessionSnapshotType.NORMAL_END)
assertPayloadPopulated(payload)
val span = checkNotNull(payload.spans?.single())
assertEquals("my-span", span.name)
assertNotNull(payload.spans?.single())
}

private fun assertPayloadPopulated(payload: SessionPayload) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,25 @@ package io.embrace.android.embracesdk.fakes
import io.embrace.android.embracesdk.arch.destination.SpanAttributeData
import io.embrace.android.embracesdk.arch.destination.SpanEventData
import io.embrace.android.embracesdk.arch.schema.AppTerminationCause
import io.embrace.android.embracesdk.internal.clock.millisToNanos
import io.embrace.android.embracesdk.internal.spans.CurrentSessionSpan
import io.embrace.android.embracesdk.internal.spans.EmbraceSpanData
import io.embrace.android.embracesdk.spans.EmbraceSpan
import io.opentelemetry.sdk.trace.data.StatusData
import java.util.concurrent.atomic.AtomicInteger

internal class FakeCurrentSessionSpan : CurrentSessionSpan {

internal class FakeCurrentSessionSpan(
private val clock: FakeClock = FakeClock()
) : CurrentSessionSpan {
var initializedCallCount = 0
var addedEvents = mutableListOf<SpanEventData>()
var spanData = listOf<EmbraceSpanData>()
var addedAttributes = mutableListOf<SpanAttributeData>()
var sessionSpan: FakeSpanData? = null

private val sessionIteration = AtomicInteger(1)

override fun initializeService(sdkInitStartTimeMs: Long) {
sessionSpan = newSessionSpan(sdkInitStartTimeMs)
}

override fun <T> addEvent(obj: T, mapper: T.() -> SpanEventData): Boolean {
Expand All @@ -35,18 +42,29 @@ internal class FakeCurrentSessionSpan : CurrentSessionSpan {
}

override fun endSession(appTerminationCause: AppTerminationCause?): List<EmbraceSpanData> {
return spanData
val endingSessionSpan = checkNotNull(sessionSpan)
endingSessionSpan.endTimeNanos = clock.nowInNanos()
endingSessionSpan.spanStatus = if (appTerminationCause == null) StatusData.ok() else StatusData.error()
sessionIteration.incrementAndGet()
sessionSpan = if (appTerminationCause == null) newSessionSpan(clock.now()) else null
return listOf(EmbraceSpanData((endingSessionSpan)))
}

override fun canStartNewSpan(parent: EmbraceSpan?, internal: Boolean): Boolean {
return true
}

override fun getSessionId(): String {
return "testSessionId"
return "testSessionId$sessionIteration"
}

fun getAttribute(key: String): String? = addedAttributes.lastOrNull { it.key == key }?.value

fun attributeCount(): Int = addedAttributes.size

private fun newSessionSpan(startTimeMs: Long) =
FakeSpanData(
startEpochNanos = startTimeMs.millisToNanos(),
name = "fake-session-span${getSessionId()}"
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.embrace.android.embracesdk.fakes

import io.embrace.android.embracesdk.payload.Session
import io.embrace.android.embracesdk.payload.SessionMessage
import io.embrace.android.embracesdk.session.message.FinalEnvelopeParams
import io.embrace.android.embracesdk.session.message.InitialEnvelopeParams
import io.embrace.android.embracesdk.session.message.PayloadMessageCollator

internal class FakePayloadCollator : PayloadMessageCollator {
override fun buildInitialSession(params: InitialEnvelopeParams): Session {
TODO("Not yet implemented")
}

override fun buildFinalSessionMessage(params: FinalEnvelopeParams.SessionParams): SessionMessage {
TODO("Not yet implemented")
}

override fun buildFinalBackgroundActivityMessage(params: FinalEnvelopeParams.BackgroundActivityParams): SessionMessage {
TODO("Not yet implemented")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import io.embrace.android.embracesdk.internal.payload.EnvelopeMetadata
import io.embrace.android.embracesdk.internal.payload.EnvelopeResource
import io.embrace.android.embracesdk.internal.payload.SessionPayload
import io.embrace.android.embracesdk.internal.payload.toOldPayload
import io.embrace.android.embracesdk.internal.spans.EmbraceSpanData
import io.embrace.android.embracesdk.payload.Session
import io.embrace.android.embracesdk.payload.Session.Companion.APPLICATION_STATE_FOREGROUND
import io.embrace.android.embracesdk.payload.SessionMessage
Expand All @@ -25,14 +26,18 @@ internal fun fakeSession(
messageType = Session.MESSAGE_TYPE_END
)

internal fun fakeV1SessionMessage(): SessionMessage = SessionMessage(
session = fakeSession()
internal fun fakeV1SessionMessage(session: Session = fakeSession()): SessionMessage = SessionMessage(
session = session
)

internal fun fakeV1EndedSessionMessage(): SessionMessage = SessionMessage(
session = fakeSession().copy(endTime = 160000500000L),
spans = listOfNotNull(testSpan),
spanSnapshots = listOfNotNull(),
internal fun fakeV1EndedSessionMessage(
session: Session = fakeSession(),
spans: List<EmbraceSpanData> = listOfNotNull(testSpan),
spanSnapshots: List<EmbraceSpanData> = listOfNotNull(),
): SessionMessage = SessionMessage(
session = session.copy(endTime = 160000500000L),
spans = spans,
spanSnapshots = spanSnapshots,
)

internal fun fakeV1EndedSessionMessageWithSnapshot(): SessionMessage = SessionMessage(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ internal class FakeSpanData(
private var kind: SpanKind = SpanKind.INTERNAL,
private var spanContext: SpanContext = newTraceRootContext(),
private var parentSpanContext: SpanContext = SpanContext.getInvalid(),
private var status: StatusData = StatusData.unset(),
private var startEpochNanos: Long = DEFAULT_START_TIME_MS.millisToNanos(),
private var attributes: Attributes =
Attributes.builder().fromMap(
Expand All @@ -44,21 +43,21 @@ internal class FakeSpanData(
)
),
private var links: MutableList<LinkData> = mutableListOf(),
private var endEpochNanos: Long = 0L,
private var hasEnded: Boolean = false,
private var resource: Resource = Resource.empty()
private var resource: Resource = Resource.empty(),
var spanStatus: StatusData = StatusData.unset(),
var endTimeNanos: Long = 0L
) : SpanData {
override fun getName(): String = name
override fun getKind(): SpanKind = kind
override fun getSpanContext(): SpanContext = spanContext
override fun getParentSpanContext(): SpanContext = parentSpanContext
override fun getStatus(): StatusData = status
override fun getStatus(): StatusData = spanStatus
override fun getStartEpochNanos(): Long = startEpochNanos
override fun getAttributes(): Attributes = attributes
override fun getEvents(): MutableList<EventData> = events
override fun getLinks(): MutableList<LinkData> = links
override fun getEndEpochNanos(): Long = endEpochNanos
override fun hasEnded(): Boolean = hasEnded
override fun getEndEpochNanos(): Long = endTimeNanos
override fun hasEnded(): Boolean = status != StatusData.unset()
override fun getTotalRecordedEvents(): Int = events.size
override fun getTotalRecordedLinks(): Int = links.size
override fun getTotalAttributeCount(): Int = attributes.size()
Expand All @@ -73,8 +72,8 @@ internal class FakeSpanData(
val perfSpanCompleted =
FakeSpanData(
name = "completed-perf-span",
status = StatusData.ok(),
endEpochNanos = (DEFAULT_START_TIME_MS + 60000L).millisToNanos()
spanStatus = StatusData.ok(),
endTimeNanos = (DEFAULT_START_TIME_MS + 60000L).millisToNanos()
)

private fun newTraceRootContext() = SpanContext.create(
Expand Down

0 comments on commit a3ee76a

Please sign in to comment.