test_angstrom.ml 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. open Angstrom
  2. module Alcotest = struct
  3. include Alcotest
  4. let bigstring =
  5. Alcotest.testable
  6. (fun fmt _bs -> Fmt.pf fmt "<bigstring>")
  7. ( = )
  8. end
  9. let check ?size f p is =
  10. let open Buffered in
  11. let state =
  12. List.fold_left (fun state chunk ->
  13. feed state (`String chunk))
  14. (parse ?initial_buffer_size:size p) is
  15. in
  16. f (state_to_result (feed state `Eof))
  17. let check_ok ?size ~msg test p is r =
  18. let r = Ok r in
  19. check ?size (fun result -> Alcotest.(check (result test string)) msg r result)
  20. p is
  21. let check_fail ?size ~msg p is =
  22. let r = Error "" in
  23. check ?size (fun result -> Alcotest.(check (result reject pass)) msg r result)
  24. p is
  25. let check_c ?size ~msg p is r = check_ok ?size ~msg Alcotest.char p is r
  26. let check_lc ?size ~msg p is r = check_ok ?size ~msg Alcotest.(list char) p is r
  27. let check_co ?size ~msg p is r = check_ok ?size ~msg Alcotest.(option char) p is r
  28. let check_s ?size ~msg p is r = check_ok ?size ~msg Alcotest.string p is r
  29. let check_bs ?size ~msg p is r = check_ok ?size ~msg Alcotest.bigstring p is r
  30. let check_ls ?size ~msg p is r = check_ok ?size ~msg Alcotest.(list string) p is r
  31. let check_int ?size ~msg p is r = check_ok ?size ~msg Alcotest.int p is r
  32. let bigstring_of_string s = Bigstringaf.of_string s ~off:0 ~len:(String.length s)
  33. let basic_constructors =
  34. [ "peek_char", `Quick, begin fun () ->
  35. check_co ~msg:"singleton input" peek_char ["t"] (Some 't');
  36. check_co ~msg:"longer input" peek_char ["true"] (Some 't');
  37. check_co ~msg:"empty input" peek_char [""] None;
  38. end
  39. ; "peek_char_fail", `Quick, begin fun () ->
  40. check_c ~msg:"singleton input" peek_char_fail ["t"] 't';
  41. check_c ~msg:"longer input" peek_char_fail ["true"] 't';
  42. check_fail ~msg:"empty input" peek_char_fail [""]
  43. end
  44. ; "char", `Quick, begin fun () ->
  45. check_c ~msg:"singleton 'a'" (char 'a') ["a"] 'a';
  46. check_c ~msg:"prefix 'a'" (char 'a') ["asdf"] 'a';
  47. check_fail ~msg:"'a' failure" (char 'a') ["b"];
  48. check_fail ~msg:"empty buffer" (char 'a') [""]
  49. end
  50. ; "int8", `Quick, begin fun () ->
  51. check_int ~msg:"singleton 'a'" (int8 0x0061) ["a"] 0x61;
  52. check_int ~msg:"prefix 'a'" (int8 0xff61) ["asdf"] 0x61;
  53. check_fail ~msg:"'a' failure" (int8 0xff61) ["b"];
  54. check_fail ~msg:"empty buffer" (int8 0xff61) [""];
  55. end
  56. ; "not_char", `Quick, begin fun () ->
  57. check_c ~msg:"not 'a' singleton" (not_char 'a') ["b"] 'b';
  58. check_c ~msg:"not 'a' prefix" (not_char 'a') ["baba"] 'b';
  59. check_fail ~msg:"not 'a' failure" (not_char 'a') ["a"];
  60. check_fail ~msg:"empty buffer" (not_char 'a') [""]
  61. end
  62. ; "any_char", `Quick, begin fun () ->
  63. check_c ~msg:"non-empty buffer" any_char ["a"] 'a';
  64. check_fail ~msg:"empty buffer" any_char [""]
  65. end
  66. ; "any_{,u}int8", `Quick, begin fun () ->
  67. check_int ~msg:"positive sign preserved" any_int8 ["\127"] 127;
  68. check_int ~msg:"negative sign preserved" any_int8 ["\129"] (-127);
  69. check_int ~msg:"sign invariant" any_uint8 ["\127"] 127;
  70. check_int ~msg:"sign invariant" any_uint8 ["\129"] (129)
  71. end
  72. ; "string", `Quick, begin fun () ->
  73. check_s ~msg:"empty string, non-empty buffer" (string "") ["asdf"] "";
  74. check_s ~msg:"empty string, empty buffer" (string "") [""] "";
  75. check_s ~msg:"exact string match" (string "asdf") ["asdf"] "asdf";
  76. check_s ~msg:"string is prefix of input" (string "as") ["asdf"] "as";
  77. check_fail ~msg:"input is prefix of string" (string "asdf") ["asd"];
  78. check_fail ~msg:"non-empty string, empty input" (string "test") [""]
  79. end
  80. ; "string_ci", `Quick, begin fun () ->
  81. check_s ~msg:"empty string, non-empty input" (string_ci "") ["asdf"] "";
  82. check_s ~msg:"empty string, empty input" (string_ci "") [""] "";
  83. check_s ~msg:"exact string match" (string_ci "asdf") ["AsDf"] "AsDf";
  84. check_s ~msg:"string is prefix of input" (string_ci "as") ["AsDf"] "As";
  85. check_fail ~msg:"input is prefix of string" (string_ci "asdf") ["Asd"];
  86. check_fail ~msg:"non-empty string, empty input" (string_ci "test") [""]
  87. end
  88. ; "take_bigstring", `Quick, begin fun () ->
  89. check_bs ~msg:"empty bigstring" (take_bigstring 0) ["asdf"] (bigstring_of_string "");
  90. check_bs ~msg:"bigstring" (take_bigstring 2) ["asdf"] (bigstring_of_string "as");
  91. check_fail ~msg:"asking for too much" (take_bigstring 5) ["asdf"];
  92. end
  93. ; "take_while", `Quick, begin fun () ->
  94. check_s ~msg:"true, non-empty input" (take_while (fun _ -> true)) ["asdf"] "asdf";
  95. check_s ~msg:"true, empty input" (take_while (fun _ -> true)) [""] "";
  96. check_s ~msg:"false, non-empty input" (take_while (fun _ -> false)) ["asdf"] "";
  97. check_s ~msg:"false, empty input" (take_while (fun _ -> false)) [""] "";
  98. end
  99. ; "take_while1", `Quick, begin fun () ->
  100. check_s ~msg:"true, non-empty input" (take_while1 (fun _ -> true)) ["asdf"] "asdf";
  101. check_fail ~msg:"false, non-empty input" (take_while1 (fun _ -> false)) ["asdf"];
  102. check_fail ~msg:"true, empty input" (take_while1 (fun _ -> true)) [""];
  103. check_fail ~msg:"false, empty input" (take_while1 (fun _ -> false)) [""];
  104. end
  105. ; "advance", `Quick, begin fun () ->
  106. check_s ~msg:"non-empty input" (advance 3 >>= fun () -> take 1) ["asdf"] "f";
  107. check_fail ~msg:"advance more than available" (advance 5) ["asdf"];
  108. check_fail ~msg:"advance on empty input" (advance 3) [""];
  109. end
  110. ]
  111. module type EndianBigstring = sig
  112. val set_int16 : Bigstringaf.t -> int -> int -> unit
  113. val set_int32 : Bigstringaf.t -> int -> int32 -> unit
  114. val set_int64 : Bigstringaf.t -> int -> int64 -> unit
  115. val set_float : Bigstringaf.t -> int -> float -> unit
  116. val set_double : Bigstringaf.t -> int -> float -> unit
  117. end
  118. module Endian(Es : EndianBigstring) = struct
  119. type 'a endian = {
  120. name : string;
  121. size : int;
  122. zero : 'a;
  123. min : 'a;
  124. max : 'a;
  125. dump : Bigstringaf.t -> int -> 'a -> unit;
  126. testable : 'a Alcotest.testable
  127. }
  128. let int16 = {
  129. name = "int16";
  130. size = 2;
  131. zero = 0;
  132. min = ~-32768;
  133. max = 32767;
  134. dump = Es.set_int16;
  135. testable = Alcotest.int
  136. }
  137. let int32 = {
  138. name = "int32";
  139. size = 4;
  140. zero = Int32.zero;
  141. min = Int32.min_int;
  142. max = Int32.max_int;
  143. dump = Es.set_int32;
  144. testable = Alcotest.int32
  145. }
  146. let int64 = {
  147. name = "int64";
  148. size = 8;
  149. zero = Int64.zero;
  150. min = Int64.min_int;
  151. max = Int64.max_int;
  152. dump = Es.set_int64;
  153. testable = Alcotest.int64
  154. }
  155. let float = {
  156. name = "float";
  157. size = 4;
  158. zero = 0.0;
  159. (* XXX: Not really min/max *)
  160. min = ~-.2e10;
  161. max = 2e10;
  162. dump = Es.set_float;
  163. testable = Alcotest.float 0.0
  164. }
  165. let double = {
  166. name = "double";
  167. size = 8;
  168. zero = 0.0;
  169. (* XXX: Not really min/max *)
  170. min = ~-.2e30;
  171. max = 2e30;
  172. dump = Es.set_double;
  173. testable = Alcotest.float 0.0
  174. }
  175. let uint16 = { int16 with name = "uint16"; min = 0; max = 65535 }
  176. let uint32 = { int32 with name = "uint32" }
  177. let dump actual size value =
  178. let buf = Bigstringaf.of_string ~off:0 ~len:size (String.make size '\xff') in
  179. actual buf 0 value;
  180. Bigstringaf.substring ~off:0 ~len:size buf
  181. let make_tests e parse = e.name, `Quick, begin fun () ->
  182. check_ok ~msg:"zero" e.testable parse [dump e.dump e.size e.zero] e.zero;
  183. check_ok ~msg:"min" e.testable parse [dump e.dump e.size e.min ] e.min;
  184. check_ok ~msg:"max" e.testable parse [dump e.dump e.size e.max ] e.max;
  185. check_ok ~msg:"trailing" e.testable parse [dump e.dump (e.size + 1) e.zero] e.zero;
  186. end
  187. module type EndianSig = module type of LE
  188. let tests (module E : EndianSig) = [
  189. make_tests int16 E.any_int16;
  190. make_tests int32 E.any_int32;
  191. make_tests int64 E.any_int64;
  192. make_tests uint16 E.any_uint16;
  193. make_tests float E.any_float;
  194. make_tests double E.any_double;
  195. ]
  196. end
  197. let little_endian =
  198. let module E = Endian(struct
  199. let set_int16 = Bigstringaf.unsafe_set_int16_le
  200. let set_int32 = Bigstringaf.unsafe_set_int32_le
  201. let set_int64 = Bigstringaf.unsafe_set_int64_le
  202. let set_float bs off f = Bigstringaf.unsafe_set_int32_le bs off (Int32.bits_of_float f)
  203. let set_double bs off d = Bigstringaf.unsafe_set_int64_le bs off (Int64.bits_of_float d)
  204. end) in
  205. E.tests (module LE)
  206. let big_endian =
  207. let module E = Endian(struct
  208. let set_int16 = Bigstringaf.unsafe_set_int16_be
  209. let set_int32 = Bigstringaf.unsafe_set_int32_be
  210. let set_int64 = Bigstringaf.unsafe_set_int64_be
  211. let set_float bs off f = Bigstringaf.unsafe_set_int32_be bs off (Int32.bits_of_float f)
  212. let set_double bs off d = Bigstringaf.unsafe_set_int64_be bs off (Int64.bits_of_float d)
  213. end) in
  214. E.tests (module BE)
  215. let monadic =
  216. [ "fail", `Quick, begin fun () ->
  217. check_fail ~msg:"non-empty input" (fail "<msg>") ["asdf"];
  218. check_fail ~msg:"empty input" (fail "<msg>") [""]
  219. end
  220. ; "return", `Quick, begin fun () ->
  221. check_s ~msg:"non-empty input" (return "test") ["asdf"] "test";
  222. check_s ~msg:"empty input" (return "test") [""] "test";
  223. end
  224. ; "bind", `Quick, begin fun () ->
  225. check_s ~msg:"data dependency" (take 2 >>= fun s -> string s) ["asas"] "as";
  226. end
  227. ]
  228. let applicative =
  229. [ "applicative", `Quick, begin fun () ->
  230. check_s ~msg:"`foo *> bar` returns bar" (string "foo" *> string "bar") ["foobar"] "bar";
  231. check_s ~msg:"`foo <* bar` returns bar" (string "foo" <* string "bar") ["foobar"] "foo";
  232. end
  233. ]
  234. let alternative =
  235. [ "alternative", `Quick, begin fun () ->
  236. check_c ~msg:"char a | char b" (char 'a' <|> char 'b') ["a"] 'a';
  237. check_c ~msg:"char b | char a" (char 'b' <|> char 'a') ["a"] 'a';
  238. check_s ~msg:"string 'a' | string 'b'" (string "a" <|> string "b") ["a"] "a";
  239. check_s ~msg:"string 'b' | string 'a'" (string "b" <|> string "a") ["a"] "a";
  240. end ]
  241. let combinators =
  242. [ "many", `Quick, begin fun () ->
  243. check_lc ~msg:"empty input" (many (char 'a')) [""] [];
  244. check_lc ~msg:"single char" (many (char 'a')) ["a"] ['a'];
  245. check_lc ~msg:"two chars" (many (char 'a')) ["aa"] ['a'; 'a'];
  246. end
  247. ; "many_till", `Quick, begin fun () ->
  248. check_lc ~msg:"not greedy" (many_till any_char (char '-')) ["ab-ab-"] ['a'; 'b'];
  249. end
  250. ; "sep_by1", `Quick, begin fun () ->
  251. let parser = sep_by1 (char ',') (char 'a') in
  252. check_lc ~msg:"single char" parser ["a"] ['a'];
  253. check_lc ~msg:"many chars" parser ["a,a"] ['a'; 'a'];
  254. check_lc ~msg:"no trailing sep" parser ["a,"] ['a'];
  255. end
  256. ; "count", `Quick, begin fun () ->
  257. check_lc ~msg:"empty input" (count 0 (char 'a')) [""] [];
  258. check_lc ~msg:"exact input" (count 1 (char 'a')) ["a"] ['a'];
  259. check_lc ~msg:"additonal input" (count 2 (char 'a')) ["aaa"] ['a'; 'a'];
  260. check_fail ~msg:"bad input" (count 2 (char 'a')) ["abb"];
  261. end
  262. ; "scan_state", `Quick, begin fun () ->
  263. check_s ~msg:"scan_state" (scan_state "" (fun s -> function
  264. | 'a' -> Some s
  265. | '.' -> None
  266. | c -> Some ((String.make 1 c) ^ s)
  267. )) ["abaacba."] "bcb";
  268. let p =
  269. count 2 (scan_state "" (fun s -> function
  270. | '.' -> None
  271. | c -> Some (s ^ String.make 1 c)
  272. ))
  273. >>| String.concat "" in
  274. check_s ~msg:"state reset between runs" p ["bcd."] "bcd";
  275. end
  276. ]
  277. let incremental =
  278. [ "within chunk boundary", `Quick, begin fun () ->
  279. check_s ~msg:"string on each side of 2 inputs"
  280. (string "this" *> string "that") ["this"; "that"] "that";
  281. check_s ~msg:"string on each side of 3 inputs"
  282. (string "thi" *> string "st" *> string "hat") ["thi"; "st"; "hat"] "hat";
  283. check_s ~msg:"string straddling 2 inputs"
  284. (string "thisthat") ["this"; "that"] "thisthat";
  285. check_s ~msg:"string straddling 3 inputs"
  286. (string "thisthat") ["thi"; "st"; "hat"] "thisthat";
  287. end
  288. ; "peek_char and empty chunks", `Quick, begin fun () ->
  289. let decoder len =
  290. let open Angstrom in
  291. let buf = Buffer.create len in
  292. fix @@ fun m ->
  293. available >>= function
  294. | 0 -> peek_char >>= (function
  295. | Some _ -> commit *> m
  296. | None ->
  297. let ret = Buffer.contents buf in
  298. Buffer.clear buf;
  299. commit *> return ret)
  300. | n -> take n >>= fun chunk -> Buffer.add_string buf chunk; commit *> m
  301. in
  302. check_s ~msg:"empty input multiple times and peek_char"
  303. (decoder 0xFF) [ "Whole Lotta Love"; ""; ""; "" ] "Whole Lotta Love"
  304. end
  305. ; "across chunk boundary", `Quick, begin fun () ->
  306. check_s ~size:4 ~msg:"string on each side of 2 chunks"
  307. (string "this" *> string "that") ["this"; "that"] "that";
  308. check_s ~size:3 ~msg:"string on each side of 3 chunks"
  309. (string "thi" *> string "st" *> string "hat") ["thi"; "st"; "hat"] "hat";
  310. check_s ~size:4 ~msg:"string straddling 2 chunks"
  311. (string "thisthat") ["this"; "that"] "thisthat";
  312. check_s ~size:3 ~msg:"string straddling 3 chunks"
  313. (string "thisthat") ["thi"; "st"; "hat"] "thisthat";
  314. end
  315. ; "across chunk boundary with commit", `Quick, begin fun () ->
  316. check_s ~size:4 ~msg:"string on each side of 2 chunks"
  317. (string "this" *> commit *> string "that") ["this"; "that"] "that";
  318. check_s ~size:3 ~msg:"string on each side of 3 chunks"
  319. (string "thi" *> string "st" *> commit *> string "hat") ["thi"; "st"; "hat"] "hat";
  320. end ]
  321. let count_while_regression =
  322. [ "proper position set after count_while", `Quick, begin fun () ->
  323. check_s ~msg:"take_while then eof"
  324. (take_while (fun _ -> true) <* end_of_input) ["asdf"; ""] "asdf";
  325. check_s ~msg:"take_while1 then eof"
  326. (take_while1 (fun _ -> true) <* end_of_input) ["asdf"; ""] "asdf";
  327. end ]
  328. let choice_commit =
  329. [ "", `Quick, begin fun () ->
  330. let p =
  331. choice [ string "@@" *> commit *> char '*'
  332. ; string "@" *> commit *> char '!' ]
  333. in
  334. Alcotest.(check (result reject string))
  335. "commit to branch"
  336. (Error ": char '*'")
  337. (parse_string p "@@^");
  338. end ]
  339. let input =
  340. let test p input ~off ~len expect =
  341. match Angstrom.Unbuffered.parse p with
  342. | Done _ | Fail _ -> assert false
  343. | Partial { continue; committed } ->
  344. Alcotest.(check int) "committed is zero" 0 committed;
  345. let bs = Bigstringaf.of_string input ~off:0 ~len:(String.length input) in
  346. let state = continue bs ~off ~len Complete in
  347. Alcotest.(check (result string string))
  348. "offset and length respected"
  349. (Ok expect)
  350. (Angstrom.Unbuffered.state_to_result state);
  351. in
  352. [ "offset and length respected", `Quick, begin fun () ->
  353. let open Angstrom in
  354. let take_all = take_while (fun _ -> true) in
  355. test take_all "abcd" ~off:1 ~len:2 "bc";
  356. test (take 4 *> take_all) "abcdefg" ~off:0 ~len:7 "efg";
  357. end ]
  358. ;;
  359. let () =
  360. Alcotest.run "test suite"
  361. [ "basic constructors" , basic_constructors
  362. ; "little endian" , little_endian
  363. ; "big endian" , big_endian
  364. ; "monadic interface" , monadic
  365. ; "applicative interface" , applicative
  366. ; "alternative" , alternative
  367. ; "combinators" , combinators
  368. ; "incremental input" , incremental
  369. ; "count_while regression", count_while_regression
  370. ; "choice and commit" , choice_commit
  371. ; "input" , input
  372. ]