Skip to content

Commit ea89299

Browse files
committed
feat(runtest): dune runtest for (inline_tests)
Signed-off-by: Ali Caglayan <[email protected]>
1 parent 07abd7b commit ea89299

File tree

3 files changed

+136
-16
lines changed

3 files changed

+136
-16
lines changed

bin/import.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ include struct
4141
module Executables = Executables
4242
module Dune_load = Dune_load
4343
module Dir_contents = Dir_contents
44+
module Sub_system_name = Sub_system_name
45+
module Inline_tests_info = Inline_tests_info
4446
end
4547

4648
include struct

bin/runtest_common.ml

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module Test_kind = struct
55
| Runtest of Path.t
66
| Cram of Path.t * Source.Cram_test.t
77
| Test_executable of Path.t * string (* dir, executable name *)
8+
| Inline_tests of Path.t * string (* dir, library name *)
89

910
let alias ~contexts = function
1011
| Cram (dir, cram) ->
@@ -15,6 +16,10 @@ module Test_kind = struct
1516
runtst aliases? *)
1617
let name = Dune_engine.Alias.Name.of_string ("runtest-" ^ exe_name) in
1718
Alias.in_dir ~name ~recursive:false ~contexts dir
19+
| Inline_tests (dir, lib_name) ->
20+
(* CR-someday Alizter: get the proper alias where it is defined. *)
21+
let name = Dune_engine.Alias.Name.of_string ("runtest-" ^ lib_name) in
22+
Alias.in_dir ~name ~recursive:false ~contexts dir
1823
| Runtest dir ->
1924
Alias.in_dir ~name:Dune_rules.Alias.runtest ~recursive:true ~contexts dir
2025
;;
@@ -40,7 +45,13 @@ let find_cram_test cram_tests path =
4045
| Error (Dune_rules.Cram_rules.Missing_run_t _) | Ok _ -> None)
4146
;;
4247

