Skip to content

Commit 69c622f

Browse files
authored
Merge pull request #601 from SGrondin/idempotent-tests
Make MDX tests idempotent
2 parents 4db6533 + 6a85a11 commit 69c622f

File tree

5 files changed

+122
-52
lines changed

5 files changed

+122
-52
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
_build
2+
_opam
3+
.ocamlformat
24
.*.swp
35
*.install

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,16 @@ To append to the string part, it's convenient to use the `/` operator:
780780
let ( / ) = Eio.Path.( / )
781781
```
782782

783+
<!--
784+
Cleanup previous runs due to [dune runtest --watch] not doing it
785+
```ocaml
786+
Eio_main.run @@ fun env ->
787+
let cwd = Eio.Stdenv.cwd env in
788+
["link-to-dir1"; "link-to-tmp"; "test.txt"; "dir1"]
789+
|> List.iter (fun p -> Eio.Path.rmtree ~missing_ok:true (cwd / p))
790+
```
791+
-->
792+
783793
`env` provides two initial paths:
784794

785795
- `cwd` restricts access to files beneath the current working directory.

lib_eio/path.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ let kind ~follow t =
110110
let is_file t =
111111
kind ~follow:true t = `Regular_file
112112

113-
let is_directory t =
113+
let is_directory t =
114114
kind ~follow:true t = `Directory
115115

116116
let with_open_in path fn =

tests/fs.md

Lines changed: 99 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ open Eio.Std
1717
1818
let ( / ) = Path.( / )
1919
20-
let run fn =
20+
let run ?clear:(paths = []) fn =
2121
Eio_main.run @@ fun env ->
22+
let cwd = Eio.Stdenv.cwd env in
23+
List.iter (fun p -> Eio.Path.rmtree ~missing_ok:true (cwd / p)) paths;
2224
fn env
2325
2426
let try_read_file path =
@@ -69,13 +71,26 @@ let try_rmtree ?missing_ok path =
6971
let chdir path =
7072
traceln "chdir %S" path;
7173
Unix.chdir path
74+
75+
let try_stat path =
76+
let stat ~follow =
77+
match Eio.Path.stat ~follow path with
78+
| info -> Fmt.str "@[<h>%a@]" Eio.File.Stat.pp_kind info.kind
79+
| exception Eio.Io (e, _) -> Fmt.str "@[<h>%a@]" Eio.Exn.pp_err e
80+
in
81+
let a = stat ~follow:false in
82+
let b = stat ~follow:true in
83+
if a = b then
84+
traceln "%a -> %s" Eio.Path.pp path a
85+
else
86+
traceln "%a -> %s / %s" Eio.Path.pp path a b
7287
```
7388

7489
# Basic test cases
7590

7691
Creating a file and reading it back:
7792
```ocaml
78-
# run @@ fun env ->
93+
# run ~clear:["test-file"] @@ fun env ->
7994
let cwd = Eio.Stdenv.cwd env in
8095
Path.save ~create:(`Exclusive 0o666) (cwd / "test-file") "my-data";
8196
traceln "Got %S" @@ Path.load (cwd / "test-file");;
@@ -179,7 +194,7 @@ Appending to an existing file:
179194
# Mkdir
180195

181196
```ocaml
182-
# run @@ fun env ->
197+
# run ~clear:["subdir"] @@ fun env ->
183198
let cwd = Eio.Stdenv.cwd env in
184199
try_mkdir (cwd / "subdir");
185200
try_mkdir (cwd / "subdir/nested");
@@ -188,19 +203,18 @@ Appending to an existing file:
188203
+mkdir <cwd:subdir> -> ok
189204
+mkdir <cwd:subdir/nested> -> ok
190205
- : unit = ()
191-
# Unix.unlink "subdir/nested/test-file"; Unix.rmdir "subdir/nested"; Unix.rmdir "subdir";;
206+
# Unix.unlink "subdir/nested/test-file";
207+
Unix.rmdir "subdir/nested";
208+
Unix.rmdir "subdir";;
192209
- : unit = ()
193210
```
194211

