This commit is contained in:
swrup 2025-11-11 02:07:51 +01:00
parent aa2ff7b2f0
commit 2f3113f55d
11742 changed files with 1223940 additions and 0 deletions

View file

@ -0,0 +1,79 @@
Basic tests for the file-watching mode.
$ . ./helpers.sh
----------------------------------------------------------------------------------
* Compile a simple rule
$ echo "(lang dune 2.0)" > dune-project
$ cat > x <<EOF
> original-contents
> EOF
$ cat >dune <<EOF
> (rule
> (target y)
> (deps x)
> (action (system "cat x > y")))
> EOF
$ start_dune
$ build y
Success
$ cat _build/default/y
original-contents
$ echo new-contents > x
$ build y
Success
$ cat _build/default/y
new-contents
$ echo new-contents2 > x
$ build y
Success
$ cat _build/default/y
new-contents2
----------------------------------------------------------------------------------
* File rename
$ mv x z
$ build y
Failure
$ echo new-contents3 > z
$ build y
Failure
$ mv z x
$ build y
Success
$ cat _build/default/y
new-contents3
$ with_timeout dune shutdown
$ cat .#dune-output
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
File "dune", lines 1-4, characters 0-59:
1 | (rule
2 | (target y)
3 | (deps x)
4 | (action (system "cat x > y")))
Error: No rule found for x
Had 1 error, waiting for filesystem changes...
File "dune", lines 1-4, characters 0-59:
1 | (rule
2 | (target y)
3 | (deps x)
4 | (action (system "cat x > y")))
Error: No rule found for x
Had 1 error, waiting for filesystem changes...
Success, waiting for filesystem changes...

View file

@ -0,0 +1,81 @@
Test rules that copy source files in file-watching mode.
$ . ./helpers.sh
$ echo '(lang dune 3.0)' > dune-project
$ cat > dune <<EOF
> (rule
> (deps (glob_files *.txt))
> (target summary)
> (action (system "cat %{deps} > %{target}")))
> EOF
$ echo a > a.txt
$ start_dune
$ build summary
Success
$ cat _build/default/summary
a
Add [b.txt] manually. Dune notices this.
$ echo b > b.txt
$ build summary
Success
$ cat _build/default/summary
a
b
Now add [c.txt] via a new rule. Note that we do not request [c.txt] to be built,
only [summary], but Dune still builds it. Presumably, this isn't a bug but a
consequence of using a glob in this directory, which forces all *.txt rules.
$ cat > dune <<EOF
> (rule
> (deps (glob_files *.txt))
> (target summary)
> (action (system "cat %{deps} > %{target}")))
> (rule
> (target c.txt)
> (action (write-file %{target} "c\n")))
> EOF
$ build summary
Success
$ cat _build/default/summary
a
b
c
Dune notices that [d.txt] appears via promotion.
$ mkdir subdir
$ cat > subdir/dune <<EOF
> (rule
> (target d.txt)
> (mode (promote (into ..)))
> (action (write-file %{target} "d\n")))
> EOF
$ build summary subdir/d.txt
Success
$ cat _build/default/summary
a
b
c
d
Note that [d.txt] is here but [c.txt] isn't (it's not promoted).
$ ls *.txt
a.txt
b.txt
d.txt
We're done.
$ stop_dune
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...

View file

@ -0,0 +1,152 @@
Test directory target promotion in file-watching mode.
$ . ./helpers.sh
$ echo '(lang dune 3.0)' > dune-project
$ mkdir test; cd test
$ cat > dune-project <<EOF
> (lang dune 3.0)
> (using directory-targets 0.1)
> EOF
$ cat > dune <<EOF
> (rule
> (mode promote)
> (deps src (sandbox always))
> (targets (dir d1))
> (action (system "mkdir -p d1/d2; cp src d1/a; cp src d1/b; cp src d1/d2/c")))
> EOF
$ echo -n "*" > src
$ start_dune
$ build d1
Success
$ cat d1/a d1/b d1/d2/c
***
Change [src] and rebuild.
$ echo -n "+" > src
$ build d1
Success
$ cat d1/a d1/b d1/d2/c
+++
Remove a file and rebuild.
$ rm d1/a
$ build d1
Success
$ cat d1/a d1/b d1/d2/c
+++
Remove a directory and rebuild.
$ rm -rf d2
$ build d1
Success
$ cat d1/a d1/b d1/d2/c
+++
Modify a file and rebuild.
$ echo -n "*" > d1/b
$ cat d1/a d1/b d1/d2/c
+*+
$ build d1
Success
$ cat d1/a d1/b d1/d2/c
+++
Replace a directory with a file and rebuild.
$ rm -rf d1/d2
$ touch d1/d2
$ build d1
Success
$ cat d1/a d1/b d1/d2/c
+++
Replace a file with a directory and rebuild.
$ rm d1/b
$ mkdir -p d1/b
$ build d1
Success
$ cat d1/a d1/b d1/d2/c
+++
Add some unexpected files and directories and check that Dune deletes them.
$ mkdir -p d1/unexpected-dir-1
$ mkdir -p d1/d2/unexpected-dir-2
$ touch d1/unexpected-file-1
$ touch d1/unexpected-dir-1/unexpected-file-2
$ touch d1/d2/unexpected-file-3
$ build d1
Success
$ cat d1/a d1/b d1/d2/c
+++
$ ls d1 | grep unexpected
[1]
$ ls d1/d2 | grep unexpected
[1]
We're done.
$ stop_dune
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
Now test file-system events generated during directory target promotion.
$ rm -rf d1
$ start_dune --debug-cache=fs
$ build d1
Success
$ stop_dune > .#debug-output
Show that Dune ignores the initial "dune-workspace" events (injected by Dune).
$ cat .#debug-output | grep dune-workspace
Updating dir_contents cache for "dune-workspace": Skipped
Updating file_digest cache for "dune-workspace": Skipped
Updating path_stat cache for "dune-workspace": Updated { changed = false }
Dune correctly notices that the contents of . changed because [d1] was created.
$ cat .#debug-output | grep '"."'
Updating dir_contents cache for ".": Updated { changed = true }
Updating file_digest cache for ".": Skipped
Updating path_stat cache for ".": Updated { changed = false }
Here [path_stat] of [d1] changed, because it didn't exist before the build. Dune
later recomputed [path_stat] once again, when [d1/b] was modified, and the
result remained unchanged (because fields like [mtime] are ignored). The result
of [dir_contents] also remained unchanged because Dune fixed the listing of [d1]
by re-promoting the directory target.
$ cat .#debug-output | grep \"d1\"
Updating dir_contents cache for "d1": Updated { changed = false }
Updating file_digest cache for "d1": Skipped
Updating path_stat cache for "d1": Updated { changed = true }
Updating dir_contents cache for "d1": Updated { changed = false }
Updating file_digest cache for "d1": Skipped
Updating path_stat cache for "d1": Updated { changed = false }
Events below occurred because we replaced file [d1/b] with a directory. Dune
undid this change to bring the promoted directory target up to date, which
explains why [file_digest] remained unchanged.
$ cat .#debug-output | grep d1/b
Updating dir_contents cache for "d1/b": Skipped
Updating file_digest cache for "d1/b": Updated { changed = false }
Updating path_stat cache for "d1/b": Skipped

