Skip to content

Commit 98ce06a

Browse files
committed
feat(oxcaml): parameterised inline_tests
Signed-off-by: Arthur Wendling <[email protected]>
1 parent f181cca commit 98ce06a

File tree

11 files changed

+261
-44
lines changed

11 files changed

+261
-44
lines changed

.github/workflows/workflow.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,10 +373,10 @@ jobs:
373373
opam-pin: false
374374

375375
- name: Pin deps
376-
run: opam pin add -n . --with-version=3.20.2+ox
376+
run: opam pin add -n . --with-version=3.21.0
377377

378378
- name: Install deps
379-
run: opam install csexp pp re spawn uutf ./dune.opam
379+
run: opam install csexp pp re spawn uutf ppx_inline_test ./dune.opam
380380

381381
- name: Build dune
382382
run: opam exec -- make bootstrap

doc/tests.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,24 @@ a ``deps`` field the ``inline_tests`` field. The argument of this
288288
(inline_tests (deps data.txt))
289289
(preprocess (pps ppx_expect)))
290290
291+
Specifying Inline Test arguments for Parameterised Libraries
292+
------------------------------------------------------------
293+
294+
If your library is parameterised (see
295+
:doc:`/reference/dune/library_parameter`), you must specify which
296+
implementation of the parameters to use with the ``arguments`` field. For
297+
example, suppose `foo` is a parameterised library that takes parameters
298+
`a_param` and `b_param`, you can specify the implementations of these
299+
parameters for inline tests as follows:
300+
301+
.. code:: ocaml
302+
303+
(library
304+
(name foo)
305+
(parameters a_param b_param)
306+
(inline_tests
307+
(arguments a_impl b_impl)))
308+
291309
292310
Passing Special Arguments to the Test Runner
293311
--------------------------------------------

