Closed keynmol closed 3 years ago
Hm, this test passes (on OS X):
test("supports watching multiple files".only) {
Stream
.resource((Watcher.default[IO], tempFile, tempFile).tupled)
.flatMap { case (w, f1, f2) =>
val events = Stream(f1, f2)
.covary[IO]
.foreach(f => w.watch(f, modifiers = modifiers).void) ++ w.events()
events
.scan(0) {
case (cnt, Watcher.Event.Modified(_, _)) =>
cnt + 1
case (cnt, _) =>
cnt
}
.takeWhile(_ < 2)
.concurrently(
smallDelay ++ Stream.eval(modify(f1)) ++ smallDelay ++ Stream.eval(modify(f2))
)
}
.compile
.drain
}
This works too, which registers the watches after the event stream is opened:
Stream
.resource((Watcher.default[IO], tempFile, tempFile).tupled)
.flatMap { case (w, f1, f2) =>
w.events()
.scan(0) {
case (cnt, Watcher.Event.Modified(_, _)) =>
cnt + 1
case (cnt, _) =>
cnt
}
.takeWhile(_ < 2)
.concurrently(
smallDelay ++ Stream
.exec(List(f1, f2).traverse(f => w.watch(f, modifiers = modifiers)).void) ++
smallDelay ++ Stream.eval(modify(f1)) ++ smallDelay ++ Stream.eval(modify(f2))
)
}
.compile
.drain
hmm.
I'm on Linux - and given it's filesystem specific, could it be the cause? Scastie presumably runs on Linux as well.
Do you see anything obviously wrong in my snippet? In my case I open event stream after registering all the watchers (like your first example)
Also, if you have this test in a branch - would be interesting to see if CI's ubuntu runner passes it as well.
Right, I ran my snippet on my OS X machine, it printed out an empty List and laptop crashed :D So I'd say inconclusive
Too funny. I'll open a PR with the test above and see how GHA handles it.
I checked - that branch also passes on my machine, but not if I apply this patch (test times out after 30 seconds):
diff --git a/io/src/test/scala/fs2/io/file/WatcherSuite.scala b/io/src/test/scala
/fs2/io/file/WatcherSuite.scala
index b7ae13cb6..aa8e0cdef 100644
--- a/io/src/test/scala/fs2/io/file/WatcherSuite.scala
+++ b/io/src/test/scala/fs2/io/file/WatcherSuite.scala
@@ -71,10 +71,15 @@ class WatcherSuite extends Fs2Suite with BaseFileSuite {
}
test("supports watching multiple files") {
+ val setup = tempDirectory.flatMap {dir =>
+ val files = cats.effect.Resource.eval(aFile(dir).product(aFile(dir)))
+ Watcher.default[IO].product(files)
+ }
+
Stream
- .resource((Watcher.default[IO], tempFile, tempFile).tupled)
- .flatMap { case (w, f1, f2) =>
- w.events()
+ .resource(setup)
+ .flatMap { case (w, (f1, f2)) =>
+ w.events().debug()
.scan(0) {
case (cnt, Watcher.Event.Modified(_, _)) =>
cnt + 1
I think files being in the same folder is key here - which is how my snippet is different from the current test
OK the bug is roughly here: https://github.com/typelevel/fs2/blob/6a625c5c7f60857a5b9773c26692bd6c8ac2ba3a/io/src/main/scala/fs2/io/file/Watcher.scala#L294-L301
Events for both paths get fired here but the earlier registered path gets filtered out in that code.
Aha, because both paths have the same WatchKey
This issue is caused by WatchService
only supporting watching directories, not individual files (https://stackoverflow.com/questions/16251273/can-i-watch-for-single-file-change-with-watchservice-not-the-whole-directory#comment37744531_16251273). We can support watching individual files but we'll need to change some internal structure of Watcher
to store multiple watched paths for an individual WatchKey
.
Fixed in #2382
fs2 3.0.2
The way I understood how
Watcher
can be used is:As long as the watcher resource is open, calling
w.watch(path)
(where path is a file) on it should add files to the list of watches. Andw.events()
would be a stream of events coming from both files.What I think is happening instead, is only the last path added via
w.watch(path)
receives notifications.Here's a reproduction as a self-contained ammonite script (alternatively, here it is in a Scastie: https://scastie.scala-lang.org/KiUg7NdURt6J7ULU8R3CUw) :
It outputs
whereas I would expect events from both paths.
If you switch the order of
w.watch
calls, you'll see notifications for the other file.If you replace both calls with one
w.watch(temp)
(containing folder), you'll see notifications for both files