Skip to content

Commit 5b94225

Browse files
committed
Extend indexingRescanOnOverflow test
1 parent 3581cb6 commit 5b94225

File tree

1 file changed

+83
-22
lines changed

1 file changed

+83
-22
lines changed

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

Lines changed: 83 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@
2626
*/
2727
package engineering.swat.watch;
2828

29+
import static engineering.swat.watch.WatchEvent.Kind.OVERFLOW;
2930
import static org.awaitility.Awaitility.await;
3031

3132
import java.io.IOException;
3233
import java.nio.file.Files;
34+
import java.util.concurrent.Semaphore;
3335
import java.util.concurrent.atomic.AtomicBoolean;
3436
import java.util.concurrent.atomic.AtomicInteger;
3537
import java.util.function.Predicate;
@@ -165,47 +167,106 @@ void memorylessRescanOnOverflow() throws IOException, InterruptedException {
165167

166168
@Test
167169
void indexingRescanOnOverflow() throws IOException, InterruptedException {
170+
// Preface: This test looks a bit hacky because there's no API to
171+
// directly manipulate, or prevent the auto-manipulation of, the index
172+
// inside a watch. I've added some comments below to make it make sense.
173+
168174
var directory = testDir.getTestDirectory();
169-
Files.writeString(directory.resolve("a.txt"), "foo");
170-
Files.writeString(directory.resolve("b.txt"), "bar");
171175

176+
var semaphore = new Semaphore(0);
172177
var nCreated = new AtomicInteger();
173178
var nModified = new AtomicInteger();
179+
var nDeleted = new AtomicInteger();
180+
174181
var watchConfig = Watcher.watch(directory, WatchScope.PATH_AND_CHILDREN)
175182
.approximate(OnOverflow.DIRTY)
176183
.on(e -> {
177-
switch (e.getKind()) {
178-
case CREATED:
179-
nCreated.incrementAndGet();
180-
break;
181-
case MODIFIED:
182-
nModified.incrementAndGet();
183-
break;
184-
default:
185-
break;
184+
var kind = e.getKind();
185+
if (kind != OVERFLOW) {
186+
// Threads can handle non-`OVERFLOW` events *only after*
187+
// everything is "ready" for that (in which case a token is
188+
// released to the semaphore, which is initially empty). See
189+
// below for an explanation of "readiness".
190+
semaphore.acquireUninterruptibly();
191+
switch (e.getKind()) {
192+
case CREATED:
193+
nCreated.incrementAndGet();
194+
break;
195+
case MODIFIED:
196+
nModified.incrementAndGet();
197+
break;
198+
case DELETED:
199+
nDeleted.incrementAndGet();
200+
break;
201+
default:
202+
break;
203+
}
204+
semaphore.release();
186205
}
187206
});
188207

189208
try (var watch = watchConfig.start()) {
209+
// At this point, the index of last-modified-times inside `watch` is
210+
// populated with initial values.
211+
212+
Files.writeString(directory.resolve("a.txt"), "foo");
213+
Files.writeString(directory.resolve("b.txt"), "bar");
214+
Files.delete(directory.resolve("c.txt"));
215+
Files.createFile(directory.resolve("d.txt"));
216+
// At this point, regular events have been generated for a.txt,
217+
// b.txt, c.txt, and d.txt by the file system. These events won't be
218+
// handled by `watch` just yet, though, because the semaphore is
219+
// still empty (i.e., event-handling threads are blocked from making
220+
// progress). Thus, the index inside `watch` still contains the
221+
// initial last-modified-times. (Warning: The blockade works only
222+
// when the rescanner runs after the user-defined event-handler.
223+
// Currently, this is the case, but changing their order probably
224+
// breaks this test.)
225+
190226
var overflow = new WatchEvent(WatchEvent.Kind.OVERFLOW, directory);
191227
((EventHandlingWatch) watch).handleEvent(overflow);
192-
193228
Thread.sleep(TestHelper.NORMAL_WAIT.toMillis());
194-
await("Overflow shouldn't trigger created events")
195-
.until(nCreated::get, Predicate.isEqual(0));
196-
await("Overflow shouldn't trigger modified events")
197-
.until(nModified::get, Predicate.isEqual(0));
229+
// At this point, the current thread has presumably slept long
230+
// enough for the `OVERFLOW` event to have been handled by the
231+
// rescanner. This means that synthetic events must have been issued
232+
// (because the index still contained the initial last-modified
233+
// times).
234+
235+
// Readiness achieved: Threads can now start handling non-`OVERFLOW`
236+
// events.
237+
semaphore.release();
198238

239+
await("Overflow should trigger created events")
240+
.until(nCreated::get, Predicate.isEqual(2)); // 1 synthetic event + 1 regular event
241+
await("Overflow should trigger modified events")
242+
.until(nModified::get, Predicate.isEqual(4)); // 2 synthetic events + 2 regular events
243+
await("Overflow should trigger deleted events")
244+
.until(nDeleted::get, Predicate.isEqual(2)); // 1 synthetic event + 1 regular event
245+
246+
// Let's do some more file operations, trigger another `OVERFLOW`
247+
// event, and observe that synthetic events *aren't* issued this
248+
// time (because the index was already updated when the regular
249+
// events were handled).
199250
Files.writeString(directory.resolve("b.txt"), "baz");
200-
await("File write should trigger modified event")
201-
.until(nModified::get, Predicate.isEqual(1));
251+
Files.createFile(directory.resolve("c.txt"));
252+
Files.delete(directory.resolve("d.txt"));
253+
254+
await("File create should trigger regular created event")
255+
.until(nCreated::get, Predicate.isEqual(3));
256+
await("File write should trigger regular modified event")
257+
.until(nModified::get, Predicate.isEqual(5));
258+
await("File delete should trigger regular deleted event")
259+
.until(nDeleted::get, Predicate.isEqual(3));
202260

203261
((EventHandlingWatch) watch).handleEvent(overflow);
204262
Thread.sleep(TestHelper.NORMAL_WAIT.toMillis());
205-
await("Overflow shouldn't trigger created event after file write (and index updated)")
206-
.until(nCreated::get, Predicate.isEqual(0));
207-
await("Overflow shouldn't trigger modified event after file write (and index updated)")
208-
.until(nModified::get, Predicate.isEqual(1)); // Still 1 (because of the real MODIFIED)
263+
264+
await("Overflow shouldn't trigger synthetic created event after file create (and index updated)")
265+
.until(nCreated::get, Predicate.isEqual(3));
266+
await("Overflow shouldn't trigger synthetic modified event after file write (and index updated)")
267+
.until(nModified::get, Predicate.isEqual(5));
268+
await("Overflow shouldn't trigger synthetic deleted event after file delete (and index updated)")
269+
.until(nDeleted::get, Predicate.isEqual(3));
209270
}
210271
}
211272
}

0 commit comments

Comments
 (0)