236 lines
9.7 KiB
Swift
236 lines
9.7 KiB
Swift
// ── SharedTimerData Tests ─────────────────────────────────────
|
|
|
|
import XCTest
|
|
@testable import ChronoMind
|
|
|
|
final class SharedTimerDataTests: XCTestCase {
|
|
|
|
// MARK: - TimerSnapshot Conversion
|
|
|
|
func testCMTimerToSnapshot() {
|
|
let now = Date()
|
|
let target = now.addingTimeInterval(3600)
|
|
let warning = CascadeWarning(
|
|
id: "w1",
|
|
minutesBefore: 10,
|
|
fired: true,
|
|
firedAt: now.addingTimeInterval(-600),
|
|
scheduledTime: now.addingTimeInterval(-600)
|
|
)
|
|
let warning2 = CascadeWarning(
|
|
id: "w2",
|
|
minutesBefore: 5,
|
|
fired: false,
|
|
firedAt: nil,
|
|
scheduledTime: target.addingTimeInterval(-300)
|
|
)
|
|
|
|
var timer = createAlarm(CreateAlarmParams(
|
|
label: "Test Alarm",
|
|
targetTime: target,
|
|
urgency: .important
|
|
))
|
|
timer.warnings = [warning, warning2]
|
|
timer.pomodoroConfig = PomodoroConfig(workMinutes: 25, breakMinutes: 5, rounds: 4)
|
|
timer.pomodoroState = PomodoroState(currentRound: 2, isBreak: false, roundStartedAt: now)
|
|
|
|
let snapshot = timer.toSnapshot()
|
|
|
|
XCTAssertEqual(snapshot.id, timer.id)
|
|
XCTAssertEqual(snapshot.label, "Test Alarm")
|
|
XCTAssertEqual(snapshot.type, .alarm)
|
|
XCTAssertEqual(snapshot.urgency, .important)
|
|
XCTAssertEqual(snapshot.state, .active)
|
|
XCTAssertEqual(snapshot.targetTime, target)
|
|
XCTAssertEqual(snapshot.totalWarnings, 2)
|
|
XCTAssertEqual(snapshot.firedWarnings, 1)
|
|
XCTAssertEqual(snapshot.nextWarningTime, target.addingTimeInterval(-300))
|
|
XCTAssertEqual(snapshot.pomodoroCurrentRound, 2)
|
|
XCTAssertEqual(snapshot.pomodoroTotalRounds, 4)
|
|
XCTAssertEqual(snapshot.pomodoroIsBreak, false)
|
|
}
|
|
|
|
func testSnapshotWithNoWarnings() {
|
|
let now = Date()
|
|
let target = now.addingTimeInterval(600)
|
|
let timer = createCountdown(CreateCountdownParams(
|
|
label: "Quick",
|
|
duration: 600,
|
|
urgency: .gentle
|
|
))
|
|
|
|
let snapshot = timer.toSnapshot()
|
|
|
|
XCTAssertEqual(snapshot.label, "Quick")
|
|
XCTAssertEqual(snapshot.type, .countdown)
|
|
XCTAssertEqual(snapshot.urgency, .gentle)
|
|
XCTAssertNil(snapshot.pomodoroCurrentRound)
|
|
XCTAssertNil(snapshot.pomodoroTotalRounds)
|
|
}
|
|
|
|
// MARK: - TimerSnapshot Helpers
|
|
|
|
func testRemainingSeconds() {
|
|
let target = Date().addingTimeInterval(120)
|
|
let snapshot = TimerSnapshot(
|
|
id: "t1", label: "Test", type: .countdown, urgency: .standard,
|
|
state: .active, targetTime: target, duration: 120,
|
|
startedAt: Date(), elapsedBeforePause: 0, snoozeCount: 0,
|
|
category: nil, pomodoroCurrentRound: nil, pomodoroTotalRounds: nil,
|
|
pomodoroIsBreak: nil, nextWarningTime: nil, totalWarnings: 0, firedWarnings: 0
|
|
)
|
|
|
|
let remaining = snapshot.remainingSeconds()
|
|
XCTAssertGreaterThan(remaining, 118)
|
|
XCTAssertLessThanOrEqual(remaining, 120)
|
|
}
|
|
|
|
func testRemainingSecondsPaused() {
|
|
let target = Date().addingTimeInterval(300)
|
|
let snapshot = TimerSnapshot(
|
|
id: "t2", label: "Paused", type: .countdown, urgency: .standard,
|
|
state: .paused, targetTime: target, duration: 600,
|
|
startedAt: Date(), elapsedBeforePause: 200, snoozeCount: 0,
|
|
category: nil, pomodoroCurrentRound: nil, pomodoroTotalRounds: nil,
|
|
pomodoroIsBreak: nil, nextWarningTime: nil, totalWarnings: 0, firedWarnings: 0
|
|
)
|
|
|
|
// When paused, remaining = duration - elapsedBeforePause
|
|
let remaining = snapshot.remainingSeconds()
|
|
XCTAssertEqual(remaining, 400)
|
|
}
|
|
|
|
func testHasFired() {
|
|
let pastTarget = Date().addingTimeInterval(-10)
|
|
let snapshot = TimerSnapshot(
|
|
id: "t3", label: "Fired", type: .alarm, urgency: .critical,
|
|
state: .firing, targetTime: pastTarget, duration: nil,
|
|
startedAt: nil, elapsedBeforePause: 0, snoozeCount: 0,
|
|
category: nil, pomodoroCurrentRound: nil, pomodoroTotalRounds: nil,
|
|
pomodoroIsBreak: nil, nextWarningTime: nil, totalWarnings: 0, firedWarnings: 0
|
|
)
|
|
|
|
XCTAssertTrue(snapshot.hasFired())
|
|
}
|
|
|
|
func testHasNotFired() {
|
|
let futureTarget = Date().addingTimeInterval(3600)
|
|
let snapshot = TimerSnapshot(
|
|
id: "t4", label: "Active", type: .alarm, urgency: .standard,
|
|
state: .active, targetTime: futureTarget, duration: nil,
|
|
startedAt: nil, elapsedBeforePause: 0, snoozeCount: 0,
|
|
category: nil, pomodoroCurrentRound: nil, pomodoroTotalRounds: nil,
|
|
pomodoroIsBreak: nil, nextWarningTime: nil, totalWarnings: 0, firedWarnings: 0
|
|
)
|
|
|
|
XCTAssertFalse(snapshot.hasFired())
|
|
}
|
|
|
|
func testRemainingCompact() {
|
|
let target = Date().addingTimeInterval(3600)
|
|
let snapshot = TimerSnapshot(
|
|
id: "t5", label: "Hour", type: .countdown, urgency: .standard,
|
|
state: .active, targetTime: target, duration: 3600,
|
|
startedAt: Date(), elapsedBeforePause: 0, snoozeCount: 0,
|
|
category: nil, pomodoroCurrentRound: nil, pomodoroTotalRounds: nil,
|
|
pomodoroIsBreak: nil, nextWarningTime: nil, totalWarnings: 0, firedWarnings: 0
|
|
)
|
|
|
|
let compact = snapshot.remainingCompact()
|
|
// Should be something like "59m" or "1h"
|
|
XCTAssertFalse(compact.isEmpty)
|
|
}
|
|
|
|
// MARK: - Snapshot Codable
|
|
|
|
func testSnapshotEncodeDecode() throws {
|
|
let snapshot = TimerSnapshot(
|
|
id: "codec-test", label: "Encode Me", type: .pomodoro, urgency: .critical,
|
|
state: .warning, targetTime: Date().addingTimeInterval(500), duration: 1500,
|
|
startedAt: Date(), elapsedBeforePause: 100, snoozeCount: 1,
|
|
category: "work", pomodoroCurrentRound: 3, pomodoroTotalRounds: 4,
|
|
pomodoroIsBreak: false, nextWarningTime: Date().addingTimeInterval(200),
|
|
totalWarnings: 3, firedWarnings: 2
|
|
)
|
|
|
|
let encoder = JSONEncoder()
|
|
let data = try encoder.encode(snapshot)
|
|
|
|
let decoder = JSONDecoder()
|
|
let decoded = try decoder.decode(TimerSnapshot.self, from: data)
|
|
|
|
XCTAssertEqual(decoded.id, "codec-test")
|
|
XCTAssertEqual(decoded.label, "Encode Me")
|
|
XCTAssertEqual(decoded.type, .pomodoro)
|
|
XCTAssertEqual(decoded.urgency, .critical)
|
|
XCTAssertEqual(decoded.state, .warning)
|
|
XCTAssertEqual(decoded.duration, 1500)
|
|
XCTAssertEqual(decoded.elapsedBeforePause, 100)
|
|
XCTAssertEqual(decoded.snoozeCount, 1)
|
|
XCTAssertEqual(decoded.category, "work")
|
|
XCTAssertEqual(decoded.pomodoroCurrentRound, 3)
|
|
XCTAssertEqual(decoded.pomodoroTotalRounds, 4)
|
|
XCTAssertEqual(decoded.pomodoroIsBreak, false)
|
|
XCTAssertEqual(decoded.totalWarnings, 3)
|
|
XCTAssertEqual(decoded.firedWarnings, 2)
|
|
}
|
|
|
|
func testSnapshotArrayEncodeDecode() throws {
|
|
let snapshots = [
|
|
TimerSnapshot(
|
|
id: "a1", label: "Timer A", type: .alarm, urgency: .important,
|
|
state: .active, targetTime: Date().addingTimeInterval(1000), duration: nil,
|
|
startedAt: nil, elapsedBeforePause: 0, snoozeCount: 0,
|
|
category: nil, pomodoroCurrentRound: nil, pomodoroTotalRounds: nil,
|
|
pomodoroIsBreak: nil, nextWarningTime: nil, totalWarnings: 0, firedWarnings: 0
|
|
),
|
|
TimerSnapshot(
|
|
id: "a2", label: "Timer B", type: .countdown, urgency: .gentle,
|
|
state: .paused, targetTime: Date().addingTimeInterval(2000), duration: 2000,
|
|
startedAt: Date(), elapsedBeforePause: 500, snoozeCount: 0,
|
|
category: nil, pomodoroCurrentRound: nil, pomodoroTotalRounds: nil,
|
|
pomodoroIsBreak: nil, nextWarningTime: nil, totalWarnings: 1, firedWarnings: 0
|
|
),
|
|
]
|
|
|
|
let data = try JSONEncoder().encode(snapshots)
|
|
let decoded = try JSONDecoder().decode([TimerSnapshot].self, from: data)
|
|
|
|
XCTAssertEqual(decoded.count, 2)
|
|
XCTAssertEqual(decoded[0].id, "a1")
|
|
XCTAssertEqual(decoded[1].id, "a2")
|
|
XCTAssertEqual(decoded[1].state, .paused)
|
|
}
|
|
|
|
// MARK: - Equatable
|
|
|
|
func testSnapshotEquatable() {
|
|
let s1 = TimerSnapshot(
|
|
id: "eq1", label: "A", type: .alarm, urgency: .standard,
|
|
state: .active, targetTime: Date(), duration: nil,
|
|
startedAt: nil, elapsedBeforePause: 0, snoozeCount: 0,
|
|
category: nil, pomodoroCurrentRound: nil, pomodoroTotalRounds: nil,
|
|
pomodoroIsBreak: nil, nextWarningTime: nil, totalWarnings: 0, firedWarnings: 0
|
|
)
|
|
let s2 = TimerSnapshot(
|
|
id: "eq1", label: "B", type: .countdown, urgency: .critical,
|
|
state: .firing, targetTime: Date(), duration: 100,
|
|
startedAt: nil, elapsedBeforePause: 0, snoozeCount: 0,
|
|
category: nil, pomodoroCurrentRound: nil, pomodoroTotalRounds: nil,
|
|
pomodoroIsBreak: nil, nextWarningTime: nil, totalWarnings: 0, firedWarnings: 0
|
|
)
|
|
let s3 = TimerSnapshot(
|
|
id: "eq2", label: "A", type: .alarm, urgency: .standard,
|
|
state: .active, targetTime: Date(), duration: nil,
|
|
startedAt: nil, elapsedBeforePause: 0, snoozeCount: 0,
|
|
category: nil, pomodoroCurrentRound: nil, pomodoroTotalRounds: nil,
|
|
pomodoroIsBreak: nil, nextWarningTime: nil, totalWarnings: 0, firedWarnings: 0
|
|
)
|
|
|
|
// Same id = equal
|
|
XCTAssertEqual(s1, s2)
|
|
// Different id = not equal
|
|
XCTAssertNotEqual(s1, s3)
|
|
}
|
|
}
|