mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-15 02:36:12 -04:00
refactor(support): Add zipFile/compressedSize to Ready, harden session manager
Add zipFile and compressedSize fields to DebugSession.Ready, populated during scan. Simplify RecorderActivityVM by reading compressed size directly from session model. Apply code review fixes: fsMutex in zipSessionAsync, CancellationException handling, ZipOutputStream use{}, atomic file moves, dedup deriveSessionId, LifecycleResumeEffect for session refresh.
This commit is contained in:
+223
@@ -0,0 +1,223 @@
|
||||
package eu.darken.capod.common.debug.recording.core
|
||||
|
||||
import io.kotest.matchers.collections.shouldBeEmpty
|
||||
import io.kotest.matchers.collections.shouldHaveSize
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.types.shouldBeInstanceOf
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.io.TempDir
|
||||
import testhelpers.BaseTest
|
||||
import java.io.File
|
||||
|
||||
class DebugSessionManagerSessionLogicTest : BaseTest() {
|
||||
|
||||
@TempDir
|
||||
lateinit var tempDir: File
|
||||
|
||||
private lateinit var externalLogsDir: File
|
||||
private lateinit var cacheLogsDir: File
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
externalLogsDir = File(tempDir, "external/debug/logs").also { it.mkdirs() }
|
||||
cacheLogsDir = File(tempDir, "cache/debug/logs").also { it.mkdirs() }
|
||||
}
|
||||
|
||||
private fun logDirs() = listOf(externalLogsDir, cacheLogsDir)
|
||||
|
||||
@Nested
|
||||
inner class ParseCreatedAt {
|
||||
@Test
|
||||
fun `standard format extracts timestamp`() {
|
||||
DebugSessionManager.parseCreatedAt("capod_1.2.3_1709810400000_abcd1234", 99L) shouldBe 1709810400000L
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `zip suffix is stripped before parsing`() {
|
||||
DebugSessionManager.parseCreatedAt("capod_1.2.3_1709810400000_abcd1234.zip", 99L) shouldBe 1709810400000L
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `too few parts returns fallback`() {
|
||||
DebugSessionManager.parseCreatedAt("capod_1.2.3", 99L) shouldBe 99L
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `non-numeric timestamp returns fallback`() {
|
||||
DebugSessionManager.parseCreatedAt("capod_1.2.3_notanumber_abcd1234", 99L) shouldBe 99L
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `timestamp in seconds returns fallback`() {
|
||||
DebugSessionManager.parseCreatedAt("capod_1.2.3_1709810400_abcd1234", 99L) shouldBe 99L
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `empty string returns fallback`() {
|
||||
DebugSessionManager.parseCreatedAt("", 42L) shouldBe 42L
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class DeriveSessionId {
|
||||
@Test
|
||||
fun `external dir gets ext prefix`() {
|
||||
val file = File("/storage/emulated/0/Android/data/pkg/files/debug/logs/session1")
|
||||
DebugSessionManager.deriveSessionId(file) shouldBe "ext:session1"
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `cache dir gets cache prefix`() {
|
||||
val file = File("/data/data/pkg/cache/debug/logs/session1")
|
||||
DebugSessionManager.deriveSessionId(file) shouldBe "cache:session1"
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `zip suffix is stripped`() {
|
||||
val file = File("/data/data/pkg/cache/debug/logs/session1.zip")
|
||||
DebugSessionManager.deriveSessionId(file) shouldBe "cache:session1"
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class ScanSessions {
|
||||
@Test
|
||||
fun `empty directories returns empty list`() {
|
||||
val result = DebugSessionManager.scanSessions(logDirectories = logDirs())
|
||||
result.shouldBeEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `dir with valid core log returns Ready`() {
|
||||
val sessionDir = File(externalLogsDir, "capod_1.0_1700000000000_abcd1234").also { it.mkdirs() }
|
||||
File(sessionDir, "core.log").writeText("some log content")
|
||||
|
||||
val result = DebugSessionManager.scanSessions(logDirectories = logDirs())
|
||||
|
||||
result shouldHaveSize 1
|
||||
val session = result.first()
|
||||
session.shouldBeInstanceOf<DebugSession.Ready>()
|
||||
session.id shouldBe "ext:capod_1.0_1700000000000_abcd1234"
|
||||
session.logDir shouldBe sessionDir
|
||||
session.zipFile shouldBe null
|
||||
session.compressedSize shouldBe 0L
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `dir with empty core log returns Failed EMPTY_LOG`() {
|
||||
val sessionDir = File(externalLogsDir, "capod_1.0_1700000000000_abcd1234").also { it.mkdirs() }
|
||||
File(sessionDir, "core.log").createNewFile()
|
||||
|
||||
val result = DebugSessionManager.scanSessions(logDirectories = logDirs())
|
||||
|
||||
result shouldHaveSize 1
|
||||
val session = result.first()
|
||||
session.shouldBeInstanceOf<DebugSession.Failed>()
|
||||
(session as DebugSession.Failed).reason shouldBe DebugSession.Failed.Reason.EMPTY_LOG
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `dir with no core log returns Failed MISSING_LOG`() {
|
||||
File(externalLogsDir, "capod_1.0_1700000000000_abcd1234").mkdirs()
|
||||
|
||||
val result = DebugSessionManager.scanSessions(logDirectories = logDirs())
|
||||
|
||||
result shouldHaveSize 1
|
||||
val session = result.first()
|
||||
session.shouldBeInstanceOf<DebugSession.Failed>()
|
||||
(session as DebugSession.Failed).reason shouldBe DebugSession.Failed.Reason.MISSING_LOG
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `standalone non-empty zip returns Ready with null logDir`() {
|
||||
File(externalLogsDir, "capod_1.0_1700000000000_abcd1234.zip").writeText("zipdata")
|
||||
|
||||
val result = DebugSessionManager.scanSessions(logDirectories = logDirs())
|
||||
|
||||
result shouldHaveSize 1
|
||||
val session = result.first()
|
||||
session.shouldBeInstanceOf<DebugSession.Ready>()
|
||||
(session as DebugSession.Ready).logDir shouldBe null
|
||||
session.zipFile shouldBe File(externalLogsDir, "capod_1.0_1700000000000_abcd1234.zip")
|
||||
session.compressedSize shouldBe File(externalLogsDir, "capod_1.0_1700000000000_abcd1234.zip").length()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `standalone empty zip returns Failed CORRUPT_ZIP`() {
|
||||
File(externalLogsDir, "capod_1.0_1700000000000_abcd1234.zip").createNewFile()
|
||||
|
||||
val result = DebugSessionManager.scanSessions(logDirectories = logDirs())
|
||||
|
||||
result shouldHaveSize 1
|
||||
val session = result.first()
|
||||
session.shouldBeInstanceOf<DebugSession.Failed>()
|
||||
(session as DebugSession.Failed).reason shouldBe DebugSession.Failed.Reason.CORRUPT_ZIP
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `dir plus sibling zip reports combined diskSize`() {
|
||||
val sessionDir = File(externalLogsDir, "capod_1.0_1700000000000_abcd1234").also { it.mkdirs() }
|
||||
File(sessionDir, "core.log").writeText("log content here")
|
||||
File(externalLogsDir, "capod_1.0_1700000000000_abcd1234.zip").writeText("zipdata12345")
|
||||
|
||||
val result = DebugSessionManager.scanSessions(logDirectories = logDirs())
|
||||
|
||||
result shouldHaveSize 1
|
||||
val session = result.first() as DebugSession.Ready
|
||||
val expectedDirSize = sessionDir.walkTopDown().filter { it.isFile }.sumOf { it.length() }
|
||||
val zipFile = File(externalLogsDir, "capod_1.0_1700000000000_abcd1234.zip")
|
||||
val expectedZipSize = zipFile.length()
|
||||
session.diskSize shouldBe expectedDirSize + expectedZipSize
|
||||
session.zipFile shouldBe zipFile
|
||||
session.compressedSize shouldBe expectedZipSize
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `dir missing core log but valid sibling zip returns Ready with null logDir`() {
|
||||
File(externalLogsDir, "capod_1.0_1700000000000_abcd1234").mkdirs()
|
||||
File(externalLogsDir, "capod_1.0_1700000000000_abcd1234.zip").writeText("zipdata")
|
||||
|
||||
val result = DebugSessionManager.scanSessions(logDirectories = logDirs())
|
||||
|
||||
result shouldHaveSize 1
|
||||
val session = result.first()
|
||||
session.shouldBeInstanceOf<DebugSession.Ready>()
|
||||
(session as DebugSession.Ready).logDir shouldBe null
|
||||
session.zipFile shouldBe File(externalLogsDir, "capod_1.0_1700000000000_abcd1234.zip")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `active recording dir returns Recording`() {
|
||||
val sessionDir = File(externalLogsDir, "capod_1.0_1700000000000_abcd1234").also { it.mkdirs() }
|
||||
File(sessionDir, "core.log").writeText("recording in progress")
|
||||
|
||||
val result = DebugSessionManager.scanSessions(
|
||||
logDirectories = logDirs(),
|
||||
activeDir = sessionDir,
|
||||
recordingStartedAt = 1700000000000L,
|
||||
)
|
||||
|
||||
result shouldHaveSize 1
|
||||
val session = result.first()
|
||||
session.shouldBeInstanceOf<DebugSession.Recording>()
|
||||
(session as DebugSession.Recording).startedAt shouldBe 1700000000000L
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `multiple sessions sorted by createdAt descending then id ascending`() {
|
||||
val oldDir = File(externalLogsDir, "capod_1.0_1600000000000_abcd1234").also { it.mkdirs() }
|
||||
File(oldDir, "core.log").writeText("old log")
|
||||
|
||||
val newDir = File(externalLogsDir, "capod_1.0_1700000000000_abcd1234").also { it.mkdirs() }
|
||||
File(newDir, "core.log").writeText("new log")
|
||||
|
||||
val result = DebugSessionManager.scanSessions(logDirectories = logDirs())
|
||||
|
||||
result shouldHaveSize 2
|
||||
result[0].createdAt shouldBe 1700000000000L
|
||||
result[1].createdAt shouldBe 1600000000000L
|
||||
}
|
||||
}
|
||||
}
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
package eu.darken.capod.common.debug.recording.core
|
||||
|
||||
import io.kotest.matchers.collections.shouldBeEmpty
|
||||
import io.kotest.matchers.collections.shouldHaveSize
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.types.shouldBeInstanceOf
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.io.TempDir
|
||||
import testhelpers.BaseTest
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Tests for [DebugSessionManager] overlay and reconciliation logic.
|
||||
*
|
||||
* These tests exercise the instance-level behaviors (zippingIds overlay, failedZipIds overlay,
|
||||
* orphan detection) by calling the companion [scanSessions] and then manually applying overlays,
|
||||
* mirroring what [DebugSessionManager.applyOverlays] and [DebugSessionManager.reconcileOrphans] do.
|
||||
*
|
||||
* Full integration tests with mocked RecorderModule are deferred until MockK/Java 21 compat is resolved.
|
||||
*/
|
||||
class DebugSessionManagerTest : BaseTest() {
|
||||
|
||||
@TempDir
|
||||
lateinit var tempDir: File
|
||||
|
||||
private lateinit var externalLogsDir: File
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
externalLogsDir = File(tempDir, "external/debug/logs").also { it.mkdirs() }
|
||||
}
|
||||
|
||||
private fun logDirs() = listOf(externalLogsDir)
|
||||
|
||||
private fun scanAndOverlay(
|
||||
zippingIds: Set<String> = emptySet(),
|
||||
failedZipIds: Set<String> = emptySet(),
|
||||
activeDir: File? = null,
|
||||
recordingStartedAt: Long = 0L,
|
||||
): List<DebugSession> {
|
||||
val raw = DebugSessionManager.scanSessions(
|
||||
logDirectories = logDirs(),
|
||||
activeDir = activeDir,
|
||||
recordingStartedAt = recordingStartedAt,
|
||||
)
|
||||
return raw.map { session ->
|
||||
when {
|
||||
session.id in zippingIds -> DebugSession.Compressing(
|
||||
id = session.id,
|
||||
displayName = session.displayName,
|
||||
createdAt = session.createdAt,
|
||||
diskSize = session.diskSize,
|
||||
path = (session as? DebugSession.Ready)?.logDir ?: File(""),
|
||||
)
|
||||
|
||||
session.id in failedZipIds && session !is DebugSession.Failed -> DebugSession.Failed(
|
||||
id = session.id,
|
||||
displayName = session.displayName,
|
||||
createdAt = session.createdAt,
|
||||
diskSize = session.diskSize,
|
||||
path = (session as? DebugSession.Ready)?.logDir ?: File(""),
|
||||
reason = DebugSession.Failed.Reason.ZIP_FAILED,
|
||||
)
|
||||
|
||||
else -> session
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class ZippingIdsOverlay {
|
||||
@Test
|
||||
fun `session in zippingIds appears as Compressing`() {
|
||||
val sessionDir = File(externalLogsDir, "capod_1.0_1700000000000_abcd1234").also { it.mkdirs() }
|
||||
File(sessionDir, "core.log").writeText("done recording")
|
||||
|
||||
val result = scanAndOverlay(zippingIds = setOf("ext:capod_1.0_1700000000000_abcd1234"))
|
||||
|
||||
result shouldHaveSize 1
|
||||
result.first().shouldBeInstanceOf<DebugSession.Compressing>()
|
||||
result.first().id shouldBe "ext:capod_1.0_1700000000000_abcd1234"
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `session not in zippingIds remains Ready`() {
|
||||
val sessionDir = File(externalLogsDir, "capod_1.0_1700000000000_abcd1234").also { it.mkdirs() }
|
||||
File(sessionDir, "core.log").writeText("done recording")
|
||||
|
||||
val result = scanAndOverlay(zippingIds = setOf("ext:some_other_session"))
|
||||
|
||||
result shouldHaveSize 1
|
||||
result.first().shouldBeInstanceOf<DebugSession.Ready>()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class FailedZipIdsOverlay {
|
||||
@Test
|
||||
fun `session in failedZipIds appears as Failed ZIP_FAILED`() {
|
||||
val sessionDir = File(externalLogsDir, "capod_1.0_1700000000000_abcd1234").also { it.mkdirs() }
|
||||
File(sessionDir, "core.log").writeText("done recording")
|
||||
|
||||
val result = scanAndOverlay(failedZipIds = setOf("ext:capod_1.0_1700000000000_abcd1234"))
|
||||
|
||||
result shouldHaveSize 1
|
||||
val session = result.first()
|
||||
session.shouldBeInstanceOf<DebugSession.Failed>()
|
||||
(session as DebugSession.Failed).reason shouldBe DebugSession.Failed.Reason.ZIP_FAILED
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `already-failed session is not overridden by failedZipIds`() {
|
||||
File(externalLogsDir, "capod_1.0_1700000000000_abcd1234").mkdirs()
|
||||
|
||||
val result = scanAndOverlay(failedZipIds = setOf("ext:capod_1.0_1700000000000_abcd1234"))
|
||||
|
||||
result shouldHaveSize 1
|
||||
val session = result.first()
|
||||
session.shouldBeInstanceOf<DebugSession.Failed>()
|
||||
(session as DebugSession.Failed).reason shouldBe DebugSession.Failed.Reason.MISSING_LOG
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class OrphanDetection {
|
||||
@Test
|
||||
fun `Ready session with logDir and sibling zip is not an orphan`() {
|
||||
val sessionDir = File(externalLogsDir, "capod_1.0_1700000000000_abcd1234").also { it.mkdirs() }
|
||||
File(sessionDir, "core.log").writeText("log content")
|
||||
File(externalLogsDir, "capod_1.0_1700000000000_abcd1234.zip").writeText("zipdata")
|
||||
|
||||
val sessions = scanAndOverlay()
|
||||
|
||||
sessions shouldHaveSize 1
|
||||
val session = sessions.first() as DebugSession.Ready
|
||||
session.logDir shouldBe sessionDir
|
||||
session.zipFile shouldBe File(externalLogsDir, "capod_1.0_1700000000000_abcd1234.zip")
|
||||
|
||||
val orphans = sessions.filterIsInstance<DebugSession.Ready>().filter { ready ->
|
||||
ready.logDir != null && (ready.zipFile == null || ready.compressedSize == 0L)
|
||||
}
|
||||
orphans.shouldBeEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Ready session with logDir but no sibling zip is detected as orphan`() {
|
||||
val sessionDir = File(externalLogsDir, "capod_1.0_1700000000000_abcd1234").also { it.mkdirs() }
|
||||
File(sessionDir, "core.log").writeText("log content")
|
||||
|
||||
val sessions = scanAndOverlay()
|
||||
|
||||
sessions shouldHaveSize 1
|
||||
val session = sessions.first() as DebugSession.Ready
|
||||
session.logDir shouldBe sessionDir
|
||||
session.zipFile shouldBe null
|
||||
session.compressedSize shouldBe 0L
|
||||
|
||||
val orphans = sessions.filterIsInstance<DebugSession.Ready>().filter { ready ->
|
||||
ready.logDir != null && (ready.zipFile == null || ready.compressedSize == 0L)
|
||||
}
|
||||
orphans shouldHaveSize 1
|
||||
orphans.first().id shouldBe "ext:capod_1.0_1700000000000_abcd1234"
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `orphan already in zippingIds is not re-detected`() {
|
||||
val sessionDir = File(externalLogsDir, "capod_1.0_1700000000000_abcd1234").also { it.mkdirs() }
|
||||
File(sessionDir, "core.log").writeText("log content")
|
||||
|
||||
val zipping = setOf("ext:capod_1.0_1700000000000_abcd1234")
|
||||
val sessions = scanAndOverlay(zippingIds = zipping)
|
||||
|
||||
sessions shouldHaveSize 1
|
||||
sessions.first().shouldBeInstanceOf<DebugSession.Compressing>()
|
||||
|
||||
// No Ready sessions remain, so no orphans to detect
|
||||
val orphans = sessions.filterIsInstance<DebugSession.Ready>().filter { ready ->
|
||||
ready.logDir != null && ready.id !in zipping &&
|
||||
(ready.zipFile == null || ready.compressedSize == 0L)
|
||||
}
|
||||
orphans.shouldBeEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class DeleteGuards {
|
||||
@Test
|
||||
fun `active recording session cannot be zipped`() {
|
||||
val sessionDir = File(externalLogsDir, "capod_1.0_1700000000000_abcd1234").also { it.mkdirs() }
|
||||
File(sessionDir, "core.log").writeText("recording in progress")
|
||||
|
||||
val sessions = scanAndOverlay(activeDir = sessionDir, recordingStartedAt = 1700000000000L)
|
||||
|
||||
sessions shouldHaveSize 1
|
||||
sessions.first().shouldBeInstanceOf<DebugSession.Recording>()
|
||||
// Verify the session IS a Recording — the manager would reject zip/delete calls for this
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `zipping session should not be deleted`() {
|
||||
val sessionDir = File(externalLogsDir, "capod_1.0_1700000000000_abcd1234").also { it.mkdirs() }
|
||||
File(sessionDir, "core.log").writeText("done recording")
|
||||
|
||||
val zipping = setOf("ext:capod_1.0_1700000000000_abcd1234")
|
||||
val sessions = scanAndOverlay(zippingIds = zipping)
|
||||
|
||||
sessions shouldHaveSize 1
|
||||
sessions.first().shouldBeInstanceOf<DebugSession.Compressing>()
|
||||
// The manager would reject deleteSession for IDs in zippingIds
|
||||
}
|
||||
}
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
package eu.darken.capod.main.ui.settings.support
|
||||
|
||||
import eu.darken.capod.common.debug.recording.core.DebugSession
|
||||
import io.kotest.matchers.collections.shouldBeEmpty
|
||||
import io.kotest.matchers.collections.shouldHaveSize
|
||||
import io.kotest.matchers.shouldBe
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
import testhelpers.BaseTest
|
||||
import java.io.File
|
||||
|
||||
class SupportViewModelStateTest : BaseTest() {
|
||||
|
||||
private fun readySession(id: String = "ext:s1", diskSize: Long = 100L) = DebugSession.Ready(
|
||||
id = id,
|
||||
displayName = "s1",
|
||||
createdAt = 1700000000000L,
|
||||
diskSize = diskSize,
|
||||
logDir = File("/tmp/s1"),
|
||||
zipFile = null,
|
||||
compressedSize = 0L,
|
||||
)
|
||||
|
||||
private fun recordingSession(id: String = "ext:rec") = DebugSession.Recording(
|
||||
id = id,
|
||||
displayName = "rec",
|
||||
createdAt = 1700000000000L,
|
||||
diskSize = 50L,
|
||||
path = File("/tmp/rec"),
|
||||
startedAt = 1700000000000L,
|
||||
)
|
||||
|
||||
private fun failedSession(id: String = "ext:fail", diskSize: Long = 10L) = DebugSession.Failed(
|
||||
id = id,
|
||||
displayName = "fail",
|
||||
createdAt = 1700000000000L,
|
||||
diskSize = diskSize,
|
||||
path = File("/tmp/fail"),
|
||||
reason = DebugSession.Failed.Reason.EMPTY_LOG,
|
||||
)
|
||||
|
||||
private fun compressingSession(id: String = "ext:comp", diskSize: Long = 75L) = DebugSession.Compressing(
|
||||
id = id,
|
||||
displayName = "comp",
|
||||
createdAt = 1700000000000L,
|
||||
diskSize = diskSize,
|
||||
path = File("/tmp/comp"),
|
||||
)
|
||||
|
||||
@Nested
|
||||
inner class LogSessionCount {
|
||||
@Test
|
||||
fun `empty sessions returns 0`() {
|
||||
SupportViewModel.State().logSessionCount shouldBe 0
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `excludes Recording sessions`() {
|
||||
val state = SupportViewModel.State(
|
||||
sessions = listOf(recordingSession(), readySession()),
|
||||
)
|
||||
state.logSessionCount shouldBe 1
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `includes Compressing and Failed sessions`() {
|
||||
val state = SupportViewModel.State(
|
||||
sessions = listOf(compressingSession(), failedSession(), readySession()),
|
||||
)
|
||||
state.logSessionCount shouldBe 3
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `only Recording sessions returns 0`() {
|
||||
val state = SupportViewModel.State(
|
||||
sessions = listOf(recordingSession()),
|
||||
)
|
||||
state.logSessionCount shouldBe 0
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class LogFolderSize {
|
||||
@Test
|
||||
fun `empty sessions returns 0`() {
|
||||
SupportViewModel.State().logFolderSize shouldBe 0L
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sums all session diskSizes`() {
|
||||
val state = SupportViewModel.State(
|
||||
sessions = listOf(
|
||||
readySession(id = "ext:a", diskSize = 100L),
|
||||
failedSession(id = "ext:b", diskSize = 50L),
|
||||
recordingSession(),
|
||||
compressingSession(id = "ext:c", diskSize = 75L),
|
||||
),
|
||||
)
|
||||
state.logFolderSize shouldBe 100L + 50L + 50L + 75L
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class FailedSessions {
|
||||
@Test
|
||||
fun `empty sessions returns empty`() {
|
||||
SupportViewModel.State().failedSessions.shouldBeEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `no failed sessions returns empty`() {
|
||||
val state = SupportViewModel.State(
|
||||
sessions = listOf(readySession(), recordingSession()),
|
||||
)
|
||||
state.failedSessions.shouldBeEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `filters to only Failed instances`() {
|
||||
val failed1 = failedSession(id = "ext:f1")
|
||||
val failed2 = failedSession(id = "ext:f2")
|
||||
val state = SupportViewModel.State(
|
||||
sessions = listOf(readySession(), failed1, recordingSession(), failed2),
|
||||
)
|
||||
state.failedSessions shouldHaveSize 2
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user