prop_check 0.7.1 → 0.10.2
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/.gitignore +2 -1
- data/CHANGELOG.md +1 -0
- data/Gemfile +8 -3
- data/README.md +81 -14
- data/lib/prop_check.rb +7 -0
- data/lib/prop_check/generator.rb +20 -1
- data/lib/prop_check/helper.rb +8 -0
- data/lib/prop_check/helper/lazy_append.rb +18 -18
- data/lib/prop_check/hooks.rb +124 -0
- data/lib/prop_check/lazy_tree.rb +9 -9
- data/lib/prop_check/property.rb +99 -88
- 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
- data/prop_check.gemspec +1 -3
- metadata +9 -37
- data/Gemfile.lock +0 -50
- data/lib/prop_check/property/check_evaluator.rb +0 -45
- data/lib/prop_check/rspec.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 88b54994bb83cb44c77b887dfc6839e19b467bddd8a32dd7d1fe91e624261799
|
4
|
+
data.tar.gz: e515d1522c939cb72eed02ed9fd98b458b1944a16b6c1f50f6c6bbc7076caaa5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3ba9d57ffa4e1d1f7d15b9c202cf232b5a931a57ac6d2d2bba4537030400e892d3154d2ab2db9ce97f6896bf97bb3a64a3cbdba02ac9cc15a561c815e7963aa6
|
7
|
+
data.tar.gz: 90a2fd3a15cf2ed27fc1eb1aea4273e75c39143df861bcf0a25b456c6373a088452a7d4b5e1b52aa52a0f61b1c94b0ceceef442bf3507793417d38ad91bcb2b3
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
- 0.8.0 New syntax that is more explicit, passng generated values to blocks as parameters.
|
data/Gemfile
CHANGED
@@ -3,6 +3,11 @@ source "https://rubygems.org"
|
|
3
3
|
# Specify your gem's dependencies in prop_check.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
-
gem
|
7
|
-
|
8
|
-
|
6
|
+
gem "bundler", "~> 2.0"
|
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/README.md
CHANGED
@@ -14,7 +14,7 @@ It features:
|
|
14
14
|
- Shrinking to a minimal counter-example on failure.
|
15
15
|
|
16
16
|
|
17
|
-
## TODOs before release
|
17
|
+
## TODOs before stable release
|
18
18
|
|
19
19
|
Before releasing this gem on Rubygems, the following things need to be finished:
|
20
20
|
|
@@ -27,10 +27,11 @@ Before releasing this gem on Rubygems, the following things need to be finished:
|
|
27
27
|
- [x] Look into customization of settings from e.g. command line arguments.
|
28
28
|
- [x] Good, unicode-compliant, string generators.
|
29
29
|
- [x] Filtering generator outputs.
|
30
|
+
- [x] Before/after/around hooks to add setup/teardown logic to be called before/after/around each time a check is run with new data.
|
30
31
|
|
31
32
|
# Nice-to-haves
|
32
33
|
|
33
|
-
- [
|
34
|
+
- [x] Basic integration with RSpec. See also https://groups.google.com/forum/#!msg/rspec/U-LmL0OnO-Y/iW_Jcd6JBAAJ for progress on this.
|
34
35
|
- [ ] `aggregate` , `resize` and similar generator-modifying calls (c.f. PropEr's variants of these) which will help with introspection/metrics.
|
35
36
|
- [ ] Integration with other Ruby test frameworks.
|
36
37
|
- Stateful property testing. If implemented at some point, will probably happen in a separate add-on library.
|
@@ -66,8 +67,9 @@ _(to be precise: a method on the execution context is defined which returns the
|
|
66
67
|
Raise an exception from the block if there is a problem. If there is no problem, just return normally.
|
67
68
|
|
68
69
|
```ruby
|
70
|
+
include PropCheck::Generators
|
69
71
|
# testing that Enumerable#sort sorts in ascending order
|
70
|
-
PropCheck.forall(
|
72
|
+
PropCheck.forall(array(integer)) do |numbers|
|
71
73
|
sorted_numbers = numbers.sort
|
72
74
|
|
73
75
|
# Check that no number is smaller than the previous number
|
@@ -77,6 +79,64 @@ PropCheck.forall(numbers: array(integer())) do
|
|
77
79
|
end
|
78
80
|
```
|
79
81
|
|
82
|
+
|
83
|
+
Here is another example, using it inside a test case.
|
84
|
+
Here we check if `naive_average` indeed always returns an integer for all arrays of numbers we can pass it:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
# Somewhere you have this function definition:
|
88
|
+
def naive_average(array)
|
89
|
+
array.sum / array.length
|
90
|
+
end
|
91
|
+
```
|
92
|
+
```ruby
|
93
|
+
# And then in a test case:
|
94
|
+
include PropCheck::Generators
|
95
|
+
PropCheck.forall(numbers: array(integer)) do |numbers:|
|
96
|
+
result = naive_average(numbers)
|
97
|
+
unless result.is_a?(Integer) do
|
98
|
+
raise "Expected the average to be an integer!"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Or if you e.g. are using RSpec:
|
103
|
+
describe "#naive_average" do
|
104
|
+
include PropCheck
|
105
|
+
include PropCheck::Generators
|
106
|
+
|
107
|
+
it "returns an integer for any input" do
|
108
|
+
forall(numbers: array(integer)) do |numbers:|
|
109
|
+
result = naive_average(numbers)
|
110
|
+
expect(result).to be_a(Integer)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
```
|
115
|
+
|
116
|
+
When running this particular example PropCheck very quickly finds out that we have made a programming mistake:
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
ZeroDivisionError:
|
120
|
+
(after 6 successful property test runs)
|
121
|
+
Failed on:
|
122
|
+
`{
|
123
|
+
:numbers => []
|
124
|
+
}`
|
125
|
+
|
126
|
+
Exception message:
|
127
|
+
---
|
128
|
+
divided by 0
|
129
|
+
---
|
130
|
+
|
131
|
+
(shrinking impossible)
|
132
|
+
---
|
133
|
+
```
|
134
|
+
|
135
|
+
Clearly we forgot to handle the case of an empty array being passed to the function.
|
136
|
+
This is a good example of the kind of conceptual bugs that PropCheck (and property-based testing in general)
|
137
|
+
are able to check for.
|
138
|
+
|
139
|
+
|
80
140
|
#### Shrinking
|
81
141
|
|
82
142
|
When a failure is found, PropCheck will re-run the block given to `forall` to test
|
@@ -88,8 +148,8 @@ PropCheck will see if the failure still happens with `x = 50`.
|
|
88
148
|
If it does , it will try `x = 25`. If not, it will try `x = 75`, and so on.
|
89
149
|
|
90
150
|
This means if something only goes wrong for `x = 2`, the program will try:
|
91
|
-
- `x = 100`(fails)
|
92
|
-
- x = 50`(fails),
|
151
|
+
- `x = 100`(fails),
|
152
|
+
- `x = 50`(fails),
|
93
153
|
- `x = 25`(fails),
|
94
154
|
- `x = 12`(fails),
|
95
155
|
- `x = 6`(fails),
|
@@ -106,21 +166,28 @@ A short summary:
|
|
106
166
|
- Arrays and hashes shrink to fewer elements, as well as shrinking their elements.
|
107
167
|
- Strings shrink to shorter strings, as well as characters earlier in their alphabet.
|
108
168
|
|
169
|
+
### Builtin Generators
|
170
|
+
|
171
|
+
PropCheck comes with [many builtin generators in the PropCheck::Generators](https://www.rubydoc.info/github/Qqwy/ruby-prop_check/master/PropCheck/Generators) module.
|
172
|
+
|
173
|
+
It contains generators for:
|
174
|
+
- (any, positive, negative, etc.) integers,
|
175
|
+
- (any, only real-valued) floats,
|
176
|
+
- (any, printable only, alphanumeric only, etc) strings and symbols
|
177
|
+
- fixed-size arrays and hashes
|
178
|
+
- as well as varying-size arrays and hashes.
|
179
|
+
- and many more!
|
180
|
+
|
181
|
+
It is common to call `include PropCheck::Generators` in e.g. your testing-suite files to be able to use these.
|
182
|
+
If you want to be more explicit (but somewhat more verbose) when calling these functions. feel free to e.g. create a module-alias (like `PG = PropCheck::Generators`) instead.
|
109
183
|
|
110
184
|
### Writing Custom Generators
|
111
185
|
|
112
|
-
PropCheck comes bundled with a bunch of common generators
|
113
|
-
- integers
|
114
|
-
- floats
|
115
|
-
- strings
|
116
|
-
- symbols
|
117
|
-
- arrays
|
118
|
-
- hashes
|
119
|
-
etc.
|
186
|
+
As described in the previous section, PropCheck already comes bundled with a bunch of common generators.
|
120
187
|
|
121
188
|
However, you can easily adapt them to generate your own datatypes:
|
122
189
|
|
123
|
-
#### Generator#wrap
|
190
|
+
#### Generators#constant / Generator#wrap
|
124
191
|
|
125
192
|
Always returns the given value. No shrinking.
|
126
193
|
|
data/lib/prop_check.rb
CHANGED
@@ -9,6 +9,13 @@ require 'prop_check/helper'
|
|
9
9
|
# You probably want to look at the documentation of
|
10
10
|
# PropCheck::Generator and PropCheck::Generators
|
11
11
|
# to find out more about how to use generators.
|
12
|
+
#
|
13
|
+
# Common usage is to call `extend PropCheck` in your (testing) modules.
|
14
|
+
#
|
15
|
+
# This will:
|
16
|
+
# 1. Add the local method `forall` which will call `PropCheck.forall`
|
17
|
+
# 2. `include PropCheck::Generators`.
|
18
|
+
#
|
12
19
|
module PropCheck
|
13
20
|
module Errors
|
14
21
|
class Error < StandardError; end
|
data/lib/prop_check/generator.rb
CHANGED
@@ -72,7 +72,7 @@ module PropCheck
|
|
72
72
|
# end.flatten
|
73
73
|
# end
|
74
74
|
Generator.new do |size, rng|
|
75
|
-
outer_result = generate(size, rng)
|
75
|
+
outer_result = self.generate(size, rng)
|
76
76
|
outer_result.bind do |outer_val|
|
77
77
|
inner_generator = generator_proc.call(outer_val)
|
78
78
|
inner_generator.generate(size, rng)
|
@@ -91,5 +91,24 @@ module PropCheck
|
|
91
91
|
result.map(&proc)
|
92
92
|
end
|
93
93
|
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# Creates a new Generator that only produces a value when the block `condition` returns a truthy value.
|
97
|
+
def where(&condition)
|
98
|
+
self.map do |*result|
|
99
|
+
if condition.call(*result)
|
100
|
+
result
|
101
|
+
else
|
102
|
+
:"_PropCheck.filter_me"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
# self.map do |*result|
|
106
|
+
# if condition.call(*result)
|
107
|
+
# result
|
108
|
+
# else
|
109
|
+
# :'_PropCheck.filter_me'
|
110
|
+
# end
|
111
|
+
# end
|
112
|
+
end
|
94
113
|
end
|
95
114
|
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
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# @api private
|
5
|
+
# Contains the logic to combine potentially many before/after/around hooks
|
6
|
+
# into a single pair of procedures called `before` and `after`.
|
7
|
+
#
|
8
|
+
# _Note: This module is an implementation detail of PropCheck._
|
9
|
+
#
|
10
|
+
# These can be invoked by manually calling `#before` and `#after`.
|
11
|
+
# Important:
|
12
|
+
# - Always call first `#before` and then `#after`.
|
13
|
+
# This is required to make sure that `around` callbacks will work properly.
|
14
|
+
# - Make sure that if you call `#before`, to also call `#after`.
|
15
|
+
# It is thus highly recommended to call `#after` inside an `ensure`.
|
16
|
+
# This is to make sure that `around` callbacks indeed perform their proper cleanup.
|
17
|
+
#
|
18
|
+
# Alternatively, check out `PropCheck::Hooks::Enumerable` which allows
|
19
|
+
# wrapping the elements of an enumerable with hooks.
|
20
|
+
class PropCheck::Hooks
|
21
|
+
# attr_reader :before, :after, :around
|
22
|
+
def initialize()
|
23
|
+
@before = proc {}
|
24
|
+
@after = proc {}
|
25
|
+
@around = proc { |*args, &block| block.call(*args) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def wrap_enum(enumerable)
|
29
|
+
PropCheck::Hooks::Enumerable.new(enumerable, self)
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
##
|
34
|
+
# Wraps a block with all hooks that were configured this far.
|
35
|
+
#
|
36
|
+
# This means that whenever the block is called,
|
37
|
+
# the before/around/after hooks are called before/around/after it.
|
38
|
+
def wrap_block(&block)
|
39
|
+
proc { |*args| call(*args, &block) }
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# Wraps a block with all hooks that were configured this far,
|
44
|
+
# and immediately calls it using the given `*args`.
|
45
|
+
#
|
46
|
+
# See also #wrap_block
|
47
|
+
def call(*args, &block)
|
48
|
+
begin
|
49
|
+
@before.call()
|
50
|
+
@around.call do
|
51
|
+
block.call(*args)
|
52
|
+
end
|
53
|
+
ensure
|
54
|
+
@after.call()
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# Adds `hook` to the `before` proc.
|
60
|
+
# It is called after earlier-added `before` procs.
|
61
|
+
def add_before(&hook)
|
62
|
+
old_before = @before
|
63
|
+
@before = proc {
|
64
|
+
old_before.call
|
65
|
+
hook.call
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
# Adds `hook` to the `after` proc.
|
71
|
+
# It is called before earlier-added `after` procs.
|
72
|
+
def add_after(&hook)
|
73
|
+
old_after = @after
|
74
|
+
@after = proc {
|
75
|
+
hook.call
|
76
|
+
old_after.call
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Adds `hook` to the `around` proc.
|
82
|
+
# It is called _inside_ earlier-added `around` procs.
|
83
|
+
def add_around(&hook)
|
84
|
+
old_around = @around
|
85
|
+
@around = proc do |&block|
|
86
|
+
old_around.call do |*args|
|
87
|
+
hook.call(*args, &block)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# @api private
|
94
|
+
# Wraps enumerable `inner` with a `PropCheck::Hooks` object
|
95
|
+
# such that the before/after/around hooks are called
|
96
|
+
# before/after/around each element that is fetched from `inner`.
|
97
|
+
#
|
98
|
+
# This is very helpful if you need to perform cleanup logic
|
99
|
+
# before/after/around e.g. data is generated or fetched.
|
100
|
+
#
|
101
|
+
# Note that whatever is after a `yield` in an `around` hook
|
102
|
+
# is not guaranteed to be called (for instance when a StopIteration is raised).
|
103
|
+
# Thus: make sure you use `ensure` to clean up resources.
|
104
|
+
class Enumerable
|
105
|
+
include ::Enumerable
|
106
|
+
|
107
|
+
def initialize(inner, hooks)
|
108
|
+
@inner = inner
|
109
|
+
@hooks = hooks
|
110
|
+
end
|
111
|
+
|
112
|
+
def each(&task)
|
113
|
+
return to_enum(:each) unless block_given?
|
114
|
+
|
115
|
+
enum = @inner.to_enum
|
116
|
+
|
117
|
+
wrapped_yielder = @hooks.wrap_block do
|
118
|
+
yield enum.next(&task)
|
119
|
+
end
|
120
|
+
|
121
|
+
loop(&wrapped_yielder)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
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,14 +68,15 @@ 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
|
-
squish
|
74
|
+
squish
|
75
|
+
.call(self, [])
|
77
76
|
|
78
77
|
# base = [root]
|
79
78
|
# recursive = children.map(&:each)
|
80
|
-
# res =
|
79
|
+
# res = PropCheck::Helper.lazy_append(base, recursive)
|
81
80
|
|
82
81
|
# return res.each(&block) if block_given?
|
83
82
|
|
@@ -99,7 +98,8 @@ module PropCheck
|
|
99
98
|
# >> LazyTree.new(1, [LazyTree.new(2, [LazyTree.new(3)]), LazyTree.new(4)]).to_a
|
100
99
|
# => [1, 4, 2, 3]
|
101
100
|
def to_a
|
102
|
-
each
|
101
|
+
each
|
102
|
+
.force
|
103
103
|
end
|
104
104
|
|
105
105
|
# TODO: fix implementation
|
data/lib/prop_check/property.rb
CHANGED
@@ -2,24 +2,42 @@ require 'stringio'
|
|
2
2
|
require "awesome_print"
|
3
3
|
|
4
4
|
require 'prop_check/property/configuration'
|
5
|
-
require 'prop_check/property/
|
5
|
+
require 'prop_check/property/output_formatter'
|
6
|
+
require 'prop_check/property/shrinker'
|
7
|
+
require 'prop_check/hooks'
|
6
8
|
module PropCheck
|
7
9
|
##
|
8
10
|
# Run properties
|
9
11
|
class Property
|
10
12
|
|
11
13
|
##
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
14
|
+
# Main entry-point to create (and possibly immediately run) a property-test.
|
15
|
+
#
|
16
|
+
# This method accepts a list of generators and a block.
|
17
|
+
# The block will then be executed many times, passing the values generated by the generators
|
18
|
+
# as respective arguments:
|
19
|
+
#
|
20
|
+
# ```
|
21
|
+
# include PropCheck::Generators
|
22
|
+
# PropCheck.forall(integer(), float()) { |x, y| ... }
|
23
|
+
# ```
|
24
|
+
#
|
25
|
+
# It is also possible (and recommended when having more than a few generators) to use a keyword-list
|
26
|
+
# of generators instead:
|
27
|
+
#
|
28
|
+
# ```
|
29
|
+
# include PropCheck::Generators
|
30
|
+
# PropCheck.forall(x: integer(), y: float()) { |x:, y:| ... }
|
31
|
+
# ```
|
32
|
+
#
|
15
33
|
#
|
16
34
|
# If you do not pass a block right away,
|
17
35
|
# a Property object is returned, which you can call the other instance methods
|
18
36
|
# of this class on before finally passing a block to it using `#check`.
|
19
|
-
# (so `forall(
|
20
|
-
def self.forall(
|
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)
|
21
39
|
|
22
|
-
property = new(bindings)
|
40
|
+
property = new(*bindings)
|
23
41
|
|
24
42
|
return property.check(&block) if block_given?
|
25
43
|
|
@@ -45,12 +63,14 @@ module PropCheck
|
|
45
63
|
|
46
64
|
attr_reader :bindings, :condition
|
47
65
|
|
48
|
-
def initialize(**
|
49
|
-
raise ArgumentError, 'No bindings specified!' if bindings.empty?
|
66
|
+
def initialize(*bindings, **kwbindings)
|
67
|
+
raise ArgumentError, 'No bindings specified!' if bindings.empty? && kwbindings.empty?
|
50
68
|
|
51
69
|
@bindings = bindings
|
52
|
-
@
|
70
|
+
@kwbindings = kwbindings
|
71
|
+
@condition = proc { true }
|
53
72
|
@config = self.class.configuration
|
73
|
+
@hooks = PropCheck::Hooks.new
|
54
74
|
end
|
55
75
|
|
56
76
|
##
|
@@ -87,21 +107,69 @@ module PropCheck
|
|
87
107
|
# Only filter if you have few inputs to reject. Otherwise, improve your generators.
|
88
108
|
def where(&condition)
|
89
109
|
original_condition = @condition.dup
|
90
|
-
@condition =
|
110
|
+
@condition = proc do |*args|
|
111
|
+
original_condition.call(*args) && condition.call(*args)
|
112
|
+
end
|
91
113
|
|
92
114
|
self
|
93
115
|
end
|
94
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.
|
122
|
+
def before(&hook)
|
123
|
+
@hooks.add_before(&hook)
|
124
|
+
self
|
125
|
+
end
|
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.
|
132
|
+
def after(&hook)
|
133
|
+
@hooks.add_after(&hook)
|
134
|
+
self
|
135
|
+
end
|
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.
|
150
|
+
def around(&hook)
|
151
|
+
@hooks.add_around(&hook)
|
152
|
+
self
|
153
|
+
end
|
154
|
+
|
95
155
|
##
|
96
156
|
# Checks the property (after settings have been altered using the other instance methods in this class.)
|
97
157
|
def check(&block)
|
98
|
-
|
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)
|
99
167
|
|
100
168
|
n_runs = 0
|
101
169
|
n_successful = 0
|
102
170
|
|
103
171
|
# Loop stops at first exception
|
104
|
-
|
172
|
+
attempts_enum(binding_generator).each do |generator_result|
|
105
173
|
n_runs += 1
|
106
174
|
check_attempt(generator_result, n_successful, &block)
|
107
175
|
n_successful += 1
|
@@ -113,6 +181,10 @@ module PropCheck
|
|
113
181
|
private def ensure_not_exhausted!(n_runs)
|
114
182
|
return if n_runs >= @config.n_runs
|
115
183
|
|
184
|
+
raise_generator_exhausted!
|
185
|
+
end
|
186
|
+
|
187
|
+
private def raise_generator_exhausted!()
|
116
188
|
raise Errors::GeneratorExhaustedError, """
|
117
189
|
Could not perform `n_runs = #{@config.n_runs}` runs,
|
118
190
|
(exhausted #{@config.max_generate_attempts} tries)
|
@@ -124,7 +196,7 @@ module PropCheck
|
|
124
196
|
end
|
125
197
|
|
126
198
|
private def check_attempt(generator_result, n_successful, &block)
|
127
|
-
|
199
|
+
block.call(*generator_result.root)
|
128
200
|
|
129
201
|
# immediately stop (without shrinnking) for when the app is asked
|
130
202
|
# to close by outside intervention
|
@@ -152,100 +224,39 @@ module PropCheck
|
|
152
224
|
raise e, output_string, e.backtrace
|
153
225
|
end
|
154
226
|
|
155
|
-
private def
|
227
|
+
private def attempts_enum(binding_generator)
|
228
|
+
@hooks
|
229
|
+
.wrap_enum(raw_attempts_enum(binding_generator))
|
230
|
+
.lazy
|
231
|
+
.take(@config.n_runs)
|
232
|
+
end
|
156
233
|
|
234
|
+
private def raw_attempts_enum(binding_generator)
|
157
235
|
rng = Random::DEFAULT
|
158
|
-
n_runs = 0
|
159
236
|
size = 1
|
160
237
|
(0...@config.max_generate_attempts)
|
161
238
|
.lazy
|
162
239
|
.map { binding_generator.generate(size, rng) }
|
163
|
-
.reject { |val| val.root == :"_PropCheck.filter_me" }
|
164
|
-
.select { |val|
|
240
|
+
.reject { |val| val.root.any? { |elem| elem == :"_PropCheck.filter_me" }}
|
241
|
+
.select { |val| @condition.call(*val.root) }
|
165
242
|
.map do |result|
|
166
|
-
n_runs += 1
|
167
243
|
size += 1
|
168
244
|
|
169
245
|
result
|
170
246
|
end
|
171
|
-
.take_while { n_runs <= @config.n_runs }
|
172
247
|
end
|
173
248
|
|
174
249
|
private def show_problem_output(problem, generator_results, n_successful, &block)
|
175
250
|
output = @config.verbose ? STDOUT : StringIO.new
|
176
|
-
output = pre_output(output, n_successful, generator_results.root, problem)
|
251
|
+
output = PropCheck::Property::OutputFormatter.pre_output(output, n_successful, generator_results.root, problem)
|
177
252
|
shrunken_result, shrunken_exception, n_shrink_steps = shrink(generator_results, output, &block)
|
178
|
-
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)
|
179
254
|
|
180
255
|
[output, shrunken_result, shrunken_exception, n_shrink_steps]
|
181
256
|
end
|
182
257
|
|
183
|
-
private def
|
184
|
-
|
185
|
-
output.puts "(after #{n_successful} successful property test runs)"
|
186
|
-
output.puts "Failed on: "
|
187
|
-
output.puts "`#{print_roots(generated_root)}`"
|
188
|
-
output.puts ""
|
189
|
-
output.puts "Exception message:\n---\n#{problem}"
|
190
|
-
output.puts "---"
|
191
|
-
output.puts ""
|
192
|
-
|
193
|
-
output
|
194
|
-
end
|
195
|
-
|
196
|
-
private def post_output(output, n_shrink_steps, shrunken_result, shrunken_exception)
|
197
|
-
output.puts ''
|
198
|
-
output.puts "Shrunken input (after #{n_shrink_steps} shrink steps):"
|
199
|
-
output.puts "`#{print_roots(shrunken_result)}`"
|
200
|
-
output.puts ""
|
201
|
-
output.puts "Shrunken exception:\n---\n#{shrunken_exception}"
|
202
|
-
output.puts "---"
|
203
|
-
output.puts ""
|
204
|
-
|
205
|
-
output
|
206
|
-
end
|
207
|
-
|
208
|
-
private def print_roots(lazy_tree_hash)
|
209
|
-
# lazy_tree_hash.map do |name, val|
|
210
|
-
# "#{name} = #{val.inspect}"
|
211
|
-
# end.join(", ")
|
212
|
-
lazy_tree_hash.ai
|
213
|
-
end
|
214
|
-
|
215
|
-
private def shrink(bindings_tree, io, &fun)
|
216
|
-
io.puts 'Shrinking...' if @config.verbose
|
217
|
-
problem_child = bindings_tree
|
218
|
-
siblings = problem_child.children.lazy
|
219
|
-
parent_siblings = nil
|
220
|
-
problem_exception = nil
|
221
|
-
shrink_steps = 0
|
222
|
-
(0..@config.max_shrink_steps).each do
|
223
|
-
begin
|
224
|
-
sibling = siblings.next
|
225
|
-
rescue StopIteration
|
226
|
-
break if parent_siblings.nil?
|
227
|
-
|
228
|
-
siblings = parent_siblings.lazy
|
229
|
-
parent_siblings = nil
|
230
|
-
next
|
231
|
-
end
|
232
|
-
|
233
|
-
shrink_steps += 1
|
234
|
-
io.print '.' if @config.verbose
|
235
|
-
|
236
|
-
begin
|
237
|
-
CheckEvaluator.new(sibling.root, &fun).call
|
238
|
-
rescue Exception => e
|
239
|
-
problem_child = sibling
|
240
|
-
parent_siblings = siblings
|
241
|
-
siblings = problem_child.children.lazy
|
242
|
-
problem_exception = e
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
|
-
io.puts "(Note: Exceeded #{@config.max_shrink_steps} shrinking steps, the maximum.)" if shrink_steps >= @config.max_shrink_steps
|
247
|
-
|
248
|
-
[problem_child.root, problem_exception, shrink_steps]
|
258
|
+
private def shrink(bindings_tree, io, &block)
|
259
|
+
PropCheck::Property::Shrinker.call(bindings_tree, io, @hooks, @config, &block)
|
249
260
|
end
|
250
261
|
end
|
251
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
data/prop_check.gemspec
CHANGED
@@ -36,7 +36,5 @@ Gem::Specification.new do |spec|
|
|
36
36
|
|
37
37
|
spec.required_ruby_version = '>= 2.5.1'
|
38
38
|
|
39
|
-
spec.
|
40
|
-
spec.add_development_dependency "rake", "~> 12.3"
|
41
|
-
spec.add_development_dependency "rspec", "~> 3.0"
|
39
|
+
spec.add_dependency 'awesome_print', '~> 1.8'
|
42
40
|
end
|
metadata
CHANGED
@@ -1,57 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prop_check
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.2
|
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
|
-
name:
|
14
|
+
name: awesome_print
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
20
|
-
type: :
|
19
|
+
version: '1.8'
|
20
|
+
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: rake
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '12.3'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '12.3'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rspec
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '3.0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '3.0'
|
26
|
+
version: '1.8'
|
55
27
|
description: PropCheck allows you to do property-based testing, including shrinking.
|
56
28
|
(akin to Haskell's QuickCheck, Erlang's PropEr, Elixir's StreamData). This means
|
57
29
|
that your test are run many times with different, autogenerated inputs, and as soon
|
@@ -71,7 +43,6 @@ files:
|
|
71
43
|
- CHANGELOG.md
|
72
44
|
- CODE_OF_CONDUCT.md
|
73
45
|
- Gemfile
|
74
|
-
- Gemfile.lock
|
75
46
|
- LICENSE.txt
|
76
47
|
- README.md
|
77
48
|
- Rakefile
|
@@ -82,11 +53,12 @@ files:
|
|
82
53
|
- lib/prop_check/generators.rb
|
83
54
|
- lib/prop_check/helper.rb
|
84
55
|
- lib/prop_check/helper/lazy_append.rb
|
56
|
+
- lib/prop_check/hooks.rb
|
85
57
|
- lib/prop_check/lazy_tree.rb
|
86
58
|
- lib/prop_check/property.rb
|
87
|
-
- lib/prop_check/property/check_evaluator.rb
|
88
59
|
- lib/prop_check/property/configuration.rb
|
89
|
-
- lib/prop_check/
|
60
|
+
- lib/prop_check/property/output_formatter.rb
|
61
|
+
- lib/prop_check/property/shrinker.rb
|
90
62
|
- lib/prop_check/version.rb
|
91
63
|
- prop_check.gemspec
|
92
64
|
homepage: https://github.com/Qqwy/ruby-prop_check/
|
data/Gemfile.lock
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
prop_check (0.7.0)
|
5
|
-
|
6
|
-
GEM
|
7
|
-
remote: https://rubygems.org/
|
8
|
-
specs:
|
9
|
-
awesome_print (1.8.0)
|
10
|
-
diff-lcs (1.3)
|
11
|
-
docile (1.3.2)
|
12
|
-
doctest-core (0.0.2)
|
13
|
-
doctest-rspec (0.0.3)
|
14
|
-
doctest-core (~> 0.0.2)
|
15
|
-
rspec
|
16
|
-
json (2.2.0)
|
17
|
-
rake (12.3.3)
|
18
|
-
rspec (3.8.0)
|
19
|
-
rspec-core (~> 3.8.0)
|
20
|
-
rspec-expectations (~> 3.8.0)
|
21
|
-
rspec-mocks (~> 3.8.0)
|
22
|
-
rspec-core (3.8.1)
|
23
|
-
rspec-support (~> 3.8.0)
|
24
|
-
rspec-expectations (3.8.4)
|
25
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
26
|
-
rspec-support (~> 3.8.0)
|
27
|
-
rspec-mocks (3.8.1)
|
28
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
29
|
-
rspec-support (~> 3.8.0)
|
30
|
-
rspec-support (3.8.2)
|
31
|
-
simplecov (0.16.1)
|
32
|
-
docile (~> 1.1)
|
33
|
-
json (>= 1.8, < 3)
|
34
|
-
simplecov-html (~> 0.10.0)
|
35
|
-
simplecov-html (0.10.2)
|
36
|
-
|
37
|
-
PLATFORMS
|
38
|
-
ruby
|
39
|
-
|
40
|
-
DEPENDENCIES
|
41
|
-
awesome_print
|
42
|
-
bundler (~> 2.0)
|
43
|
-
doctest-rspec
|
44
|
-
prop_check!
|
45
|
-
rake (~> 12.3)
|
46
|
-
rspec (~> 3.0)
|
47
|
-
simplecov
|
48
|
-
|
49
|
-
BUNDLED WITH
|
50
|
-
2.1.4
|
@@ -1,45 +0,0 @@
|
|
1
|
-
module PropCheck
|
2
|
-
class Property
|
3
|
-
##
|
4
|
-
# A wrapper class that implements the 'Cloaker' concept
|
5
|
-
# which allows us to refer to variables set in 'bindings',
|
6
|
-
# while still being able to access things that are only in scope
|
7
|
-
# in the creator of '&block'.
|
8
|
-
#
|
9
|
-
# This allows us to bind the variables specified in `bindings`
|
10
|
-
# one way during checking and another way during shrinking.
|
11
|
-
class CheckEvaluator
|
12
|
-
include RSpec::Matchers if Object.const_defined?('RSpec')
|
13
|
-
|
14
|
-
def initialize(bindings, &block)
|
15
|
-
@caller = block.binding.receiver
|
16
|
-
@block = block
|
17
|
-
define_named_instance_methods(bindings)
|
18
|
-
end
|
19
|
-
|
20
|
-
def call
|
21
|
-
self.instance_exec(&@block)
|
22
|
-
end
|
23
|
-
|
24
|
-
private def define_named_instance_methods(results)
|
25
|
-
results.each do |name, result|
|
26
|
-
define_singleton_method(name) { result }
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
##
|
31
|
-
# Dispatches to caller whenever something is not part of `bindings`.
|
32
|
-
# (No need to invoke this method manually)
|
33
|
-
def method_missing(method, *args, &block)
|
34
|
-
@caller.__send__(method, *args, &block) || super
|
35
|
-
end
|
36
|
-
|
37
|
-
##
|
38
|
-
# Checks respond_to of caller whenever something is not part of `bindings`.
|
39
|
-
# (No need to invoke this method manually)
|
40
|
-
def respond_to_missing?(*args)
|
41
|
-
@caller.respond_to?(*args) || super
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
data/lib/prop_check/rspec.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
module PropCheck
|
2
|
-
##
|
3
|
-
# Integration with RSpec
|
4
|
-
module RSpec
|
5
|
-
# To make it available within examples
|
6
|
-
def self.extend_object(obj)
|
7
|
-
obj.define_method(:forall) do |*args, **kwargs, &block|
|
8
|
-
if block_given?
|
9
|
-
PropCheck::Property.forall(*args, **kwargs) do
|
10
|
-
instance_exec(self, &block)
|
11
|
-
end
|
12
|
-
else
|
13
|
-
PropCheck::Property.forall(*args, **kwargs)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|