-
-
Notifications
You must be signed in to change notification settings - Fork 60
/
test_helpers.ml
279 lines (220 loc) · 7.43 KB
/
test_helpers.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
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
(* This Source Code Form is subject to the terms of the Mozilla Public License,
v. 2.0. If a copy of the MPL was not distributed with this file, You can
obtain one at https://mozilla.org/MPL/2.0/. *)
open OUnit2
let _directory = "_scratch"
let _coverage = "_coverage"
let _preserve_directory = "_preserve"
let _test_context = ref None
let _read_file name =
let buffer = Buffer.create 4096 in
let channel = open_in name in
try
let rec read () =
try input_char channel |> Buffer.add_char buffer; read ()
with End_of_file -> ()
in
read ();
close_in channel;
Buffer.contents buffer
with exn ->
close_in_noerr channel;
raise exn
let _command_failed ?status command =
match status with
| None -> Printf.sprintf "'%s' did not exit" command |> failwith
| Some v -> Printf.sprintf "'%s' failed with status %i" command v |> failwith
let _run_int command =
begin
match !_test_context with
| None -> ()
| Some context -> logf context `Info "Running '%s'" command
end;
match Unix.system command with
| Unix.WEXITED v -> v
| _ -> _command_failed command
let run command =
let v = _run_int command in
if v <> 0 then _command_failed command ~status:v
let _run_bool command = _run_int command = 0
let _with_directory context f =
if Sys.file_exists _directory then run ("rm -r " ^ _directory);
Unix.mkdir _directory 0o755;
let old_wd = Sys.getcwd () in
let new_wd = Filename.concat old_wd _directory in
Sys.chdir new_wd;
_test_context := Some context;
let restore () =
_test_context := None;
Sys.chdir old_wd;
let move =
if Sys.file_exists _coverage then true
else
try Unix.mkdir _coverage 0o755; true
with _ -> false
in
if move then begin
let files =
Sys.readdir _directory
|> Array.to_list
|> List.filter (fun s -> Filename.check_suffix s ".out.meta")
in
let rec destination_file n =
let candidate =
Printf.sprintf "meta%04d.out" n |> Filename.concat _coverage in
if Sys.file_exists candidate then destination_file (n + 1)
else candidate
in
files |> List.iter (fun source ->
Sys.rename (Filename.concat _directory source) (destination_file 0))
end;
run ("rm -r " ^ _directory)
in
logf context `Info "In directory '%s'" new_wd;
try f (); restore ()
with exn -> restore (); raise exn
let _compiler = ref "none"
let _object = ref "none"
let _library = ref "none"
let compiler () = !_compiler
let with_bisect_args arguments =
let ppxopt =
if String.trim arguments = "" then ""
else "-ppxopt 'bisect_ppx," ^ arguments ^ "'"
in
"-package bisect_ppx " ^ ppxopt
let with_bisect () = with_bisect_args ""
type compiler = Ocamlc | Ocamlopt
let _with_compiler compiler f =
begin
match compiler with
| Ocamlc ->
_compiler := "ocamlc";
_object := "cmo";
_library := "cma"
| Ocamlopt ->
_compiler := "ocamlopt";
_object := "cmx";
_library := "cmxa"
end;
f ()
let _bytecode_only =
try Sys.getenv "BYTECODE_ONLY" <> ""
with Not_found -> false
let test name f =
let bytecode =
"byte" >:: fun context ->
_with_directory context (fun () ->
_with_compiler Ocamlc f)
in
let native =
"native" >:: fun context ->
_with_directory context (fun () ->
_with_compiler Ocamlopt f)
in
name >::: (if _bytecode_only then [bytecode] else [bytecode; native])
let have_binary binary =
_run_bool ("which " ^ binary ^ " > /dev/null 2> /dev/null")
let have_package package =
_run_bool ("ocamlfind query " ^ package ^ "> /dev/null 2> /dev/null")
let ocamlc_version () =
Scanf.sscanf Sys.ocaml_version "%u.%u%[.]%[0-9]"
(fun major minor _periods patchlevel ->
major, minor, try Some (int_of_string patchlevel) with _ -> None)
let ocamlc_403_or_more () =
ocamlc_version () >= (4,3,None)
let ocamlc_404_or_more () =
ocamlc_version () >= (4,4,None)
let if_package package =
skip_if (not @@ have_package package) (package ^ " not installed")
let compile ?(r = "") arguments source =
let source_copy = Filename.basename source in
let intermediate = Filename.dirname source = _directory in
begin
if not intermediate then
let source_actual = Filename.concat Filename.parent_dir_name source in
run ("cp " ^ source_actual ^ " " ^ source_copy)
end;
Printf.sprintf
"%s ocamlfind %s -linkpkg %s %s %s"
"OCAMLPATH=../../../install/default/lib:$OCAMLPATH"
!_compiler arguments source_copy r
|> run
let report ?(f = "bisect*.out") ?(r = "") arguments =
Printf.sprintf
"../../../install/default/bin/bisect-ppx-report %s %s %s" arguments f r
|> run
let _preserve file destination =
let destination =
destination
|> Filename.concat _preserve_directory
|> Filename.concat Filename.parent_dir_name
in
run ("mkdir -p " ^ (Filename.dirname destination));
run ("cp " ^ file ^ " " ^ destination)
let diff ?preserve_as reference =
let preserve_as =
match preserve_as with
| None -> reference
| Some preserve_as -> preserve_as
in
let reference_actual = Filename.concat Filename.parent_dir_name reference in
let command =
Printf.sprintf
"diff -au --label %s --label %s %s output"
preserve_as "'actual output'" reference_actual
in
let status = _run_int (command ^ " > /dev/null") in
match status with
| 0 -> ()
| 1 ->
_preserve "output" preserve_as;
_run_int (command ^ " > delta") |> ignore;
let delta = _read_file "delta" in
Printf.sprintf "Difference against '%s':\n\n%s" preserve_as delta
|> assert_failure
| _ -> _command_failed command ~status
let normalize_source source normalized =
let source = _read_file source in
let normalized_file = open_out normalized in
try
let lexbuf = Lexing.from_string source in
let structure = Parse.implementation lexbuf in
let formatter = Format.formatter_of_out_channel normalized_file in
Pprintast.structure formatter structure;
Format.pp_print_newline formatter ();
close_out_noerr normalized_file
with e ->
close_out_noerr normalized_file;
raise e
let diff_ast reference =
let reference_actual = Filename.concat Filename.parent_dir_name reference in
normalize_source reference_actual "_dsource";
diff ~preserve_as:reference "_scratch/_dsource"
let compile_compare cflags directory =
let directory = Filename.concat "fixtures" directory in
let tests =
Sys.readdir directory
|> Array.to_list
|> List.filter (fun f -> Filename.check_suffix f ".ml")
|> List.filter (fun f ->
let f = Filename.chop_suffix f ".ml" in
not (Filename.check_suffix f ".reference"))
|> List.filter (fun f ->
let prefix = "test_" in
let prefix_length = String.length prefix in
String.length f < prefix_length || String.sub f 0 prefix_length <> prefix)
|> List.map begin fun f ->
let source = Filename.concat directory f in
let title = Filename.chop_suffix f ".ml" in
let reference = Filename.concat directory (title ^ ".reference.ml") in
test title (fun () ->
if Filename.check_suffix title "_403" then
skip_if (not (ocamlc_403_or_more ())) "requires OCaml 4.03 or more";
if Filename.check_suffix title "_404" then
skip_if (not (ocamlc_404_or_more ())) "requires OCaml 4.04 or more";
compile ((cflags ()) ^ " -w -A -dsource") source ~r:"2> output";
diff_ast reference)
end
in
directory >::: tests