Source file ppx_runtime.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
[@@@mutate exclude_file]
type origin = string option * string
type frame = {
name : string;
tags : string list;
origin : origin;
names : (string, int ref) Hashtbl.t;
mutable children : Windtrap.test list;
}
let groups : frame list ref = ref []
let top_level : (origin * Windtrap.test) list ref = ref []
let top_names : (string option * string * string, int ref) Hashtbl.t =
Hashtbl.create 16
let origins : origin list ref = ref []
let claimed = ref false
let partitions_of drives =
List.sort_uniq String.compare
(List.filter_map
(fun (library, partition) ->
if drives library then Some partition else None)
!origins)
let undriven_guard =
lazy
(let owner = Unix.getpid () in
at_exit (fun () ->
if (not !claimed) && Unix.getpid () = owner then begin
Printf.eprintf
"windtrap: registered inline tests were never driven: this \
executable links ppx_windtrap-preprocessed test code of no \
library (%s) and nothing ran it.\n\
windtrap: move the tests into a library stanza with \
(inline_tests), whose inline runner dune builds and drives, or \
drive the runner protocol yourself \
(Ppx_windtrap_runtime.Ppx_runtime.init/exit). Exiting 2: nothing \
ran.\n\
%!"
(String.concat ", " (partitions_of Option.is_none));
Stdlib.exit 2
end))
let module_name partition =
String.capitalize_ascii (List.hd (String.split_on_char '.' partition))
let rec unique names key name =
match Hashtbl.find_opt names (key name) with
| None ->
Hashtbl.add names (key name) (ref 1);
name
| Some n ->
incr n;
unique names key (Printf.sprintf "%s (%d)" name !n)
let note_origin ?library file =
if Option.is_none library then Lazy.force undriven_guard;
let origin = (library, Filename.basename file) in
origins := origin :: !origins;
origin
let scoped_name (library, partition) name =
match !groups with
| [] ->
unique top_names (fun name -> (library, module_name partition, name)) name
| frame :: _ -> unique frame.names Fun.id name
let register origin tree =
match !groups with
| [] -> top_level := (origin, tree) :: !top_level
| frame :: _ -> frame.children <- tree :: frame.children
let add_test ?library ~file ~pos ~tags name fn =
let origin = note_origin ?library file in
let name = scoped_name origin name in
register origin (Windtrap.test ~__POS__:pos ~tags name fn)
let enter_group ?library ~file ~tags name =
let origin = note_origin ?library file in
let name = scoped_name origin name in
groups :=
{ name; tags; origin; names = Hashtbl.create 8; children = [] } :: !groups
let leave_group () =
match !groups with
| [] -> invalid_arg "Ppx_runtime.leave_group: no group is open"
| frame :: rest ->
groups := rest;
register frame.origin
(Windtrap.group ~tags:frame.tags frame.name (List.rev frame.children))
module Baseline = Windtrap.Private.Baseline
module Loc = Windtrap.Private.Loc
module Run = Windtrap.Private.Run
let reached : (Windtrap.pos, unit) Hashtbl.t = Hashtbl.create 16
let reach node = Hashtbl.replace reached node ()
let unreached_message others =
let line (_, line, _, _) = string_of_int line in
let head = "the body returned without reaching this node" in
match others with
| [] -> head
| [ other ] -> Printf.sprintf "%s, nor the node of line %s" head (line other)
| others ->
Printf.sprintf "%s, nor the nodes of lines %s" head
(String.concat ", " (List.map line others))
let expect_test ~pos ~body_end ~nodes body output =
Hashtbl.reset reached;
Loc.delimit body;
Run.check_baseline ~loc:(Loc.of_pos body_end)
(Baseline.Trailing { pos })
(output ());
match List.filter (fun node -> not (Hashtbl.mem reached node)) nodes with
| [] -> ()
| first :: others -> Windtrap.fail ~__POS__:first (unreached_message others)
type protocol = {
prog : string;
runner : string option;
partition : string option;
list_partitions : bool;
}
let unread =
{ prog = ""; runner = None; partition = None; list_partitions = false }
let protocol = ref unread
let init argv =
claimed := true;
let rec parse p = function
| "inline-test-runner" :: library :: rest ->
parse { p with runner = Some library } rest
| "-partition" :: file :: rest ->
parse { p with partition = Some file } rest
| "-list-partitions" :: rest -> parse { p with list_partitions = true } rest
| _ :: rest -> parse p rest
| [] -> p
in
let prog = if Array.length argv > 0 then argv.(0) else "" in
protocol := parse { unread with prog } (Array.to_list argv)
let drives library =
Option.is_none library || Option.equal String.equal library !protocol.runner
let collect () =
claimed := true;
match !groups with
| _ :: _ ->
invalid_arg "Ppx_runtime.collect: a module%test group was never closed"
| [] ->
let registered = List.rev !top_level in
top_level := [];
Hashtbl.reset top_names;
let in_partition partition =
match !protocol.partition with
| None -> true
| Some wanted -> String.equal partition wanted
in
let by_module = Hashtbl.create 16 and first_seen = ref [] in
List.iter
(fun ((library, partition), tree) ->
if drives library && in_partition partition then
let name = module_name partition in
match Hashtbl.find_opt by_module name with
| Some trees -> trees := tree :: !trees
| None ->
first_seen := name :: !first_seen;
Hashtbl.add by_module name (ref [ tree ]))
registered;
List.rev_map
(fun name ->
Windtrap.group name (List.rev !(Hashtbl.find by_module name)))
!first_seen
let exit () =
let p = !protocol in
match (p.runner, p.list_partitions) with
| None, _ -> Stdlib.exit 0
| Some _, true ->
List.iter print_endline (partitions_of drives);
Stdlib.exit 0
| Some library, false ->
let suite =
match p.partition with
| Some file -> library ^ "/" ^ file
| None -> library
in
Stdlib.exit
(Windtrap.run ~argv:[| p.prog; "--corrected" |] suite (collect ()))