View file

@ -0,0 +1,27 @@
Demonstrate running "dune promote" concurrently with a passive rpc server
$ . ./helpers.sh
$ echo '(lang dune 3.20)' > dune-project
$ echo " $ echo hello" > my_test.t
$ start_dune
The test expectedly fails (see errors at the end of this file)
$ build "(alias my_test)"
Failure
Promotion happens on the running RPC server.
$ dune promote
Success
$ build "(alias my_test)"
Success
$ stop_dune
File "my_test.t", line 1, characters 0-0:
Error: Files _build/default/my_test.t and _build/default/my_test.t.corrected
differ.
Had 1 error, waiting for filesystem changes...
Promoting _build/default/my_test.t.corrected to my_test.t.
Success, waiting for filesystem changes...

View file

@ -0,0 +1,26 @@
(cram
(deps helpers.sh))
(cram
; see https://github.com/ocaml/dune/pull/4728
(enabled_if
(<> "macosx" %{ocaml-config:system}))
(applies_to * \ path-pwd))
(cram
; see https://github.com/ocaml/dune/pull/4728
(enabled_if
(= "macosx" %{ocaml-config:system}))
(applies_to path-pwd))
(cram
(applies_to what-dune-watches)
(deps %{bin:strace}))
;; These tests sometimes get stuck and time out:
(cram
(applies_to
watching-eager-concurrent-build-command
watching-eager-concurrent-exec-command)
(enabled_if false))

View file

