import Foundation import IdentifiedCollections import Testing @testable import Prowl extension PerformanceBenchmarks { /// Pins the #648/#655 directory index against the pre-#648 shape it replaced: /// every agent row scanning every worktree, with `PathPolicy` normalizing both /// sides of each containment test — filesystem round-trips per (row, worktree) /// pair. The index normalizes each worktree once at build time and each query /// once at lookup, so even the worst case (build plus a full query batch) /// must beat one naive batch. @Suite struct WorktreeDirectoryIndexBenchmarks { @Test func indexBuildPlusQueryBatchOutpacesThePerRowScan() throws { let fileManager = FileManager.default let tempRoot = fileManager.temporaryDirectory.appending(path: UUID().uuidString) defer { try? fileManager.removeItem(at: tempRoot) } let repositoryCount = BenchmarkMeasurement.isFullMode ? 6 : 3 let worktreesPerRepository = 4 var repositories: IdentifiedArrayOf = [] var queries: [URL] = [] for repositoryIndex in 0.. = [] for worktreeIndex in 0..= 2, "index build plus batch was only \(BenchmarkMeasurement.ratio(medians))x the per-row scan" ) } /// The pre-#648 resolution shape: every query walks every worktree, and each /// containment test normalizes both sides again via `PathPolicy`. private static func referenceResolve(_ query: URL, in worktrees: [Worktree]) -> Worktree.ID? { var best: (id: Worktree.ID, depth: Int)? for worktree in worktrees where PathPolicy.contains(query, in: worktree.workingDirectory) { let depth = PathPolicy.normalizeURL(worktree.workingDirectory).pathComponents.count if depth > (best?.depth ?? -1) { best = (worktree.id, depth) } } return best?.id } } }