rantly 1.2.0 → 2.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 +4 -2
- data/README.md +10 -1
- data/Rakefile +15 -2
- data/VERSION.yml +2 -2
- data/lib/rantly.rb +4 -4
- data/lib/rantly/data.rb +1 -1
- data/lib/rantly/generator.rb +87 -95
- data/lib/rantly/property.rb +18 -21
- data/lib/rantly/shrinks.rb +40 -45
- data/lib/rantly/silly.rb +40 -38
- data/lib/rantly/spec.rb +4 -4
- data/rantly.gemspec +37 -42
- data/test/rantly_test.rb +85 -216
- data/test/shrinks_test.rb +38 -42
- metadata +5 -62
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13f1bd6f943416c68aff13d50f505ff92485c1613c4c01b52d8cbc78eb027b66
|
4
|
+
data.tar.gz: af1de15faf3be9d3984c1343eb59b65081d0d8c12916c07434e0353ee37a4204
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2f24ffc2cb269a377fb0b5c0e40897ba417ef991d5e219c0a0423458b8b5e760102ba1f32c157190f982419da8f8e6a32e628e1012a0d070d36045609e0a7255
|
7
|
+
data.tar.gz: bb6c3265fbfcd2a0d339dbdf87d6f38afb9e71c5f4f58531c92ce99d784ad5e66405f0a26b6caf6aed9a7cc86bebe6b4194273c389073041b7890abb79f51c03
|
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,10 @@
|
|
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.0.0'
|
6
7
|
gem 'simplecov', '>= 0'
|
7
|
-
|
8
|
+
|
9
|
+
gem 'rubocop'
|
8
10
|
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 2.4 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
data/lib/rantly.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$LOAD_PATH.include?(File.dirname(__FILE__)) || $LOAD_PATH.include?(__dir__)
|
3
3
|
|
4
4
|
class Rantly
|
5
5
|
end
|
6
6
|
|
7
7
|
require 'rantly/generator'
|
8
8
|
|
9
|
-
def Rantly(n=1
|
9
|
+
def Rantly(n = 1, &block)
|
10
10
|
if n > 1
|
11
|
-
Rantly.map(n
|
11
|
+
Rantly.map(n, &block)
|
12
12
|
else
|
13
13
|
Rantly.value(&block)
|
14
14
|
end
|
data/lib/rantly/data.rb
CHANGED
data/lib/rantly/generator.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
class Rantly
|
2
|
-
|
3
2
|
class << self
|
4
3
|
attr_writer :default_size
|
5
4
|
def singleton
|
@@ -11,20 +10,20 @@ class Rantly
|
|
11
10
|
@default_size || 6
|
12
11
|
end
|
13
12
|
|
14
|
-
def each(n,limit=10
|
15
|
-
gen.each(n,limit
|
13
|
+
def each(n, limit = 10, &block)
|
14
|
+
gen.each(n, limit, &block)
|
16
15
|
end
|
17
16
|
|
18
|
-
def map(n,limit=10
|
19
|
-
gen.map(n,limit
|
17
|
+
def map(n, limit = 10, &block)
|
18
|
+
gen.map(n, limit, &block)
|
20
19
|
end
|
21
20
|
|
22
|
-
def value(limit=10
|
23
|
-
gen.value(limit
|
21
|
+
def value(limit = 10, &block)
|
22
|
+
gen.value(limit, &block)
|
24
23
|
end
|
25
24
|
|
26
25
|
def gen
|
27
|
-
|
26
|
+
singleton
|
28
27
|
end
|
29
28
|
end
|
30
29
|
|
@@ -32,7 +31,7 @@ class Rantly
|
|
32
31
|
end
|
33
32
|
|
34
33
|
class TooManyTries < RuntimeError
|
35
|
-
def initialize(limit,nfailed)
|
34
|
+
def initialize(limit, nfailed)
|
36
35
|
@limit = limit
|
37
36
|
@nfailed = nfailed
|
38
37
|
end
|
@@ -41,38 +40,37 @@ class Rantly
|
|
41
40
|
@nfailed
|
42
41
|
end
|
43
42
|
|
44
|
-
|
45
|
-
"Exceed gen limit #{@limit}: #{@nfailed} failed guards"
|
46
|
-
end
|
43
|
+
attr_reader :limit
|
47
44
|
end
|
48
45
|
|
49
46
|
# limit attempts to 10 times of how many things we want to generate
|
50
|
-
def each(n,limit=10
|
51
|
-
generate(n,limit,block)
|
47
|
+
def each(n, limit = 10, &block)
|
48
|
+
generate(n, limit, block)
|
52
49
|
end
|
53
50
|
|
54
|
-
def map(n,limit=10
|
51
|
+
def map(n, limit = 10, &block)
|
55
52
|
acc = []
|
56
|
-
generate(n,limit,block) do |val|
|
53
|
+
generate(n, limit, block) do |val|
|
57
54
|
acc << val
|
58
55
|
end
|
59
56
|
acc
|
60
57
|
end
|
61
58
|
|
62
|
-
def value(limit=10
|
63
|
-
generate(1,limit,block) do |val|
|
59
|
+
def value(limit = 10, &block)
|
60
|
+
generate(1, limit, block) do |val|
|
64
61
|
return val
|
65
62
|
end
|
66
63
|
end
|
67
64
|
|
68
|
-
def generate(n,limit_arg,gen_block
|
65
|
+
def generate(n, limit_arg, gen_block, &handler)
|
69
66
|
limit = n * limit_arg
|
70
67
|
nfailed = 0
|
71
68
|
nsuccess = 0
|
72
69
|
while nsuccess < n
|
73
|
-
raise TooManyTries.new(limit_arg*n,nfailed) if limit
|
70
|
+
raise TooManyTries.new(limit_arg * n, nfailed) if limit.zero?
|
71
|
+
|
74
72
|
begin
|
75
|
-
val =
|
73
|
+
val = instance_eval(&gen_block)
|
76
74
|
rescue GuardFailure
|
77
75
|
nfailed += 1
|
78
76
|
limit -= 1
|
@@ -80,7 +78,7 @@ class Rantly
|
|
80
78
|
end
|
81
79
|
nsuccess += 1
|
82
80
|
limit -= 1
|
83
|
-
|
81
|
+
yield(val) if handler
|
84
82
|
end
|
85
83
|
end
|
86
84
|
|
@@ -100,87 +98,89 @@ class Rantly
|
|
100
98
|
end
|
101
99
|
|
102
100
|
def guard(test)
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
true
|
107
|
-
end
|
101
|
+
return true if test
|
102
|
+
|
103
|
+
raise GuardFailure
|
108
104
|
end
|
109
105
|
|
110
106
|
def size
|
111
107
|
@size || Rantly.default_size
|
112
108
|
end
|
113
109
|
|
114
|
-
def sized(n
|
115
|
-
raise
|
110
|
+
def sized(n, &block)
|
111
|
+
raise 'size needs to be greater than zero' if n.negative?
|
112
|
+
|
116
113
|
old_size = @size
|
117
114
|
@size = n
|
118
|
-
r =
|
115
|
+
r = instance_eval(&block)
|
119
116
|
@size = old_size
|
120
|
-
|
117
|
+
r
|
121
118
|
end
|
122
119
|
|
123
120
|
# wanna avoid going into Bignum when calling range with these.
|
124
121
|
INTEGER_MAX = (2**(0.size * 8 - 2) - 1) / 2
|
125
|
-
INTEGER_MIN = -
|
126
|
-
def integer(limit=nil)
|
122
|
+
INTEGER_MIN = -INTEGER_MAX
|
123
|
+
def integer(limit = nil)
|
127
124
|
case limit
|
128
125
|
when Range
|
129
126
|
hi = limit.end
|
130
127
|
lo = limit.begin
|
131
128
|
when Integer
|
132
|
-
raise
|
133
|
-
|
129
|
+
raise 'n should be greater than zero' if limit.negative?
|
130
|
+
|
131
|
+
hi = limit
|
132
|
+
lo = -limit
|
134
133
|
else
|
135
|
-
hi
|
134
|
+
hi = INTEGER_MAX
|
135
|
+
lo = INTEGER_MIN
|
136
136
|
end
|
137
|
-
range(lo,hi)
|
137
|
+
range(lo, hi)
|
138
138
|
end
|
139
139
|
|
140
140
|
def positive_integer
|
141
141
|
range(0)
|
142
142
|
end
|
143
143
|
|
144
|
-
def float(distribution=nil, params={})
|
144
|
+
def float(distribution = nil, params = {})
|
145
145
|
case distribution
|
146
146
|
when :normal
|
147
147
|
params[:center] ||= 0
|
148
148
|
params[:scale] ||= 1
|
149
|
-
raise
|
149
|
+
raise 'The distribution scale should be greater than zero' if params[:scale].negative?
|
150
|
+
|
150
151
|
# Sum of 6 draws from a uniform distribution give as a draw of a normal
|
151
152
|
# distribution centered in 3 (central limit theorem).
|
152
|
-
([rand, rand, rand, rand, rand, rand].
|
153
|
+
([rand, rand, rand, rand, rand, rand].sum - 3) * params[:scale] + params[:center]
|
153
154
|
else
|
154
155
|
rand
|
155
156
|
end
|
156
157
|
end
|
157
158
|
|
158
|
-
def range(lo=
|
159
|
-
lo
|
160
|
-
hi ||= INTEGER_MAX
|
161
|
-
rand(hi+1-lo) + lo
|
159
|
+
def range(lo = INTEGER_MIN, hi = INTEGER_MAX)
|
160
|
+
rand(lo..hi)
|
162
161
|
end
|
163
162
|
|
164
|
-
def call(gen
|
163
|
+
def call(gen, *args)
|
165
164
|
case gen
|
166
165
|
when Symbol
|
167
|
-
|
166
|
+
send(gen, *args)
|
168
167
|
when Array
|
169
|
-
raise
|
170
|
-
|
168
|
+
raise 'empty array' if gen.empty?
|
169
|
+
|
170
|
+
send(gen[0], *gen[1..-1])
|
171
171
|
when Proc
|
172
|
-
|
172
|
+
instance_eval(&gen)
|
173
173
|
else
|
174
174
|
raise "don't know how to call type: #{gen}"
|
175
175
|
end
|
176
176
|
end
|
177
177
|
|
178
178
|
def branch(*gens)
|
179
|
-
|
179
|
+
call(choose(*gens))
|
180
180
|
end
|
181
181
|
|
182
182
|
def choose(*vals)
|
183
|
-
vals[range(0,vals.length-1)]
|
183
|
+
vals[range(0, vals.length - 1)] if vals.length.positive?
|
184
184
|
end
|
185
185
|
|
186
186
|
def literal(value)
|
@@ -188,52 +188,50 @@ class Rantly
|
|
188
188
|
end
|
189
189
|
|
190
190
|
def boolean
|
191
|
-
range(0,1)
|
191
|
+
range(0, 1).zero?
|
192
192
|
end
|
193
193
|
|
194
194
|
def freq(*pairs)
|
195
195
|
pairs = pairs.map do |pair|
|
196
196
|
case pair
|
197
197
|
when Symbol, String, Proc
|
198
|
-
[1,pair]
|
198
|
+
[1, pair]
|
199
199
|
when Array
|
200
|
-
|
201
|
-
[1] + pair
|
202
|
-
else
|
200
|
+
if pair.first.is_a?(Integer)
|
203
201
|
pair
|
202
|
+
else
|
203
|
+
[1] + pair
|
204
204
|
end
|
205
205
|
end
|
206
206
|
end
|
207
|
-
total = pairs.inject(0) { |sum,p| sum + p.first }
|
208
|
-
raise(
|
209
|
-
|
207
|
+
total = pairs.inject(0) { |sum, p| sum + p.first }
|
208
|
+
raise("Illegal frequency:#{pairs.inspect}") if total.zero?
|
209
|
+
|
210
|
+
pos = range(1, total)
|
210
211
|
pairs.each do |p|
|
211
212
|
weight, gen, *args = p
|
212
|
-
if pos <= p[0]
|
213
|
-
|
214
|
-
|
215
|
-
pos -= weight
|
216
|
-
end
|
213
|
+
return call(gen, *args) if pos <= p[0]
|
214
|
+
|
215
|
+
pos -= weight
|
217
216
|
end
|
218
217
|
end
|
219
218
|
|
220
|
-
def array(n=
|
221
|
-
n.times.map {
|
219
|
+
def array(n = size, &block)
|
220
|
+
n.times.map { instance_eval(&block) }
|
222
221
|
end
|
223
222
|
|
224
|
-
def dict(n=
|
223
|
+
def dict(n = size, &block)
|
225
224
|
h = {}
|
226
225
|
each(n) do
|
227
|
-
k,v = instance_eval(&block)
|
228
|
-
h[k] = v if guard(!h.
|
226
|
+
k, v = instance_eval(&block)
|
227
|
+
h[k] = v if guard(!h.key?(k))
|
229
228
|
end
|
230
229
|
h
|
231
230
|
end
|
232
231
|
|
233
232
|
module Chars
|
234
|
-
|
235
233
|
class << self
|
236
|
-
ASCII = (0..127).to_a.each_with_object(
|
234
|
+
ASCII = (0..127).to_a.each_with_object('') { |i, obj| obj << i }
|
237
235
|
|
238
236
|
def of(regexp)
|
239
237
|
ASCII.scan(regexp).to_a.map! { |char| char[0].ord }
|
@@ -254,41 +252,35 @@ class Rantly
|
|
254
252
|
XDIGIT = Chars.of(/[[:xdigit:]]/)
|
255
253
|
ASCII = Chars.of(/./)
|
256
254
|
|
257
|
-
|
258
255
|
CLASSES = {
|
259
|
-
:
|
260
|
-
:
|
261
|
-
:
|
262
|
-
:
|
263
|
-
:
|
264
|
-
:
|
265
|
-
:
|
266
|
-
:
|
267
|
-
:
|
268
|
-
:
|
269
|
-
:
|
270
|
-
:
|
271
|
-
:
|
272
|
-
}
|
273
|
-
|
256
|
+
alnum: ALNUM,
|
257
|
+
alpha: ALPHA,
|
258
|
+
blank: BLANK,
|
259
|
+
cntrl: CNTRL,
|
260
|
+
digit: DIGIT,
|
261
|
+
graph: GRAPH,
|
262
|
+
lower: LOWER,
|
263
|
+
print: PRINT,
|
264
|
+
punct: PUNCT,
|
265
|
+
space: SPACE,
|
266
|
+
upper: UPPER,
|
267
|
+
xdigit: XDIGIT,
|
268
|
+
ascii: ASCII
|
269
|
+
}.freeze
|
274
270
|
end
|
275
271
|
|
276
|
-
def string(char_class
|
272
|
+
def string(char_class = :print)
|
277
273
|
chars = case char_class
|
278
274
|
when Regexp
|
279
275
|
Chars.of(char_class)
|
280
276
|
when Symbol
|
281
277
|
Chars::CLASSES[char_class]
|
282
278
|
end
|
283
|
-
raise
|
279
|
+
raise 'bad arg' unless chars
|
284
280
|
|
285
|
-
char_strings = chars.map
|
281
|
+
char_strings = chars.map(&:chr)
|
286
282
|
str = Array.new(size)
|
287
|
-
|
288
|
-
while current_index < size
|
289
|
-
str[current_index] = char_strings.sample
|
290
|
-
current_index += 1
|
291
|
-
end
|
283
|
+
size.times { |i| str[i] = char_strings.sample }
|
292
284
|
str.join
|
293
285
|
end
|
294
286
|
end
|