rantly 0.1.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.
- 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
|