tests/CCHeap: improve existing tests

- label all tests
- decouple tests about different heap functions
- random instances now have better coverage of possible cases:
  + more variability in size
    (previously, some tests were limited to a fixed size)
  + high probability of duplicates
    (previously, the probability of duplicates was negligible,
    because elements were drawn uniformly from the full `int` range)
- the test for `of_list, take_exn` is now more precise
  (added a duplicate element)
- the test for `to_list_sorted` is now more precise
  (checks that the resulting list is what we want,
  instead of just checking that it is sorted)
- the test for `filter` is now more precise
  (also checks that no element has been spuriously dropped)
- more uniform style for easier reading, using `|>`
This commit is contained in:
Glen Mével 2024-07-27 17:22:42 +02:00
parent fdfc806afb
commit 78e67a9f4a

View file

@ -2,21 +2,30 @@ open CCHeap
module T = (val Containers_testlib.make ~__FILE__ ()) module T = (val Containers_testlib.make ~__FILE__ ())
include T include T
(* A QCheck generator for natural numbers that are not too large (larger than
* [small_nat] but smaller than [big_nat]), with a bias towards smaller numbers.
* This also happens to be what QCheck uses for picking a length for a list
* generated by [QCheck.list].
* QCheck defines this generator under the name [nat] but does not expose it. *)
let medium_nat =
Q.make ~print:Q.Print.int ~shrink:Q.Shrink.int ~small:(fun _ -> 1)
(fun st ->
let p = Random.State.float st 1. in
if p < 0.5 then Random.State.int st 10
else if p < 0.75 then Random.State.int st 100
else if p < 0.95 then Random.State.int st 1_000
else Random.State.int st 10_000
)
module H = CCHeap.Make (struct module H = CCHeap.Make (struct
type t = int type t = int
let leq x y = x <= y let leq x y = x <= y
end) end)
let rec is_sorted l = ;;
match l with
| [ _ ] | [] -> true
| x :: (y :: _ as l') -> x <= y && is_sorted l'
let extract_list = H.to_list_sorted;; t ~name:"of_list, take_exn" @@ fun () ->
let h = H.of_list [ 5; 4; 3; 4; 1; 42; 0 ] in
t @@ fun () ->
let h = H.of_list [ 5; 3; 4; 1; 42; 0 ] in
let h, x = H.take_exn h in let h, x = H.take_exn h in
assert_equal ~printer:string_of_int 0 x; assert_equal ~printer:string_of_int 0 x;
let h, x = H.take_exn h in let h, x = H.take_exn h in
@ -26,82 +35,88 @@ assert_equal ~printer:string_of_int 3 x;
let h, x = H.take_exn h in let h, x = H.take_exn h in
assert_equal ~printer:string_of_int 4 x; assert_equal ~printer:string_of_int 4 x;
let h, x = H.take_exn h in let h, x = H.take_exn h in
assert_equal ~printer:string_of_int 4 x;
let h, x = H.take_exn h in
assert_equal ~printer:string_of_int 5 x; assert_equal ~printer:string_of_int 5 x;
let h, x = H.take_exn h in let h, x = H.take_exn h in
assert_equal ~printer:string_of_int 42 x; assert_equal ~printer:string_of_int 42 x;
assert_raises assert_raises ((=) H.Empty) (fun () -> H.take_exn h);
(function
| H.Empty -> true
| _ -> false)
(fun () -> H.take_exn h);
true true
;; ;;
q ~count:30 q ~name:"of_list, to_list"
Q.(list_of_size Gen.(return 1_000) int) ~count:30
Q.(list medium_nat)
(fun l -> (fun l ->
(* put elements into a heap *) (l |> H.of_list |> H.to_list |> List.sort CCInt.compare)
let h = H.of_iter (Iter.of_list l) in = (l |> List.sort CCInt.compare))
assert_equal 1_000 (H.size h);
let l' = extract_list h in
is_sorted l')
;; ;;
(* test filter *) q ~name:"of_list, to_list_sorted"
q ~count:30 ~count:30
Q.(list_of_size Gen.(return 1_000) int) Q.(list medium_nat)
(fun l -> (fun l ->
(* put elements into a heap *) (l |> H.of_list |> H.to_list_sorted)
let h = H.of_iter (Iter.of_list l) in = (l |> List.sort CCInt.compare))
let h = H.filter (fun x -> x mod 2 = 0) h in
assert (H.to_iter h |> Iter.for_all (fun x -> x mod 2 = 0));
let l' = extract_list h in
is_sorted l')
;; ;;
q (* The remaining tests assume the correctness of
Q.(list_of_size Gen.(return 1_000) int) [of_list], [to_list], [to_list_sorted]. *)
q ~name:"size"
~count:30
Q.(list_of_size Gen.small_nat medium_nat)
(fun l -> (fun l ->
(* put elements into a heap *) (l |> H.of_list |> H.size)
let h = H.of_iter (Iter.of_list l) in = (l |> List.length))
let l' = H.to_iter_sorted h |> Iter.to_list in
is_sorted l')
;; ;;
q q ~name:"filter"
Q.(list int) Q.(list medium_nat)
(fun l -> (fun l ->
extract_list (H.of_list l) = extract_list (H.of_gen (CCList.to_gen l))) let p = (fun x -> x mod 2 = 0) in
let l' = l |> H.of_list |> H.filter p |> H.to_list in
List.for_all p l' && List.length l' = List.length (List.filter p l))
;; ;;
q q ~name:"to_iter_sorted"
Q.(list int) Q.(list_of_size Gen.small_nat medium_nat)
(fun l -> (fun l ->
let h = H.of_list l in (l |> H.of_list |> H.to_iter_sorted |> Iter.to_list)
H.to_gen h |> CCList.of_gen |> List.sort Stdlib.compare = (l |> List.sort CCInt.compare))
= (H.to_list h |> List.sort Stdlib.compare))
;; ;;
q q ~name:"of_gen"
Q.(list int) Q.(list_of_size Gen.small_nat medium_nat)
(fun l -> (fun l ->
let h = H.of_list l in (l |> CCList.to_gen |> H.of_gen |> H.to_list_sorted)
H.to_string string_of_int h = (l |> List.sort CCInt.compare))
= (List.sort Stdlib.compare l |> List.map string_of_int |> String.concat ","))
;; ;;
q q ~name:"to_gen"
Q.(list int) Q.(list_of_size Gen.small_nat medium_nat)
(fun l -> (fun l ->
let h = H.of_list l in (l |> H.of_list |> H.to_gen |> CCList.of_gen |> List.sort CCInt.compare)
H.to_string ~sep:" " string_of_int h = (l |> List.sort CCInt.compare))
= (List.sort Stdlib.compare l |> List.map string_of_int |> String.concat " "))
;; ;;
q q ~name:"to_string with default sep"
Q.(list_of_size Gen.(return 1_000) int) Q.(list_of_size Gen.small_nat medium_nat)
(fun l ->
(l |> H.of_list |> H.to_string string_of_int)
= (l |> List.sort CCInt.compare |> List.map string_of_int |> String.concat ","))
;;
q ~name:"to_string with space as sep"
Q.(list_of_size Gen.small_nat medium_nat)
(fun l ->
(l |> H.of_list |> H.to_string ~sep:" " string_of_int)
= (l |> List.sort CCInt.compare |> List.map string_of_int |> String.concat " "))
;;
q ~name:"Make_from_compare"
Q.(list_of_size Gen.small_nat medium_nat)
(fun l -> (fun l ->
let module H' = Make_from_compare (CCInt) in let module H' = Make_from_compare (CCInt) in
let h = H'.of_list l in (l |> H'.of_list |> H'.to_list_sorted)
let l' = H'.to_list_sorted h in = (l |> List.sort CCInt.compare))
is_sorted l')