import ASRKit import CryptoKit import Foundation import SpeechCore enum CheckError: Error, CustomStringConvertible { case failed(String) var description: String { switch self { case .failed(let message): return message } } } func check(_ condition: @autoclosure () throws -> Bool, _ message: String) throws { if try !condition() { throw CheckError.failed(message) } } func makeBundle(schema: Int, corrupt: Bool = false) throws -> URL { let dir = FileManager.default.temporaryDirectory .appendingPathComponent("spktest-\(UUID().uuidString)") try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) let payload = Data("hello speechkit".utf8) try payload.write(to: dir.appendingPathComponent("a.bin")) var sha = SHA256.hash(data: payload).map { String(format: "%02x", $0) }.joined() if corrupt { sha = String(sha.reversed()) } let manifest: [String: Any] = [ "schemaVersion": schema, "bundleVersion": "0.0.1", "assets": ["a": ["file": "a.bin", "sha256": sha, "sizeBytes": payload.count]], ] let data = try JSONSerialization.data(withJSONObject: manifest) try data.write(to: dir.appendingPathComponent("manifest.json")) return dir } func expectSpeechError(_ label: String, _ body: () throws -> Void) throws { do { try body() throw CheckError.failed("\(label): expected SpeechError") } catch is SpeechError { return } } do { let validURL = try makeBundle(schema: 2) let bundle = try AssetBundle(at: validURL, verifyHashes: true) try check(try bundle.url("a").lastPathComponent == "a.bin", "asset URL should resolve") try expectSpeechError("wrong schema") { _ = try AssetBundle(at: makeBundle(schema: 99)) } try expectSpeechError("corrupted asset") { _ = try AssetBundle(at: makeBundle(schema: 2, corrupt: true), verifyHashes: true) } try expectSpeechError("missing asset") { _ = try bundle.url("nonexistent") } try check(MelExtractor.bucket(forFrames: 100) == 500, "100 frames should use 500 bucket") try check(MelExtractor.bucket(forFrames: 500) == 500, "500 frames should use 500 bucket") try check(MelExtractor.bucket(forFrames: 501) == 1000, "501 frames should use 1000 bucket") try check(MelExtractor.bucket(forFrames: 2999) == 3000, "2999 frames should use 3000 bucket") try check(MelExtractor.bucket(forFrames: 9999) == 3000, "long audio should clamp to 3000 bucket") try check(AudioTower.arkAudioTokenCount(sampleCount: 67_263) == 52, "4.2s reference should produce 52 audio tokens") print("dev-check PASS") } catch { fputs("dev-check FAIL: \(error)\n", stderr) exit(1) }