195212
Creating directories with nesting, symlinks, etc:
196213
```ocaml
197-
# Unix.symlink "/" "to-root";;
198-
- : unit = ()
199-
# Unix.symlink "subdir" "to-subdir";;
200-
- : unit = ()
201-
# Unix.symlink "foo" "dangle";;
202-
- : unit = ()
203-
# run @@ fun env ->
214+
# run ~clear:["to-subdir"; "to-root"; "dangle"] @@ fun env ->
215+
Unix.symlink "/" "to-root";
216+
Unix.symlink "subdir" "to-subdir";
217+
Unix.symlink "foo" "dangle";
204218
let cwd = Eio.Stdenv.cwd env in
205219
try_mkdir (cwd / "subdir");
206220
try_mkdir (cwd / "to-subdir/nested");
@@ -265,7 +279,7 @@ let split path = Eio.Path.split (fake_dir, path) |> Option.map (fun ((_, dirname
265279
Recursively creating directories with `mkdirs`.
266280

267281
```ocaml
268-
# run @@ fun env ->
282+
# run ~clear:["subdir1"] @@ fun env ->
269283
let cwd = Eio.Stdenv.cwd env in
270284
let nested = cwd / "subdir1" / "subdir2" / "subdir3" in
271285
try_mkdirs nested;
@@ -287,7 +301,7 @@ Recursively creating directories with `mkdirs`.
287301
Some edge cases for `mkdirs`.
288302

