Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid needless shrinking and refactor fragile STM tests #499

Merged
merged 11 commits into from
Dec 20, 2024
Merged
14 changes: 11 additions & 3 deletions src/ephemeron/dune
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
;; Linearization tests of the stdlib Ephemeron module

(test
(name stm_tests)
(modules stm_tests)
(name stm_tests_seq)
(modules stm_tests_spec stm_tests_seq)
(package multicoretests)
(libraries qcheck-stm.sequential qcheck-stm.domain)
(libraries qcheck-stm.sequential)
(action (run %{test} --verbose))
)

(test
(name stm_tests_par)
(modules stm_tests_spec stm_tests_par)
(package multicoretests)
(libraries qcheck-stm.domain)
(action (run %{test} --verbose))
)

Expand Down
179 changes: 0 additions & 179 deletions src/ephemeron/stm_tests.ml

This file was deleted.

7 changes: 7 additions & 0 deletions src/ephemeron/stm_tests_par.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
(** parallel STM tests of Ephemeron *)

module ETest_dom = STM_domain.Make(Stm_tests_spec)

let _ =
QCheck_base_runner.run_tests_main
[ ETest_dom.neg_agree_test_par ~count:1000 ~name:"STM Ephemeron test parallel" ]
7 changes: 7 additions & 0 deletions src/ephemeron/stm_tests_seq.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
(** sequential STM tests of Ephemeron *)

module ETest_seq = STM_sequential.Make(Stm_tests_spec)

let _ =
QCheck_base_runner.run_tests_main
[ ETest_seq.agree_test ~count:1000 ~name:"STM Ephemeron test sequential" ]
153 changes: 153 additions & 0 deletions src/ephemeron/stm_tests_spec.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
open QCheck
open STM

(** STM test specification of Ephemeron *)

(*
module Ephemeron.S =
sig
type key
type 'a t
val create : int -> 'a t
val clear : 'a t -> unit
val reset : 'a t -> unit
val copy : 'a t -> 'a t
val add : 'a t -> key -> 'a -> unit
val remove : 'a t -> key -> unit
val find : 'a t -> key -> 'a
val find_opt : 'a t -> key -> 'a option
val find_all : 'a t -> key -> 'a list
val replace : 'a t -> key -> 'a -> unit
val mem : 'a t -> key -> bool
val iter : (key -> 'a -> unit) -> 'a t -> unit
val filter_map_inplace : (key -> 'a -> 'a option) -> 'a t -> unit
val fold : (key -> 'a -> 'b -> 'b) -> 'a t -> 'b -> 'b
val length : 'a t -> int
val stats : 'a t -> Hashtbl.statistics
val to_seq : 'a t -> (key * 'a) Seq.t
val to_seq_keys : 'a t -> key Seq.t
val to_seq_values : 'a t -> 'a Seq.t
val add_seq : 'a t -> (key * 'a) Seq.t -> unit
val replace_seq : 'a t -> (key * 'a) Seq.t -> unit
val of_seq : (key * 'a) Seq.t -> 'a t
val clean : 'a t -> unit
remove all dead bindings. Done automatically during automatic resizing.
val stats_alive : 'a t -> Hashtbl.statistics
same as Hashtbl.SeededS.stats but only count the alive bindings
*)

module E = Ephemeron.K1.Make(struct
[@@@warning "-unused-value-declaration"]
(* support Int64.hash added in 5.1, without triggering an 'unused hash' error *)
external seeded_hash_param :
int -> int -> int -> 'a -> int = "caml_hash" [@@noalloc]
let hash x = seeded_hash_param 10 100 0 x
include Stdlib.Int64
end)

type key = int64
type data = int64
type sut = data E.t

type state = (key * data) list
type cmd =
| Clear
| Add of key * data
| Remove of key
| Find of key
| Find_opt of key
| Find_all of key
| Replace of key * data
| Mem of key
| Length
| Clean

let pp_cmd par fmt x =
let open Util.Pp in
let pp_key = pp_int64 in
let pp_data = pp_int64 in
match x with
| Clear -> cst0 "Clear" fmt
| Add (x, y) -> cst2 pp_key pp_data "Add" par fmt x y
| Remove x -> cst1 pp_key "Remove" par fmt x
| Find x -> cst1 pp_key "Find" par fmt x
| Find_opt x -> cst1 pp_key "Find_opt" par fmt x
| Find_all x -> cst1 pp_key "Find_all" par fmt x
| Replace (x, y) -> cst2 pp_key pp_data "Replace" par fmt x y
| Mem x -> cst1 pp_key "Mem" par fmt x
| Length -> cst0 "Length" fmt
| Clean -> cst0 "Clean" fmt

let show_cmd = Util.Pp.to_show pp_cmd

let init_sut () = Gc.minor (); E.create 42
let cleanup _ = ()

let arb_cmd s =
let key =
if s = []
then Gen.(map Int64.of_int small_int)
else Gen.(oneof [oneofl (List.map fst s); map Int64.of_int small_int]) in
let data = Gen.(map Int64.of_int small_int) in
QCheck.make ~print:show_cmd
Gen.(frequency
[ 1,return Clear;
2,map2 (fun k v -> Add (k, v)) key data;
2,map (fun k -> Remove k) key;
3,map (fun k -> Find k) key;
3,map (fun k -> Find_opt k) key;
3,map (fun k -> Find_all k) key;
2,map2 (fun k v -> Replace (k, v)) key data;
3,map (fun k -> Mem k) key;
2,return Length;
1,return Clean; ])

let next_state c s =
match c with
| Clear -> []
| Add (k, v) -> (k,v)::s
| Remove k -> List.remove_assoc k s
| Find _
| Find_opt _
| Find_all _ -> s
| Replace (k,v) -> (k,v)::(List.remove_assoc k s)
| Mem _
| Length
| Clean -> s

let run c e =
let data = int64 in
match c with
| Clear -> Res (unit, E.clear e)
| Add (k, v) -> Res (unit, E.add e k v)
| Remove k -> Res (unit, E.remove e k)
| Find k -> Res (result data exn, protect (E.find e) k)
| Find_opt k -> Res (option data, E.find_opt e k)
| Find_all k -> Res (list data, E.find_all e k)
| Replace (k,v) -> Res (unit, E.replace e k v)
| Mem k -> Res (bool, E.mem e k)
| Length -> Res (int, E.length e)
| Clean -> Res (unit, E.clean e)

let init_state = []

let precond _ _ = true
let postcond c (s : state) res =
match c,res with
| Clear, Res ((Unit,_),_) -> true
| Add (_,_), Res ((Unit,_),_) -> true
| Remove _, Res ((Unit,_),_) -> true
| Find k, Res ((Result (Int64,Exn),_),r) ->
r = Error Not_found || r = protect (List.assoc k) s
| Find_opt k, Res ((Option Int64,_),r) ->
r = None || r = List.assoc_opt k s
| Find_all k, Res ((List Int64,_),r) ->
let filter = fun (k',v') -> if k' = k then Some v' else None in
let vs_state = List.filter_map filter s in
(* some entries may have been GC'ed - test only for inclusion *)
List.for_all (fun v -> List.mem v vs_state) (List.sort Int64.compare r)
| Replace (_,_), Res ((Unit,_),_) -> true
| Mem k, Res ((Bool,_),r) -> r = false || r = List.mem_assoc k s (*effectively: no postcond*)
| Length, Res ((Int,_),r) -> r <= List.length s
| Clean, Res ((Unit,_),_) -> true
| _ -> false
Loading
Loading