Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RUMM-2497 SnapshotProcessor - do not write anything if there was no mutation detected #1038

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ tasks.register("buildNdkIntegrationTestsArtifacts") {
dependsOn(":instrumented:integration:assembleDebug")
}

nightlyTestsCoverageConfig(threshold = 0.88f)
nightlyTestsCoverageConfig(threshold = 0.87f)
kover {
isDisabled = false
disabledProjects = setOf(
Expand Down
1 change: 1 addition & 0 deletions detekt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,7 @@ datadog:
- "kotlin.collections.List.forEach(kotlin.Function1)"
- "kotlin.collections.List.getOrNull(kotlin.Int)"
- "kotlin.collections.List.isEmpty()"
- "kotlin.collections.List.isNotEmpty()"
- "kotlin.collections.List.joinToString(kotlin.CharSequence, kotlin.CharSequence, kotlin.CharSequence, kotlin.Int, kotlin.CharSequence, kotlin.Function1?)"
- "kotlin.collections.List.lastOrNull()"
- "kotlin.collections.List.lastOrNull(kotlin.Function1)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal class MutationResolver {
fun resolveMutations(
prevSnapshot: List<MobileSegment.Wireframe>,
currentSnapshot: List<MobileSegment.Wireframe>
): MobileSegment.MobileIncrementalData.MobileMutationData {
): MobileSegment.MobileIncrementalData.MobileMutationData? {
val prevSnapshotAsMap = prevSnapshot.associateBy { it.id() }.toMutableMap()
val updates: MutableList<MobileSegment.WireframeUpdateMutation> = LinkedList()
val adds: MutableList<MobileSegment.Add> = LinkedList()
Expand All @@ -31,11 +31,16 @@ internal class MutationResolver {
prevWireframe = currentWireframe
}
val removes = prevSnapshotAsMap.map { MobileSegment.Remove(it.key) }
return MobileSegment.MobileIncrementalData.MobileMutationData(
adds = adds,
removes = removes,
updates = updates
)

return if (adds.isNotEmpty() || removes.isNotEmpty() || updates.isNotEmpty()) {
MobileSegment.MobileIncrementalData.MobileMutationData(
adds = adds,
removes = removes,
updates = updates
)
} else {
null
}
}

private fun resolveUpdateMutation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,20 +137,27 @@ internal class SnapshotProcessor(
records.add(focusRecord)
}

val record = if (fullSnapshotRequired) {
MobileSegment.MobileRecord.MobileFullSnapshotRecord(
timestamp,
MobileSegment.Data(wireframes)
if (fullSnapshotRequired) {
records.add(
MobileSegment.MobileRecord.MobileFullSnapshotRecord(
timestamp,
MobileSegment.Data(wireframes)
)
)
} else {
MobileSegment.MobileRecord.MobileIncrementalSnapshotRecord(
timestamp,
mutationResolver.resolveMutations(prevSnapshot, wireframes)
)
mutationResolver.resolveMutations(prevSnapshot, wireframes)?.let {
records.add(
MobileSegment.MobileRecord.MobileIncrementalSnapshotRecord(
timestamp,
it
)
)
}
}
records.add(record)
prevSnapshot = wireframes
writer.write(bundleRecordInEnrichedRecord(newRumContext, records))
if (records.isNotEmpty()) {
writer.write(bundleRecordInEnrichedRecord(newRumContext, records))
}
}

private fun isLastFullSnapshotTime(): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.datadog.android.sessionreplay.utils.ForgeConfigurator
import fr.xgouchet.elmyr.Forge
import fr.xgouchet.elmyr.junit5.ForgeConfiguration
import fr.xgouchet.elmyr.junit5.ForgeExtension
import java.util.ArrayList
import java.util.LinkedList
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
Expand Down Expand Up @@ -60,9 +61,9 @@ internal class MutationResolverTest {
)

// Then
assertThat(mutations.adds).isEqualTo(expectedAdditions)
assertThat(mutations.removes).isNullOrEmpty()
assertThat(mutations.updates).isNullOrEmpty()
assertThat(mutations?.adds).isEqualTo(expectedAdditions)
assertThat(mutations?.removes).isNullOrEmpty()
assertThat(mutations?.updates).isNullOrEmpty()
}

@Test
Expand All @@ -87,9 +88,9 @@ internal class MutationResolverTest {
)

// Then
assertThat(mutations.adds).isEqualTo(expectedAdds)
assertThat(mutations.removes).isNullOrEmpty()
assertThat(mutations.updates).isNullOrEmpty()
assertThat(mutations?.adds).isEqualTo(expectedAdds)
assertThat(mutations?.removes).isNullOrEmpty()
assertThat(mutations?.updates).isNullOrEmpty()
}

// endregion
Expand All @@ -115,9 +116,9 @@ internal class MutationResolverTest {
)

// Then
assertThat(mutations.adds).isNullOrEmpty()
assertThat(mutations.removes).isEqualTo(expectedRemovals)
assertThat(mutations.updates).isNullOrEmpty()
assertThat(mutations?.adds).isNullOrEmpty()
assertThat(mutations?.removes).isEqualTo(expectedRemovals)
assertThat(mutations?.updates).isNullOrEmpty()
}

