prop_check 0.10.0 → 0.10.1
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/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: 5b0170f4c948f3ffb903842c33204d4a9d5f74dd4f1d29313a60dec638670623
|
4
|
+
data.tar.gz: 447b87275c1e379502694d96591a871a527535e290daf3cc255476965752e1d2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 533aae467c23eb1ecc974e842e735b49f3cfdef7ae51090ef11ad86e95f90f81660a6617565df4aab6470d2c2ae9ff5978826b48ed2686a727ac53b7c065a7b1
|
7
|
+
data.tar.gz: 4d6cc50716b03427a0aba7c4617a2970cc791c524d6aa427d22a63870853118cd72c6eca469fcd1e42ce2288f42a63b7fc577b248aea47a2892f324d94377db8
|
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/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.10.
|
4
|
+
version: 0.10.1
|
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/
|