rantly 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.textile +296 -0
- data/Rakefile +56 -0
- data/Rant.gemspec +54 -0
- data/VERSION.yml +5 -0
- data/lib/rant.rb +7 -0
- data/lib/rant/data.rb +13 -0
- data/lib/rant/generator.rb +263 -0
- data/lib/rant/property.rb +50 -0
- data/lib/rant/silly.rb +116 -0
- data/lib/rant/spec.rb +10 -0
- data/rantly.gemspec +56 -0
- data/test/rant_test.rb +284 -0
- data/test/test_helper.rb +10 -0
- metadata +72 -0
data/.document
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Howard Yeh
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,296 @@
|
|
1
|
+
h1. Imperative Random Data Generator and Quickcheck
|
2
|
+
|
3
|
+
You can use Rant to generate random test data, and use its Test::Unit extension for property-based testing.
|
4
|
+
|
5
|
+
Rant is basically a recursive descent interpreter, each of its method returns a random value of some type (string, integer, float, etc.).
|
6
|
+
|
7
|
+
Its implementation has no alien mathematics inside. Completely side-effect-free-free.
|
8
|
+
|
9
|
+
h1. Install
|
10
|
+
|
11
|
+
<pre><code>
|
12
|
+
$ gem install hayeah-rant --source http://gems.github.com
|
13
|
+
</code></pre>
|
14
|
+
|
15
|
+
<pre><code>
|
16
|
+
$ irb
|
17
|
+
> gem 'rant'
|
18
|
+
> require 'rant'
|
19
|
+
> Rant.gen.value { [integer,float] }
|
20
|
+
=> [20991307, 0.025756845811823]
|
21
|
+
> Rant.gen.value { [integer,float]}
|
22
|
+
=> [-376856492, 0.452245765751706]
|
23
|
+
</code></pre>
|
24
|
+
|
25
|
+
|
26
|
+
h1. Data Generation
|
27
|
+
|
28
|
+
You can create random generators from the Rant class. Rant.gen is just returns a class instance of Rant.
|
29
|
+
|
30
|
+
<pre><code>
|
31
|
+
> gen = Rant.new
|
32
|
+
> gen.value { [integer,float] }
|
33
|
+
=> [-163260081, 0.356075765934108]
|
34
|
+
</code></pre>
|
35
|
+
|
36
|
+
h2. Getting Random Data Values
|
37
|
+
|
38
|
+
<pre><code>
|
39
|
+
Rant#map(n,limit=10)
|
40
|
+
call the generator n times, and collect values
|
41
|
+
Rant#each(n,limit=10)
|
42
|
+
call a random block n times
|
43
|
+
Rant#value(limit=10)
|
44
|
+
call a random block once, and get its value.
|
45
|
+
</code></pre>
|
46
|
+
|
47
|
+
To collect an array of random data,
|
48
|
+
|
49
|
+
<pre><code>
|
50
|
+
# we want 5
|
51
|
+
> gen.map(5) { integer }
|
52
|
+
=> [-380638946, -29645239, 344840868, 308052180, -154360970]
|
53
|
+
</code></pre>
|
54
|
+
|
55
|
+
To iterate over random data,
|
56
|
+
|
57
|
+
<pre><code>
|
58
|
+
> gen.each(5) { puts integer }
|
59
|
+
296971291
|
60
|
+
504994512
|
61
|
+
-402790444
|
62
|
+
113152364
|
63
|
+
502842783
|
64
|
+
=> nil
|
65
|
+
</code></pre>
|
66
|
+
|
67
|
+
To get one value of random data,
|
68
|
+
|
69
|
+
<pre><code>
|
70
|
+
> gen.value { integer }
|
71
|
+
=> 278101042
|
72
|
+
</code></pre>
|
73
|
+
|
74
|
+
The optional argument @limit@ is used with generator guard. By default, if you want to generate n items, the generator tries at most n * 10 times.
|
75
|
+
|
76
|
+
This almost always succeeds,
|
77
|
+
|
78
|
+
<pre><code>
|
79
|
+
> gen.map(5) { i = integer; guard i > 0; i }
|
80
|
+
=> [511765059, 250554234, 305947804, 127809156, 285960387]
|
81
|
+
</code></pre>
|
82
|
+
|
83
|
+
This always fails,
|
84
|
+
|
85
|
+
<pre><code>
|
86
|
+
> gen.map(10) { guard integer.is_a?(Float) }
|
87
|
+
Rant::TooManyTries: Exceed gen limit 100: 101 failed guards)
|
88
|
+
</code></pre>
|
89
|
+
|
90
|
+
h2. Random Generating Methods
|
91
|
+
|
92
|
+
The API is similiar to QuickCheck, but not exactly the same. In particular @choose@ picks a random element from an array, and @range@ picks a integer from an interval.
|
93
|
+
|
94
|
+
h3. Simple Randomness
|
95
|
+
|
96
|
+
<pre><code>
|
97
|
+
Rant#integer(n=nil)
|
98
|
+
random positive or negative integer. Fixnum only.
|
99
|
+
Rant#range(lo,hi)
|
100
|
+
random integer between lo and hi.
|
101
|
+
Rant#float
|
102
|
+
random float
|
103
|
+
Rant#bool
|
104
|
+
true or false
|
105
|
+
Rant#literal(value)
|
106
|
+
No-op. returns value.
|
107
|
+
Rant#choose(*vals)
|
108
|
+
Pick one value from among vals.
|
109
|
+
</code></pre>
|
110
|
+
|
111
|
+
h3. Meta Randomness
|
112
|
+
|
113
|
+
A rant generator is just a mini interpreter. It's often useful to go meta,
|
114
|
+
|
115
|
+
<pre><code>
|
116
|
+
Rant#call(gen)
|
117
|
+
If gen is a Symbol, just do a method call with send.
|
118
|
+
If gen is an Array, the first element of the array is the method name, the rest are args.
|
119
|
+
If gen is a Proc, instance_eval it with the generator.
|
120
|
+
</code></pre>
|
121
|
+
|
122
|
+
<pre><code>
|
123
|
+
> gen.value { call(:integer) }
|
124
|
+
=> -240998958
|
125
|
+
</code></pre>
|
126
|
+
|
127
|
+
<pre><code>
|
128
|
+
> gen.value { call([:range,0,10]) }
|
129
|
+
=> 2
|
130
|
+
</code></pre>
|
131
|
+
|
132
|
+
<pre><code>
|
133
|
+
> gen.value { call(Proc.new { [integer] })}
|
134
|
+
=> [522807620]
|
135
|
+
</code></pre>
|
136
|
+
|
137
|
+
The @call@ method is useful to implement other abstractions (See next subsection).
|
138
|
+
|
139
|
+
<pre><code>
|
140
|
+
Rant#branch(*args)
|
141
|
+
Pick a random arg among args, and Rant#call it.
|
142
|
+
</code></pre>
|
143
|
+
|
144
|
+
50-50 chance getting an integer or float,
|
145
|
+
|
146
|
+
<pre><code>
|
147
|
+
> gen.value { branch :integer, :float }
|
148
|
+
=> 0.0489446702931332
|
149
|
+
> gen.value { branch :integer, :float }
|
150
|
+
=> 494934533
|
151
|
+
</code></pre>
|
152
|
+
|
153
|
+
|
154
|
+
h3. Frequencies
|
155
|
+
|
156
|
+
<pre><code>
|
157
|
+
Rant#freq(*pairs)
|
158
|
+
Takes a list of 2-tuples, the first of which is the weight, and the second a Rant#callable value, and returns a random value picked from the pairs. Follows the distribution pattern specified by the weights.
|
159
|
+
</code></pre>
|
160
|
+
|
161
|
+
Twice as likely to get a float than integer. Never gets a ranged integer.
|
162
|
+
|
163
|
+
<pre><code>
|
164
|
+
> gen.value { freq [1,:integer], [2,:float], [0,:range,0,10] }
|
165
|
+
</code></pre>
|
166
|
+
|
167
|
+
If the "pair" is not an array, but just a symbol, @freq@ assumes that the weight is 1.
|
168
|
+
|
169
|
+
<pre><code>
|
170
|
+
# 50-50 between integer and float
|
171
|
+
> gen.value { freq :integer, :float }
|
172
|
+
</code></pre>
|
173
|
+
|
174
|
+
If a "pair" is an Array, but the first element is not an Integer, @freq@ assumes that it's a Rant method-call with arguments, and the weight is one.
|
175
|
+
|
176
|
+
<pre><code>
|
177
|
+
# 50-50 chance generating integer limited by 10, or by 20.
|
178
|
+
> gen.value { freq [:integer,10], [:integer 20] }
|
179
|
+
</code></pre>
|
180
|
+
|
181
|
+
|
182
|
+
|
183
|
+
h3. Sized Structure
|
184
|
+
|
185
|
+
A Rant generator keeps track of how large a datastructure it should generate with its @size@ attribute.
|
186
|
+
|
187
|
+
<pre><code>
|
188
|
+
Rant#size
|
189
|
+
returns the current size
|
190
|
+
Rant#sized(n,&block)
|
191
|
+
sets the size for the duration of recursive call of block. Block is instance_eval with the generator.
|
192
|
+
</code></pre>
|
193
|
+
|
194
|
+
Rant provides two methods that depends on the size
|
195
|
+
|
196
|
+
<pre><code>
|
197
|
+
Rant#array(*branches)
|
198
|
+
returns a sized array consisted of elements by Rant#calling random branches.
|
199
|
+
Rant#string(char_class=:print)
|
200
|
+
returns a sized random string, consisted of only chars from a char_class.
|
201
|
+
</code></pre>
|
202
|
+
|
203
|
+
The avaiable char classes for strings are:
|
204
|
+
|
205
|
+
<pre><code>
|
206
|
+
:alnum
|
207
|
+
:alpha
|
208
|
+
:blank
|
209
|
+
:cntrl
|
210
|
+
:digit
|
211
|
+
:graph
|
212
|
+
:lower
|
213
|
+
:print
|
214
|
+
:punct
|
215
|
+
:space
|
216
|
+
:upper
|
217
|
+
:xdigit
|
218
|
+
:ascii
|
219
|
+
</code></pre>
|
220
|
+
|
221
|
+
<pre><code>
|
222
|
+
# sized 10 array of integer or float
|
223
|
+
> gen.value { sized(10) { array(:integer,:float)}}
|
224
|
+
=> [417733046, -375385433, 0.967812380000118, 26478621, 0.888588160450082, 250944144, 305584916, -151858342, 0.308123867823313, 0.316824642414253]
|
225
|
+
|
226
|
+
# fails if you forget to set the size.
|
227
|
+
> gen.value { array(:integer,:float)}
|
228
|
+
RuntimeError: size not set
|
229
|
+
|
230
|
+
</code></pre>
|
231
|
+
|
232
|
+
If you set the size once, it applies to all subsequent recursive structures. Here's a sized 10 array of sized 10 strings,
|
233
|
+
|
234
|
+
<pre><code>
|
235
|
+
> gen.value { sized(10) { array(:string)} }
|
236
|
+
=> ["1c}C/,9I#}", "hpA/UWPJ\\j", "H'~ERtI`|]", "%OUaW\\%uQZ", "Z2QdY=G~G!", "H<o|<FARGQ", "g>ojnxGDT3", "]a:L[B>bhb", "_Kl=&{tH^<", "ly]Yfb?`6c"]
|
237
|
+
</code></pre>
|
238
|
+
|
239
|
+
Or a sized 10 array of sized 5 strings,
|
240
|
+
|
241
|
+
<pre><code>
|
242
|
+
> gen.value { sized(10) { array Proc.new {sized(5) {string}}}}
|
243
|
+
=> ["S\"jf ", "d\\F-$", "-_8pa", "IN0iF", "SxRV$", ".{kQ7", "6>;fo", "}.D8)", "P(tS'", "y0v/v"]
|
244
|
+
</code></pre>
|
245
|
+
|
246
|
+
Rant#array actually just delegate to Rant#freq, so you can use freq pairs:
|
247
|
+
|
248
|
+
<pre><code>
|
249
|
+
> gen.value { sized(10) {array [1,:integer],[2,:float] }}
|
250
|
+
=> [0.983334733158678, -418176338, 0.976947175363592, 0.703390570421286, -478680395, 5483631, 0.966944106783513, 110469205, 0.540859146793544, 0.521813810037025]
|
251
|
+
</code></pre>
|
252
|
+
|
253
|
+
|
254
|
+
h1. Property Testing
|
255
|
+
|
256
|
+
Rant extends Test::Unit for property testing. The extension is in its own module. So you need to require it.
|
257
|
+
|
258
|
+
<pre><code>
|
259
|
+
require 'rant/check'
|
260
|
+
</code></pre>
|
261
|
+
|
262
|
+
It defines,
|
263
|
+
|
264
|
+
<pre><code>
|
265
|
+
Test::Unit::Assertions#property_of(&block)
|
266
|
+
The block is used to generate random data with a generator. The method returns a Rant::Property instance, that has the method 'check'.
|
267
|
+
</code></pre>
|
268
|
+
|
269
|
+
It's like this, using the gem 'shoulda'
|
270
|
+
|
271
|
+
<pre><code>
|
272
|
+
# checks that integer only generates fixnum.
|
273
|
+
should "generate Fixnum only" do
|
274
|
+
property_of { integer }.check { |i| assert i.is_a?(Integer) }
|
275
|
+
end
|
276
|
+
</code></pre>
|
277
|
+
|
278
|
+
The check block takes the generated data as its argument. One idiom I find useful is to include a parameter of the random data for the check argument. For example, if I want to check that Rant#array generates the right sized array, I could say,
|
279
|
+
|
280
|
+
<pre><code>
|
281
|
+
should "generate right sized array" do
|
282
|
+
property_of {
|
283
|
+
len = integer
|
284
|
+
[len,sized(len) { array :integer }]
|
285
|
+
}.check { |(len,arr)|
|
286
|
+
assert_equal len, arr.length
|
287
|
+
}
|
288
|
+
end
|
289
|
+
</code></pre>
|
290
|
+
|
291
|
+
That's about it. Enjoy :)
|
292
|
+
|
293
|
+
|
294
|
+
h1. Copyright
|
295
|
+
|
296
|
+
Copyright (c) 2009 Howard Yeh. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "rantly"
|
8
|
+
gem.summary = %Q{Ruby Imperative Random Data Generator and Quickcheck}
|
9
|
+
gem.email = "hayeah@gmail.com"
|
10
|
+
gem.homepage = "http://github.com/hayeah/rantly"
|
11
|
+
gem.authors = ["Howard Yeh"]
|
12
|
+
|
13
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
14
|
+
end
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'rake/testtask'
|
20
|
+
Rake::TestTask.new(:test) do |test|
|
21
|
+
test.libs << 'lib' << 'test'
|
22
|
+
test.pattern = 'test/**/*_test.rb'
|
23
|
+
test.verbose = true
|
24
|
+
end
|
25
|
+
|
26
|
+
begin
|
27
|
+
require 'rcov/rcovtask'
|
28
|
+
Rcov::RcovTask.new do |test|
|
29
|
+
test.libs << 'test'
|
30
|
+
test.pattern = 'test/**/*_test.rb'
|
31
|
+
test.verbose = true
|
32
|
+
end
|
33
|
+
rescue LoadError
|
34
|
+
task :rcov do
|
35
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
task :default => :test
|
41
|
+
|
42
|
+
require 'rake/rdoctask'
|
43
|
+
Rake::RDocTask.new do |rdoc|
|
44
|
+
if File.exist?('VERSION.yml')
|
45
|
+
config = YAML.load(File.read('VERSION.yml'))
|
46
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
47
|
+
else
|
48
|
+
version = ""
|
49
|
+
end
|
50
|
+
|
51
|
+
rdoc.rdoc_dir = 'rdoc'
|
52
|
+
rdoc.title = "rant #{version}"
|
53
|
+
rdoc.rdoc_files.include('README*')
|
54
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
55
|
+
end
|
56
|
+
|
data/Rant.gemspec
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{Rant}
|
8
|
+
s.version = "0.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Howard Yeh"]
|
12
|
+
s.date = %q{2009-10-01}
|
13
|
+
s.email = %q{hayeah@gmail.com}
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"LICENSE",
|
16
|
+
"README.textile"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".document",
|
20
|
+
".gitignore",
|
21
|
+
"LICENSE",
|
22
|
+
"README.textile",
|
23
|
+
"Rakefile",
|
24
|
+
"Rant.gemspec",
|
25
|
+
"VERSION.yml",
|
26
|
+
"lib/rant.rb",
|
27
|
+
"lib/rant/check.rb",
|
28
|
+
"lib/rant/data.rb",
|
29
|
+
"lib/rant/generator.rb",
|
30
|
+
"lib/rant/silly.rb",
|
31
|
+
"test/rant_test.rb",
|
32
|
+
"test/test_helper.rb"
|
33
|
+
]
|
34
|
+
s.has_rdoc = true
|
35
|
+
s.homepage = %q{http://github.com/hayeah/rant}
|
36
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
37
|
+
s.require_paths = ["lib"]
|
38
|
+
s.rubygems_version = %q{1.3.1}
|
39
|
+
s.summary = %q{Ruby Imperative Random Data Generator and Quickcheck}
|
40
|
+
s.test_files = [
|
41
|
+
"test/rant_test.rb",
|
42
|
+
"test/test_helper.rb"
|
43
|
+
]
|
44
|
+
|
45
|
+
if s.respond_to? :specification_version then
|
46
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
47
|
+
s.specification_version = 2
|
48
|
+
|
49
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
50
|
+
else
|
51
|
+
end
|
52
|
+
else
|
53
|
+
end
|
54
|
+
end
|
data/VERSION.yml
ADDED
data/lib/rant.rb
ADDED
data/lib/rant/data.rb
ADDED
@@ -0,0 +1,263 @@
|
|
1
|
+
class Rant
|
2
|
+
|
3
|
+
class << self
|
4
|
+
attr_writer :default_size
|
5
|
+
def singleton
|
6
|
+
@singleton ||= Rant.new
|
7
|
+
@singleton
|
8
|
+
end
|
9
|
+
|
10
|
+
def default_size
|
11
|
+
@default_size || 6
|
12
|
+
end
|
13
|
+
|
14
|
+
def gen
|
15
|
+
self.singleton
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class GuardFailure < RuntimeError
|
20
|
+
end
|
21
|
+
|
22
|
+
class TooManyTries < RuntimeError
|
23
|
+
|
24
|
+
def initialize(limit,nfailed)
|
25
|
+
@limit = limit
|
26
|
+
@nfailed = nfailed
|
27
|
+
end
|
28
|
+
|
29
|
+
def tries
|
30
|
+
@nfailed
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
"Exceed gen limit #{@limit}: #{@nfailed} failed guards)"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# limit attempts to 10 times of how many things we want to generate
|
39
|
+
def each(n,limit=10,&block)
|
40
|
+
generate(n,limit,block)
|
41
|
+
end
|
42
|
+
|
43
|
+
def map(n,limit=10,&block)
|
44
|
+
acc = []
|
45
|
+
generate(n,limit,block) do |val|
|
46
|
+
acc << val
|
47
|
+
end
|
48
|
+
acc
|
49
|
+
end
|
50
|
+
|
51
|
+
def value(limit=10,&block)
|
52
|
+
generate(1,limit,block) do |val|
|
53
|
+
return val
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def generate(n,limit_arg,gen_block,&handler)
|
58
|
+
limit = n * limit_arg
|
59
|
+
nfailed = 0
|
60
|
+
nsuccess = 0
|
61
|
+
while nsuccess < n
|
62
|
+
raise TooManyTries.new(limit_arg*n,nfailed) if limit < 0
|
63
|
+
begin
|
64
|
+
val = self.instance_eval(&gen_block)
|
65
|
+
rescue GuardFailure
|
66
|
+
nfailed += 1
|
67
|
+
limit -= 1
|
68
|
+
next
|
69
|
+
end
|
70
|
+
nsuccess += 1
|
71
|
+
limit -= 1
|
72
|
+
handler.call(val) if handler
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
attr_accessor :classifiers
|
77
|
+
|
78
|
+
def initialize
|
79
|
+
reset
|
80
|
+
end
|
81
|
+
|
82
|
+
def reset
|
83
|
+
@size = nil
|
84
|
+
@classifiers = Hash.new(0)
|
85
|
+
end
|
86
|
+
|
87
|
+
def classify(classifier)
|
88
|
+
@classifiers[classifier] += 1
|
89
|
+
end
|
90
|
+
|
91
|
+
def guard(test)
|
92
|
+
raise GuardFailure.new unless test
|
93
|
+
end
|
94
|
+
|
95
|
+
def size
|
96
|
+
@size || Rant.default_size
|
97
|
+
end
|
98
|
+
|
99
|
+
def sized(n,&block)
|
100
|
+
raise "size needs to be greater than zero" if n < 0
|
101
|
+
old_size = @size
|
102
|
+
@size = n
|
103
|
+
r = self.instance_eval(&block)
|
104
|
+
@size = old_size
|
105
|
+
return r
|
106
|
+
end
|
107
|
+
|
108
|
+
# wanna avoid going into Bignum when calling range with these.
|
109
|
+
INTEGER_MAX = (2**(0.size * 8 -2) -1) / 2
|
110
|
+
INTEGER_MIN = -(INTEGER_MAX)
|
111
|
+
def integer(limit=nil)
|
112
|
+
case limit
|
113
|
+
when Range
|
114
|
+
hi = limit.end
|
115
|
+
lo = limit.begin
|
116
|
+
when Integer
|
117
|
+
raise "n should be greater than zero" if limit < 0
|
118
|
+
hi, lo = limit, -limit
|
119
|
+
else
|
120
|
+
hi, lo = INTEGER_MAX, INTEGER_MIN
|
121
|
+
end
|
122
|
+
range(lo,hi)
|
123
|
+
end
|
124
|
+
|
125
|
+
def positive_integer
|
126
|
+
range(0)
|
127
|
+
end
|
128
|
+
|
129
|
+
def float
|
130
|
+
rand
|
131
|
+
end
|
132
|
+
|
133
|
+
def range(lo=nil,hi=nil)
|
134
|
+
lo ||= INTEGER_MIN
|
135
|
+
hi ||= INTEGER_MAX
|
136
|
+
rand(hi+1-lo) + lo
|
137
|
+
end
|
138
|
+
|
139
|
+
def call(gen,*args)
|
140
|
+
case gen
|
141
|
+
when Symbol
|
142
|
+
return self.send(gen,*args)
|
143
|
+
when Array
|
144
|
+
raise "empty array" if gen.empty?
|
145
|
+
return self.send(gen[0],*gen[1..-1])
|
146
|
+
when Proc
|
147
|
+
return self.instance_eval(&gen)
|
148
|
+
else
|
149
|
+
raise "don't know how to call type: #{gen}"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def branch(*gens)
|
154
|
+
self.call(choose(*gens))
|
155
|
+
end
|
156
|
+
|
157
|
+
def choose(*vals)
|
158
|
+
vals[range(0,vals.length-1)]
|
159
|
+
end
|
160
|
+
|
161
|
+
def literal(value)
|
162
|
+
value
|
163
|
+
end
|
164
|
+
|
165
|
+
def boolean
|
166
|
+
range(0,1) == 0 ? true : false
|
167
|
+
end
|
168
|
+
|
169
|
+
def freq(*pairs)
|
170
|
+
pairs = pairs.map do |pair|
|
171
|
+
case pair
|
172
|
+
when Symbol, String, Proc
|
173
|
+
[1,pair]
|
174
|
+
when Array
|
175
|
+
unless pair.first.is_a?(Integer)
|
176
|
+
[1] + pair
|
177
|
+
else
|
178
|
+
pair
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
total = pairs.inject(0) { |sum,p| sum + p.first }
|
183
|
+
raise(RuntimeError, "Illegal frequency:#{pairs.inspect}") if total == 0
|
184
|
+
pos = range(1,total)
|
185
|
+
pairs.each do |p|
|
186
|
+
weight, gen, *args = p
|
187
|
+
if pos <= p[0]
|
188
|
+
return self.call(gen,*args)
|
189
|
+
else
|
190
|
+
pos -= weight
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def array(*freq_pairs)
|
196
|
+
acc = []
|
197
|
+
self.size.times { acc << freq(*freq_pairs) }
|
198
|
+
acc
|
199
|
+
end
|
200
|
+
|
201
|
+
module Chars
|
202
|
+
|
203
|
+
class << self
|
204
|
+
ASCII = ""
|
205
|
+
(0..127).to_a.each do |i|
|
206
|
+
ASCII << i
|
207
|
+
end
|
208
|
+
|
209
|
+
def of(regexp)
|
210
|
+
ASCII.scan(regexp).to_a.map! { |char| char[0] }
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
ALNUM = Chars.of /[[:alnum:]]/
|
215
|
+
ALPHA = Chars.of /[[:alpha:]]/
|
216
|
+
BLANK = Chars.of /[[:blank:]]/
|
217
|
+
CNTRL = Chars.of /[[:cntrl:]]/
|
218
|
+
DIGIT = Chars.of /[[:digit:]]/
|
219
|
+
GRAPH = Chars.of /[[:graph:]]/
|
220
|
+
LOWER = Chars.of /[[:lower:]]/
|
221
|
+
PRINT = Chars.of /[[:print:]]/
|
222
|
+
PUNCT = Chars.of /[[:punct:]]/
|
223
|
+
SPACE = Chars.of /[[:space:]]/
|
224
|
+
UPPER = Chars.of /[[:upper:]]/
|
225
|
+
XDIGIT = Chars.of /[[:xdigit:]]/
|
226
|
+
ASCII = Chars.of /./
|
227
|
+
|
228
|
+
|
229
|
+
CLASSES = {
|
230
|
+
:alnum => ALNUM,
|
231
|
+
:alpha => ALPHA,
|
232
|
+
:blank => BLANK,
|
233
|
+
:cntrl => CNTRL,
|
234
|
+
:digit => DIGIT,
|
235
|
+
:graph => GRAPH,
|
236
|
+
:lower => LOWER,
|
237
|
+
:print => PRINT,
|
238
|
+
:punct => PUNCT,
|
239
|
+
:space => SPACE,
|
240
|
+
:upper => UPPER,
|
241
|
+
:xdigit => XDIGIT,
|
242
|
+
:ascii => ASCII,
|
243
|
+
}
|
244
|
+
|
245
|
+
end
|
246
|
+
|
247
|
+
def string(char_class=:print)
|
248
|
+
chars = case char_class
|
249
|
+
when Regexp
|
250
|
+
Chars.of(char_class)
|
251
|
+
when Symbol
|
252
|
+
Chars::CLASSES[char_class]
|
253
|
+
end
|
254
|
+
raise "bad arg" unless chars
|
255
|
+
str = ""
|
256
|
+
size.times do
|
257
|
+
str << choose(*chars)
|
258
|
+
end
|
259
|
+
str
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'rant'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
class Rant::Property
|
6
|
+
|
7
|
+
def initialize(property)
|
8
|
+
@property = property
|
9
|
+
end
|
10
|
+
|
11
|
+
def check(n=100,limit=10,&assertion)
|
12
|
+
i = 0
|
13
|
+
test_data = nil
|
14
|
+
begin
|
15
|
+
Rant.singleton.generate(n,limit,@property) do |val|
|
16
|
+
test_data = val
|
17
|
+
assertion.call(val) if assertion
|
18
|
+
puts "" if i % 100 == 0
|
19
|
+
print "." if i % 10 == 0
|
20
|
+
i += 1
|
21
|
+
end
|
22
|
+
puts
|
23
|
+
puts "success: #{i} tests"
|
24
|
+
rescue Rant::TooManyTries => e
|
25
|
+
puts
|
26
|
+
puts "too many tries: #{e.tries}"
|
27
|
+
raise e
|
28
|
+
rescue => boom
|
29
|
+
puts
|
30
|
+
puts "failure: #{i} tests, on:"
|
31
|
+
pp test_data
|
32
|
+
raise boom
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def report
|
37
|
+
distribs = self.classifiers.sort { |a,b| b[1] <=> a[1] }
|
38
|
+
total = distribs.inject(0) { |sum,pair| sum + pair[1]}
|
39
|
+
distribs.each do |(classifier,count)|
|
40
|
+
format "%10.5f%% of => %s", count, classifier
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module Test::Unit::Assertions
|
46
|
+
def property_of(&block)
|
47
|
+
Rant::Property.new(block)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
data/lib/rant/silly.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'rant'
|
2
|
+
module Rant::Silly
|
3
|
+
class << self
|
4
|
+
def love_letter(n)
|
5
|
+
Rant.new.extend(Rant::Silly::Love).value { letter(n) }
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module Rant::Silly::Love
|
11
|
+
|
12
|
+
def letter(n=3)
|
13
|
+
body = sized(n) { array(:paragraph) }.join "\n\n"
|
14
|
+
<<-EOS
|
15
|
+
#{address}:
|
16
|
+
|
17
|
+
#{body}
|
18
|
+
|
19
|
+
#{sign}
|
20
|
+
|
21
|
+
#{post_script}
|
22
|
+
EOS
|
23
|
+
end
|
24
|
+
|
25
|
+
def address
|
26
|
+
"my #{extremifier} #{pedestal_label}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def extremifier
|
30
|
+
choose "most","ultimate","unbelievable","incredible","burning"
|
31
|
+
end
|
32
|
+
|
33
|
+
def pedestal_label
|
34
|
+
choose "beloved","desire","dove","virgin goddess","existential solution","lighthouse","beacon","holy mother","queen","mistress"
|
35
|
+
end
|
36
|
+
|
37
|
+
def double_plus_good
|
38
|
+
choose "holy","shiny","glittering","joyous","delicious"
|
39
|
+
end
|
40
|
+
|
41
|
+
def how_i_feel
|
42
|
+
choose "my heart aches","my spine pines","my spirit wanders and wonders","my soul is awed","my loin burns"
|
43
|
+
end
|
44
|
+
|
45
|
+
def paragraph
|
46
|
+
sized(range(2,4)) { array(:sentence)}.join " "
|
47
|
+
end
|
48
|
+
|
49
|
+
def sentence
|
50
|
+
freq \
|
51
|
+
Proc.new { "when #{how_i_feel}, my #{pedestal_label}, i feel the need to #{stalk_action}, but this is not because #{how_i_feel}, but rather a symptom of my being your #{whoami}." },
|
52
|
+
Proc.new { "because you are my #{pedestal_label}, and i am your #{whoami}, no, rather your #{whoami}, #{fragment}."},
|
53
|
+
Proc.new { "do not think that saying '#{how_i_feel}' suffices to show the depth of how #{how_i_feel}, because more than that, #{fantasy}"},
|
54
|
+
Proc.new { "as a #{whoami}, that #{how_i_feel} is never quite enough for you, my #{double_plus_good} #{pedestal_label}."}
|
55
|
+
end
|
56
|
+
|
57
|
+
def fragment
|
58
|
+
fun = fantasy
|
59
|
+
choose "i hope to god #{fun}", "i believe #{fun}", "i will that #{fun}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def caused_by
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
def whoami
|
67
|
+
"#{extremifier} #{humbleizer} #{groveler}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def sign
|
71
|
+
"your #{whoami}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def humbleizer
|
75
|
+
choose "undeserving","insignificant","unremarkable","fearful","menial"
|
76
|
+
end
|
77
|
+
|
78
|
+
def groveler
|
79
|
+
choose "slave","servant","captive","lapdog"
|
80
|
+
end
|
81
|
+
|
82
|
+
def post_script
|
83
|
+
"ps: #{i_am_stalking_you}, and hope that #{fantasy}"
|
84
|
+
end
|
85
|
+
|
86
|
+
def i_am_stalking_you
|
87
|
+
"every #{time_duration} i #{stalk_action}"
|
88
|
+
end
|
89
|
+
|
90
|
+
def fantasy
|
91
|
+
freq \
|
92
|
+
Proc.new {
|
93
|
+
make = choose "raise","nurture","bring into the world"
|
94
|
+
babies = choose "brood of babies","#{double_plus_good} angels"
|
95
|
+
good = double_plus_good
|
96
|
+
effect = choose "the world becomes all the more #{good}",
|
97
|
+
"we may at the end of our lives rest in #{good} peace.",
|
98
|
+
"you, my #{pedestal_label}, would continue to live."
|
99
|
+
"we would #{make} #{babies}, so #{effect}."
|
100
|
+
},
|
101
|
+
Proc.new {
|
102
|
+
do_thing = choose "kiss","hug","read poetry to each other","massage","whisper empty nothings into each others' ears","be with each other, and oblivious to the entire world"
|
103
|
+
affect = choose "joy", "mindfulness", "calm", "sanctity"
|
104
|
+
"we would #{do_thing} with #{double_plus_good} #{affect}"
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
def stalk_action
|
109
|
+
choose "think of you","dream of us together","look at your picture and sigh"
|
110
|
+
end
|
111
|
+
|
112
|
+
def time_duration
|
113
|
+
choose "once in a while","night","day","hour","minute"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
data/lib/rant/spec.rb
ADDED
data/rantly.gemspec
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{rantly}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Howard Yeh"]
|
12
|
+
s.date = %q{2009-11-30}
|
13
|
+
s.email = %q{hayeah@gmail.com}
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"LICENSE",
|
16
|
+
"README.textile"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".document",
|
20
|
+
".gitignore",
|
21
|
+
"LICENSE",
|
22
|
+
"README.textile",
|
23
|
+
"Rakefile",
|
24
|
+
"Rant.gemspec",
|
25
|
+
"VERSION.yml",
|
26
|
+
"lib/rant.rb",
|
27
|
+
"lib/rant/data.rb",
|
28
|
+
"lib/rant/generator.rb",
|
29
|
+
"lib/rant/property.rb",
|
30
|
+
"lib/rant/silly.rb",
|
31
|
+
"lib/rant/spec.rb",
|
32
|
+
"rantly.gemspec",
|
33
|
+
"test/rant_test.rb",
|
34
|
+
"test/test_helper.rb"
|
35
|
+
]
|
36
|
+
s.homepage = %q{http://github.com/hayeah/rantly}
|
37
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
38
|
+
s.require_paths = ["lib"]
|
39
|
+
s.rubygems_version = %q{1.3.5}
|
40
|
+
s.summary = %q{Ruby Imperative Random Data Generator and Quickcheck}
|
41
|
+
s.test_files = [
|
42
|
+
"test/rant_test.rb",
|
43
|
+
"test/test_helper.rb"
|
44
|
+
]
|
45
|
+
|
46
|
+
if s.respond_to? :specification_version then
|
47
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
48
|
+
s.specification_version = 3
|
49
|
+
|
50
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
51
|
+
else
|
52
|
+
end
|
53
|
+
else
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
data/test/rant_test.rb
ADDED
@@ -0,0 +1,284 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'rant/check'
|
3
|
+
|
4
|
+
module RantTest
|
5
|
+
end
|
6
|
+
|
7
|
+
# check we generate the right kind of data.
|
8
|
+
## doesn't check for distribution
|
9
|
+
class RantTest::Generator < Test::Unit::TestCase
|
10
|
+
def setup
|
11
|
+
Rant.gen.reset
|
12
|
+
end
|
13
|
+
|
14
|
+
should "fail test generation" do
|
15
|
+
assert_raises(Rant::TooManyTries) {
|
16
|
+
property_of { guard range(0,1) < 0 }.check
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
should "generate literal value by returning itself" do
|
21
|
+
property_of {
|
22
|
+
i = integer
|
23
|
+
[i,literal(i)]
|
24
|
+
}.check { |(a,b)|
|
25
|
+
assert_equal a, b
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
should "generate integer in range" do
|
30
|
+
property_of {
|
31
|
+
i = integer
|
32
|
+
[i,range(i,i)]
|
33
|
+
}.check { |(a,b)|
|
34
|
+
assert_equal a, b
|
35
|
+
}
|
36
|
+
property_of {
|
37
|
+
lo, hi = [integer(100),integer(100)].sort
|
38
|
+
[lo,hi,range(lo,hi)]
|
39
|
+
}.check { |(lo,hi,int)|
|
40
|
+
assert((lo..hi).include?(int))
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
should "generate Fixnum only" do
|
45
|
+
property_of { integer }.check { |i| assert i.is_a?(Integer) }
|
46
|
+
end
|
47
|
+
|
48
|
+
should "generate integer less than abs(n)" do
|
49
|
+
property_of {
|
50
|
+
n = range(0,10)
|
51
|
+
[n,integer(n)]
|
52
|
+
}.check {|(n,i)|
|
53
|
+
assert n.abs >= i.abs
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
should "generate Float" do
|
58
|
+
property_of { float }.check { |f| assert f.is_a?(Float)}
|
59
|
+
end
|
60
|
+
|
61
|
+
should "generate Boolean" do
|
62
|
+
property_of { bool }.check { |t|
|
63
|
+
assert t == true || t == false
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
should "generate empty strings" do
|
68
|
+
property_of {
|
69
|
+
sized(0) { string }
|
70
|
+
}.check { |s|
|
71
|
+
assert s.empty?
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
should "generate strings with the right regexp char classes" do
|
76
|
+
char_classes = Rant::Chars::CLASSES.keys
|
77
|
+
property_of {
|
78
|
+
char_class = choose(*char_classes)
|
79
|
+
len = range(0,10)
|
80
|
+
sized(len) { [len,char_class,string(char_class)]}
|
81
|
+
}.check { |(len,char_class,str)|
|
82
|
+
t = true
|
83
|
+
chars = Rant::Chars::CLASSES[char_class]
|
84
|
+
str.each_byte { |c|
|
85
|
+
unless chars.include?(c)
|
86
|
+
t = false
|
87
|
+
break
|
88
|
+
end
|
89
|
+
}
|
90
|
+
assert_equal len, str.length
|
91
|
+
assert t
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
should "generate strings matching regexp" do
|
96
|
+
property_of {
|
97
|
+
sized(10) { string(/[abcd]/) }
|
98
|
+
}.check { |s|
|
99
|
+
assert s =~ /[abcd]+/
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
# call
|
104
|
+
|
105
|
+
should "call Symbol as method call (no arg)" do
|
106
|
+
property_of {call(:integer)}.check { |i| i.is_a?(Integer)}
|
107
|
+
end
|
108
|
+
|
109
|
+
should "call Symbol as method call (with arg)" do
|
110
|
+
property_of {
|
111
|
+
n = range(0,100)
|
112
|
+
[n,call(:integer,n)]
|
113
|
+
}.check { |(n,i)|
|
114
|
+
assert n.abs >= i.abs
|
115
|
+
}
|
116
|
+
end
|
117
|
+
|
118
|
+
should "call Array by calling first element as method, the rest as args" do
|
119
|
+
assert_raise(RuntimeError) {
|
120
|
+
Rant.gen.value {
|
121
|
+
call []
|
122
|
+
}
|
123
|
+
}
|
124
|
+
property_of {
|
125
|
+
i = integer
|
126
|
+
[i,call(choose([:literal,i],[:range,i,i]))]
|
127
|
+
}.check { |(a,b)|
|
128
|
+
assert_equal a, b
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
should "call Proc with generator.instance_eval" do
|
133
|
+
property_of {
|
134
|
+
call Proc.new { true }
|
135
|
+
}.check { |o|
|
136
|
+
assert_equal true, o
|
137
|
+
}
|
138
|
+
property_of {
|
139
|
+
i0 = range(0,100)
|
140
|
+
i1,s = call Proc.new {
|
141
|
+
range(i0+1,i0+100)
|
142
|
+
}
|
143
|
+
[i0,i1]
|
144
|
+
}.check { |(i0,i1)|
|
145
|
+
assert i0.is_a?(Fixnum) && i1.is_a?(Fixnum)
|
146
|
+
assert i0 != i1
|
147
|
+
assert i1 > i0
|
148
|
+
}
|
149
|
+
end
|
150
|
+
|
151
|
+
should "raise if calling on any other value" do
|
152
|
+
assert_raise(RuntimeError) {
|
153
|
+
Rant.gen.call 0
|
154
|
+
}
|
155
|
+
end
|
156
|
+
|
157
|
+
# branch
|
158
|
+
|
159
|
+
should "branch by Rant#calling one of the args" do
|
160
|
+
property_of {
|
161
|
+
branch :integer, :integer, :integer
|
162
|
+
}.check { |o|
|
163
|
+
assert o.is_a?(Fixnum)
|
164
|
+
}
|
165
|
+
property_of {
|
166
|
+
sized(10) { branch :integer, :string }
|
167
|
+
}.check { |o|
|
168
|
+
assert o.is_a?(Fixnum) || o.is_a?(String)
|
169
|
+
}
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
# choose
|
174
|
+
|
175
|
+
should "choose a value from args " do
|
176
|
+
property_of {
|
177
|
+
choose
|
178
|
+
}.check {|o|
|
179
|
+
assert_nil o
|
180
|
+
}
|
181
|
+
property_of {
|
182
|
+
choose 1
|
183
|
+
}.check { |o|
|
184
|
+
assert_equal 1, o
|
185
|
+
}
|
186
|
+
property_of {
|
187
|
+
choose 1,2
|
188
|
+
}.check { |o|
|
189
|
+
assert o == 1 || o == 2
|
190
|
+
}
|
191
|
+
property_of {
|
192
|
+
arr = sized(10) { array(:integer) }
|
193
|
+
choose(*arr)
|
194
|
+
}.check { |o|
|
195
|
+
assert o.is_a?(Fixnum)
|
196
|
+
}
|
197
|
+
property_of {
|
198
|
+
# array of array of ints
|
199
|
+
arr = sized(10) { array(Proc.new { array(:integer)})}
|
200
|
+
# choose an array from an array of arrays of ints
|
201
|
+
choose(*arr)
|
202
|
+
}.check { |arr|
|
203
|
+
assert arr.is_a?(Array)
|
204
|
+
assert arr.all? { |o| o.is_a?(Fixnum)}
|
205
|
+
}
|
206
|
+
end
|
207
|
+
|
208
|
+
# freq
|
209
|
+
|
210
|
+
should "not pick an element with 0 frequency" do
|
211
|
+
property_of {
|
212
|
+
sized(10) {
|
213
|
+
array Proc.new { freq([0,:string],[1,:integer]) }
|
214
|
+
}
|
215
|
+
}.check { |arr|
|
216
|
+
assert arr.all? { |o| o.is_a?(Integer)}
|
217
|
+
}
|
218
|
+
end
|
219
|
+
|
220
|
+
should "handle degenerate freq pairs" do
|
221
|
+
assert_raise(RuntimeError) {
|
222
|
+
Rant.gen.value {
|
223
|
+
freq
|
224
|
+
}
|
225
|
+
}
|
226
|
+
property_of {
|
227
|
+
i = integer
|
228
|
+
[i,freq([:literal,i])]
|
229
|
+
}.check { |(a,b)|
|
230
|
+
assert_equal a, b
|
231
|
+
}
|
232
|
+
end
|
233
|
+
|
234
|
+
# array
|
235
|
+
|
236
|
+
should "generate empty array" do
|
237
|
+
property_of {
|
238
|
+
sized(0) { array(:integer)}
|
239
|
+
}.check { |o|
|
240
|
+
assert o.empty?
|
241
|
+
}
|
242
|
+
end
|
243
|
+
|
244
|
+
should "generate the right sized nested arrays" do
|
245
|
+
property_of {
|
246
|
+
size1 = range(5,10)
|
247
|
+
size2 = range(0,size1-1)
|
248
|
+
array = sized(size1) { array(Proc.new { sized(size2) { array(:integer)}})}
|
249
|
+
[size1,array]
|
250
|
+
}.check { |(size1,outter_array)|
|
251
|
+
assert_equal size1, outter_array.size
|
252
|
+
assert outter_array.all? { |inner_array| inner_array.size < size1 }
|
253
|
+
}
|
254
|
+
end
|
255
|
+
|
256
|
+
should "generate array with right types" do
|
257
|
+
property_of {
|
258
|
+
sized(10) { array :integer,:string,:float }
|
259
|
+
}.check { |arr|
|
260
|
+
assert arr.all? { |o|
|
261
|
+
case o
|
262
|
+
when Fixnum, Float, String
|
263
|
+
true
|
264
|
+
else
|
265
|
+
false
|
266
|
+
end
|
267
|
+
}
|
268
|
+
}
|
269
|
+
end
|
270
|
+
|
271
|
+
should "raise if generating an array without size" do
|
272
|
+
assert_raise(RuntimeError) {
|
273
|
+
Rant.gen.value { array(:integer) }
|
274
|
+
}
|
275
|
+
end
|
276
|
+
|
277
|
+
end
|
278
|
+
|
279
|
+
|
280
|
+
|
281
|
+
# TODO: check that distributions of different methods look roughly correct.
|
282
|
+
class RantTest::Distribution
|
283
|
+
|
284
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rantly
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Howard Yeh
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-11-30 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: hayeah@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- LICENSE
|
24
|
+
- README.textile
|
25
|
+
files:
|
26
|
+
- .document
|
27
|
+
- .gitignore
|
28
|
+
- LICENSE
|
29
|
+
- README.textile
|
30
|
+
- Rakefile
|
31
|
+
- Rant.gemspec
|
32
|
+
- VERSION.yml
|
33
|
+
- lib/rant.rb
|
34
|
+
- lib/rant/data.rb
|
35
|
+
- lib/rant/generator.rb
|
36
|
+
- lib/rant/property.rb
|
37
|
+
- lib/rant/silly.rb
|
38
|
+
- lib/rant/spec.rb
|
39
|
+
- rantly.gemspec
|
40
|
+
- test/rant_test.rb
|
41
|
+
- test/test_helper.rb
|
42
|
+
has_rdoc: true
|
43
|
+
homepage: http://github.com/hayeah/rantly
|
44
|
+
licenses: []
|
45
|
+
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options:
|
48
|
+
- --charset=UTF-8
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.3.5
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: Ruby Imperative Random Data Generator and Quickcheck
|
70
|
+
test_files:
|
71
|
+
- test/rant_test.rb
|
72
|
+
- test/test_helper.rb
|