289303
```ocaml
290-
# run @@ fun env ->
304+
# run ~clear:["test"] @@ fun env ->
291305
let cwd = Eio.Stdenv.cwd env in
292306
try_mkdirs (cwd / ".");
293307
try_mkdirs (cwd / "././");
@@ -307,7 +321,7 @@ Some edge cases for `mkdirs`.
307321
You can remove a file using unlink:
308322

309323
```ocaml
310-
# run @@ fun env ->
324+
# run ~clear:["file"; "subdir/file2"] @@ fun env ->
311325
Switch.run @@ fun sw ->
312326
let cwd = Eio.Stdenv.cwd env in
313327
Path.save ~create:(`Exclusive 0o600) (cwd / "file") "data";
@@ -352,12 +366,67 @@ Removing something that doesn't exist or is out of scope:
352366
- : unit = ()
353367
```
354368

369+
Reads and writes follow symlinks, but unlink operates on the symlink itself:
370+
371+
```ocaml
372+
# run ~clear:["link1"; "linkdir"; "linkroot"; "dir1"; "file2"] @@ fun env ->
373+
Switch.run @@ fun sw ->
374+
let cwd = Eio.Stdenv.cwd env in
375+
let fs = Eio.Stdenv.fs env in
376+
377+
try_mkdir (cwd / "dir1");
378+
let file1 = cwd / "dir1" / "file1" in
379+
let file2 = cwd / "file2" in
380+
try_write_file ~create:(`Exclusive 0o600) file1 "data1";
381+
try_write_file ~create:(`Exclusive 0o400) file2 "data2";
382+
Unix.symlink "dir1/file1" "link1";
383+
Unix.symlink "../file2" "dir1/link2";
384+
Unix.symlink "dir1" "linkdir";
385+
Unix.symlink "/" "linkroot";
386+
try_read_file file1;
387+
try_read_file (cwd / "link1");
388+
try_read_file (cwd / "linkdir" / "file1");
389+
390+
try_stat file1;
391+
try_stat (cwd / "link1");
392+
try_stat (cwd / "linkdir");
393+
try_stat (cwd / "linkroot");
394+
try_stat (fs / "linkroot");
395+
396+
Fun.protect ~finally:(fun () -> chdir "..") (fun () ->
397+
chdir "dir1";
398+
try_read_file (cwd / "file1");
399+
(* Should remove link itself even though it's poiting outside of cwd *)
400+
Path.unlink (cwd / "link2")
401+
);
402+
try_read_file file2;
403+
Path.unlink (cwd / "link1");
404+
Path.unlink (cwd / "linkdir");
405+
Path.unlink (cwd / "linkroot")
406+
+mkdir <cwd:dir1> -> ok
407+
+write <cwd:dir1/file1> -> ok
408+
+write <cwd:file2> -> ok
409+
+read <cwd:dir1/file1> -> "data1"
410+
+read <cwd:link1> -> "data1"
411+
+read <cwd:linkdir/file1> -> "data1"
412+
+<cwd:dir1/file1> -> regular file
413+
+<cwd:link1> -> symbolic link / regular file
414+
+<cwd:linkdir> -> symbolic link / directory
415+
+<cwd:linkroot> -> symbolic link / Fs Permission_denied _
416+
+<fs:linkroot> -> symbolic link / directory
417+
+chdir "dir1"
418+
+read <cwd:file1> -> "data1"
419+
+chdir ".."
420+
+read <cwd:file2> -> "data2"
421+
- : unit = ()
422+
```
423+
355424
# Rmdir
356425

357426
Similar to `unlink`, but works on directories:
358427

359428
```ocaml
360-
# run @@ fun env ->
429+
# run ~clear:["d1"; "subdir/d2"; "subdir/d3"] @@ fun env ->
361430
Switch.run @@ fun sw ->
362431
let cwd = Eio.Stdenv.cwd env in
363432
try_mkdir (cwd / "d1");
@@ -405,7 +474,7 @@ Removing something that doesn't exist or is out of scope:
405474
# Recursive removal
406475

407476
```ocaml
408-
# run @@ fun env ->
477+
# run ~clear:["foo"] @@ fun env ->
409478
Switch.run @@ fun sw ->
410479
let cwd = Eio.Stdenv.cwd env in
411480
let foo = cwd / "foo" in
@@ -431,7 +500,7 @@ Removing something that doesn't exist or is out of scope:
431500

432501
Create a sandbox, write a file with it, then read it from outside:
433502
```ocaml
434-
# run @@ fun env ->
503+
# run ~clear:["sandbox"] @@ fun env ->
435504
Switch.run @@ fun sw ->
436505
let cwd = Eio.Stdenv.cwd env in
437506
try_mkdir (cwd / "sandbox");
@@ -450,7 +519,7 @@ Create a sandbox, write a file with it, then read it from outside:
450519
We create a directory and chdir into it.
451520
Using `cwd` we can't access the parent, but using `fs` we can:
452521
```ocaml
453-
# run @@ fun env ->
522+
# run ~clear:["fs-test"; "outside-cwd"] @@ fun env ->
454523
let cwd = Eio.Stdenv.cwd env in
455524
let fs = Eio.Stdenv.fs env in
456525
try_mkdir (cwd / "fs-test");
@@ -476,7 +545,7 @@ Using `cwd` we can't access the parent, but using `fs` we can:
476545
Reading directory entries under `cwd` and outside of `cwd`.
477546

478547
```ocaml
479-
# run @@ fun env ->
548+
# run ~clear:["readdir"] @@ fun env ->
480549
let cwd = Eio.Stdenv.cwd env in
481550
try_mkdir (cwd / "readdir");
482551
Path.with_open_dir (cwd / "readdir") @@ fun tmpdir ->
@@ -499,7 +568,8 @@ Reading directory entries under `cwd` and outside of `cwd`.
499568
An error from the underlying directory, not the sandbox:
500569

501570
```ocaml
502-
# Unix.mkdir "test-no-access" 0;;
571+
# run ~clear:["test-no-access"] @@ fun env ->
572+
Unix.mkdir "test-no-access" 0;;
503573
- : unit = ()
504574
# run @@ fun env ->
505575
let cwd = Eio.Stdenv.cwd env in
@@ -530,7 +600,7 @@ Exception: Eio.Io Fs Permission_denied _,
530600
## Streamling lines
531601

532602
```ocaml
533-
# run @@ fun env ->
603+
# run ~clear:["test-data"] @@ fun env ->
534604
let cwd = Eio.Stdenv.cwd env in
535605
Path.save ~create:(`Exclusive 0o600) (cwd / "test-data") "one\ntwo\nthree";
536606
Path.with_lines (cwd / "test-data") (fun lines ->
@@ -609,7 +679,7 @@ let try_rename t =
609679
Confined:
610680

611681
```ocaml
612-
# run @@ fun env -> try_rename env#cwd;;
682+
# run ~clear:["tmp"; "dir"] @@ fun env -> try_rename env#cwd;;
613683
+mkdir <cwd:tmp> -> ok
614684
+rename <cwd:tmp> to <cwd:dir> -> ok
615685
+write <cwd:foo> -> ok
@@ -639,22 +709,7 @@ Unconfined:
639709
# Stat
640710

641711
```ocaml
642-
let try_stat path =
643-
let stat ~follow =
644-
match Eio.Path.stat ~follow path with
645-
| info -> Fmt.str "@[<h>%a@]" Eio.File.Stat.pp_kind info.kind
646-
| exception Eio.Io (e, _) -> Fmt.str "@[<h>%a@]" Eio.Exn.pp_err e
647-
in
648-
let a = stat ~follow:false in
649-
let b = stat ~follow:true in
650-
if a = b then
651-
traceln "%a -> %s" Eio.Path.pp path a
652-
else
653-
traceln "%a -> %s / %s" Eio.Path.pp path a b
654-
```
655-
656-
```ocaml
657-
# run @@ fun env ->
712+
# run ~clear:["stat_subdir"; "stat_reg"] @@ fun env ->
658713
let cwd = Eio.Stdenv.cwd env in
659714
Switch.run @@ fun sw ->
660715
try_mkdir (cwd / "stat_subdir");
@@ -666,10 +721,10 @@ let try_stat path =
666721
- : unit = ()
667722
```
668723

669-
Fstatat:
724+
# Fstatat:
670725

671726
```ocaml
672-
# run @@ fun env ->
727+
# run ~clear:["stat_subdir2"; "symlink"; "broken-symlink"; "parent-symlink"] @@ fun env ->
673728
let cwd = Eio.Stdenv.cwd env in
674729
Switch.run @@ fun sw ->
675730
try_mkdir (cwd / "stat_subdir2");
@@ -701,7 +756,7 @@ Fstatat:
701756
Check reading and writing vectors at arbitrary offsets:
702757

703758
```ocaml
704-
# run @@ fun env ->
759+
# run ~clear:["test.txt"] @@ fun env ->
705760
let cwd = Eio.Stdenv.cwd env in
706761
let path = cwd / "test.txt" in
707762
Path.with_open_out path ~create:(`Exclusive 0o600) @@ fun file ->
@@ -741,7 +796,7 @@ Reading at the end of a file:
741796
Ensure reads can be cancelled promptly, even if there is no need to wait:
742797