@ -0,0 +1,467 @@
Tests for [Fs_memo] module.
$ . ./helpers.sh
$ test () {
> echo "------------------------------------------"
> before=$(cat _build/default/result 2>/dev/null)
> start_dune --debug-cache=fs
> build . | grep -v Success
> between=$(cat _build/default/result)
> eval "$@"
> build . | grep -v Success
> stop_dune | grep -v dune-workspace >> .#tmp
> after=$(cat _build/default/result)
> cat .#tmp | grep -v Updating
> echo "------------------------------------------"
> echo "result = '$before' -> '$between' -> '$after'"
> echo "------------------------------------------"
> cat .#tmp | grep Updating | sort
> rm .#tmp
> }
The action ignores the dependency [dep]. We use it to force rerunning the action
when necessary.
$ echo "(lang dune 2.0)" > dune-project
$ cat >dune <<EOF
> (rule
> (alias default)
> (deps dep
> (glob_files file-?)
> (glob_files dir/file-?)
> (glob_files dir/subdir/file-?))
> (target result)
> (action (system "\| echo Executing rule...
> "\| echo %{deps} |
> "\| tr ' ' '\n' |
> "\| xargs -n 1 |
> "\| grep -v dep |
> "\| xargs cat > result
> )))
> EOF
$ echo -n 1 > file-1
$ touch dep
Note that we receive two events for [file-2] because it's first created empty
and then the contents is written to it.
$ test "echo -n 2 > file-2"
------------------------------------------
Executing rule...
Success, waiting for filesystem changes...
Executing rule...
Success, waiting for filesystem changes...
------------------------------------------
result = '' -> '1' -> '12'
------------------------------------------
Updating dir_contents cache for ".": Updated { changed = true }
Updating dir_contents cache for "file-2": Skipped
Updating dir_contents cache for "file-2": Skipped
Updating file_digest cache for ".": Skipped
Updating file_digest cache for "file-2": Skipped
Updating file_digest cache for "file-2": Skipped
Updating path_stat cache for ".": Updated { changed = false }
Updating path_stat cache for "file-2": Skipped
Updating path_stat cache for "file-2": Skipped
Note that Dune did not re-execute the rule because the set of files matching
the glob remains unchanged.
$ test "mkdir dir"
------------------------------------------
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
------------------------------------------
result = '12' -> '12' -> '12'
------------------------------------------
Updating dir_contents cache for ".": Updated { changed = true }
Updating dir_contents cache for "dir": Skipped
Updating file_digest cache for ".": Skipped
Updating file_digest cache for "dir": Skipped
Updating path_stat cache for ".": Updated { changed = false }
Updating path_stat cache for "dir": Skipped
We create [dir/file-3] before running Dune, so we only observe a single
[file_digest] change event with the file watcher.
$ echo -n '?' > dir/file-3
$ test "echo -n 3 > dir/file-3"
------------------------------------------
Executing rule...
Success, waiting for filesystem changes...
Executing rule...
Success, waiting for filesystem changes...
------------------------------------------
result = '12' -> '12?' -> '123'
------------------------------------------
Updating dir_contents cache for "dir/file-3": Skipped
Updating file_digest cache for "dir/file-3": Updated { changed = true }
Updating path_stat cache for "dir/file-3": Skipped
Now, Dune similarly updates [file_digest] for [file-2].
$ test "echo -n '*' > file-2"
------------------------------------------
Success, waiting for filesystem changes...
Executing rule...
Success, waiting for filesystem changes...
------------------------------------------
result = '123' -> '123' -> '1*3'
------------------------------------------
Updating dir_contents cache for "file-2": Skipped
Updating file_digest cache for "file-2": Updated { changed = true }
Updating path_stat cache for "file-2": Skipped
On deletion of a file, we receive events for the file and the parent directory.
$ test "rm file-2"
------------------------------------------
Success, waiting for filesystem changes...
Executing rule...
Success, waiting for filesystem changes...
------------------------------------------
result = '1*3' -> '1*3' -> '13'
------------------------------------------
Updating dir_contents cache for ".": Updated { changed = true }
Updating dir_contents cache for "file-2": Skipped
Updating file_digest cache for ".": Skipped
Updating file_digest cache for "file-2": Updated { changed = true }
Updating path_stat cache for ".": Updated { changed = false }
Updating path_stat cache for "file-2": Skipped
Dune notices that [dir_contents] of both [dir] and [.] changed, and also that
[dir/file-3]'s digest changed (from a digest to the error about missing file).
$ test "mv dir/file-3 ."
------------------------------------------
Success, waiting for filesystem changes...
Executing rule...
Success, waiting for filesystem changes...
------------------------------------------
result = '13' -> '13' -> '13'
------------------------------------------
Updating dir_contents cache for ".": Updated { changed = true }
Updating dir_contents cache for "dir": Updated { changed = true }
Updating dir_contents cache for "dir/file-3": Skipped
Updating dir_contents cache for "file-3": Skipped
Updating file_digest cache for ".": Skipped
Updating file_digest cache for "dir": Skipped
Updating file_digest cache for "dir/file-3": Updated { changed = true }
Updating file_digest cache for "file-3": Skipped
Updating path_stat cache for ".": Updated { changed = false }
Updating path_stat cache for "dir": Updated { changed = false }
Updating path_stat cache for "dir/file-3": Skipped
Updating path_stat cache for "file-3": Skipped
$ test "mkdir dir/subdir"
------------------------------------------
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
------------------------------------------
result = '13' -> '13' -> '13'
------------------------------------------
Updating dir_contents cache for "dir": Updated { changed = true }
Updating dir_contents cache for "dir/subdir": Skipped
Updating file_digest cache for "dir": Skipped
Updating file_digest cache for "dir/subdir": Skipped
Updating path_stat cache for "dir": Updated { changed = false }
Updating path_stat cache for "dir/subdir": Skipped
Again, there are two events for [file-4]: for creation and modification.
$ test "echo -n 4 > dir/subdir/file-4"
------------------------------------------
Success, waiting for filesystem changes...
Executing rule...
Success, waiting for filesystem changes...
------------------------------------------
result = '13' -> '13' -> '134'
------------------------------------------
Updating dir_contents cache for "dir/subdir": Updated { changed = true }
Updating dir_contents cache for "dir/subdir/file-4": Skipped
Updating dir_contents cache for "dir/subdir/file-4": Skipped
Updating file_digest cache for "dir/subdir": Skipped
Updating file_digest cache for "dir/subdir/file-4": Skipped
Updating file_digest cache for "dir/subdir/file-4": Skipped
Updating path_stat cache for "dir/subdir": Updated { changed = false }
Updating path_stat cache for "dir/subdir/file-4": Skipped
Updating path_stat cache for "dir/subdir/file-4": Skipped
Here we are getting duplicate events for directories [dir] and [dir/subdir]
because we watch each of them both directly and via their parents.
$ test "mv dir/subdir ."
------------------------------------------
Success, waiting for filesystem changes...
Executing rule...
Success, waiting for filesystem changes...
------------------------------------------
result = '134' -> '134' -> '13'
------------------------------------------
Updating dir_contents cache for ".": Updated { changed = true }
Updating dir_contents cache for "dir": Updated { changed = false }
Updating dir_contents cache for "dir": Updated { changed = true }
Updating dir_contents cache for "dir/subdir": Updated { changed = false }
Updating dir_contents cache for "dir/subdir": Updated { changed = true }
Updating dir_contents cache for "subdir": Skipped
Updating file_digest cache for ".": Skipped
Updating file_digest cache for "dir": Skipped
Updating file_digest cache for "dir": Skipped
Updating file_digest cache for "dir/subdir": Skipped
Updating file_digest cache for "dir/subdir": Skipped
Updating file_digest cache for "subdir": Skipped
Updating path_stat cache for ".": Updated { changed = false }
Updating path_stat cache for "dir": Updated { changed = false }
Updating path_stat cache for "dir": Updated { changed = false }
Updating path_stat cache for "dir/subdir": Updated { changed = false }
Updating path_stat cache for "dir/subdir": Updated { changed = true }
Updating path_stat cache for "subdir": Skipped
This seems to be a bug: Dune doesn't notice that a directory's permission
changed and succeeds instead of failing.
$ test "chmod -r subdir"
------------------------------------------
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
------------------------------------------
result = '13' -> '13' -> '13'
------------------------------------------
If we repeat the test, we finally see the failure.
# CR-someday amokhov: Fix this.
$ test "echo How about now?"
------------------------------------------
Failure
How about now?
Failure
Error: inotify_add_watch(subdir): Permission denied
Had 1 error, waiting for filesystem changes...
Error: inotify_add_watch(subdir): Permission denied
Had 1 error, waiting for filesystem changes...
------------------------------------------
result = '13' -> '13' -> '13'
------------------------------------------
Same problem in the other direction.
$ test "chmod +r subdir"
------------------------------------------
Failure
Failure
Error: inotify_add_watch(subdir): Permission denied
Had 1 error, waiting for filesystem changes...
Error: inotify_add_watch(subdir): Permission denied
Had 1 error, waiting for filesystem changes...
------------------------------------------
result = '13' -> '13' -> '13'
------------------------------------------
$ test "echo How about now?"
------------------------------------------
How about now?
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
------------------------------------------
result = '13' -> '13' -> '13'
------------------------------------------
Same problem for files.
$ test "chmod -r file-1"
------------------------------------------
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
------------------------------------------
result = '13' -> '13' -> '13'
------------------------------------------
$ test "chmod +r file-1"
------------------------------------------
Failure
Failure
Error: file-1: Permission denied
-> required by _build/default/file-1
-> required by _build/default/result
-> required by alias default
Had 1 error, waiting for filesystem changes...
Error: file-1: Permission denied
-> required by _build/default/file-1
-> required by _build/default/result
-> required by alias default
Had 1 error, waiting for filesystem changes...
------------------------------------------
result = '13' -> '13' -> '13'
------------------------------------------
Dune receives one event for [file-1] when moving it within the same directory,
and two events for [file-5]: for moving and for changing. There are two events
for [dir_contents] of [.] because moving a file is interpreted as deleting and
then creating a file.
$ test "mv file-1 file-5; echo -n 5 > file-5"
------------------------------------------
Success, waiting for filesystem changes...
Executing rule...
Success, waiting for filesystem changes...
------------------------------------------
result = '13' -> '13' -> '35'
------------------------------------------
Updating dir_contents cache for ".": Updated { changed = false }
Updating dir_contents cache for ".": Updated { changed = true }
Updating dir_contents cache for "file-1": Skipped
Updating dir_contents cache for "file-5": Skipped
Updating dir_contents cache for "file-5": Skipped
Updating file_digest cache for ".": Skipped
Updating file_digest cache for ".": Skipped
Updating file_digest cache for "file-1": Updated { changed = true }
Updating file_digest cache for "file-5": Skipped
Updating file_digest cache for "file-5": Skipped
Updating path_stat cache for ".": Updated { changed = false }
Updating path_stat cache for ".": Updated { changed = false }
Updating path_stat cache for "file-1": Skipped
Updating path_stat cache for "file-5": Skipped
Updating path_stat cache for "file-5": Skipped
Tests for watching symbolic links.
First, create a symbolic link. Dune correctly updates the [result].
$ test "ln -s ../file-3 dir/file-6"
------------------------------------------
Success, waiting for filesystem changes...
Executing rule...
Success, waiting for filesystem changes...
------------------------------------------
result = '35' -> '35' -> '353'
------------------------------------------
Updating dir_contents cache for "dir": Updated { changed = true }
Updating dir_contents cache for "dir/file-6": Skipped
Updating file_digest cache for "dir": Skipped
Updating file_digest cache for "dir/file-6": Skipped
Updating path_stat cache for "dir": Updated { changed = false }
Updating path_stat cache for "dir/file-6": Skipped
Now, delete the symbolic link. Dune receives the corresponding events and
reruns the affected action.
$ test "rm dir/file-6"
------------------------------------------
Success, waiting for filesystem changes...
Executing rule...
Success, waiting for filesystem changes...
------------------------------------------
result = '353' -> '353' -> '35'
------------------------------------------
Updating dir_contents cache for "dir": Updated { changed = true }
Updating dir_contents cache for "dir/file-6": Skipped
Updating file_digest cache for "dir": Skipped
Updating file_digest cache for "dir/file-6": Updated { changed = true }
Updating path_stat cache for "dir": Updated { changed = false }
Updating path_stat cache for "dir/file-6": Updated { changed = true }
Now test symbolic links to directories.
$ mkdir another-dir
$ rmdir dir
$ ln -s another-dir dir
At first, things appear to be working well: we correctly discover [dir/file-7]
and re-execute the rule.
$ test "echo -n 7 > another-dir/file-7"
------------------------------------------
Success, waiting for filesystem changes...
Executing rule...
Success, waiting for filesystem changes...
------------------------------------------
result = '35' -> '35' -> '357'
------------------------------------------
Updating dir_contents cache for "dir": Updated { changed = true }
Updating dir_contents cache for "dir/file-7": Skipped
Updating dir_contents cache for "dir/file-7": Skipped
Updating file_digest cache for "dir": Skipped
Updating file_digest cache for "dir/file-7": Skipped
Updating file_digest cache for "dir/file-7": Skipped
Updating path_stat cache for "dir": Updated { changed = false }
Updating path_stat cache for "dir/file-7": Skipped
Updating path_stat cache for "dir/file-7": Skipped
Deleting [dir] triggers a rebuild.
$ test "rm dir"
------------------------------------------
Success, waiting for filesystem changes...
Executing rule...
Success, waiting for filesystem changes...
------------------------------------------
result = '357' -> '357' -> '35'
------------------------------------------
Updating dir_contents cache for ".": Updated { changed = true }
Updating dir_contents cache for "dir": Updated { changed = true }
Updating file_digest cache for ".": Skipped
Updating file_digest cache for "dir": Skipped
Updating path_stat cache for ".": Updated { changed = false }
Updating path_stat cache for "dir": Updated { changed = true }
Restoring the symlink is correctly noticed.
$ test "ln -s another-dir dir"
------------------------------------------
Success, waiting for filesystem changes...
Executing rule...
Success, waiting for filesystem changes...
------------------------------------------
result = '35' -> '35' -> '357'
------------------------------------------
Updating dir_contents cache for ".": Updated { changed = true }
Updating dir_contents cache for "dir": Skipped
Updating file_digest cache for ".": Skipped
Updating file_digest cache for "dir": Skipped
Updating path_stat cache for ".": Updated { changed = false }
Updating path_stat cache for "dir": Skipped
However, deleting [another-dir] isn't handled correctly.
# CR-someday amokhov: Fix this.
$ test "rm another-dir/file-7; sleep 0.001; rmdir another-dir"
------------------------------------------
Success, waiting for filesystem changes...
Executing rule...
Success, waiting for filesystem changes...
------------------------------------------
result = '357' -> '357' -> '35'
------------------------------------------
Updating dir_contents cache for ".": Updated { changed = true }
Updating dir_contents cache for "another-dir": Updated { changed = true }
Updating dir_contents cache for "dir": Updated { changed = true }
Updating dir_contents cache for "dir/file-7": Skipped
Updating file_digest cache for ".": Skipped
Updating file_digest cache for "another-dir": Skipped
Updating file_digest cache for "dir": Skipped
Updating file_digest cache for "dir/file-7": Updated { changed = true }
Updating path_stat cache for ".": Updated { changed = false }
Updating path_stat cache for "another-dir": Updated { changed = true }
Updating path_stat cache for "dir": Updated { changed = false }
Updating path_stat cache for "dir/file-7": Skipped
If we force a rebuild, Dune belatedly notices that [another-dir/file-7] is now
unreachable but doesn't complain about the symlink [dir] now being broken. We
should fix this too.
$ test "echo force > dep"
------------------------------------------
Success, waiting for filesystem changes...
Executing rule...
Success, waiting for filesystem changes...
------------------------------------------
result = '35' -> '35' -> '35'
------------------------------------------
Updating dir_contents cache for "dep": Skipped
Updating file_digest cache for "dep": Updated { changed = true }
Updating path_stat cache for "dep": Skipped

