Git object storage and pack files for Eio
at main 185 lines 6.8 kB view raw
1(** Fuzz tests for Git config parsing. *) 2 3open Alcobar 4 5let truncate ?(max_len = 4096) buf = 6 if String.length buf > max_len then String.sub buf 0 max_len else buf 7 8(** Parse - must not crash on arbitrary input. *) 9let test_parse_crash_safety buf = 10 let buf = truncate buf in 11 let _ = Git.Config.of_string buf in 12 () 13 14(** Roundtrip - parsed config must serialize and re-parse. *) 15let test_roundtrip buf = 16 let buf = truncate buf in 17 let config = Git.Config.of_string buf in 18 let serialized = Git.Config.to_string config in 19 let reparsed = Git.Config.of_string serialized in 20 let orig_sections = Git.Config.all_sections config in 21 let reparsed_sections = Git.Config.all_sections reparsed in 22 if List.length orig_sections <> List.length reparsed_sections then 23 fail "section count mismatch after roundtrip" 24 25(** Set/get consistency - value must be retrievable after set. *) 26let test_set_get sec_buf key_buf value_buf = 27 (* Sanitize section name to be valid *) 28 let sec_name = truncate ~max_len:32 sec_buf in 29 let sec_name = 30 String.map 31 (fun c -> 32 if (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') then c else 'x') 33 sec_name 34 in 35 let sec_name = if String.length sec_name = 0 then "core" else sec_name in 36 (* Sanitize key name *) 37 let key = truncate ~max_len:32 key_buf in 38 let key = 39 String.map 40 (fun c -> 41 if 42 (c >= 'a' && c <= 'z') 43 || (c >= 'A' && c <= 'Z') 44 || (c >= '0' && c <= '9') 45 then c 46 else 'x') 47 key 48 in 49 let key = if String.length key = 0 then "key" else key in 50 (* Sanitize value (no newlines) *) 51 let value = truncate ~max_len:256 value_buf in 52 let value = String.map (fun c -> if c = '\n' then ' ' else c) value in 53 let section = Git.Config.section sec_name in 54 let config = Git.Config.set Git.Config.empty ~section ~key ~value in 55 match Git.Config.find config section key with 56 | None -> fail "key not found after set" 57 | Some v -> 58 let v = String.trim v in 59 let value = String.trim value in 60 if v <> value then failf "value mismatch: got '%s', expected '%s'" v value 61 62(** Unset - value must not be retrievable after unset. *) 63let test_unset sec_buf key_buf = 64 let sec_name = truncate ~max_len:32 sec_buf in 65 let sec_name = 66 String.map 67 (fun c -> 68 if (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') then c else 'x') 69 sec_name 70 in 71 let sec_name = if String.length sec_name = 0 then "core" else sec_name in 72 let key = truncate ~max_len:32 key_buf in 73 let key = String.map (fun c -> if c >= 'a' && c <= 'z' then c else 'x') key in 74 let key = if String.length key = 0 then "key" else key in 75 let section = Git.Config.section sec_name in 76 let config = Git.Config.set Git.Config.empty ~section ~key ~value:"test" in 77 let config = Git.Config.unset config ~section ~key in 78 if Option.is_some (Git.Config.find config section key) then 79 fail "key still present after unset" 80 81(** Boolean parsing - true/false/yes/no/on/off/1/0 all work. *) 82let test_bool_values n = 83 let values = [| "true"; "false"; "yes"; "no"; "on"; "off"; "1"; "0" |] in 84 let expected = [| true; false; true; false; true; false; true; false |] in 85 let idx = n mod 8 in 86 let value = values.(idx) in 87 let section = Git.Config.section "test" in 88 let config = Git.Config.set Git.Config.empty ~section ~key:"flag" ~value in 89 match Git.Config.bool config section "flag" with 90 | None -> fail "failed to parse boolean" 91 | Some parsed -> 92 if parsed <> expected.(idx) then 93 failf "boolean mismatch for '%s': got %b, expected %b" value parsed 94 expected.(idx) 95 96(** Integer parsing - valid integers parse correctly. *) 97let test_int_values n = 98 let section = Git.Config.section "test" in 99 let value = string_of_int n in 100 let config = Git.Config.set Git.Config.empty ~section ~key:"count" ~value in 101 match Git.Config.int config section "count" with 102 | None -> fail "failed to parse integer" 103 | Some parsed -> 104 if parsed <> n then failf "integer mismatch: got %d, expected %d" parsed n 105 106(** Add supports multi-valued keys. *) 107let test_multivalue n = 108 let section = Git.Config.section "test" in 109 let count = (n mod 5) + 1 in 110 let config = 111 List.fold_left 112 (fun cfg i -> 113 Git.Config.add cfg ~section ~key:"item" ~value:(string_of_int i)) 114 Git.Config.empty (List.init count Fun.id) 115 in 116 let all = Git.Config.all config section "item" in 117 if List.length all <> count then 118 failf "multivalue count mismatch: got %d, expected %d" (List.length all) 119 count 120 121(** Section with subsection. *) 122let test_subsection name_buf sub_buf = 123 let name = truncate ~max_len:32 name_buf in 124 let name = 125 String.map (fun c -> if c >= 'a' && c <= 'z' then c else 'x') name 126 in 127 let name = if String.length name = 0 then "remote" else name in 128 let sub = truncate ~max_len:32 sub_buf in 129 let sub = 130 String.map (fun c -> if c = '"' || c = '\\' || c = '\n' then '_' else c) sub 131 in 132 let sub = if String.length sub = 0 then "origin" else sub in 133 let section = Git.Config.section_sub name sub in 134 let config = 135 Git.Config.set Git.Config.empty ~section ~key:"url" 136 ~value:"https://example.com" 137 in 138 match Git.Config.find config section "url" with 139 | None -> fail "key not found in subsection" 140 | Some v -> 141 if v <> "https://example.com" then fail "value mismatch in subsection" 142 143(** Get remotes from config. *) 144let test_get_remotes () = 145 let config_content = 146 {|[remote "origin"] 147 url = https://github.com/user/repo.git 148[remote "upstream"] 149 url = https://github.com/other/repo.git 150|} 151 in 152 let config = Git.Config.of_string config_content in 153 let remotes = Git.Config.remotes config in 154 if List.length remotes <> 2 then 155 failf "expected 2 remotes, got %d" (List.length remotes) 156 157(** Get branches from config. *) 158let test_get_branches () = 159 let config_content = 160 {|[branch "main"] 161 remote = origin 162 merge = refs/heads/main 163[branch "feature"] 164 remote = upstream 165|} 166 in 167 let config = Git.Config.of_string config_content in 168 let branches = Git.Config.branches config in 169 if List.length branches <> 2 then 170 failf "expected 2 branches, got %d" (List.length branches) 171 172let suite = 173 ( "config", 174 [ 175 test_case "parse crash safety" [ bytes ] test_parse_crash_safety; 176 test_case "roundtrip" [ bytes ] test_roundtrip; 177 test_case "set/get" [ bytes; bytes; bytes ] test_set_get; 178 test_case "unset" [ bytes; bytes ] test_unset; 179 test_case "bool values" [ uint8 ] test_bool_values; 180 test_case "int values" [ int ] test_int_values; 181 test_case "multivalue" [ uint8 ] test_multivalue; 182 test_case "subsection" [ bytes; bytes ] test_subsection; 183 test_case "get_remotes" [ const () ] test_get_remotes; 184 test_case "get_branches" [ const () ] test_get_branches; 185 ] )