general: working through bugs and maintainability
This commit is contained in:
@@ -6,15 +6,13 @@
|
||||
//
|
||||
|
||||
import Combine
|
||||
import CoreGraphics
|
||||
import XCTest
|
||||
@testable import Gaze
|
||||
|
||||
@MainActor
|
||||
final class FullscreenDetectionServiceTests: XCTestCase {
|
||||
func testPermissionDeniedKeepsStateFalse() {
|
||||
let mockManager = MockPermissionManager(status: ScreenCaptureAuthorizationStatus.denied)
|
||||
let service = FullscreenDetectionService(permissionManager: mockManager)
|
||||
let service = FullscreenDetectionService(permissionManager: MockPermissionManager(status: .denied))
|
||||
|
||||
let expectation = expectation(description: "No change")
|
||||
expectation.isInverted = true
|
||||
@@ -30,77 +28,6 @@ final class FullscreenDetectionServiceTests: XCTestCase {
|
||||
wait(for: [expectation], timeout: 0.5)
|
||||
cancellable.cancel()
|
||||
}
|
||||
|
||||
func testFullscreenStateBecomesTrueWhenWindowMatchesScreen() {
|
||||
let mockManager = MockPermissionManager(status: ScreenCaptureAuthorizationStatus.authorized)
|
||||
let environment = MockFullscreenEnvironment(
|
||||
frontmostPID: 42,
|
||||
windowDescriptors: [
|
||||
FullscreenWindowDescriptor(
|
||||
ownerPID: 42,
|
||||
layer: 0,
|
||||
bounds: CGRect(x: 0, y: 0, width: 1920, height: 1080)
|
||||
)
|
||||
],
|
||||
screenFrames: [CGRect(x: 0, y: 0, width: 1920, height: 1080)]
|
||||
)
|
||||
|
||||
let service = FullscreenDetectionService(
|
||||
permissionManager: mockManager,
|
||||
environmentProvider: environment
|
||||
)
|
||||
|
||||
let expectation = expectation(description: "Fullscreen detected")
|
||||
|
||||
let cancellable = service.$isFullscreenActive
|
||||
.dropFirst()
|
||||
.sink { isActive in
|
||||
if isActive {
|
||||
expectation.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
service.forceUpdate()
|
||||
|
||||
wait(for: [expectation], timeout: 0.5)
|
||||
cancellable.cancel()
|
||||
}
|
||||
|
||||
func testFullscreenStateStaysFalseWhenWindowDoesNotMatchScreen() {
|
||||
let mockManager = MockPermissionManager(status: ScreenCaptureAuthorizationStatus.authorized)
|
||||
let environment = MockFullscreenEnvironment(
|
||||
frontmostPID: 42,
|
||||
windowDescriptors: [
|
||||
FullscreenWindowDescriptor(
|
||||
ownerPID: 42,
|
||||
layer: 0,
|
||||
bounds: CGRect(x: 100, y: 100, width: 800, height: 600)
|
||||
)
|
||||
],
|
||||
screenFrames: [CGRect(x: 0, y: 0, width: 1920, height: 1080)]
|
||||
)
|
||||
|
||||
let service = FullscreenDetectionService(
|
||||
permissionManager: mockManager,
|
||||
environmentProvider: environment
|
||||
)
|
||||
|
||||
let expectation = expectation(description: "No fullscreen")
|
||||
expectation.isInverted = true
|
||||
|
||||
let cancellable = service.$isFullscreenActive
|
||||
.dropFirst()
|
||||
.sink { isActive in
|
||||
if isActive {
|
||||
expectation.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
service.forceUpdate()
|
||||
|
||||
wait(for: [expectation], timeout: 0.5)
|
||||
cancellable.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
|
||||
@@ -44,12 +44,13 @@ final class IntegrationTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testDisablingTimerRemovesFromEngine() {
|
||||
settingsManager.settings.blinkTimer.enabled = true
|
||||
timerEngine.start()
|
||||
XCTAssertNotNil(timerEngine.timerStates[.builtIn(.blink)])
|
||||
|
||||
var config = TimerConfiguration(enabled: false, intervalSeconds: 5 * 60)
|
||||
settingsManager.updateTimerConfiguration(for: .blink, configuration: config)
|
||||
|
||||
// Stop and restart to apply the disabled setting
|
||||
timerEngine.stop()
|
||||
settingsManager.settings.blinkTimer.enabled = false
|
||||
timerEngine.start()
|
||||
XCTAssertNil(timerEngine.timerStates[.builtIn(.blink)])
|
||||
}
|
||||
@@ -100,17 +101,18 @@ final class IntegrationTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testResetToDefaultsAffectsTimerEngine() {
|
||||
let config = TimerConfiguration(enabled: false, intervalSeconds: 5 * 60)
|
||||
settingsManager.updateTimerConfiguration(for: .blink, configuration: config)
|
||||
|
||||
// Blink is disabled by default, enable it first
|
||||
settingsManager.settings.blinkTimer.enabled = true
|
||||
timerEngine.start()
|
||||
XCTAssertNil(timerEngine.timerStates[.builtIn(.blink)])
|
||||
XCTAssertNotNil(timerEngine.timerStates[.builtIn(.blink)])
|
||||
|
||||
// Reset to defaults (blink disabled)
|
||||
timerEngine.stop()
|
||||
settingsManager.resetToDefaults()
|
||||
timerEngine.start()
|
||||
|
||||
XCTAssertNotNil(timerEngine.timerStates[.builtIn(.blink)])
|
||||
XCTAssertEqual(timerEngine.timerStates[.builtIn(.blink)]?.remainingSeconds, 5 * 60)
|
||||
// Blink should now be disabled (per defaults)
|
||||
XCTAssertNil(timerEngine.timerStates[.builtIn(.blink)])
|
||||
}
|
||||
|
||||
func testTimerEngineRespectsDisabledTimers() {
|
||||
@@ -124,6 +126,8 @@ final class IntegrationTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testCompleteWorkflow() {
|
||||
// Enable all timers for this test
|
||||
settingsManager.settings.blinkTimer.enabled = true
|
||||
timerEngine.start()
|
||||
|
||||
XCTAssertEqual(timerEngine.timerStates.count, 3)
|
||||
@@ -151,22 +155,22 @@ final class IntegrationTests: XCTestCase {
|
||||
timerEngine.triggerReminder(for: .builtIn(.lookAway))
|
||||
XCTAssertNotNil(timerEngine.activeReminder)
|
||||
|
||||
for (_, state) in timerEngine.timerStates {
|
||||
XCTAssertTrue(state.isPaused)
|
||||
}
|
||||
// Only the triggered timer should be paused
|
||||
XCTAssertTrue(timerEngine.isTimerPaused(.builtIn(.lookAway)))
|
||||
|
||||
timerEngine.dismissReminder()
|
||||
XCTAssertNil(timerEngine.activeReminder)
|
||||
|
||||
for (_, state) in timerEngine.timerStates {
|
||||
XCTAssertFalse(state.isPaused)
|
||||
}
|
||||
// The triggered timer should be resumed
|
||||
XCTAssertFalse(timerEngine.isTimerPaused(.builtIn(.lookAway)))
|
||||
}
|
||||
|
||||
func testSettingsAutoSaveIntegration() {
|
||||
let config = TimerConfiguration(enabled: false, intervalSeconds: 900)
|
||||
settingsManager.updateTimerConfiguration(for: .lookAway, configuration: config)
|
||||
|
||||
// Force save to persist immediately (settings debounce by 500ms normally)
|
||||
settingsManager.save()
|
||||
settingsManager.load()
|
||||
|
||||
let loadedConfig = settingsManager.timerConfiguration(for: .lookAway)
|
||||
|
||||
@@ -17,8 +17,8 @@ final class AppSettingsTests: XCTestCase {
|
||||
XCTAssertEqual(settings.lookAwayTimer.intervalSeconds, 20 * 60)
|
||||
XCTAssertEqual(settings.lookAwayCountdownSeconds, 20)
|
||||
|
||||
XCTAssertTrue(settings.blinkTimer.enabled)
|
||||
XCTAssertEqual(settings.blinkTimer.intervalSeconds, 5 * 60)
|
||||
XCTAssertFalse(settings.blinkTimer.enabled)
|
||||
XCTAssertEqual(settings.blinkTimer.intervalSeconds, 7 * 60)
|
||||
|
||||
XCTAssertTrue(settings.postureTimer.enabled)
|
||||
XCTAssertEqual(settings.postureTimer.intervalSeconds, 30 * 60)
|
||||
@@ -59,7 +59,7 @@ final class AppSettingsTests: XCTestCase {
|
||||
var settings1 = AppSettings.defaults
|
||||
var settings2 = AppSettings.defaults
|
||||
|
||||
settings2.blinkTimer.enabled = false
|
||||
settings2.blinkTimer.enabled = true
|
||||
XCTAssertNotEqual(settings1, settings2)
|
||||
}
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@ final class SettingsManagerTests: XCTestCase {
|
||||
XCTAssertEqual(defaults.lookAwayTimer.intervalSeconds, 20 * 60)
|
||||
XCTAssertEqual(defaults.lookAwayCountdownSeconds, 20)
|
||||
|
||||
XCTAssertTrue(defaults.blinkTimer.enabled)
|
||||
XCTAssertEqual(defaults.blinkTimer.intervalSeconds, 5 * 60)
|
||||
XCTAssertFalse(defaults.blinkTimer.enabled)
|
||||
XCTAssertEqual(defaults.blinkTimer.intervalSeconds, 7 * 60)
|
||||
|
||||
XCTAssertTrue(defaults.postureTimer.enabled)
|
||||
XCTAssertEqual(defaults.postureTimer.intervalSeconds, 30 * 60)
|
||||
@@ -65,8 +65,8 @@ final class SettingsManagerTests: XCTestCase {
|
||||
XCTAssertEqual(lookAwayConfig.intervalSeconds, 20 * 60)
|
||||
|
||||
let blinkConfig = settingsManager.timerConfiguration(for: .blink)
|
||||
XCTAssertTrue(blinkConfig.enabled)
|
||||
XCTAssertEqual(blinkConfig.intervalSeconds, 5 * 60)
|
||||
XCTAssertFalse(blinkConfig.enabled)
|
||||
XCTAssertEqual(blinkConfig.intervalSeconds, 7 * 60)
|
||||
|
||||
let postureConfig = settingsManager.timerConfiguration(for: .posture)
|
||||
XCTAssertTrue(postureConfig.enabled)
|
||||
|
||||
@@ -29,6 +29,8 @@ final class TimerEngineTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testTimerInitialization() {
|
||||
// Enable all timers for this test (blink is disabled by default)
|
||||
settingsManager.settings.blinkTimer.enabled = true
|
||||
timerEngine.start()
|
||||
|
||||
XCTAssertEqual(timerEngine.timerStates.count, 3)
|
||||
@@ -38,8 +40,7 @@ final class TimerEngineTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testDisabledTimersNotInitialized() {
|
||||
settingsManager.settings.blinkTimer.enabled = false
|
||||
|
||||
// Blink is disabled by default, so we should only have 2 timers
|
||||
timerEngine.start()
|
||||
|
||||
XCTAssertEqual(timerEngine.timerStates.count, 2)
|
||||
@@ -59,6 +60,7 @@ final class TimerEngineTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testPauseAllTimers() {
|
||||
settingsManager.settings.blinkTimer.enabled = true
|
||||
timerEngine.start()
|
||||
timerEngine.pause()
|
||||
|
||||
@@ -68,6 +70,7 @@ final class TimerEngineTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testResumeAllTimers() {
|
||||
settingsManager.settings.blinkTimer.enabled = true
|
||||
timerEngine.start()
|
||||
timerEngine.pause()
|
||||
timerEngine.resume()
|
||||
@@ -120,6 +123,8 @@ final class TimerEngineTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testDismissReminderResetsTimer() {
|
||||
settingsManager.settings.blinkTimer.enabled = true
|
||||
settingsManager.settings.blinkTimer.intervalSeconds = 7 * 60
|
||||
timerEngine.start()
|
||||
timerEngine.timerStates[.builtIn(.blink)]?.remainingSeconds = 0
|
||||
timerEngine.activeReminder = .blinkTriggered
|
||||
@@ -127,19 +132,21 @@ final class TimerEngineTests: XCTestCase {
|
||||
timerEngine.dismissReminder()
|
||||
|
||||
XCTAssertNil(timerEngine.activeReminder)
|
||||
XCTAssertEqual(timerEngine.timerStates[.builtIn(.blink)]?.remainingSeconds, 5 * 60)
|
||||
XCTAssertEqual(timerEngine.timerStates[.builtIn(.blink)]?.remainingSeconds, 7 * 60)
|
||||
}
|
||||
|
||||
func testDismissLookAwayResumesTimers() {
|
||||
func testDismissLookAwayResumesTimer() {
|
||||
timerEngine.start()
|
||||
timerEngine.activeReminder = .lookAwayTriggered(countdownSeconds: 20)
|
||||
timerEngine.pause()
|
||||
// Trigger reminder pauses only the lookAway timer
|
||||
timerEngine.triggerReminder(for: .builtIn(.lookAway))
|
||||
|
||||
XCTAssertNotNil(timerEngine.activeReminder)
|
||||
XCTAssertTrue(timerEngine.isTimerPaused(.builtIn(.lookAway)))
|
||||
|
||||
timerEngine.dismissReminder()
|
||||
|
||||
for (_, state) in timerEngine.timerStates {
|
||||
XCTAssertFalse(state.isPaused)
|
||||
}
|
||||
// After dismiss, the lookAway timer should be resumed
|
||||
XCTAssertFalse(timerEngine.isTimerPaused(.builtIn(.lookAway)))
|
||||
}
|
||||
|
||||
func testTriggerReminderForLookAway() {
|
||||
@@ -154,12 +161,12 @@ final class TimerEngineTests: XCTestCase {
|
||||
XCTFail("Expected lookAwayTriggered reminder")
|
||||
}
|
||||
|
||||
for (_, state) in timerEngine.timerStates {
|
||||
XCTAssertTrue(state.isPaused)
|
||||
}
|
||||
// Only the triggered timer should be paused
|
||||
XCTAssertTrue(timerEngine.isTimerPaused(.builtIn(.lookAway)))
|
||||
}
|
||||
|
||||
func testTriggerReminderForBlink() {
|
||||
settingsManager.settings.blinkTimer.enabled = true
|
||||
timerEngine.start()
|
||||
|
||||
timerEngine.triggerReminder(for: .builtIn(.blink))
|
||||
@@ -214,13 +221,16 @@ final class TimerEngineTests: XCTestCase {
|
||||
XCTAssertEqual(formatted, "1:00:00")
|
||||
}
|
||||
|
||||
func testMultipleStartCallsResetTimers() {
|
||||
func testMultipleStartCallsPreserveTimerState() {
|
||||
// When start() is called multiple times while already running,
|
||||
// it should preserve existing timer state (not reset)
|
||||
timerEngine.start()
|
||||
timerEngine.timerStates[.builtIn(.lookAway)]?.remainingSeconds = 100
|
||||
|
||||
timerEngine.start()
|
||||
|
||||
XCTAssertEqual(timerEngine.timerStates[.builtIn(.lookAway)]?.remainingSeconds, 20 * 60)
|
||||
// Timer state is preserved since interval hasn't changed
|
||||
XCTAssertEqual(timerEngine.timerStates[.builtIn(.lookAway)]?.remainingSeconds, 100)
|
||||
}
|
||||
|
||||
func testSkipNextPreservesPausedState() {
|
||||
@@ -249,26 +259,25 @@ final class TimerEngineTests: XCTestCase {
|
||||
XCTAssertNil(timerEngine.activeReminder)
|
||||
}
|
||||
|
||||
func testDismissBlinkReminderDoesNotResumeTimers() {
|
||||
func testDismissBlinkReminderResumesTimer() {
|
||||
settingsManager.settings.blinkTimer.enabled = true
|
||||
timerEngine.start()
|
||||
timerEngine.activeReminder = .blinkTriggered
|
||||
timerEngine.triggerReminder(for: .builtIn(.blink))
|
||||
|
||||
timerEngine.dismissReminder()
|
||||
|
||||
for (_, state) in timerEngine.timerStates {
|
||||
XCTAssertFalse(state.isPaused)
|
||||
}
|
||||
// The blink timer should be resumed after dismissal
|
||||
XCTAssertFalse(timerEngine.isTimerPaused(.builtIn(.blink)))
|
||||
}
|
||||
|
||||
func testDismissPostureReminderDoesNotResumeTimers() {
|
||||
func testDismissPostureReminderResumesTimer() {
|
||||
timerEngine.start()
|
||||
timerEngine.activeReminder = .postureTriggered
|
||||
timerEngine.triggerReminder(for: .builtIn(.posture))
|
||||
|
||||
timerEngine.dismissReminder()
|
||||
|
||||
for (_, state) in timerEngine.timerStates {
|
||||
XCTAssertFalse(state.isPaused)
|
||||
}
|
||||
// The posture timer should be resumed after dismissal
|
||||
XCTAssertFalse(timerEngine.isTimerPaused(.builtIn(.posture)))
|
||||
}
|
||||
|
||||
func testAllTimersStartWhenEnabled() {
|
||||
|
||||
Reference in New Issue
Block a user