Skip to content

Commit 2aa7db2

Browse files
authored
Merge pull request #25 from SWAT-engineering/improved-overflow-support/overflow-policies-for-file-watches
Improved overflow support: Overflow auto-handling for file watches
2 parents 546fbe8 + c92d17f commit 2aa7db2

File tree

3 files changed

+87
-1
lines changed

3 files changed

+87
-1
lines changed

src/main/java/engineering/swat/watch/Watcher.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ public ActiveWatch start() throws IOException {
198198
}
199199
}
200200
case PATH_ONLY: {
201-
var result = new JDKFileWatch(path, executor, eventHandler);
201+
var result = new JDKFileWatch(path, executor, h);
202202
result.open();
203203
return result;
204204
}

src/main/java/engineering/swat/watch/impl/jdk/JDKFileWatch.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ public JDKFileWatch(Path file, Executor exec, BiConsumer<EventHandlingWatch, Wat
5757
assert !parent.equals(file);
5858

5959
this.internal = new JDKDirectoryWatch(parent, exec, (w, e) -> {
60+
if (e.getKind() == WatchEvent.Kind.OVERFLOW) {
61+
var overflow = new WatchEvent(WatchEvent.Kind.OVERFLOW, file);
62+
eventHandler.accept(w, overflow);
63+
}
6064
if (fileName.equals(e.getRelativePath())) {
6165
eventHandler.accept(w, e);
6266
}

src/test/java/engineering/swat/watch/SingleFileTests.java

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,34 @@
2626
*/
2727
package engineering.swat.watch;
2828

29+
import static engineering.swat.watch.WatchEvent.Kind.CREATED;
30+
import static engineering.swat.watch.WatchEvent.Kind.DELETED;
31+
import static engineering.swat.watch.WatchEvent.Kind.MODIFIED;
32+
import static engineering.swat.watch.WatchEvent.Kind.OVERFLOW;
2933
import static org.awaitility.Awaitility.await;
3034

3135
import java.io.IOException;
3236
import java.nio.file.Files;
37+
import java.nio.file.Path;
3338
import java.nio.file.attribute.FileTime;
3439
import java.time.Instant;
40+
import java.util.ArrayList;
41+
import java.util.Arrays;
42+
import java.util.Collections;
43+
import java.util.List;
3544
import java.util.concurrent.atomic.AtomicBoolean;
45+
import java.util.function.Consumer;
46+
import java.util.function.Predicate;
47+
import java.util.stream.Stream;
3648

3749
import org.awaitility.Awaitility;
3850
import org.junit.jupiter.api.AfterEach;
3951
import org.junit.jupiter.api.BeforeAll;
4052
import org.junit.jupiter.api.BeforeEach;
4153
import org.junit.jupiter.api.Test;
4254

55+
import engineering.swat.watch.impl.EventHandlingWatch;
56+
4357
class SingleFileTests {
4458
private TestDirectory testDir;
4559

@@ -117,4 +131,72 @@ void singleFileThatMonitorsOnlyADirectory() throws IOException, InterruptedExcep
117131
.untilTrue(seen);
118132
}
119133
}
134+
135+
@Test
136+
void noRescanOnOverflow() throws IOException, InterruptedException {
137+
var bookkeeper = new Bookkeeper();
138+
try (var watch = startWatchAndTriggerOverflow(OnOverflow.NONE, bookkeeper)) {
139+
Thread.sleep(TestHelper.SHORT_WAIT.toMillis());
140+
141+
await("Overflow shouldn't trigger created, modified, or deleted events")
142+
.until(() -> bookkeeper.fullPaths(CREATED, MODIFIED, DELETED).count() == 0);
143+
await("Overflow should be visible to user-defined event handler")
144+
.until(() -> bookkeeper.fullPaths(OVERFLOW).count() == 1);
145+
}
146+
}
147+
148+
@Test
149+
void memorylessRescanOnOverflow() throws IOException, InterruptedException {
150+
var bookkeeper = new Bookkeeper();
151+
try (var watch = startWatchAndTriggerOverflow(OnOverflow.ALL, bookkeeper)) {
152+
Thread.sleep(TestHelper.SHORT_WAIT.toMillis());
153+
154+
var isFile = Predicate.isEqual(watch.getPath());
155+
var isNotFile = Predicate.not(isFile);
156+
157+
await("Overflow should trigger created event for `file`")
158+
.until(() -> bookkeeper.fullPaths(CREATED).filter(isFile).count() == 1);
159+
await("Overflow shouldn't trigger created events for other files")
160+
.until(() -> bookkeeper.fullPaths(CREATED).filter(isNotFile).count() == 0);
161+
await("Overflow shouldn't trigger modified events (`file` is empty)")
162+
.until(() -> bookkeeper.fullPaths(MODIFIED).count() == 0);
163+
await("Overflow shouldn't trigger deleted events")
164+
.until(() -> bookkeeper.fullPaths(DELETED).count() == 0);
165+
await("Overflow should be visible to user-defined event handler")
166+
.until(() -> bookkeeper.fullPaths(OVERFLOW).count() == 1);
167+
}
168+
}
169+
170+
private ActiveWatch startWatchAndTriggerOverflow(OnOverflow whichFiles, Bookkeeper bookkeeper) throws IOException {
171+
var parent = testDir.getTestDirectory();
172+
var file = parent.resolve("a.txt");
173+
174+
var watch = Watcher
175+
.watch(file, WatchScope.PATH_ONLY)
176+
.approximate(whichFiles)
177+
.on(bookkeeper)
178+
.start();
179+
180+
var overflow = new WatchEvent(WatchEvent.Kind.OVERFLOW, parent);
181+
((EventHandlingWatch) watch).handleEvent(overflow);
182+
return watch;
183+
}
184+
185+
private static class Bookkeeper implements Consumer<WatchEvent> {
186+
private final List<WatchEvent> events = Collections.synchronizedList(new ArrayList<>());
187+
188+
public Stream<WatchEvent> events(WatchEvent.Kind... kinds) {
189+
var list = Arrays.asList(kinds.length == 0 ? WatchEvent.Kind.values() : kinds);
190+
return events.stream().filter(e -> list.contains(e.getKind()));
191+
}
192+
193+
public Stream<Path> fullPaths(WatchEvent.Kind... kinds) {
194+
return events(kinds).map(WatchEvent::calculateFullPath);
195+
}
196+
197+
@Override
198+
public void accept(WatchEvent e) {
199+
events.add(e);
200+
}
201+
}
120202
}

0 commit comments

Comments
 (0)