import XCTest #if canImport(cmux_DEV) @testable import cmux_DEV #elseif canImport(cmux) @testable import cmux #endif final class CommandPaletteSearchEngineTests: XCTestCase { private struct FixtureEntry { let id: String let rank: Int let title: String let searchableTexts: [String] } private struct FixtureResult: Equatable { let id: String let rank: Int let title: String let score: Int let titleMatchIndices: Set } private func makeCommandEntries(count: Int) -> [FixtureEntry] { (0.. [FixtureEntry] { (0.. [FixtureResult] { let corpus = entries.map { entry in CommandPaletteSearchCorpusEntry( payload: entry.id, rank: entry.rank, title: entry.title, searchableTexts: entry.searchableTexts ) } return CommandPaletteSearchEngine.search(entries: corpus, query: query) { _, _ in 0 } .map { FixtureResult( id: $0.payload, rank: $0.rank, title: $0.title, score: $0.score, titleMatchIndices: $0.titleMatchIndices ) } } private func legacyResults( entries: [FixtureEntry], query: String ) -> [FixtureResult] { let queryIsEmpty = query.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty let results: [FixtureResult] = queryIsEmpty ? entries.map { entry in FixtureResult(id: entry.id, rank: entry.rank, title: entry.title, score: 0, titleMatchIndices: []) } : entries.compactMap { entry in guard let fuzzyScore = CommandPaletteFuzzyMatcher.score( query: query, candidates: entry.searchableTexts ) else { return nil } return FixtureResult( id: entry.id, rank: entry.rank, title: entry.title, score: fuzzyScore, titleMatchIndices: CommandPaletteFuzzyMatcher.matchCharacterIndices( query: query, candidate: entry.title ) ) } return results.sorted { lhs, rhs in if lhs.score != rhs.score { return lhs.score > rhs.score } if lhs.rank != rhs.rank { return lhs.rank < rhs.rank } return lhs.title.localizedCaseInsensitiveCompare(rhs.title) == .orderedAscending } } private func benchmarkElapsedMs(operation: () -> Void) -> Double { let start = DispatchTime.now().uptimeNanoseconds operation() let elapsed = DispatchTime.now().uptimeNanoseconds - start return Double(elapsed) / 1_000_000 } private func repeatedQueries(_ baseQueries: [String], repetitions: Int) -> [String] { Array(repeating: baseQueries, count: repetitions).flatMap { $0 } } func testOptimizedSearchMatchesLegacyPipeline() { let commandEntries = makeCommandEntries(count: 96) let switcherEntries = makeSwitcherEntries(count: 64) let queries = [ "rename", "rename tab", "workspace", "feature-12", "3004", "toggle side", "open dir", "phoenix", "apply update", ] for query in queries { XCTAssertEqual( optimizedResults(entries: commandEntries, query: query), legacyResults(entries: commandEntries, query: query), "Command corpus mismatch for query \(query)" ) XCTAssertEqual( optimizedResults(entries: switcherEntries, query: query), legacyResults(entries: switcherEntries, query: query), "Switcher corpus mismatch for query \(query)" ) } } func testCommandSearchBenchmarkBeatsLegacyPipeline() { let entries = makeCommandEntries(count: 900) let corpus = entries.map { entry in CommandPaletteSearchCorpusEntry( payload: entry.id, rank: entry.rank, title: entry.title, searchableTexts: entry.searchableTexts ) } let queries = repeatedQueries( ["rename", "rename tab", "open dir", "toggle side", "apply update", "notif", "split right", "cmux"], repetitions: 12 ) for query in queries.prefix(8) { _ = legacyResults(entries: entries, query: query) _ = CommandPaletteSearchEngine.search(entries: corpus, query: query) { _, _ in 0 } } let legacyMs = benchmarkElapsedMs { for query in queries { _ = legacyResults(entries: entries, query: query) } } let optimizedMs = benchmarkElapsedMs { for query in queries { _ = CommandPaletteSearchEngine.search(entries: corpus, query: query) { _, _ in 0 } } } print(String(format: "BENCH cmd+shift+p legacy=%.2fms optimized=%.2fms", legacyMs, optimizedMs)) XCTAssertLessThan(optimizedMs, legacyMs) } func testSwitcherSearchBenchmarkBeatsLegacyPipeline() { let entries = makeSwitcherEntries(count: 400) let corpus = entries.map { entry in CommandPaletteSearchCorpusEntry( payload: entry.id, rank: entry.rank, title: entry.title, searchableTexts: entry.searchableTexts ) } let queries = repeatedQueries( ["workspace 12", "phoenix", "feature-18", "rename-tab", "3007", "9202", "switch", "worktrees"], repetitions: 12 ) for query in queries.prefix(8) { _ = legacyResults(entries: entries, query: query) _ = CommandPaletteSearchEngine.search(entries: corpus, query: query) { _, _ in 0 } } let legacyMs = benchmarkElapsedMs { for query in queries { _ = legacyResults(entries: entries, query: query) } } let optimizedMs = benchmarkElapsedMs { for query in queries { _ = CommandPaletteSearchEngine.search(entries: corpus, query: query) { _, _ in 0 } } } print(String(format: "BENCH cmd+p legacy=%.2fms optimized=%.2fms", legacyMs, optimizedMs)) XCTAssertLessThan(optimizedMs, legacyMs) } }