434 lines
12 KiB
OCaml
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)) |}]
|
|
;;
|