Skip to content

Commit e1ccbb0

Browse files
authored
Merge pull request #547 from ocaml-multicore/expand-stm-bytes
Expand on STM Bytes test
2 parents 3871589 + a30ef4d commit e1ccbb0

10 files changed

+91
-17
lines changed

.github/workflows/common.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ jobs:
8787
uses: actions/checkout@v4
8888
with:
8989
repository: c-cube/qcheck
90-
ref: v0.23
90+
ref: v0.25
9191
path: multicoretests/qcheck
9292

9393
- name: Pre-Setup

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
and `STM_thread` by utilizing `Gc.Memprof` callbacks. Avoid on 5.0-5.2
77
without `Gc.Memprof` support.
88
- #546: Speed up `Lin`'s default `string` and `bytes` shrinkers.
9+
- #547: Add `Util.Pp.{cst4,cst5}`
910

1011
## 0.7
1112

dune-project

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ the multicore run-time of OCaml 5.0.")
1717
(tags ("test" "test suite" "property" "qcheck" "quickcheck" "multicore" "non-determinism"))
1818
(depends
1919
base-domains
20-
(qcheck-core (>= "0.23"))
20+
(qcheck-core (>= "0.25"))
2121
(qcheck-lin (= :version))
2222
(qcheck-stm (= :version))))
2323

@@ -31,7 +31,7 @@ sequential and parallel tests against a declarative model.")
3131
(depopts base-domains)
3232
(depends
3333
(ocaml (>= 4.12))
34-
(qcheck-core (>= "0.23"))
34+
(qcheck-core (>= "0.25"))
3535
(qcheck-multicoretests-util (= :version))))
3636

3737
(package
@@ -46,7 +46,7 @@ and explained by some sequential interleaving.")
4646
(depopts base-domains)
4747
(depends
4848
(ocaml (>= 4.12))
49-
(qcheck-core (>= "0.23"))
49+
(qcheck-core (>= "0.25"))
5050
(qcheck-multicoretests-util (= :version))))
5151

5252
(package
@@ -57,4 +57,4 @@ multicore programs.")
5757
(tags ("test" "property" "qcheck" "quickcheck" "multicore" "non-determinism"))
5858
(depends
5959
(ocaml (>= 4.12))
60-
(qcheck-core (>= "0.23"))))
60+
(qcheck-core (>= "0.25"))))

lib/util.ml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,16 @@ module Pp = struct
146146
fprintf fmt "%s@[<2>%s (@,%a,@ %a,@ %a)@]%s" o name (pp1 false) x
147147
(pp2 false) y (pp3 false) z c
148148

149+
let cst4 (pp1 : 'a t) (pp2 : 'b t) (pp3 : 'c t) (pp4 : 'd t) name par fmt x y z w =
150+
let o, c = if par then ("(", ")") else ("", "") in
151+
fprintf fmt "%s@[<2>%s (@,%a,@ %a,@ %a,@ %a)@]%s" o name (pp1 false) x
152+
(pp2 false) y (pp3 false) z (pp4 false) w c
153+
154+
let cst5 (pp1 : 'a t) (pp2 : 'b t) (pp3 : 'c t) (pp4 : 'd t) (pp5 : 'e t) name par fmt x y z w v =
155+
let o, c = if par then ("(", ")") else ("", "") in
156+
fprintf fmt "%s@[<2>%s (@,%a,@ %a,@ %a,@ %a,@ %a)@]%s" o name (pp1 false) x
157+
(pp2 false) y (pp3 false) z (pp4 false) w (pp5 false) v c
158+
149159
let pp_exn = of_show Printexc.to_string
150160
let pp_unit _ fmt () = pp_print_string fmt "()"
151161
let pp_bool _ fmt b = fprintf fmt "%B" b

lib/util.mli

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,16 @@ module Pp : sig
7979
[name] with three parameters, using [pp]i to pretty-print its argument
8080
[v]i, wrapping itself into parentheses when [par]. *)
8181

