101 lines
3.7 KiB
Swift
101 lines
3.7 KiB
Swift
import AdaptiveRefreshCore
|
|
import Foundation
|
|
import Testing
|
|
|
|
struct AdaptiveRefreshPolicyCoreTests {
|
|
private static let referenceNow = Date(timeIntervalSinceReferenceDate: 800_000_000)
|
|
|
|
private func input(
|
|
ageSeconds: TimeInterval?,
|
|
lowPowerModeEnabled: Bool = false,
|
|
thermalPressure: AdaptiveRefreshPolicyCore.ThermalPressure = .nominal)
|
|
-> AdaptiveRefreshPolicyCore.Input
|
|
{
|
|
AdaptiveRefreshPolicyCore.Input(
|
|
now: Self.referenceNow,
|
|
lastMenuOpenAt: ageSeconds.map { Self.referenceNow.addingTimeInterval(-$0) },
|
|
lowPowerModeEnabled: lowPowerModeEnabled,
|
|
thermalPressure: thermalPressure)
|
|
}
|
|
|
|
@Test(arguments: [
|
|
(-600.0, AdaptiveRefreshPolicyCore.Reason.recentInteraction, 120),
|
|
(0.0, .recentInteraction, 120),
|
|
(299.0, .recentInteraction, 120),
|
|
(300.0, .recentInteraction, 120),
|
|
(301.0, .warm, 300),
|
|
(3599.0, .warm, 300),
|
|
(3600.0, .warm, 300),
|
|
(3601.0, .idle, 900),
|
|
(14399.0, .idle, 900),
|
|
(14400.0, .longIdle, 1800),
|
|
(100_000.0, .longIdle, 1800),
|
|
])
|
|
func `age determines the canonical table boundary`(
|
|
ageSeconds: TimeInterval,
|
|
expectedReason: AdaptiveRefreshPolicyCore.Reason,
|
|
expectedDelaySeconds: Int)
|
|
{
|
|
let decision = AdaptiveRefreshPolicyCore().nextDelay(for: self.input(ageSeconds: ageSeconds))
|
|
#expect(decision.reason == expectedReason)
|
|
#expect(decision.delay == .seconds(expectedDelaySeconds))
|
|
}
|
|
|
|
@Test
|
|
func `nil last menu open is long idle`() {
|
|
let decision = AdaptiveRefreshPolicyCore().nextDelay(for: self.input(ageSeconds: nil))
|
|
#expect(decision.reason == .longIdle)
|
|
#expect(decision.delay == .seconds(30 * 60))
|
|
}
|
|
|
|
@Test
|
|
func `low power mode wins over recent interaction`() {
|
|
let decision = AdaptiveRefreshPolicyCore().nextDelay(for: self.input(
|
|
ageSeconds: 0,
|
|
lowPowerModeEnabled: true))
|
|
#expect(decision.reason == .constrained)
|
|
#expect(decision.delay == .seconds(30 * 60))
|
|
}
|
|
|
|
@Test
|
|
func `thermal pressure wins when no menu open is recorded`() {
|
|
let decision = AdaptiveRefreshPolicyCore().nextDelay(for: self.input(
|
|
ageSeconds: nil,
|
|
thermalPressure: .constrained))
|
|
#expect(decision.reason == .constrained)
|
|
#expect(decision.delay == .seconds(30 * 60))
|
|
}
|
|
|
|
@Test
|
|
func `future timestamps read as recent`() {
|
|
let decision = AdaptiveRefreshPolicyCore().nextDelay(for: self.input(ageSeconds: -1_000_000))
|
|
#expect(decision.reason == .recentInteraction)
|
|
#expect(decision.delay == .seconds(2 * 60))
|
|
}
|
|
|
|
@Test
|
|
func `every decision stays within the two to thirty minute bounds`() {
|
|
let ages: [TimeInterval?] = [nil, -1_000_000, 0, 300, 301, 3600, 3601, 14399, 14400, 1_000_000]
|
|
for age in ages {
|
|
for lowPowerModeEnabled in [false, true] {
|
|
for thermalPressure in [
|
|
AdaptiveRefreshPolicyCore.ThermalPressure.nominal,
|
|
.constrained,
|
|
] {
|
|
let decision = AdaptiveRefreshPolicyCore().nextDelay(for: self.input(
|
|
ageSeconds: age,
|
|
lowPowerModeEnabled: lowPowerModeEnabled,
|
|
thermalPressure: thermalPressure))
|
|
#expect(decision.delay >= .seconds(2 * 60))
|
|
#expect(decision.delay <= .seconds(30 * 60))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Test
|
|
func `nominal heuristic interval remains five minutes`() {
|
|
#expect(AdaptiveRefreshPolicyCore.nominalIntervalForHeuristics == 5 * 60)
|
|
}
|
|
}
|