743798
```ocaml
744-
# Eio_main.run @@ fun env ->
799+
# run @@ fun env ->
745800
Eio.Path.with_open_out (env#fs / "/dev/zero") ~create:`Never @@ fun null ->
746801
Fiber.both
747802
(fun () ->
@@ -755,7 +810,7 @@ Exception: Failure "Simulated error".
755810
# Native paths
756811

757812
```ocaml
758-
# Eio_main.run @@ fun env ->
813+
# run ~clear:["native-sub"] @@ fun env ->
759814
let cwd = Sys.getcwd () ^ "/" in
760815
let test x =
761816
let native = Eio.Path.native x in
@@ -800,7 +855,7 @@ Exception: Failure "Simulated error".
800855
# Seek, truncate and sync
801856

802857
```ocaml
803-
# Eio_main.run @@ fun env ->
858+
# run @@ fun env ->
804859
Eio.Path.with_open_out (env#cwd / "seek-test") ~create:(`If_missing 0o700) @@ fun file ->
805860
Eio.File.truncate file (Int63.of_int 10);
806861
assert ((Eio.File.stat file).size = (Int63.of_int 10));

tests/process.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ let () = Eio.Exn.Backend.show := false
1616
1717
let ( / ) = Eio.Path.( / )
1818
19-
let run fn =
19+
let run ?clear:(paths = []) fn =
2020
Eio_main.run @@ fun env ->
21+
let cwd = Eio.Stdenv.cwd env in
22+
List.iter (fun p -> Eio.Path.rmtree ~missing_ok:true (cwd / p)) paths;
2123
fn env#process_mgr env
2224
2325
let status_to_string = Fmt.to_to_string Eio.Process.pp_status
@@ -57,12 +59,12 @@ A switch will stop a process when it is released:
5759
Passing in flows allows you to redirect the child process' stdout:
5860

5961
```ocaml
60-
# run @@ fun mgr env ->
62+
# run ~clear:["process-test.txt"] @@ fun mgr env ->
6163
let fs = Eio.Stdenv.fs env in
62-
let filename = "process-test.txt" in
63-
Eio.Path.(with_open_out ~create:(`Exclusive 0o600) (fs / filename)) @@ fun stdout ->
64+
let path = fs / "process-test.txt" in
65+
Eio.Path.(with_open_out ~create:(`Exclusive 0o600) path) @@ fun stdout ->
6466
Process.run mgr ~stdout [ "echo"; "Hello" ];
65-
Eio.Path.(load (fs / filename));;
67+
Eio.Path.(load path);;
6668
- : string = "Hello\n"
6769
```
6870

@@ -120,8 +122,9 @@ Changing directory (unconfined):
120122
Changing directory (confined):
121123

122124
```ocaml
123-
# run @@ fun mgr env ->
124-
let subdir = env#cwd / "proc-sub-dir" in
125+
# run ~clear:["proc-sub-dir"] @@ fun mgr env ->
126+
let cwd = Eio.Stdenv.cwd env in
127+
let subdir = cwd / "proc-sub-dir" in
125128
Eio.Path.mkdir subdir ~perm:0o700;
126129
Eio.Path.with_open_dir subdir @@ fun subdir ->
127130
Eio.Path.save (subdir / "test-cwd") "test-data" ~create:(`Exclusive 0o600);

0 commit comments

Comments
 (0)