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

RUM-2600: Add traversal flag to snapshot items #1837

Merged
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ internal class RecordedDataQueueHandler {

internal companion object {
@VisibleForTesting
internal const val MAX_DELAY_MS = 200L
internal const val MAX_DELAY_MS = 1000L

private val THREAD_POOL_MAX_KEEP_ALIVE_MS = TimeUnit.SECONDS.toMillis(5)
private const val CORE_DEFAULT_POOL_SIZE = 1 // Only one thread will be kept alive
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,22 @@ internal class SnapshotRecordedDataQueueItem(
recordedQueuedItemContext: RecordedQueuedItemContext,
internal val systemInformation: SystemInformation
) : RecordedDataQueueItem(recordedQueuedItemContext) {
internal var nodes = emptyList<Node>()
@Volatile internal var nodes = emptyList<Node>()

@Volatile internal var isFinishedTraversal = false
internal var pendingJobs = AtomicInteger(0)

override fun isValid(): Boolean {
if (!isFinishedTraversal) {
// item is always valid unless traversal has finished
return true
}

return nodes.isNotEmpty()
}

override fun isReady(): Boolean {
return pendingJobs.get() == 0
return isFinishedTraversal && pendingJobs.get() == 0
}

internal fun incrementPendingJobs() = pendingJobs.incrementAndGet()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,12 @@ internal class WindowsOnDrawListener(

if (nodes.isNotEmpty()) {
item.nodes = nodes
if (item.isReady()) {
recordedDataQueueHandler.tryToConsumeItems()
}
}

item.isFinishedTraversal = true

if (item.isReady()) {
recordedDataQueueHandler.tryToConsumeItems()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ internal class RecordedDataQueueHandlerTest {
// Given
val item = testedHandler.addSnapshotItem(mockSystemInformation) ?: fail("item is null")
item.nodes = fakeNodeData
item.isFinishedTraversal = true

whenever(mockTimeProvider.getDeviceTimestamp())
.thenReturn(item.recordedQueuedItemContext.timestamp)
Expand Down Expand Up @@ -464,6 +465,10 @@ internal class RecordedDataQueueHandlerTest {
val item2 = createFakeSnapshotItemWithDelayMs(2)
val item3 = createFakeSnapshotItemWithDelayMs(3)

item1.isFinishedTraversal = true
item2.isFinishedTraversal = true
item3.isFinishedTraversal = true

assertThat(testedHandler.recordedDataQueue.size).isEqualTo(3)
val itemTimestamp = item1.recordedQueuedItemContext.timestamp

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
package com.datadog.android.sessionreplay.internal.async

import com.datadog.android.sessionreplay.forge.ForgeConfigurator
import com.datadog.android.sessionreplay.internal.processor.RecordedQueuedItemContext
import com.datadog.android.sessionreplay.internal.recorder.SystemInformation
import fr.xgouchet.elmyr.annotation.Forgery
import fr.xgouchet.elmyr.junit5.ForgeConfiguration
import fr.xgouchet.elmyr.junit5.ForgeExtension
Expand All @@ -15,6 +17,7 @@ import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.extension.Extensions
import org.mockito.Mock
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.junit.jupiter.MockitoSettings
import org.mockito.quality.Strictness
Expand All @@ -30,17 +33,24 @@ internal class SnapshotRecordedDataQueueItemTest {
@Forgery
lateinit var fakeSnapshotRecordedDataQueueItem: SnapshotRecordedDataQueueItem

lateinit var testedItem: SnapshotRecordedDataQueueItem
@Forgery
lateinit var fakeRecordedQueuedItemContext: RecordedQueuedItemContext

@Mock
lateinit var mockSystemInformation: SystemInformation

private lateinit var testedItem: SnapshotRecordedDataQueueItem

@BeforeEach
fun `set up`() {
testedItem = fakeSnapshotRecordedDataQueueItem
}

@Test
fun `M return false W isValid() { Snapshot with empty nodes }`() {
fun `M return false W isValid() { finished traversal with empty nodes }`() {
// Given
testedItem.nodes = emptyList()
testedItem.isFinishedTraversal = true

// Then
assertThat(testedItem.isValid()).isFalse()
Expand All @@ -52,15 +62,38 @@ internal class SnapshotRecordedDataQueueItemTest {
assertThat(testedItem.isValid()).isTrue()
}

@Test
fun `M return true W isValid() { fresh node that has not finished traversal }`() {
// Given
testedItem = SnapshotRecordedDataQueueItem(
fakeRecordedQueuedItemContext,
mockSystemInformation
)

// Then
assertThat(testedItem.isValid()).isTrue()
}

@Test
fun `M return true W isReady() { Snapshot with no pending images }`() {
// Given
testedItem.pendingJobs.set(0)
testedItem.isFinishedTraversal = true

// Then
assertThat(testedItem.isReady()).isTrue()
}

@Test
fun `M return false W isReady() { not finished traveral }`() {
// Given
testedItem.pendingJobs.set(0)
testedItem.isFinishedTraversal = false

// Then
assertThat(testedItem.isReady()).isFalse()
}

@Test
fun `M return false W isReady() { Snapshot with pending images greater than 0 }`() {
// Given
Expand Down