|
| 1 | +(** Sequential and Parallel model-based tests of mpsc_queue *) |
| 2 | + |
| 3 | +open QCheck |
| 4 | +open STM |
| 5 | +open Util |
| 6 | +module Mpsc_queue = Lockfree.Mpsc_queue |
| 7 | + |
| 8 | +module MPSCConf = struct |
| 9 | + type cmd = Push of int | Pop | Push_head of int | Is_empty | Close |
| 10 | + |
| 11 | + let show_cmd c = |
| 12 | + match c with |
| 13 | + | Push i -> "Push " ^ string_of_int i |
| 14 | + | Pop -> "Pop" |
| 15 | + | Push_head i -> "Push_head" ^ string_of_int i |
| 16 | + | Is_empty -> "Is_empty" |
| 17 | + | Close -> "Close" |
| 18 | + |
| 19 | + type state = bool * int list |
| 20 | + type sut = int Mpsc_queue.t |
| 21 | + |
| 22 | + let producer_cmd _s = |
| 23 | + let int_gen = Gen.nat in |
| 24 | + QCheck.make ~print:show_cmd |
| 25 | + (Gen.oneof |
| 26 | + [ |
| 27 | + Gen.map (fun i -> Push i) int_gen; |
| 28 | + Gen.return Is_empty; |
| 29 | + Gen.return Close; |
| 30 | + ]) |
| 31 | + |
| 32 | + let arb_cmd _s = |
| 33 | + let int_gen = Gen.nat in |
| 34 | + QCheck.make ~print:show_cmd |
| 35 | + (Gen.oneof |
| 36 | + [ |
| 37 | + Gen.return Pop; |
| 38 | + Gen.map (fun i -> Push_head i) int_gen; |
| 39 | + Gen.return Is_empty; |
| 40 | + Gen.return Close; |
| 41 | + ]) |
| 42 | + |
| 43 | + let init_state = (false, []) |
| 44 | + let init_sut () = Mpsc_queue.create () |
| 45 | + let cleanup _ = () |
| 46 | + |
| 47 | + let next_state c (is_closed, s) = |
| 48 | + match c with |
| 49 | + | Push i -> |
| 50 | + (is_closed, if not is_closed then i :: List.rev s |> List.rev else s) |
| 51 | + | Push_head i -> (is_closed, if not (is_closed && s = []) then i :: s else s) |
| 52 | + | Is_empty -> (is_closed, s) |
| 53 | + | Pop -> ( (is_closed, match s with [] -> s | _ :: s' -> s')) |
| 54 | + | Close -> (true, s) |
| 55 | + |
| 56 | + let precond _ _ = true |
| 57 | + |
| 58 | + let run c d = |
| 59 | + match c with |
| 60 | + | Push i -> Res (result unit exn, protect (fun d -> Mpsc_queue.push d i) d) |
| 61 | + | Pop -> Res (result (option int) exn, protect Mpsc_queue.pop d) |
| 62 | + | Push_head i -> |
| 63 | + Res (result unit exn, protect (fun d -> Mpsc_queue.push_head d i) d) |
| 64 | + | Is_empty -> Res (result bool exn, protect Mpsc_queue.is_empty d) |
| 65 | + | Close -> Res (result unit exn, protect Mpsc_queue.close d) |
| 66 | + |
| 67 | + let postcond c ((is_closed, s) : state) res = |
| 68 | + match (c, res) with |
| 69 | + | Push _, Res ((Result (Unit, Exn), _), res) -> |
| 70 | + if is_closed then res = Error Mpsc_queue.Closed else res = Ok () |
| 71 | + | Push_head _, Res ((Result (Unit, Exn), _), res) -> |
| 72 | + if is_closed && s = [] then res = Error Mpsc_queue.Closed |
| 73 | + else res = Ok () |
| 74 | + | Pop, Res ((Result (Option Int, Exn), _), res) -> ( |
| 75 | + match s with |
| 76 | + | [] -> |
| 77 | + if is_closed then res = Error Mpsc_queue.Closed else res = Ok None |
| 78 | + | x :: _ -> res = Ok (Some x)) |
| 79 | + | Is_empty, Res ((Result (Bool, Exn), _), res) -> |
| 80 | + if is_closed && s = [] then res = Error Mpsc_queue.Closed |
| 81 | + else res = Ok (s = []) |
| 82 | + | Close, Res ((Result (Unit, Exn), _), res) -> |
| 83 | + if is_closed then res = Error Mpsc_queue.Closed else res = Ok () |
| 84 | + | _, _ -> false |
| 85 | +end |
| 86 | + |
| 87 | +module MPSC_seq = STM_sequential.Make (MPSCConf) |
| 88 | +module MPSC_dom = STM_domain.Make (MPSCConf) |
| 89 | + |
| 90 | +(* [arb_cmds_par] differs in what each triple component generates: |
| 91 | + "Consumer domain" cmds can't be [Push] (but can be [Pop], [Is_empty], [Close] or [Push_head]), |
| 92 | + "producer domain" cmds can't be [Push_head] or [Pop] (but can be [Push], [Is_empty] or [Close]). *) |
| 93 | +let arb_cmds_par = |
| 94 | + MPSC_dom.arb_triple 20 12 MPSCConf.arb_cmd MPSCConf.arb_cmd |
| 95 | + MPSCConf.producer_cmd |
| 96 | + |
| 97 | +(* A parallel agreement test - w/repeat and retries combined *) |
| 98 | +let agree_test_par_asym ~count ~name = |
| 99 | + let rep_count = 50 in |
| 100 | + Test.make ~retries:10 ~count ~name arb_cmds_par (fun triple -> |
| 101 | + assume (MPSC_dom.all_interleavings_ok triple); |
| 102 | + repeat rep_count MPSC_dom.agree_prop_par_asym triple) |
| 103 | + |
| 104 | +let () = |
| 105 | + let count = 1000 in |
| 106 | + QCheck_base_runner.run_tests_main |
| 107 | + [ |
| 108 | + MPSC_seq.agree_test ~count ~name:"STM Lockfree.Mpsc_queue test sequential"; |
| 109 | + agree_test_par_asym ~count ~name:"STM Lockfree.Mpsc_queue test parallel"; |
| 110 | + MPSC_dom.neg_agree_test_par ~count |
| 111 | + ~name:"STM Lockfree.Mpsc_queue test parallel, negative"; |
| 112 | + ] |
0 commit comments