Skip to content

Commit f18fe59

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

File tree

4 files changed

+132
-10
lines changed

4 files changed

+132
-10
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: 28 additions & 10 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+
`Inline_tests_library lib_name
6074
| Some (Library _ | Executables _ | Melange _) | None -> `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 `Runnable (Filename.remove_extension ml_file)
78+
then `Test_executable (Filename.remove_extension ml_file)
6579
else (
6680
match exe_names with
67-
| [ single_exe ] -> `Runnable single_exe
81+
| [ single_exe ] -> `Test_executable single_exe
6882
| [] | _ :: _ -> `Multiple_executables))
6983
;;
7084

@@ -84,9 +98,9 @@ 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
101+
classify_ml_test ~sctx ~dir:parent_dir ~ml_file
88102
>>| function
89-
| `Runnable _ -> true
103+
| `Test_executable _ | `Inline_tests_library _ -> true
90104
| `Multiple_executables | `Not_a_test -> false)
91105
and+ dir_candidates =
92106
let* parent_source_dir = Source_tree.find_dir parent_dir in
@@ -129,19 +143,23 @@ let disambiguate_test_name ~sctx path =
129143
| None ->
130144
(* Check for test executables *)
131145
let filename = Path.Source.basename path in
132-
let* test_exe_opt =
133-
find_test_executable ~sctx ~dir:parent_dir ~ml_file:filename
146+
let* test_kind_opt =
147+
classify_ml_test ~sctx ~dir:parent_dir ~ml_file:filename
134148
>>| function
135-
| `Runnable exe_name -> Some exe_name
149+
| `Test_executable exe_name -> Some (`Test_exe exe_name)
150+
| `Inline_tests_library lib_name -> Some (`Inline_tests lib_name)
136151
| `Multiple_executables ->
137152
User_error.raise
138153
[ Pp.text "Running multiple test executables at once is not yet supported" ]
139154
| `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. *)

src/dune_rules/dune_rules.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ module Pkg_build_progress = Pkg_build_progress
6464
module Compile_time = Compile_time
6565
module Cram_rules = Cram_rules
6666
module Instrumentation = Instrumentation
67+
module Sub_system_name = Sub_system_name
68+
module Inline_tests_info = Inline_tests_info
6769

6870
module Install_rules = struct
6971
let install_file = Install_rules.install_file
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)