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

118 lines
4.4 KiB
Swift

import AdaptiveReplayKit
import Foundation
import Testing
struct ReplayTraceSegmentationTests {
private static let epoch = Date(timeIntervalSinceReferenceDate: 0)
private func at(_ seconds: TimeInterval) -> Date {
Self.epoch.addingTimeInterval(seconds)
}
private func decision(_ seconds: TimeInterval, delay: TimeInterval = 600) -> AdaptiveRefreshTraceRecord {
.decision(
timestamp: self.at(seconds),
menuAgeSeconds: nil,
lowPowerModeEnabled: false,
thermalState: .nominal,
reason: "longIdle",
delaySeconds: delay)
}
@Test
func `segmentation excludes only time beyond the expected deadline`() {
let firstRun = stride(from: 0.0, through: 3000.0, by: 600.0).map { self.decision($0) }
let secondRun = stride(from: 68400.0, through: 71400.0, by: 600.0).map { self.decision($0) }
let trace = firstRun + secondRun + [.refreshCompleted(timestamp: self.at(72000))]
let report = ReplayTraceSegmenter.automatic(trace)
#expect(report.segments.count == 2)
#expect(report.segments[0].start == self.at(0))
#expect(report.segments[0].end == self.at(3600))
#expect(report.segments[1].start == self.at(68400))
#expect(report.segments[1].end == self.at(72000))
#expect(report.excludedGapSeconds == 18 * 60 * 60)
#expect(report.includedSpanSeconds == 2 * 60 * 60)
}
@Test
func `segmented rate uses summed span instead of averaging segment rates`() {
let firstRun = stride(from: 0.0, through: 3000.0, by: 600.0).map { self.decision($0) }
let secondRun = stride(from: 68400.0, through: 71400.0, by: 600.0).map { self.decision($0) }
let trace = firstRun + secondRun + [.refreshCompleted(timestamp: self.at(72000))]
let metrics = ReplayEngine.runSegmented(trace: trace, policy: FixedIntervalPolicy(minutes: 10))
#expect(metrics.totalRefreshCount == 12)
#expect(metrics.simulatedSpanSeconds == 7200)
#expect(metrics.refreshCountPer24h == 144)
#expect(metrics.segmentCount == 2)
#expect(metrics.excludedGapSeconds == 18 * 60 * 60)
}
@Test
func `a normal scheduled wait remains in the preceding segment`() {
let trace = [
self.decision(0, delay: 1800),
.menuOpen(timestamp: self.at(1700)),
self.decision(4000, delay: 1800),
]
let report = ReplayTraceSegmenter.automatic(trace)
#expect(report.segments.count == 2)
#expect(report.segments[0].end == self.at(1800))
#expect(report.excludedGapSeconds == 2200)
}
@Test
func `menu opens before the first recorded refresh are censored equally`() throws {
let trace = [
self.decision(0, delay: 600),
.menuOpen(timestamp: self.at(100)),
.refreshCompleted(timestamp: self.at(600)),
self.decision(600, delay: 600),
.menuOpen(timestamp: self.at(700)),
.refreshCompleted(timestamp: self.at(1200)),
]
let metrics = ReplayEngine.runSegmented(trace: trace, policy: FixedIntervalPolicy(minutes: 10))
#expect(metrics.boundaryCensoredMenuOpenCount == 1)
#expect(try #require(metrics.stalenessAtMenuOpen).sampleCount == 1)
}
@Test
func `recorded refresh anchors staleness before a policy refresh`() throws {
let trace = [
self.decision(0, delay: 600),
.refreshCompleted(timestamp: self.at(600)),
.menuOpen(timestamp: self.at(700)),
self.decision(1200, delay: 600),
]
let metrics = ReplayEngine.runSegmented(trace: trace, policy: ManualPolicy())
let staleness = try #require(metrics.stalenessAtMenuOpen)
#expect(staleness.sampleCount == 1)
#expect(staleness.mean == 100)
}
@Test
func `recorded refresh supersedes an earlier simulated refresh`() throws {
let trace = [
self.decision(0, delay: 650),
.refreshCompleted(timestamp: self.at(650)),
.menuOpen(timestamp: self.at(700)),
self.decision(1200, delay: 600),
]
let metrics = ReplayEngine.runSegmented(trace: trace, policy: FixedIntervalPolicy(minutes: 10))
let staleness = try #require(metrics.stalenessAtMenuOpen)
#expect(staleness.sampleCount == 1)
#expect(staleness.mean == 50)
}
}