Skip to content

Commit d7c3d9d

Browse files
committed
feature(boot): strip let%expect_test
allows us to write ppx_expect tests in more places without depending on the ppx itself As an example, add some unit tests for Dune_sexp.Escape. These will be handy once we drop support for pre 4.14 Signed-off-by: Rudi Grinberg <me@rgrinberg.com>
1 parent 350b976 commit d7c3d9d

File tree

8 files changed

+208
-12
lines changed

8 files changed

+208
-12
lines changed

boot/bootstrap.ml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ let keep_generated_files =
2525
!keep_generated_files
2626
;;
2727

28-
let modules = [ "boot/types"; "boot/libs"; "boot/duneboot" ]
28+
let pps = "boot/pps"
29+
let modules = pps :: [ "boot/types"; "boot/libs"; "boot/duneboot" ]
2930
let duneboot = ".duneboot"
3031
let prog = duneboot ^ ".exe"
3132

@@ -40,6 +41,8 @@ let () =
4041
if not keep_generated_files
4142
then
4243
at_exit (fun () ->
44+
(try Sys.remove "boot/pps.ml" with
45+
| Sys_error _ -> ());
4346
Array.iter (Sys.readdir ".") ~f:(fun fn ->
4447
if
4548
String.length fn >= String.length duneboot
@@ -71,9 +74,9 @@ let read_file fn =
7174

7275
let () =
7376
let v = Scanf.sscanf Sys.ocaml_version "%d.%d.%d" (fun a b c -> a, b, c) in
74-
let compiler, which =
77+
let compiler, ocamllex, which =
7578
if v >= min_supported_natively
76-
then "ocamlc", None
79+
then "ocamlc", "ocamllex", None
7780
else (
7881
let compiler = "ocamlfind -toolchain secondary ocamlc" in
7982
let output_fn, out = Filename.open_temp_file "duneboot" "ocamlfind-output" in
@@ -95,8 +98,10 @@ let () =
9598
a
9699
b);
97100
exit 2);
98-
compiler, Some "--secondary")
101+
let ocamllex = "ocamlfind -toolchain secondary ocamllex" in
102+
compiler, ocamllex, Some "--secondary")
99103
in
104+
exit_if_non_zero (runf "%s -q -o %s %s" ocamllex (pps ^ ".ml") (pps ^ ".mll"));
100105
exit_if_non_zero
101106
(runf
102107
"%s %s -intf-suffix .dummy -g -o %s -I boot %sunix.cma %s"

boot/dune

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
(name runtest)
88
(deps libs.ml))
99

10+
(ocamllex pps)
11+
1012
(alias
1113
(name check)
1214
(deps libs.ml))

boot/duneboot.ml

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -609,24 +609,24 @@ module Io = struct
609609
| Ok s -> s
610610
;;
611611

612-
let do_then_copy ~f a b =
612+
let do_then_copy ~f ~pp a b =
613613
let s = read_file a in
614614
with_file_out b ~f:(fun oc ->
615615
f oc;
616-
output_string oc s)
616+
output_string oc (if pp then Pps.pp s else s))
617617
;;
618618

619619
(* copy a file - fails if the file exists *)
620-
let copy a b = do_then_copy ~f:(fun _ -> ()) a b
620+
let copy a b = do_then_copy ~pp:false ~f:(fun _ -> ()) a b
621621

622622
(* copy a file and insert a header - fails if the file exists *)
623-
let copy_with_header ~header a b =
624-
do_then_copy ~f:(fun oc -> output_string oc header) a b
623+
let copy_with_header ~pp ~header a b =
624+
do_then_copy ~pp ~f:(fun oc -> output_string oc header) a b
625625
;;
626626

627627
(* copy a file and insert a directive - fails if the file exists *)
628628
let copy_with_directive ~directive a b =
629-
do_then_copy ~f:(fun oc -> fprintf oc "#%s 1 %S\n" directive a) a b
629+
do_then_copy ~pp:false ~f:(fun oc -> fprintf oc "#%s 1 %S\n" directive a) a b
630630
;;
631631

632632
let rec rm_rf fn =
@@ -1683,8 +1683,11 @@ module Library = struct
16831683
| Header | C _ ->
16841684
Io.copy_with_directive ~directive:"line" fn dst;
16851685
Fiber.return [ mangled ]
1686-
| Ml { kind = `Ml | `Mli; _ } ->
1687-
Io.copy_with_header ~header fn dst;
1686+
| Ml { kind = `Mli; _ } ->
1687+
Io.copy_with_header ~pp:false ~header fn dst;
1688+
Fiber.return [ mangled ]
1689+
| Ml { kind = `Ml; _ } ->
1690+
Io.copy_with_header ~pp:true ~header fn dst;
16881691
Fiber.return [ mangled ]
16891692
| Ml { kind = `Mll; _ } -> copy_lexer fn dst ~header >>> Fiber.return [ mangled ]
16901693
| Ml { kind = `Mly; _ } ->

boot/pps.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
val pp : string -> string

boot/pps.mll

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
let b = Buffer.create 512
3+
}
4+
5+
rule pp = parse
6+
| "let%expect_test" { skip lexbuf }
7+
| _ as c { Buffer.add_char b c; pp lexbuf }
8+
| eof { () }
9+
10+
and skip = parse
11+
| ";;" { pp lexbuf }
12+
| _ { skip lexbuf }
13+
| eof { failwith "unterminated let%expect_test" }
14+
15+
{
16+
let pp s =
17+
let lb = Lexing.from_string s in
18+
Buffer.clear b;
19+
pp lb;
20+
Buffer.contents b
21+
}

