Skip to content

Commit 8964281

Browse files
committed
Add a custom runner for tests
This custom runner allows to display the result of a test in the same way on Unix and Windows (by mapping Windows error codes to their equivalent result on Unix) It also uses GitHub CI formats when available so that test failures are referenced as such at their positions in the logs
1 parent 650c23e commit 8964281

File tree

2 files changed

+136
-0
lines changed

2 files changed

+136
-0
lines changed

tools/dune

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
(executable
2+
(name runner)
3+
(public_name runner)
4+
(package multicoretests)
5+
(libraries unix))

tools/runner.ml

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
(* Custom runner for the tests so that:
2+
- error codes on Windows are turned back into their Unix meaninrgs
3+
- anchors are added to CI logs with relevant information *)
4+
5+
let use_github_anchors = Sys.getenv_opt "CI" = Some "true"
6+
7+
let signals =
8+
let open Sys in
9+
[
10+
(sigabrt, "ABRT");
11+
(sigalrm, "ALRM");
12+
(sigfpe, "FPE");
13+
(sighup, "HUP");
14+
(sigill, "ILL");
15+
(sigint, "INT");
16+
(sigkill, "KILL");
17+
(sigpipe, "PIPE");
18+
(sigquit, "QUIT");
19+
(sigsegv, "SEGV");
20+
(sigterm, "TERM");
21+
(sigusr1, "USR1");
22+
(sigusr2, "USR2");
23+
(sigchld, "CHLD");
24+
(sigcont, "CONT");
25+
(sigstop, "STOP");
26+
(sigtstp, "TSTP");
27+
(sigttin, "TTIN");
28+
(sigttou, "TTOU");
29+
(sigvtalrm, "VTALRM");
30+
(sigprof, "PROF");
31+
(sigbus, "BUS");
32+
(sigpoll, "POLL");
33+
(sigsys, "SYS");
34+
(sigtrap, "TRAP");
35+
(sigurg, "URG");
36+
(sigxcpu, "XCPU");
37+
(sigxfsz, "XFSZ");
38+
]
39+
40+
let error fmt cmd msg =
41+
if use_github_anchors then
42+
Format.fprintf fmt "\n::error title=%s in %s::%s in %s\n%!" msg cmd msg cmd
43+
else Format.fprintf fmt "\nError: %s in %s\n%!" msg cmd
44+
45+
let pp_status_unix fmt cmd status =
46+
let open Unix in
47+
(match status with
48+
| WEXITED 0 -> ()
49+
| WEXITED s -> error fmt cmd (Printf.sprintf "Exit %d" s)
50+
| WSIGNALED s ->
51+
let msg =
52+
match List.assoc_opt s signals with
53+
| Some signal -> "Signal " ^ signal
54+
| None -> Printf.sprintf "Unknown signal %d" s
55+
in
56+
error fmt cmd msg
57+
| WSTOPPED s ->
58+
let msg =
59+
match List.assoc_opt s signals with
60+
| Some signal -> "Stop with signal " ^ signal
61+
| None -> Printf.sprintf "Stop with unknown signal %d" s
62+
in
63+
error fmt cmd msg);
64+
status = WEXITED 0
65+
66+
(* Under Windows, there is no such thing as terminating due to a
67+
signal, so the WSIGNALED and WSTOPPED cases are dead code.
68+
69+
The strategy is to use conventional exit values (which are 32-bit,
70+
not just 8-bit like on Unix) to describe the cause.
71+
The documentation of ”NTSTATUS Values” list {e many} cases, too
72+
many to handle them all. This is where the value akin to SEGV comes
73+
from. Other special cases will be caught as they appear.
74+
75+
The value used to match ABRT comes from the code of the abort
76+
function in the standard library.
77+
78+
{{:https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55}NTSTATUS Values}
79+
*)
80+
let pp_status_win fmt cmd status =
81+
let open Unix in
82+
(match status with
83+
| WEXITED 0 -> ()
84+
| WEXITED 3 -> error fmt cmd "Signal ABRT"
85+
| WEXITED -1073741819 (* 0xC0000005 *) -> error fmt cmd "Signal SEGV"
86+
| WEXITED s -> error fmt cmd (Printf.sprintf "Exit %d" s)
87+
(* Those last 2 cases are dead code on Windows *)
88+
| WSIGNALED s ->
89+
let msg =
90+
match List.assoc_opt s signals with
91+
| Some signal -> "Signal " ^ signal
92+
| None -> Printf.sprintf "Unknown signal %d" s
93+
in
94+
error fmt cmd msg
95+
| WSTOPPED s ->
96+
let msg =
97+
match List.assoc_opt s signals with
98+
| Some signal -> "Stop with signal " ^ signal
99+
| None -> Printf.sprintf "Stop with unknown signal %d" s
100+
in
101+
error fmt cmd msg);
102+
status = WEXITED 0
103+
104+
let pp_status = if Sys.win32 then pp_status_win else pp_status_unix
105+
106+
let run ofmt efmt argv =
107+
let argv =
108+
match argv with [| cmd |] -> [| cmd; "--verbose" |] | _ -> argv
109+
in
110+
let testdir = Filename.basename (Sys.getcwd ()) in
111+
let exe, cmd =
112+
if Filename.is_implicit argv.(0) then
113+
( Filename.concat Filename.current_dir_name argv.(0),
114+
Filename.concat testdir argv.(0) )
115+
else (argv.(0), argv.(0))
116+
in
117+
let cmdline = String.concat " " (Array.to_list argv) in
118+
Format.fprintf ofmt "\n\nStarting (in %s) %s:\n%!" testdir cmdline;
119+
let pid = Unix.(create_process exe argv stdin stdout stderr) in
120+
let _, status = Unix.waitpid [] pid in
121+
pp_status efmt cmd status
122+
123+
let _ =
124+
let open Format in
125+
if Array.length Sys.argv < 2 then (
126+
fprintf err_formatter
127+
"\nError: %s expects the\n command to run as argument\n%!" Sys.argv.(0);
128+
exit 1);
129+
let cmd = Array.sub Sys.argv 1 (Array.length Sys.argv - 1) in
130+
let success = run std_formatter err_formatter cmd in
131+
if not success then exit 1

0 commit comments

Comments
 (0)