43-
let find_test_executable ~sctx ~dir ~ml_file =
48+
let has_inline_tests (lib : Dune_rules.Library.t) =
49+
Dune_rules.Sub_system_name.Map.mem
50+
lib.sub_systems
51+
Dune_rules.Inline_tests_info.Tests.name
52+
;;
53+
54+
let classify_ml_test ~sctx ~dir ~ml_file =
4455
let open Memo.O in
4556
let module_name = Filename.remove_extension ml_file in
4657
match Dune_lang.Module_name.of_string_opt module_name with
@@ -57,14 +68,17 @@ let find_test_executable ~sctx ~dir ~ml_file =
5768
~libs:(Dune_rules.Scope.libs scope)
5869
[ module_name ]
5970
>>| (function
71+
| Some (Library lib) when has_inline_tests lib ->
72+
let lib_name = snd lib.name |> Lib_name.Local.to_string in
73+
Ok (`Inline_tests_library lib_name)
6074
| Some (Library _ | Executables _ | Melange _) | None -> Error `Not_a_test
6175
| Some (Tests ({ exes; _ } as _test)) ->
6276
let exe_names = Nonempty_list.to_list exes.names |> List.map ~f:snd in
6377
if List.mem exe_names (Filename.remove_extension ml_file) ~equal:String.equal
64-
then Ok (Filename.remove_extension ml_file)
78+
then Ok (`Test_executable (Filename.remove_extension ml_file))
6579
else (
6680
match exe_names with
67-
| [ single_exe ] -> Ok single_exe
81+
| [ single_exe ] -> Ok (`Test_executable single_exe)
6882
| [] | _ :: _ -> Error `Not_an_entry_point))
6983
;;
7084

@@ -84,7 +98,7 @@ let all_tests_of_dir ~sctx parent_dir =
8498
|> Filename.Set.to_list
8599
|> List.filter ~f:(fun f -> String.is_suffix f ~suffix:".ml")
86100
|> Memo.List.filter ~f:(fun ml_file ->
87-
find_test_executable ~sctx ~dir:parent_dir ~ml_file >>| Result.is_ok)
101+
classify_ml_test ~sctx ~dir:parent_dir ~ml_file >>| Result.is_ok)
88102
and+ dir_candidates =
89103
let* parent_source_dir = Source_tree.find_dir parent_dir in
90104
match parent_source_dir with
@@ -126,22 +140,26 @@ let disambiguate_test_name ~sctx path =
126140
| None ->
127141
(* Check for test executables *)
128142
let filename = Path.Source.basename path in
129-
let* test_exe_opt =
130-
find_test_executable ~sctx ~dir:parent_dir ~ml_file:filename
143+
let* test_kind_opt =
144+
classify_ml_test ~sctx ~dir:parent_dir ~ml_file:filename
131145
>>| function
132-
| Ok exe_name -> Some exe_name
133-
| Error `Not_an_entry_point ->
134-
User_error.raise
135-
[ Pp.textf
136-
"%S is used by multiple test executables and cannot be run directly."
137-
filename
138-
]
139-
| Error `Not_a_test -> None
146+
| Ok (`Test_executable exe_name) -> Some (`Test_exe exe_name)
147+
| Ok (`Inline_tests_library lib_name) -> Some (`Inline_tests lib_name)
148+
| Error `Not_an_entry_point ->
149+
User_error.raise
150+
[ Pp.textf
151+
"%S is used by multiple test executables and cannot be run directly."
152+
filename
153+
]
154+
| Error `Not_a_test -> None
140155
in
141-
(match test_exe_opt with
142-
| Some exe_name ->
156+
(match test_kind_opt with
157+
| Some (`Test_exe exe_name) ->
143158
(* Found a test executable for this ML file *)
144159
Memo.return (Test_kind.Test_executable (Path.source parent_dir, exe_name))
160+
| Some (`Inline_tests lib_name) ->
161+
(* Found an inline tests library for this ML file *)
162+
Memo.return (Test_kind.Inline_tests (Path.source parent_dir, lib_name))
145163
| None ->
146164
(* If we don't find it, then we assume the user intended a directory for
147165
@runtest to be used. *)
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
Test running inline tests by specifying ML source files directly.
2+
3+
$ cat > dune-project <<EOF
4+
> (lang dune 3.21)
5+
> EOF
6+
7+
Set up a simple inline tests backend and libraries:
8+
9+
$ cat > dune <<EOF
10+
> (library
11+
> (name test_backend)
12+
> (modules ())
13+
> (inline_tests.backend
14+
> (generate_runner (run sed "s/(\\\\*TEST:\\\\(.*\\\\)\\\\*)/let () = if \\"%{inline_tests}\\" = \\"enabled\\" then \\\\1;;/" %{impl-files}))))
15+
>
16+
> (library
17+
> (name mylib)
18+
> (modules lib)
19+
> (inline_tests (backend test_backend)))
20+
>
21+
> (library
22+
> (name regular_lib)
23+
> (modules regular))
24+
> EOF
25+
26+
$ cat > lib.ml <<EOF
27+
> let add x y = x + y
28+
> (*TEST: assert false *)
29+
> EOF
30+
31+
$ cat > regular.ml <<EOF
32+
> let subtract x y = x - y
33+
> EOF
34+
35+
Error when specifying a library ML file without inline_tests:
36+
37+
$ dune test regular.ml
38+
Error: "regular.ml" does not match any known test.
39+
[1]
40+
41+
When specifying a library with inline_tests, it should run the test (and fail):
42+
43+
$ dune test lib.ml
44+
File "dune", line 10, characters 1-38:
45+
10 | (inline_tests (backend test_backend)))
46+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
47+
Fatal error: exception File ".mylib.inline-tests/main.ml-gen", line 2, characters 40-46: Assertion failed
48+
[1]
49+
50+
Error when specifying a non-existent ML file:
51+
52+
$ dune test nonexistent.ml
53+
Error: "nonexistent.ml" does not match any known test.
54+
[1]
55+
56+
Test that inline tests are included in suggestions:
57+
58+
$ dune test li.ml
59+
Error: "li.ml" does not match any known test.
60+
Hint: did you mean lib.ml?
61+
[1]
62+
63+
Can run inline tests from _build directory:
64+
65+
$ dune test _build/default/lib.ml
66+
File "dune", line 10, characters 1-38:
67+
10 | (inline_tests (backend test_backend)))
68+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
69+
Fatal error: exception File ".mylib.inline-tests/main.ml-gen", line 2, characters 40-46: Assertion failed
70+
[1]
71+
72+
Test with multiple contexts:
73+
74+
$ cat > dune-workspace <<EOF
75+
> (lang dune 3.20)
76+
> (context (default))
77+
> (context (default (name alt)))
78+
> EOF
79+
80+
Running inline tests in multiple contexts:
81+
82+
$ dune test lib.ml
83+
File "dune", line 10, characters 1-38:
84+
10 | (inline_tests (backend test_backend)))
85+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
86+
Fatal error: exception File ".mylib.inline-tests/main.ml-gen", line 2, characters 40-46: Assertion failed
87+
File "dune", line 10, characters 1-38:
88+
10 | (inline_tests (backend test_backend)))
89+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
90+
Fatal error: exception File ".mylib.inline-tests/main.ml-gen", line 2, characters 40-46: Assertion failed
91+
[1]
92+
93+
Running inline tests in a specific build directory:
94+
95+
$ dune test _build/alt/lib.ml
96+
File "dune", line 10, characters 1-38:
97+
10 | (inline_tests (backend test_backend)))
98+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
99+
Fatal error: exception File ".mylib.inline-tests/main.ml-gen", line 2, characters 40-46: Assertion failed
100+
[1]

0 commit comments

Comments
 (0)