(*--------------------------------------------------------------------------- Copyright (c) 2025 Thomas Gazagnaire. All rights reserved. SPDX-License-Identifier: MIT ---------------------------------------------------------------------------*) (* Crowbar-based fuzz testing for Punycode encoding/decoding *) open Crowbar (* Test that encode_utf8 never crashes on arbitrary input *) let test_encode_no_crash input = (try ignore (Punycode.encode_utf8 input) with Punycode.Error _ -> ()); check true (* Test that decode_utf8 never crashes on arbitrary input *) let test_decode_no_crash input = (try ignore (Punycode.decode_utf8 input) with Punycode.Error _ -> ()); check true (* Test roundtrip: encode then decode should give back original (case-insensitive) IDNA/Punycode lowercases ASCII characters during encoding per RFC 5891 *) let test_roundtrip input = try let encoded = Punycode.encode_utf8 input in let decoded = Punycode.decode_utf8 encoded in (* Compare lowercase versions since IDNA lowercases ASCII *) check_eq ~pp:Format.pp_print_string (String.lowercase_ascii input) (String.lowercase_ascii decoded) with Punycode.Error _ -> (* Some inputs might not encode/decode, that's ok *) check true (* Test ASCII-only strings (should pass through mostly unchanged) *) let test_ascii_string input = if String.length input > 0 then begin let ascii_only = String.init (String.length input mod 64) (fun i -> Char.chr (Char.code input.[i mod String.length input] mod 128)) in if String.length ascii_only > 0 then try ignore (Punycode.encode_utf8 ascii_only) with Punycode.Error _ -> () end; check true (* Test inputs starting with ACE prefix "xn--" *) let test_ace_prefix input = let ace_input = "xn--" ^ input in (try ignore (Punycode.decode_utf8 ace_input) with Punycode.Error _ -> ()); check true let suite = ( "punycode", [ test_case "encode no crash" [ bytes ] test_encode_no_crash; test_case "decode no crash" [ bytes ] test_decode_no_crash; test_case "roundtrip" [ bytes ] test_roundtrip; test_case "ascii string" [ bytes ] test_ascii_string; test_case "ace prefix" [ bytes ] test_ace_prefix; ] )