mirror of
https://github.com/c-cube/ocaml-containers.git
synced 2025-12-05 19:00:31 -05:00
343 lines
9.6 KiB
OCaml
343 lines
9.6 KiB
OCaml
module Test = (val Containers_testlib.make ~__FILE__ ())
|
|
open Test
|
|
open Containers_pvec
|
|
|
|
let spf = Printf.sprintf
|
|
|
|
let _listuniq =
|
|
let g = Q.(small_list (pair small_int small_int)) in
|
|
Q.map_same_type
|
|
(fun l ->
|
|
CCList.sort_uniq ~cmp:(fun a b -> Stdlib.compare (fst a) (fst b)) l)
|
|
g
|
|
;;
|
|
|
|
t @@ fun () -> is_empty empty;;
|
|
t @@ fun () -> not (is_empty (return 2));;
|
|
t @@ fun () -> length (return 2) = 1;;
|
|
|
|
q ~name:"get over of_list" _listuniq (fun l ->
|
|
let m = of_list l in
|
|
List.for_all (fun (i, y) -> get m i = y) @@ List.mapi CCPair.make l)
|
|
;;
|
|
|
|
(* regression test for #298 *)
|
|
t ~name:"reg 298" @@ fun () ->
|
|
let rec consume x =
|
|
match pop_opt x with
|
|
| None -> ()
|
|
| Some (_, x) -> consume x
|
|
in
|
|
consume (of_list CCList.(1 -- 100));
|
|
true
|
|
;;
|
|
|
|
q ~name:"push length pop"
|
|
Q.(pair int (small_list int))
|
|
(fun (x, l) ->
|
|
let q0 = of_list l in
|
|
let q = push q0 x in
|
|
assert_equal (length q) (length q0 + 1);
|
|
let y, q = pop q in
|
|
assert_equal x y;
|
|
assert_equal (to_list q) (to_list q0);
|
|
true)
|
|
;;
|
|
|
|
q
|
|
Q.(pair (fun1 Observable.int bool) (small_list int))
|
|
(fun (f, l) ->
|
|
let f = Q.Fn.apply f in
|
|
List.map f l = (of_list l |> map f |> to_list))
|
|
;;
|
|
|
|
q
|
|
Q.(pair (small_list int) (small_list int))
|
|
(fun (l1, l2) -> l1 @ l2 = (append (of_list l1) (of_list l2) |> to_list))
|
|
;;
|
|
|
|
q Q.(small_list int) (fun l -> l = to_list (of_list l));;
|
|
|
|
q _listuniq (fun l ->
|
|
List.sort Stdlib.compare l
|
|
= (l |> Iter.of_list |> of_iter |> to_iter |> Iter.to_list
|
|
|> List.sort Stdlib.compare))
|
|
;;
|
|
|
|
q _listuniq (fun l ->
|
|
List.sort Stdlib.compare l
|
|
= (l |> CCSeq.of_list |> of_seq |> to_seq |> CCSeq.to_list
|
|
|> List.sort Stdlib.compare))
|
|
;;
|
|
|
|
t @@ fun () -> choose empty = None;;
|
|
t @@ fun () -> choose (of_list [ 1, 1; 2, 2 ]) <> None;;
|
|
|
|
q
|
|
Q.(pair (small_list int) (small_list int))
|
|
(fun (l1, l2) -> equal CCInt.equal (of_list l1) (of_list l2) = (l1 = l2))
|
|
;;
|
|
|
|
q Q.(small_list int) (fun l1 -> equal CCInt.equal (of_list l1) (of_list l1))
|
|
|
|
let arb_list_with_idx =
|
|
let open Q in
|
|
let shrink (l, i) =
|
|
Iter.(Shrink.(list ~shrink:int l) >|= fun l -> l, min i (List.length l - 1))
|
|
in
|
|
let gen =
|
|
Gen.(
|
|
let* l = small_list int in
|
|
let+ i =
|
|
if l = [] then
|
|
return 0
|
|
else
|
|
0 -- (List.length l - 1)
|
|
in
|
|
l, i)
|
|
in
|
|
make ~shrink ~print:Print.(pair (list int) int) gen
|
|
;;
|
|
|
|
q arb_list_with_idx (fun (l1, i) ->
|
|
if l1 <> [] then (
|
|
let l2 =
|
|
let x = List.nth l1 i in
|
|
CCList.set_at_idx i (x + 1) l1
|
|
in
|
|
not (equal CCInt.equal (of_list l1) (of_list l2))
|
|
) else
|
|
true)
|
|
|
|
module Ref_impl = struct
|
|
type +'a t = 'a list
|
|
|
|
let empty : _ t = []
|
|
let length = List.length
|
|
let push x l : _ t = l @ [ x ]
|
|
let get i l = List.nth l i
|
|
let to_list l = l
|
|
let to_seq = CCSeq.of_list
|
|
let add_list l l2 : _ t = List.append l l2
|
|
let append self l2 : _ t = List.append self l2
|
|
let flat_map sub l : _ t = CCList.flat_map (fun x -> sub @ [x]) l
|
|
|
|
let to_list_via_reviter m =
|
|
let l = ref [] in
|
|
iter_rev (fun x -> l := x :: !l) m;
|
|
!l
|
|
|
|
let pop_exn l =
|
|
match List.rev l with
|
|
| x :: tl -> x, List.rev tl
|
|
| [] -> invalid_arg "empty"
|
|
|
|
let last_opt l =
|
|
if l = [] then
|
|
None
|
|
else
|
|
Some (List.nth l (List.length l - 1))
|
|
|
|
let is_empty l = l = []
|
|
|
|
let choose l =
|
|
match l with
|
|
| [] -> false
|
|
| _ :: _ -> true
|
|
end
|
|
|
|
let to_list_via_iter m =
|
|
let l = ref [] in
|
|
iter (fun x -> l := x :: !l) m;
|
|
List.rev !l
|
|
|
|
let to_list_via_reviter m =
|
|
let l = ref [] in
|
|
iter_rev (fun x -> l := x :: !l) m;
|
|
!l
|
|
|
|
module Op = struct
|
|
type 'a t =
|
|
| Push of 'a
|
|
| Pop
|
|
(* TODO: set *)
|
|
| Append of 'a list
|
|
| Add_list of 'a list
|
|
| Flat_map of 'a list
|
|
| Check_get of int
|
|
| Check_choose
|
|
| Check_is_empty
|
|
| Check_len
|
|
| Check_to_list
|
|
| Check_iter
|
|
| Check_rev_iter
|
|
| Check_to_gen
|
|
| Check_last
|
|
|
|
let well_formed ops : bool =
|
|
let rec loop size = function
|
|
| [] -> true
|
|
| Push _ :: tl -> loop (size + 1) tl
|
|
| Pop :: tl -> size >= 0 && loop (size - 1) tl
|
|
| Add_list l :: tl -> loop (size + List.length l) tl
|
|
| Append l :: tl -> loop (size + List.length l) tl
|
|
| Flat_map sub :: tl -> loop (size * (1+ List.length sub)) tl
|
|
| Check_get x :: tl -> x < size && loop size tl
|
|
| Check_choose :: tl
|
|
| Check_is_empty :: tl
|
|
| Check_len :: tl
|
|
| Check_to_list :: tl
|
|
| Check_iter :: tl
|
|
| Check_rev_iter :: tl
|
|
| Check_last :: tl
|
|
| Check_to_gen :: tl ->
|
|
loop size tl
|
|
in
|
|
loop 0 ops
|
|
|
|
let show show_x (self : _ t) : string =
|
|
match self with
|
|
| Push x -> spf "push %s" (show_x x)
|
|
| Pop -> "pop"
|
|
| Add_list l -> spf "add_list [%s]" (String.concat ";" @@ List.map show_x l)
|
|
| Append l -> spf "append [%s]" (String.concat ";" @@ List.map show_x l)
|
|
| Flat_map l -> spf "flat_map [%s]" (String.concat ";" @@ List.map show_x l)
|
|
| Check_get i -> spf "check_get %d" i
|
|
| Check_choose -> "check_choose"
|
|
| Check_is_empty -> "check_is_empty"
|
|
| Check_len -> "check_len"
|
|
| Check_to_list -> "check_to_list"
|
|
| Check_iter -> "check_rev_iter"
|
|
| Check_rev_iter -> "check_rev_iter"
|
|
| Check_to_gen -> "check_to_gen"
|
|
| Check_last -> "check_last"
|
|
|
|
let shrink shrink_x (op : _ t) : _ Q.Iter.t =
|
|
let open Q.Shrink in
|
|
let open Q.Iter in
|
|
match op with
|
|
| Push x -> shrink_x x >|= fun x -> Push x
|
|
| Pop -> empty
|
|
| Add_list l -> list ~shrink:shrink_x l >|= fun x -> Add_list x
|
|
| Append l -> list ~shrink:shrink_x l >|= fun x -> Append x
|
|
| Flat_map l -> list ~shrink:shrink_x l >|= fun x -> Flat_map x
|
|
| Check_get _ | Check_choose | Check_is_empty | Check_len | Check_to_list
|
|
| Check_to_gen | Check_last | Check_rev_iter | Check_iter ->
|
|
empty
|
|
|
|
let shrink_l shrink_x : _ t list Q.Shrink.t =
|
|
Q.Shrink.list ~shrink:(shrink shrink_x) |> Q.Shrink.filter well_formed
|
|
|
|
type 'a op = 'a t
|
|
|
|
(* generate list of length [n] *)
|
|
let gen (gen_x : 'a Q.Gen.t) n : 'a t list Q.Gen.t =
|
|
let open Q.Gen in
|
|
let rec loop size n : 'a op list Q.Gen.t =
|
|
if n = 0 then
|
|
return []
|
|
else (
|
|
let op =
|
|
frequency
|
|
@@ List.flatten
|
|
[
|
|
[
|
|
(3, gen_x >|= fun x -> Push x, size + 1);
|
|
1, return (Check_choose, size);
|
|
1, return (Check_is_empty, size);
|
|
1, return (Check_to_list, size);
|
|
1, return (Check_to_gen, size);
|
|
1, return (Check_last, size);
|
|
1, return (Check_iter, size);
|
|
1, return (Check_rev_iter, size);
|
|
];
|
|
(if size > 0 then
|
|
[
|
|
1, return (Pop, size - 1);
|
|
(1, 0 -- (size - 1) >|= fun x -> Check_get x, size);
|
|
]
|
|
else
|
|
[]);
|
|
[
|
|
( 1,
|
|
small_list gen_x >|= fun l ->
|
|
Add_list l, size + List.length l );
|
|
( 1,
|
|
small_list gen_x >|= fun l ->
|
|
Append l, size + List.length l );
|
|
( 1,
|
|
list_size (0--5) gen_x >|= fun l ->
|
|
Flat_map l, size * (1+ List.length l ));
|
|
];
|
|
]
|
|
in
|
|
|
|
op >>= fun (op, size) ->
|
|
loop size (n - 1) >>= fun tl -> return (op :: tl)
|
|
)
|
|
in
|
|
loop 0 n
|
|
end
|
|
|
|
let arb_ops_int : int Op.t list Q.arbitrary =
|
|
Q.make
|
|
~print:(fun o ->
|
|
spf "[%s]" @@ String.concat ";" @@ List.map (Op.show @@ spf "%d") o)
|
|
~shrink:(Op.shrink_l Q.Shrink.int)
|
|
Q.Gen.(0 -- 40 >>= fun len -> Op.gen small_int len)
|
|
|
|
let check_ops ~show_x (ops : 'a Op.t list) : unit =
|
|
let fail () =
|
|
Q.Test.fail_reportf "on list [%s]"
|
|
(String.concat ";" @@ List.map (Op.show show_x) ops)
|
|
in
|
|
let cur = ref empty in
|
|
let cur_ref = ref Ref_impl.empty in
|
|
List.iter
|
|
(fun (op : _ Op.t) ->
|
|
match op with
|
|
| Op.Push x ->
|
|
cur := push !cur x;
|
|
cur_ref := Ref_impl.push x !cur_ref
|
|
| Op.Pop ->
|
|
let x1, cur' = pop !cur in
|
|
cur := cur';
|
|
let x2, cur_ref' = Ref_impl.pop_exn !cur_ref in
|
|
cur_ref := cur_ref';
|
|
if x1 <> x2 then fail ()
|
|
| Op.Add_list l ->
|
|
cur := add_list !cur l;
|
|
cur_ref := Ref_impl.add_list !cur_ref l
|
|
| Op.Append l ->
|
|
cur := append !cur (of_list l);
|
|
cur_ref := Ref_impl.append !cur_ref l
|
|
| Op.Flat_map sub ->
|
|
cur := flat_map (fun x -> push (of_list sub) x) !cur;
|
|
cur_ref := Ref_impl.flat_map sub !cur_ref
|
|
| Op.Check_get i -> if get !cur i <> Ref_impl.get i !cur_ref then fail ()
|
|
| Op.Check_is_empty ->
|
|
if is_empty !cur <> Ref_impl.is_empty !cur_ref then fail ()
|
|
| Op.Check_len -> if length !cur <> Ref_impl.length !cur_ref then fail ()
|
|
| Op.Check_to_list ->
|
|
if to_list !cur <> Ref_impl.to_list !cur_ref then fail ()
|
|
| Op.Check_iter ->
|
|
if to_list_via_iter !cur <> Ref_impl.to_list !cur_ref then fail ()
|
|
| Op.Check_rev_iter ->
|
|
if to_list !cur <> Ref_impl.to_list !cur_ref then fail ()
|
|
| Op.Check_choose ->
|
|
if Option.is_some (choose !cur) <> Ref_impl.choose !cur_ref then fail ()
|
|
| Op.Check_last ->
|
|
if last_opt !cur <> Ref_impl.last_opt !cur_ref then fail ()
|
|
| Op.Check_to_gen ->
|
|
if
|
|
to_seq !cur |> CCSeq.to_list
|
|
<> (Ref_impl.to_seq !cur_ref |> CCSeq.to_list)
|
|
then
|
|
fail ())
|
|
ops;
|
|
()
|
|
|
|
let () =
|
|
q ~count:1000 ~name:"ops" ~long_factor:10 arb_ops_int (fun ops ->
|
|
check_ops ~show_x:(spf "%d") ops;
|
|
true)
|