prop_check 0.10.1 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.tool-versions +1 -1
- data/lib/prop_check/generator.rb +32 -24
- data/lib/prop_check/generators.rb +61 -24
- data/lib/prop_check/helper.rb +14 -0
- data/lib/prop_check/lazy_tree.rb +9 -17
- data/lib/prop_check/property.rb +34 -23
- data/lib/prop_check/property/configuration.rb +14 -2
- data/lib/prop_check/property/shrinker.rb +2 -1
- data/lib/prop_check/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '099a3bafe1fae426f476e3add7fb93ab333506670c72c35bb78e5e505b9f0764'
|
4
|
+
data.tar.gz: d1c433ef97043f38c84a5bf8768fb46aa5fcc97f00518da831fbebc36142d2e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 782411f48a77c4bc2213d6ecaecd70d7d7c3609c13bab2125ce853dedd7d3029fd8801ac882154d8c924cea212698740d8e330f8e2b90f460a5a87484413e560
|
7
|
+
data.tar.gz: dd18135801b66e2f073e82066506f8bb26cf031a4bd8d2fc208864acd7ef3a7ec564f8364628d34e4ab40429a5444a1dbc41f329bb4c7d3a608011407bb0a923
|
data/.tool-versions
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby 2.
|
1
|
+
ruby 2.7.1
|
data/lib/prop_check/generator.rb
CHANGED
@@ -10,6 +10,8 @@ module PropCheck
|
|
10
10
|
class Generator
|
11
11
|
@@default_size = 10
|
12
12
|
@@default_rng = Random.new
|
13
|
+
@@max_consecutive_attempts = 100
|
14
|
+
@@default_kwargs = {size: @@default_size, rng: @@default_rng, max_consecutive_attempts: @@max_consecutive_attempts}
|
13
15
|
|
14
16
|
##
|
15
17
|
# Being a special kind of Proc, a Generator wraps a block.
|
@@ -20,26 +22,38 @@ module PropCheck
|
|
20
22
|
##
|
21
23
|
# Given a `size` (integer) and a random number generator state `rng`,
|
22
24
|
# generate a LazyTree.
|
23
|
-
def generate(
|
24
|
-
|
25
|
+
def generate(**kwargs)
|
26
|
+
kwargs = @@default_kwargs.merge(kwargs)
|
27
|
+
max_consecutive_attempts = kwargs[:max_consecutive_attempts]
|
28
|
+
|
29
|
+
(0..max_consecutive_attempts).each do
|
30
|
+
res = @block.call(**kwargs)
|
31
|
+
return res unless res.root == :"_PropCheck.filter_me"
|
32
|
+
end
|
33
|
+
|
34
|
+
raise Errors::GeneratorExhaustedError, """
|
35
|
+
Exhausted #{max_consecutive_attempts} consecutive generation attempts.
|
36
|
+
|
37
|
+
Probably too few generator results were adhering to a `where` condition.
|
38
|
+
"""
|
25
39
|
end
|
26
40
|
|
27
41
|
##
|
28
42
|
# Generates a value, and only return this value
|
29
43
|
# (drop information for shrinking)
|
30
44
|
#
|
31
|
-
# >> Generators.integer.call(1000, Random.new(42))
|
45
|
+
# >> Generators.integer.call(size: 1000, rng: Random.new(42))
|
32
46
|
# => 126
|
33
|
-
def call(
|
34
|
-
generate(
|
47
|
+
def call(**kwargs)
|
48
|
+
generate(**@@default_kwargs.merge(kwargs)).root
|
35
49
|
end
|
36
50
|
|
37
51
|
##
|
38
52
|
# Returns `num_of_samples` values from calling this Generator.
|
39
53
|
# This is mostly useful for debugging if a generator behaves as you intend it to.
|
40
|
-
def sample(num_of_samples = 10,
|
54
|
+
def sample(num_of_samples = 10, **kwargs)
|
41
55
|
num_of_samples.times.map do
|
42
|
-
call(
|
56
|
+
call(**@@default_kwargs.merge(kwargs))
|
43
57
|
end
|
44
58
|
end
|
45
59
|
|
@@ -49,10 +63,10 @@ module PropCheck
|
|
49
63
|
#
|
50
64
|
# Keen readers may notice this as the Monadic 'pure'/'return' implementation for Generators.
|
51
65
|
#
|
52
|
-
# >> Generators.integer.bind { |a| Generators.integer.bind { |b| Generator.wrap([a , b]) } }.call(100, Random.new(42))
|
66
|
+
# >> Generators.integer.bind { |a| Generators.integer.bind { |b| Generator.wrap([a , b]) } }.call(size: 100, rng: Random.new(42))
|
53
67
|
# => [2, 79]
|
54
68
|
def self.wrap(val)
|
55
|
-
Generator.new {
|
69
|
+
Generator.new { LazyTree.wrap(val) }
|
56
70
|
end
|
57
71
|
|
58
72
|
##
|
@@ -61,7 +75,7 @@ module PropCheck
|
|
61
75
|
#
|
62
76
|
# Keen readers may notice this as the Monadic 'bind' (sometimes known as '>>=') implementation for Generators.
|
63
77
|
#
|
64
|
-
# >> Generators.integer.bind { |a| Generators.integer.bind { |b| Generator.wrap([a , b]) } }.call(100, Random.new(42))
|
78
|
+
# >> Generators.integer.bind { |a| Generators.integer.bind { |b| Generator.wrap([a , b]) } }.call(size: 100, rng: Random.new(42))
|
65
79
|
# => [2, 79]
|
66
80
|
def bind(&generator_proc)
|
67
81
|
# Generator.new do |size, rng|
|
@@ -71,11 +85,11 @@ module PropCheck
|
|
71
85
|
# inner_generator.generate(size, rng)
|
72
86
|
# end.flatten
|
73
87
|
# end
|
74
|
-
Generator.new do |
|
75
|
-
outer_result = self.generate(
|
88
|
+
Generator.new do |**kwargs|
|
89
|
+
outer_result = self.generate(**kwargs)
|
76
90
|
outer_result.bind do |outer_val|
|
77
91
|
inner_generator = generator_proc.call(outer_val)
|
78
|
-
inner_generator.generate(
|
92
|
+
inner_generator.generate(**kwargs)
|
79
93
|
end
|
80
94
|
end
|
81
95
|
end
|
@@ -83,11 +97,11 @@ module PropCheck
|
|
83
97
|
##
|
84
98
|
# Creates a new Generator that returns a value by running `proc` on the output of the current Generator.
|
85
99
|
#
|
86
|
-
# >> Generators.choose(32..128).map(&:chr).call(10, Random.new(42))
|
100
|
+
# >> Generators.choose(32..128).map(&:chr).call(size: 10, rng: Random.new(42))
|
87
101
|
# => "S"
|
88
102
|
def map(&proc)
|
89
|
-
Generator.new do |
|
90
|
-
result = self.generate(
|
103
|
+
Generator.new do |**kwargs|
|
104
|
+
result = self.generate(**kwargs)
|
91
105
|
result.map(&proc)
|
92
106
|
end
|
93
107
|
end
|
@@ -96,19 +110,13 @@ module PropCheck
|
|
96
110
|
# Creates a new Generator that only produces a value when the block `condition` returns a truthy value.
|
97
111
|
def where(&condition)
|
98
112
|
self.map do |result|
|
99
|
-
if condition.call(*result)
|
113
|
+
# if condition.call(*result)
|
114
|
+
if PropCheck::Helper.call_splatted(result, &condition)
|
100
115
|
result
|
101
116
|
else
|
102
117
|
:"_PropCheck.filter_me"
|
103
118
|
end
|
104
119
|
end
|
105
|
-
# self.map do |*result|
|
106
|
-
# if condition.call(*result)
|
107
|
-
# result
|
108
|
-
# else
|
109
|
-
# :'_PropCheck.filter_me'
|
110
|
-
# end
|
111
|
-
# end
|
112
120
|
end
|
113
121
|
end
|
114
122
|
end
|
@@ -10,6 +10,7 @@ module PropCheck
|
|
10
10
|
# where you want to use them.
|
11
11
|
module Generators
|
12
12
|
extend self
|
13
|
+
|
13
14
|
##
|
14
15
|
# Always returns the same value, regardless of `size` or `rng` (random number generator state)
|
15
16
|
#
|
@@ -59,7 +60,7 @@ module PropCheck
|
|
59
60
|
# >> r = Random.new(42); Generators.choose(0..5).sample(size: 20000, rng: r)
|
60
61
|
# => [3, 4, 2, 4, 4, 1, 2, 2, 2, 4]
|
61
62
|
def choose(range)
|
62
|
-
Generator.new do |
|
63
|
+
Generator.new do |rng:, **|
|
63
64
|
val = rng.rand(range)
|
64
65
|
LazyTree.new(val, integer_shrink(val))
|
65
66
|
end
|
@@ -73,14 +74,14 @@ module PropCheck
|
|
73
74
|
#
|
74
75
|
# Shrinks to integers closer to zero.
|
75
76
|
#
|
76
|
-
# >> Generators.integer.call(2, Random.new(42))
|
77
|
+
# >> Generators.integer.call(size: 2, rng: Random.new(42))
|
77
78
|
# => 1
|
78
|
-
# >> Generators.integer.call(10000, Random.new(42))
|
79
|
+
# >> Generators.integer.call(size: 10000, rng: Random.new(42))
|
79
80
|
# => 5795
|
80
81
|
# >> r = Random.new(42); Generators.integer.sample(size: 20000, rng: r)
|
81
82
|
# => [-4205, -19140, 18158, -8716, -13735, -3150, 17194, 1962, -3977, -18315]
|
82
83
|
def integer
|
83
|
-
Generator.new do |size
|
84
|
+
Generator.new do |size:, rng:, **|
|
84
85
|
val = rng.rand(-size..size)
|
85
86
|
LazyTree.new(val, integer_shrink(val))
|
86
87
|
end
|
@@ -193,12 +194,12 @@ module PropCheck
|
|
193
194
|
#
|
194
195
|
# Shrinks element generators, one at a time (trying last one first).
|
195
196
|
#
|
196
|
-
# >> Generators.tuple(Generators.integer, Generators.real_float).call(10, Random.new(42))
|
197
|
+
# >> Generators.tuple(Generators.integer, Generators.real_float).call(size: 10, rng: Random.new(42))
|
197
198
|
# => [-4, 13.0]
|
198
199
|
def tuple(*generators)
|
199
|
-
Generator.new do |
|
200
|
+
Generator.new do |**kwargs|
|
200
201
|
LazyTree.zip(generators.map do |generator|
|
201
|
-
generator.generate(
|
202
|
+
generator.generate(**kwargs)
|
202
203
|
end)
|
203
204
|
end
|
204
205
|
end
|
@@ -210,7 +211,7 @@ module PropCheck
|
|
210
211
|
#
|
211
212
|
# Shrinks element generators.
|
212
213
|
#
|
213
|
-
# >> Generators.fixed_hash(a: Generators.integer(), b: Generators.real_float(), c: Generators.integer()).call(10, Random.new(42))
|
214
|
+
# >> Generators.fixed_hash(a: Generators.integer(), b: Generators.real_float(), c: Generators.integer()).call(size: 10, rng: Random.new(42))
|
214
215
|
# => {:a=>-4, :b=>13.0, :c=>-3}
|
215
216
|
def fixed_hash(hash)
|
216
217
|
keypair_generators =
|
@@ -227,17 +228,42 @@ module PropCheck
|
|
227
228
|
# is generated by `element_generator`.
|
228
229
|
#
|
229
230
|
# Shrinks to shorter arrays (with shrunken elements).
|
231
|
+
# Accepted keyword arguments:
|
232
|
+
#
|
233
|
+
# `empty:` When false, behaves the same as `min: 1`
|
234
|
+
# `min:` Ensures at least this many elements are generated. (default: 0)
|
235
|
+
# `max:` Ensures at most this many elements are generated. When nil, an arbitrary count is used instead. (default: nil)
|
236
|
+
#
|
230
237
|
#
|
238
|
+
# >> Generators.array(Generators.positive_integer).sample(5, size: 1, rng: Random.new(42))
|
239
|
+
# => [[2], [2], [2], [1], [2]]
|
231
240
|
# >> Generators.array(Generators.positive_integer).sample(5, size: 10, rng: Random.new(42))
|
232
241
|
# => [[10, 5, 1, 4], [5, 9, 1, 1, 11, 8, 4, 9, 11, 10], [6], [11, 11, 2, 2, 7, 2, 6, 5, 5], [2, 10, 9, 7, 9, 5, 11, 3]]
|
233
|
-
|
234
|
-
|
235
|
-
|
242
|
+
#
|
243
|
+
# >> Generators.array(Generators.positive_integer, empty: true).sample(5, size: 1, rng: Random.new(1))
|
244
|
+
# => [[], [2], [], [], [2]]
|
245
|
+
# >> Generators.array(Generators.positive_integer, empty: false).sample(5, size: 1, rng: Random.new(1))
|
246
|
+
# => [[2], [1], [2], [1], [1]]
|
247
|
+
|
248
|
+
|
249
|
+
def array(element_generator, min: 0, max: nil, empty: true)
|
250
|
+
min = 1 if min.zero? && !empty
|
251
|
+
|
252
|
+
res = proc do |count|
|
253
|
+
count = min + 1 if count < min
|
254
|
+
count += 1 if count == min && min != 0
|
255
|
+
generators = (min...count).map do
|
236
256
|
element_generator.clone
|
237
257
|
end
|
238
258
|
|
239
259
|
tuple(*generators)
|
240
260
|
end
|
261
|
+
|
262
|
+
if max.nil?
|
263
|
+
nonnegative_integer.bind(&res)
|
264
|
+
else
|
265
|
+
proc.call(max)
|
266
|
+
end
|
241
267
|
end
|
242
268
|
|
243
269
|
##
|
@@ -249,11 +275,22 @@ module PropCheck
|
|
249
275
|
#
|
250
276
|
# >> Generators.hash(Generators.printable_ascii_string, Generators.positive_integer).sample(5, size: 3, rng: Random.new(42))
|
251
277
|
# => [{""=>2, "g\\4"=>4, "rv"=>2}, {"7"=>2}, {"!"=>1, "E!"=>1}, {"kY5"=>2}, {}]
|
252
|
-
def hash(
|
253
|
-
|
254
|
-
|
278
|
+
def hash(*args, **kwargs)
|
279
|
+
if args.length == 2
|
280
|
+
hash_of(*args, **kwargs)
|
281
|
+
else
|
282
|
+
super
|
283
|
+
end
|
255
284
|
end
|
256
285
|
|
286
|
+
##
|
287
|
+
#
|
288
|
+
# Alias for `#hash` that does not conflict with a possibly overriden `Object#hash`.
|
289
|
+
#
|
290
|
+
def hash_of(key_generator, value_generator, **kwargs)
|
291
|
+
array(tuple(key_generator, value_generator), **kwargs)
|
292
|
+
.map(&:to_h)
|
293
|
+
end
|
257
294
|
|
258
295
|
@alphanumeric_chars = [('a'..'z'), ('A'..'Z'), ('0'..'9')].flat_map(&:to_a).freeze
|
259
296
|
##
|
@@ -276,8 +313,8 @@ module PropCheck
|
|
276
313
|
#
|
277
314
|
# >> Generators.alphanumeric_string.sample(5, size: 10, rng: Random.new(42))
|
278
315
|
# => ["ZCoQ", "8uM", "wkkx0JNx", "v0bxRDLb", "Gl5v8RyWA6"]
|
279
|
-
def alphanumeric_string
|
280
|
-
array(alphanumeric_char).map(&:join)
|
316
|
+
def alphanumeric_string(**kwargs)
|
317
|
+
array(alphanumeric_char, **kwargs).map(&:join)
|
281
318
|
end
|
282
319
|
|
283
320
|
@printable_ascii_chars = (' '..'~').to_a.freeze
|
@@ -302,8 +339,8 @@ module PropCheck
|
|
302
339
|
#
|
303
340
|
# >> Generators.printable_ascii_string.sample(5, size: 10, rng: Random.new(42))
|
304
341
|
# => ["S|.g", "rvjjw7\"5T!", "=", "!_[4@", "Y"]
|
305
|
-
def printable_ascii_string
|
306
|
-
array(printable_ascii_char).map(&:join)
|
342
|
+
def printable_ascii_string(**kwargs)
|
343
|
+
array(printable_ascii_char, **kwargs).map(&:join)
|
307
344
|
end
|
308
345
|
|
309
346
|
@ascii_chars = [
|
@@ -341,8 +378,8 @@ module PropCheck
|
|
341
378
|
#
|
342
379
|
# >> Generators.ascii_string.sample(5, size: 10, rng: Random.new(42))
|
343
380
|
# => ["S|.g", "drvjjw\b\a7\"", "!w=E!_[4@k", "x", "zZI{[o"]
|
344
|
-
def ascii_string
|
345
|
-
array(ascii_char).map(&:join)
|
381
|
+
def ascii_string(**kwargs)
|
382
|
+
array(ascii_char, **kwargs).map(&:join)
|
346
383
|
end
|
347
384
|
|
348
385
|
@printable_chars = [
|
@@ -372,8 +409,8 @@ module PropCheck
|
|
372
409
|
#
|
373
410
|
# >> Generators.printable_string.sample(5, size: 10, rng: Random.new(42))
|
374
411
|
# => ["", "Ȍ", "𐁂", "Ȕ", ""]
|
375
|
-
def printable_string
|
376
|
-
array(printable_char).map(&:join)
|
412
|
+
def printable_string(**kwargs)
|
413
|
+
array(printable_char, **kwargs).map(&:join)
|
377
414
|
end
|
378
415
|
|
379
416
|
##
|
@@ -398,8 +435,8 @@ module PropCheck
|
|
398
435
|
#
|
399
436
|
# >> Generators.string.sample(5, size: 10, rng: Random.new(42))
|
400
437
|
# => ["\u{A3DB3}𠍜\u{3F46A}\u{1AEBC}", "𡡹\u{DED74}𪱣\u{43E97}ꂂ\u{50695}\u{C0301}", "\u{4FD9D}", "\u{C14BF}\u{193BB}𭇋\u{76B58}", "𦐺\u{9FDDB}\u{80ABB}\u{9E3CF}𐂽\u{14AAE}"]
|
401
|
-
def string
|
402
|
-
array(char).map(&:join)
|
438
|
+
def string(**kwargs)
|
439
|
+
array(char, **kwargs).map(&:join)
|
403
440
|
end
|
404
441
|
|
405
442
|
##
|
data/lib/prop_check/helper.rb
CHANGED
@@ -31,5 +31,19 @@ module PropCheck
|
|
31
31
|
def lazy_append(this_enumerator, other_enumerator)
|
32
32
|
[this_enumerator, other_enumerator].lazy.flat_map(&:lazy)
|
33
33
|
end
|
34
|
+
|
35
|
+
def call_splatted(val, &block)
|
36
|
+
case val
|
37
|
+
when Hash
|
38
|
+
block.call(**val)
|
39
|
+
else
|
40
|
+
block.call(val)
|
41
|
+
end
|
42
|
+
# if kwval != {}
|
43
|
+
# block.call(**kwval)
|
44
|
+
# else
|
45
|
+
# block.call(*val)
|
46
|
+
# end
|
47
|
+
end
|
34
48
|
end
|
35
49
|
end
|
data/lib/prop_check/lazy_tree.rb
CHANGED
@@ -7,6 +7,8 @@ module PropCheck
|
|
7
7
|
class LazyTree
|
8
8
|
require 'prop_check/helper'
|
9
9
|
|
10
|
+
include Enumerable
|
11
|
+
|
10
12
|
attr_accessor :root, :children
|
11
13
|
def initialize(root, children = [].lazy)
|
12
14
|
@root = root
|
@@ -66,25 +68,15 @@ module PropCheck
|
|
66
68
|
# >> LazyTree.new(1, [LazyTree.new(2, [LazyTree.new(3)]), LazyTree.new(4)]).each.force
|
67
69
|
# => [1, 4, 2, 3]
|
68
70
|
def each(&block)
|
69
|
-
|
70
|
-
new_children = tree.children.reduce(list) { |acc, elem| squish.call(elem, acc) }
|
71
|
-
PropCheck::Helper.lazy_append([tree.root], new_children)
|
72
|
-
end
|
73
|
-
|
74
|
-
squish
|
75
|
-
.call(self, [])
|
71
|
+
self.to_enum(:each) unless block_given?
|
76
72
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
# return res.each(&block) if block_given?
|
82
|
-
|
83
|
-
# res
|
73
|
+
squish([])
|
74
|
+
.each(&block)
|
75
|
+
end
|
84
76
|
|
85
|
-
|
86
|
-
|
87
|
-
|
77
|
+
protected def squish(arr)
|
78
|
+
new_children = self.children.reduce(arr) { |acc, elem| elem.squish(acc) }
|
79
|
+
PropCheck::Helper.lazy_append([self.root], new_children)
|
88
80
|
end
|
89
81
|
|
90
82
|
##
|
data/lib/prop_check/property.rb
CHANGED
@@ -35,9 +35,9 @@ module PropCheck
|
|
35
35
|
# a Property object is returned, which you can call the other instance methods
|
36
36
|
# of this class on before finally passing a block to it using `#check`.
|
37
37
|
# (so `forall(Generators.integer) do |val| ... end` and forall(Generators.integer).check do |val| ... end` are the same)
|
38
|
-
def self.forall(*bindings, &block)
|
38
|
+
def self.forall(*bindings, **kwbindings, &block)
|
39
39
|
|
40
|
-
property = new(*bindings)
|
40
|
+
property = new(*bindings, **kwbindings)
|
41
41
|
|
42
42
|
return property.check(&block) if block_given?
|
43
43
|
|
@@ -66,8 +66,9 @@ module PropCheck
|
|
66
66
|
def initialize(*bindings, **kwbindings)
|
67
67
|
raise ArgumentError, 'No bindings specified!' if bindings.empty? && kwbindings.empty?
|
68
68
|
|
69
|
-
@bindings = bindings
|
70
|
-
@kwbindings = kwbindings
|
69
|
+
# @bindings = bindings
|
70
|
+
# @kwbindings = kwbindings
|
71
|
+
@gen = gen_from_bindings(bindings, kwbindings)
|
71
72
|
@condition = proc { true }
|
72
73
|
@config = self.class.configuration
|
73
74
|
@hooks = PropCheck::Hooks.new
|
@@ -106,14 +107,17 @@ module PropCheck
|
|
106
107
|
# you might encounter a GeneratorExhaustedError.
|
107
108
|
# Only filter if you have few inputs to reject. Otherwise, improve your generators.
|
108
109
|
def where(&condition)
|
109
|
-
original_condition = @condition.dup
|
110
|
-
@condition = proc do
|
111
|
-
|
112
|
-
|
110
|
+
# original_condition = @condition.dup
|
111
|
+
# @condition = proc do |val|
|
112
|
+
# call_splatted(val, &original_condition) && call_splatted(val, &condition)
|
113
|
+
# # original_condition.call(val) && condition.call(val)
|
114
|
+
# end
|
115
|
+
@gen = @gen.where(&condition)
|
113
116
|
|
114
117
|
self
|
115
118
|
end
|
116
119
|
|
120
|
+
|
117
121
|
##
|
118
122
|
# Calls `hook` before each time a check is run with new data.
|
119
123
|
#
|
@@ -155,21 +159,11 @@ module PropCheck
|
|
155
159
|
##
|
156
160
|
# Checks the property (after settings have been altered using the other instance methods in this class.)
|
157
161
|
def check(&block)
|
158
|
-
gens =
|
159
|
-
if @kwbindings != {}
|
160
|
-
kwbinding_generator = PropCheck::Generators.fixed_hash(**@kwbindings)
|
161
|
-
@bindings + [kwbinding_generator]
|
162
|
-
else
|
163
|
-
@bindings
|
164
|
-
end
|
165
|
-
binding_generator = PropCheck::Generators.tuple(*gens)
|
166
|
-
# binding_generator = PropCheck::Generators.fixed_hash(**@kwbindings)
|
167
|
-
|
168
162
|
n_runs = 0
|
169
163
|
n_successful = 0
|
170
164
|
|
171
165
|
# Loop stops at first exception
|
172
|
-
attempts_enum(
|
166
|
+
attempts_enum(@gen).each do |generator_result|
|
173
167
|
n_runs += 1
|
174
168
|
check_attempt(generator_result, n_successful, &block)
|
175
169
|
n_successful += 1
|
@@ -178,6 +172,25 @@ module PropCheck
|
|
178
172
|
ensure_not_exhausted!(n_runs)
|
179
173
|
end
|
180
174
|
|
175
|
+
private def gen_from_bindings(bindings, kwbindings)
|
176
|
+
if bindings == [] && kwbindings != {}
|
177
|
+
PropCheck::Generators.fixed_hash(**kwbindings)
|
178
|
+
elsif bindings != [] && kwbindings == {}
|
179
|
+
if bindings.size == 1
|
180
|
+
bindings.first
|
181
|
+
else
|
182
|
+
PropCheck::Generators.tuple(*bindings)
|
183
|
+
end
|
184
|
+
else
|
185
|
+
raise ArgumentError,
|
186
|
+
'Attempted to use both normal and keyword bindings at the same time.
|
187
|
+
This is not supported because of the separation of positional and keyword arguments
|
188
|
+
(the old behaviour is deprecated in Ruby 2.7 and will be removed in 3.0)
|
189
|
+
c.f. https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/
|
190
|
+
'
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
181
194
|
private def ensure_not_exhausted!(n_runs)
|
182
195
|
return if n_runs >= @config.n_runs
|
183
196
|
|
@@ -196,7 +209,7 @@ module PropCheck
|
|
196
209
|
end
|
197
210
|
|
198
211
|
private def check_attempt(generator_result, n_successful, &block)
|
199
|
-
|
212
|
+
PropCheck::Helper.call_splatted(generator_result.root, &block)
|
200
213
|
|
201
214
|
# immediately stop (without shrinnking) for when the app is asked
|
202
215
|
# to close by outside intervention
|
@@ -236,9 +249,7 @@ module PropCheck
|
|
236
249
|
size = 1
|
237
250
|
(0...@config.max_generate_attempts)
|
238
251
|
.lazy
|
239
|
-
.map { binding_generator.generate(size, rng) }
|
240
|
-
.reject { |val| val.root.any? { |elem| elem == :"_PropCheck.filter_me" }}
|
241
|
-
.select { |val| @condition.call(*val.root) }
|
252
|
+
.map { binding_generator.generate(size: size, rng: rng, max_consecutive_attempts: @config.max_consecutive_attempts) }
|
242
253
|
.map do |result|
|
243
254
|
size += 1
|
244
255
|
|
@@ -1,8 +1,20 @@
|
|
1
1
|
module PropCheck
|
2
2
|
class Property
|
3
|
-
Configuration = Struct.new(
|
3
|
+
Configuration = Struct.new(
|
4
|
+
:verbose,
|
5
|
+
:n_runs,
|
6
|
+
:max_generate_attempts,
|
7
|
+
:max_shrink_steps,
|
8
|
+
:max_consecutive_attempts,
|
9
|
+
keyword_init: true) do
|
4
10
|
|
5
|
-
def initialize(
|
11
|
+
def initialize(
|
12
|
+
verbose: false,
|
13
|
+
n_runs: 1_000,
|
14
|
+
max_generate_attempts: 10_000,
|
15
|
+
max_shrink_steps: 10_000,
|
16
|
+
max_consecutive_attempts: 30
|
17
|
+
)
|
6
18
|
super
|
7
19
|
end
|
8
20
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'prop_check/helper'
|
1
2
|
class PropCheck::Property::Shrinker
|
2
3
|
def initialize(bindings_tree, io, hooks, config)
|
3
4
|
@problem_child = bindings_tree
|
@@ -62,7 +63,7 @@ class PropCheck::Property::Shrinker
|
|
62
63
|
|
63
64
|
private def safe_call_block(sibling, &block)
|
64
65
|
begin
|
65
|
-
|
66
|
+
PropCheck::Helper.call_splatted(sibling.root, &block)
|
66
67
|
# It is correct that we want to rescue _all_ Exceptions
|
67
68
|
# not only 'StandardError's
|
68
69
|
rescue Exception => e
|
data/lib/prop_check/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prop_check
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Qqwy/Wiebe-Marten Wijnja
|
@@ -83,7 +83,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
83
83
|
- !ruby/object:Gem::Version
|
84
84
|
version: '0'
|
85
85
|
requirements: []
|
86
|
-
rubygems_version: 3.
|
86
|
+
rubygems_version: 3.1.2
|
87
87
|
signing_key:
|
88
88
|
specification_version: 4
|
89
89
|
summary: PropCheck allows you to do property-based testing, including shrinking.
|