prop_check 0.10.1 → 0.11.1
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|