rantly 1.2.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem version](https://badge.fury.io/rb/rantly.svg)](https://badge.fury.io/rb/rantly)
|
2
2
|
[![Build Status](https://travis-ci.org/rantly-rb/rantly.svg?branch=master)](https://travis-ci.org/rantly-rb/rantly)
|
3
|
+
[![Coverage Status](https://coveralls.io/repos/github/rantly-rb/rantly/badge.svg?branch=master)](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
|
+
![img](/logo/Rantly.png)
|
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
|