82+
val cst4 : 'a t -> 'b t -> 'c t -> 'd t -> string -> bool -> Format.formatter -> 'a -> 'b -> 'c -> 'd -> unit
83+
(** [cst4 pp1 pp2 pp3 pp4 name par v1 v2 v3 v4 fmt] pretty-prints a constructor
84+
[name] with four parameters, using [pp]i to pretty-print its argument
85+
[v]i, wrapping itself into parentheses when [par]. *)
86+
87+
val cst5 : 'a t -> 'b t -> 'c t -> 'd t -> 'e t -> string -> bool -> Format.formatter -> 'a -> 'b -> 'c -> 'd -> 'e -> unit
88+
(** [cst5 pp1 pp2 pp3 pp4 pp5 name par v1 v2 v3 v4 v5 fmt] pretty-prints a
89+
constructor [name] with five parameters, using [pp]i to pretty-print its
90+
argument [v]i, wrapping itself into parentheses when [par]. *)
91+
8292
val pp_exn : exn t
8393
(** Pretty-printer for exceptions reusing the standard {!Printexc.to_string}.
8494
The exception message will be wrapped conservatively (ie too often) in

multicoretests.opam

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ bug-reports: "https://github.com/ocaml-multicore/multicoretests/issues"
2222
depends: [
2323
"dune" {>= "3.0"}
2424
"base-domains"
25-
"qcheck-core" {>= "0.23"}
25+
"qcheck-core" {>= "0.25"}
2626
"qcheck-lin" {= version}
2727
"qcheck-stm" {= version}
2828
"odoc" {with-doc}

qcheck-lin.opam

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ bug-reports: "https://github.com/ocaml-multicore/multicoretests/issues"
2323
depends: [
2424
"dune" {>= "3.0"}
2525
"ocaml" {>= "4.12"}
26-
"qcheck-core" {>= "0.23"}
26+
"qcheck-core" {>= "0.25"}
2727
"qcheck-multicoretests-util" {= version}
2828
"odoc" {with-doc}
2929
]

qcheck-multicoretests-util.opam

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ bug-reports: "https://github.com/ocaml-multicore/multicoretests/issues"
1515
depends: [
1616
"dune" {>= "3.0"}
1717
"ocaml" {>= "4.12"}
18-
"qcheck-core" {>= "0.23"}
18+
"qcheck-core" {>= "0.25"}
1919
"odoc" {with-doc}
2020
]
2121
build: [

qcheck-stm.opam

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ bug-reports: "https://github.com/ocaml-multicore/multicoretests/issues"
2323
depends: [
2424
"dune" {>= "3.0"}
2525
"ocaml" {>= "4.12"}
26-
"qcheck-core" {>= "0.23"}
26+
"qcheck-core" {>= "0.25"}
2727
"qcheck-multicoretests-util" {= version}
2828
"odoc" {with-doc}
2929
]

src/bytes/stm_tests.ml

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,14 @@ struct
99
| Length
1010
| Get of int
1111
| Set of int * char
12-
| Sub of int * int
1312
| Copy
13+
| To_string
14+
| Sub of int * int
15+
| Sub_string of int * int
1416
| Fill of int * int * char
17+
| Blit_string of string * int * int * int
18+
| Index of char
19+
| Index_opt of char
1520
| To_seq
1621

1722
let pp_cmd par fmt x =
@@ -20,27 +25,46 @@ struct
2025
| Length -> cst0 "Length" fmt
2126
| Get x -> cst1 pp_int "Get" par fmt x
2227
| Set (x, y) -> cst2 pp_int pp_char "Set" par fmt x y
23-
| Sub (x, y) -> cst2 pp_int pp_int "Sub" par fmt x y
2428
| Copy -> cst0 "Copy" fmt
29+
| To_string -> cst0 "To_string" fmt
30+
| Sub (x, y) -> cst2 pp_int pp_int "Sub" par fmt x y
31+
| Sub_string (x, y) -> cst2 pp_int pp_int "Sub_string" par fmt x y
2532
| Fill (x, y, z) -> cst3 pp_int pp_int pp_char "Fill" par fmt x y z
33+
| Blit_string (x, y, z, w) -> cst4 pp_string pp_int pp_int pp_int "Blit_string" par fmt x y z w
34+
| Index x -> cst1 pp_char "Index" par fmt x
35+
| Index_opt x -> cst1 pp_char "Index_opt" par fmt x
2636
| To_seq -> cst0 "To_seq" fmt
2737

2838
let show_cmd = Util.Pp.to_show pp_cmd
2939

3040
type state = char list
3141
type sut = Bytes.t
3242

43+
let shrink_char c = if c = 'a' then Iter.empty else Iter.return 'a' (* much faster than the default *)
44+
45+
let shrink_cmd c = match c with
46+
| Blit_string (src,spos,dpos,l) ->
47+
let open Iter in (* shrink spos int before src string *)
48+
(Iter.map (fun spos -> Blit_string (src,spos,dpos,l)) (Shrink.int spos))
49+
<+> (Iter.map (fun src -> Blit_string (src,spos,dpos,l)) (Shrink.string ~shrink:shrink_char src))
50+
| _ -> Iter.empty
51+
3352
let arb_cmd s =
3453
let int_gen = Gen.(oneof [small_nat; int_bound (List.length s - 1)]) in
3554
let char_gen = Gen.printable in
36-
QCheck.make ~print:show_cmd (*~shrink:shrink_cmd*)
55+
QCheck.make ~print:show_cmd ~shrink:shrink_cmd
3756
Gen.(oneof
3857
[ return Length;
3958
map (fun i -> Get i) int_gen;
4059
map2 (fun i c -> Set (i,c)) int_gen char_gen;
41-
map2 (fun i len -> Sub (i,len)) int_gen int_gen; (* hack: reusing int_gen for length *)
4260
return Copy;
61+
return To_string;
62+
map2 (fun i len -> Sub (i,len)) int_gen int_gen; (* hack: reusing int_gen for length *)
63+
map2 (fun i len -> Sub_string (i,len)) int_gen int_gen; (* hack: reusing int_gen for length *)
4364
map3 (fun i len c -> Fill (i,len,c)) int_gen int_gen char_gen; (* hack: reusing int_gen for length*)
65+
map4 (fun src spos dpos l -> Blit_string (src,spos,dpos,l)) string_small int_gen int_gen int_gen; (* hack: reusing int_gen for length*)
66+
map (fun c -> Index c) char_gen;
67+
map (fun c -> Index_opt c) char_gen;
4468
return To_seq;
4569
])
4670

@@ -52,12 +76,21 @@ struct
5276
| Length -> s
5377
| Get _ -> s
5478
| Set (i,c) -> List.mapi (fun j c' -> if i = j then c else c') s
55-
| Sub (_,_) -> s
5679
| Copy -> s
80+
| To_string -> s
81+
| Sub (_,_) -> s
82+
| Sub_string (_,_) -> s
5783
| Fill (i,l,c) ->
5884
if i >= 0 && l >= 0 && i+l-1 < (List.length s)
5985
then List.mapi (fun j c' -> if i <= j && j <= i+l-1 then c else c') s
6086
else s
87+
| Blit_string (src,spos,dpos,l) ->
88+
if spos >= 0 && l >= 0 && spos+l-1 < (String.length src)
89+
&& dpos >= 0 && dpos+l-1 < (List.length s)
90+
then List.mapi (fun j c' -> if dpos <= j && j <= dpos+l-1 then src.[spos+j-dpos] else c') s
91+
else s
92+
| Index _ -> s
93+
| Index_opt _ -> s
6194
| To_seq -> s
6295

6396
let init_sut () = Bytes.make byte_size 'a'
@@ -70,9 +103,14 @@ struct
70103
| Length -> Res (int, Bytes.length b)
71104
| Get i -> Res (result char exn, protect (Bytes.get b) i)
72105
| Set (i,c) -> Res (result unit exn, protect (Bytes.set b i) c)
73-
| Sub (i,l) -> Res (result (bytes) exn, protect (Bytes.sub b i) l)
74106
| Copy -> Res (bytes, Bytes.copy b)
107+
| To_string -> Res (string, Bytes.to_string b)
108+
| Sub (i,l) -> Res (result bytes exn, protect (Bytes.sub b i) l)
109+
| Sub_string (i,l) -> Res (result string exn, protect (Bytes.sub_string b i) l)
75110
| Fill (i,l,c) -> Res (result unit exn, protect (Bytes.fill b i l) c)
111+
| Blit_string (src,spos,dpos,l) -> Res (result unit exn, protect (Bytes.blit_string src spos b dpos) l)
112+
| Index c -> Res (result int exn, protect (Bytes.index b) c)
113+
| Index_opt c -> Res (option int, Bytes.index_opt b c)
76114
| To_seq -> Res (seq char, List.to_seq (List.of_seq (Bytes.to_seq b)))
77115

78116
let postcond c (s: char list) res = match c, res with
@@ -85,15 +123,30 @@ struct
85123
if i < 0 || i >= List.length s
86124
then r = Error (Invalid_argument "index out of bounds")
87125
else r = Ok ()
126+
| Copy, Res ((Bytes,_),r) -> r = Bytes.of_seq (List.to_seq s)
127+
| To_string, Res ((String,_),r) -> r = String.of_seq (List.to_seq s)
88128
| Sub (i,l), Res ((Result (Bytes,Exn),_), r) ->
89129
if i < 0 || l < 0 || i+l > List.length s
90130
then r = Error (Invalid_argument "String.sub / Bytes.sub")
91131
else r = Ok (Bytes.of_seq (List.to_seq (List.filteri (fun j _ -> i <= j && j <= i+l-1) s)))
92-
| Copy, Res ((Bytes,_),r) -> r = Bytes.of_seq (List.to_seq s)
132+
| Sub_string (i,l), Res ((Result (String,Exn),_), r) ->
133+
if i < 0 || l < 0 || i+l > List.length s
134+
then r = Error (Invalid_argument "String.sub / Bytes.sub")
135+
else r = Ok (String.of_seq (List.to_seq (List.filteri (fun j _ -> i <= j && j <= i+l-1) s)))
93136
| Fill (i,l,_), Res ((Result (Unit,Exn),_), r) ->
94137
if i < 0 || l < 0 || i+l > List.length s
95-
then r = Error (Invalid_argument "String.fill / Bytes.fill" )
138+
then r = Error (Invalid_argument "String.fill / Bytes.fill")
139+
else r = Ok ()
140+
| Blit_string (src,spos,dpos,l), Res ((Result (Unit,Exn),_), r) ->
141+
if spos < 0 || dpos < 0 || l < 0 || spos+l > String.length src || dpos+l > List.length s
142+
then r = Error (Invalid_argument "String.blit / Bytes.blit_string")
96143
else r = Ok ()
144+
| Index c, Res ((Result (Int,Exn),_), r) ->
145+
(match List.find_index (fun c' -> c' = c) s with
146+
| Some i -> r = Ok i
147+
| None -> r = Error Not_found)
148+
| Index_opt c, Res ((Option Int,_), r) ->
149+
r = List.find_index (fun c' -> c' = c) s
97150
| To_seq, Res ((Seq Char,_),r) -> Seq.equal (=) r (List.to_seq s)
98151
| _, _ -> false
99152
end

0 commit comments

Comments
 (0)