Christopher Hotchkiss Christopher Hotchkiss
Login

Code

remindwall / RWPureSwift / Tests / EditSettingsNewTests / TopLevel / ScreenOffSettingTests.swift

Back to remindwall — Current RemindWall

Download this file

import AppTypes
import ComposableArchitecture
import CoreGraphics
import Dao
import DependenciesTestSupport
import Foundation
import ScreenControl
import Testing

@testable import EditSettingsNew_TopLevel

@MainActor
@Suite("ScreenOffSetting Feature Tests", .dependencies {
    // .syncedSetting reads the database at State init and stamps lastModified
    // on every schedule write.
    $0.defaultDatabase = try! $0.appDatabase()
    $0.uuid = .incrementing
    $0.date = .constant(Date(timeIntervalSince1970: 0))
})
struct ScreenOffSettingTests {

    @Test("initial state has nil schedule")
    func initialState() async {
        let state = ScreenOffSettingFeature.State()
        #expect(state.schedule == nil)
    }

    // MARK: - Set Actions

    @Test("setStartTime updates start time directly")
    func setStartTime() async {
        var state = ScreenOffSettingFeature.State()
        state.$schedule.withLock { $0 = ScreenOffSchedule(startHour: 22, startMinute: 0, endHour: 6, endMinute: 0) }

        let store = TestStore(initialState: state) {
            ScreenOffSettingFeature()
        }

        await store.send(.setStartTime(hour: 21, minute: 30)) {
            $0.$schedule.withLock { $0 = ScreenOffSchedule(startHour: 21, startMinute: 30, endHour: 6, endMinute: 0) }
        }
    }

    @Test("setEndTime updates end time directly")
    func setEndTime() async {
        var state = ScreenOffSettingFeature.State()
        state.$schedule.withLock { $0 = ScreenOffSchedule(startHour: 22, startMinute: 0, endHour: 6, endMinute: 0) }

        let store = TestStore(initialState: state) {
            ScreenOffSettingFeature()
        }

        await store.send(.setEndTime(hour: 7, minute: 15)) {
            $0.$schedule.withLock { $0 = ScreenOffSchedule(startHour: 22, startMinute: 0, endHour: 7, endMinute: 15) }
        }
    }

    @Test("setSchedule updates both times directly")
    func setSchedule() async {
        var state = ScreenOffSettingFeature.State()
        state.$schedule.withLock { $0 = ScreenOffSchedule(startHour: 22, startMinute: 0, endHour: 6, endMinute: 0) }

        let store = TestStore(initialState: state) {
            ScreenOffSettingFeature()
        }

        await store.send(.setSchedule(startHour: 23, startMinute: 30, endHour: 7, endMinute: 45)) {
            $0.$schedule.withLock { $0 = ScreenOffSchedule(startHour: 23, startMinute: 30, endHour: 7, endMinute: 45) }
        }
    }

    @Test("setStartTime with nil schedule produces no state changes")
    func setStartTimeNilSchedule() async {
        let store = TestStore(initialState: ScreenOffSettingFeature.State()) {
            ScreenOffSettingFeature()
        }
        await store.send(.setStartTime(hour: 10, minute: 30))
    }

    @Test("setStartTime clamps negative values")
    func setStartTimeClamps() async {
        var state = ScreenOffSettingFeature.State()
        state.$schedule.withLock { $0 = ScreenOffSchedule(startHour: 22, startMinute: 0, endHour: 6, endMinute: 0) }

        let store = TestStore(initialState: state) {
            ScreenOffSettingFeature()
        }

        await store.send(.setStartTime(hour: -1, minute: -5)) {
            $0.$schedule.withLock { $0 = ScreenOffSchedule(startHour: 23, startMinute: 55, endHour: 6, endMinute: 0) }
        }
    }

    // MARK: - Nil schedule guard

    @Test("actions with nil schedule produce no state changes")
    func nilScheduleNoOp() async {
        let store = TestStore(initialState: ScreenOffSettingFeature.State()) {
            ScreenOffSettingFeature()
        }

        await store.send(.setStartTime(hour: 10, minute: 30))
        await store.send(.setEndTime(hour: 7, minute: 0))
        await store.send(.setSchedule(startHour: 22, startMinute: 0, endHour: 6, endMinute: 0))
    }

    // MARK: - Test Screen Off

    @Test("testScreenOff dims and restores brightness with countdown")
    func testScreenOff() async {
        var state = ScreenOffSettingFeature.State()
        state.$schedule.withLock { $0 = .default }

        let brightnessValues = LockIsolated<[CGFloat]>([])
        let clock = TestClock()

        let store = TestStore(initialState: state) {
            ScreenOffSettingFeature()
        } withDependencies: {
            $0.continuousClock = clock
            $0.screenControl.getBrightness = { 0.75 }
            $0.screenControl.setBrightness = { value in
                brightnessValues.withValue { $0.append(value) }
            }
        }

        await store.send(.testScreenOff) {
            $0.isTesting = true
            $0.testStatus = "Saving brightness…"
        }

        // Receive "Dimming screen…" progress
        await store.receive(\._testProgress) {
            $0.testStatus = "Dimming screen…"
        }

        // Advance through the 10-second countdown
        for remaining in stride(from: 10, through: 1, by: -1) {
            await store.receive(\._testProgress) {
                $0.testStatus = "Screen off — restoring in \(remaining)s"
            }
            await clock.advance(by: .seconds(1))
        }

        // Receive "Restoring brightness…" progress
        await store.receive(\._testProgress) {
            $0.testStatus = "Restoring brightness…"
        }

        await store.receive(\._testComplete) {
            $0.isTesting = false
            $0.testStatus = nil
        }

        #expect(brightnessValues.value == [0.0, 0.75])
    }

    @Test("testScreenOff ignored while already testing")
    func testScreenOffWhileTesting() async {
        var state = ScreenOffSettingFeature.State()
        state.$schedule.withLock { $0 = .default }
        state.isTesting = true

        let store = TestStore(initialState: state) {
            ScreenOffSettingFeature()
        }

        await store.send(.testScreenOff)
    }
}