From 22c46e7e85b5f0301d525903f9ad6d72ba14e334 Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sat, 29 Nov 2025 23:27:02 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=85=20Improve=20FSNotifierTest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/unit/Watchers/FSNotifierTest.swift | 88 +++++++++++++++++------- 1 file changed, 64 insertions(+), 24 deletions(-) diff --git a/tests/unit/Watchers/FSNotifierTest.swift b/tests/unit/Watchers/FSNotifierTest.swift index 26ca1ad2..fd59c3e5 100644 --- a/tests/unit/Watchers/FSNotifierTest.swift +++ b/tests/unit/Watchers/FSNotifierTest.swift @@ -11,37 +11,33 @@ import Foundation @Suite(.serialized) struct FSNotifierTest { - /** - This test verifies that FSNotifier fires the onChange callback when a file is modified. - */ - @Test func notifier_fires_when_file_is_modified_and_debounces_correctly() async throws { + + @Test func notifier_fires_when_file_is_modified() async throws { // Create a temporary file to monitor let tempDir = FileManager.default.temporaryDirectory let testFile = tempDir.appendingPathComponent("fs_notifier_test_\(UUID().uuidString).txt") FileManager.default.createFile(atPath: testFile.path, contents: nil) - defer { - try? FileManager.default.removeItem(at: testFile) - } - + // Our variable to keep track of let eventFired = Locked(0) + + // Our debouncer let debouncer = Debouncer() - // Create notifier - let notifier = FSNotifier( - for: testFile, - eventMask: .write, - onChange: { - Task { - // Debouncer is an actor so this is allowed - await debouncer.debounce(for: 1.0) { - eventFired.value += 1 - } - } - } - ) + // Set up the notifier + let notifier = FSNotifier(for: testFile, eventMask: .write, onChange: { + Task { await debouncer.debounce(for: 1.0) { + eventFired.value += 1 + }} + }) - // Modify the file, twice + // Cleanup for later + defer { + try? FileManager.default.removeItem(at: testFile) + notifier.terminate() + } + + // Modify the file, twice, debounce should work try "hello".write(to: testFile, atomically: false, encoding: .utf8) try "hello".write(to: testFile, atomically: false, encoding: .utf8) @@ -55,8 +51,52 @@ struct FSNotifierTest { // Verify after another second, our second write is actually noted await delay(seconds: 1.2) #expect(eventFired.value == 2) + } - // Clean up notifier - notifier.terminate() + @Test func notifier_suspends_and_resumes_correctly() async throws { + // Create a temporary file to monitor + let tempDir = FileManager.default.temporaryDirectory + let testFile = tempDir.appendingPathComponent("fs_notifier_test_\(UUID().uuidString).txt") + FileManager.default.createFile(atPath: testFile.path, contents: nil) + + // Our variable to keep track of + let eventFired = Locked(0) + + // Create notifier + let notifier = FSNotifier(for: testFile, eventMask: .write, onChange: { + Task { eventFired.value += 1 } + }) + + // Cleanup for later + defer { + try? FileManager.default.removeItem(at: testFile) + notifier.terminate() + } + + // Modify the file, twice + try "hello".write(to: testFile, atomically: false, encoding: .utf8) + await delay(seconds: 0.2) + #expect(eventFired.value == 1) + + // Try to write again (after debounce timing) + try "hello".write(to: testFile, atomically: false, encoding: .utf8) + await delay(seconds: 0.2) + #expect(eventFired.value == 2) + + // Now, we will suspend + await notifier.suspend() + + // Despite writing to the file, our event did not fire + try "hello".write(to: testFile, atomically: false, encoding: .utf8) + await delay(seconds: 0.2) + #expect(eventFired.value == 2) + + // Now, we will resume + await notifier.resume() + + // Our event should have fired again + try "hello".write(to: testFile, atomically: false, encoding: .utf8) + await delay(seconds: 0.2) + #expect(eventFired.value == 3) } }