mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2026-03-07 21:27:55 -05:00
391 lines
8.8 KiB
OCaml
391 lines
8.8 KiB
OCaml
open CCResult
|
|
module T = (val Containers_testlib.make ~__FILE__ ())
|
|
include T;;
|
|
|
|
t @@ fun () -> Error "ohno 42" = fail_printf "ohno %d" 42;;
|
|
t @@ fun () -> Error "ohno 42" = fail_fprintf "ohno %d" 42;;
|
|
|
|
eq (Error "error\ncontext:message(number 42, foo: true)")
|
|
(add_ctxf "message(number %d, foo: %B)" 42 true (Error "error"))
|
|
;;
|
|
|
|
t @@ fun () ->
|
|
let called_with = ref None in
|
|
let f e = called_with := Some e in
|
|
iter_err f (Ok 1);
|
|
assert (!called_with = None);
|
|
iter_err f (Error 1);
|
|
assert (!called_with = Some 1);
|
|
true
|
|
;;
|
|
|
|
t @@ fun () -> get_or_failwith (Ok 1) = 1;;
|
|
|
|
t @@ fun () ->
|
|
try
|
|
ignore @@ get_or_failwith (Error "e");
|
|
false
|
|
with Failure msg -> msg = "e"
|
|
;;
|
|
|
|
eq (get_lazy (fun _ -> 2) (Ok 1)) 1;;
|
|
eq (get_lazy (fun _ -> 2) (Error "error")) 2;;
|
|
eq 42 (fold_ok ( + ) 2 (Ok 40));;
|
|
eq 40 (fold_ok ( + ) 40 (Error "foo"));;
|
|
eq (Ok []) (flatten_l []);;
|
|
eq (Ok [ 1; 2; 3 ]) (flatten_l [ Ok 1; Ok 2; Ok 3 ]);;
|
|
eq (Error "ohno") (flatten_l [ Ok 1; Error "ohno"; Ok 2; Ok 3; Error "wut" ]);;
|
|
|
|
(* Additional comprehensive tests for CCResult *)
|
|
|
|
(* Test return and fail *)
|
|
eq (Ok 42) (return 42);;
|
|
eq (Error "failed") (fail "failed");;
|
|
|
|
(* Test of_exn and of_exn_trace *)
|
|
t @@ fun () ->
|
|
match of_exn (Failure "test") with
|
|
| Error msg -> String.length msg > 0
|
|
| Ok _ -> false
|
|
;;
|
|
|
|
t @@ fun () ->
|
|
match of_exn_trace (Failure "test") with
|
|
| Error msg -> String.length msg > 0
|
|
| Ok _ -> false
|
|
;;
|
|
|
|
(* Test opt_map *)
|
|
eq (Ok (Some 6)) (opt_map (fun x -> Ok (x * 2)) (Some 3));;
|
|
eq (Ok None) (opt_map (fun x -> Ok (x * 2)) None);;
|
|
eq (Error "err") (opt_map (fun _ -> Error "err") (Some 3));;
|
|
|
|
(* Test map *)
|
|
eq (Ok 3) (map (( + ) 1) (Ok 2));;
|
|
eq (Error "e") (map (( + ) 1) (Error "e"));;
|
|
t @@ fun () -> map String.uppercase_ascii (Ok "hello") = Ok "HELLO";;
|
|
|
|
(* Test map_err *)
|
|
eq (Ok 5) (map_err String.uppercase_ascii (Ok 5));;
|
|
eq (Error "ERROR") (map_err String.uppercase_ascii (Error "error"));;
|
|
|
|
(* Test map2 *)
|
|
eq (Ok "HELLO")
|
|
(map2 String.uppercase_ascii String.uppercase_ascii (Ok "hello"))
|
|
;;
|
|
|
|
eq (Error "ERROR")
|
|
(map2 String.uppercase_ascii String.uppercase_ascii (Error "error"))
|
|
;;
|
|
|
|
(* Test iter *)
|
|
t @@ fun () ->
|
|
let r = ref 0 in
|
|
iter (fun x -> r := x) (Ok 42);
|
|
!r = 42
|
|
;;
|
|
|
|
t @@ fun () ->
|
|
let r = ref 0 in
|
|
iter (fun x -> r := x) (Error "e");
|
|
!r = 0
|
|
;;
|
|
|
|
(* Test get_exn *)
|
|
eq 42 (get_exn (Ok 42));;
|
|
|
|
t @@ fun () ->
|
|
try
|
|
ignore (get_exn (Error "error"));
|
|
false
|
|
with Invalid_argument _ -> true
|
|
;;
|
|
|
|
(* Test get_or *)
|
|
eq 5 (get_or (Ok 5) ~default:0);;
|
|
eq 0 (get_or (Error "e") ~default:0);;
|
|
|
|
(* Test apply_or *)
|
|
eq 10 (apply_or (fun x -> Ok (x * 2)) 5);;
|
|
|
|
t @@ fun () ->
|
|
apply_or
|
|
(fun x ->
|
|
if x > 0 then
|
|
Ok (x * 2)
|
|
else
|
|
Error "neg")
|
|
5
|
|
= 10
|
|
;;
|
|
|
|
t @@ fun () ->
|
|
apply_or
|
|
(fun x ->
|
|
if x > 0 then
|
|
Ok (x * 2)
|
|
else
|
|
Error "neg")
|
|
(-5)
|
|
= -5
|
|
;;
|
|
|
|
(* Test map_or *)
|
|
eq 10 (map_or (fun x -> x * 2) (Ok 5) ~default:0);;
|
|
eq 0 (map_or (fun x -> x * 2) (Error "e") ~default:0);;
|
|
|
|
(* Test catch *)
|
|
eq 5 (catch (Ok 5) ~ok:CCFun.id ~err:(fun _ -> 0));;
|
|
eq 0 (catch (Error "e") ~ok:CCFun.id ~err:(fun _ -> 0));;
|
|
|
|
eq "ERROR: e"
|
|
(catch (Error "e") ~ok:Int.to_string ~err:(fun e -> "ERROR: " ^ e))
|
|
;;
|
|
|
|
(* Test flat_map *)
|
|
eq (Ok 3) (flat_map (fun x -> Ok (x + 1)) (Ok 2));;
|
|
eq (Error "e") (flat_map (fun x -> Ok (x + 1)) (Error "e"));;
|
|
eq (Error "e2") (flat_map (fun _ -> Error "e2") (Ok 2));;
|
|
|
|
(* Test fold *)
|
|
eq 10 (fold ~ok:CCFun.id ~error:(fun _ -> 0) (Ok 10));;
|
|
eq 0 (fold ~ok:CCFun.id ~error:(fun _ -> 0) (Error "e"));;
|
|
|
|
(* Test is_ok and is_error *)
|
|
t @@ fun () -> is_ok (Ok 1);;
|
|
t @@ fun () -> not (is_ok (Error "e"));;
|
|
t @@ fun () -> is_error (Error "e");;
|
|
t @@ fun () -> not (is_error (Ok 1));;
|
|
|
|
(* Test guard and guard_str *)
|
|
t @@ fun () ->
|
|
match guard (fun () -> 42) with
|
|
| Ok 42 -> true
|
|
| _ -> false
|
|
;;
|
|
|
|
t @@ fun () ->
|
|
match guard (fun () -> failwith "error") with
|
|
| Error _ -> true
|
|
| _ -> false
|
|
;;
|
|
|
|
t @@ fun () ->
|
|
match guard_str (fun () -> 42) with
|
|
| Ok 42 -> true
|
|
| _ -> false
|
|
;;
|
|
|
|
t @@ fun () ->
|
|
match guard_str (fun () -> failwith "test error") with
|
|
| Error msg -> String.length msg > 0
|
|
| _ -> false
|
|
;;
|
|
|
|
(* Test guard_str_trace *)
|
|
t @@ fun () ->
|
|
match guard_str_trace (fun () -> 42) with
|
|
| Ok 42 -> true
|
|
| _ -> false
|
|
;;
|
|
|
|
t @@ fun () ->
|
|
match guard_str_trace (fun () -> failwith "test error") with
|
|
| Error msg -> String.length msg > 0
|
|
| _ -> false
|
|
;;
|
|
|
|
(* Test wrap functions *)
|
|
eq (Ok 6) (wrap1 (( + ) 1) 5);;
|
|
|
|
t @@ fun () ->
|
|
match wrap1 (fun _ -> failwith "error") () with
|
|
| Error _ -> true
|
|
| _ -> false
|
|
;;
|
|
|
|
eq (Ok 7) (wrap2 ( + ) 3 4);;
|
|
|
|
t @@ fun () ->
|
|
match wrap2 (fun _ _ -> failwith "error") 1 2 with
|
|
| Error _ -> true
|
|
| _ -> false
|
|
;;
|
|
|
|
eq (Ok 10) (wrap3 (fun a b c -> a + b + c) 2 3 5);;
|
|
|
|
t @@ fun () ->
|
|
match wrap3 (fun _ _ _ -> failwith "error") 1 2 3 with
|
|
| Error _ -> true
|
|
| _ -> false
|
|
;;
|
|
|
|
(* Test pure *)
|
|
eq (Ok 42) (pure 42);;
|
|
|
|
(* Test join *)
|
|
eq (Ok 5) (join (Ok (Ok 5)));;
|
|
eq (Error "e") (join (Ok (Error "e")));;
|
|
eq (Error "e") (join (Error "e"));;
|
|
|
|
(* Test both *)
|
|
eq (Ok (3, 5)) (both (Ok 3) (Ok 5));;
|
|
eq (Error "e1") (both (Error "e1") (Ok 5));;
|
|
eq (Error "e2") (both (Ok 3) (Error "e2"));;
|
|
eq (Error "e1") (both (Error "e1") (Error "e2"));;
|
|
|
|
(* Test map_l *)
|
|
eq (Ok [ 2; 3; 4 ]) (map_l (fun x -> Ok (x + 1)) [ 1; 2; 3 ]);;
|
|
|
|
eq (Error "e")
|
|
(map_l
|
|
(fun x ->
|
|
if x > 0 then
|
|
Ok x
|
|
else
|
|
Error "e")
|
|
[ 1; -1; 2 ])
|
|
;;
|
|
|
|
eq (Ok []) (map_l (fun x -> Ok x) []);;
|
|
|
|
(* Test fold_l *)
|
|
eq (Ok 6) (fold_l (fun acc x -> Ok (acc + x)) 0 [ 1; 2; 3 ]);;
|
|
|
|
eq (Error "e")
|
|
(fold_l
|
|
(fun _ x ->
|
|
if x > 0 then
|
|
Ok x
|
|
else
|
|
Error "e")
|
|
0 [ 1; -1; 2 ])
|
|
;;
|
|
|
|
(* Test choose *)
|
|
eq (Ok 1) (choose [ Ok 1; Ok 2; Ok 3 ]);;
|
|
eq (Ok 2) (choose [ Error "e1"; Ok 2; Ok 3 ]);;
|
|
eq (Ok 3) (choose [ Error "e1"; Error "e2"; Ok 3 ]);;
|
|
eq (Error [ "e1"; "e2"; "e3" ]) (choose [ Error "e1"; Error "e2"; Error "e3" ])
|
|
;;
|
|
eq (Error []) (choose []);;
|
|
|
|
(* Test retry *)
|
|
t @@ fun () ->
|
|
let attempts = ref 0 in
|
|
let f () =
|
|
incr attempts;
|
|
if !attempts < 3 then
|
|
Error "fail"
|
|
else
|
|
Ok "success"
|
|
in
|
|
match retry 5 f with
|
|
| Ok "success" -> !attempts = 3
|
|
| _ -> false
|
|
;;
|
|
|
|
t @@ fun () ->
|
|
let attempts = ref 0 in
|
|
let f () =
|
|
incr attempts;
|
|
Error "always fails"
|
|
in
|
|
match retry 3 f with
|
|
| Error errs -> !attempts = 3 && List.length errs = 3
|
|
| _ -> false
|
|
;;
|
|
|
|
(* Test to_opt *)
|
|
eq (Some 5) (to_opt (Ok 5));;
|
|
eq None (to_opt (Error "e"));;
|
|
|
|
(* Test of_opt *)
|
|
eq (Ok 5) (of_opt (Some 5));;
|
|
eq (Error "option is None") (of_opt None);;
|
|
|
|
(* Test equal *)
|
|
t @@ fun () -> equal ~err:String.equal Int.equal (Ok 5) (Ok 5);;
|
|
t @@ fun () -> not (equal ~err:String.equal Int.equal (Ok 5) (Ok 6));;
|
|
t @@ fun () -> equal ~err:String.equal Int.equal (Error "e") (Error "e");;
|
|
t @@ fun () -> not (equal ~err:String.equal Int.equal (Error "e1") (Error "e2"))
|
|
;;
|
|
t @@ fun () -> not (equal ~err:String.equal Int.equal (Ok 5) (Error "e"));;
|
|
|
|
(* Test compare *)
|
|
t @@ fun () -> compare ~err:String.compare Int.compare (Ok 5) (Ok 5) = 0;;
|
|
t @@ fun () -> compare ~err:String.compare Int.compare (Ok 5) (Ok 6) < 0;;
|
|
t @@ fun () -> compare ~err:String.compare Int.compare (Ok 6) (Ok 5) > 0;;
|
|
|
|
t @@ fun () ->
|
|
compare ~err:String.compare Int.compare (Error "a") (Error "a") = 0
|
|
;;
|
|
|
|
t @@ fun () ->
|
|
compare ~err:String.compare Int.compare (Error "a") (Error "b") < 0
|
|
;;
|
|
|
|
t @@ fun () -> compare ~err:String.compare Int.compare (Error "a") (Ok 5) < 0;;
|
|
t @@ fun () -> compare ~err:String.compare Int.compare (Ok 5) (Error "a") > 0;;
|
|
|
|
(* Property-based tests *)
|
|
q Q.int (fun x -> return x = Ok x);;
|
|
q Q.(result int string) (fun r -> is_ok r = not (is_error r));;
|
|
q Q.(result int string) (fun r -> map CCFun.id r = r);;
|
|
q Q.(result int string) (fun r -> map_err CCFun.id r = r);;
|
|
q Q.(result int string) (fun r -> flat_map return r = r);;
|
|
q Q.(result int string) (fun r -> equal ~err:String.equal Int.equal r r);;
|
|
|
|
q
|
|
Q.(result int string)
|
|
(fun r -> compare ~err:String.compare Int.compare r r = 0)
|
|
;;
|
|
|
|
q
|
|
Q.(result int string)
|
|
(fun r ->
|
|
of_opt (to_opt r)
|
|
=
|
|
match r with
|
|
| Ok x -> Ok x
|
|
| Error _ -> Error "option is None")
|
|
;;
|
|
|
|
q Q.int (fun x -> to_opt (Ok x) = Some x);;
|
|
q Q.string (fun e -> to_opt (Error e) = None);;
|
|
|
|
q
|
|
Q.(pair (result int string) int)
|
|
(fun (r, default) ->
|
|
let v = get_or r ~default in
|
|
match r with
|
|
| Ok x -> v = x
|
|
| Error _ -> v = default)
|
|
;;
|
|
|
|
q
|
|
Q.(list (result int string))
|
|
(fun l ->
|
|
match flatten_l l with
|
|
| Ok values ->
|
|
List.for_all
|
|
(function
|
|
| Ok _ -> true
|
|
| Error _ -> false)
|
|
l
|
|
&& List.length values <= List.length l
|
|
| Error _ ->
|
|
List.exists
|
|
(function
|
|
| Error _ -> true
|
|
| Ok _ -> false)
|
|
l)
|
|
;;
|
|
|
|
(* Additional focused tests for high-value functions *)
|
|
t @@ fun () -> map (( + ) 1) (Ok 2) = Ok 3;;
|
|
t @@ fun () -> is_ok (Ok 1) && not (is_ok (Error "e"));;
|
|
t @@ fun () -> to_opt (Ok 5) = Some 5 && to_opt (Error "e") = None;;
|
|
t @@ fun () -> both (Ok 3) (Ok 5) = Ok (3, 5);;
|
|
q Q.int (fun x -> return x = Ok x);;
|
|
q Q.int (fun x -> to_opt (Ok x) = Some x)
|