prop_check 0.6.1 → 0.9.0
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/.travis.yml +12 -1
- data/CHANGELOG.md +1 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +6 -4
- data/README.md +75 -15
- data/lib/prop_check.rb +23 -4
- data/lib/prop_check/generator.rb +20 -1
- data/lib/prop_check/generators.rb +87 -15
- data/lib/prop_check/helper/lazy_append.rb +18 -0
- data/lib/prop_check/lazy_tree.rb +8 -15
- data/lib/prop_check/property.rb +109 -31
- data/lib/prop_check/version.rb +1 -1
- data/prop_check.gemspec +1 -1
- metadata +9 -11
- data/lib/prop_check/property/check_evaluator.rb +0 -45
- data/lib/prop_check/rspec.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8e5aeef61ddf82569ca885327d32b9fe8fd939f2fdbef48709cb19504b5cf041
|
4
|
+
data.tar.gz: d914dcac32f7a3a976661a0280c71f0a7a665411550aed9902317b04c82ee290
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85c5b76ac37ba8e03b2c41c04c4cc359844fa2e52d0af8893787531c23f3a54b046eb7eed13b055fed44c8983f87cbfe492d1524746624a3744300a73caf01da
|
7
|
+
data.tar.gz: 9beb7ac4e605c72cc8e9b3907e727ae532c2eec4a530b6a9b7c31445135e37c8849c9f186d4571aaf38c24e28989ebe856ed2e5e873baf2ed7b740a73561dae7
|
data/.tool-versions
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby 2.5
|
1
|
+
ruby 2.6.5
|
data/.travis.yml
CHANGED
@@ -4,4 +4,15 @@ language: ruby
|
|
4
4
|
cache: bundler
|
5
5
|
rvm:
|
6
6
|
- 2.5.1
|
7
|
-
before_install: gem install bundler -v 2.0.
|
7
|
+
before_install: gem install bundler -v 2.0.2
|
8
|
+
env:
|
9
|
+
global:
|
10
|
+
- CC_TEST_REPORTER_ID=9d18f5b43e49eecd6c3da64d85ea9c765d3606c129289d7c8cadf6d448713311
|
11
|
+
before_script:
|
12
|
+
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
13
|
+
- chmod +x ./cc-test-reporter
|
14
|
+
- ./cc-test-reporter before-build
|
15
|
+
script:
|
16
|
+
- bundle exec rspec
|
17
|
+
after_script:
|
18
|
+
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
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
data/Gemfile.lock
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
prop_check (0.
|
4
|
+
prop_check (0.9.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
+
awesome_print (1.8.0)
|
9
10
|
diff-lcs (1.3)
|
10
11
|
docile (1.3.2)
|
11
12
|
doctest-core (0.0.2)
|
@@ -13,7 +14,7 @@ GEM
|
|
13
14
|
doctest-core (~> 0.0.2)
|
14
15
|
rspec
|
15
16
|
json (2.2.0)
|
16
|
-
rake (
|
17
|
+
rake (12.3.3)
|
17
18
|
rspec (3.8.0)
|
18
19
|
rspec-core (~> 3.8.0)
|
19
20
|
rspec-expectations (~> 3.8.0)
|
@@ -37,12 +38,13 @@ PLATFORMS
|
|
37
38
|
ruby
|
38
39
|
|
39
40
|
DEPENDENCIES
|
41
|
+
awesome_print
|
40
42
|
bundler (~> 2.0)
|
41
43
|
doctest-rspec
|
42
44
|
prop_check!
|
43
|
-
rake (~>
|
45
|
+
rake (~> 12.3)
|
44
46
|
rspec (~> 3.0)
|
45
47
|
simplecov
|
46
48
|
|
47
49
|
BUNDLED WITH
|
48
|
-
2.
|
50
|
+
2.1.4
|
data/README.md
CHANGED
@@ -2,6 +2,11 @@
|
|
2
2
|
|
3
3
|
PropCheck allows you to do Property Testing in Ruby.
|
4
4
|
|
5
|
+
[![Gem](https://img.shields.io/gem/v/prop_check.svg)](https://rubygems.org/gems/prop_check)
|
6
|
+
[![Build Status](https://travis-ci.org/Qqwy/ruby-prop_check.svg?branch=master)](https://travis-ci.org/Qqwy/ruby-prop_check)
|
7
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/71897f5e6193a5124a53/maintainability)](https://codeclimate.com/github/Qqwy/ruby-prop_check/maintainability)
|
8
|
+
[![RubyDoc](https://img.shields.io/badge/%F0%9F%93%9ARubyDoc-documentation-informational.svg)](https://www.rubydoc.info/github/Qqwy/ruby-prop_check/master/PropCheck)
|
9
|
+
|
5
10
|
It features:
|
6
11
|
|
7
12
|
- Generators for common datatypes.
|
@@ -9,7 +14,7 @@ It features:
|
|
9
14
|
- Shrinking to a minimal counter-example on failure.
|
10
15
|
|
11
16
|
|
12
|
-
## TODOs before release
|
17
|
+
## TODOs before stable release
|
13
18
|
|
14
19
|
Before releasing this gem on Rubygems, the following things need to be finished:
|
15
20
|
|
@@ -21,11 +26,11 @@ Before releasing this gem on Rubygems, the following things need to be finished:
|
|
21
26
|
- [x] Stop after a ludicrous amount of generator runs, to prevent malfunctioning (infinitely looping) generators from blowing up someone's computer.
|
22
27
|
- [x] Look into customization of settings from e.g. command line arguments.
|
23
28
|
- [x] Good, unicode-compliant, string generators.
|
24
|
-
- [
|
29
|
+
- [x] Filtering generator outputs.
|
25
30
|
|
26
31
|
# Nice-to-haves
|
27
32
|
|
28
|
-
- [
|
33
|
+
- [x] Basic integration with RSpec. See also https://groups.google.com/forum/#!msg/rspec/U-LmL0OnO-Y/iW_Jcd6JBAAJ for progress on this.
|
29
34
|
- [ ] `aggregate` , `resize` and similar generator-modifying calls (c.f. PropEr's variants of these) which will help with introspection/metrics.
|
30
35
|
- [ ] Integration with other Ruby test frameworks.
|
31
36
|
- Stateful property testing. If implemented at some point, will probably happen in a separate add-on library.
|
@@ -61,8 +66,9 @@ _(to be precise: a method on the execution context is defined which returns the
|
|
61
66
|
Raise an exception from the block if there is a problem. If there is no problem, just return normally.
|
62
67
|
|
63
68
|
```ruby
|
69
|
+
include PropCheck::Generators
|
64
70
|
# testing that Enumerable#sort sorts in ascending order
|
65
|
-
PropCheck.forall(
|
71
|
+
PropCheck.forall(array(integer)) do |numbers|
|
66
72
|
sorted_numbers = numbers.sort
|
67
73
|
|
68
74
|
# Check that no number is smaller than the previous number
|
@@ -72,6 +78,50 @@ PropCheck.forall(numbers: array(integer())) do
|
|
72
78
|
end
|
73
79
|
```
|
74
80
|
|
81
|
+
|
82
|
+
Here is another example, using it inside a test case.
|
83
|
+
Here we check if `naive_average` indeed always returns an integer for all arrays of numbers we can pass it:
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
# Somewhere you have this function definition:
|
87
|
+
def naive_average(array)
|
88
|
+
array.sum / array.length
|
89
|
+
end
|
90
|
+
|
91
|
+
# And then in a test case:
|
92
|
+
include PropCheck::Generators
|
93
|
+
PropCheck.forall(numbers: array(integer)) do |numbers:|
|
94
|
+
result = naive_average(numbers)
|
95
|
+
unless result.is_a?(Integer) do
|
96
|
+
raise "Expected the average to be an integer!"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
When running this particular example PropCheck very quickly finds out that we have made a programming mistake:
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
ZeroDivisionError:
|
105
|
+
(after 6 successful property test runs)
|
106
|
+
Failed on:
|
107
|
+
`{
|
108
|
+
:numbers => []
|
109
|
+
}`
|
110
|
+
|
111
|
+
Exception message:
|
112
|
+
---
|
113
|
+
divided by 0
|
114
|
+
---
|
115
|
+
|
116
|
+
(shrinking impossible)
|
117
|
+
---
|
118
|
+
```
|
119
|
+
|
120
|
+
Clearly we forgot to handle the case of an empty array being passed to the function.
|
121
|
+
This is a good example of the kind of conceptual bugs that PropCheck (and property-based testing in general)
|
122
|
+
are able to check for.
|
123
|
+
|
124
|
+
|
75
125
|
#### Shrinking
|
76
126
|
|
77
127
|
When a failure is found, PropCheck will re-run the block given to `forall` to test
|
@@ -83,8 +133,8 @@ PropCheck will see if the failure still happens with `x = 50`.
|
|
83
133
|
If it does , it will try `x = 25`. If not, it will try `x = 75`, and so on.
|
84
134
|
|
85
135
|
This means if something only goes wrong for `x = 2`, the program will try:
|
86
|
-
- `x = 100`(fails)
|
87
|
-
- x = 50`(fails),
|
136
|
+
- `x = 100`(fails),
|
137
|
+
- `x = 50`(fails),
|
88
138
|
- `x = 25`(fails),
|
89
139
|
- `x = 12`(fails),
|
90
140
|
- `x = 6`(fails),
|
@@ -101,21 +151,28 @@ A short summary:
|
|
101
151
|
- Arrays and hashes shrink to fewer elements, as well as shrinking their elements.
|
102
152
|
- Strings shrink to shorter strings, as well as characters earlier in their alphabet.
|
103
153
|
|
154
|
+
### Builtin Generators
|
155
|
+
|
156
|
+
PropCheck comes with [many builtin generators in the PropCheck::Generators](https://www.rubydoc.info/github/Qqwy/ruby-prop_check/master/PropCheck/Generators) module.
|
157
|
+
|
158
|
+
It contains generators for:
|
159
|
+
- (any, positive, negative, etc.) integers,
|
160
|
+
- (any, only real-valued) floats,
|
161
|
+
- (any, printable only, alphanumeric only, etc) strings and symbols
|
162
|
+
- fixed-size arrays and hashes
|
163
|
+
- as well as varying-size arrays and hashes.
|
164
|
+
- and many more!
|
165
|
+
|
166
|
+
It is common to call `include PropCheck::Generators` in e.g. your testing-suite files to be able to use these.
|
167
|
+
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.
|
104
168
|
|
105
169
|
### Writing Custom Generators
|
106
170
|
|
107
|
-
PropCheck comes bundled with a bunch of common generators
|
108
|
-
- integers
|
109
|
-
- floats
|
110
|
-
- strings
|
111
|
-
- symbols
|
112
|
-
- arrays
|
113
|
-
- hashes
|
114
|
-
etc.
|
171
|
+
As described in the previous section, PropCheck already comes bundled with a bunch of common generators.
|
115
172
|
|
116
173
|
However, you can easily adapt them to generate your own datatypes:
|
117
174
|
|
118
|
-
#### Generator#wrap
|
175
|
+
#### Generators#constant / Generator#wrap
|
119
176
|
|
120
177
|
Always returns the given value. No shrinking.
|
121
178
|
|
@@ -157,6 +214,9 @@ you can use `Generators.frequency` which takes a hash of (integer_frequency => g
|
|
157
214
|
There are even more functions in the `Generator` class and the `Generators` module that you might want to use,
|
158
215
|
although above are the most generally useful ones.
|
159
216
|
|
217
|
+
[PropCheck::Generator documentation](https://www.rubydoc.info/github/Qqwy/ruby-prop_check/master/PropCheck/Generator)
|
218
|
+
[PropCheck::Generators documentation](https://www.rubydoc.info/github/Qqwy/ruby-prop_check/master/PropCheck/Generators)
|
219
|
+
|
160
220
|
## Development
|
161
221
|
|
162
222
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/lib/prop_check.rb
CHANGED
@@ -3,14 +3,33 @@ require 'prop_check/property'
|
|
3
3
|
require 'prop_check/generator'
|
4
4
|
require 'prop_check/generators'
|
5
5
|
require 'prop_check/helper'
|
6
|
+
##
|
7
|
+
# Main module of the PropCheck library.
|
8
|
+
#
|
9
|
+
# You probably want to look at the documentation of
|
10
|
+
# PropCheck::Generator and PropCheck::Generators
|
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
|
+
#
|
6
19
|
module PropCheck
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
20
|
+
module Errors
|
21
|
+
class Error < StandardError; end
|
22
|
+
class UserError < Error; end
|
23
|
+
class GeneratorExhaustedError < UserError; end
|
24
|
+
class MaxShrinkStepsExceededError < UserError; end
|
25
|
+
end
|
11
26
|
|
12
27
|
extend self
|
13
28
|
|
29
|
+
##
|
30
|
+
# Runs a property.
|
31
|
+
#
|
32
|
+
# See the README for more details.
|
14
33
|
def forall(*args, **kwargs, &block)
|
15
34
|
PropCheck::Property.forall(*args, **kwargs, &block)
|
16
35
|
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
|
@@ -1,3 +1,6 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
1
4
|
require 'prop_check/generator'
|
2
5
|
require 'prop_check/lazy_tree'
|
3
6
|
module PropCheck
|
@@ -116,27 +119,40 @@ module PropCheck
|
|
116
119
|
end
|
117
120
|
|
118
121
|
##
|
119
|
-
# Generates floating
|
122
|
+
# Generates floating-point numbers
|
120
123
|
# These start small (around 0)
|
121
124
|
# and become more extreme (large positive and large negative numbers)
|
122
125
|
#
|
126
|
+
# Will only generate 'reals',
|
127
|
+
# that is: no infinity, no NaN,
|
128
|
+
# no numbers testing the limits of floating-point arithmetic.
|
123
129
|
#
|
124
130
|
# Shrinks to numbers closer to zero.
|
125
131
|
#
|
126
|
-
#
|
127
|
-
|
128
|
-
|
129
|
-
# integer.bind do |b|
|
130
|
-
# integer.bind do |c|
|
131
|
-
# Generator.wrap(fraction(a, b, c))
|
132
|
-
# end
|
133
|
-
# end
|
134
|
-
# end
|
132
|
+
# >> Generators.real_float().sample(10, size: 10, rng: Random.new(42))
|
133
|
+
# => [-2.2, -0.2727272727272727, 4.0, 1.25, -3.7272727272727275, -8.833333333333334, -8.090909090909092, 1.1428571428571428, 0.0, 8.0]
|
134
|
+
def real_float
|
135
135
|
tuple(integer, integer, integer).map do |a, b, c|
|
136
136
|
fraction(a, b, c)
|
137
137
|
end
|
138
138
|
end
|
139
139
|
|
140
|
+
@special_floats = [Float::NAN, Float::INFINITY, -Float::INFINITY, Float::MAX, Float::MIN, 0.0.next_float, 0.0.prev_float]
|
141
|
+
##
|
142
|
+
# Generates floating-point numbers
|
143
|
+
# Will generate NaN, Infinity, -Infinity,
|
144
|
+
# as well as Float::EPSILON, Float::MAX, Float::MIN,
|
145
|
+
# 0.0.next_float, 0.0.prev_float,
|
146
|
+
# to test the handling of floating-point edge cases.
|
147
|
+
# Approx. 1/100 generated numbers is a special one.
|
148
|
+
#
|
149
|
+
# Shrinks to smaller, real floats.
|
150
|
+
# >> Generators.float().sample(10, size: 10, rng: Random.new(42))
|
151
|
+
# => [4.0, 9.555555555555555, 0.0, -Float::INFINITY, 5.5, -5.818181818181818, 1.1428571428571428, 0.0, 8.0, 7.857142857142858]
|
152
|
+
def float
|
153
|
+
frequency(99 => real_float, 1 => one_of(*@special_floats.map(&method(:constant))))
|
154
|
+
end
|
155
|
+
|
140
156
|
##
|
141
157
|
# Picks one of the given generators in `choices` at random uniformly every time.
|
142
158
|
#
|
@@ -156,6 +172,9 @@ module PropCheck
|
|
156
172
|
# (representing the relative frequency of this generator)
|
157
173
|
# and values to be generators.
|
158
174
|
#
|
175
|
+
# Side note: If you want to use the same frequency number for multiple generators,
|
176
|
+
# Ruby syntax requires you to send an array of two-element arrays instead of a hash.
|
177
|
+
#
|
159
178
|
# Shrinks to arbitrary elements (since hashes are not ordered).
|
160
179
|
#
|
161
180
|
# >> Generators.frequency(5 => Generators.integer, 1 => Generators.printable_ascii_char).sample(size: 10, rng: Random.new(42))
|
@@ -174,7 +193,7 @@ module PropCheck
|
|
174
193
|
#
|
175
194
|
# Shrinks element generators, one at a time (trying last one first).
|
176
195
|
#
|
177
|
-
# >> Generators.tuple(Generators.integer, Generators.
|
196
|
+
# >> Generators.tuple(Generators.integer, Generators.real_float).call(10, Random.new(42))
|
178
197
|
# => [-4, 13.0]
|
179
198
|
def tuple(*generators)
|
180
199
|
Generator.new do |size, rng|
|
@@ -191,7 +210,7 @@ module PropCheck
|
|
191
210
|
#
|
192
211
|
# Shrinks element generators.
|
193
212
|
#
|
194
|
-
# >> Generators.fixed_hash(a: Generators.integer(), b: Generators.
|
213
|
+
# >> Generators.fixed_hash(a: Generators.integer(), b: Generators.real_float(), c: Generators.integer()).call(10, Random.new(42))
|
195
214
|
# => {:a=>-4, :b=>13.0, :c=>-3}
|
196
215
|
def fixed_hash(hash)
|
197
216
|
keypair_generators =
|
@@ -242,7 +261,9 @@ module PropCheck
|
|
242
261
|
# containing one of a..z, A..Z, 0..9
|
243
262
|
#
|
244
263
|
# Shrinks towards lowercase 'a'.
|
245
|
-
#
|
264
|
+
#
|
265
|
+
# >> Generators.alphanumeric_char.sample(5, size: 10, rng: Random.new(42))
|
266
|
+
# => ["M", "Z", "C", "o", "Q"]
|
246
267
|
def alphanumeric_char
|
247
268
|
one_of(*@alphanumeric_chars.map(&method(:constant)))
|
248
269
|
end
|
@@ -250,7 +271,11 @@ module PropCheck
|
|
250
271
|
##
|
251
272
|
# Generates a string
|
252
273
|
# containing only the characters a..z, A..Z, 0..9
|
274
|
+
#
|
253
275
|
# Shrinks towards fewer characters, and towards lowercase 'a'.
|
276
|
+
#
|
277
|
+
# >> Generators.alphanumeric_string.sample(5, size: 10, rng: Random.new(42))
|
278
|
+
# => ["ZCoQ", "8uM", "wkkx0JNx", "v0bxRDLb", "Gl5v8RyWA6"]
|
254
279
|
def alphanumeric_string
|
255
280
|
array(alphanumeric_char).map(&:join)
|
256
281
|
end
|
@@ -272,7 +297,11 @@ module PropCheck
|
|
272
297
|
##
|
273
298
|
# Generates strings
|
274
299
|
# from the printable ASCII character set.
|
300
|
+
#
|
275
301
|
# Shrinks towards fewer characters, and towards ' '.
|
302
|
+
#
|
303
|
+
# >> Generators.printable_ascii_string.sample(5, size: 10, rng: Random.new(42))
|
304
|
+
# => ["S|.g", "rvjjw7\"5T!", "=", "!_[4@", "Y"]
|
276
305
|
def printable_ascii_string
|
277
306
|
array(printable_ascii_char).map(&:join)
|
278
307
|
end
|
@@ -295,7 +324,11 @@ module PropCheck
|
|
295
324
|
##
|
296
325
|
# Generates a single-character string
|
297
326
|
# from the printable ASCII character set.
|
327
|
+
#
|
298
328
|
# Shrinks towards '\n'.
|
329
|
+
#
|
330
|
+
# >> Generators.ascii_char.sample(size: 10, rng: Random.new(42))
|
331
|
+
# => ["d", "S", "|", ".", "g", "\\", "4", "d", "r", "v"]
|
299
332
|
def ascii_char
|
300
333
|
one_of(*@ascii_chars.map(&method(:constant)))
|
301
334
|
end
|
@@ -303,7 +336,11 @@ module PropCheck
|
|
303
336
|
##
|
304
337
|
# Generates strings
|
305
338
|
# from the printable ASCII character set.
|
339
|
+
#
|
306
340
|
# Shrinks towards fewer characters, and towards '\n'.
|
341
|
+
#
|
342
|
+
# >> Generators.ascii_string.sample(5, size: 10, rng: Random.new(42))
|
343
|
+
# => ["S|.g", "drvjjw\b\a7\"", "!w=E!_[4@k", "x", "zZI{[o"]
|
307
344
|
def ascii_string
|
308
345
|
array(ascii_char).map(&:join)
|
309
346
|
end
|
@@ -321,6 +358,8 @@ module PropCheck
|
|
321
358
|
#
|
322
359
|
# Shrinks towards characters with lower codepoints, e.g. ASCII
|
323
360
|
#
|
361
|
+
# >> Generators.printable_char.sample(size: 10, rng: Random.new(42))
|
362
|
+
# => ["吏", "", "", "", "", "", "", "", "", "Ȍ"]
|
324
363
|
def printable_char
|
325
364
|
one_of(*@printable_chars.map(&method(:constant)))
|
326
365
|
end
|
@@ -331,6 +370,8 @@ module PropCheck
|
|
331
370
|
#
|
332
371
|
# Shrinks towards shorter strings, and towards characters with lower codepoints, e.g. ASCII
|
333
372
|
#
|
373
|
+
# >> Generators.printable_string.sample(5, size: 10, rng: Random.new(42))
|
374
|
+
# => ["", "Ȍ", "𐁂", "Ȕ", ""]
|
334
375
|
def printable_string
|
335
376
|
array(printable_char).map(&:join)
|
336
377
|
end
|
@@ -341,6 +382,8 @@ module PropCheck
|
|
341
382
|
#
|
342
383
|
# Shrinks towards characters with lower codepoints, e.g. ASCII
|
343
384
|
#
|
385
|
+
# >> Generators.printable_char.sample(size: 10, rng: Random.new(42))
|
386
|
+
# => ["吏", "", "", "", "", "", "", "", "", "Ȍ"]
|
344
387
|
def char
|
345
388
|
choose(0..0x10FFFF).map do |num|
|
346
389
|
[num].pack('U')
|
@@ -353,6 +396,8 @@ module PropCheck
|
|
353
396
|
#
|
354
397
|
# Shrinks towards characters with lower codepoints, e.g. ASCII
|
355
398
|
#
|
399
|
+
# >> Generators.string.sample(5, size: 10, rng: Random.new(42))
|
400
|
+
# => ["\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}"]
|
356
401
|
def string
|
357
402
|
array(char).map(&:join)
|
358
403
|
end
|
@@ -361,7 +406,9 @@ module PropCheck
|
|
361
406
|
# Generates either `true` or `false`
|
362
407
|
#
|
363
408
|
# Shrinks towards `false`
|
364
|
-
#
|
409
|
+
#
|
410
|
+
# >> Generators.boolean.sample(5, size: 10, rng: Random.new(42))
|
411
|
+
# => [false, true, false, false, false]
|
365
412
|
def boolean
|
366
413
|
one_of(constant(false), constant(true))
|
367
414
|
end
|
@@ -370,6 +417,9 @@ module PropCheck
|
|
370
417
|
# Generates always `nil`.
|
371
418
|
#
|
372
419
|
# Does not shrink.
|
420
|
+
#
|
421
|
+
# >> Generators.nil.sample(5, size: 10, rng: Random.new(42))
|
422
|
+
# => [nil, nil, nil, nil, nil]
|
373
423
|
def nil
|
374
424
|
constant(nil)
|
375
425
|
end
|
@@ -379,15 +429,34 @@ module PropCheck
|
|
379
429
|
#
|
380
430
|
# Shrinks towards `nil`.
|
381
431
|
#
|
432
|
+
# >> Generators.falsey.sample(5, size: 10, rng: Random.new(42))
|
433
|
+
# => [nil, false, nil, nil, nil]
|
382
434
|
def falsey
|
383
435
|
one_of(constant(nil), constant(false))
|
384
436
|
end
|
385
437
|
|
438
|
+
##
|
439
|
+
# Generates symbols consisting of lowercase letters and potentially underscores.
|
440
|
+
#
|
441
|
+
# Shrinks towards shorter symbols and the letter 'a'.
|
442
|
+
#
|
443
|
+
# >> Generators.simple_symbol.sample(5, size: 10, rng: Random.new(42))
|
444
|
+
# => [:tokh, :gzswkkxudh, :vubxlfbu, :lzvlyq__jp, :oslw]
|
445
|
+
def simple_symbol
|
446
|
+
alphabet = ('a'..'z').to_a
|
447
|
+
alphabet << '_'
|
448
|
+
array(one_of(*alphabet.map(&method(:constant))))
|
449
|
+
.map(&:join)
|
450
|
+
.map(&:to_sym)
|
451
|
+
end
|
452
|
+
|
386
453
|
##
|
387
454
|
# Generates common terms that are not `nil` or `false`.
|
388
455
|
#
|
389
456
|
# Shrinks towards simpler terms, like `true`, an empty array, a single character or an integer.
|
390
457
|
#
|
458
|
+
# >> Generators.truthy.sample(5, size: 10, rng: Random.new(42))
|
459
|
+
# => [[4, 0, -3, 10, -4, 8, 0, 0, 10], -3, [5.5, -5.818181818181818, 1.1428571428571428, 0.0, 8.0, 7.857142857142858, -0.6666666666666665, 5.25], [], ["\u{9E553}\u{DD56E}\u{A5BBB}\u{8BDAB}\u{3E9FC}\u{C4307}\u{DAFAE}\u{1A022}\u{938CD}\u{70631}", "\u{C4C01}\u{32D85}\u{425DC}"]]
|
391
460
|
def truthy
|
392
461
|
one_of(constant(true),
|
393
462
|
constant([]),
|
@@ -399,7 +468,7 @@ module PropCheck
|
|
399
468
|
array(float),
|
400
469
|
array(char),
|
401
470
|
array(string),
|
402
|
-
hash(
|
471
|
+
hash(simple_symbol, integer),
|
403
472
|
hash(string, integer),
|
404
473
|
hash(string, string)
|
405
474
|
)
|
@@ -408,6 +477,9 @@ module PropCheck
|
|
408
477
|
##
|
409
478
|
# Generates whatever `other_generator` generates
|
410
479
|
# but sometimes instead `nil`.`
|
480
|
+
#
|
481
|
+
# >> Generators.nillable(Generators.integer).sample(20, size: 10, rng: Random.new(42))
|
482
|
+
# => [9, 10, 8, 0, 10, -3, -8, 10, 1, -9, -10, nil, 1, 6, nil, 1, 9, -8, 8, 10]
|
411
483
|
def nillable(other_generator)
|
412
484
|
frequency(9 => other_generator, 1 => constant(nil))
|
413
485
|
end
|
@@ -0,0 +1,18 @@
|
|
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/lazy_tree.rb
CHANGED
@@ -1,22 +1,13 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
module LazyAppend
|
5
|
-
refine Enumerable do
|
6
|
-
## >> [1,2,3].lazy_append([4,5.6]).to_a
|
7
|
-
## => [1,2,3,4,5,6]
|
8
|
-
def lazy_append(other_enumerator)
|
9
|
-
[self, other_enumerator].lazy.flat_map(&:lazy)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'prop_check/helper/lazy_append'
|
13
4
|
|
14
5
|
module PropCheck
|
15
6
|
##
|
16
7
|
# A Rose tree with the root being eager,
|
17
8
|
# and the children computed lazily, on demand.
|
18
9
|
class LazyTree
|
19
|
-
using LazyAppend
|
10
|
+
using PropCheck::Helper::LazyAppend
|
20
11
|
|
21
12
|
attr_accessor :root, :children
|
22
13
|
def initialize(root, children = [].lazy)
|
@@ -82,7 +73,8 @@ module PropCheck
|
|
82
73
|
[tree.root].lazy_append(new_children)
|
83
74
|
end
|
84
75
|
|
85
|
-
squish
|
76
|
+
squish
|
77
|
+
.call(self, [])
|
86
78
|
|
87
79
|
# base = [root]
|
88
80
|
# recursive = children.map(&:each)
|
@@ -108,7 +100,8 @@ module PropCheck
|
|
108
100
|
# >> LazyTree.new(1, [LazyTree.new(2, [LazyTree.new(3)]), LazyTree.new(4)]).to_a
|
109
101
|
# => [1, 4, 2, 3]
|
110
102
|
def to_a
|
111
|
-
each
|
103
|
+
each
|
104
|
+
.force
|
112
105
|
end
|
113
106
|
|
114
107
|
# TODO: fix implementation
|
data/lib/prop_check/property.rb
CHANGED
@@ -1,41 +1,90 @@
|
|
1
1
|
require 'stringio'
|
2
|
+
require "awesome_print"
|
2
3
|
|
3
4
|
require 'prop_check/property/configuration'
|
4
|
-
require 'prop_check/property/check_evaluator'
|
5
5
|
module PropCheck
|
6
|
+
##
|
7
|
+
# Run properties
|
6
8
|
class Property
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
|
10
|
+
##
|
11
|
+
# Main entry-point to create (and possibly immediately run) a property-test.
|
12
|
+
#
|
13
|
+
# This method accepts a list of generators and a block.
|
14
|
+
# The block will then be executed many times, passing the values generated by the generators
|
15
|
+
# as respective arguments:
|
16
|
+
#
|
17
|
+
# ```
|
18
|
+
# include PropCheck::Generators
|
19
|
+
# PropCheck.forall(integer(), float()) { |x, y| ... }
|
20
|
+
# ```
|
21
|
+
#
|
22
|
+
# It is also possible (and recommended when having more than a few generators) to use a keyword-list
|
23
|
+
# of generators instead:
|
24
|
+
#
|
25
|
+
# ```
|
26
|
+
# include PropCheck::Generators
|
27
|
+
# PropCheck.forall(x: integer(), y: float()) { |x:, y:| ... }
|
28
|
+
# ```
|
29
|
+
#
|
30
|
+
#
|
31
|
+
# If you do not pass a block right away,
|
32
|
+
# a Property object is returned, which you can call the other instance methods
|
33
|
+
# of this class on before finally passing a block to it using `#check`.
|
34
|
+
# (so `forall(Generators.integer) do |val| ... end` and forall(Generators.integer).check do |val| ... end` are the same)
|
35
|
+
def self.forall(*bindings, &block)
|
36
|
+
|
37
|
+
property = new(*bindings)
|
11
38
|
|
12
39
|
return property.check(&block) if block_given?
|
13
40
|
|
14
41
|
property
|
15
42
|
end
|
16
43
|
|
44
|
+
##
|
45
|
+
# Returns the default configuration of the library as it is configured right now
|
46
|
+
# for introspection.
|
47
|
+
#
|
48
|
+
# For the configuration of a single property, check its `configuration` instance method.
|
49
|
+
# See PropCheck::Property::Configuration for more info on available settings.
|
17
50
|
def self.configuration
|
18
51
|
@configuration ||= Configuration.new
|
19
52
|
end
|
20
53
|
|
54
|
+
##
|
55
|
+
# Yields the library's configuration object for you to alter.
|
56
|
+
# See PropCheck::Property::Configuration for more info on available settings.
|
21
57
|
def self.configure
|
22
58
|
yield(configuration)
|
23
59
|
end
|
24
60
|
|
25
61
|
attr_reader :bindings, :condition
|
26
62
|
|
27
|
-
def initialize(**
|
28
|
-
raise ArgumentError, 'No bindings specified!' if bindings.empty?
|
63
|
+
def initialize(*bindings, **kwbindings)
|
64
|
+
raise ArgumentError, 'No bindings specified!' if bindings.empty? && kwbindings.empty?
|
29
65
|
|
30
66
|
@bindings = bindings
|
31
|
-
@
|
67
|
+
@kwbindings = kwbindings
|
68
|
+
@condition = proc { true }
|
32
69
|
@config = self.class.configuration
|
33
70
|
end
|
34
71
|
|
72
|
+
##
|
73
|
+
# Returns the configuration of this property
|
74
|
+
# for introspection.
|
75
|
+
#
|
76
|
+
# See PropCheck::Property::Configuration for more info on available settings.
|
35
77
|
def configuration
|
36
78
|
@config
|
37
79
|
end
|
38
80
|
|
81
|
+
##
|
82
|
+
# Allows you to override the configuration of this property
|
83
|
+
# by giving a hash with new settings.
|
84
|
+
#
|
85
|
+
# If no other changes need to occur before you want to check the property,
|
86
|
+
# you can immediately pass a block to this method.
|
87
|
+
# (so `forall(a: Generators.integer).with_config(verbose: true) do ... end` is the same as `forall(a: Generators.integer).with_config(verbose: true).check do ... end`)
|
39
88
|
def with_config(**config, &block)
|
40
89
|
@config = @config.merge(config)
|
41
90
|
|
@@ -44,15 +93,35 @@ module PropCheck
|
|
44
93
|
self
|
45
94
|
end
|
46
95
|
|
47
|
-
|
96
|
+
##
|
97
|
+
# filters the generator using the given `condition`.
|
98
|
+
# The final property checking block will only be run if the condition is truthy.
|
99
|
+
#
|
100
|
+
# If wanted, multiple `where`-conditions can be specified on a property.
|
101
|
+
# Be aware that if you filter away too much generated inputs,
|
102
|
+
# you might encounter a GeneratorExhaustedError.
|
103
|
+
# Only filter if you have few inputs to reject. Otherwise, improve your generators.
|
104
|
+
def where(&condition)
|
48
105
|
original_condition = @condition.dup
|
49
|
-
@condition =
|
106
|
+
@condition = proc do |**kwargs|
|
107
|
+
original_condition.call(**kwargs) && condition.call(**kwargs)
|
108
|
+
end
|
50
109
|
|
51
110
|
self
|
52
111
|
end
|
53
112
|
|
113
|
+
##
|
114
|
+
# Checks the property (after settings have been altered using the other instance methods in this class.)
|
54
115
|
def check(&block)
|
55
|
-
|
116
|
+
gens =
|
117
|
+
if @kwbindings != {}
|
118
|
+
kwbinding_generator = PropCheck::Generators.fixed_hash(**@kwbindings)
|
119
|
+
@bindings + [kwbinding_generator]
|
120
|
+
else
|
121
|
+
@bindings
|
122
|
+
end
|
123
|
+
binding_generator = PropCheck::Generators.tuple(*gens)
|
124
|
+
# binding_generator = PropCheck::Generators.fixed_hash(**@kwbindings)
|
56
125
|
|
57
126
|
n_runs = 0
|
58
127
|
n_successful = 0
|
@@ -70,7 +139,11 @@ module PropCheck
|
|
70
139
|
private def ensure_not_exhausted!(n_runs)
|
71
140
|
return if n_runs >= @config.n_runs
|
72
141
|
|
73
|
-
|
142
|
+
raise_generator_exhausted!
|
143
|
+
end
|
144
|
+
|
145
|
+
private def raise_generator_exhausted!()
|
146
|
+
raise Errors::GeneratorExhaustedError, """
|
74
147
|
Could not perform `n_runs = #{@config.n_runs}` runs,
|
75
148
|
(exhausted #{@config.max_generate_attempts} tries)
|
76
149
|
because too few generator results were adhering to
|
@@ -81,7 +154,7 @@ module PropCheck
|
|
81
154
|
end
|
82
155
|
|
83
156
|
private def check_attempt(generator_result, n_successful, &block)
|
84
|
-
|
157
|
+
block.call(*generator_result.root)
|
85
158
|
|
86
159
|
# immediately stop (without shrinnking) for when the app is asked
|
87
160
|
# to close by outside intervention
|
@@ -117,8 +190,8 @@ module PropCheck
|
|
117
190
|
(0...@config.max_generate_attempts)
|
118
191
|
.lazy
|
119
192
|
.map { binding_generator.generate(size, rng) }
|
120
|
-
.reject { |val| val.root == :"_PropCheck.filter_me" }
|
121
|
-
.select { |val|
|
193
|
+
.reject { |val| val.root.any? { |elem| elem == :"_PropCheck.filter_me" }}
|
194
|
+
.select { |val| @condition.call(*val.root) }
|
122
195
|
.map do |result|
|
123
196
|
n_runs += 1
|
124
197
|
size += 1
|
@@ -131,7 +204,7 @@ module PropCheck
|
|
131
204
|
private def show_problem_output(problem, generator_results, n_successful, &block)
|
132
205
|
output = @config.verbose ? STDOUT : StringIO.new
|
133
206
|
output = pre_output(output, n_successful, generator_results.root, problem)
|
134
|
-
shrunken_result, shrunken_exception, n_shrink_steps =
|
207
|
+
shrunken_result, shrunken_exception, n_shrink_steps = shrink(generator_results, output, &block)
|
135
208
|
output = post_output(output, n_shrink_steps, shrunken_result, shrunken_exception)
|
136
209
|
|
137
210
|
[output, shrunken_result, shrunken_exception, n_shrink_steps]
|
@@ -151,24 +224,29 @@ module PropCheck
|
|
151
224
|
end
|
152
225
|
|
153
226
|
private def post_output(output, n_shrink_steps, shrunken_result, shrunken_exception)
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
227
|
+
if n_shrink_steps == 0
|
228
|
+
output.puts '(shrinking impossible)'
|
229
|
+
else
|
230
|
+
output.puts ''
|
231
|
+
output.puts "Shrunken input (after #{n_shrink_steps} shrink steps):"
|
232
|
+
output.puts "`#{print_roots(shrunken_result)}`"
|
233
|
+
output.puts ""
|
234
|
+
output.puts "Shrunken exception:\n---\n#{shrunken_exception}"
|
235
|
+
output.puts "---"
|
236
|
+
output.puts ""
|
237
|
+
end
|
162
238
|
output
|
163
239
|
end
|
164
240
|
|
165
|
-
private def print_roots(
|
166
|
-
|
167
|
-
|
168
|
-
|
241
|
+
private def print_roots(lazy_tree_val)
|
242
|
+
if lazy_tree_val.is_a?(Array) && lazy_tree_val.length == 1 && lazy_tree_val[0].is_a?(Hash)
|
243
|
+
lazy_tree_val[0].ai
|
244
|
+
else
|
245
|
+
lazy_tree_val.ai
|
246
|
+
end
|
169
247
|
end
|
170
248
|
|
171
|
-
private def
|
249
|
+
private def shrink(bindings_tree, io, &block)
|
172
250
|
io.puts 'Shrinking...' if @config.verbose
|
173
251
|
problem_child = bindings_tree
|
174
252
|
siblings = problem_child.children.lazy
|
@@ -190,12 +268,12 @@ module PropCheck
|
|
190
268
|
io.print '.' if @config.verbose
|
191
269
|
|
192
270
|
begin
|
193
|
-
|
194
|
-
rescue Exception =>
|
271
|
+
block.call(*sibling.root)
|
272
|
+
rescue Exception => e
|
195
273
|
problem_child = sibling
|
196
274
|
parent_siblings = siblings
|
197
275
|
siblings = problem_child.children.lazy
|
198
|
-
problem_exception =
|
276
|
+
problem_exception = e
|
199
277
|
end
|
200
278
|
end
|
201
279
|
|
data/lib/prop_check/version.rb
CHANGED
data/prop_check.gemspec
CHANGED
@@ -37,6 +37,6 @@ Gem::Specification.new do |spec|
|
|
37
37
|
spec.required_ruby_version = '>= 2.5.1'
|
38
38
|
|
39
39
|
spec.add_development_dependency "bundler", "~> 2.0"
|
40
|
-
spec.add_development_dependency "rake", "~>
|
40
|
+
spec.add_development_dependency "rake", "~> 12.3"
|
41
41
|
spec.add_development_dependency "rspec", "~> 3.0"
|
42
42
|
end
|
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.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Qqwy/Wiebe-Marten Wijnja
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-07-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '12.3'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '12.3'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -81,11 +81,10 @@ files:
|
|
81
81
|
- lib/prop_check/generator.rb
|
82
82
|
- lib/prop_check/generators.rb
|
83
83
|
- lib/prop_check/helper.rb
|
84
|
+
- lib/prop_check/helper/lazy_append.rb
|
84
85
|
- lib/prop_check/lazy_tree.rb
|
85
86
|
- lib/prop_check/property.rb
|
86
|
-
- lib/prop_check/property/check_evaluator.rb
|
87
87
|
- lib/prop_check/property/configuration.rb
|
88
|
-
- lib/prop_check/rspec.rb
|
89
88
|
- lib/prop_check/version.rb
|
90
89
|
- prop_check.gemspec
|
91
90
|
homepage: https://github.com/Qqwy/ruby-prop_check/
|
@@ -95,7 +94,7 @@ metadata:
|
|
95
94
|
homepage_uri: https://github.com/Qqwy/ruby-prop_check/
|
96
95
|
source_code_uri: https://github.com/Qqwy/ruby-prop_check/
|
97
96
|
changelog_uri: https://github.com/Qqwy/ruby-prop_check/CHANGELOG.md
|
98
|
-
post_install_message:
|
97
|
+
post_install_message:
|
99
98
|
rdoc_options: []
|
100
99
|
require_paths:
|
101
100
|
- lib
|
@@ -110,9 +109,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
110
109
|
- !ruby/object:Gem::Version
|
111
110
|
version: '0'
|
112
111
|
requirements: []
|
113
|
-
|
114
|
-
|
115
|
-
signing_key:
|
112
|
+
rubygems_version: 3.0.3
|
113
|
+
signing_key:
|
116
114
|
specification_version: 4
|
117
115
|
summary: PropCheck allows you to do property-based testing, including shrinking.
|
118
116
|
test_files: []
|
@@ -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,14 +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
|
-
PropCheck::Property.forall(*args, **kwargs) do
|
9
|
-
instance_exec(self, &block)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|