mte/unikernel/duniverse/base/test/test_map.ml
2025-11-11 02:07:51 +01:00

434 lines
12 KiB
OCaml

open! Import
open! Map
let%expect_test "Finished_or_unfinished <-> Continue_or_stop" =
(* These functions are implemented using [Caml.Obj.magic]. It is important to test them
comprehensively. *)
List.iter2_exn Continue_or_stop.all Finished_or_unfinished.all ~f:(fun c_or_s f_or_u ->
print_s [%sexp (c_or_s : Continue_or_stop.t), (f_or_u : Finished_or_unfinished.t)];
require_equal
[%here]
(module Continue_or_stop)
c_or_s
(Finished_or_unfinished.to_continue_or_stop f_or_u);
require_equal
[%here]
(module Finished_or_unfinished)
f_or_u
(Finished_or_unfinished.of_continue_or_stop c_or_s));
[%expect {|
(Continue Finished)
(Stop Unfinished)
|}]
;;
let%test _ =
invariants (of_increasing_iterator_unchecked (module Int) ~len:20 ~f:(fun x -> x, x))
;;
let%test _ = invariants (Poly.of_increasing_iterator_unchecked ~len:20 ~f:(fun x -> x, x))
let add12 t = add_exn t ~key:1 ~data:2
type int_map = int Map.M(Int).t [@@deriving compare, hash, sexp]
let%expect_test "[add_exn] success" =
print_s [%sexp (add12 (empty (module Int)) : int_map)];
[%expect {| ((1 2)) |}]
;;
let%expect_test "[add_exn] failure" =
show_raise (fun () -> add12 (add12 (empty (module Int))));
[%expect {| (raised ("[Map.add_exn] got key already present" (key 1))) |}]
;;
let%expect_test "[add] success" =
print_s [%sexp (add (empty (module Int)) ~key:1 ~data:2 : int_map Or_duplicate.t)];
[%expect {| (Ok ((1 2))) |}]
;;
let%expect_test "[add] duplicate" =
print_s
[%sexp (add (add12 (empty (module Int))) ~key:1 ~data:2 : int_map Or_duplicate.t)];
[%expect {| Duplicate |}]
;;
let%expect_test "[Map.of_alist_multi] preserves value ordering" =
print_s
[%sexp
(Map.of_alist_multi (module String) [ "a", 1; "a", 2; "b", 1; "b", 3 ]
: int list Map.M(String).t)];
[%expect {|
((a (1 2))
(b (1 3)))
|}]
;;
let%expect_test "find_exn" =
let map = Map.of_alist_exn (module String) [ "one", 1; "two", 2; "three", 3 ] in
let test_success key =
require_does_not_raise [%here] (fun () ->
print_s [%sexp (Map.find_exn map key : int)])
in
test_success "one";
[%expect {| 1 |}];
test_success "two";
[%expect {| 2 |}];
test_success "three";
[%expect {| 3 |}];
let test_failure key = require_does_raise [%here] (fun () -> Map.find_exn map key) in
test_failure "zero";
[%expect {| (Not_found_s ("Map.find_exn: not found" zero)) |}];
test_failure "four";
[%expect {| (Not_found_s ("Map.find_exn: not found" four)) |}]
;;
let%expect_test "[t_of_sexp] error on duplicate" =
let sexp = Sexplib.Sexp.of_string "((0 a)(1 b)(2 c)(1 d))" in
(match [%of_sexp: string Map.M(String).t] sexp with
| t -> print_cr [%here] [%message "did not raise" (t : string Map.M(String).t)]
| exception (Sexp.Of_sexp_error _ as exn) -> print_s (sexp_of_exn exn)
| exception exn -> print_cr [%here] [%message "wrong kind of exception" (exn : exn)]);
[%expect {| (Of_sexp_error "Map.t_of_sexp_direct: duplicate key" (invalid_sexp 1)) |}]
;;
let%expect_test "combine_errors" =
let test list =
let input =
list
|> List.map ~f:(Result.map_error ~f:Error.of_string)
|> List.mapi ~f:(fun k x -> Int.succ k, x)
|> Map.of_alist_exn (module Int)
in
let output = Map.combine_errors input in
print_s [%sexp (output : string Map.M(Int).t Or_error.t)]
in
(* empty *)
test [];
[%expect {| (Ok ()) |}];
(* singletons *)
test [ Ok "one" ];
test [ Error "one" ];
[%expect {|
(Ok ((1 one)))
(Error ((1 one)))
|}];
(* multiple ok *)
test [ Ok "one"; Ok "two"; Ok "three" ];
[%expect {|
(Ok (
(1 one)
(2 two)
(3 three)))
|}];
(* multiple errors *)
test [ Error "one"; Error "two"; Error "three" ];
[%expect {|
(Error (
(1 one)
(2 two)
(3 three)))
|}];
(* one error among oks *)
test [ Error "one"; Ok "two"; Ok "three" ];
test [ Ok "one"; Error "two"; Ok "three" ];
test [ Ok "one"; Ok "two"; Error "three" ];
[%expect {|
(Error ((1 one)))
(Error ((2 two)))
(Error ((3 three)))
|}];
(* one ok among errors *)
test [ Ok "one"; Error "two"; Error "three" ];
test [ Error "one"; Ok "two"; Error "three" ];
test [ Error "one"; Error "two"; Ok "three" ];
[%expect
{|
(Error (
(2 two)
(3 three)))
(Error (
(1 one)
(3 three)))
(Error (
(1 one)
(2 two)))
|}]
;;
let%test_module "Poly" =
(module struct
let%test _ = length Poly.empty = 0
let%test _ =
let a = Poly.of_alist_exn [] in
Poly.equal Base.Poly.equal a Poly.empty
;;
let%test _ =
let a = Poly.of_alist_exn [ "a", 1 ] in
let b = Poly.of_alist_exn [ 1, "b" ] in
length a = length b
;;
end)
;;
let%test_module "[symmetric_diff]" =
(module struct
let%expect_test "examples" =
let test alist1 alist2 =
Map.symmetric_diff
~data_equal:Int.equal
(Map.of_alist_exn (module String) alist1)
(Map.of_alist_exn (module String) alist2)
|> Sequence.to_list
|> [%sexp_of: (string, int) Symmetric_diff_element.t list]
|> print_s
in
test [] [];
[%expect {| () |}];
test [ "one", 1 ] [];
[%expect {| ((one (Left 1))) |}];
test [] [ "two", 2 ];
[%expect {| ((two (Right 2))) |}];
test [ "one", 1; "two", 2 ] [ "one", 1; "two", 2 ];
[%expect {| () |}];
test [ "one", 1; "two", 2 ] [ "one", 1; "two", 3 ];
[%expect {| ((two (Unequal (2 3)))) |}]
;;
module String_to_int_map = struct
type t = int Map.M(String).t [@@deriving equal, sexp_of]
open Base_quickcheck
let quickcheck_generator =
Generator.map_t_m (module String) Generator.string Generator.int
;;
let quickcheck_observer = Observer.map_t Observer.string Observer.int
let quickcheck_shrinker = Shrinker.map_t Shrinker.string Shrinker.int
end
let apply_diff_left_to_right map (key, elt) =
match elt with
| `Right data | `Unequal (_, data) -> Map.set map ~key ~data
| `Left _ -> Map.remove map key
;;
let apply_diff_right_to_left map (key, elt) =
match elt with
| `Left data | `Unequal (data, _) -> Map.set map ~key ~data
| `Right _ -> Map.remove map key
;;
(* This is a deterministic benchmark rather than a test, measuring the number of
comparisons made by fold_symmetric_diff. *)
let%expect_test "number of key comparisons" =
let count = ref 0 in
let measure_comparisons f =
let c = !count in
f ();
!count - c
in
let module Key = struct
module T = struct
type t = int [@@deriving sexp_of]
let compare x y =
Int.incr count;
compare_int x y
;;
end
include T
include Comparator.Make (T)
end
in
let (_m : unit Map.M(Key).t), map_pairs =
List.fold
(List.init 1000 ~f:Fn.id)
~init:(Map.empty (module Key), [])
~f:(fun (m, acc) i ->
let m' = Map.add_exn m ~key:i ~data:() in
m', (m, m') :: acc)
in
print_s [%sexp (!count : int)];
[%expect {| 9_966 |}];
count := 0;
let diffs = ref 0 in
let counts =
List.map map_pairs ~f:(fun (m, m') ->
measure_comparisons (fun () ->
diffs
:= !diffs
+ Map.fold_symmetric_diff
~init:0
~f:(fun n _ -> n + 1)
~data_equal:(fun () () -> true)
(m : unit Map.M(Key).t)
m'))
in
let worst_counts =
List.sort counts ~compare:[%compare: int] |> List.rev |> fun l -> List.take l 20
in
(* The smaller these numbers are, the better. *)
print_s [%sexp (!diffs : int), (!count : int)];
[%expect {| (1_000 10_955) |}];
print_s [%sexp (worst_counts : int list)];
[%expect {| (12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12) |}]
;;
let%expect_test "reconstructing in both directions" =
let test (map1, map2) =
let diff = Map.symmetric_diff map1 map2 ~data_equal:Int.equal in
require_equal
[%here]
(module String_to_int_map)
(Sequence.fold diff ~init:map1 ~f:apply_diff_left_to_right)
map2;
require_equal
[%here]
(module String_to_int_map)
map1
(Sequence.fold diff ~init:map2 ~f:apply_diff_right_to_left)
in
Base_quickcheck.Test.run_exn
~f:test
(module struct
type t = String_to_int_map.t * String_to_int_map.t
[@@deriving quickcheck, sexp_of]
end)
;;
let%expect_test "vs [fold_symmetric_diff]" =
let test (map1, map2) =
require_compare_equal
[%here]
(module struct
type t = (string, int) Symmetric_diff_element.t list
[@@deriving compare, sexp_of]
end)
(Map.symmetric_diff map1 map2 ~data_equal:Int.equal
|> Sequence.fold ~init:[] ~f:(Fn.flip List.cons))
(Map.fold_symmetric_diff
map1
map2
~data_equal:Int.equal
~init:[]
~f:(Fn.flip List.cons))
in
Base_quickcheck.Test.run_exn
~f:test
(module struct
type t = String_to_int_map.t * String_to_int_map.t
[@@deriving quickcheck, sexp_of]
end)
;;
end)
;;
let%test_module "of_alist_multi key equality" =
(module struct
module Key = struct
module T = struct
type t = string * int [@@deriving sexp_of]
let compare = [%compare: string * _]
end
include T
include Comparator.Make (T)
end
let alist = [ ("a", 1), 1; ("a", 2), 3; ("b", 0), 0; ("a", 3), 2 ]
let%expect_test "of_alist_multi chooses the first key" =
print_s [%sexp (Map.of_alist_multi (module Key) alist : int list Map.M(Key).t)];
[%expect {| (((a 1) (1 3 2)) ((b 0) (0))) |}]
;;
let%test_unit "of_{alist,sequence}_multi have the same behaviour" =
[%test_result: int list Map.M(Key).t]
~expect:(Map.of_alist_multi (module Key) alist)
(Map.of_sequence_multi (module Key) (Sequence.of_list alist))
;;
end)
;;
let%expect_test "remove returns the same object if there's nothing to do" =
let map1 = Map.of_alist_exn (module Int) [ 1, "one"; 3, "three" ] in
let map2 = Map.remove map1 2 in
require [%here] (phys_equal map1 map2)
;;
let%expect_test "[map_keys]" =
let test m c ~f =
print_s
[%sexp
(Map.map_keys c ~f m
: [ `Duplicate_key of string | `Ok of string Map.M(String).t ])]
in
let map = Map.of_alist_exn (module Int) [ 1, "one"; 2, "two"; 3, "three" ] in
test map (module String) ~f:Int.to_string;
[%expect {|
(Ok (
(1 one)
(2 two)
(3 three)))
|}];
test map (module String) ~f:(fun x -> Int.to_string (x / 2));
[%expect {| (Duplicate_key 1) |}]
;;
let%expect_test "[fold_until]" =
let test t =
print_s
[%sexp
(Map.fold_until
t
~init:0
~f:(fun ~key ~data acc -> if key > 2 then Stop data else Continue (acc + key))
~finish:Int.to_string
: string)]
in
let map = Map.of_alist_exn (module Int) [ 1, "one"; 2, "two"; 3, "three" ] in
test map;
[%expect {| three |}];
let map = Map.of_alist_exn (module Int) [ -1, "minus-one"; 1, "one"; 2, "two" ] in
test map;
[%expect {| 2 |}]
;;
let%expect_test "[sum]" =
let test t = print_s [%sexp (Map.sum (module Int) t ~f:(( * ) 2) : int)] in
let map = Map.of_alist_exn (module String) [ "A", 1; "B", 2; "C", 3 ] in
test map;
[%expect {| 12 |}]
;;
let%expect_test "[sumi]" =
let test t =
print_s [%sexp (Map.sumi (module Int) t ~f:(fun ~key ~data -> key * data) : int)]
in
let map = Map.of_alist_exn (module Int) [ 1, 1; 2, 2; 3, 3 ] in
test map;
[%expect {| 14 |}]
;;
let%expect_test "[merge_disjoint_exn] success" =
let map1 = Map.of_alist_exn (module Int) [ 1, "one"; 2, "two" ] in
let map2 = Map.of_alist_exn (module Int) [ 3, "three" ] in
print_s [%sexp (Map.merge_disjoint_exn map1 map2 : string Map.M(Int).t)];
[%expect {|
((1 one)
(2 two)
(3 three))
|}]
;;
let%expect_test "[merge_disjoint_exn] failure" =
let map1 = Map.of_alist_exn (module Int) [ 1, "one"; 2, "two" ] in
let map2 = Map.of_alist_exn (module Int) [ 2, "two"; 3, "three" ] in
show_raise (fun () -> Map.merge_disjoint_exn map1 map2);
[%expect {| (raised ("Map.merge_disjoint_exn: duplicate key" 2)) |}]
;;