Files
swift-inotify/Tests/InotifyIntegrationTests/InotifyLimitTests.swift
T
T. R. Bernstein 6375a23328 Report queue overflows instead of dropping them
`IN_Q_OVERFLOW` arrives with watch descriptor -1, so the path lookup
failed and the event was silently discarded. It is now delivered
with an empty path so consumers can rescan after the kernel dropped
events.
2026-09-13 23:06:28 +02:00

75 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 event.mask.contains(.queueOverflow) { 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 != nil, "Expected a queue overflow event after \(index) file creations and \(received) received events")
#expect(overflow?.watchDescriptor == -1)
#expect(overflow?.path == "")
}
}
}
}