// endregion
Expand Down Expand Up @@ -150,9 +151,9 @@ internal class MutationResolverTest {
)

// Then
assertThat(mutations.adds).isNullOrEmpty()
assertThat(mutations.removes).isNullOrEmpty()
assertThat(mutations.updates).isEqualTo(expectedUpdates)
assertThat(mutations?.adds).isNullOrEmpty()
assertThat(mutations?.removes).isNullOrEmpty()
assertThat(mutations?.updates).isEqualTo(expectedUpdates)
}

@Test
Expand Down Expand Up @@ -181,9 +182,9 @@ internal class MutationResolverTest {
)

// Then
assertThat(mutations.adds).isNullOrEmpty()
assertThat(mutations.removes).isNullOrEmpty()
assertThat(mutations.updates).isEqualTo(expectedUpdates)
assertThat(mutations?.adds).isNullOrEmpty()
assertThat(mutations?.removes).isNullOrEmpty()
assertThat(mutations?.updates).isEqualTo(expectedUpdates)
}

@Test
Expand Down Expand Up @@ -218,9 +219,9 @@ internal class MutationResolverTest {
)

// Then
assertThat(mutations.adds).isNullOrEmpty()
assertThat(mutations.removes).isNullOrEmpty()
assertThat(mutations.updates).isEqualTo(expectedUpdates)
assertThat(mutations?.adds).isNullOrEmpty()
assertThat(mutations?.removes).isNullOrEmpty()
assertThat(mutations?.updates).isEqualTo(expectedUpdates)
}

@Test
Expand Down Expand Up @@ -256,9 +257,9 @@ internal class MutationResolverTest {
)

// Then
assertThat(mutations.adds).isNullOrEmpty()
assertThat(mutations.removes).isNullOrEmpty()
assertThat(mutations.updates).isEqualTo(expectedUpdates)
assertThat(mutations?.adds).isNullOrEmpty()
assertThat(mutations?.removes).isNullOrEmpty()
assertThat(mutations?.updates).isEqualTo(expectedUpdates)
}

@Test
Expand All @@ -284,9 +285,9 @@ internal class MutationResolverTest {
)

// Then
assertThat(mutations.adds).isNullOrEmpty()
assertThat(mutations.removes).isNullOrEmpty()
assertThat(mutations.updates).isNullOrEmpty()
assertThat(mutations?.adds).isNullOrEmpty()
assertThat(mutations?.removes).isNullOrEmpty()
assertThat(mutations?.updates).isNullOrEmpty()
}

// endregion
Expand Down Expand Up @@ -319,9 +320,9 @@ internal class MutationResolverTest {
)

// Then
assertThat(mutations.adds).isNullOrEmpty()
assertThat(mutations.removes).isNullOrEmpty()
assertThat(mutations.updates).isEqualTo(expectedUpdates)
assertThat(mutations?.adds).isNullOrEmpty()
assertThat(mutations?.removes).isNullOrEmpty()
assertThat(mutations?.updates).isEqualTo(expectedUpdates)
}

@Test
Expand Down Expand Up @@ -350,9 +351,9 @@ internal class MutationResolverTest {
)

// Then
assertThat(mutations.adds).isNullOrEmpty()
assertThat(mutations.removes).isNullOrEmpty()
assertThat(mutations.updates).isEqualTo(expectedUpdates)
assertThat(mutations?.adds).isNullOrEmpty()
assertThat(mutations?.removes).isNullOrEmpty()
assertThat(mutations?.updates).isEqualTo(expectedUpdates)
}

@Test
Expand Down Expand Up @@ -387,9 +388,9 @@ internal class MutationResolverTest {
)

// Then
assertThat(mutations.adds).isNullOrEmpty()
assertThat(mutations.removes).isNullOrEmpty()
assertThat(mutations.updates).isEqualTo(expectedUpdates)
assertThat(mutations?.adds).isNullOrEmpty()
assertThat(mutations?.removes).isNullOrEmpty()
assertThat(mutations?.updates).isEqualTo(expectedUpdates)
}

@Test
Expand Down Expand Up @@ -425,9 +426,9 @@ internal class MutationResolverTest {
)

// Then
assertThat(mutations.adds).isNullOrEmpty()
assertThat(mutations.removes).isNullOrEmpty()
assertThat(mutations.updates).isEqualTo(expectedUpdates)
assertThat(mutations?.adds).isNullOrEmpty()
assertThat(mutations?.removes).isNullOrEmpty()
assertThat(mutations?.updates).isEqualTo(expectedUpdates)
}

