prop_check 0.6.0 → 0.8.0
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 +3 -0
- data/.tool-versions +1 -1
- data/.travis.yml +13 -2
- data/CHANGELOG.md +1 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +6 -4
- data/README.md +73 -13
- data/lib/prop_check.rb +16 -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/rspec.rb +9 -3
- data/lib/prop_check/version.rb +1 -1
- data/prop_check.gemspec +5 -3
- metadata +12 -14
- data/lib/prop_check/property/check_evaluator.rb +0 -45
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 71879bc6575991fe6582a3f98213ee01fb5c9230cf042f95769eea801c366b1f
|
4
|
+
data.tar.gz: 9e351641ffb936461634a871cb984efff16c3ff5e9de0e60d411d33385786ec4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 704ee74432f653b9993312d965ba1605ab366f67d564061a82522d8ac647b21689f47abdc836483dd82ffa4e3810882e33cccbd47c37e22cf8013d82147c2a20
|
7
|
+
data.tar.gz: 12535b39f7ff460bba0fb26bb3156f819e8d96ccf2ed7e2da28b6ab63472af3f42341a2c92e8fe437d5dccb56ce77ee7969e036c7484c833655ad5108e7fd3d9
|
data/.gitignore
CHANGED
data/.tool-versions
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby 2.5
|
1
|
+
ruby 2.6.5
|
data/.travis.yml
CHANGED
@@ -3,5 +3,16 @@ sudo: false
|
|
3
3
|
language: ruby
|
4
4
|
cache: bundler
|
5
5
|
rvm:
|
6
|
-
- 2.
|
7
|
-
before_install: gem install bundler -v 2.0.
|
6
|
+
- 2.5.1
|
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.7.1)
|
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
|
+
[](https://rubygems.org/gems/prop_check)
|
6
|
+
[](https://travis-ci.org/Qqwy/ruby-prop_check)
|
7
|
+
[](https://codeclimate.com/github/Qqwy/ruby-prop_check/maintainability)
|
8
|
+
[](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(array(integer)) do |array|
|
94
|
+
result = naive_average(array)
|
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
|
+
:array => []
|
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
|
@@ -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,26 @@ 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.
|
6
12
|
module PropCheck
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
13
|
+
module Errors
|
14
|
+
class Error < StandardError; end
|
15
|
+
class UserError < Error; end
|
16
|
+
class GeneratorExhaustedError < UserError; end
|
17
|
+
class MaxShrinkStepsExceededError < UserError; end
|
18
|
+
end
|
11
19
|
|
12
20
|
extend self
|
13
21
|
|
22
|
+
##
|
23
|
+
# Runs a property.
|
24
|
+
#
|
25
|
+
# See the README for more details.
|
14
26
|
def forall(*args, **kwargs, &block)
|
15
27
|
PropCheck::Property.forall(*args, **kwargs, &block)
|
16
28
|
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/rspec.rb
CHANGED
@@ -1,13 +1,19 @@
|
|
1
1
|
module PropCheck
|
2
2
|
##
|
3
3
|
# Integration with RSpec
|
4
|
+
#
|
5
|
+
# Currently very basic; it does two things:
|
6
|
+
# 1. adds the local `forall` method to examples that calls `PropCheck.forall`
|
7
|
+
# 2. adds `include PropCheck::Generators` statement.
|
4
8
|
module RSpec
|
5
9
|
# To make it available within examples
|
6
10
|
def self.extend_object(obj)
|
11
|
+
obj.instance_eval do
|
12
|
+
include PropCheck::Generators
|
13
|
+
end
|
14
|
+
|
7
15
|
obj.define_method(:forall) do |*args, **kwargs, &block|
|
8
|
-
PropCheck
|
9
|
-
instance_exec(self, &block)
|
10
|
-
end
|
16
|
+
PropCheck.forall(*args, **kwargs, &block)
|
11
17
|
end
|
12
18
|
end
|
13
19
|
end
|
data/lib/prop_check/version.rb
CHANGED
data/prop_check.gemspec
CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["Qqwy/Wiebe-Marten Wijnja"]
|
10
10
|
spec.email = ["w-m@wmcode.nl"]
|
11
11
|
|
12
|
-
spec.summary = %q{PropCheck allows you to do property-based testing
|
13
|
-
spec.description = %q{PropCheck allows you to do property-based testing
|
12
|
+
spec.summary = %q{PropCheck allows you to do property-based testing, including shrinking.}
|
13
|
+
spec.description = %q{PropCheck allows you to do property-based testing, including shrinking. (akin to Haskell's QuickCheck, Erlang's PropEr, Elixir's StreamData). This means that your test are run many times with different, autogenerated inputs, and as soon as a failing case is found, this input is simplified, in the end giving you back the simplest input that made the test fail.}
|
14
14
|
spec.homepage = "https://github.com/Qqwy/ruby-prop_check/"
|
15
15
|
spec.license = "MIT"
|
16
16
|
|
@@ -34,7 +34,9 @@ Gem::Specification.new do |spec|
|
|
34
34
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
35
35
|
spec.require_paths = ["lib"]
|
36
36
|
|
37
|
+
spec.required_ruby_version = '>= 2.5.1'
|
38
|
+
|
37
39
|
spec.add_development_dependency "bundler", "~> 2.0"
|
38
|
-
spec.add_development_dependency "rake", "~>
|
40
|
+
spec.add_development_dependency "rake", "~> 12.3"
|
39
41
|
spec.add_development_dependency "rspec", "~> 3.0"
|
40
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.8.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-20 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
|
@@ -52,7 +52,7 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.0'
|
55
|
-
description: PropCheck allows you to do property-based testing
|
55
|
+
description: PropCheck allows you to do property-based testing, including shrinking.
|
56
56
|
(akin to Haskell's QuickCheck, Erlang's PropEr, Elixir's StreamData). This means
|
57
57
|
that your test are run many times with different, autogenerated inputs, and as soon
|
58
58
|
as a failing case is found, this input is simplified, in the end giving you back
|
@@ -81,9 +81,9 @@ 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
88
|
- lib/prop_check/rspec.rb
|
89
89
|
- lib/prop_check/version.rb
|
@@ -95,7 +95,7 @@ metadata:
|
|
95
95
|
homepage_uri: https://github.com/Qqwy/ruby-prop_check/
|
96
96
|
source_code_uri: https://github.com/Qqwy/ruby-prop_check/
|
97
97
|
changelog_uri: https://github.com/Qqwy/ruby-prop_check/CHANGELOG.md
|
98
|
-
post_install_message:
|
98
|
+
post_install_message:
|
99
99
|
rdoc_options: []
|
100
100
|
require_paths:
|
101
101
|
- lib
|
@@ -103,17 +103,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
103
103
|
requirements:
|
104
104
|
- - ">="
|
105
105
|
- !ruby/object:Gem::Version
|
106
|
-
version:
|
106
|
+
version: 2.5.1
|
107
107
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
108
|
requirements:
|
109
109
|
- - ">="
|
110
110
|
- !ruby/object:Gem::Version
|
111
111
|
version: '0'
|
112
112
|
requirements: []
|
113
|
-
|
114
|
-
|
115
|
-
signing_key:
|
113
|
+
rubygems_version: 3.0.3
|
114
|
+
signing_key:
|
116
115
|
specification_version: 4
|
117
|
-
summary: PropCheck allows you to do property-based testing
|
118
|
-
(akin to Haskell's QuickCheck, Erlang's PropEr, Elixir's StreamData)
|
116
|
+
summary: PropCheck allows you to do property-based testing, including shrinking.
|
119
117
|
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
|