|
| 1 | +open QCheck |
| 2 | +open STM |
| 3 | + |
| 4 | +exception Cleanup_without_init |
| 5 | +exception Already_cleaned |
| 6 | +exception Random_postcond_failure |
| 7 | + |
| 8 | +type status = Inited | Cleaned |
| 9 | + |
| 10 | +let status = ref None (* global ref to keep track of cleanup/init status *) |
| 11 | + |
| 12 | +(** This is a variant of refs to test for missing and double cleanup *) |
| 13 | + |
| 14 | +module RConf = |
| 15 | +struct |
| 16 | + |
| 17 | + type cmd = |
| 18 | + | Get |
| 19 | + | Set of int |
| 20 | + | Add of int [@@deriving show { with_path = false }] |
| 21 | + |
| 22 | + let gen_cmd = |
| 23 | + let int_gen = Gen.nat in |
| 24 | + (Gen.oneof |
| 25 | + [Gen.return Get; |
| 26 | + Gen.map (fun i -> Set i) int_gen; |
| 27 | + Gen.map (fun i -> Add i) int_gen; |
| 28 | + ]) |
| 29 | + let arb_cmd _ = make ~print:show_cmd gen_cmd |
| 30 | + |
| 31 | + type state = int |
| 32 | + |
| 33 | + let init_state = 0 |
| 34 | + |
| 35 | + let next_state c s = match c with |
| 36 | + | Get -> s |
| 37 | + | Set i -> i |
| 38 | + | Add i -> s+i |
| 39 | + |
| 40 | + type sut = int ref |
| 41 | + |
| 42 | + let init_sut () = |
| 43 | + assert (!status = None || !status = Some Cleaned); |
| 44 | + status := Some Inited; |
| 45 | + ref 0 |
| 46 | + |
| 47 | + let cleanup _ = match !status with |
| 48 | + | None -> raise Cleanup_without_init |
| 49 | + | Some Cleaned -> raise Already_cleaned |
| 50 | + | Some Inited -> status := Some Cleaned |
| 51 | + |
| 52 | + let run c r = match c with |
| 53 | + | Get -> Res (int, !r) |
| 54 | + | Set i -> Res (unit, (r:=i)) |
| 55 | + | Add i -> Res (unit, let old = !r in r := i + old) (* buggy: not atomic *) |
| 56 | + |
| 57 | + let precond _ _ = true |
| 58 | + |
| 59 | + let postcond c (s:state) res = match c,res with |
| 60 | + | Get, Res ((Int,_),r) -> if r>70 then raise Random_postcond_failure; r = s |
| 61 | + | Set _, Res ((Unit,_),_) |
| 62 | + | Add _, Res ((Unit,_),_) -> true |
| 63 | + | _,_ -> false |
| 64 | +end |
| 65 | + |
| 66 | +module RT_seq = STM_sequential.Make(RConf) |
| 67 | +module RT_dom = STM_domain.Make(RConf) |
| 68 | + |
| 69 | +let rand = Random.State.make_self_init () |
| 70 | +let i = ref 0 |
| 71 | +;; |
| 72 | +for _i=1 to 250 do |
| 73 | + try |
| 74 | + Test.check_exn ~rand (RT_seq.agree_test ~count:1000 ~name:"STM ensure cleanup test sequential") |
| 75 | + with _e -> incr i; assert (!status = Some Cleaned); |
| 76 | +done; |
| 77 | +assert (!i = 250); |
| 78 | +Printf.printf "STM ensure cleanup: sequential test OK\n%!"; |
| 79 | +(* reset things *) |
| 80 | +i := 0; |
| 81 | +status := None; |
| 82 | +for _i=1 to 100 do |
| 83 | + try |
| 84 | + Test.check_exn ~rand |
| 85 | + (Test.make ~count:1000 ~name:"STM ensure cleanup test parallel" |
| 86 | + (RT_dom.arb_cmds_triple 20 12) RT_dom.agree_prop_par) (* without retries *) |
| 87 | + with _e -> incr i; assert (!status = Some Cleaned); |
| 88 | +done; |
| 89 | +assert (!i = 100); |
| 90 | +Printf.printf "STM ensure cleanup: parallel test OK\n%!"; |
0 commit comments