The stream's element is now the enum InotifyEvent, and the struct that describes a change to a watched item is FileSystemEvent. A queue overflow was an event with descriptor -1 and an empty path that every consumer had to know about; as a case, the compiler makes them handle it. The enum is also where failed watches of a growing tree will be reported, since no call site can catch them.
73 lines
2.4 KiB
Swift
73 lines
2.4 KiB
Swift
import Testing
|
|
import Foundation
|
|
@testable import Inotify
|
|
|
|
@Suite("Inotify Limits", .serialized)
|
|
struct InotifyLimitTests {
|
|
@Test func throwsIfInotifyUpperLimitReached() async throws {
|
|
try await withTempDir { dir in
|
|
try await withInotifyWatchLimit(of: 10) {
|
|
try createSubdirectorytree(at: dir, foldersPerLevel: 4, levels: 3)
|
|
try await Task.sleep(for: .milliseconds(100))
|
|
|
|
await #expect(throws: InotifyError.self) {
|
|
let watcher = try Inotify()
|
|
try await watcher.addRecursiveWatch(forDirectory: dir, mask: .allEvents)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Test func watchesMassivSubtreesIfAllowed() async throws {
|
|
try await withTempDir { dir in
|
|
try await withInotifyWatchLimit(of: 1000) {
|
|
try createSubdirectorytree(at: dir, foldersPerLevel: 8, levels: 3)
|
|
let subDirectory = "\(dir)/Folder 8/Folder 8/Folder 8"
|
|
let filepath = "\(subDirectory)/new-file.txt"
|
|
try await Task.sleep(for: .milliseconds(100))
|
|
|
|
let events = try await getEventsForTrigger(
|
|
in: dir,
|
|
mask: [.create],
|
|
recursive: .recursive
|
|
) { _ in
|
|
assert(FileManager.default.fileExists(atPath: subDirectory))
|
|
try createFile(at: "\(filepath)", contents: "hello")
|
|
}
|
|
|
|
let createEvent = events.first { $0.mask.contains(.create) && $0.path.string == filepath }
|
|
#expect(createEvent != nil, "Expected CREATE for '\(filepath)', got: \(events)")
|
|
}
|
|
}
|
|
}
|
|
|
|
@Test func reportsQueueOverflowInsteadOfDroppingIt() async throws {
|
|
try await withTempDir { dir in
|
|
try await withInotifyWatchLimit(of: 1, for: [.queuedEvents]) {
|
|
let watcher = try Inotify()
|
|
try await watcher.addWatch(path: dir, mask: .allEvents)
|
|
let overflowTask = Task { () -> (InotifyEvent?, Int) in
|
|
var received = 0
|
|
for await event in await watcher.events {
|
|
received += 1
|
|
if case .queueOverflow = event { return (event, received) }
|
|
}
|
|
return (nil, received)
|
|
}
|
|
|
|
let deadline = ContinuousClock.now + .seconds(5)
|
|
var index = 0
|
|
while !overflowTask.isCancelled, ContinuousClock.now < deadline {
|
|
try createFile(at: "\(dir)/burst-\(index).txt", contents: "hello")
|
|
index += 1
|
|
if index % 200 == 0 { await Task.yield() }
|
|
}
|
|
overflowTask.cancel()
|
|
let (overflow, received) = await overflowTask.value
|
|
|
|
#expect(overflow == .queueOverflow, "Expected a queue overflow event after \(index) file creations and \(received) received events")
|
|
}
|
|
}
|
|
}
|
|
}
|