OCaml implementation of the Mozilla Public Suffix service
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

test: add missing test files for pds, publicsuffix, punycode, rpmsg (E605)

- ocaml-pds: test_blob_store (13 tests), test_sqlite_blockstore (14 tests)
- ocaml-publicsuffix: test_publicsuffix (21 tests, Mozilla PSL vectors),
test_publicsuffix_cmd (11 tests)
- ocaml-punycode: test_punycode_idna (51 tests, RFC 5891 IDNA vectors)
- ocaml-rpmsg: test_rpmsg (9 tests, Linux kernel rpmsg constants)

+465
+3
test/cmd/dune
··· 1 + (test 2 + (name test_publicsuffix_cmd) 3 + (libraries publicsuffix publicsuffix.cmd cmdliner alcotest))
+164
test/cmd/test_publicsuffix_cmd.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (* Alcotest unit tests for the Publicsuffix_cmd module. 7 + 8 + The module exposes Cmdliner Term.t values. We evaluate each term by 9 + providing synthetic argv through Cmdliner.Cmd.eval_value, which lets 10 + us exercise the full term pipeline (argument parsing + PSL lookup) 11 + without spawning a subprocess. *) 12 + 13 + open Alcotest 14 + 15 + let psl = Publicsuffix.create () 16 + 17 + (* Helper: evaluate a Cmdliner term with a given domain argument and return 18 + the result. We build a throwaway Cmd.t, set argv to include the domain, 19 + and extract the value produced by eval_value. *) 20 + let eval_term_with_domain term domain = 21 + let info = Cmdliner.Cmd.info "test" in 22 + let cmd = Cmdliner.Cmd.v info term in 23 + let argv = [| "test"; domain |] in 24 + match Cmdliner.Cmd.eval_value ~argv cmd with 25 + | Ok (`Ok v) -> Some v 26 + | _ -> None 27 + 28 + let eval_term_no_args term = 29 + let info = Cmdliner.Cmd.info "test" in 30 + let cmd = Cmdliner.Cmd.v info term in 31 + let argv = [| "test" |] in 32 + match Cmdliner.Cmd.eval_value ~argv cmd with 33 + | Ok (`Ok v) -> Some v 34 + | _ -> None 35 + 36 + (* ---------- registrable_term ------------------------------------------ *) 37 + 38 + let test_registrable_term () = 39 + let term = Publicsuffix_cmd.registrable_term psl in 40 + match eval_term_with_domain term "www.example.com" with 41 + | Some (Ok v) -> check string "registrable" "example.com" v 42 + | Some (Error e) -> fail (Publicsuffix.error_to_string e) 43 + | None -> fail "term evaluation failed" 44 + 45 + let test_registrable_term_suffix () = 46 + let term = Publicsuffix_cmd.registrable_term psl in 47 + match eval_term_with_domain term "com" with 48 + | Some (Error Publicsuffix.Domain_is_public_suffix) -> () 49 + | Some (Ok v) -> fail (Printf.sprintf "expected error, got Ok %S" v) 50 + | Some (Error e) -> fail (Publicsuffix.error_to_string e) 51 + | None -> fail "term evaluation failed" 52 + 53 + (* ---------- suffix_term ----------------------------------------------- *) 54 + 55 + let test_suffix_term () = 56 + let term = Publicsuffix_cmd.suffix_term psl in 57 + match eval_term_with_domain term "www.example.co.uk" with 58 + | Some (Ok v) -> check string "suffix" "co.uk" v 59 + | Some (Error e) -> fail (Publicsuffix.error_to_string e) 60 + | None -> fail "term evaluation failed" 61 + 62 + (* ---------- is_suffix_term -------------------------------------------- *) 63 + 64 + let test_is_suffix_term () = 65 + let term = Publicsuffix_cmd.is_suffix_term psl in 66 + match eval_term_with_domain term "com" with 67 + | Some (Ok v) -> check bool "is suffix" true v 68 + | Some (Error e) -> fail (Publicsuffix.error_to_string e) 69 + | None -> fail "term evaluation failed" 70 + 71 + let test_is_suffix_term_false () = 72 + let term = Publicsuffix_cmd.is_suffix_term psl in 73 + match eval_term_with_domain term "example.com" with 74 + | Some (Ok v) -> check bool "not suffix" false v 75 + | Some (Error e) -> fail (Publicsuffix.error_to_string e) 76 + | None -> fail "term evaluation failed" 77 + 78 + (* ---------- is_registrable_term --------------------------------------- *) 79 + 80 + let test_is_registrable_term () = 81 + let term = Publicsuffix_cmd.is_registrable_term psl in 82 + match eval_term_with_domain term "example.com" with 83 + | Some (Ok v) -> check bool "is registrable" true v 84 + | Some (Error e) -> fail (Publicsuffix.error_to_string e) 85 + | None -> fail "term evaluation failed" 86 + 87 + (* ---------- section terms --------------------------------------------- *) 88 + 89 + let test_registrable_section_term () = 90 + let term = Publicsuffix_cmd.registrable_section_term psl in 91 + match eval_term_with_domain term "example.blogspot.com" with 92 + | Some (Ok (domain, sec)) -> 93 + check string "domain" "example.blogspot.com" domain; 94 + check bool "private section" true (sec = Publicsuffix.Private) 95 + | Some (Error e) -> fail (Publicsuffix.error_to_string e) 96 + | None -> fail "term evaluation failed" 97 + 98 + let test_suffix_section_term () = 99 + let term = Publicsuffix_cmd.suffix_section_term psl in 100 + match eval_term_with_domain term "example.com" with 101 + | Some (Ok (suffix, sec)) -> 102 + check string "suffix" "com" suffix; 103 + check bool "ICANN section" true (sec = Publicsuffix.ICANN) 104 + | Some (Error e) -> fail (Publicsuffix.error_to_string e) 105 + | None -> fail "term evaluation failed" 106 + 107 + (* ---------- stats_term ------------------------------------------------ *) 108 + 109 + let test_stats_term () = 110 + let term = Publicsuffix_cmd.stats_term psl in 111 + match eval_term_no_args term with 112 + | Some (total, icann, priv) -> 113 + check bool "total > 0" true (total > 0); 114 + check bool "icann > 0" true (icann > 0); 115 + check bool "private > 0" true (priv > 0); 116 + check bool "sum" true (icann + priv = total) 117 + | None -> fail "term evaluation failed" 118 + 119 + (* ---------- version_term ---------------------------------------------- *) 120 + 121 + let test_version_term () = 122 + let term = Publicsuffix_cmd.version_term psl in 123 + match eval_term_no_args term with 124 + | Some (version, commit) -> 125 + check bool "version non-empty" true (String.length version > 0); 126 + check bool "commit non-empty" true (String.length commit > 0) 127 + | None -> fail "term evaluation failed" 128 + 129 + (* ---------- domain_arg ------------------------------------------------ *) 130 + 131 + let test_domain_arg_missing () = 132 + (* When no domain argument is provided, the registrable_term should fail 133 + at the Cmdliner level (missing required positional argument). *) 134 + let term = Publicsuffix_cmd.registrable_term psl in 135 + let info = Cmdliner.Cmd.info "test" in 136 + let cmd = Cmdliner.Cmd.v info term in 137 + let argv = [| "test" |] in 138 + match Cmdliner.Cmd.eval_value ~argv cmd with 139 + | Ok (`Ok _) -> fail "expected failure for missing domain" 140 + | _ -> () (* Any non-Ok result is expected *) 141 + 142 + (* ---------- suite export ---------------------------------------------- *) 143 + 144 + let suite = 145 + [ 146 + ( "publicsuffix_cmd", 147 + [ 148 + test_case "registrable_term" `Quick test_registrable_term; 149 + test_case "registrable_term suffix error" `Quick 150 + test_registrable_term_suffix; 151 + test_case "suffix_term" `Quick test_suffix_term; 152 + test_case "is_suffix_term true" `Quick test_is_suffix_term; 153 + test_case "is_suffix_term false" `Quick test_is_suffix_term_false; 154 + test_case "is_registrable_term" `Quick test_is_registrable_term; 155 + test_case "registrable_section_term" `Quick 156 + test_registrable_section_term; 157 + test_case "suffix_section_term" `Quick test_suffix_section_term; 158 + test_case "stats_term" `Quick test_stats_term; 159 + test_case "version_term" `Quick test_version_term; 160 + test_case "domain_arg missing" `Quick test_domain_arg_missing; 161 + ] ); 162 + ] 163 + 164 + let () = Alcotest.run "Publicsuffix_cmd" suite
+4
test/dune
··· 4 4 (executable 5 5 (name psl_test) 6 6 (libraries publicsuffix)) 7 + 8 + (test 9 + (name test_publicsuffix) 10 + (libraries publicsuffix alcotest))
+294
test/test_publicsuffix.ml
··· 1 + (*--------------------------------------------------------------------------- 2 + Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved. 3 + SPDX-License-Identifier: ISC 4 + ---------------------------------------------------------------------------*) 5 + 6 + (* Alcotest unit tests for the Publicsuffix library. 7 + 8 + Test vectors are drawn from the official Mozilla PSL test algorithm: 9 + https://raw.githubusercontent.com/publicsuffix/list/master/tests/test_psl.txt *) 10 + 11 + open Alcotest 12 + 13 + let psl = Publicsuffix.create () 14 + 15 + (* ---------- helpers --------------------------------------------------- *) 16 + 17 + let check_ok msg expected f = 18 + match f () with 19 + | Ok v -> check string msg expected v 20 + | Error e -> fail (Publicsuffix.error_to_string e) 21 + 22 + let check_ok_bool msg expected f = 23 + match f () with 24 + | Ok v -> check bool msg expected v 25 + | Error e -> fail (Publicsuffix.error_to_string e) 26 + 27 + let check_err msg expected_err f = 28 + match f () with 29 + | Ok v -> fail (Printf.sprintf "%s: expected error, got Ok %S" msg v) 30 + | Error e -> 31 + check string msg 32 + (Publicsuffix.error_to_string expected_err) 33 + (Publicsuffix.error_to_string e) 34 + 35 + let check_err_bool msg expected_err f = 36 + match f () with 37 + | Ok v -> fail (Printf.sprintf "%s: expected error, got Ok %b" msg v) 38 + | Error e -> 39 + check string msg 40 + (Publicsuffix.error_to_string expected_err) 41 + (Publicsuffix.error_to_string e) 42 + 43 + let check_section msg expected_section f = 44 + match f () with 45 + | Ok (_, sec) -> 46 + check string msg expected_section 47 + (match sec with Publicsuffix.ICANN -> "ICANN" | Private -> "PRIVATE") 48 + | Error e -> fail (Publicsuffix.error_to_string e) 49 + 50 + (* ---------- public_suffix tests --------------------------------------- *) 51 + 52 + let test_suffix_basic () = 53 + check_ok "com" "com" (fun () -> 54 + Publicsuffix.public_suffix psl "www.example.com"); 55 + check_ok "co.uk" "co.uk" (fun () -> 56 + Publicsuffix.public_suffix psl "www.example.co.uk"); 57 + check_ok "example.com suffix" "com" (fun () -> 58 + Publicsuffix.public_suffix psl "example.com"); 59 + check_ok "com itself" "com" (fun () -> Publicsuffix.public_suffix psl "com") 60 + 61 + let test_suffix_wildcard () = 62 + (* *.mm rule: c.mm is a public suffix *) 63 + check_ok "ide.kyoto.jp" "ide.kyoto.jp" (fun () -> 64 + Publicsuffix.public_suffix psl "b.ide.kyoto.jp") 65 + 66 + let test_suffix_exception () = 67 + (* *.kobe.jp wildcard but !city.kobe.jp exception *) 68 + check_ok "city.kobe.jp exception => kobe.jp" "kobe.jp" (fun () -> 69 + Publicsuffix.public_suffix psl "city.kobe.jp"); 70 + (* *.ck wildcard but !www.ck exception *) 71 + check_ok "www.ck exception => ck" "ck" (fun () -> 72 + Publicsuffix.public_suffix psl "www.ck") 73 + 74 + let test_suffix_trailing_dot () = 75 + check_ok "trailing dot preserved" "com." (fun () -> 76 + Publicsuffix.public_suffix psl "example.com."); 77 + check_ok "no trailing dot" "com" (fun () -> 78 + Publicsuffix.public_suffix psl "example.com") 79 + 80 + let test_suffix_errors () = 81 + check_err "empty domain" Publicsuffix.Empty_domain (fun () -> 82 + Publicsuffix.public_suffix psl ""); 83 + check_err "leading dot" Publicsuffix.Leading_dot (fun () -> 84 + Publicsuffix.public_suffix psl ".example.com") 85 + 86 + (* ---------- registrable_domain tests ---------------------------------- *) 87 + 88 + let test_reg_icann_basic () = 89 + check_ok "example.com" "example.com" (fun () -> 90 + Publicsuffix.registrable_domain psl "example.com"); 91 + check_ok "www.example.com" "example.com" (fun () -> 92 + Publicsuffix.registrable_domain psl "www.example.com"); 93 + check_ok "b.example.com" "example.com" (fun () -> 94 + Publicsuffix.registrable_domain psl "b.example.com"); 95 + check_ok "a.b.example.com" "example.com" (fun () -> 96 + Publicsuffix.registrable_domain psl "a.b.example.com") 97 + 98 + let test_reg_second_level () = 99 + (* uk.com is itself a suffix *) 100 + check_err "uk.com is suffix" Publicsuffix.Domain_is_public_suffix (fun () -> 101 + Publicsuffix.registrable_domain psl "uk.com"); 102 + check_ok "example.uk.com" "example.uk.com" (fun () -> 103 + Publicsuffix.registrable_domain psl "example.uk.com"); 104 + check_ok "b.example.uk.com" "example.uk.com" (fun () -> 105 + Publicsuffix.registrable_domain psl "b.example.uk.com") 106 + 107 + let test_reg_wildcard () = 108 + (* *.mm rule *) 109 + check_err "mm is suffix" Publicsuffix.Domain_is_public_suffix (fun () -> 110 + Publicsuffix.registrable_domain psl "mm"); 111 + check_err "c.mm is suffix (wildcard)" Publicsuffix.Domain_is_public_suffix 112 + (fun () -> Publicsuffix.registrable_domain psl "c.mm"); 113 + check_ok "b.c.mm" "b.c.mm" (fun () -> 114 + Publicsuffix.registrable_domain psl "b.c.mm"); 115 + check_ok "a.b.c.mm" "b.c.mm" (fun () -> 116 + Publicsuffix.registrable_domain psl "a.b.c.mm") 117 + 118 + let test_reg_exception () = 119 + (* *.ck wildcard but !www.ck exception *) 120 + check_err "test.ck is suffix" Publicsuffix.Domain_is_public_suffix (fun () -> 121 + Publicsuffix.registrable_domain psl "test.ck"); 122 + check_ok "www.ck (exception)" "www.ck" (fun () -> 123 + Publicsuffix.registrable_domain psl "www.ck"); 124 + check_ok "foo.www.ck" "www.ck" (fun () -> 125 + Publicsuffix.registrable_domain psl "www.www.ck"); 126 + (* *.kobe.jp but !city.kobe.jp *) 127 + check_ok "city.kobe.jp (exception)" "city.kobe.jp" (fun () -> 128 + Publicsuffix.registrable_domain psl "city.kobe.jp"); 129 + check_ok "www.city.kobe.jp" "city.kobe.jp" (fun () -> 130 + Publicsuffix.registrable_domain psl "www.city.kobe.jp") 131 + 132 + let test_reg_com_is_suffix () = 133 + check_err "com is suffix" Publicsuffix.Domain_is_public_suffix (fun () -> 134 + Publicsuffix.registrable_domain psl "com") 135 + 136 + let test_reg_trailing_dot () = 137 + check_ok "trailing dot preserved" "example.com." (fun () -> 138 + Publicsuffix.registrable_domain psl "www.example.com."); 139 + check_ok "no trailing dot" "example.com" (fun () -> 140 + Publicsuffix.registrable_domain psl "www.example.com") 141 + 142 + let test_reg_errors () = 143 + check_err "empty domain" Publicsuffix.Empty_domain (fun () -> 144 + Publicsuffix.registrable_domain psl ""); 145 + check_err "leading dot" Publicsuffix.Leading_dot (fun () -> 146 + Publicsuffix.registrable_domain psl ".example.com"); 147 + check_err "domain is suffix" Publicsuffix.Domain_is_public_suffix (fun () -> 148 + Publicsuffix.registrable_domain psl "com") 149 + 150 + let test_reg_idn () = 151 + check_ok "food lion" "xn--85x722f.com.cn" (fun () -> 152 + Publicsuffix.registrable_domain psl "\xe9\xa3\x9f\xe7\x8b\xae.com.cn"); 153 + check_ok "punycode input" "xn--85x722f.com.cn" (fun () -> 154 + Publicsuffix.registrable_domain psl "xn--85x722f.com.cn"); 155 + check_ok "food lion with company" "xn--85x722f.xn--55qx5d.cn" (fun () -> 156 + Publicsuffix.registrable_domain psl 157 + "\xe9\xa3\x9f\xe7\x8b\xae.\xe5\x85\xac\xe5\x8f\xb8.cn"); 158 + check_ok "www prefix idn" "xn--85x722f.xn--55qx5d.cn" (fun () -> 159 + Publicsuffix.registrable_domain psl 160 + "www.\xe9\xa3\x9f\xe7\x8b\xae.\xe5\x85\xac\xe5\x8f\xb8.cn") 161 + 162 + let test_reg_mixed_case () = 163 + check_err "COM is suffix" Publicsuffix.Domain_is_public_suffix (fun () -> 164 + Publicsuffix.registrable_domain psl "COM"); 165 + check_ok "example.COM" "example.com" (fun () -> 166 + Publicsuffix.registrable_domain psl "example.COM"); 167 + check_ok "WwW.example.COM" "example.com" (fun () -> 168 + Publicsuffix.registrable_domain psl "WwW.example.COM") 169 + 170 + (* ---------- is_public_suffix tests ------------------------------------ *) 171 + 172 + let test_is_suffix () = 173 + check_ok_bool "com" true (fun () -> Publicsuffix.is_public_suffix psl "com"); 174 + check_ok_bool "example.com" false (fun () -> 175 + Publicsuffix.is_public_suffix psl "example.com"); 176 + check_ok_bool "co.uk" true (fun () -> 177 + Publicsuffix.is_public_suffix psl "co.uk"); 178 + check_ok_bool "example.co.uk" false (fun () -> 179 + Publicsuffix.is_public_suffix psl "example.co.uk"); 180 + (* wildcard: *.ck makes test.ck a suffix *) 181 + check_ok_bool "test.ck (wildcard)" true (fun () -> 182 + Publicsuffix.is_public_suffix psl "test.ck"); 183 + (* exception: !www.ck means www.ck is NOT a suffix *) 184 + check_ok_bool "www.ck (exception)" false (fun () -> 185 + Publicsuffix.is_public_suffix psl "www.ck"); 186 + check_ok_bool "ide.kyoto.jp" true (fun () -> 187 + Publicsuffix.is_public_suffix psl "ide.kyoto.jp") 188 + 189 + let test_is_suffix_errors () = 190 + check_err_bool "empty domain" Publicsuffix.Empty_domain (fun () -> 191 + Publicsuffix.is_public_suffix psl ""); 192 + check_err_bool "leading dot" Publicsuffix.Leading_dot (fun () -> 193 + Publicsuffix.is_public_suffix psl ".com") 194 + 195 + (* ---------- is_registrable_domain tests ------------------------------- *) 196 + 197 + let test_is_registrable () = 198 + check_ok_bool "example.com" true (fun () -> 199 + Publicsuffix.is_registrable_domain psl "example.com"); 200 + check_ok_bool "www.example.com" false (fun () -> 201 + Publicsuffix.is_registrable_domain psl "www.example.com"); 202 + check_ok_bool "com" false (fun () -> 203 + Publicsuffix.is_registrable_domain psl "com"); 204 + check_ok_bool "city.kobe.jp (exception)" true (fun () -> 205 + Publicsuffix.is_registrable_domain psl "city.kobe.jp"); 206 + check_ok_bool "www.city.kobe.jp" false (fun () -> 207 + Publicsuffix.is_registrable_domain psl "www.city.kobe.jp") 208 + 209 + (* ---------- section classification tests ------------------------------ *) 210 + 211 + let test_section_suffix () = 212 + check_section "com is ICANN" "ICANN" (fun () -> 213 + Publicsuffix.public_suffix_with_section psl "example.com"); 214 + check_section "co.uk is ICANN" "ICANN" (fun () -> 215 + Publicsuffix.public_suffix_with_section psl "example.co.uk"); 216 + check_section "blogspot.com is PRIVATE" "PRIVATE" (fun () -> 217 + Publicsuffix.public_suffix_with_section psl "example.blogspot.com"); 218 + check_section "github.io is PRIVATE" "PRIVATE" (fun () -> 219 + Publicsuffix.public_suffix_with_section psl "example.github.io") 220 + 221 + let test_section_registrable () = 222 + check_section "registrable ICANN" "ICANN" (fun () -> 223 + Publicsuffix.registrable_domain_with_section psl "www.example.com"); 224 + check_section "registrable blogspot PRIVATE" "PRIVATE" (fun () -> 225 + Publicsuffix.registrable_domain_with_section psl 226 + "www.example.blogspot.com"); 227 + check_section "registrable github PRIVATE" "PRIVATE" (fun () -> 228 + Publicsuffix.registrable_domain_with_section psl "myproject.github.io") 229 + 230 + (* ---------- rule count tests ------------------------------------------ *) 231 + 232 + let test_rule_counts () = 233 + let total = Publicsuffix.rule_count psl in 234 + let icann = Publicsuffix.icann_rule_count psl in 235 + let priv = Publicsuffix.private_rule_count psl in 236 + check bool "total > 0" true (total > 0); 237 + check bool "icann > 0" true (icann > 0); 238 + check bool "private > 0" true (priv > 0); 239 + check bool "icann + private = total" true (icann + priv = total); 240 + (* Sanity: the real PSL has thousands of rules *) 241 + check bool "total > 1000" true (total > 1000) 242 + 243 + (* ---------- version / commit ------------------------------------------ *) 244 + 245 + let test_version_info () = 246 + let v = Publicsuffix.version psl in 247 + let c = Publicsuffix.commit psl in 248 + check bool "version non-empty" true (String.length v > 0); 249 + check bool "commit non-empty" true (String.length c > 0) 250 + 251 + (* ---------- suite export ---------------------------------------------- *) 252 + 253 + let suite = 254 + [ 255 + ( "public_suffix", 256 + [ 257 + test_case "basic lookups" `Quick test_suffix_basic; 258 + test_case "wildcard rules" `Quick test_suffix_wildcard; 259 + test_case "exception rules" `Quick test_suffix_exception; 260 + test_case "trailing dot" `Quick test_suffix_trailing_dot; 261 + test_case "error cases" `Quick test_suffix_errors; 262 + ] ); 263 + ( "registrable_domain", 264 + [ 265 + test_case "ICANN basic" `Quick test_reg_icann_basic; 266 + test_case "second-level suffix" `Quick test_reg_second_level; 267 + test_case "wildcard rules" `Quick test_reg_wildcard; 268 + test_case "exception rules" `Quick test_reg_exception; 269 + test_case "com is suffix" `Quick test_reg_com_is_suffix; 270 + test_case "trailing dot" `Quick test_reg_trailing_dot; 271 + test_case "error cases" `Quick test_reg_errors; 272 + test_case "IDN / punycode" `Quick test_reg_idn; 273 + test_case "mixed case" `Quick test_reg_mixed_case; 274 + ] ); 275 + ( "is_public_suffix", 276 + [ 277 + test_case "basic checks" `Quick test_is_suffix; 278 + test_case "error cases" `Quick test_is_suffix_errors; 279 + ] ); 280 + ( "is_registrable_domain", 281 + [ test_case "basic checks" `Quick test_is_registrable ] ); 282 + ( "section", 283 + [ 284 + test_case "suffix section" `Quick test_section_suffix; 285 + test_case "registrable section" `Quick test_section_registrable; 286 + ] ); 287 + ( "statistics", 288 + [ 289 + test_case "rule counts" `Quick test_rule_counts; 290 + test_case "version info" `Quick test_version_info; 291 + ] ); 292 + ] 293 + 294 + let () = run "Publicsuffix" suite