Closed jeffh closed 9 years ago
I'm trying to wrap my head around the shrinking. Say I have GenArray(GenPositiveInteger)
The checker finds a failure of the property under test at [1,37, 9, 47].
How does it walk the rose tree when you have a composition of generators like that? Your Integer shrinking example was very well written by the way!
Hey @chadbrewbaker, thanks for being interested! I take no credit since the design is from test.check :smiley:.
If you haven't read it already, there is some documentation on how Fox's shrinking works. Fox will walk the tree depth-first assuming the generated value fails.
The array shrinking is necessarily more complex than integers because it generates "permutations" of the generated array. A shrinking step (immediate children in a rose tree) for an array generator are
[0, 37, 9, 47]
, [1, 0, 9, 47]
, [1, 18, 9, 47]
, etc.)[37, 9, 47]
, [1, 9, 47]
, etc.)The shrinking step is applied recursively to cover all permutations. The internal complexity mostly comes from generating rose trees from the first step type, since each element's children of its rose tree needs to be replace the original value.
If you want details of the shrinking implementation, start at +[FOXRoseTree shrinkTreeFromRoseTrees:]. What I just described above can be seen +[FOXRoseTree sequenceByExpandingRoseTrees:]. Just by eye-ball, I'd say close to half of the RoseTree implementation is to cater to shrinking collections (arrays and tuples).
Of course, if you just build a generator on top of array or tuple generators, this shrinking behavior is "free".
Ok. So with three composed generators f(g(h())), the outer generator f() is shrunk and the inner generator values g(h()) stay fixed?
It depends on how the generators are implemented. Each generator can customize the shrinking behavior as needed. f can choose to shrink the tree produced by g. The same applies for g(h()).
This makes something like FOXGenMap
and FOXMap
different:
FOXGenMap
applies a function on the rose tree and returns a (possibly new) rose tree.FOXMap
applies a function to each element in the rose tree.So you could create a generator that suppresses shrinking by:
// coding from the waist, may not actually compile
id<FOXGenerator> NoShrinking(id<FOXGenerator> generator) {
return FOXGenMap(generator, ^FOXRoseTree *(FOXRoseTree *generatedTree) {
return [[FOXRoseTree alloc] initWithValue:generatedTree.value];
});
}
For more comprehensive coverage, it would be useful to provide famous value generators. These generators prioritize "famous" values (minimum/maximum values) at a higher probability than can be achieved through normal generation:
This can be accomplished with
FOXFrequency
with a 1-10% probability of a generating famous values.Tests:
Header Docs:
Docs: