`deinit` closed the inotify descriptor while its dispatch source was still active. The kernel drops the epoll registration on close, but libdispatch keeps its own; an instance created afterwards that reuses the descriptor number could inherit that stale state and never receive events. Roughly one test run in three lost a single event this way. The reader is now cancelled in `deinit` and the descriptor closed in its cancel handler, as libdispatch requires.
25 lines
705 B
Swift
25 lines
705 B
Swift
import Foundation
|
|
import Testing
|
|
@testable import Inotify
|
|
|
|
@Suite("Instance Lifecycle")
|
|
struct LifecycleTests {
|
|
@Test func aDeallocatedInstanceDoesNotStealEventsOfItsSuccessor() async throws {
|
|
try await withTempDir { dir in
|
|
let filename = "after-reuse.txt"
|
|
do {
|
|
let predecessor = try Inotify()
|
|
try await predecessor.addWatch(path: dir, mask: .create)
|
|
}
|
|
|
|
let events = try await getEventsForTrigger(
|
|
in: dir,
|
|
mask: .create,
|
|
) { try createFile(at: "\($0)/\(filename)") }
|
|
|
|
let createEvent = events.first { $0.mask.contains(.create) && $0.path.lastComponent?.string == filename }
|
|
#expect(createEvent != nil, "Expected CREATE for '\(filename)', got: \(events)")
|
|
}
|
|
}
|
|
}
|