-
Notifications
You must be signed in to change notification settings - Fork 44
Description
As the new QCheck.Shrink.string
unit tests document, on a failure path (going through all shrinking candidates without luck) the current approach may be a bit too exhaustive:
qcheck/test/core/QCheck_unit_tests.ml
Lines 93 to 103 in f37621a
("string \"vi5x92xgG\"", "vi5x92xgG", (* A less exhaustive string shrinker would be preferable *) | |
["vi5x9"; "vi52xgG"; "vix92xgG"; "5x92xgG"; | |
"v5x92xgG"; "i5x92xgG"; "li5x92xgG"; "qi5x92xgG"; "ti5x92xgG"; "ui5x92xgG"; | |
"ve5x92xgG"; "vg5x92xgG"; "vh5x92xgG"; | |
"viKx92xgG"; "vi@x92xgG"; "vi:x92xgG"; "vi7x92xgG"; "vi6x92xgG"; | |
"vi5m92xgG"; "vi5s92xgG"; "vi5v92xgG"; "vi5w92xgG"; | |
"vi5xM2xgG"; "vi5xC2xgG"; "vi5x>2xgG"; "vi5x;2xgG"; "vi5x:2xgG"; | |
"vi5x9IxgG"; "vi5x9=xgG"; "vi5x97xgG"; "vi5x94xgG"; "vi5x93xgG"; | |
"vi5x92mgG"; "vi5x92sgG"; "vi5x92vgG"; "vi5x92wgG"; | |
"vi5x92xdG"; "vi5x92xfG"; | |
"vi5x92xgT"; "vi5x92xgM"; "vi5x92xgJ"; "vi5x92xgH"]); |
By falling back on Shrink.list_spine
we get a nice and short candidate sequence when we stay clear of element reduction:
# Shrink.string ~shrink:Shrink.nil "zzzzz" (fun xs -> Printf.printf "%s\n" Print.(string xs));;
zzz
zzzz
zzz
zzzz
However, exhaustively trying to reduce all O(n) entries, say O(log n) times, brings this to O(n log n) which may be a bit much:
# Shrink.string ~shrink:Shrink.char "zzzzz" (fun xs -> Printf.printf "%s\n" Print.(string xs));;
zzz
zzzz
zzz
zzzz
nzzzz
tzzzz
wzzzz
yzzzz
znzzz
ztzzz
zwzzz
zyzzz
zznzz
zztzz
zzwzz
zzyzz
zzznz
zzztz
zzzwz
zzzyz
zzzzn
zzzzt
zzzzw
zzzzy
We are getting bitten by this in ocaml-multicore/multicoretests, as remarked by @edwintorok here: ocaml-multicore/multicoretests#329 (comment)
As an alternative to avoiding character-shrinks above a certain threshold, one could consider alternative approaches, e.g.
- reducing only the first non-
'a'
char - reducing several
char
s simultaneously"zzzzz"
-> "aaazz" - ...