Files
2026-07-13 12:22:33 +08:00

97 lines
3.8 KiB
Swift

import AdaptiveReplayKit
import Foundation
import Testing
/// Purely informational trace-level stats surfaced by `AdaptiveReplayCLI` — computed directly from
/// raw `decision` records, independent of any `ReplayPolicy` or `ReplayEngine` simulation.
struct ActivityCoverageStatsTests {
private static let referenceNow = Date(timeIntervalSinceReferenceDate: 800_000_000)
private static func decision(codex: TimeInterval?, claude: TimeInterval?) -> AdaptiveRefreshTraceRecord {
.decision(
timestamp: self.referenceNow,
menuAgeSeconds: nil,
lowPowerModeEnabled: false,
thermalState: .nominal,
reason: "warm",
delaySeconds: 300,
codexActivitySeconds: codex,
claudeActivitySeconds: claude)
}
@Test
func `an empty trace reports zero decisions and zero fractions`() {
let stats = ActivityCoverageStats.compute(from: [])
#expect(stats.decisionCount == 0)
#expect(stats.sampledCount == 0)
#expect(stats.activeCount == 0)
#expect(stats.sampledFraction == 0)
#expect(stats.activeFraction == 0)
}
@Test
func `non decision records are ignored entirely`() {
let records: [AdaptiveRefreshTraceRecord] = [
.menuOpen(timestamp: Self.referenceNow),
.refreshCompleted(timestamp: Self.referenceNow),
]
let stats = ActivityCoverageStats.compute(from: records)
#expect(stats.decisionCount == 0)
}
@Test
func `a decision with neither activity field set counts toward decisionCount but not sampledCount`() {
let stats = ActivityCoverageStats.compute(from: [Self.decision(codex: nil, claude: nil)])
#expect(stats.decisionCount == 1)
#expect(stats.sampledCount == 0)
#expect(stats.activeCount == 0)
}
@Test
func `a decision with only one activity field set still counts as sampled`() {
let stats = ActivityCoverageStats.compute(from: [Self.decision(codex: 500, claude: nil)])
#expect(stats.sampledCount == 1)
}
@Test
func `a sampled decision under the active threshold on either CLI counts as active`() {
let codexActive = ActivityCoverageStats.compute(from: [Self.decision(codex: 100, claude: nil)])
#expect(codexActive.activeCount == 1)
let claudeActive = ActivityCoverageStats.compute(from: [Self.decision(codex: nil, claude: 100)])
#expect(claudeActive.activeCount == 1)
}
@Test
func `a sampled decision at or above the active threshold on both CLIs does not count as active`() {
let stats = ActivityCoverageStats.compute(from: [Self.decision(codex: 500, claude: 400)])
#expect(stats.sampledCount == 1)
#expect(stats.activeCount == 0)
}
@Test
func `fractions are computed against decisionCount and sampledCount respectively`() {
let records: [AdaptiveRefreshTraceRecord] = [
Self.decision(codex: 100, claude: nil), // sampled, active
Self.decision(codex: 500, claude: 400), // sampled, not active
Self.decision(codex: nil, claude: nil), // not sampled
Self.decision(codex: nil, claude: nil), // not sampled
]
let stats = ActivityCoverageStats.compute(from: records)
#expect(stats.decisionCount == 4)
#expect(stats.sampledCount == 2)
#expect(stats.activeCount == 1)
#expect(stats.sampledFraction == 0.5)
#expect(stats.activeFraction == 0.5)
}
@Test
func `a custom active threshold changes the active classification`() {
let stats = ActivityCoverageStats.compute(
from: [Self.decision(codex: 250, claude: nil)],
activeThresholdSeconds: 60)
#expect(stats.sampledCount == 1)
#expect(stats.activeCount == 0)
}
}