@Test
Expand Down Expand Up @@ -455,9 +456,9 @@ internal class MutationResolverTest {
)

// Then
assertThat(mutations.adds).isNullOrEmpty()
assertThat(mutations.removes).isNullOrEmpty()
assertThat(mutations.updates).isEqualTo(expectedUpdates)
assertThat(mutations?.adds).isNullOrEmpty()
assertThat(mutations?.removes).isNullOrEmpty()
assertThat(mutations?.updates).isEqualTo(expectedUpdates)
}

@Test
Expand Down Expand Up @@ -493,9 +494,9 @@ internal class MutationResolverTest {
)

// Then
assertThat(mutations.adds).isNullOrEmpty()
assertThat(mutations.removes).isNullOrEmpty()
assertThat(mutations.updates).isEqualTo(expectedUpdates)
assertThat(mutations?.adds).isNullOrEmpty()
assertThat(mutations?.removes).isNullOrEmpty()
assertThat(mutations?.updates).isEqualTo(expectedUpdates)
}

@Test
Expand Down Expand Up @@ -542,13 +543,13 @@ internal class MutationResolverTest {
)

// Then
assertThat(mutations.adds).isNullOrEmpty()
assertThat(mutations.removes).isNullOrEmpty()
assertThat(mutations.updates).isEqualTo(expectedUpdates)
assertThat(mutations?.adds).isNullOrEmpty()
assertThat(mutations?.removes).isNullOrEmpty()
assertThat(mutations?.updates).isEqualTo(expectedUpdates)
}

@Test
fun `M return empty mutations W resolveMutation {Text wireframes types are not matching }`(
fun `M return null W resolveMutation {Text wireframes types are not matching }`(
forge: Forge
) {
// Given
Expand All @@ -570,9 +571,53 @@ internal class MutationResolverTest {
)

// Then
assertThat(mutations.adds).isNullOrEmpty()
assertThat(mutations.removes).isNullOrEmpty()
assertThat(mutations.updates).isNullOrEmpty()
assertThat(mutations).isNull()
}

// endregion

// region no mutation

@Test
fun `M return null W resolveMutation { Text wireframes, no mutation }`(
forge: Forge
) {
// Given
val fakePrevSnapshot = forge.aList(size = forge.anInt(min = 2, max = 10)) {
forge.getForgery(MobileSegment.Wireframe.TextWireframe::class.java)
}

val fakeCurrentSnapshot = ArrayList(fakePrevSnapshot)

// When
val mutations = testedMutationResolver.resolveMutations(
fakePrevSnapshot,
fakeCurrentSnapshot
)

// Then
assertThat(mutations).isNull()
}

@Test
fun `M return null W resolveMutation { Shape wireframes, no mutation }`(
forge: Forge
) {
// Given
val fakePrevSnapshot = forge.aList(size = forge.anInt(min = 2, max = 10)) {
forge.getForgery(MobileSegment.Wireframe.ShapeWireframe::class.java)
}

val fakeCurrentSnapshot = ArrayList(fakePrevSnapshot)

// When
val mutations = testedMutationResolver.resolveMutations(
fakePrevSnapshot,
fakeCurrentSnapshot
)

// Then
assertThat(mutations).isNull()
}

// endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,38 @@ internal class SnapshotProcessorTest {
assertThat(incrementalSnapshotRecord.data).isEqualTo(fakeMutationData)
}

@Test
fun `M do nothing W process { no mutation was detected }`(forge: Forge) {
// Given
val fakeSnapshot1 = forge.aSingleLevelSnapshot()
val fakeSnapshot2 = fakeSnapshot1.copy()
val fakeFlattenedSnapshot1 = forge.aList {
getForgery(MobileSegment.Wireframe::class.java)
}
val fakeFlattenedSnapshot2 = ArrayList(fakeFlattenedSnapshot1)
whenever(mockNodeFlattener.flattenNode(fakeSnapshot1)).thenReturn(fakeFlattenedSnapshot1)
whenever(mockNodeFlattener.flattenNode(fakeSnapshot2)).thenReturn(fakeFlattenedSnapshot2)
whenever(
mockMutationResolver.resolveMutations(
fakeFlattenedSnapshot1,
fakeFlattenedSnapshot2
)
).thenReturn(null)
testedProcessor.process(fakeSnapshot1)

// When
testedProcessor.process(fakeSnapshot2)

// Then
// We should only send the FullSnapshotRecord. The IncrementalSnapshotRecord will not be
// send as there was no mutation data detected.
val captor = argumentCaptor<EnrichedRecord>()
verify(mockWriter, times(1)).write(captor.capture())
assertThat(captor.firstValue.records.size).isEqualTo(3)
assertThat(captor.firstValue.records[2])
.isInstanceOf(MobileSegment.MobileRecord.MobileFullSnapshotRecord::class.java)
}

// region TouchData

@Test
Expand Down