From 0169be7da411a27c376bab67dc3d2fcdea8e18f3 Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Mon, 21 Apr 2025 13:38:20 +0200 Subject: [PATCH 1/3] Add a test to see if a file move/rename between nested dirs is properly observed --- .../engineering/swat/watch/SmokeTests.java | 58 +++++++++++++++++++ .../engineering/swat/watch/TestHelper.java | 2 +- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/test/java/engineering/swat/watch/SmokeTests.java b/src/test/java/engineering/swat/watch/SmokeTests.java index 592e64e0..2eaaee97 100644 --- a/src/test/java/engineering/swat/watch/SmokeTests.java +++ b/src/test/java/engineering/swat/watch/SmokeTests.java @@ -112,4 +112,62 @@ void watchSingleFile() throws IOException { await("Single file change").untilTrue(changed); } } + + @Test + void moveRegularFileBetweenNestedDirectories() throws IOException { + var parent = testDir.getTestDirectory(); + var child1 = Files.createDirectories(parent.resolve("from")); + var child2 = Files.createDirectories(parent.resolve("to")); + var file = Files.createFile(child1.resolve("file.txt")); + + var parentWatchBookkeeper = new TestHelper.Bookkeeper(); + var parentWatchConfig = Watcher + .watch(parent, WatchScope.PATH_AND_ALL_DESCENDANTS) + .on(parentWatchBookkeeper); + + var child1WatchBookkeeper = new TestHelper.Bookkeeper(); + var child1WatchConfig = Watcher + .watch(child1, WatchScope.PATH_AND_CHILDREN) + .on(child1WatchBookkeeper); + + var child2WatchBookkeeper = new TestHelper.Bookkeeper(); + var child2WatchConfig = Watcher + .watch(child2, WatchScope.PATH_AND_CHILDREN) + .on(child2WatchBookkeeper); + + var fileWatchBookkeeper = new TestHelper.Bookkeeper(); + var fileWatchConfig = Watcher + .watch(file, WatchScope.PATH_ONLY) + .on(fileWatchBookkeeper); + + try (var parentWatch = parentWatchConfig.start(); + var child1Watch = child1WatchConfig.start(); + var child2Watch = child2WatchConfig.start(); + var fileWatch = fileWatchConfig.start()) { + + var source = child1.resolve(file.getFileName()); + var target = child2.resolve(file.getFileName()); + Files.move(source, target); + + await("Move should be observed as delete by `parent` watch (file tree)") + .until(() -> parentWatchBookkeeper + .events().kind(DELETED).rootPath(parent).relativePath(parent.relativize(source)).any()); + + await("Move should be observed as create by `parent` watch (file tree)") + .until(() -> parentWatchBookkeeper + .events().kind(CREATED).rootPath(parent).relativePath(parent.relativize(target)).any()); + + await("Move should be observed as delete by `child1` watch (single directory)") + .until(() -> child1WatchBookkeeper + .events().kind(DELETED).rootPath(child1).relativePath(child1.relativize(source)).any()); + + await("Move should be observed as create by `child2` watch (single directory)") + .until(() -> child2WatchBookkeeper + .events().kind(CREATED).rootPath(child2).relativePath(child2.relativize(target)).any()); + + await("Move should be observed as delete by `file` watch (regular file)") + .until(() -> fileWatchBookkeeper + .events().kind(DELETED).rootPath(source).any()); + } + } } diff --git a/src/test/java/engineering/swat/watch/TestHelper.java b/src/test/java/engineering/swat/watch/TestHelper.java index b5321ed4..478d8770 100644 --- a/src/test/java/engineering/swat/watch/TestHelper.java +++ b/src/test/java/engineering/swat/watch/TestHelper.java @@ -48,7 +48,7 @@ public class TestHelper { var os = System.getProperty("os.name", "?").toLowerCase(); if (os.contains("mac")) { // OSX is SLOW on it's watches - delayFactor *= 2; + delayFactor *= 3; } else if (os.contains("win")) { // windows watches can be slow to get everything From 6ed64f6831e5f567cf6209ebf243c5d8d3794efc Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Mon, 21 Apr 2025 14:12:04 +0200 Subject: [PATCH 2/3] Fix issue in `JDKFileWatch` that the wrong division of root/relative path was reported --- .../engineering/swat/watch/impl/jdk/JDKFileWatch.java | 9 +++------ .../java/engineering/swat/watch/SingleFileTests.java | 10 ++++------ 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/main/java/engineering/swat/watch/impl/jdk/JDKFileWatch.java b/src/main/java/engineering/swat/watch/impl/jdk/JDKFileWatch.java index ab64116e..b1ef6a6c 100644 --- a/src/main/java/engineering/swat/watch/impl/jdk/JDKFileWatch.java +++ b/src/main/java/engineering/swat/watch/impl/jdk/JDKFileWatch.java @@ -61,12 +61,9 @@ public JDKFileWatch(Path file, Executor exec, assert !parent.equals(file); this.internal = new JDKDirectoryWatch(parent, exec, (w, e) -> { - if (e.getKind() == WatchEvent.Kind.OVERFLOW) { - var overflow = new WatchEvent(WatchEvent.Kind.OVERFLOW, file); - eventHandler.accept(w, overflow); - } - if (fileName.equals(e.getRelativePath())) { - eventHandler.accept(w, e); + var kind = e.getKind(); + if (kind == WatchEvent.Kind.OVERFLOW || e.getRelativePath().equals(fileName)) { + eventHandler.accept(w, new WatchEvent(kind, file)); } }, eventFilter); diff --git a/src/test/java/engineering/swat/watch/SingleFileTests.java b/src/test/java/engineering/swat/watch/SingleFileTests.java index 870b986a..3417c240 100644 --- a/src/test/java/engineering/swat/watch/SingleFileTests.java +++ b/src/test/java/engineering/swat/watch/SingleFileTests.java @@ -143,13 +143,11 @@ void memorylessRescanOnOverflow() throws IOException, InterruptedException { try (var watch = startWatchAndTriggerOverflow(Approximation.ALL, bookkeeper)) { Thread.sleep(TestHelper.SHORT_WAIT.toMillis()); - var fileName = watch.getPath().getFileName(); - var parent = watch.getPath().getParent(); - - await("Overflow should trigger created event for `" + fileName + "`") - .until(() -> bookkeeper.events().kind(CREATED).rootPath(parent).relativePath(fileName).any()); + var path = watch.getPath(); + await("Overflow should trigger created event for `" + path + "`") + .until(() -> bookkeeper.events().kind(CREATED).rootPath(path).any()); await("Overflow shouldn't trigger created events for other files") - .until(() -> bookkeeper.events().kind(CREATED).rootPath(parent).relativePathNot(fileName).none()); + .until(() -> bookkeeper.events().kind(CREATED).rootPathNot(path).none()); await("Overflow shouldn't trigger modified or deleted events") .until(() -> bookkeeper.events().kind(MODIFIED, DELETED).none()); await("Overflow should be visible to user-defined event handler") From c4809f24d40d2596bc0c02869f7117473fa31465 Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Mon, 21 Apr 2025 14:25:49 +0200 Subject: [PATCH 3/3] Fix compilation failures after merging --- .../java/engineering/swat/watch/SmokeTests.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/test/java/engineering/swat/watch/SmokeTests.java b/src/test/java/engineering/swat/watch/SmokeTests.java index 714de81d..65dcc719 100644 --- a/src/test/java/engineering/swat/watch/SmokeTests.java +++ b/src/test/java/engineering/swat/watch/SmokeTests.java @@ -121,23 +121,23 @@ void moveRegularFileBetweenNestedDirectories() throws IOException { var file = Files.createFile(child1.resolve("file.txt")); var parentWatchBookkeeper = new TestHelper.Bookkeeper(); - var parentWatchConfig = Watcher - .watch(parent, WatchScope.PATH_AND_ALL_DESCENDANTS) + var parentWatchConfig = Watch + .build(parent, WatchScope.PATH_AND_ALL_DESCENDANTS) .on(parentWatchBookkeeper); var child1WatchBookkeeper = new TestHelper.Bookkeeper(); - var child1WatchConfig = Watcher - .watch(child1, WatchScope.PATH_AND_CHILDREN) + var child1WatchConfig = Watch + .build(child1, WatchScope.PATH_AND_CHILDREN) .on(child1WatchBookkeeper); var child2WatchBookkeeper = new TestHelper.Bookkeeper(); - var child2WatchConfig = Watcher - .watch(child2, WatchScope.PATH_AND_CHILDREN) + var child2WatchConfig = Watch + .build(child2, WatchScope.PATH_AND_CHILDREN) .on(child2WatchBookkeeper); var fileWatchBookkeeper = new TestHelper.Bookkeeper(); - var fileWatchConfig = Watcher - .watch(file, WatchScope.PATH_ONLY) + var fileWatchConfig = Watch + .build(file, WatchScope.PATH_ONLY) .on(fileWatchBookkeeper); try (var parentWatch = parentWatchConfig.start();