src/dune_digest/dune

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
(library
22
(name dune_digest)
3+
(preprocess
4+
(pps ppx_expect))
5+
(inline_tests)
36
(libraries
47
fiber
58
dune_scheduler

src/dune_sexp/dune

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
(library
22
(name dune_sexp)
33
(synopsis "[Internal] S-expression library")
4+
(inline_tests)
5+
(preprocess (pps ppx_expect))
46
(libraries stdune uutf))
57

68
(ocamllex lexer versioned_file_first_line)

src/dune_sexp/escape.ml

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,162 @@ let quoted s =
132132
Bytes.unsafe_set s' (n + 1) '"';
133133
Bytes.unsafe_to_string s'
134134
;;
135+
136+
let%expect_test "escaped - plain strings pass through unchanged" =
137+
let test s =
138+
let r = escaped s in
139+
Printf.printf "%S -> %S\n" s r
140+
in
141+
test "hello";
142+
test "foo_bar";
143+
test "123";
144+
test "a/b/c";
145+
[%expect
146+
{|
147+
"hello" -> "hello"
148+
"foo_bar" -> "foo_bar"
149+
"123" -> "123"
150+
"a/b/c" -> "a/b/c"
151+
|}]
152+
;;
153+
154+
let%expect_test "escaped - special characters are escaped" =
155+
let test s =
156+
let r = escaped s in
157+
Printf.printf "%S -> %S\n" s r
158+
in
159+
test "has\"quote";
160+
test "back\\slash";
161+
test "new\nline";
162+
test "tab\there";
163+
test "car\rret";
164+
test "back\bspace";
165+
[%expect
166+
{|
167+
"has\"quote" -> "has\\\"quote"
168+
"back\\slash" -> "back\\\\slash"
169+
"new\nline" -> "new\\nline"
170+
"tab\there" -> "tab\\there"
171+
"car\rret" -> "car\\rret"
172+
"back\bspace" -> "back\\bspace"
173+
|}]
174+
;;
175+
176+
let%expect_test "escaped - percent brace escaping" =
177+
let test s =
178+
let r = escaped s in
179+
Printf.printf "%S -> %S\n" s r
180+
in
181+
test "%{var}";
182+
test "100%";
183+
test "%%";
184+
test "%alone";
185+
[%expect
186+
{|
187+
"%{var}" -> "\\%{var}"
188+
"100%" -> "100%"
189+
"%%" -> "%%"
190+
"%alone" -> "%alone"
191+
|}]
192+
;;
193+
194+
let%expect_test "escaped - empty string" =
195+
let r = escaped "" in
196+
Printf.printf "%S -> %S\n" "" r;
197+
[%expect {| "" -> "" |}]
198+
;;
199+
200+
let%expect_test "escaped - non-ascii bytes are octal-escaped" =
201+
let test s =
202+
let r = escaped s in
203+
Printf.printf "%S -> %S\n" s r
204+
in
205+
test "\x00";
206+
test "\x01";
207+
test "\x7f";
208+
test "\xff";
209+
[%expect
210+
{|
211+
"\000" -> "\000"
212+
"\001" -> "\001"
213+
"\127" -> "\127"
214+
"\255" -> "\\255"
215+
|}]
216+
;;
217+
218+
let%expect_test "escaped - valid utf8 passes through" =
219+
let test s =
220+
let r = escaped s in
221+
Printf.printf "%S -> %S\n" s r
222+
in
223+
(* 2-byte: é *)
224+
test "\xc3\xa9";
225+
(* 3-byte: € *)
226+
test "\xe2\x82\xac";
227+
(* 4-byte: 𝄞 *)
228+
test "\xf0\x9d\x84\x9e";
229+
[%expect
230+
{|
231+
"\195\169" -> "\195\169"
232+
"\226\130\172" -> "\226\130\172"
233+
"\240\157\132\158" -> "\240\157\132\158"
234+
|}]
235+
;;
236+
237+
let%expect_test "quoted - wraps in double quotes" =
238+
let test s =
239+
let r = quoted s in
240+
Printf.printf "%S -> %s\n" s r
241+
in
242+
test "";
243+
test "hello";
244+
test "has space";
245+
test "has\"quote";
246+
test "new\nline";
247+
test "%{var}";
248+
[%expect
249+
{|
250+
"" -> ""
251+
"hello" -> "hello"
252+
"has space" -> "has space"
253+
"has\"quote" -> "has\"quote"
254+
"new\nline" -> "new\nline"
255+
"%{var}" -> "\%{var}"
256+
|}]
257+
;;
258+
259+
let%expect_test "quote_length - matches actual escaped length" =
260+
let test s =
261+
let ql = quote_length s in
262+
let actual = String.length (escaped s) in
263+
if ql <> actual
264+
then Printf.printf "MISMATCH %S: quote_length=%d escaped_length=%d\n" s ql actual
265+
in
266+
test "";
267+
test "hello";
268+
test "has\"quote";
269+
test "new\nline";
270+
test "%{var}";
271+
test "\x00\xff";
272+
test "\xc3\xa9";
273+
test "\xe2\x82\xac";
274+
test "\xf0\x9d\x84\x9e";
275+
print_endline "all match";
276+
[%expect {| all match |}]
277+
;;
278+
279+
let%expect_test "escaped - mixed content" =
280+
let test s =
281+
let r = escaped s in
282+
Printf.printf "%S -> %S\n" s r
283+
in
284+
test "hello\nworld\t!";
285+
test "say \"hi\" and \\go";
286+
test "%{x} is 100%";
287+
[%expect
288+
{|
289+
"hello\nworld\t!" -> "hello\\nworld\\t!"
290+
"say \"hi\" and \\go" -> "say \\\"hi\\\" and \\\\go"
291+
"%{x} is 100%" -> "\\%{x} is 100%"
292+
|}]
293+
;;

0 commit comments

Comments
 (0)