diff options
Diffstat (limited to 'test')
| -rw-r--r-- | test/test_escape.ml | 42 |
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 ] ) |
