aboutsummaryrefslogtreecommitdiff
path: root/test/test_escape.ml
diff options
context:
space:
mode:
Diffstat (limited to 'test/test_escape.ml')
-rw-r--r--test/test_escape.ml42
1 files changed, 34 insertions, 8 deletions
diff --git a/test/test_escape.ml b/test/test_escape.ml
index 1587388..35a9b9d 100644
--- a/test/test_escape.ml
+++ b/test/test_escape.ml
@@ -70,16 +70,41 @@ let test_fold_long_line () =
let test_fold_never_splits_utf8 () =
let long = "SUMMARY:" ^ String.concat "" (List.init 40 (fun _ -> "\xc4\x99\xc5\x9b\xc4\x87")) in
let out = E.fold_ics long in
- let stripped =
- String.concat ""
- (List.filter_map
- (fun l ->
- let l = if l <> "" && l.[String.length l - 1] = '\r' then String.sub l 0 (String.length l - 1) else l in
- if l = "" then None else if l.[0] = ' ' then Some (String.sub l 1 (String.length l - 1)) else Some l)
- (String.split_on_char '\n' out))
+ let chunks =
+ List.filter_map
+ (fun l ->
+ let l = if l <> "" && l.[String.length l - 1] = '\r' then String.sub l 0 (String.length l - 1) else l in
+ if l = "" then None else if l.[0] = ' ' then Some (String.sub l 1 (String.length l - 1)) else Some l)
+ (String.split_on_char '\n' out)
in
+ (* The named property: no chunk may START with a UTF-8 continuation byte
+ (0x80-0xBF) -- that would mean the previous fold cut mid-character.
+ Unfolding losslessly (below) cannot detect this on its own: concatenation
+ is insensitive to where the cuts fell, so a fold at ANY position still
+ round-trips. *)
+ List.iter
+ (fun c ->
+ if String.length c > 0 then
+ Alcotest.(check bool) "chunk does not start mid-UTF-8" true (Char.code c.[0] land 0xC0 <> 0x80))
+ chunks;
+ let stripped = String.concat "" chunks in
check "unfolds to the original" long stripped
+(* B1 regression: 100 consecutive UTF-8 continuation bytes (0x80-0xBF) is not
+ producible by valid UTF-8 (whose longest continuation run is 3), but
+ fold_ics must stay TOTAL on arbitrary octet strings. A backoff loop with no
+ hard-cut fallback backs `cut` all the way down to `pos`, yielding a
+ zero-length chunk and recursing on the identical position forever. *)
+let test_fold_pathological_input_terminates () =
+ let pathological = String.make 100 '\x80' in
+ let out = E.fold_ics pathological in
+ let lines = String.split_on_char '\n' out in
+ List.iter
+ (fun l ->
+ let l = if l <> "" && l.[String.length l - 1] = '\r' then String.sub l 0 (String.length l - 1) else l in
+ if String.length l > 75 then Alcotest.failf "line of %d octets exceeds 75" (String.length l))
+ (List.filter (fun l -> l <> "") lines)
+
let suite =
( "Escape",
[ Alcotest.test_case "latex" `Quick test_latex;
@@ -90,4 +115,5 @@ let suite =
Alcotest.test_case "flavour names" `Quick test_flavour_names;
Alcotest.test_case "fold: short unchanged" `Quick test_fold_short_line_unchanged;
Alcotest.test_case "fold: long line" `Quick test_fold_long_line;
- Alcotest.test_case "fold: never splits utf8" `Quick test_fold_never_splits_utf8 ] )
+ Alcotest.test_case "fold: never splits utf8" `Quick test_fold_never_splits_utf8;
+ Alcotest.test_case "fold: pathological input terminates" `Quick test_fold_pathological_input_terminates ] )