Files
OSGKeyboard/OSGKeyboardTests/LocalASRModelCatalogTests.swift
T
Rocky 200265fbd6 feat(macos): local ASR model manager, menu-bar polish, and release 0.5.2
Adds a bundled local ASR model catalog for the macOS app with one-click
Sherpa Qwen3 / SenseVoice downloads (pause/resume, inline actions) and a
shared model storage directory used by MLX Qwen3. Fixes the light-mode
sidebar material and makes the menu-bar icon follow the system appearance
with a refreshed status mark. Renames the built product to OSGKeyboard.app.

Bumps version to 0.5.2 (build 19).
2026-07-09 08:55:37 +08:00

89 lines
3.5 KiB
Swift

// LocalASRModelCatalogTests.swift
// OSGKeyboardTests
import XCTest
@testable import OSGKeyboardShared
final class LocalASRModelCatalogTests: XCTestCase {
func testBundledCatalogLoads() throws {
let catalog = try LocalASRModelCatalog.loadBundled()
XCTAssertEqual(catalog.schemaVersion, 1)
XCTAssertFalse(catalog.models.isEmpty)
XCTAssertTrue(catalog.models.contains { $0.id == "qwen3-mlx-1.7b" })
XCTAssertTrue(catalog.models.contains { $0.id == "sherpa-qwen3-0.6b-int8" })
}
func testCapabilitiesForSherpaQwen3() throws {
let catalog = try LocalASRModelCatalog.loadBundled()
let model = try XCTUnwrap(LocalASRModelCatalog.model("sherpa-qwen3-0.6b-int8", in: catalog))
let caps = LocalASRModelCatalog.capabilities(for: model)
XCTAssertEqual(caps.hotwordMode, .recognizerScoped)
XCTAssertTrue(model.supportsHotwords)
}
func testManifestRoundTrip() throws {
let manifest = LocalASRInstalledManifest(
selectedModelId: "sherpa-qwen3-0.6b-int8",
installedModelIDs: ["sherpa-qwen3-0.6b-int8"]
)
let url = FileManager.default.temporaryDirectory
.appendingPathComponent("manifest-\(UUID().uuidString).json")
defer { try? FileManager.default.removeItem(at: url) }
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
try encoder.encode(manifest).write(to: url)
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let loaded = try decoder.decode(LocalASRInstalledManifest.self, from: Data(contentsOf: url))
XCTAssertEqual(loaded.selectedModelId, manifest.selectedModelId)
XCTAssertEqual(loaded.installedModelIDs, manifest.installedModelIDs)
}
func testBiasDiagnosticsStoreRoundTrip() {
LocalASRBiasDiagnosticsStore.clear()
let payload = LocalASRBiasPayload(
hardHotwords: ["Cursor"],
promptBias: "test",
corpusContext: nil,
polishFragment: "fragment",
correctionPairs: [],
diagnostics: LocalASRBiasDiagnostics(userTermCount: 2, builtinTermCount: 3)
)
LocalASRBiasDiagnosticsStore.save(
payload: payload,
modelId: "qwen3-mlx-1.7b",
backendLabel: "MLX"
)
let snapshot = LocalASRBiasDiagnosticsStore.load()
XCTAssertEqual(snapshot?.modelId, "qwen3-mlx-1.7b")
XCTAssertEqual(snapshot?.diagnostics.userTermCount, 2)
XCTAssertEqual(snapshot?.hotwordCount, 1)
LocalASRBiasDiagnosticsStore.clear()
}
func testSherpaAdapterProducesHardHotwords() throws {
let fixtureURL = FileManager.default.temporaryDirectory
.appendingPathComponent("phrases-\(UUID().uuidString).tsv")
try "word\tpinyin\tsource\tweight\nSwiftUI\tswift ui\tcomputer_terms\t5\n"
.write(to: fixtureURL, atomically: true, encoding: .utf8)
defer { try? FileManager.default.removeItem(at: fixtureURL) }
var dict = PersonalDictionary.empty
_ = dict.upsertManual(term: "Kubernetes")
let payload = LocalASRBiasAdapter.adapt(
LocalASRBiasRequest(
dictionary: dict,
locale: Locale(identifier: "zh-CN"),
capabilities: .sherpaQwen3
),
lexicon: BuiltinLexiconIndex(fixtureURL: fixtureURL)
)
XCTAssertFalse(payload.hardHotwords.isEmpty)
XCTAssertTrue(payload.hardHotwords.contains("Kubernetes"))
}
}