chore: adiciona .gitignore e commit.command

This commit is contained in:
João Henrique
2026-08-18 08:25:29 -04:00
parent 68958fde00
commit 8fca456ceb
215 changed files with 65752 additions and 0 deletions
+41
View File
@@ -0,0 +1,41 @@
import ComposableArchitecture
import Foundation
import XCTest
@testable import Hex
@MainActor
final class HistoryPlaybackTests: XCTestCase {
func testStoppingPlaybackCompletesWaitersExactlyOnce() async {
let controller = AudioPlayerController()
let waiter = Task {
await controller.waitForPlaybackToFinish()
}
await Task.yield()
controller.stop()
controller.stop()
await waiter.value
await controller.waitForPlaybackToFinish()
}
func testStalePlaybackFinishedDoesNotStopCurrentPlayback() async {
let transcriptID = UUID()
let playbackID = UUID()
let store = TestStore(
initialState: HistoryFeature.State(
transcriptionHistory: Shared(value: .init()),
playingTranscriptID: transcriptID,
playbackID: playbackID
)
) {
HistoryFeature()
}
await store.send(.playbackFinished(UUID()))
XCTAssertEqual(store.state.playingTranscriptID, transcriptID)
XCTAssertEqual(store.state.playbackID, playbackID)
}
}
+18
View File
@@ -0,0 +1,18 @@
import XCTest
@testable import Hex
final class KeyEventMonitorClientTests: XCTestCase {
func testRegisteringFirstHandlerWhenInputMonitoringIsDeniedDoesNotReenterQueue() {
let monitor = KeyEventMonitorClientLive(
accessibilityTrustProvider: { false },
accessibilityTrustPrompt: { false },
inputMonitoringTrustProvider: { false }
)
let token = monitor.handleKeyEvent { _ in false }
XCTAssertTrue(monitor.readState { true })
token.cancel()
}
}
+45
View File
@@ -0,0 +1,45 @@
import Foundation
import XCTest
@testable import Hex
final class LegacyModelCacheMigratorTests: XCTestCase {
func testReplacesIncompleteCurrentDirectoryWithValidLegacyDirectory() throws {
let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
let legacy = root.appendingPathComponent("model-coreml")
let current = root.appendingPathComponent("model")
defer { try? FileManager.default.removeItem(at: root) }
try FileManager.default.createDirectory(at: legacy, withIntermediateDirectories: true)
try FileManager.default.createDirectory(at: current, withIntermediateDirectories: true)
try Data("complete".utf8).write(to: legacy.appendingPathComponent("model.bin"))
try Data("partial".utf8).write(to: current.appendingPathComponent("model.bin.partial"))
let migrated = try LegacyModelCacheMigrator.migrate(from: legacy, to: current) {
FileManager.default.fileExists(atPath: $0.appendingPathComponent("model.bin").path)
}
XCTAssertTrue(migrated)
XCTAssertFalse(FileManager.default.fileExists(atPath: legacy.path))
XCTAssertTrue(FileManager.default.fileExists(atPath: current.appendingPathComponent("model.bin").path))
XCTAssertFalse(FileManager.default.fileExists(atPath: current.appendingPathComponent("model.bin.partial").path))
}
func testRestoresBothDirectoriesWhenLegacyDirectoryIsInvalid() throws {
let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
let legacy = root.appendingPathComponent("model-coreml")
let current = root.appendingPathComponent("model")
defer { try? FileManager.default.removeItem(at: root) }
try FileManager.default.createDirectory(at: legacy, withIntermediateDirectories: true)
try FileManager.default.createDirectory(at: current, withIntermediateDirectories: true)
try Data("legacy".utf8).write(to: legacy.appendingPathComponent("legacy.bin"))
try Data("partial".utf8).write(to: current.appendingPathComponent("partial.bin"))
XCTAssertThrowsError(
try LegacyModelCacheMigrator.migrate(from: legacy, to: current) { _ in false }
)
XCTAssertTrue(FileManager.default.fileExists(atPath: legacy.appendingPathComponent("legacy.bin").path))
XCTAssertTrue(FileManager.default.fileExists(atPath: current.appendingPathComponent("partial.bin").path))
}
}
+134
View File
@@ -0,0 +1,134 @@
import ComposableArchitecture
import HexCore
import XCTest
@testable import Hex
@MainActor
final class ModelDownloadFeatureTests: XCTestCase {
func testPersistedModelDisplaysWhileAvailabilityLoads() {
let state = makeState(selectedModel: ParakeetModel.englishV2.identifier)
XCTAssertTrue(state.availableModels.isEmpty)
XCTAssertEqual(state.selectedModelNameForDisplay, ParakeetModel.englishV2.identifier)
}
func testSuggestedParakeetFollowsOutputLanguage() {
var state = makeState(selectedModel: "")
XCTAssertEqual(state.preferredParakeetIdentifier, ParakeetModel.englishV2.identifier)
state.$hexSettings.withLock { $0.outputLanguage = "en" }
XCTAssertEqual(state.preferredParakeetIdentifier, ParakeetModel.englishV2.identifier)
state.$hexSettings.withLock { $0.outputLanguage = "fr" }
XCTAssertEqual(state.preferredParakeetIdentifier, ParakeetModel.multilingualV3.identifier)
}
func testFailedModelRefreshPreservesInstalledSelection() async {
var state = makeState(selectedModel: "installed-model")
state.availableModels = [ModelInfo(name: "installed-model", isDownloaded: true)]
let store = TestStore(initialState: state) {
ModelDownloadFeature()
} withDependencies: {
$0.transcription.getRecommendedModels = { throw TestError() }
$0.transcription.getAvailableModels = { [] }
}
await store.send(.fetchModels) {
$0.isLoadingModels = true
}
await store.receive(\.modelsLoadFailed) {
$0.isLoadingModels = false
}
XCTAssertEqual(store.state.hexSettings.selectedModel, "installed-model")
XCTAssertTrue(store.state.availableModels[id: "installed-model"]?.isDownloaded == true)
}
func testStaleDownloadUpdatesAreIgnored() async {
let currentID = UUID()
var state = makeState(selectedModel: "installed-model")
state.isDownloading = true
state.downloadingModelName = "new-model"
state.activeDownloadID = currentID
state.downloadProgress = 0.25
let store = TestStore(initialState: state) {
ModelDownloadFeature()
}
await store.send(.downloadProgress(id: UUID(), progress: 0.9))
await store.send(.downloadCompleted(id: UUID(), result: .success("other-model")))
XCTAssertEqual(store.state.activeDownloadID, currentID)
XCTAssertEqual(store.state.downloadProgress, 0.25)
XCTAssertEqual(store.state.hexSettings.selectedModel, "installed-model")
}
func testModelsLoadedNeverClearsSelectionWhenNothingDetected() async {
// Regression: 0.8.0 cleared selectedModel to "" when an availability scan
// came back empty (e.g. after FluidAudio moved its cache directory),
// permanently breaking transcription with no visible error.
var state = makeState(selectedModel: ParakeetModel.englishV2.identifier)
state.availableModels = []
let store = TestStore(initialState: state) {
ModelDownloadFeature()
}
store.exhaustivity = .off
await store.send(.modelsLoaded(recommended: "", available: []))
XCTAssertEqual(store.state.hexSettings.selectedModel, ParakeetModel.englishV2.identifier)
}
func testSelectedModelIsDownloadedMatchesPatterns() {
var state = makeState(selectedModel: "distil*large-v3")
state.availableModels = [
ModelInfo(name: "distil-whisper_distil-large-v3", isDownloaded: true)
]
XCTAssertTrue(state.selectedModelIsDownloaded)
state.availableModels = [
ModelInfo(name: "distil-whisper_distil-large-v3", isDownloaded: false)
]
XCTAssertFalse(state.selectedModelIsDownloaded)
var emptyState = makeState(selectedModel: "")
emptyState.availableModels = [ModelInfo(name: "some-model", isDownloaded: true)]
XCTAssertFalse(emptyState.selectedModelIsDownloaded)
}
func testDeletingAnotherModelDoesNotChangeSelection() async {
var state = makeState(selectedModel: "selected-model")
state.availableModels = [
ModelInfo(name: "selected-model", isDownloaded: true),
ModelInfo(name: "other-model", isDownloaded: true),
]
let store = TestStore(initialState: state) {
ModelDownloadFeature()
} withDependencies: {
$0.transcription.deleteModel = { _ in
try await Task.sleep(for: .seconds(60))
}
}
await store.send(.deleteModel("other-model"))
XCTAssertEqual(store.state.hexSettings.selectedModel, "selected-model")
await store.skipInFlightEffects()
}
private func makeState(selectedModel: String) -> ModelDownloadFeature.State {
var settings = HexSettings()
settings.selectedModel = selectedModel
settings.hasCompletedModelBootstrap = true
return ModelDownloadFeature.State(
hexSettings: Shared(value: settings),
modelBootstrapState: Shared(value: .init(isModelReady: true))
)
}
}
private struct TestError: Error {}
+374
View File
@@ -0,0 +1,374 @@
import AppKit
import ComposableArchitecture
import Foundation
import HexCore
import XCTest
@testable import Hex
@MainActor
final class RecordingRaceTests: XCTestCase {
func testNewRecordingCancelsPendingDiscardCleanup() async throws {
let now = Date(timeIntervalSince1970: 1_234)
let activeApp = NSWorkspace.shared.frontmostApplication
let stopURL = FileManager.default.temporaryDirectory
.appendingPathComponent("discard-cleanup-\(UUID().uuidString).wav")
let created = FileManager.default.createFile(
atPath: stopURL.path,
contents: Data("test".utf8)
)
XCTAssertTrue(created)
defer { try? FileManager.default.removeItem(at: stopURL) }
let probe = RecordingProbe(stopURL: stopURL)
let store = TestStore(initialState: Self.makeState()) {
TranscriptionFeature()
} withDependencies: {
$0.date.now = now
$0.recording.startRecording = {
await probe.recordStart()
}
$0.recording.stopRecording = {
await probe.beginStop()
}
$0.sleepManagement.preventSleep = { _ in }
$0.sleepManagement.allowSleep = {}
$0.soundEffects.play = { _ in }
}
await store.send(.startRecording) {
$0.isRecording = true
$0.recordingStartTime = now
$0.sourceAppBundleID = activeApp?.bundleIdentifier
$0.sourceAppName = activeApp?.localizedName
}
await store.send(.discard) {
$0.isRecording = false
$0.isPrewarming = false
}
await probe.waitForPendingStop()
await store.send(.startRecording) {
$0.isRecording = true
$0.recordingStartTime = now
$0.sourceAppBundleID = activeApp?.bundleIdentifier
$0.sourceAppName = activeApp?.localizedName
}
await probe.resumePendingStop()
await store.finish()
let counts = await probe.counts()
XCTAssertEqual(counts.startCalls, 2)
XCTAssertEqual(counts.stopCalls, 1)
XCTAssertFalse(FileManager.default.fileExists(atPath: stopURL.path))
}
func testStopGuardIgnoresOnlyStaleSessions() {
let currentSessionID = UUID()
XCTAssertFalse(
RecordingClientLive.shouldIgnoreStopRequest(
snapshotSessionID: currentSessionID,
currentSessionID: currentSessionID
)
)
XCTAssertFalse(
RecordingClientLive.shouldIgnoreStopRequest(
snapshotSessionID: nil,
currentSessionID: currentSessionID
)
)
XCTAssertTrue(
RecordingClientLive.shouldIgnoreStopRequest(
snapshotSessionID: currentSessionID,
currentSessionID: UUID()
)
)
}
func testCaptureControllerIgnoresCallbacksFromOlderGeneration() {
XCTAssertTrue(
SuperFastCaptureController.shouldProcessCallback(
callbackGeneration: 2,
currentGeneration: 2
)
)
XCTAssertFalse(
SuperFastCaptureController.shouldProcessCallback(
callbackGeneration: 1,
currentGeneration: 2
)
)
}
func testFailedRecordingStopEndsTranscription() async {
let now = Date(timeIntervalSince1970: 1_234)
var state = Self.makeState()
state.isRecording = true
state.recordingStartTime = now
state.$hexSettings.withLock { settings in
settings.hotkey = HotKey(key: .a, modifiers: [.command])
}
let store = TestStore(initialState: state) {
TranscriptionFeature()
} withDependencies: {
$0.date.now = now
$0.recording.stopRecording = { .failed(.fallbackExportFailed("copy failed")) }
$0.sleepManagement.allowSleep = {}
}
await store.send(.stopRecording) {
$0.isRecording = false
$0.isTranscribing = true
$0.error = nil
$0.isPrewarming = true
}
await store.receive(\.transcriptionError) {
$0.isTranscribing = false
$0.isPrewarming = false
$0.error = "Failed to export recorded audio: copy failed"
}
}
func testShortRecordingReleasesSleepAssertion() async throws {
let now = Date(timeIntervalSince1970: 1_234)
let stopURL = FileManager.default.temporaryDirectory
.appendingPathComponent("short-recording-\(UUID().uuidString).wav")
let created = FileManager.default.createFile(
atPath: stopURL.path,
contents: Data("test".utf8)
)
XCTAssertTrue(created)
defer { try? FileManager.default.removeItem(at: stopURL) }
let probe = SleepProbe()
let store = TestStore(initialState: Self.makeState()) {
TranscriptionFeature()
} withDependencies: {
$0.date.now = now
$0.recording.startRecording = {}
$0.recording.stopRecording = { .captured(stopURL) }
$0.sleepManagement.preventSleep = { _ in
await probe.recordPreventSleep()
}
$0.sleepManagement.allowSleep = {
await probe.recordAllowSleep()
}
$0.soundEffects.play = { _ in }
}
await store.send(.startRecording) {
$0.isRecording = true
$0.recordingStartTime = now
$0.sourceAppBundleID = NSWorkspace.shared.frontmostApplication?.bundleIdentifier
$0.sourceAppName = NSWorkspace.shared.frontmostApplication?.localizedName
}
await store.send(.stopRecording) {
$0.isRecording = false
}
await store.finish()
let counts = await probe.counts()
XCTAssertEqual(counts.preventSleepCalls, 1)
XCTAssertEqual(counts.allowSleepCalls, 1)
XCTAssertFalse(FileManager.default.fileExists(atPath: stopURL.path))
}
func testDiscardCancelsPendingRecordingStart() async {
let now = Date(timeIntervalSince1970: 1_234)
let stopURL = FileManager.default.temporaryDirectory
.appendingPathComponent("pending-start-discard-\(UUID().uuidString).wav")
let sleepProbe = PendingSleepProbe()
let recordingProbe = RecordingProbe(stopURL: stopURL)
let store = TestStore(initialState: Self.makeState()) {
TranscriptionFeature()
} withDependencies: {
$0.date.now = now
$0.recording.startRecording = {
await recordingProbe.recordStart()
}
$0.recording.stopRecording = {
await recordingProbe.beginImmediateStop()
}
$0.sleepManagement.preventSleep = { _ in
await sleepProbe.preventSleep()
}
$0.sleepManagement.allowSleep = {}
$0.soundEffects.play = { _ in }
}
await store.send(.startRecording) {
$0.isRecording = true
$0.recordingStartTime = now
$0.sourceAppBundleID = NSWorkspace.shared.frontmostApplication?.bundleIdentifier
$0.sourceAppName = NSWorkspace.shared.frontmostApplication?.localizedName
}
await sleepProbe.waitUntilPending()
await store.send(.discard) {
$0.isRecording = false
$0.isPrewarming = false
}
await sleepProbe.resume()
await store.finish()
let counts = await recordingProbe.counts()
XCTAssertEqual(counts.startCalls, 0)
XCTAssertEqual(counts.stopCalls, 1)
}
func testEmptyTranscriptionDeletesCapturedAudio() async throws {
let audioURL = FileManager.default.temporaryDirectory
.appendingPathComponent("empty-transcription-\(UUID().uuidString).wav")
let created = FileManager.default.createFile(
atPath: audioURL.path,
contents: Data("test".utf8)
)
XCTAssertTrue(created)
defer { try? FileManager.default.removeItem(at: audioURL) }
let store = TestStore(initialState: Self.makeState()) {
TranscriptionFeature()
}
await store.send(.transcriptionResult("", audioURL, 1.25))
await store.finish()
XCTAssertFalse(FileManager.default.fileExists(atPath: audioURL.path))
}
func testHistoryUsesRecordingDurationCapturedAtStop() async {
let duration = 1.25
let audioURL = FileManager.default.temporaryDirectory
.appendingPathComponent("history-duration-\(UUID().uuidString).wav")
let transcript = Transcript(
timestamp: Date(timeIntervalSince1970: 1_234),
text: "hello",
audioPath: audioURL,
duration: duration,
sourceAppBundleID: nil,
sourceAppName: nil
)
let probe = TranscriptPersistenceProbe()
let store = TestStore(initialState: Self.makeState()) {
TranscriptionFeature()
} withDependencies: {
$0.transcriptPersistence.save = { text, audioURL, duration, sourceAppBundleID, sourceAppName in
await probe.record(duration: duration)
return transcript
}
$0.pasteboard.paste = { _ in }
$0.soundEffects.play = { _ in }
}
await store.send(.transcriptionResult("hello", audioURL, duration))
while await probe.duration == nil {
await Task.yield()
}
store.assert {
$0.$transcriptionHistory.withLock { $0.history = [transcript] }
}
await store.finish()
let storedDuration = await probe.duration
XCTAssertEqual(storedDuration, duration)
}
private static func makeState() -> TranscriptionFeature.State {
TranscriptionFeature.State(
hexSettings: Shared(value: .init()),
isRemappingScratchpadFocused: false,
modelBootstrapState: Shared(value: .init(isModelReady: true)),
transcriptionHistory: Shared(value: .init())
)
}
}
private actor RecordingProbe {
private let stopURL: URL
private var startCalls = 0
private var stopCalls = 0
private var stopContinuation: CheckedContinuation<RecordingStopResult, Never>?
init(stopURL: URL) {
self.stopURL = stopURL
}
func recordStart() {
startCalls += 1
}
func beginStop() async -> RecordingStopResult {
stopCalls += 1
return await withCheckedContinuation { continuation in
stopContinuation = continuation
}
}
func beginImmediateStop() -> RecordingStopResult {
stopCalls += 1
return .captured(stopURL)
}
func waitForPendingStop() async {
while stopContinuation == nil {
await Task.yield()
}
}
func resumePendingStop() {
stopContinuation?.resume(returning: .captured(stopURL))
stopContinuation = nil
}
func counts() -> (startCalls: Int, stopCalls: Int) {
(startCalls, stopCalls)
}
}
private actor SleepProbe {
private var preventSleepCalls = 0
private var allowSleepCalls = 0
func recordPreventSleep() {
preventSleepCalls += 1
}
func recordAllowSleep() {
allowSleepCalls += 1
}
func counts() -> (preventSleepCalls: Int, allowSleepCalls: Int) {
(preventSleepCalls, allowSleepCalls)
}
}
private actor PendingSleepProbe {
private var continuation: CheckedContinuation<Void, Never>?
func preventSleep() async {
await withCheckedContinuation { continuation in
self.continuation = continuation
}
}
func waitUntilPending() async {
while continuation == nil {
await Task.yield()
}
}
func resume() {
continuation?.resume()
continuation = nil
}
}
private actor TranscriptPersistenceProbe {
private(set) var duration: TimeInterval?
func record(duration: TimeInterval) {
self.duration = duration
}
}