prop_check 0.10.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +6 -4
- data/lib/prop_check/generator.rb +15 -3
- data/lib/prop_check/generators.rb +41 -15
- data/lib/prop_check/helper.rb +8 -0
- data/lib/prop_check/helper/lazy_append.rb +18 -18
- data/lib/prop_check/hooks.rb +2 -0
- data/lib/prop_check/lazy_tree.rb +5 -7
- data/lib/prop_check/property.rb +28 -71
- data/lib/prop_check/property/output_formatter.rb +41 -0
- data/lib/prop_check/property/shrinker.rb +79 -0
- data/lib/prop_check/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6938e33edff09860d467d560675d04173ac8f44c487f65f22a4253bcad01a465
|
4
|
+
data.tar.gz: 1ed4f6b097d008461e251f285ad52fcd32dfce27cc306862b250cea45e0b954a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e8d7498c00b09f17ec90fced0b130158e1919270837bad1af82ca7f4140f8904816c377cdd3489aea38d6ef36b911a128a92ca956c598daf9f0f88654ca4ac64
|
7
|
+
data.tar.gz: b05e4c9364808468af3c5b9807cc8cc8e036a780e449efd0468f7b577853bd3a286371a3312073c0cf5952c5debb12dd6b13ad2d3e8d0fed0952839db680757c
|
data/Gemfile
CHANGED
@@ -4,8 +4,10 @@ source "https://rubygems.org"
|
|
4
4
|
gemspec
|
5
5
|
|
6
6
|
gem "bundler", "~> 2.0"
|
7
|
-
gem "rake", "~> 12.3", require: false, group: :test
|
8
|
-
gem "rspec", "~> 3.0", require: false, group: :test
|
9
|
-
gem "doctest-rspec", require: false, group: :test
|
10
|
-
gem "simplecov", require: false, group: :test
|
11
7
|
|
8
|
+
group :test do
|
9
|
+
gem "rake", "~> 12.3", require: false
|
10
|
+
gem "rspec", "~> 3.0", require: false
|
11
|
+
gem "doctest-rspec", require: false
|
12
|
+
gem "simplecov", require: false
|
13
|
+
end
|
data/lib/prop_check/generator.rb
CHANGED
@@ -10,6 +10,7 @@ module PropCheck
|
|
10
10
|
class Generator
|
11
11
|
@@default_size = 10
|
12
12
|
@@default_rng = Random.new
|
13
|
+
@@max_consecutive_attempts = 100
|
13
14
|
|
14
15
|
##
|
15
16
|
# Being a special kind of Proc, a Generator wraps a block.
|
@@ -20,8 +21,19 @@ module PropCheck
|
|
20
21
|
##
|
21
22
|
# Given a `size` (integer) and a random number generator state `rng`,
|
22
23
|
# generate a LazyTree.
|
23
|
-
def generate(size = @@default_size, rng = @@default_rng)
|
24
|
-
|
24
|
+
def generate(size = @@default_size, rng = @@default_rng, max_consecutive_attempts = @@max_consecutive_attempts)
|
25
|
+
(0..max_consecutive_attempts).each do
|
26
|
+
res = @block.call(size, rng)
|
27
|
+
next if res == :"PropCheck.filter_me"
|
28
|
+
|
29
|
+
return res
|
30
|
+
end
|
31
|
+
|
32
|
+
raise Errors::GeneratorExhaustedError, """
|
33
|
+
Exhausted #{max_consecutive_attempts} consecutive generation attempts.
|
34
|
+
|
35
|
+
Probably too few generator results were adhering to a `where` condition.
|
36
|
+
"""
|
25
37
|
end
|
26
38
|
|
27
39
|
##
|
@@ -96,7 +108,7 @@ module PropCheck
|
|
96
108
|
# Creates a new Generator that only produces a value when the block `condition` returns a truthy value.
|
97
109
|
def where(&condition)
|
98
110
|
self.map do |result|
|
99
|
-
if condition.call(
|
111
|
+
if condition.call(result)
|
100
112
|
result
|
101
113
|
else
|
102
114
|
:"_PropCheck.filter_me"
|
@@ -227,19 +227,45 @@ module PropCheck
|
|
227
227
|
# is generated by `element_generator`.
|
228
228
|
#
|
229
229
|
# Shrinks to shorter arrays (with shrunken elements).
|
230
|
+
# Accepted keyword arguments:
|
230
231
|
#
|
232
|
+
# `empty:` When false, behaves the same as `min: 1`
|
233
|
+
# `min:` Ensures at least this many elements are generated. (default: 0)
|
234
|
+
# `max:` Ensures at most this many elements are generated. When nil, an arbitrary count is used instead. (default: nil)
|
235
|
+
#
|
236
|
+
#
|
237
|
+
# >> Generators.array(Generators.positive_integer).sample(5, size: 1, rng: Random.new(42))
|
238
|
+
# => [[2], [2], [2], [1], [2]]
|
231
239
|
# >> Generators.array(Generators.positive_integer).sample(5, size: 10, rng: Random.new(42))
|
232
240
|
# => [[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
|
-
|
241
|
+
#
|
242
|
+
# >> Generators.array(Generators.positive_integer, empty: true).sample(5, size: 1, rng: Random.new(1))
|
243
|
+
# => [[], [2], [], [], [2]]
|
244
|
+
# >> Generators.array(Generators.positive_integer, empty: false).sample(5, size: 1, rng: Random.new(1))
|
245
|
+
# => [[2], [1], [2], [1], [1]]
|
246
|
+
|
247
|
+
|
248
|
+
def array(element_generator, min: 0, max: nil, empty: true)
|
249
|
+
min = 1 if min.zero? && !empty
|
250
|
+
|
251
|
+
res = proc do |count|
|
252
|
+
count = min + 1 if count < min
|
253
|
+
count += 1 if count == min && min != 0
|
254
|
+
generators = (min...count).map do
|
236
255
|
element_generator.clone
|
237
256
|
end
|
238
257
|
|
239
258
|
tuple(*generators)
|
240
259
|
end
|
260
|
+
|
261
|
+
if max.nil?
|
262
|
+
nonnegative_integer.bind(&res)
|
263
|
+
else
|
264
|
+
proc.call(max)
|
265
|
+
end
|
241
266
|
end
|
242
267
|
|
268
|
+
|
243
269
|
##
|
244
270
|
# Generates a hash of key->values,
|
245
271
|
# where each of the keys is made using the `key_generator`
|
@@ -249,8 +275,8 @@ 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(key_generator, value_generator)
|
253
|
-
array(tuple(key_generator, value_generator))
|
278
|
+
def hash(key_generator, value_generator, **kwargs)
|
279
|
+
array(tuple(key_generator, value_generator), **kwargs)
|
254
280
|
.map(&:to_h)
|
255
281
|
end
|
256
282
|
|
@@ -276,8 +302,8 @@ module PropCheck
|
|
276
302
|
#
|
277
303
|
# >> Generators.alphanumeric_string.sample(5, size: 10, rng: Random.new(42))
|
278
304
|
# => ["ZCoQ", "8uM", "wkkx0JNx", "v0bxRDLb", "Gl5v8RyWA6"]
|
279
|
-
def alphanumeric_string
|
280
|
-
array(alphanumeric_char).map(&:join)
|
305
|
+
def alphanumeric_string(**kwargs)
|
306
|
+
array(alphanumeric_char, **kwargs).map(&:join)
|
281
307
|
end
|
282
308
|
|
283
309
|
@printable_ascii_chars = (' '..'~').to_a.freeze
|
@@ -302,8 +328,8 @@ module PropCheck
|
|
302
328
|
#
|
303
329
|
# >> Generators.printable_ascii_string.sample(5, size: 10, rng: Random.new(42))
|
304
330
|
# => ["S|.g", "rvjjw7\"5T!", "=", "!_[4@", "Y"]
|
305
|
-
def printable_ascii_string
|
306
|
-
array(printable_ascii_char).map(&:join)
|
331
|
+
def printable_ascii_string(**kwargs)
|
332
|
+
array(printable_ascii_char, **kwargs).map(&:join)
|
307
333
|
end
|
308
334
|
|
309
335
|
@ascii_chars = [
|
@@ -341,8 +367,8 @@ module PropCheck
|
|
341
367
|
#
|
342
368
|
# >> Generators.ascii_string.sample(5, size: 10, rng: Random.new(42))
|
343
369
|
# => ["S|.g", "drvjjw\b\a7\"", "!w=E!_[4@k", "x", "zZI{[o"]
|
344
|
-
def ascii_string
|
345
|
-
array(ascii_char).map(&:join)
|
370
|
+
def ascii_string(**kwargs)
|
371
|
+
array(ascii_char, **kwargs).map(&:join)
|
346
372
|
end
|
347
373
|
|
348
374
|
@printable_chars = [
|
@@ -372,8 +398,8 @@ module PropCheck
|
|
372
398
|
#
|
373
399
|
# >> Generators.printable_string.sample(5, size: 10, rng: Random.new(42))
|
374
400
|
# => ["", "Ȍ", "𐁂", "Ȕ", ""]
|
375
|
-
def printable_string
|
376
|
-
array(printable_char).map(&:join)
|
401
|
+
def printable_string(**kwargs)
|
402
|
+
array(printable_char, **kwargs).map(&:join)
|
377
403
|
end
|
378
404
|
|
379
405
|
##
|
@@ -398,8 +424,8 @@ module PropCheck
|
|
398
424
|
#
|
399
425
|
# >> Generators.string.sample(5, size: 10, rng: Random.new(42))
|
400
426
|
# => ["\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)
|
427
|
+
def string(**kwargs)
|
428
|
+
array(char, **kwargs).map(&:join)
|
403
429
|
end
|
404
430
|
|
405
431
|
##
|
data/lib/prop_check/helper.rb
CHANGED
@@ -23,5 +23,13 @@ module PropCheck
|
|
23
23
|
end
|
24
24
|
end.lazy
|
25
25
|
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# allow lazy appending of two (potentially lazy) enumerators:
|
29
|
+
# >> PropCheck::Helper::LazyAppend.lazy_append([1,2,3],[4,5.6]).to_a
|
30
|
+
# => [1,2,3,4,5,6]
|
31
|
+
def lazy_append(this_enumerator, other_enumerator)
|
32
|
+
[this_enumerator, other_enumerator].lazy.flat_map(&:lazy)
|
33
|
+
end
|
26
34
|
end
|
27
35
|
end
|
@@ -1,18 +1,18 @@
|
|
1
|
-
module PropCheck
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
1
|
+
# module PropCheck
|
2
|
+
# module Helper
|
3
|
+
# ##
|
4
|
+
# # A refinement for enumerators
|
5
|
+
# # to allow lazy appending of two (potentially lazy) enumerators:
|
6
|
+
# # >> [1,2,3].lazy_append([4,5.6]).to_a
|
7
|
+
# # => [1,2,3,4,5,6]
|
8
|
+
# module LazyAppend
|
9
|
+
# refine Enumerable do
|
10
|
+
# ## >> [1,2,3].lazy_append([4,5.6]).to_a
|
11
|
+
# ## => [1,2,3,4,5,6]
|
12
|
+
# def lazy_append(other_enumerator)
|
13
|
+
# [self, other_enumerator].lazy.flat_map(&:lazy)
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
# end
|
data/lib/prop_check/hooks.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
##
|
4
|
+
# @api private
|
4
5
|
# Contains the logic to combine potentially many before/after/around hooks
|
5
6
|
# into a single pair of procedures called `before` and `after`.
|
6
7
|
#
|
@@ -89,6 +90,7 @@ class PropCheck::Hooks
|
|
89
90
|
end
|
90
91
|
|
91
92
|
##
|
93
|
+
# @api private
|
92
94
|
# Wraps enumerable `inner` with a `PropCheck::Hooks` object
|
93
95
|
# such that the before/after/around hooks are called
|
94
96
|
# before/after/around each element that is fetched from `inner`.
|
data/lib/prop_check/lazy_tree.rb
CHANGED
@@ -1,13 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'prop_check/helper/lazy_append'
|
4
|
-
|
5
3
|
module PropCheck
|
6
4
|
##
|
7
5
|
# A Rose tree with the root being eager,
|
8
6
|
# and the children computed lazily, on demand.
|
9
7
|
class LazyTree
|
10
|
-
|
8
|
+
require 'prop_check/helper'
|
11
9
|
|
12
10
|
attr_accessor :root, :children
|
13
11
|
def initialize(root, children = [].lazy)
|
@@ -38,7 +36,7 @@ module PropCheck
|
|
38
36
|
# root_children = root_tree.children
|
39
37
|
# flattened_children = children.map(&:flatten)
|
40
38
|
|
41
|
-
# combined_children =
|
39
|
+
# combined_children = PropCheck::Helper.lazy_append(root_children, flattened_children)
|
42
40
|
|
43
41
|
# LazyTree.new(root_root, combined_children)
|
44
42
|
# end
|
@@ -53,7 +51,7 @@ module PropCheck
|
|
53
51
|
inner_children = inner_tree.children
|
54
52
|
mapped_children = children.map { |child| child.bind(&fun) }
|
55
53
|
|
56
|
-
combined_children =
|
54
|
+
combined_children = PropCheck::Helper.lazy_append(inner_children, mapped_children)
|
57
55
|
|
58
56
|
LazyTree.new(inner_root, combined_children)
|
59
57
|
end
|
@@ -70,7 +68,7 @@ module PropCheck
|
|
70
68
|
def each(&block)
|
71
69
|
squish = lambda do |tree, list|
|
72
70
|
new_children = tree.children.reduce(list) { |acc, elem| squish.call(elem, acc) }
|
73
|
-
[tree.root]
|
71
|
+
PropCheck::Helper.lazy_append([tree.root], new_children)
|
74
72
|
end
|
75
73
|
|
76
74
|
squish
|
@@ -78,7 +76,7 @@ module PropCheck
|
|
78
76
|
|
79
77
|
# base = [root]
|
80
78
|
# recursive = children.map(&:each)
|
81
|
-
# res =
|
79
|
+
# res = PropCheck::Helper.lazy_append(base, recursive)
|
82
80
|
|
83
81
|
# return res.each(&block) if block_given?
|
84
82
|
|
data/lib/prop_check/property.rb
CHANGED
@@ -2,6 +2,8 @@ require 'stringio'
|
|
2
2
|
require "awesome_print"
|
3
3
|
|
4
4
|
require 'prop_check/property/configuration'
|
5
|
+
require 'prop_check/property/output_formatter'
|
6
|
+
require 'prop_check/property/shrinker'
|
5
7
|
require 'prop_check/hooks'
|
6
8
|
module PropCheck
|
7
9
|
##
|
@@ -112,16 +114,39 @@ module PropCheck
|
|
112
114
|
self
|
113
115
|
end
|
114
116
|
|
117
|
+
##
|
118
|
+
# Calls `hook` before each time a check is run with new data.
|
119
|
+
#
|
120
|
+
# This is useful to add setup logic
|
121
|
+
# When called multiple times, earlier-added hooks will be called _before_ `hook` is called.
|
115
122
|
def before(&hook)
|
116
123
|
@hooks.add_before(&hook)
|
117
124
|
self
|
118
125
|
end
|
119
126
|
|
127
|
+
##
|
128
|
+
# Calls `hook` after each time a check is run with new data.
|
129
|
+
#
|
130
|
+
# This is useful to add teardown logic
|
131
|
+
# When called multiple times, earlier-added hooks will be called _after_ `hook` is called.
|
120
132
|
def after(&hook)
|
121
133
|
@hooks.add_after(&hook)
|
122
134
|
self
|
123
135
|
end
|
124
136
|
|
137
|
+
##
|
138
|
+
# Calls `hook` around each time a check is run with new data.
|
139
|
+
#
|
140
|
+
# `hook` should `yield` to the passed block.
|
141
|
+
#
|
142
|
+
# When called multiple times, earlier-added hooks will be wrapped _around_ `hook`.
|
143
|
+
#
|
144
|
+
# Around hooks will be called after all `#before` hooks
|
145
|
+
# and before all `#after` hooks.
|
146
|
+
#
|
147
|
+
# Note that if the block passed to `hook` raises an exception,
|
148
|
+
# it is possible for the code after `yield` not to be called.
|
149
|
+
# So make sure that cleanup logic is wrapped with the `ensure` keyword.
|
125
150
|
def around(&hook)
|
126
151
|
@hooks.add_around(&hook)
|
127
152
|
self
|
@@ -223,83 +248,15 @@ module PropCheck
|
|
223
248
|
|
224
249
|
private def show_problem_output(problem, generator_results, n_successful, &block)
|
225
250
|
output = @config.verbose ? STDOUT : StringIO.new
|
226
|
-
output = pre_output(output, n_successful, generator_results.root, problem)
|
251
|
+
output = PropCheck::Property::OutputFormatter.pre_output(output, n_successful, generator_results.root, problem)
|
227
252
|
shrunken_result, shrunken_exception, n_shrink_steps = shrink(generator_results, output, &block)
|
228
|
-
output = post_output(output, n_shrink_steps, shrunken_result, shrunken_exception)
|
253
|
+
output = PropCheck::Property::OutputFormatter.post_output(output, n_shrink_steps, shrunken_result, shrunken_exception)
|
229
254
|
|
230
255
|
[output, shrunken_result, shrunken_exception, n_shrink_steps]
|
231
256
|
end
|
232
257
|
|
233
|
-
private def pre_output(output, n_successful, generated_root, problem)
|
234
|
-
output.puts ""
|
235
|
-
output.puts "(after #{n_successful} successful property test runs)"
|
236
|
-
output.puts "Failed on: "
|
237
|
-
output.puts "`#{print_roots(generated_root)}`"
|
238
|
-
output.puts ""
|
239
|
-
output.puts "Exception message:\n---\n#{problem}"
|
240
|
-
output.puts "---"
|
241
|
-
output.puts ""
|
242
|
-
|
243
|
-
output
|
244
|
-
end
|
245
|
-
|
246
|
-
private def post_output(output, n_shrink_steps, shrunken_result, shrunken_exception)
|
247
|
-
if n_shrink_steps == 0
|
248
|
-
output.puts '(shrinking impossible)'
|
249
|
-
else
|
250
|
-
output.puts ''
|
251
|
-
output.puts "Shrunken input (after #{n_shrink_steps} shrink steps):"
|
252
|
-
output.puts "`#{print_roots(shrunken_result)}`"
|
253
|
-
output.puts ""
|
254
|
-
output.puts "Shrunken exception:\n---\n#{shrunken_exception}"
|
255
|
-
output.puts "---"
|
256
|
-
output.puts ""
|
257
|
-
end
|
258
|
-
output
|
259
|
-
end
|
260
|
-
|
261
|
-
private def print_roots(lazy_tree_val)
|
262
|
-
if lazy_tree_val.is_a?(Array) && lazy_tree_val.length == 1 && lazy_tree_val[0].is_a?(Hash)
|
263
|
-
lazy_tree_val[0].ai
|
264
|
-
else
|
265
|
-
lazy_tree_val.ai
|
266
|
-
end
|
267
|
-
end
|
268
|
-
|
269
258
|
private def shrink(bindings_tree, io, &block)
|
270
|
-
|
271
|
-
problem_child = bindings_tree
|
272
|
-
siblings = problem_child.children.lazy
|
273
|
-
parent_siblings = nil
|
274
|
-
problem_exception = nil
|
275
|
-
shrink_steps = 0
|
276
|
-
@hooks.wrap_enum(0..@config.max_shrink_steps).lazy.each do
|
277
|
-
begin
|
278
|
-
sibling = siblings.next
|
279
|
-
rescue StopIteration
|
280
|
-
break if parent_siblings.nil?
|
281
|
-
|
282
|
-
siblings = parent_siblings.lazy
|
283
|
-
parent_siblings = nil
|
284
|
-
next
|
285
|
-
end
|
286
|
-
|
287
|
-
shrink_steps += 1
|
288
|
-
io.print '.' if @config.verbose
|
289
|
-
|
290
|
-
begin
|
291
|
-
block.call(*sibling.root)
|
292
|
-
rescue Exception => e
|
293
|
-
problem_child = sibling
|
294
|
-
parent_siblings = siblings
|
295
|
-
siblings = problem_child.children.lazy
|
296
|
-
problem_exception = e
|
297
|
-
end
|
298
|
-
end
|
299
|
-
|
300
|
-
io.puts "(Note: Exceeded #{@config.max_shrink_steps} shrinking steps, the maximum.)" if shrink_steps >= @config.max_shrink_steps
|
301
|
-
|
302
|
-
[problem_child.root, problem_exception, shrink_steps]
|
259
|
+
PropCheck::Property::Shrinker.call(bindings_tree, io, @hooks, @config, &block)
|
303
260
|
end
|
304
261
|
end
|
305
262
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
##
|
2
|
+
# @api private
|
3
|
+
module PropCheck::Property::OutputFormatter
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def pre_output(output, n_successful, generated_root, problem)
|
7
|
+
output.puts ""
|
8
|
+
output.puts "(after #{n_successful} successful property test runs)"
|
9
|
+
output.puts "Failed on: "
|
10
|
+
output.puts "`#{print_roots(generated_root)}`"
|
11
|
+
output.puts ""
|
12
|
+
output.puts "Exception message:\n---\n#{problem}"
|
13
|
+
output.puts "---"
|
14
|
+
output.puts ""
|
15
|
+
|
16
|
+
output
|
17
|
+
end
|
18
|
+
|
19
|
+
def post_output(output, n_shrink_steps, shrunken_result, shrunken_exception)
|
20
|
+
if n_shrink_steps == 0
|
21
|
+
output.puts '(shrinking impossible)'
|
22
|
+
else
|
23
|
+
output.puts ''
|
24
|
+
output.puts "Shrunken input (after #{n_shrink_steps} shrink steps):"
|
25
|
+
output.puts "`#{print_roots(shrunken_result)}`"
|
26
|
+
output.puts ""
|
27
|
+
output.puts "Shrunken exception:\n---\n#{shrunken_exception}"
|
28
|
+
output.puts "---"
|
29
|
+
output.puts ""
|
30
|
+
end
|
31
|
+
output
|
32
|
+
end
|
33
|
+
|
34
|
+
def print_roots(lazy_tree_val)
|
35
|
+
if lazy_tree_val.is_a?(Array) && lazy_tree_val.length == 1 && lazy_tree_val[0].is_a?(Hash)
|
36
|
+
lazy_tree_val[0].ai
|
37
|
+
else
|
38
|
+
lazy_tree_val.ai
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
class PropCheck::Property::Shrinker
|
2
|
+
def initialize(bindings_tree, io, hooks, config)
|
3
|
+
@problem_child = bindings_tree
|
4
|
+
@io = io
|
5
|
+
@siblings = @problem_child.children.lazy
|
6
|
+
@parent_siblings = nil
|
7
|
+
@problem_exception = nil
|
8
|
+
@shrink_steps = 0
|
9
|
+
@hooks = hooks
|
10
|
+
@config = config
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.call(bindings_tree, io, hooks, config, &block)
|
14
|
+
self
|
15
|
+
.new(bindings_tree, io, hooks, config)
|
16
|
+
.call(&block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(&block)
|
20
|
+
@io.puts 'Shrinking...' if @config.verbose
|
21
|
+
|
22
|
+
shrink(&block)
|
23
|
+
|
24
|
+
print_shrinking_exceeded_message if @shrink_steps >= @config.max_shrink_steps
|
25
|
+
|
26
|
+
[@problem_child.root, @problem_exception, @shrink_steps]
|
27
|
+
end
|
28
|
+
|
29
|
+
private def shrink(&block)
|
30
|
+
wrapped_enum.each do
|
31
|
+
instruction, sibling = safe_read_sibling
|
32
|
+
break if instruction == :break
|
33
|
+
next if instruction == :next
|
34
|
+
|
35
|
+
inc_shrink_step
|
36
|
+
|
37
|
+
safe_call_block(sibling, &block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private def wrapped_enum
|
42
|
+
@hooks.wrap_enum(0..@config.max_shrink_steps).lazy
|
43
|
+
end
|
44
|
+
|
45
|
+
private def inc_shrink_step
|
46
|
+
@shrink_steps += 1
|
47
|
+
@io.print '.' if @config.verbose
|
48
|
+
end
|
49
|
+
|
50
|
+
private def safe_read_sibling
|
51
|
+
begin
|
52
|
+
sibling = @siblings.next
|
53
|
+
[:continue, sibling]
|
54
|
+
rescue StopIteration
|
55
|
+
return [:break, nil] if @parent_siblings.nil?
|
56
|
+
|
57
|
+
@siblings = @parent_siblings.lazy
|
58
|
+
@parent_siblings = nil
|
59
|
+
[:next, nil]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private def safe_call_block(sibling, &block)
|
64
|
+
begin
|
65
|
+
block.call(*sibling.root)
|
66
|
+
# It is correct that we want to rescue _all_ Exceptions
|
67
|
+
# not only 'StandardError's
|
68
|
+
rescue Exception => e
|
69
|
+
@problem_child = sibling
|
70
|
+
@parent_siblings = @siblings
|
71
|
+
@siblings = @problem_child.children.lazy
|
72
|
+
@problem_exception = e
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
private def print_shrinking_exceeded_message
|
77
|
+
@io.puts "(Note: Exceeded #{@config.max_shrink_steps} shrinking steps, the maximum.)"
|
78
|
+
end
|
79
|
+
end
|
data/lib/prop_check/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
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.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Qqwy/Wiebe-Marten Wijnja
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-08-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: awesome_print
|
@@ -57,6 +57,8 @@ files:
|
|
57
57
|
- lib/prop_check/lazy_tree.rb
|
58
58
|
- lib/prop_check/property.rb
|
59
59
|
- lib/prop_check/property/configuration.rb
|
60
|
+
- lib/prop_check/property/output_formatter.rb
|
61
|
+
- lib/prop_check/property/shrinker.rb
|
60
62
|
- lib/prop_check/version.rb
|
61
63
|
- prop_check.gemspec
|
62
64
|
homepage: https://github.com/Qqwy/ruby-prop_check/
|