src/dune_rules/inline_tests.ml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,23 @@ include Sub_system.Register_end_point (struct
264264
Resolve.Memo.List.concat_map backends ~f:(fun (backend : Backend.t) ->
265265
backend.runner_libraries)
266266
in
267-
let* lib = Lib.DB.resolve lib_db (loc, Library.best_name lib) in
267+
let* arguments =
268+
Resolve.Memo.lift_memo
269+
@@ Memo.List.map info.arguments ~f:(fun (loc, dep) ->
270+
let open Memo.O in
271+
let+ dep = Lib.DB.resolve lib_db (loc, dep) in
272+
loc, dep)
273+
in
274+
let* lib =
275+
let* lib = Lib.DB.resolve lib_db (loc, Library.best_name lib) in
276+
Resolve.Memo.lift
277+
@@ Lib.Parameterised.instantiate
278+
~from:`inline_tests
279+
~loc
280+
lib
281+
arguments
282+
~parent_parameters:[]
283+
in
268284
let* more_libs =
269285
Resolve.Memo.List.map info.libraries ~f:(Lib.DB.resolve lib_db)
270286
in

src/dune_rules/inline_tests_info.ml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ module Tests = struct
134134
; executable_link_flags : Ordered_set_lang.Unexpanded.t
135135
; backend : (Loc.t * Lib_name.t) option
136136
; libraries : (Loc.t * Lib_name.t) list
137+
; arguments : (Loc.t * Lib_name.t) list
137138
; enabled_if : Blang.t
138139
}
139140

@@ -165,6 +166,12 @@ module Tests = struct
165166
ocaml_flags, link_flags))
166167
and+ backend = field_o "backend" (located Lib_name.decode)
167168
and+ libraries = field "libraries" (repeat (located Lib_name.decode)) ~default:[]
169+
and+ arguments =
170+
field
171+
"arguments"
172+
(Dune_lang.Syntax.since Dune_lang.Oxcaml.syntax (0, 1)
173+
>>> repeat (located Lib_name.decode))
174+
~default:[]
168175
and+ modes =
169176
field
170177
"modes"
@@ -180,6 +187,7 @@ module Tests = struct
180187
; executable_link_flags
181188
; backend
182189
; libraries
190+
; arguments
183191
; modes
184192
; enabled_if
185193
})

src/dune_rules/inline_tests_info.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ module Tests : sig
4646
; executable_link_flags : Ordered_set_lang.Unexpanded.t
4747
; backend : (Loc.t * Lib_name.t) option
4848
; libraries : (Loc.t * Lib_name.t) list
49+
; arguments : (Loc.t * Lib_name.t) list
4950
; enabled_if : Blang.t
5051
}
5152

src/dune_rules/lib.ml

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -264,19 +264,34 @@ module Error = struct
264264
]
265265
;;
266266

267-
let missing_parameter ~loc p =
267+
let missing_parameter_inline_tests ~loc p =
268268
let name = Lib_name.to_string (Lib_info.name p) in
269269
make_resolve
270270
~loc
271-
[ Pp.textf "Parameter %S is missing." name ]
271+
[ Pp.textf "To run the inline tests, please provide the missing parameter %S." name
272+
]
272273
~hints:
273274
[ Pp.textf
274-
"Pass an argument implementing %s to the dependency, or add (parameters %s)"
275-
name
275+
"Add (arguments ...) to the inline_tests to specify which implementation of \
276+
the parameter %S to use."
276277
name
277278
]
278279
;;
279280

281+
let missing_parameter_depends ~loc p =
282+
let name = Lib_name.to_string (Lib_info.name p) in
283+
make_resolve
284+
~loc
285+
[ Pp.textf "Missing argument for parameter %S." name ]
286+
~hints:[ Pp.textf "Pass an argument implementing %S to the dependency." name ]
287+
;;
288+
289+
let missing_parameter ~from ~loc ~loc_param p =
290+
match from with
291+
| `depends -> missing_parameter_depends ~loc p
292+
| `inline_tests -> missing_parameter_inline_tests ~loc:loc_param p
293+
;;
294+
280295
let missing_implements ~loc p =
281296
let name = Lib_name.to_string (Lib_info.name p) in
282297
make_resolve
@@ -375,7 +390,7 @@ module T = struct
375390
; pps : t list Resolve.t
376391
; resolved_selects : Resolved_select.t list Resolve.t
377392
; allow_unused_libraries : t list Resolve.t
378-
; parameters : t list Resolve.t
393+
; parameters : (Loc.t * t) list Resolve.t
379394
; arguments : t option list
380395
; implements : t Resolve.t option
381396
; project : Dune_project.t option
@@ -477,7 +492,7 @@ let name t = t.name
477492
let info t = t.info
478493
let project t = t.project
479494
let implements t = Option.map ~f:Memo.return t.implements
480-
let parameters t = Resolve.Memo.lift t.parameters
495+
let parameters t = Resolve.Memo.lift (Resolve.map ~f:(List.map ~f:snd) t.parameters)
481496
let requires t = Memo.return t.requires
482497
let re_exports t = Memo.return t.re_exports
483498
let ppx_runtime_deps t = Memo.return t.ppx_runtime_deps
@@ -558,7 +573,7 @@ module Parameterised = struct
558573
let parameterised_arguments t =
559574
let open Resolve.O in
560575
let+ parameters = t.parameters in
561-
List.combine parameters t.arguments
576+
List.map2 ~f:(fun (loc, param) arg -> loc, param, arg) parameters t.arguments
562577
;;
563578

564579
let apply_arguments t new_arguments =
@@ -574,27 +589,26 @@ module Parameterised = struct
574589
| [], _ ->
575590
(* Ignore remaining arguments *)
576591
Resolve.return (List.rev acc)
577-
| keep, [] ->
578-
(* Keep the remaining existing parameters *)
579-
Resolve.return (List.rev_append acc keep)
580-
| (param_intf, Some arg) :: existing, _ ->
592+
| (_, _, None) :: existing, [] ->
593+
(* Keep required parameter which are still unspecifed *)
594+
go (None :: acc) existing []
595+
| (_, _, Some arg) :: existing, _ ->
581596
(* Keep already applied parameter *)
582-
go ((param_intf, Some arg) :: acc) existing given'
583-
| ((param_intf, None) as keep) :: existing, (param_intf', arg) :: given ->
597+
go (Some arg :: acc) existing given'
598+
| (_, param_intf, None) :: existing, (param_intf', arg) :: given ->
584599
(match compare param_intf param_intf' with
585600
| Eq ->
586601
(* Apply the argument to the unset parameter *)
587-
go ((param_intf, Some arg) :: acc) existing given
602+
go (Some arg :: acc) existing given
588603
| Lt ->
589604
(* Keep the existing parameter as being unknown *)
590-
go (keep :: acc) existing given'
605+
go (None :: acc) existing given'
591606
| Gt ->
592607
(* Skip unwanted argument *)
593608
go acc existing' given)
594609
in
595610
let* t_arguments = parameterised_arguments t in
596611
let+ arguments = go [] t_arguments new_arguments in
597-
let arguments = List.map ~f:snd arguments in
598612
{ t with arguments }
599613
;;
600614

@@ -615,15 +629,19 @@ module Parameterised = struct
615629
List.sort arguments ~compare:(fun (param, _) (param', _) -> compare param param')
616630
;;
617631

618-
let instantiate ~loc lib args ~parent_parameters =
632+
let instantiate ~loc ~from lib args ~parent_parameters =
619633
let open Resolve.O in
620634
let* args = make_arguments args in
621635
let* lib = apply_arguments lib args in
622636
let+ () =
623637
let* all_args = parameterised_arguments lib in
638+
let is_inherited param =
639+
List.exists parent_parameters ~f:(fun (_, parent_param) ->
640+
equal param parent_param)
641+
in
624642
Resolve.List.iter all_args ~f:(function
625-
| param, None when not (List.exists parent_parameters ~f:(equal param)) ->
626-
Error.missing_parameter ~loc param.info
643+
| loc_param, param, None when not (is_inherited param) ->
644+
Error.missing_parameter ~from ~loc ~loc_param param.info
627645
| _ -> Resolve.return ())
628646
in
629647
lib
@@ -636,7 +654,7 @@ module Parameterised = struct
636654
let open Resolve.O in
637655
let* parent_arguments = parameterised_arguments parent in
638656
let parent_arguments =
639-
List.filter_map parent_arguments ~f:(fun (param, opt_arg) ->
657+
List.filter_map parent_arguments ~f:(fun (_loc, param, opt_arg) ->
640658
Option.map opt_arg ~f:(fun arg -> param, arg))
641659
in
642660
let* arguments =
@@ -1142,14 +1160,14 @@ module rec Resolve_names : sig
11421160
: db
11431161
-> Lib_dep.t list
11441162
-> private_deps:private_deps
1145-
-> parameters:lib list
1163+
-> parameters:(Loc.t * lib) list
11461164
-> Resolved.deps Memo.t
11471165

11481166
val resolve_deps_and_add_runtime_deps
11491167
: db
11501168
-> Lib_dep.t list
11511169
-> private_deps:private_deps
1152-
-> parameters:t list
1170+
-> parameters:(Loc.t * t) list
11531171
-> pps:(Loc.t * Lib_name.t) list
11541172
-> dune_version:Dune_lang.Syntax.Version.t option
11551173
-> Resolved.t Memo.t
@@ -1210,7 +1228,7 @@ end = struct
12101228
| _ :: ps -> check_duplicates ps
12111229
in
12121230
let+ () = check_duplicates parameters in
1213-
List.map parameters ~f:(fun (_, _, param) -> param)
1231+
List.map parameters ~f:(fun (loc, _, param) -> loc, param)
12141232
;;
12151233

12161234
let instantiate_impl db (name, info, hidden) =
@@ -1298,6 +1316,7 @@ end = struct
12981316
"expected Virtual or Parameter"
12991317
[ "implements", to_dyn impl ])
13001318
in
1319+
let requires_params = List.map ~f:snd requires_params in
13011320
let requires = List.concat [ requires_implements; requires_params; requires ] in
13021321
let (_ : Set.t), requires =
13031322
List.fold_left requires ~init:(Set.empty, []) ~f:(fun (seen, lst) lib ->
@@ -1747,7 +1766,12 @@ end = struct
17471766
>>| Option.map ~f:(fun dep ->
17481767
let open Resolve.O in
17491768
let* dep = dep in
1750-
Parameterised.instantiate ~loc dep arguments ~parent_parameters:parameters)
1769+
Parameterised.instantiate
1770+
~loc
1771+
~from:`depends
1772+
dep
1773+
arguments
1774+
~parent_parameters:parameters)
17511775
in
17521776
Memo.List.fold_left ~init:Resolved.Builder.empty deps ~f:(fun acc (dep : Lib_dep.t) ->
17531777
match dep with
@@ -1853,6 +1877,7 @@ end = struct
18531877
let open Resolve.Memo.O in
18541878
let* resolved = Memo.return resolved in
18551879
let* runtime_deps = runtime_deps in
1880+
let parameters = List.map ~f:snd parameters in
18561881
re_exports_closure (List.concat [ resolved; runtime_deps; parameters ])
18571882
and+ pps = pps in
18581883
{ Resolved.requires; pps; selects; re_exports }
@@ -2594,9 +2619,7 @@ let to_dune_lib
25942619
~lib_field:(Option.map ~f:Memo.return lib.implements)
25952620
and+ parameters =
25962621
let+ lib_parameters = Resolve.Memo.lift lib.parameters in
2597-
List.map
2598-
(List.combine (Lib_info.parameters info) lib_parameters)
2599-
~f:(fun ((loc, _), param) -> loc, mangled_name param)
2622+
List.map lib_parameters ~f:(fun (loc, param) -> loc, mangled_name param)
26002623
and+ default_implementation =
26012624
use_public_name
26022625
~info_field:(Lib_info.default_implementation info)

src/dune_rules/lib.mli

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@ module Parameterised : sig
3030

3131
val instantiate
3232
: loc:Loc.t
33+
-> from:[ `depends | `inline_tests ]
3334
-> t
3435
-> (Loc.t * t Resolve.t) list
35-
-> parent_parameters:t list
36+
-> parent_parameters:(Loc.t * t) list
3637
-> t Resolve.t
3738
end
3839

src/dune_rules/parameterised_rules.ml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,11 @@ let resolve_instantiation scope instance_name =
349349
| Some lib ->
350350
Memo.List.map ~f:go args
351351
>>| List.map ~f:(fun arg -> Loc.none, arg)
352-
>>| Lib.Parameterised.instantiate ~loc:Loc.none lib ~parent_parameters:[]
352+
>>| Lib.Parameterised.instantiate
353+
~loc:Loc.none
354+
~from:`depends
355+
lib
356+
~parent_parameters:[]
353357
in
354358
go (Parameterised_name.of_string instance_name) |> Resolve.Memo.read_memo
355359
;;

test/blackbox-tests/test-cases/oxcaml/instantiate-parameterised.t

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,10 @@ It's an error for the binary to partially instantiate `lib_ab`:
7070
File "bin/dune", line 3, characters 26-32:
7171
3 | (libraries (instantiate lib_ab b_impl))) ; missing a_impl
7272
^^^^^^
73-
Error: Parameter "project.a" is missing.
73+
Error: Missing argument for parameter "project.a".
7474
-> required by _build/default/bin/bin.exe
7575
-> required by _build/install/default/bin/project.bin
76-
Hint: Pass an argument implementing project.a to the dependency, or add
77-
(parameters project.a)
76+
Hint: Pass an argument implementing "project.a" to the dependency.
7877
[1]
7978
8079
It's an error to instantiate twice without renamming: (dune might be able to
@@ -141,12 +140,11 @@ dependencies, because its parameter `b` is missing:
141140
File "bin/dune", line 6, characters 31-37:
142141
6 | (instantiate lib_ab a_impl a_of_b)))
143142
^^^^^^
144-
Error: Parameter "project.b" is missing.
143+
Error: Missing argument for parameter "project.b".
145144
-> required by _build/default/bin/.bin.eobjs/native/dune__exe__Bin.cmx
146145
-> required by _build/default/bin/bin.exe
147146
-> required by _build/install/default/bin/project.bin
148-
Hint: Pass an argument implementing project.b to the dependency, or add
149-
(parameters project.b)
147+
Hint: Pass an argument implementing "project.b" to the dependency.
150148
[1]
151149
152150
However `lib_ab` can depend on `a_of_b`, such that the parameter `b` will be

test/blackbox-tests/test-cases/oxcaml/library-field-parameters.t

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -335,13 +335,12 @@ required parameters.
335335
File "bin/dune", line 1, characters 34-37:
336336
1 | (executable (name bin) (libraries lib))
337337
^^^
338-
Error: Parameter "project.a" is missing.
338+
Error: Missing argument for parameter "project.a".
339339
-> required by _build/default/bin/.bin.eobjs/native/dune__exe__Bin.cmx
340340
-> required by _build/default/bin/bin.exe
341341
-> required by alias bin/all
342342
-> required by alias default
343-
Hint: Pass an argument implementing project.a to the dependency, or add
344-
(parameters project.a)
343+
Hint: Pass an argument implementing "project.a" to the dependency.
345344
[1]
346345

347346
$ rm -r bin
@@ -357,14 +356,13 @@ Same for libraries:
357356
File "lib2/dune", line 1, characters 32-35:
358357
1 | (library (name lib2) (libraries lib))
359358
^^^
360-
Error: Parameter "project.a" is missing.
359+
Error: Missing argument for parameter "project.a".
361360
-> required by library "lib2" in _build/default/lib2
362361
-> required by _build/default/lib2/.lib2.objs/native/lib2.cmx
363362
-> required by _build/default/lib2/lib2.a
364363
-> required by alias lib2/all
365364
-> required by alias default
366-
Hint: Pass an argument implementing project.a to the dependency, or add
367-
(parameters project.a)
365+
Hint: Pass an argument implementing "project.a" to the dependency.
368366
[1]
369367

370368
It works if `lib2` is itself parameterised with the same parameters as `lib`:

0 commit comments

Comments
 (0)