package tezt
Test framework for unit tests, integration tests, and regression tests
Install
Dune Dependency
Authors
Maintainers
Sources
tezt-4.1.0.tar.bz2
md5=88c2d9d3da75ff554599bc34cbf5acbe
sha512=e60294514ecc4a989ce663ebb306e56f654dcfaffb7dbe5e3f05f5a13c9c2ff64dadde4a77b0d9a8567a76a6a7a2b25e0940ccd2a956ffcb85ff9300bfebe3bc
doc/src/tezt.json/JSON.ml.html
Source file JSON.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
(*****************************************************************************) (* *) (* Open Source License *) (* Copyright (c) 2020-2022 Nomadic Labs <contact@nomadic-labs.com> *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) (* to deal in the Software without restriction, including without limitation *) (* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) (* and/or sell copies of the Software, and to permit persons to whom the *) (* Software is furnished to do so, subject to the following conditions: *) (* *) (* The above copyright notice and this permission notice shall be included *) (* in all copies or substantial portions of the Software. *) (* *) (* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) (* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) (* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) (* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) (* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) (* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) (* DEALINGS IN THE SOFTWARE. *) (* *) (*****************************************************************************) (* String representations of JSON values, annotated with their origin. Used to store raw JSON in errors, for display purposes. Field [raw_origin] is the [~origin] argument of [parse], and field [raw_string] is the string representation. *) type raw = {raw_origin : string; raw_string : string} (* [raw] is only present for values that cannot be found easily elsewhere. [origin] is the string representation of the value of type [origin]. It is more precise than [raw.raw_origin]. [message] is the error message. *) type error = {raw : raw option; origin : string; message : string} let show_error {raw; origin; message} = match raw with | None -> Printf.sprintf "%s: %s" origin message | Some {raw_origin; raw_string} -> Printf.sprintf "%s = %s\n%s: %s" raw_origin raw_string origin message exception Error of error let () = Printexc.register_printer @@ function | Error error -> Some (show_error error) | _ -> None type u = Ezjsonm.value (* Each [JSON.t] comes with its origin so that we can print nice error messages. - [Origin] denotes the original JSON value. Field [name] describes where it comes from, for instance ["RPC response"]. Field [json] is the full original JSON value. - [Field] denotes a field taken a JSON object. This JSON object itself originates from [origin], and the field name is [name]. - [Item] denotes an item taken a JSON array. This JSON array itself originates from [origin], and the item index is [index]. - [Error] denotes a field or an item taken from [origin] but which does not exist. The exact reason why it does not exist is [message]. *) type origin = | Origin of {name : string; json : u} | Field of {origin : origin; name : string} | Item of {origin : origin; index : int} | Error of {origin : origin; message : string} type t = {origin : origin; node : u} let encode_u = Ezjsonm.value_to_string ~minify:false let encode {node; _} = Ezjsonm.value_to_string ~minify:false node let encode_to_file_u filename json = let ch = open_out filename in try Ezjsonm.value_to_channel ~minify:false ch json ; close_out ch with exn -> close_out ch ; raise exn let encode_to_file filename json = encode_to_file_u filename json.node let annotate ~origin node = {origin = Origin {name = origin; json = node}; node} let unannotate {node; _} = node let fail_string origin message = let rec gather_origin message fields = function | Origin {name; json} -> let origin = match fields with | [] -> name | _ :: _ -> name ^ ", at " ^ String.concat "" fields in raise (Error { raw = Some {raw_origin = name; raw_string = encode_u json}; origin; message; }) | Field {origin; name} -> gather_origin message (("." ^ name) :: fields) origin | Item {origin; index} -> gather_origin message (("[" ^ string_of_int index ^ "]") :: fields) origin | Error {origin; message} -> gather_origin message [] origin in gather_origin message [] origin let fail origin x = Printf.ksprintf (fail_string origin) x let error {origin; _} = fail origin let with_open_in file read_f = let chan = open_in file in try let value = read_f chan in close_in chan ; value with x -> close_in chan ; raise x let parse_file file = let node = try with_open_in file Ezjsonm.from_channel with | Ezjsonm.Parse_error (_, message) -> (* In the error, [raw] is [None] because the raw value can be found easily in the file. *) raise (Error {raw = None; origin = file; message = "invalid JSON: " ^ message}) | Sys_error message -> raise (Error { raw = None; origin = file; message = "failed to read file: " ^ message; }) in annotate ~origin:file node let parse ~origin raw = let node = try Ezjsonm.value_from_string raw with Ezjsonm.Parse_error (_, message) -> raise (Error { raw = Some {raw_origin = origin; raw_string = raw}; origin; message = "invalid JSON: " ^ message; }) in annotate ~origin node let parse_opt ~origin raw = match Ezjsonm.from_string raw with | exception Ezjsonm.Parse_error _ -> None | node -> Some {origin = Origin {name = origin; json = node}; node} let null_because_error origin message = let origin = match origin with | Error _ -> origin | Origin _ | Field _ | Item _ -> Error {origin; message} in {origin; node = `Null} let get name {origin; node} = match node with | `O fields -> ( match List.assoc_opt name fields with | None -> null_because_error origin ("missing field: " ^ name) | Some node -> {origin = Field {origin; name}; node}) | _ -> null_because_error origin "not an object" let ( |-> ) json name = get name json let geti index {origin; node} = match node with | `A items -> ( match List.nth_opt items index with | None -> null_because_error origin ("missing item: " ^ string_of_int index) | Some node -> {origin = Item {origin; index}; node}) | _ -> null_because_error origin "not an array" let ( |=> ) json index = geti index json let check as_opt error_message json = match as_opt json with | None -> fail json.origin error_message | Some value -> value let test as_opt json = match as_opt json with None -> false | Some _ -> true let is_null {node; _} = match node with `Null -> true | _ -> false let as_opt json = match json.node with `Null -> None | _ -> Some json let as_bool_opt json = match json.node with `Bool b -> Some b | _ -> None let as_bool = check as_bool_opt "expected a boolean" let is_bool = test as_bool_opt let as_int_opt json = match json.node with | `Float f -> if Float.is_integer f then Some (Float.to_int f) else None | `String s -> int_of_string_opt s | _ -> None let as_int = check as_int_opt "expected an integer" let is_int = test as_int_opt let as_int64_opt json = match json.node with | `Float f -> if Float.is_integer f then Some (Int64.of_float f) else None | `String s -> Int64.of_string_opt s | _ -> None let as_int64 = check as_int64_opt "expected a 64-bit integer" let is_int64 = test as_int64_opt let as_int32_opt json = match json.node with | `Float f -> if Float.is_integer f then Some (Int32.of_float f) else None | `String s -> Int32.of_string_opt s | _ -> None let as_int32 = check as_int32_opt "expected a 32-bit integer" let is_int32 = test as_int32_opt let as_float_opt json = match json.node with | `Float f -> Some f | `String s -> float_of_string_opt s | _ -> None let as_float = check as_float_opt "expected a number" let is_float = test as_float_opt let as_string_opt json = match json.node with `String s -> Some s | _ -> None let as_string = check as_string_opt "expected a string" let is_string = test as_string_opt let as_list_opt json = match json.node with | `Null -> Some [] | `A l -> Some (List.mapi (fun index node -> {origin = Item {origin = json.origin; index}; node}) l) | _ -> None let as_list = check as_list_opt "expected an array" let is_list = test as_list_opt let as_object_opt json = match json.node with | `Null -> Some [] | `O l -> Some (List.map (fun (name, node) -> (name, {origin = Field {origin = json.origin; name}; node})) l) | _ -> None let as_object = check as_object_opt "expected an object" let is_object = test as_object_opt let rec assoc_put_or_replace ~key ~value = function | [] -> [(key, value)] | (k, _) :: assoc when k = key -> (key, value) :: assoc | (k, v) :: assoc -> (k, v) :: assoc_put_or_replace ~key ~value assoc let put (key, value) json = let new_fields = as_object json |> assoc_put_or_replace ~key ~value |> List.map (fun (k, v) -> (k, v.node)) in {json with node = `O new_fields} let update key f json = let v = json |-> key in put (key, f v) json let merge_objects obj1 obj2 = List.fold_left (fun obj1' (key, value) -> put (key, value) obj1') obj1 (as_object obj2) let filter_map_object json f = let new_fields = List.filter_map (fun (key, value) -> match f key value with | Some {node; _} -> Some (key, node) | None -> None) (as_object json) in {json with node = `O new_fields} let filter_object json f = filter_map_object json (fun key value -> if f key value then Some value else None) let rec equal_u (a : u) (b : u) = match (a, b) with | `O object_a, `O object_b -> let sort_object = List.sort (fun (key_a, _) (key_b, _) -> compare key_a key_b) in List.equal (fun (k, v) (k', v') -> String.equal k k' && equal_u v v') (sort_object object_a) (sort_object object_b) | `Bool b, `Bool b' -> Bool.equal b b' | `Float f, `Float f' -> Float.equal f f' | `A ls, `A ls' -> List.equal equal_u ls ls' | `Null, `Null -> true | `String s, `String s' -> String.equal s s' | `O _, _ | `Bool _, _ | `Float _, _ | `A _, _ | `Null, _ | `String _, _ -> false let equal j j' = equal_u (unannotate j) (unannotate j')
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>