Exclude items by shell pattern as well as by name
Patterns such as `.*` or `@*` are matched against an item's own name with `fnmatch`, in the same places as excluded names: resolving a tree, extending a watch to a directory that appears later, and delivering events. Dependents that prune large trees can now skip whole families of directories without listing each name.
This commit is contained in:
@@ -24,4 +24,14 @@ struct DirectoryResolverTests {
|
||||
#expect(directories.map { $0.description } == [dir])
|
||||
}
|
||||
}
|
||||
|
||||
@Test func doesNotDescendIntoDirectoriesMatchingAnExcludedPattern() async throws {
|
||||
try await withTempDir { dir in
|
||||
let excludedSubdirectory = "\(dir)/@eaDir/Inside"
|
||||
try FileManager.default.createDirectory(atPath: excludedSubdirectory, withIntermediateDirectories: true)
|
||||
let directories = try await DirectoryResolver.resolve([dir], excluding: ExclusionList(patterns: ["@*"]))
|
||||
|
||||
#expect(directories.map { $0.description } == [dir])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import Testing
|
||||
@testable import Inotify
|
||||
|
||||
@Suite("Exclusion")
|
||||
struct ExclusionTests {
|
||||
@Test func excludesANameThatMatchesAPattern() async throws {
|
||||
let inotify = try Inotify()
|
||||
await inotify.exclude(patterns: "*.tmp", "@*")
|
||||
|
||||
#expect(await inotify.isExcluded("scan.tmp"))
|
||||
#expect(await inotify.isExcluded("@eaDir"))
|
||||
#expect(await !inotify.isExcluded("scan.pdf"))
|
||||
}
|
||||
|
||||
@Test func excludesAnExactName() async throws {
|
||||
let inotify = try Inotify()
|
||||
await inotify.exclude(name: ".git")
|
||||
|
||||
#expect(await inotify.isExcluded(".git"))
|
||||
#expect(await !inotify.isExcluded(".gitignore"))
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,44 @@ struct RecursiveEventTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test func ignoresFileCreationInASubfolderMatchingAnExcludedPattern() async throws {
|
||||
try await withTempDir { dir in
|
||||
let subDirectory = "\(dir)/@eaDir"
|
||||
let filepath = "\(subDirectory)/modify-target.txt"
|
||||
try FileManager.default.createDirectory(atPath: subDirectory, withIntermediateDirectories: true)
|
||||
|
||||
let events = try await getEventsForTrigger(
|
||||
in: dir,
|
||||
mask: [.create],
|
||||
recursive: .recursive,
|
||||
excludePatterns: ["@*"]
|
||||
) { _ in try createFile(at: "\(filepath)", contents: "hello") }
|
||||
|
||||
let createEvent = events.first { $0.mask.contains(.create) && $0.path.string == filepath }
|
||||
#expect(createEvent == nil, "Did not expect CREATE for '\(filepath)', got: \(events)")
|
||||
}
|
||||
}
|
||||
|
||||
@Test func doesNotWatchANewSubfolderMatchingAnExcludedPattern() async throws {
|
||||
try await withTempDir { dir in
|
||||
let subDirectory = "\(dir)/@eaDir"
|
||||
let filepath = "\(subDirectory)/modify-target.txt"
|
||||
|
||||
let events = try await getEventsForTrigger(
|
||||
in: dir,
|
||||
mask: [.create],
|
||||
recursive: .withAutomaticSubtreeWatching,
|
||||
excludePatterns: ["@*"]
|
||||
) { _ in
|
||||
try FileManager.default.createDirectory(atPath: subDirectory, withIntermediateDirectories: true)
|
||||
try await Task.sleep(for: .milliseconds(400))
|
||||
try createFile(at: "\(filepath)", contents: "hello")
|
||||
}
|
||||
|
||||
#expect(events.isEmpty, "Did not expect any event, got: \(events)")
|
||||
}
|
||||
}
|
||||
|
||||
@Test func newSubfoldersOfRecursiveWatchAreAutomaticallyWatchedToo() async throws {
|
||||
try await withTempDir { dir in
|
||||
let subDirectory = "\(dir)/Subfolder"
|
||||
|
||||
@@ -11,10 +11,12 @@ func getEventsForTrigger(
|
||||
mask: InotifyEventMask,
|
||||
recursive: RecursivKind = .nonrecursive,
|
||||
exclude: [String] = [],
|
||||
excludePatterns: [String] = [],
|
||||
trigger: @escaping (String) async throws -> Void,
|
||||
) async throws -> [InotifyEvent] {
|
||||
let watcher = try Inotify()
|
||||
await watcher.exclude(names: exclude)
|
||||
await watcher.exclude(patterns: excludePatterns)
|
||||
switch recursive {
|
||||
case .nonrecursive:
|
||||
try await watcher.addWatch(path: dir, mask: mask)
|
||||
|
||||
Reference in New Issue
Block a user