From 5e73ad61d7cd305407f8fd28cf79b521ab8f8695 Mon Sep 17 00:00:00 2001 From: jonaskahn Date: Fri, 16 Oct 2026 18:04:45 +0700 Subject: [PATCH] fix: make keyboard pipeline tests hermetic and assert NFC at scalar level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KeyboardInputPipelineProcessTests drove KeyboardInputPipeline without injecting spotlightVisibilityProvider, so the real Spotlight window detector decided the context. On GitHub-hosted runners a Spotlight-owned window is on-screen, which flipped the pipeline into Spotlight mode (selection replacement, no Chromium empty-character break) and failed five tests deterministically. Inject { false } in every context-sensitive construction; Spotlight-behavior tests already inject their own provider. VietnameseEngineEdgeCaseTests:640 compared NFD vs NFC with String ==, which is canonical-equivalence — always equal, so the precondition could never pass. Compare unicode scalars instead, and likewise harden the per-unit-NFC invariant and the wire-text NFC asserts in KeyboardInputPipelineProcessTests, which were vacuous for the same reason. --- .../KeyboardInputPipelineProcessTests.swift | 16 +++++++++++++--- EasyKeyTests/VietnameseEngineEdgeCaseTests.swift | 9 ++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/EasyKeyTests/KeyboardInputPipelineProcessTests.swift b/EasyKeyTests/KeyboardInputPipelineProcessTests.swift index 6a33e0b..67fa605 100644 --- a/EasyKeyTests/KeyboardInputPipelineProcessTests.swift +++ b/EasyKeyTests/KeyboardInputPipelineProcessTests.swift @@ -512,6 +512,7 @@ final class KeyboardInputPipelineProcessTests: XCTestCase { var posted: [CGEvent] = [] let pipeline = KeyboardInputPipeline( settings: settings, + spotlightVisibilityProvider: { false }, eventPoster: { event, _ in posted.append(event) } ) @@ -540,6 +541,7 @@ final class KeyboardInputPipelineProcessTests: XCTestCase { var posted: [CGEvent] = [] let pipeline = KeyboardInputPipeline( settings: settings, + spotlightVisibilityProvider: { false }, eventPoster: { event, _ in posted.append(event) } ) pipeline.setActiveApplication("com.google.Chrome") @@ -573,6 +575,7 @@ final class KeyboardInputPipelineProcessTests: XCTestCase { var posted: [CGEvent] = [] let pipeline = KeyboardInputPipeline( settings: settings, + spotlightVisibilityProvider: { false }, eventPoster: { event, _ in posted.append(event) } ) pipeline.setActiveApplication("com.google.Chrome") @@ -626,6 +629,7 @@ final class KeyboardInputPipelineProcessTests: XCTestCase { var posted: [CGEvent] = [] let pipeline = KeyboardInputPipeline( settings: settings, + spotlightVisibilityProvider: { false }, chromiumAddressBarDetector: chromiumAddressBarDetector, eventPoster: { event, _ in posted.append(event) } ) @@ -717,8 +721,8 @@ final class KeyboardInputPipelineProcessTests: XCTestCase { for event in posted where event.type == .keyDown { let text = unicodeText(of: event) XCTAssertEqual( - text, - text.precomposedStringWithCanonicalMapping, + text.unicodeScalars.map(\.value), + text.precomposedStringWithCanonicalMapping.unicodeScalars.map(\.value), "wire text must be precomposed NFC, got \(text.unicodeScalars.map { String(format: "U+%04X", $0.value) })" ) } @@ -748,6 +752,7 @@ final class KeyboardInputPipelineProcessTests: XCTestCase { var posted: [CGEvent] = [] let pipeline = KeyboardInputPipeline( settings: settings, + spotlightVisibilityProvider: { false }, eventPoster: { event, _ in posted.append(event) } ) pipeline.setActiveApplication("com.google.Chrome") @@ -758,7 +763,11 @@ final class KeyboardInputPipelineProcessTests: XCTestCase { } for event in posted where event.type == .keyDown { let text = unicodeText(of: event) - XCTAssertEqual(text, text.precomposedStringWithCanonicalMapping, "explicit combining mode still types NFC") + XCTAssertEqual( + text.unicodeScalars.map(\.value), + text.precomposedStringWithCanonicalMapping.unicodeScalars.map(\.value), + "explicit combining mode still types NFC" + ) } } @@ -771,6 +780,7 @@ final class KeyboardInputPipelineProcessTests: XCTestCase { var posted: [CGEvent] = [] let pipeline = KeyboardInputPipeline( settings: settings, + spotlightVisibilityProvider: { false }, eventPoster: { event, _ in posted.append(event) } ) pipeline.setActiveApplication("com.google.Chrome") diff --git a/EasyKeyTests/VietnameseEngineEdgeCaseTests.swift b/EasyKeyTests/VietnameseEngineEdgeCaseTests.swift index 383f434..98b6be6 100644 --- a/EasyKeyTests/VietnameseEngineEdgeCaseTests.swift +++ b/EasyKeyTests/VietnameseEngineEdgeCaseTests.swift @@ -637,10 +637,13 @@ final class VietnameseEngineEdgeCaseTests: XCTestCase { typeKeys(&engine, "tuyeenf") let units = engine.renderedUnits let text = engine.currentBuffer - XCTAssertNotEqual(text, text.precomposedStringWithCanonicalMapping, "sample must actually be decomposed") + XCTAssertTrue( + text.unicodeScalars.contains { (0x0300 ... 0x036F).contains($0.value) }, + "sample must actually be decomposed" + ) XCTAssertEqual( - units.map { $0.precomposedStringWithCanonicalMapping }.joined(), - text.precomposedStringWithCanonicalMapping + units.map { $0.precomposedStringWithCanonicalMapping }.joined().unicodeScalars.map(\.value), + text.precomposedStringWithCanonicalMapping.unicodeScalars.map(\.value) ) }