import Foundation import Testing import RemoteCore @testable import GestureCore @Test func directionsRequireArmingAndNeutralThenEmitOnce() { let samples: [(Double, Double, RemoteCommand)] = [(0.9, 0, .right), (-0.9, 0, .left), (0, 0.9, .up), (0, -0.9, .down)] for (x, y, command) in samples { var gate = DirectionalGestureGate() #expect(gate.process(x: x, y: y, at: 0) == nil) let session = UUID() gate.arm(sessionID: session, at: 0) #expect(gate.process(x: x, y: y, at: 0.1) == nil) #expect(gate.process(x: 0, y: 0, at: 0.2) == nil) let intent = gate.process(x: x, y: y, at: 0.3) #expect(intent?.command == command) #expect(intent?.sessionID == session) #expect(gate.process(x: x, y: y, at: 1) == nil) #expect(gate.process(x: -x, y: -y, at: 2) == nil) } } @Test func hysteresisAndDebounceConsumeSuppressedExcursion() { var gate = DirectionalGestureGate() gate.arm(sessionID: UUID(), at: 0) #expect(gate.process(x: 0, y: 0, at: 0.1) == nil) #expect(gate.process(x: 0.9, y: 0, at: 0.2)?.command == .right) #expect(gate.process(x: 0.4, y: 0, at: 0.3) == nil) #expect(gate.process(x: 0.9, y: 0, at: 0.35) == nil) #expect(gate.process(x: 0, y: 0, at: 0.4) == nil) #expect(gate.process(x: -0.9, y: 0, at: 0.45) == nil) #expect(gate.process(x: -0.9, y: 0, at: 1) == nil) #expect(gate.process(x: 0, y: 0, at: 1.1) == nil) #expect(gate.process(x: -0.9, y: 0, at: 1.2)?.command == .left) } @Test func ambiguityInvalidInputAndOutOfOrderSamplesAreSuppressed() { var gate = DirectionalGestureGate() gate.arm(sessionID: UUID(), at: 0) #expect(gate.process(x: 0, y: 0, at: 0.1) == nil) #expect(gate.process(x: 0.9, y: 0.9, at: 0.2) == nil) #expect(gate.process(x: 0.9, y: 0, at: 0.3) == nil) #expect(gate.process(x: 0, y: 0, at: 0.4) == nil) #expect(gate.process(x: .nan, y: 0, at: 0.5) == nil) #expect(gate.process(x: 0.9, y: 0, at: 0.6) == nil) #expect(gate.process(x: 0, y: 0, at: 1) == nil) #expect(gate.process(x: 0.9, y: 0, at: 0.9) == nil) #expect(gate.process(x: 0.9, y: 0, at: 1.1)?.command == .right) } @Test func timeoutAndDisarmPreventActionsAndRearmingRequiresNeutral() { var gate = DirectionalGestureGate() gate.arm(sessionID: UUID(), at: 0) #expect(gate.process(x: 0, y: 0, at: 1) == nil) #expect(gate.process(x: 0.9, y: 0, at: 10) == nil) #expect(gate.sessionID == nil) gate.arm(sessionID: UUID(), at: 20) #expect(gate.process(x: 0.9, y: 0, at: 20.1) == nil) #expect(gate.process(x: 0, y: 0, at: 20.2) == nil) gate.disarm() #expect(gate.process(x: 0.9, y: 0, at: 20.3) == nil) } @Test func rejectsInvalidConfiguration() { #expect(throws: GestureConfiguration.ConfigurationError.self) { try GestureConfiguration(activationThreshold: 0.2, neutralThreshold: 0.3, minimumInterval: 0.3, maximumArmedDuration: 10, axisDominanceRatio: 0.6) } }