View file

@ -0,0 +1,40 @@
Reproduction case for #4923: weird things observed when we add/remove
a file in watch mode.
$ . ./helpers.sh
$ echo '(lang dune 3.0)' > dune-project
$ mkdir a b
$ cat > a/dune <<EOF
> (library (name a))
> EOF
$ cat > b/dune <<EOF
> (library (name b))
> EOF
$ touch a/a.ml b/b.ml
$ cat > dune <<EOF
> (executable (name x) (libraries b))
> EOF
$ echo 'print_endline "Hello, world!"' > x.ml
$ start_dune
$ build x.exe
Success
$ _build/default/x.exe
Hello, world!
In the past, adding a new file while Dune was running in watching mode
was corrupting Dune's internal state. For instance, here it no longer
knew how to build x.exe. This is now fixed.
$ touch b/blah
$ build x.exe
Success
$ _build/default/x.exe
Hello, world!
$ stop_dune
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...

View file

@ -0,0 +1,53 @@
A test case for #5064: errors when changing module dependencies in the
file-watching mode.
$ . ./helpers.sh
$ echo '(lang dune 3.0)' > dune-project
$ mkdir lib
$ cat > lib/dune <<EOF
> (library (name lib))
> EOF
$ cat > dune <<EOF
> (executable (name x) (libraries lib))
> EOF
$ echo 'let hello = "Hello"' > lib/a.ml
$ echo 'let world = "World"' > lib/b.ml
$ echo 'let message = A.hello ^ ", " ^ B.world' > lib/lib.ml
$ echo 'print_endline Lib.message' > x.ml
$ start_dune
$ build x.exe
Success
$ _build/default/x.exe
Hello, World
Now let's make [lib/a.ml] depend on [lib/b.ml]. It doesn't work!
$ cat > lib/a.ml <<EOF
> let _ = B.world
> let hello = "Hello"
> EOF
$ build x.exe
Success
Let's try a manual restart.
$ stop_dune
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
$ start_dune
It works now!
$ build x.exe
Success
We're done.
$ stop_dune
Success, waiting for filesystem changes...

