Files
ggbond268--mactools/Plugins/XcodeClean/Tests/XcodeCleanScannerTests.swift
T
wehub-resource-sync 1d1286fadb
Build / Build and test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:18:38 +08:00

139 lines
5.6 KiB
Swift

import XCTest
@testable import MacTools
@testable import XcodeCleanPlugin
final class XcodeCleanScannerTests: XCTestCase {
func testScansAllowedCategoryAndComputesSize() async throws {
let temp = try makeTempDirectory()
defer { try? FileManager.default.removeItem(at: temp) }
let derivedRoot = temp.appendingPathComponent("DerivedData")
try FileManager.default.createDirectory(at: derivedRoot, withIntermediateDirectories: true)
let projectDir = derivedRoot.appendingPathComponent("MyProject-abc")
try FileManager.default.createDirectory(at: projectDir, withIntermediateDirectories: true)
let largeFile = projectDir.appendingPathComponent("build.o")
try Data(repeating: 0x41, count: 1024).write(to: largeFile)
let catalog = XcodeCleanRuleCatalog(rules: [
XcodeCleanRule(
id: "test.derived",
category: .derivedData,
pathPatterns: [derivedRoot.path + "/*"]
)
])
let scanner = XcodeCleanScanner(
ruleCatalog: catalog,
fileSystem: LocalXcodeCleanFileSystem(),
allowedRoots: [derivedRoot.path],
now: { Date(timeIntervalSince1970: 0) }
)
let result = try await scanner.scan(categories: [.derivedData])
XCTAssertEqual(result.candidates.count, 1)
let candidate = try XCTUnwrap(result.candidates.first)
XCTAssertEqual(candidate.category, .derivedData)
XCTAssertEqual(candidate.safety, .allowed)
XCTAssertEqual(candidate.sizeBytes, 1024)
}
func testRejectsPathsOutsideAllowedRoots() async throws {
let temp = try makeTempDirectory()
defer { try? FileManager.default.removeItem(at: temp) }
let outsideDir = temp.appendingPathComponent("Outside")
try FileManager.default.createDirectory(at: outsideDir, withIntermediateDirectories: true)
try Data([0, 1, 2]).write(to: outsideDir.appendingPathComponent("blob"))
let catalog = XcodeCleanRuleCatalog(rules: [
XcodeCleanRule(
id: "test.outside",
category: .derivedData,
pathPatterns: [outsideDir.path + "/*"]
)
])
let scanner = XcodeCleanScanner(
ruleCatalog: catalog,
fileSystem: LocalXcodeCleanFileSystem(),
allowedRoots: [temp.appendingPathComponent("Allowed").path],
now: { Date(timeIntervalSince1970: 0) }
)
let result = try await scanner.scan(categories: [.derivedData])
XCTAssertEqual(result.candidates.count, 1)
XCTAssertEqual(result.candidates.first?.safety, .outsideAllowedRoot)
XCTAssertEqual(result.cleanableCandidates.count, 0)
}
func testSkipsCategoriesNotInRequest() async throws {
let temp = try makeTempDirectory()
defer { try? FileManager.default.removeItem(at: temp) }
let derivedRoot = temp.appendingPathComponent("DerivedData")
try FileManager.default.createDirectory(at: derivedRoot, withIntermediateDirectories: true)
try Data([0]).write(to: derivedRoot.appendingPathComponent("blob"))
let archivesRoot = temp.appendingPathComponent("Archives")
try FileManager.default.createDirectory(at: archivesRoot, withIntermediateDirectories: true)
try Data([0]).write(to: archivesRoot.appendingPathComponent("archive"))
let catalog = XcodeCleanRuleCatalog(rules: [
XcodeCleanRule(id: "test.derived", category: .derivedData, pathPatterns: [derivedRoot.path + "/*"]),
XcodeCleanRule(id: "test.archives", category: .archives, pathPatterns: [archivesRoot.path + "/*"])
])
let scanner = XcodeCleanScanner(
ruleCatalog: catalog,
allowedRoots: [derivedRoot.path, archivesRoot.path]
)
let result = try await scanner.scan(categories: [.derivedData])
XCTAssertEqual(result.candidates.map(\.category), [.derivedData])
}
func testEmitsProgressLog() async throws {
let temp = try makeTempDirectory()
defer { try? FileManager.default.removeItem(at: temp) }
let derivedRoot = temp.appendingPathComponent("DerivedData")
try FileManager.default.createDirectory(at: derivedRoot, withIntermediateDirectories: true)
try Data([0]).write(to: derivedRoot.appendingPathComponent("blob"))
let catalog = XcodeCleanRuleCatalog(rules: [
XcodeCleanRule(id: "test.derived", category: .derivedData, pathPatterns: [derivedRoot.path + "/*"])
])
let scanner = XcodeCleanScanner(
ruleCatalog: catalog,
allowedRoots: [derivedRoot.path]
)
let recorder = LogRecorder()
_ = try await scanner.scan(categories: [.derivedData]) { message in
await recorder.append(message)
}
let messages = await recorder.snapshot()
XCTAssertTrue(messages.contains { $0.text.contains("扫描分类") })
XCTAssertTrue(messages.contains { $0.text.contains("可清理") })
}
// MARK: - Helpers
private func makeTempDirectory() throws -> URL {
let url = FileManager.default.temporaryDirectory.appendingPathComponent(
"xcode-clean-scanner-\(UUID().uuidString)"
)
try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
return url
}
}
private actor LogRecorder {
private var messages: [XcodeCleanScanLogMessage] = []
func append(_ message: XcodeCleanScanLogMessage) {
messages.append(message)
}
func snapshot() -> [XcodeCleanScanLogMessage] { messages }
}