Punycode (RFC3492) in OCaml

Update fuzz tests and sync changes

+26 -38
+26 -38
fuzz/fuzz_punycode.ml
··· 9 9 10 10 (* Test that encode_utf8 never crashes on arbitrary input *) 11 11 let test_encode_no_crash input = 12 - let _ = Punycode.encode_utf8 input in 13 - () 12 + ignore (Punycode.encode_utf8 input); 13 + check true 14 14 15 15 (* Test that decode_utf8 never crashes on arbitrary input *) 16 16 let test_decode_no_crash input = 17 - let _ = Punycode.decode_utf8 input in 18 - () 17 + ignore (Punycode.decode_utf8 input); 18 + check true 19 19 20 - (* Test roundtrip: encode then decode should give back original *) 20 + (* Test roundtrip: encode then decode should give back original (case-insensitive) 21 + IDNA/Punycode lowercases ASCII characters during encoding per RFC 5891 *) 21 22 let test_roundtrip input = 22 23 match Punycode.encode_utf8 input with 23 24 | Ok encoded -> ( 24 25 match Punycode.decode_utf8 encoded with 25 - | Ok decoded -> check_eq ~pp:Format.pp_print_string input decoded 26 + | Ok decoded -> 27 + (* Compare lowercase versions since IDNA lowercases ASCII *) 28 + check_eq ~pp:Format.pp_print_string 29 + (String.lowercase_ascii input) 30 + (String.lowercase_ascii decoded) 26 31 | Error _ -> 27 32 (* Some encoded values might not decode, that's ok for fuzz testing *) 28 - ()) 33 + check true) 29 34 | Error _ -> 30 35 (* Some inputs might not encode, that's ok *) 31 - () 32 - 33 - (* Test that error printing never crashes *) 34 - let () = 35 - let pos = { Punycode.byte_offset = 0; char_index = 0 } in 36 - let errors = 37 - [ 38 - Punycode.Overflow pos; 39 - Punycode.Invalid_character (pos, Uchar.of_int 0x1F600); 40 - Punycode.Invalid_digit (pos, 'x'); 41 - Punycode.Unexpected_end pos; 42 - Punycode.Invalid_utf8 pos; 43 - Punycode.Label_too_long 100; 44 - Punycode.Empty_label; 45 - ] 46 - in 47 - List.iter 48 - (fun e -> 49 - let _ = Format.asprintf "%a" Punycode.pp_error e in 50 - let _ = Punycode.error_to_string e in 51 - ()) 52 - errors 36 + check true 53 37 54 38 (* Test ASCII-only strings (should pass through mostly unchanged) *) 55 39 let test_ascii_string input = 56 - let ascii_only = 57 - String.init 58 - (String.length input mod 64) 59 - (fun i -> Char.chr (Char.code input.[i mod String.length input] mod 128)) 60 - in 61 - if String.length ascii_only > 0 then 62 - match Punycode.encode ascii_only with Ok _ -> () | Error _ -> () 40 + if String.length input > 0 then begin 41 + let ascii_only = 42 + String.init 43 + (String.length input mod 64) 44 + (fun i -> 45 + Char.chr (Char.code input.[i mod String.length input] mod 128)) 46 + in 47 + if String.length ascii_only > 0 then 48 + ignore (Punycode.encode_utf8 ascii_only) 49 + end; 50 + check true 63 51 64 52 (* Test inputs starting with ACE prefix "xn--" *) 65 53 let test_ace_prefix input = 66 54 let ace_input = "xn--" ^ input in 67 - let _ = Punycode.decode_utf8 ace_input in 68 - () 55 + ignore (Punycode.decode_utf8 ace_input); 56 + check true 69 57 70 58 let () = 71 59 add_test ~name:"punycode: encode no crash" [ bytes ] test_encode_no_crash;