Commit | Line | Data |
---|---|---|
e69e4e8b SK |
1 | (* "exe" is for status of execution (whether any exceptions were raised) |
2 | * "out" is for status of output comparison (whether what was outputted is | |
3 | * what was expected) | |
4 | * | |
d3bdde4b SK |
5 | * code |> pass_a_exe |> pass_a_out |> ... |> pass_z_exe |> pass_z_out |
6 | * | |
7 | * pass a: | |
8 | * exe: OK | |
9 | * out: n/a | |
10 | * pass b: | |
11 | * exe: OK | |
12 | * out: OK | |
13 | * pass c: | |
14 | * exe: OK | |
15 | * out: ERROR | |
16 | * ... | |
17 | * | |
18 | * name | pass a | ... | pass z | |
19 | * ---------+--------+-----+-------- | |
20 | * exe foo | OK | ... | OK | |
21 | * out foo | OK | ... | ERROR | |
22 | * | |
23 | * *) | |
24 | ||
25 | open Printf | |
26 | ||
27 | module List = ListLabels | |
28 | module String = StringLabels | |
29 | ||
30 | module Option : sig | |
31 | type 'a t = 'a option | |
32 | ||
33 | val map : 'a t -> ('a -> 'b) -> 'b t | |
34 | end = struct | |
35 | type 'a t = 'a option | |
36 | ||
37 | let map t f = | |
38 | match t with | |
39 | | None -> None | |
40 | | Some x -> Some (f x) | |
41 | end | |
42 | ||
43 | type t = | |
44 | { name : string | |
45 | ; code : string | |
46 | ; out_lexing : (Tiger_parser.token list) option | |
47 | ; out_parsing : Tiger_absyn.t option | |
e69e4e8b | 48 | ; is_error_expected : (Tiger_error.t -> bool) |
d3bdde4b SK |
49 | } |
50 | ||
51 | type color = | |
52 | | Red | |
53 | | Yellow | |
54 | | Green | |
55 | ||
56 | ||
57 | let color_to_ansi_code = function | |
58 | | Red -> "\027[0;31m" | |
59 | | Yellow -> "\027[0;33m" | |
60 | | Green -> "\027[0;32m" | |
61 | ||
62 | let color color string = | |
63 | let color_on = color_to_ansi_code color in | |
64 | let color_off = "\027[0m" in | |
65 | sprintf "%s%s%s" color_on string color_off | |
66 | ||
e69e4e8b SK |
67 | let case |
68 | ?(out_lexing) | |
69 | ?(out_parsing) | |
70 | ?(is_error_expected=(fun _ -> false)) | |
71 | ~code | |
72 | name | |
73 | = | |
d3bdde4b SK |
74 | { name |
75 | ; code | |
76 | ; out_lexing | |
77 | ; out_parsing | |
e69e4e8b | 78 | ; is_error_expected |
d3bdde4b SK |
79 | } |
80 | ||
81 | let bar_sep = String.make 80 '-' | |
82 | let bar_end = String.make 80 '=' | |
83 | ||
84 | let indent = | |
85 | let unit_spaces = 2 in | |
86 | fun n -> | |
87 | String.make (n * unit_spaces) ' ' | |
88 | ||
89 | let pass_lexing code : (Tiger_parser.token list, string) result = | |
90 | let lexbuf = Lexing.from_string code in | |
91 | let rec tokens () = | |
92 | let token = Tiger_lexer.token lexbuf in | |
93 | (* Avoiding fragile pattern-matching *) | |
94 | if token = Tiger_parser.EOF then [] else token :: tokens () | |
95 | in | |
96 | match tokens () with | |
97 | | exception e -> Error (Printexc.to_string e) | |
98 | | tokens -> Ok tokens | |
99 | ||
e69e4e8b | 100 | let pass_parsing code : (Tiger_absyn.t, string) result = |
d3bdde4b SK |
101 | let lb = Lexing.from_string code in |
102 | match Tiger_parser.program Tiger_lexer.token lb with | |
103 | | exception Parsing.Parse_error -> | |
104 | let module L = Lexing in | |
105 | let L.({lex_curr_p = {pos_lnum=l; pos_bol=b; pos_cnum=c; _}; _}) = lb in | |
106 | let msg = sprintf "Syntax error around line: %d, column: %d" l (c - b) in | |
107 | Error msg | |
108 | | ast -> | |
109 | Ok ast | |
110 | ||
111 | let s = sprintf | |
112 | let p = printf | |
113 | let p_ln = print_newline | |
114 | let p_indent n = p "%s" (indent n) | |
115 | ||
116 | let run tests = | |
117 | let error_count = ref 0 in | |
e69e4e8b SK |
118 | let run_pass ~f ~input ~expect_output ~is_error_expected = |
119 | let output_status = "n/a" in | |
120 | let output_value = None in | |
d3bdde4b SK |
121 | match f input with |
122 | | exception e -> | |
df637739 | 123 | let status_text, error_text = |
e69e4e8b SK |
124 | (match e with |
125 | | Tiger_error.T e when is_error_expected e -> | |
df637739 | 126 | ((color Green "OK"), Tiger_error.to_string e) |
e69e4e8b SK |
127 | | Tiger_error.T e -> |
128 | incr error_count; | |
df637739 | 129 | ((color Red "ERROR"), Tiger_error.to_string e) |
e69e4e8b SK |
130 | | e -> |
131 | incr error_count; | |
df637739 | 132 | ((color Red "ERROR"), Printexc.to_string e) |
e69e4e8b SK |
133 | ) |
134 | in | |
df637739 | 135 | ( s "%s: %s" status_text error_text |
e69e4e8b SK |
136 | , output_status |
137 | , output_value | |
d3bdde4b SK |
138 | ) |
139 | | Error msg -> | |
140 | incr error_count; | |
141 | ( s "%s: %s" (color Red "ERROR") msg | |
e69e4e8b SK |
142 | , output_status |
143 | , output_value | |
d3bdde4b SK |
144 | ) |
145 | | Ok produced -> | |
e69e4e8b SK |
146 | let execution_status = s "%s" (color Green "OK") in |
147 | let output_status = | |
d3bdde4b | 148 | match |
e69e4e8b | 149 | Option.map expect_output (fun expected -> expected = produced) |
d3bdde4b SK |
150 | with |
151 | | None -> | |
e69e4e8b | 152 | s "%s" (color Yellow "expected output not provided") |
d3bdde4b SK |
153 | | Some true -> |
154 | s "%s" (color Green "OK") | |
155 | | Some false -> | |
156 | incr error_count; | |
157 | s "%s" (color Red "ERROR") | |
158 | in | |
e69e4e8b SK |
159 | let output_value = Some produced in |
160 | (execution_status, output_status, output_value) | |
d3bdde4b SK |
161 | in |
162 | List.iter tests ~f:( | |
e69e4e8b SK |
163 | fun {name; code; out_lexing; out_parsing; is_error_expected} -> |
164 | let (stat_lex_exe, stat_lex_out_cmp, _) = | |
165 | run_pass | |
166 | ~f:pass_lexing | |
167 | ~input:code | |
168 | ~expect_output:out_lexing | |
169 | ~is_error_expected | |
170 | in | |
171 | let (stat_pars_exe, stat_pars_out_cmp, _) = | |
172 | run_pass | |
173 | ~f:pass_parsing | |
174 | ~input:code | |
175 | ~expect_output:out_parsing | |
176 | ~is_error_expected | |
177 | in | |
d3bdde4b SK |
178 | p "%s" bar_sep; p_ln (); |
179 | p "Test: %S" name; p_ln (); | |
180 | p_indent 1; p "Lexing:"; p_ln (); | |
e69e4e8b SK |
181 | p_indent 2; p "exe: %s" stat_lex_exe ; p_ln (); |
182 | p_indent 2; p "out: %s" stat_lex_out_cmp; p_ln (); | |
d3bdde4b | 183 | p_indent 1; p "Parsing:"; p_ln (); |
e69e4e8b SK |
184 | p_indent 2; p "exe: %s" stat_pars_exe ; p_ln (); |
185 | p_indent 2; p "out: %s" stat_pars_out_cmp; p_ln (); | |
d3bdde4b SK |
186 | ); |
187 | p "%s" bar_end; p_ln (); | |
188 | let failures = !error_count in | |
189 | let clr = (if failures = 0 then Green else Red) in | |
190 | p "Failures: %s" (color clr (string_of_int failures)); p_ln (); | |
191 | p "%s" bar_end; p_ln (); | |
192 | exit failures |