11open Import
22
3+ module Test_kind = struct
4+ type t =
5+ | Runtest of Path .t
6+ | Cram of Path .t * Source.Cram_test .t
7+ | Test_executable of Path .t * string (* dir, executable name *)
8+
9+ let alias ~contexts = function
10+ | Cram (dir , cram ) ->
11+ let name = Dune_engine.Alias.Name. of_string (Source.Cram_test. name cram) in
12+ Alias. in_dir ~name ~recursive: false ~contexts dir
13+ | Test_executable (dir , exe_name ) ->
14+ (* CR-someday Alizter: get the proper alias, also check js_of_ocaml
15+ runtst aliases? *)
16+ let name = Dune_engine.Alias.Name. of_string (" runtest-" ^ exe_name) in
17+ Alias. in_dir ~name ~recursive: false ~contexts dir
18+ | Runtest dir ->
19+ Alias. in_dir ~name: Dune_rules.Alias. runtest ~recursive: true ~contexts dir
20+ ;;
21+ end
22+
323let cram_tests_of_dir parent_dir =
424 let open Memo.O in
525 Source_tree. find_dir parent_dir
@@ -20,13 +40,54 @@ let find_cram_test cram_tests path =
2040 | Error (Dune_rules.Cram_rules. Missing_run_t _ ) | Ok _ -> None )
2141;;
2242
23- let all_tests_of_dir parent_dir =
43+ let find_test_executable ~sctx ~dir ~ml_file =
44+ let open Memo.O in
45+ let module_name = Filename. remove_extension ml_file in
46+ match Dune_lang.Module_name. of_string_opt module_name with
47+ | None -> Memo. return `Not_a_test
48+ | Some module_name ->
49+ let build_dir =
50+ Path.Build. append_source (Super_context. context sctx |> Context. build_dir) dir
51+ in
52+ let * dir_contents = Dir_contents. get sctx ~dir: build_dir in
53+ let * ml_sources = Dir_contents. ocaml dir_contents
54+ and * scope = Dir_contents. dir dir_contents |> Dune_rules.Scope.DB. find_by_dir in
55+ Dune_rules.Ml_sources. find_origin
56+ ml_sources
57+ ~libs: (Dune_rules.Scope. libs scope)
58+ [ module_name ]
59+ >> | (function
60+ | Some (Library _ | Executables _ | Melange _ ) | None -> `Not_a_test
61+ | Some (Tests ({ exes; _ } as _test )) ->
62+ let exe_names = Nonempty_list. to_list exes.names |> List. map ~f: snd in
63+ if List. mem exe_names (Filename. remove_extension ml_file) ~equal: String. equal
64+ then `Runnable (Filename. remove_extension ml_file)
65+ else (
66+ match exe_names with
67+ | [ single_exe ] -> `Runnable single_exe
68+ | [] | _ :: _ -> `Multiple_executables ))
69+ ;;
70+
71+ let all_tests_of_dir ~sctx parent_dir =
2472 let open Memo.O in
2573 let + cram_candidates =
2674 cram_tests_of_dir parent_dir
2775 >> | List. filter_map ~f: (fun res ->
2876 Result. to_option res
2977 |> Option. map ~f: (fun test -> Source.Cram_test. path test |> Path.Source. to_string))
78+ and + test_executable_candidates =
79+ Source_tree. find_dir parent_dir
80+ >> = function
81+ | None -> Memo. return []
82+ | Some source_dir ->
83+ Source_tree.Dir. filenames source_dir
84+ |> Filename.Set. to_list
85+ |> List. filter ~f: (fun f -> String. is_suffix f ~suffix: " .ml" )
86+ |> Memo.List. filter ~f: (fun ml_file ->
87+ find_test_executable ~sctx ~dir: parent_dir ~ml_file
88+ >> | function
89+ | `Runnable _ -> true
90+ | `Multiple_executables | `Not_a_test -> false )
3091 and + dir_candidates =
3192 let * parent_source_dir = Source_tree. find_dir parent_dir in
3293 match parent_source_dir with
@@ -39,59 +100,75 @@ let all_tests_of_dir parent_dir =
39100 >> | Source_tree.Dir. path
40101 >> | Path.Source. to_string)
41102 in
42- List. concat [ cram_candidates; dir_candidates ]
103+ List. concat [ cram_candidates; test_executable_candidates; dir_candidates ]
43104 |> String.Set. of_list
44105 |> String.Set. to_list
45106;;
46107
47- let explain_unsuccessful_search path ~parent_dir =
108+ let explain_unsuccessful_search ~ sctx path ~parent_dir =
48109 let open Memo.O in
49- let + candidates = all_tests_of_dir parent_dir in
110+ let + candidates = all_tests_of_dir ~sctx parent_dir in
50111 User_error. raise
51112 ~hints: (User_message. did_you_mean (Path.Source. to_string path) ~candidates )
52113 [ Pp. textf " %S does not match any known test." (Path.Source. to_string path) ]
53114;;
54115
55- (* [disambiguate_test_name path] is a function that takes in a
56- directory [path] and classifies it as either a cram test or a directory to
116+ (* [disambiguate_test_name path] is a function that takes in a directory [path]
117+ and classifies it as either a cram test, test executable, or a directory to
57118 run tests in. *)
58- let disambiguate_test_name path =
119+ let disambiguate_test_name ~ sctx path =
59120 match Path.Source. parent path with
60- | None -> Memo. return @@ ` Runtest (Path. source Path.Source. root)
121+ | None -> Memo. return @@ Test_kind. Runtest (Path. source Path.Source. root)
61122 | Some parent_dir ->
62123 let open Memo.O in
63124 let * cram_tests = cram_tests_of_dir parent_dir in
64125 (match find_cram_test cram_tests path with
65126 | Some test ->
66127 (* If we find the cram test, then we request that is run. *)
67- Memo. return (` Cram (parent_dir, test))
128+ Memo. return (Test_kind. Cram (Path. source parent_dir, test))
68129 | None ->
69- (* If we don't find it, then we assume the user intended a directory for
70- @runtest to be used. *)
71- Source_tree. find_dir path
72- >> = (function
73- (* We need to make sure that this directory or file exists. *)
74- | Some _ -> Memo. return (`Runtest (Path. source path))
75- | None -> explain_unsuccessful_search path ~parent_dir ))
130+ (* Check for test executables *)
131+ let filename = Path.Source. basename path in
132+ let * test_exe_opt =
133+ find_test_executable ~sctx ~dir: parent_dir ~ml_file: filename
134+ >> | function
135+ | `Runnable exe_name -> Some exe_name
136+ | `Multiple_executables ->
137+ User_error. raise
138+ [ Pp. text " Running multiple test executables at once is not yet supported" ]
139+ | `Not_a_test -> None
140+ in
141+ (match test_exe_opt with
142+ | Some exe_name ->
143+ (* Found a test executable for this ML file *)
144+ Memo. return (Test_kind. Test_executable (Path. source parent_dir, exe_name))
145+ | None ->
146+ (* If we don't find it, then we assume the user intended a directory for
147+ @runtest to be used. *)
148+ Source_tree. find_dir path
149+ >> = (function
150+ (* We need to make sure that this directory or file exists. *)
151+ | Some _ -> Memo. return (Test_kind. Runtest (Path. source path))
152+ | None -> explain_unsuccessful_search ~sctx path ~parent_dir )))
76153;;
77154
78- let make_request ~dir_or_cram_test_paths ~to_cwd (setup : Import.Main.build_system ) =
79- let contexts = setup.contexts in
80- List. map dir_or_cram_test_paths ~f: (fun dir ->
155+ let make_request ~contexts ~scontexts ~to_cwd ~test_paths =
156+ List. map test_paths ~f: (fun dir ->
81157 let dir = Path. of_string dir |> Path.Expert. try_localize_external in
82- let open Action_builder.O in
83- let * contexts, alias_kind =
158+ let sctx, contexts, src_dir =
84159 match (Util. check_path contexts dir : Util.checked ) with
85160 | In_build_dir (context , dir ) ->
86- let + res = Action_builder. of_memo (disambiguate_test_name dir) in
87- [ context ], res
161+ ( Dune_engine.Context_name.Map. find_exn scontexts (Context. name context)
162+ , [ context ]
163+ , dir )
88164 | In_source_dir dir ->
89165 (* We need to adjust the path here to make up for the current working directory. *)
90166 let dir =
91167 Path.Source.L. relative Path.Source. root (to_cwd @ Path.Source. explode dir)
92168 in
93- let + res = Action_builder. of_memo (disambiguate_test_name dir) in
94- contexts, res
169+ ( Dune_engine.Context_name.Map. find_exn scontexts Context_name. default
170+ , contexts
171+ , dir )
95172 | In_private_context _ | In_install_dir _ ->
96173 User_error. raise
97174 [ Pp. textf " This path is internal to dune: %s" (Path. to_string_maybe_quoted dir)
@@ -103,17 +180,8 @@ let make_request ~dir_or_cram_test_paths ~to_cwd (setup : Import.Main.build_syst
103180 (Path. to_string_maybe_quoted dir)
104181 ]
105182 in
106- Alias. request
107- @@
108- match alias_kind with
109- | `Cram (dir , cram ) ->
110- let alias_name = Source.Cram_test. name cram in
111- Alias. in_dir
112- ~name: (Dune_engine.Alias.Name. of_string alias_name)
113- ~recursive: false
114- ~contexts
115- (Path. source dir)
116- | `Runtest dir ->
117- Alias. in_dir ~name: Dune_rules.Alias. runtest ~recursive: true ~contexts dir)
183+ let open Action_builder.O in
184+ let * res = Action_builder. of_memo (disambiguate_test_name ~sctx src_dir) in
185+ Alias. request (Test_kind. alias ~contexts res))
118186 |> Action_builder. all_unit
119187;;
0 commit comments