View file

@ -0,0 +1,26 @@
$ . ./helpers.sh
This test demonstrates a fixed bug where rules for .install are being duplicated between
rebuilds.
$ cat > dune-project << EOF
> (lang dune 3.11)
> (package
> (allow_empty)
> (name test))
> EOF
$ mkdir src
$ start_dune
$ build .
Success
$ cat > src/a
$ build .
Success
$ stop_dune
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...

View file

@ -0,0 +1,37 @@
$ . ./helpers.sh
This test demonstrates a bug where multiple rules for ccomp and .opam are loaded between
rebuilds.
$ cat > dune-project << EOF
> (lang dune 3.11)
> (package
> (allow_empty)
> (name test))
> (generate_opam_files true)
> EOF
$ mkdir src
$ cat > src/dune << EOF
> (library
> (name dune_rpc_lwt_tests)
> (foreign_stubs
> (language c)
> (names stub)))
> EOF
$ cat > src/stub.c
$ start_dune
$ build .
Success
$ cat > src/a
$ build .
Success
$ stop_dune
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...

View file

@ -0,0 +1,40 @@
DUNE_RUNNING=0
start_dune () {
((dune build "$@" --passive-watch-mode > .#dune-output 2>&1) || (echo exit $? >> .#dune-output)) &
DUNE_PID=$!;
DUNE_RUNNING=1;
}
timeout="$(command -v timeout || echo gtimeout)"
with_timeout () {
$timeout 2 "$@"
exit_code=$?
if [ "$exit_code" = 124 ]
then
echo Timed out
cat .#dune-output
else
return "$exit_code"
fi
}
stop_dune () {
with_timeout dune shutdown;
# On Linux, we may run into a bash pid aliasing bug that causes wait to
# reject the pid. Therefore we use tail to wait instead.
if [ "$(uname -s)" = "Linux" ]
then
# wait for all child processes
tail --pid=$DUNE_PID -f /dev/null;
else
# wait for dune to exit
wait $DUNE_PID;
fi
cat .#dune-output;
}
build () {
with_timeout dune rpc build --wait "$@"
}

View file

@ -0,0 +1,38 @@
We test the behavior of watch mode when we have multiple errors
$ . ./helpers.sh
$ echo "(lang dune 3.11)" > dune-project
$ start_dune
$ cat > x <<EOF
> not so
> EOF
$ cat >dune <<EOF
> (rule
> (action
> (progn
> (write-file y "different")
> (diff x y))))
> (rule
> (action
> (progn
> (write-file z "different")
> (diff x z))))
> (rule
> (deps y z)
> (action
> (write-file w "")))
> EOF
$ build w
Failure
$ stop_dune
File "x", line 1, characters 0-0:
Error: Files _build/default/x and _build/default/y differ.
File "x", line 1, characters 0-0:
Error: Files _build/default/x and _build/default/z differ.
Had 2 errors, waiting for filesystem changes...

View file

@ -0,0 +1,31 @@
This is a bug occur that occurs when we're running dune in watch mode and
adding . to $CWD
Reproduce #6907
$ . ./helpers.sh
$ export PATH=.:$PATH
$ echo "(lang dune 2.0)" > dune-project
$ start_dune
$ cat > x <<EOF
> original-contents
> EOF
$ cat >dune <<EOF
> (rule (with-stdout-to y (echo %{read:x})))
> EOF
$ build y
Success
$ touch x
$ build y
Success
$ stop_dune
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...

View file

@ -0,0 +1,47 @@
Modify an input file during the build so that dune interrupts the
build
$ . ./helpers.sh
$ echo "(lang dune 2.0)" > dune-project
$ cat > x <<EOF
> original-contents
> EOF
$ cat >dune <<'EOF'
> (rule
> (target y)
> (deps x)
> (action (bash "\> if [[ "$(cat x)" == unstable ]]; then
> "\> touch ../../go-ahead
> "\> sleep 1000
> "\> exit 1
> "\> else
> "\> cat x > y
> "\> fi
> )))
> EOF
The rule above makes the test hang if it sees an "unstable" state of
the file. This makes it easy to make sure that the dune won't finish
before we're able to cancel the build.
$ start_dune
$ build y
Success
$ cat _build/default/y
original-contents
$ echo unstable > x
$ (dune_cmd wait-for-file-to-appear go-ahead; echo new-contents > x) & build y
Success
$ cat _build/default/y
new-contents
$ stop_dune
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...

View file

@ -0,0 +1,36 @@
Modify an input file during the build so that Dune interrupts the build once.
$ . ./helpers.sh
Bad rule! You are not supposed to modify the source tree. No ice-cream for you!
$ mkdir test
$ cd test
$ echo '(lang dune 3.0)' > dune-project
$ cat > dune <<EOF
> (rule
> (deps (glob_files *.txt) (sandbox none))
> (alias default)
> (action (system "\| echo "I'm seeing: %{deps}" >> ../../../output
> "\| touch ../../new-source.txt
> )))
> EOF
$ touch old-source.txt
$ start_dune
$ build .
Success
We can see in the output below that the rule ran exactly twice. Note that the
file watcher generates the next touch event for new-source.txt but it
is ignored because the contents of new-source.txt is the same,
i.e. the empty file.
$ cat ../output
I'm seeing: old-source.txt
I'm seeing: new-source.txt old-source.txt
$ stop_dune
Success, waiting for filesystem changes...

View file

@ -0,0 +1,38 @@
Test that Dune mkdirs the right set of directories in the sandbox.
$ . ./helpers.sh
----------------------------------------------------------------------------------
* Compile a simple rule
$ echo "(lang dune 2.0)" > dune-project
$ mkdir test
$ cat >test/dune <<EOF
> (rule
> (target target)
> (mode promote)
> (deps (sandbox always))
> (action (chdir subdir (system "echo hello > ../target; pwd"))))
> EOF
$ start_dune
$ build test/target
Success
$ cat test/target
hello
Now force a rebuild. This succeeds (in the past it could fail due to [mkdir]
memoization).
$ rm _build/default/test/target test/target
$ build test
Success
$ with_timeout dune shutdown
$ cat .#dune-output | sed -e 's#.sandbox/[^/]*/default/test/subdir#.sandbox/<hash>/default/test/subdir#'
$TESTCASE_ROOT/_build/.sandbox/<hash>/default/test/subdir
Success, waiting for filesystem changes...
$TESTCASE_ROOT/_build/.sandbox/<hash>/default/test/subdir
Success, waiting for filesystem changes...

View file

@ -0,0 +1,10 @@
We demonstrate that dune can be turned off by sigpipe
$ echo '(lang dune 3.0)' > dune-project
$ touch dout
$ dune build --passive-watch-mode > dout 2>&1 &
$ pid=$!
$ kill -s PIPE $pid
$ wait $pid
[141]
$ cat dout

View file

@ -0,0 +1,200 @@
Test target promotion in file-watching mode.
$ . ./helpers.sh
$ echo '(lang dune 3.0)' > dune-project
$ cat > dune <<EOF
> (rule
> (mode promote)
> (deps original)
> (target promoted)
> (action (copy %{deps} %{target})))
> (rule
> (deps promoted)
> (target result)
> (action (system "cat promoted promoted > result")))
> EOF
$ echo hi > original
$ start_dune
$ build result
Success
$ cat promoted
hi
$ cat _build/default/result
hi
hi
Now change the [original] and rebuild.
$ echo bye > original
$ build result
Success
$ cat promoted
bye
$ cat _build/default/result
bye
bye
Now try deleting the promoted file.
$ rm promoted
$ build result
Success
$ cat promoted
bye
$ cat _build/default/result
bye
bye
Now try replacing its content.
$ echo hi > promoted
$ build result
Success
$ cat promoted
bye
$ cat _build/default/result
bye
bye
Now switch the mode to standard. Dune reports an error about multiple rules for
[_build/default/promoted], as expected (see the error at the end of the test).
$ cat > dune <<EOF
> (rule
> (mode standard)
> (deps original)
> (target promoted)
> (action (copy %{deps} %{target})))
> (rule
> (deps promoted)
> (target result)
> (action (system "cat promoted promoted > result")))
> EOF
$ build result
Failure
We use the hint and it starts to work.
$ rm -f promoted
$ build result
Success
$ cat promoted
cat: promoted: No such file or directory
[1]
$ cat _build/default/promoted
bye
$ cat _build/default/result
bye
bye
Now use [fallback] to override the rule that generates [promoted].
$ cat > dune <<EOF
> (rule
> (mode fallback)
> (deps original)
> (target promoted)
> (action (copy %{deps} %{target})))
> (rule
> (deps promoted)
> (target result)
> (action (system "cat promoted promoted > result")))
> EOF
At first, we don't have the source, so the rule is used.
$ build result
Success
$ cat promoted
cat: promoted: No such file or directory
[1]
$ cat _build/default/promoted
bye
$ cat _build/default/result
bye
bye
Now we create the source file and it overrides the rule.
$ echo hi > promoted
$ build result
Success
$ cat promoted
hi
$ cat _build/default/promoted
hi
$ cat _build/default/result
hi
hi
We're done.
$ stop_dune
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
Error: Multiple rules generated for _build/default/promoted:
- dune:1
- file present in source tree
Hint: rm -f promoted
Had 1 error, waiting for filesystem changes...
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
Now test file-system events generated during target promotion.
$ cat > dune <<EOF
> (rule
> (mode promote)
> (deps original)
> (target promoted)
> (action (copy %{deps} %{target})))
> (rule
> (deps promoted)
> (target result)
> (action (system "cat promoted promoted > result")))
> EOF
$ cat promoted
hi
$ start_dune --debug-cache=fs
$ build result
Success
$ cat promoted
bye
$ stop_dune > .#debug-output
Show that Dune ignores the initial "dune-workspace" events (injected by Dune).
$ cat .#debug-output | grep dune-workspace
Updating dir_contents cache for "dune-workspace": Skipped
Updating file_digest cache for "dune-workspace": Skipped
Updating path_stat cache for "dune-workspace": Updated { changed = false }
Show that Dune ignores "promoted" events. Events for ".#promoted.dune-temp" are
filtered out by Dune's file watcher and don't show up here. The [path_digest]
event for [promoted] is more interesting: the file's content did change from "hi"
to "bye" but Dune subscribed to it *after* making the promotion, precisely to
avoid unnecessarily restarting after receiving the event that it caused itself.
$ cat .#debug-output | grep promoted
Updating dir_contents cache for "promoted": Skipped
Updating file_digest cache for "promoted": Updated { changed = false }
Updating path_stat cache for "promoted": Skipped
Show that Dune ignores events for the . directory: [dir_contents] didn't change
because [promoted] existed before running the build. Also, the subset of fields
of [path_stat] that matter to Dune didn't change either (the [mtime] field did
change but [fs_memo] does not provide a way to subscribe to it).
$ cat .#debug-output | grep '"."'
Updating dir_contents cache for ".": Updated { changed = false }
Updating file_digest cache for ".": Skipped
Updating path_stat cache for ".": Updated { changed = false }

View file

@ -0,0 +1,53 @@
Test what happens in watch mode when we depend on dot files
$ . ./helpers.sh
$ echo '(lang dune 3.0)' > dune-project
$ cat > dune <<EOF
> (rule
> (targets y)
> (deps .x)
> (action (system "cat .x > y")))
> EOF
$ start_dune
$ echo 1 > .x
$ build y
Success
$ cat _build/default/y
1
$ echo 2 > .x
$ build y
Success
$ cat _build/default/y
2
$ stop_dune
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
Same but in a sub-directory (the exclude regexp used to be sensitive to that):
$ mkdir test
$ mv dune test/dune
$ start_dune
$ echo 1 > test/.x
$ build test/y
Success
$ cat _build/default/test/y
1
$ echo 2 > test/.x
$ build test/y
Success
$ cat _build/default/test/y
2
$ stop_dune
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...

View file

@ -0,0 +1,50 @@
Demonstrate running "dune build" concurrently with an eager rpc server.
$ echo '(lang dune 3.18)' > dune-project
$ echo '(executable (name foo))' > dune
$ echo 'let () = print_endline "Hello, World!"' > foo.ml
Build the project once before starting the watch server so the watch server starts immediately.
$ dune build
$ dune build --watch &
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
File "foo.ml", line 1, characters 9-21:
1 | let () = print_endlin "Hello, World!"
^^^^^^^^^^^^
Error: Unbound value print_endlin
Hint: Did you mean print_endline?
Had 1 error, waiting for filesystem changes...
File "foo.ml", line 1, characters 9-21:
1 | let () = print_endlin "Hello, World!"
^^^^^^^^^^^^
Error: Unbound value print_endlin
Hint: Did you mean print_endline?
Had 1 error, waiting for filesystem changes...
Demonstrate that we can run "dune build" while the watch server is running.
$ dune build
Success
Demonstrate that a warning is displayed when extra arguments are passed to
"dune build", since those arguments will be ignored.
$ dune build --auto-promote 2>&1 | sed 's/pid: [0-9]*/pid: PID/g'
Warning: Your build request is being forwarded to a running Dune instance
(pid: PID) so most command-line arguments will be ignored.
Success
Demonstrate that error messages are still printed by "dune build" when it's
acting as an RPC client while running concurrently with an RPC server.
$ echo 'let () = print_endlin "Hello, World!"' > foo.ml
$ dune build
File "$TESTCASE_ROOT/foo.ml", line 1, characters 9-21:
1 | let () = print_endlin "Hello, World!"
^^^^^^^^^^^^
Unbound value print_endlin
Hint: Did you mean print_endline?
Error: Build failed with 1 error.
[1]
$ dune shutdown
$ wait

View file

@ -0,0 +1,50 @@
Demonstrate running "dune exec" concurrently with an eager rpc server.
$ echo '(lang dune 3.18)' > dune-project
$ echo '(executable (name foo))' > dune
$ echo 'let () = print_endline "foo"' > foo.ml
$ touch README.md
Just watch the readme file so we don't accidentally build foo.exe before
testing the --no-build option:
$ dune build README.md --watch &
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
Demonstrate handling the --no-build option:
$ dune exec --no-build ./foo.exe
Error: Program './foo.exe' isn't built yet. You need to build it first or
remove the '--no-build' option.
[1]
Demonstrate running an executable from the current project:
$ dune exec ./foo.exe
foo
Demonstrate running an executable from PATH:
$ dune exec echo "bar"
Warning: As this is not the main instance of Dune it is unable to locate the
executable "echo" within this project. Dune will attempt to resolve the
executable's name within your PATH only.
bar
Demonstrate printing a warning if arguments are passed that would be ignored
due to how Dune builds via RPC:
$ dune exec --force ./foo.exe 2>&1 | sed 's/pid: [0-9]*/pid: PID/g'
Warning: Your build request is being forwarded to a running Dune instance
(pid: PID). Note that certain command line arguments may be ignored.
foo
Demonstrate trying to run exec in watch mode while another watch server is running:
$ dune exec ./foo.exe --watch 2>&1 | sed 's/pid: [0-9]*/pid: PID/g'
Error: Another instance of dune (pid: PID) has locked the _build
directory. Refusing to start a new watch server until no other instances of
dune are running.
Demonstrate running an executable via an absolute path:
$ dune exec $(which echo) "baz"
baz
$ dune shutdown
$ wait

View file

@ -0,0 +1,18 @@
Demonstrate building through rpc when server is running in eager watch
$ echo '(lang dune 3.8)' > dune-project
$ mkdir src
$ echo '(library (name foo))' > src/dune
$ touch src/a.ml
$ touch src/b.ml
$ dune build @all
$ dune build --watch &
Success, waiting for filesystem changes...
Success, waiting for filesystem changes...
$ dune rpc build --wait .
Success
$ dune shutdown
$ wait

View file

@ -0,0 +1,37 @@
Show what Dune watches via inotify
$ mkdir test
$ cd test
$ echo '(lang dune 3.0)' > dune-project
$ mkdir src
$ echo '(library (name foo))' > src/dune
$ touch src/a.ml
$ touch src/b.ml
$ strace -o ../log -e inotify_add_watch dune build --passive-watch-mode &
Success, waiting for filesystem changes...
$ dune rpc build --wait .
Success
$ dune shutdown
$ wait
The pattern below excludes absolute files as Dune currently watches
everything in the PATH, which is not very reproducible.
$ sed -nE 's/inotify_add_watch\([0-9]*, "([^/].*)", .*\) (=.*)/watch \1 \2/p' ../log | sort -u
watch . = 2
watch _build/.sync = 1
watch src = 3
Since the pattern above is not reproducible, uncomment the next lines
to see what it is really doing
# $ sed -nE 's/inotify_add_watch\([0-9]*, "(.*)", .*\) (=.*)/watch \1 \2/p' ../log | sort -u -k4n -k2 | sed "s,$OPAM_SWITCH_PREFIX,OPAM_SWITCH_PREFIX," | sed "s,$DUNE_SOURCEROOT,DUNE_SOURCEROOT,"
# watch _build/.sync = 1
# watch . = 2
# watch src = 3
# watch DUNE_SOURCEROOT/_build/default/test/blackbox-tests/test-cases/.bin = 4
# watch DUNE_SOURCEROOT/_build/install/default/bin = 5
# watch OPAM_SWITCH_PREFIX/bin = 6
# watch OPAM_SWITCH_PREFIX/lib/ocaml = 7