Source file test_container.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
open! Base
open! Container

module%template.portable Test_generic
    (Elt : sig
       type 'a t

       val of_int : int -> int t
       val to_int : int t -> int
     end)
    (Container : sig
       type 'a t [@@deriving sexp]

       include Generic with type ('a, _, _) t := 'a t with type 'a elt := 'a Elt.t

       val mem : 'a t -> 'a Elt.t -> equal:local_ ('a Elt.t -> 'a Elt.t -> bool) -> bool
       val of_list : 'a Elt.t list -> [ `Ok of 'a t | `Skip_test ]
     end) : sig
    type 'a t [@@deriving sexp]

    include Generic with type ('a, _, _) t := 'a t

    val mem : 'a t -> 'a Elt.t -> equal:local_ ('a Elt.t -> 'a Elt.t -> bool) -> bool
  end
  with type 'a t := 'a Container.t
  with type 'a elt := 'a Elt.t =
(* This signature constraint reminds us to add unit tests when functions are added to
   [Generic]. *)
struct
  open Container

  let find = find
  let find_map = find_map
  let fold = fold
  let is_empty = is_empty
  let iter = iter
  let length = length
  let mem = mem
  let sexp_of_t = sexp_of_t
  let t_of_sexp = t_of_sexp
  let to_array = to_array
  let to_list = to_list
  let fold_result = fold_result
  let fold_until = fold_until
  let iter_until = iter_until

  let%test_unit _ =
    let ( = ) = Poly.equal in
    let compare = Poly.compare in
    List.iter [ 0; 1; 2; 3; 4; 8; 128 ] ~f:(fun n ->
      let list = List.init n ~f:Elt.of_int in
      match Container.of_list list with
      | `Skip_test -> ()
      | `Ok c ->
        let sort l = List.sort l ~compare in
        let sorts_are_equal l1 l2 = sort l1 = sort l2 in
        assert (n = Container.length c);
        assert (n = 0 = Container.is_empty c);
        assert (sorts_are_equal list (Container.fold c ~init:[] ~f:(fun ac e -> e :: ac)));
        assert (sorts_are_equal list (Container.to_list c));
        assert (sorts_are_equal list (Array.to_list (Container.to_array c)));
        assert (n > 0 = Option.is_some (Container.find c ~f:(fun e -> Elt.to_int e = 0)));
        assert (
          n > 0 = Option.is_some (Container.find c ~f:(fun e -> Elt.to_int e = n - 1)));
        assert (Option.is_none (Container.find c ~f:(fun e -> Elt.to_int e = n)));
        assert (n > 0 = Container.mem c (Elt.of_int 0) ~equal:( = ));
        if n > 0 then assert (Container.mem c (Elt.of_int (n - 1)) ~equal:( = ));
        assert (not (Container.mem c (Elt.of_int n) ~equal:( = )));
        assert (
          n
          > 0
          = Option.is_some
              (Container.find_map c ~f:(fun e ->
                 if Elt.to_int e = 0 then Some () else None)));
        assert (
          n
          > 0
          = Option.is_some
              (Container.find_map c ~f:(fun e ->
                 if Elt.to_int e = n - 1 then Some () else None)));
        assert (
          Option.is_none
            (Container.find_map c ~f:(fun e -> if Elt.to_int e = n then Some () else None)));
        let r = ref 0 in
        Container.iter c ~f:(fun e -> r := !r + Elt.to_int e);
        assert (!r = List.fold list ~init:0 ~f:(fun n e -> n + Elt.to_int e));
        assert (!r = sum (module Int) c ~f:Elt.to_int);
        let c2 = [%of_sexp: int Container.t] ([%sexp_of: int Container.t] c) in
        assert (sorts_are_equal list (Container.to_list c2));
        let compare_elt a b = Int.compare (Elt.to_int a) (Elt.to_int b) in
        if n = 0
        then (
          assert (!r = 0);
          assert (min_elt ~compare:compare_elt c = None);
          assert (max_elt ~compare:compare_elt c = None))
        else (
          assert (!r = n * (n - 1) / 2);
          assert (Option.map ~f:Elt.to_int (min_elt ~compare:compare_elt c) = Some 0);
          assert (
            Option.map ~f:Elt.to_int (max_elt ~compare:compare_elt c) = Some (Int.pred n)));
        let mid = Container.length c / 2 in
        (match
           Container.fold_result c ~init:0 ~f:(fun count _elt ->
             if count = mid then Error count else Ok (count + 1))
         with
         | Ok 0 -> assert (Container.length c = 0)
         | Ok _ -> failwith "Expected fold to stop early"
         | Error x -> assert (mid = x));
        let r = ref 0 in
        (match
           Container.iter_until
             c
             ~f:(fun _ ->
               if !r = mid
               then Stop (Ok !r)
               else (
                 Int.incr r;
                 Continue ()))
             ~finish:(fun () -> Error ())
         with
         | Error () -> assert (Container.is_empty c)
         | Ok x -> assert (mid = x)))
  ;;

  let min_elt = min_elt
  let max_elt = max_elt
  let count = count
  let sum = sum
  let exists = exists
  let for_all = for_all

  let%test_unit _ =
    List.iter
      [ []
      ; [ true ]
      ; [ false ]
      ; [ false; false ]
      ; [ true; false ]
      ; [ false; true ]
      ; [ true; true ]
      ]
      ~f:(fun bools ->
        let count_should_be =
          List.fold bools ~init:0 ~f:(fun n b -> if b then n + 1 else n)
        in
        let forall_should_be = List.fold bools ~init:true ~f:(fun ac b -> b && ac) in
        let exists_should_be = List.fold bools ~init:false ~f:(fun ac b -> b || ac) in
        match
          Container.of_list (List.map bools ~f:(fun b -> Elt.of_int (if b then 1 else 0)))
        with
        | `Skip_test -> ()
        | `Ok container ->
          let is_one e = Elt.to_int e = 1 in
          let ( = ) = Poly.equal in
          assert (forall_should_be = Container.for_all container ~f:is_one);
          assert (exists_should_be = Container.exists container ~f:is_one);
          assert (count_should_be = Container.count container ~f:is_one))
  ;;
end

module%template.portable
  [@modality p] Test_S1_allow_skipping_tests (Container : sig
    type 'a t [@@deriving sexp]

    include Container.S1 with type 'a t := 'a t

    val of_list : 'a list -> [ `Ok of 'a t | `Skip_test ]
  end) =
struct
  include
    Test_generic [@modality p]
      (struct
        type 'a t = 'a

        let of_int = Fn.id
        let to_int = Fn.id
      end)
      (Container)
end

module%template.portable
  [@modality p] Test_S1 (Container : sig
    type 'a t [@@deriving sexp]

    include Container.S1 with type 'a t := 'a t

    val of_list : 'a list -> 'a t
  end) =
Test_S1_allow_skipping_tests [@modality p] (struct
    include Container

    let of_list l = `Ok (of_list l)
  end)

module Test_S0 (Container : sig
    module Elt : sig
      type t [@@deriving sexp]

      val of_int : int -> t
      val to_int : t -> int
    end

    type t [@@deriving sexp]

    include Container.S0 with type t := t and type elt := Elt.t

    val of_list : Elt.t list -> t
  end) =
struct
  include
    Test_generic
      (struct
        include Container.Elt

        type 'a t = Container.Elt.t
      end)
      (struct
        include Container

        type 'a t = Container.t [@@deriving sexp]

        let of_list l = `Ok (of_list l)
        let mem t x ~equal:_ = Container.mem t x
      end)

  (* [mem] in the second functor argument above ignores its [~equal], so this [~equal]
     should never be called. *)
  let mem t x = mem t x ~equal:(fun _ _ -> assert false)
end