rantly 1.2.0 → 3.0.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/.travis.yml +9 -6
- data/CHANGELOG.md +33 -0
- data/Gemfile +5 -2
- data/README.md +10 -1
- data/Rakefile +15 -2
- data/VERSION.yml +4 -4
- data/lib/rantly/data.rb +1 -1
- data/lib/rantly/generator.rb +88 -95
- data/lib/rantly/minitest_extensions.rb +1 -0
- data/lib/rantly/property.rb +20 -23
- data/lib/rantly/rspec_extensions.rb +1 -1
- data/lib/rantly/shrinks.rb +31 -37
- data/lib/rantly/silly.rb +40 -38
- data/lib/rantly/spec.rb +4 -4
- data/lib/rantly.rb +4 -4
- data/rantly.gemspec +37 -42
- data/test/rantly_test.rb +85 -216
- data/test/shrinks_test.rb +38 -42
- metadata +9 -66
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 406db7ec111ce8f3580b6f76970b4baf110bae5926a5cc476f55ee435d857b4a
|
4
|
+
data.tar.gz: 5e42ee8b71cd692f7cd330fce8e3b65807ee9405afeb5ef2a847111da8ebefea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d83625897b4e83a7fed19c07922d2f456e14e69f86d56fdc0d13e7e0651ac71f28d897f2208815c383e602c7ab2093c3e28b33fd9b9eb957869764d117009c7a
|
7
|
+
data.tar.gz: d2362962331c07a90c37622ef9cef880de4cbb982daa04cc4cf5c38b178202616b6637527d03249e6941beeab588a57329fc982a09237041bdb231bbca467c9a
|
data/.travis.yml
CHANGED
@@ -4,10 +4,13 @@ cache: bundler
|
|
4
4
|
after_success:
|
5
5
|
- coveralls
|
6
6
|
rvm:
|
7
|
-
- 2.0.0
|
8
|
-
- 2.1.9
|
9
|
-
- 2.2.6
|
10
|
-
- 2.3.3
|
11
7
|
- 2.4.0
|
12
|
-
-
|
13
|
-
-
|
8
|
+
- 2.5.1
|
9
|
+
- ruby-head
|
10
|
+
- jruby-9.2.0.0
|
11
|
+
- jruby-head
|
12
|
+
|
13
|
+
matrix:
|
14
|
+
allow_failures:
|
15
|
+
- rvm: jruby-head
|
16
|
+
- rvm: ruby-head
|
data/CHANGELOG.md
CHANGED
@@ -3,6 +3,38 @@ All notable changes to rantly will be documented in this file. The curated log b
|
|
3
3
|
|
4
4
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
5
5
|
|
6
|
+
## [Master](https://github.com/rantly-rb/rantly/compare/2.0.0...master) (unreleased)
|
7
|
+
|
8
|
+
### New features
|
9
|
+
|
10
|
+
### Bug fixes
|
11
|
+
|
12
|
+
### Changes
|
13
|
+
|
14
|
+
|
15
|
+
## [2.0.0](https://github.com/rantly-rb/rantly/compare/1.2.0...2.0.0) - 2019-01-08
|
16
|
+
|
17
|
+
### New features
|
18
|
+
- Add support for float ranges to `range` generator
|
19
|
+
- [Issue #60](https://github.com/rantly-rb/rantly/issues/60)
|
20
|
+
- thanks [Trevor Brown][Trevor Brown]
|
21
|
+
|
22
|
+
### Bug fixes
|
23
|
+
- `range` generator returns `nil` for invalid ranges
|
24
|
+
- [Issue #60](https://github.com/rantly-rb/rantly/issues/60)
|
25
|
+
- thanks [Ana María Martínez Gómez][Ana María Martínez Gómez]
|
26
|
+
- `choose` generator returns `nil` when no values are given
|
27
|
+
- thanks [Ana María Martínez Gómez][Ana María Martínez Gómez]
|
28
|
+
|
29
|
+
### Changes
|
30
|
+
- Only support for Ruby >= 2.4 and JRuby >= 9.2
|
31
|
+
- [Issue #42](https://github.com/rantly-rb/rantly/issues/42) and [issue #37](https://github.com/rantly-rb/rantly/issues/37)
|
32
|
+
- Do not render all shrinking levels, only the failing case and the minimal failed data.
|
33
|
+
- thanks [Ana María Martínez Gómez][Ana María Martínez Gómez]
|
34
|
+
- Improve failure/success messages
|
35
|
+
- thanks [Ana María Martínez Gómez][Ana María Martínez Gómez]
|
36
|
+
|
37
|
+
|
6
38
|
## [1.2.0](https://github.com/abargnesi/rantly/compare/1.1.0...1.2.0) - 2018-08-29
|
7
39
|
### New features
|
8
40
|
- Allow to generate floats using Gaussian distribution
|
@@ -59,3 +91,4 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
59
91
|
[Oleksii Fedorov]: https://github.com/waterlink
|
60
92
|
[Ana María Martínez Gómez]: https://github.com/Ana06
|
61
93
|
[Víctor Gallego]: https://github.com/vicgalle
|
94
|
+
[Trevor Brown]: https://github.com/Stratus3D
|
data/Gemfile
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
3
|
group :development, :test do
|
4
|
-
gem '
|
4
|
+
gem 'coveralls', '>= 0', require: false
|
5
5
|
gem 'minitest', '~> 5.10.0'
|
6
|
+
gem 'rake', '~> 12.3.3'
|
6
7
|
gem 'simplecov', '>= 0'
|
7
|
-
|
8
|
+
|
9
|
+
gem 'rubocop'
|
10
|
+
gem 'rubocop-performance', require: false
|
8
11
|
end
|
data/README.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
[](https://badge.fury.io/rb/rantly)
|
2
2
|
[](https://travis-ci.org/rantly-rb/rantly)
|
3
|
+
[](https://coveralls.io/github/rantly-rb/rantly?branch=master)
|
3
4
|
|
4
5
|
# Imperative Random Data Generator and Quickcheck
|
5
6
|
|
@@ -9,13 +10,19 @@ Rantly is basically a recursive descent interpreter, each of its method returns
|
|
9
10
|
|
10
11
|
Its implementation has no alien mathematics inside. Completely side-effect-free-free.
|
11
12
|
|
13
|
+

|
14
|
+
|
12
15
|
|
13
16
|
# Install
|
14
17
|
|
18
|
+
Rantly requires Ruby 3.2 or higher. To install Rantly add it to your Gemfile or run:
|
19
|
+
|
15
20
|
```ruby
|
16
21
|
$ gem install rantly
|
17
22
|
```
|
18
23
|
|
24
|
+
You can try it in the console by running:
|
25
|
+
|
19
26
|
```ruby
|
20
27
|
$ irb -rrantly
|
21
28
|
> Rantly { [integer,float] } # same as Rantly.value { integer }
|
@@ -401,8 +408,10 @@ Normal arrays or hashes are not shrinked.
|
|
401
408
|
|
402
409
|
Thanks to [all contributors](https://github.com/rantly-rb/rantly/graphs/contributors). :cupid: New contributors are welcome! :wink:
|
403
410
|
|
411
|
+
[Logotype](/logo) designed by: [@Richardbmx](https://github.com/richardbmx)
|
412
|
+
|
404
413
|
|
405
414
|
# License
|
406
415
|
|
407
|
-
Code published under MIT License, Copyright (c) 2009 Howard Yeh. See [LICENSE](
|
416
|
+
Code published under MIT License, Copyright (c) 2009 Howard Yeh. See [LICENSE](/LICENSE).
|
408
417
|
|
data/Rakefile
CHANGED
@@ -1,22 +1,35 @@
|
|
1
1
|
require 'rake'
|
2
2
|
|
3
|
+
task default: %i[test rubocop]
|
4
|
+
|
3
5
|
require 'rake/testtask'
|
6
|
+
|
4
7
|
Rake::TestTask.new(:test) do |test|
|
5
8
|
test.libs << 'lib' << 'test'
|
6
9
|
test.pattern = 'test/**/*_test.rb'
|
7
10
|
test.verbose = true
|
8
11
|
end
|
9
12
|
|
10
|
-
|
13
|
+
require 'rubocop/rake_task'
|
14
|
+
|
15
|
+
desc 'Run RuboCop'
|
16
|
+
RuboCop::RakeTask.new(:rubocop) do |task|
|
17
|
+
task.options = ['--display-cop-names']
|
18
|
+
end
|
19
|
+
|
20
|
+
RuboCop::RakeTask.new('rubocop:auto_gen_config') do |task|
|
21
|
+
task.options = ['--display-cop-names', '--auto-gen-config', '--auto-gen-only-exclude']
|
22
|
+
end
|
11
23
|
|
12
24
|
require 'rdoc/task'
|
25
|
+
|
13
26
|
Rake::RDocTask.new do |rdoc|
|
14
27
|
require 'yaml'
|
15
28
|
if File.exist?('VERSION.yml')
|
16
29
|
config = YAML.load(File.read('VERSION.yml'))
|
17
30
|
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
18
31
|
else
|
19
|
-
version =
|
32
|
+
version = ''
|
20
33
|
end
|
21
34
|
|
22
35
|
rdoc.rdoc_dir = 'rdoc'
|
data/VERSION.yml
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
---
|
2
|
-
:build:
|
3
|
-
:major:
|
4
|
-
:minor:
|
1
|
+
---
|
2
|
+
:build:
|
3
|
+
:major: 3
|
4
|
+
:minor: 0
|
5
5
|
:patch: 0
|
data/lib/rantly/data.rb
CHANGED
data/lib/rantly/generator.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Rantly
|
2
|
-
|
3
2
|
class << self
|
4
3
|
attr_writer :default_size
|
4
|
+
|
5
5
|
def singleton
|
6
6
|
@singleton ||= Rantly.new
|
7
7
|
@singleton
|
@@ -11,20 +11,20 @@ class Rantly
|
|
11
11
|
@default_size || 6
|
12
12
|
end
|
13
13
|
|
14
|
-
def each(n,limit=10
|
15
|
-
gen.each(n,limit
|
14
|
+
def each(n, limit = 10, &block)
|
15
|
+
gen.each(n, limit, &block)
|
16
16
|
end
|
17
17
|
|
18
|
-
def map(n,limit=10
|
19
|
-
gen.map(n,limit
|
18
|
+
def map(n, limit = 10, &block)
|
19
|
+
gen.map(n, limit, &block)
|
20
20
|
end
|
21
21
|
|
22
|
-
def value(limit=10
|
23
|
-
gen.value(limit
|
22
|
+
def value(limit = 10, &block)
|
23
|
+
gen.value(limit, &block)
|
24
24
|
end
|
25
25
|
|
26
26
|
def gen
|
27
|
-
|
27
|
+
singleton
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -32,7 +32,7 @@ class Rantly
|
|
32
32
|
end
|
33
33
|
|
34
34
|
class TooManyTries < RuntimeError
|
35
|
-
def initialize(limit,nfailed)
|
35
|
+
def initialize(limit, nfailed)
|
36
36
|
@limit = limit
|
37
37
|
@nfailed = nfailed
|
38
38
|
end
|
@@ -41,38 +41,37 @@ class Rantly
|
|
41
41
|
@nfailed
|
42
42
|
end
|
43
43
|
|
44
|
-
|
45
|
-
"Exceed gen limit #{@limit}: #{@nfailed} failed guards"
|
46
|
-
end
|
44
|
+
attr_reader :limit
|
47
45
|
end
|
48
46
|
|
49
47
|
# limit attempts to 10 times of how many things we want to generate
|
50
|
-
def each(n,limit=10
|
51
|
-
generate(n,limit,block)
|
48
|
+
def each(n, limit = 10, &block)
|
49
|
+
generate(n, limit, block)
|
52
50
|
end
|
53
51
|
|
54
|
-
def map(n,limit=10
|
52
|
+
def map(n, limit = 10, &block)
|
55
53
|
acc = []
|
56
|
-
generate(n,limit,block) do |val|
|
54
|
+
generate(n, limit, block) do |val|
|
57
55
|
acc << val
|
58
56
|
end
|
59
57
|
acc
|
60
58
|
end
|
61
59
|
|
62
|
-
def value(limit=10
|
63
|
-
generate(1,limit,block) do |val|
|
60
|
+
def value(limit = 10, &block)
|
61
|
+
generate(1, limit, block) do |val|
|
64
62
|
return val
|
65
63
|
end
|
66
64
|
end
|
67
65
|
|
68
|
-
def generate(n,limit_arg,gen_block
|
66
|
+
def generate(n, limit_arg, gen_block, &handler)
|
69
67
|
limit = n * limit_arg
|
70
68
|
nfailed = 0
|
71
69
|
nsuccess = 0
|
72
70
|
while nsuccess < n
|
73
|
-
raise TooManyTries.new(limit_arg*n,nfailed) if limit
|
71
|
+
raise TooManyTries.new(limit_arg * n, nfailed) if limit.zero?
|
72
|
+
|
74
73
|
begin
|
75
|
-
val =
|
74
|
+
val = instance_eval(&gen_block)
|
76
75
|
rescue GuardFailure
|
77
76
|
nfailed += 1
|
78
77
|
limit -= 1
|
@@ -80,7 +79,7 @@ class Rantly
|
|
80
79
|
end
|
81
80
|
nsuccess += 1
|
82
81
|
limit -= 1
|
83
|
-
|
82
|
+
yield(val) if handler
|
84
83
|
end
|
85
84
|
end
|
86
85
|
|
@@ -100,87 +99,89 @@ class Rantly
|
|
100
99
|
end
|
101
100
|
|
102
101
|
def guard(test)
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
true
|
107
|
-
end
|
102
|
+
return true if test
|
103
|
+
|
104
|
+
raise GuardFailure
|
108
105
|
end
|
109
106
|
|
110
107
|
def size
|
111
108
|
@size || Rantly.default_size
|
112
109
|
end
|
113
110
|
|
114
|
-
def sized(n
|
115
|
-
raise
|
111
|
+
def sized(n, &block)
|
112
|
+
raise 'size needs to be greater than zero' if n.negative?
|
113
|
+
|
116
114
|
old_size = @size
|
117
115
|
@size = n
|
118
|
-
r =
|
116
|
+
r = instance_eval(&block)
|
119
117
|
@size = old_size
|
120
|
-
|
118
|
+
r
|
121
119
|
end
|
122
120
|
|
123
121
|
# wanna avoid going into Bignum when calling range with these.
|
124
122
|
INTEGER_MAX = (2**(0.size * 8 - 2) - 1) / 2
|
125
|
-
INTEGER_MIN = -
|
126
|
-
def integer(limit=nil)
|
123
|
+
INTEGER_MIN = -INTEGER_MAX
|
124
|
+
def integer(limit = nil)
|
127
125
|
case limit
|
128
126
|
when Range
|
129
127
|
hi = limit.end
|
130
128
|
lo = limit.begin
|
131
129
|
when Integer
|
132
|
-
raise
|
133
|
-
|
130
|
+
raise 'n should be greater than zero' if limit.negative?
|
131
|
+
|
132
|
+
hi = limit
|
133
|
+
lo = -limit
|
134
134
|
else
|
135
|
-
hi
|
135
|
+
hi = INTEGER_MAX
|
136
|
+
lo = INTEGER_MIN
|
136
137
|
end
|
137
|
-
range(lo,hi)
|
138
|
+
range(lo, hi)
|
138
139
|
end
|
139
140
|
|
140
141
|
def positive_integer
|
141
142
|
range(0)
|
142
143
|
end
|
143
144
|
|
144
|
-
def float(distribution=nil, params={})
|
145
|
+
def float(distribution = nil, params = {})
|
145
146
|
case distribution
|
146
147
|
when :normal
|
147
148
|
params[:center] ||= 0
|
148
149
|
params[:scale] ||= 1
|
149
|
-
raise
|
150
|
+
raise 'The distribution scale should be greater than zero' if params[:scale].negative?
|
151
|
+
|
150
152
|
# Sum of 6 draws from a uniform distribution give as a draw of a normal
|
151
153
|
# distribution centered in 3 (central limit theorem).
|
152
|
-
([rand, rand, rand, rand, rand, rand].
|
154
|
+
([rand, rand, rand, rand, rand, rand].sum - 3) * params[:scale] + params[:center]
|
153
155
|
else
|
154
156
|
rand
|
155
157
|
end
|
156
158
|
end
|
157
159
|
|
158
|
-
def range(lo=
|
159
|
-
lo
|
160
|
-
hi ||= INTEGER_MAX
|
161
|
-
rand(hi+1-lo) + lo
|
160
|
+
def range(lo = INTEGER_MIN, hi = INTEGER_MAX)
|
161
|
+
rand(lo..hi)
|
162
162
|
end
|
163
163
|
|
164
|
-
def call(gen
|
164
|
+
def call(gen, *args)
|
165
165
|
case gen
|
166
166
|
when Symbol
|
167
|
-
|
167
|
+
send(gen, *args)
|
168
168
|
when Array
|
169
|
-
raise
|
170
|
-
|
169
|
+
raise 'empty array' if gen.empty?
|
170
|
+
|
171
|
+
send(gen[0], *gen[1..-1])
|
171
172
|
when Proc
|
172
|
-
|
173
|
+
instance_eval(&gen)
|
173
174
|
else
|
174
175
|
raise "don't know how to call type: #{gen}"
|
175
176
|
end
|
176
177
|
end
|
177
178
|
|
178
179
|
def branch(*gens)
|
179
|
-
|
180
|
+
call(choose(*gens))
|
180
181
|
end
|
181
182
|
|
182
183
|
def choose(*vals)
|
183
|
-
vals[range(0,vals.length-1)]
|
184
|
+
vals[range(0, vals.length - 1)] if vals.length.positive?
|
184
185
|
end
|
185
186
|
|
186
187
|
def literal(value)
|
@@ -188,52 +189,50 @@ class Rantly
|
|
188
189
|
end
|
189
190
|
|
190
191
|
def boolean
|
191
|
-
range(0,1)
|
192
|
+
range(0, 1).zero?
|
192
193
|
end
|
193
194
|
|
194
195
|
def freq(*pairs)
|
195
196
|
pairs = pairs.map do |pair|
|
196
197
|
case pair
|
197
198
|
when Symbol, String, Proc
|
198
|
-
[1,pair]
|
199
|
+
[1, pair]
|
199
200
|
when Array
|
200
|
-
|
201
|
-
[1] + pair
|
202
|
-
else
|
201
|
+
if pair.first.is_a?(Integer)
|
203
202
|
pair
|
203
|
+
else
|
204
|
+
[1] + pair
|
204
205
|
end
|
205
206
|
end
|
206
207
|
end
|
207
|
-
total = pairs.inject(0) { |sum,p| sum + p.first }
|
208
|
-
raise(
|
209
|
-
|
208
|
+
total = pairs.inject(0) { |sum, p| sum + p.first }
|
209
|
+
raise("Illegal frequency:#{pairs.inspect}") if total.zero?
|
210
|
+
|
211
|
+
pos = range(1, total)
|
210
212
|
pairs.each do |p|
|
211
213
|
weight, gen, *args = p
|
212
|
-
if pos <= p[0]
|
213
|
-
|
214
|
-
|
215
|
-
pos -= weight
|
216
|
-
end
|
214
|
+
return call(gen, *args) if pos <= p[0]
|
215
|
+
|
216
|
+
pos -= weight
|
217
217
|
end
|
218
218
|
end
|
219
219
|
|
220
|
-
def array(n=
|
221
|
-
n.times.map {
|
220
|
+
def array(n = size, &block)
|
221
|
+
n.times.map { instance_eval(&block) }
|
222
222
|
end
|
223
223
|
|
224
|
-
def dict(n=
|
224
|
+
def dict(n = size, &block)
|
225
225
|
h = {}
|
226
226
|
each(n) do
|
227
|
-
k,v = instance_eval(&block)
|
228
|
-
h[k] = v if guard(!h.
|
227
|
+
k, v = instance_eval(&block)
|
228
|
+
h[k] = v if guard(!h.key?(k))
|
229
229
|
end
|
230
230
|
h
|
231
231
|
end
|
232
232
|
|
233
233
|
module Chars
|
234
|
-
|
235
234
|
class << self
|
236
|
-
ASCII = (0..127).to_a.each_with_object(
|
235
|
+
ASCII = (0..127).to_a.each_with_object('') { |i, obj| obj << i }
|
237
236
|
|
238
237
|
def of(regexp)
|
239
238
|
ASCII.scan(regexp).to_a.map! { |char| char[0].ord }
|
@@ -254,41 +253,35 @@ class Rantly
|
|
254
253
|
XDIGIT = Chars.of(/[[:xdigit:]]/)
|
255
254
|
ASCII = Chars.of(/./)
|
256
255
|
|
257
|
-
|
258
256
|
CLASSES = {
|
259
|
-
:
|
260
|
-
:
|
261
|
-
:
|
262
|
-
:
|
263
|
-
:
|
264
|
-
:
|
265
|
-
:
|
266
|
-
:
|
267
|
-
:
|
268
|
-
:
|
269
|
-
:
|
270
|
-
:
|
271
|
-
:
|
272
|
-
}
|
273
|
-
|
257
|
+
alnum: ALNUM,
|
258
|
+
alpha: ALPHA,
|
259
|
+
blank: BLANK,
|
260
|
+
cntrl: CNTRL,
|
261
|
+
digit: DIGIT,
|
262
|
+
graph: GRAPH,
|
263
|
+
lower: LOWER,
|
264
|
+
print: PRINT,
|
265
|
+
punct: PUNCT,
|
266
|
+
space: SPACE,
|
267
|
+
upper: UPPER,
|
268
|
+
xdigit: XDIGIT,
|
269
|
+
ascii: ASCII
|
270
|
+
}.freeze
|
274
271
|
end
|
275
272
|
|
276
|
-
def string(char_class
|
273
|
+
def string(char_class = :print)
|
277
274
|
chars = case char_class
|
278
275
|
when Regexp
|
279
276
|
Chars.of(char_class)
|
280
277
|
when Symbol
|
281
278
|
Chars::CLASSES[char_class]
|
282
279
|
end
|
283
|
-
raise
|
280
|
+
raise 'bad arg' unless chars
|
284
281
|
|
285
|
-
char_strings = chars.map
|
282
|
+
char_strings = chars.map(&:chr)
|
286
283
|
str = Array.new(size)
|
287
|
-
|
288
|
-
while current_index < size
|
289
|
-
str[current_index] = char_strings.sample
|
290
|
-
current_index += 1
|
291
|
-
end
|
284
|
+
size.times { |i| str[i] = char_strings.sample }
|
292
285
|
str.join
|
293
286
|
end
|
294
287
|
end
|
data/lib/rantly/property.rb
CHANGED
@@ -3,14 +3,14 @@ require 'pp'
|
|
3
3
|
require 'stringio'
|
4
4
|
|
5
5
|
class Rantly::Property
|
6
|
-
attr_reader :failed_data, :shrunk_failed_data
|
6
|
+
attr_reader :failed_data, :shrunk_failed_data
|
7
7
|
|
8
|
-
VERBOSITY = ENV.fetch('RANTLY_VERBOSE'
|
9
|
-
RANTLY_COUNT = ENV.fetch('RANTLY_COUNT'
|
8
|
+
VERBOSITY = ENV.fetch('RANTLY_VERBOSE', 1).to_i
|
9
|
+
RANTLY_COUNT = ENV.fetch('RANTLY_COUNT', 100).to_i
|
10
10
|
|
11
11
|
def io
|
12
12
|
@io ||= if VERBOSITY >= 1
|
13
|
-
|
13
|
+
$stdout
|
14
14
|
else
|
15
15
|
StringIO.new
|
16
16
|
end
|
@@ -24,46 +24,43 @@ class Rantly::Property
|
|
24
24
|
@property = property
|
25
25
|
end
|
26
26
|
|
27
|
-
def check(n=RANTLY_COUNT,limit=10
|
27
|
+
def check(n = RANTLY_COUNT, limit = 10, &assertion)
|
28
28
|
i = 0
|
29
29
|
test_data = nil
|
30
30
|
begin
|
31
|
-
Rantly.singleton.generate(n,limit
|
31
|
+
Rantly.singleton.generate(n, limit, @property) do |val|
|
32
32
|
test_data = val
|
33
|
-
|
34
|
-
io.puts
|
35
|
-
io.print
|
33
|
+
yield(val) if assertion
|
34
|
+
io.puts '' if (i % 100).zero?
|
35
|
+
io.print '.' if (i % 10).zero?
|
36
36
|
i += 1
|
37
37
|
end
|
38
38
|
io.puts
|
39
|
-
io.puts "
|
39
|
+
io.puts "SUCCESS - #{i} successful tests"
|
40
40
|
rescue Rantly::TooManyTries => e
|
41
41
|
io.puts
|
42
|
-
io.puts "too many tries: #{e.tries}"
|
43
|
-
raise e
|
44
|
-
rescue Exception =>
|
42
|
+
io.puts "FAILURE - #{i} successful tests, too many tries: #{e.tries}"
|
43
|
+
raise e.exception("#{i} successful tests, too many tries: #{e.tries} (limit: #{e.limit})")
|
44
|
+
rescue Exception => e
|
45
45
|
io.puts
|
46
|
-
io.puts "
|
46
|
+
io.puts "FAILURE - #{i} successful tests, failed on:"
|
47
47
|
pretty_print test_data
|
48
48
|
@failed_data = test_data
|
49
49
|
if @failed_data.respond_to?(:shrink)
|
50
50
|
@shrunk_failed_data, @depth = shrinkify(assertion, @failed_data)
|
51
|
-
io.puts "
|
51
|
+
io.puts "Minimal failed data (depth #{@depth}) is:"
|
52
52
|
pretty_print @shrunk_failed_data
|
53
53
|
end
|
54
|
-
raise
|
54
|
+
raise e.exception("#{i} successful tests, failed on:\n#{test_data}\n\n#{e}\n")
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
58
|
# Explore the failures tree
|
59
|
-
def shrinkify(assertion, data, depth=0, iteration=0)
|
60
|
-
io.puts "Shrinking at depth #{depth}:"
|
61
|
-
pretty_print data
|
62
|
-
|
59
|
+
def shrinkify(assertion, data, depth = 0, iteration = 0)
|
63
60
|
min_data = data
|
64
61
|
max_depth = depth
|
65
62
|
if data.shrinkable?
|
66
|
-
while iteration < 1024
|
63
|
+
while iteration < 1024
|
67
64
|
# We assume that data.shrink is non-destructive
|
68
65
|
shrunk_data = data.shrink
|
69
66
|
begin
|
@@ -76,9 +73,9 @@ class Rantly::Property
|
|
76
73
|
max_depth = branch_depth
|
77
74
|
end
|
78
75
|
end
|
79
|
-
break
|
76
|
+
break unless data.retry?
|
80
77
|
end
|
81
78
|
end
|
82
|
-
|
79
|
+
[min_data, max_depth, iteration]
|
83
80
|
end
|
84
81
|
end
|