rushcheck 0.3 → 0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -1
- data/data/rushcheck/doc/policy.txt +107 -0
- data/data/rushcheck/doc/rushcheck.thtml +832 -0
- data/data/rushcheck/examples/candy.rb +6 -8
- data/data/rushcheck/examples/printf.rb +3 -3
- data/data/rushcheck/examples/proc.rb +4 -4
- data/data/rushcheck/examples/roguetile.rb +16 -16
- data/data/rushcheck/examples/sample.rb +10 -10
- data/data/rushcheck/rdoc/classes/Arbitrary.html +5 -5
- data/data/rushcheck/rdoc/classes/Arbitrary.src/M000082.html +18 -0
- data/data/rushcheck/rdoc/classes/Assertion.html +11 -11
- data/data/rushcheck/rdoc/classes/Assertion.src/M000019.html +20 -0
- data/data/rushcheck/rdoc/classes/Assertion.src/M000020.html +50 -0
- data/data/rushcheck/rdoc/classes/Coarbitrary.html +5 -5
- data/data/rushcheck/rdoc/classes/Coarbitrary.src/M000105.html +18 -0
- data/data/rushcheck/rdoc/classes/FalseClass.html +15 -15
- data/data/rushcheck/rdoc/classes/FalseClass.src/M000022.html +18 -0
- data/data/rushcheck/rdoc/classes/FalseClass.src/M000023.html +18 -0
- data/data/rushcheck/rdoc/classes/FalseClass.src/M000024.html +18 -0
- data/data/rushcheck/rdoc/classes/Float.html +20 -20
- data/data/rushcheck/rdoc/classes/Float.src/M000052.html +21 -0
- data/data/rushcheck/rdoc/classes/Float.src/M000053.html +18 -0
- data/data/rushcheck/rdoc/classes/Float.src/M000054.html +22 -0
- data/data/rushcheck/rdoc/classes/Float.src/M000055.html +20 -0
- data/data/rushcheck/rdoc/classes/Gen.html +102 -102
- data/data/rushcheck/rdoc/classes/Gen.src/M000030.html +4 -10
- data/data/rushcheck/rdoc/classes/Gen.src/M000031.html +29 -4
- data/data/rushcheck/rdoc/classes/Gen.src/M000032.html +6 -4
- data/data/rushcheck/rdoc/classes/Gen.src/M000033.html +14 -4
- data/data/rushcheck/rdoc/classes/Gen.src/M000034.html +10 -4
- data/data/rushcheck/rdoc/classes/Gen.src/M000035.html +4 -4
- data/data/rushcheck/rdoc/classes/Gen.src/M000036.html +4 -10
- data/data/rushcheck/rdoc/classes/Gen.src/M000037.html +4 -4
- data/data/rushcheck/rdoc/classes/Gen.src/M000038.html +4 -7
- data/data/rushcheck/rdoc/classes/Gen.src/M000039.html +4 -4
- data/data/rushcheck/rdoc/classes/Gen.src/M000040.html +10 -4
- data/data/rushcheck/rdoc/classes/Gen.src/M000041.html +4 -9
- data/data/rushcheck/rdoc/classes/Gen.src/M000042.html +7 -5
- data/data/rushcheck/rdoc/classes/Gen.src/M000043.html +4 -4
- data/data/rushcheck/rdoc/classes/Gen.src/M000044.html +4 -10
- data/data/rushcheck/rdoc/classes/Gen.src/M000045.html +23 -0
- data/data/rushcheck/rdoc/classes/Gen.src/M000046.html +19 -0
- data/data/rushcheck/rdoc/classes/Gen.src/M000047.html +18 -0
- data/data/rushcheck/rdoc/classes/Gen.src/M000048.html +24 -0
- data/data/rushcheck/rdoc/classes/Guard.html +10 -10
- data/data/rushcheck/rdoc/classes/Guard.src/M000028.html +27 -0
- data/data/rushcheck/rdoc/classes/Guard.src/M000029.html +18 -0
- data/data/rushcheck/rdoc/classes/HsRandom.html +20 -20
- data/data/rushcheck/rdoc/classes/HsRandom.src/M000088.html +22 -0
- data/data/rushcheck/rdoc/classes/HsRandom.src/M000089.html +32 -0
- data/data/rushcheck/rdoc/classes/HsRandom.src/M000090.html +18 -0
- data/data/rushcheck/rdoc/classes/Integer.html +20 -20
- data/data/rushcheck/rdoc/classes/Integer.src/M000010.html +18 -0
- data/data/rushcheck/rdoc/classes/Integer.src/M000011.html +18 -0
- data/data/rushcheck/rdoc/classes/Integer.src/M000012.html +21 -0
- data/data/rushcheck/rdoc/classes/Integer.src/M000013.html +19 -0
- data/data/rushcheck/rdoc/classes/NilClass.html +15 -15
- data/data/rushcheck/rdoc/classes/NilClass.src/M000064.html +18 -0
- data/data/rushcheck/rdoc/classes/NilClass.src/M000065.html +18 -0
- data/data/rushcheck/rdoc/classes/NilClass.src/M000066.html +18 -0
- data/data/rushcheck/rdoc/classes/Property.html +10 -10
- data/data/rushcheck/rdoc/classes/Property.src/M000077.html +26 -0
- data/data/rushcheck/rdoc/classes/Property.src/M000078.html +18 -0
- data/data/rushcheck/rdoc/classes/RandomArray.html +15 -15
- data/data/rushcheck/rdoc/classes/RandomArray.src/M000025.html +18 -0
- data/data/rushcheck/rdoc/classes/RandomArray.src/M000026.html +35 -0
- data/data/rushcheck/rdoc/classes/RandomArray.src/M000027.html +22 -0
- data/data/rushcheck/rdoc/classes/RandomBool.html +10 -10
- data/data/rushcheck/rdoc/classes/RandomBool.src/M000086.html +18 -0
- data/data/rushcheck/rdoc/classes/RandomBool.src/M000087.html +19 -0
- data/data/rushcheck/rdoc/classes/RandomGen.html +20 -20
- data/data/rushcheck/rdoc/classes/RandomGen.src/M000083.html +18 -0
- data/data/rushcheck/rdoc/classes/RandomGen.src/M000084.html +18 -0
- data/data/rushcheck/rdoc/classes/RandomGen.src/M000085.html +18 -0
- data/data/rushcheck/rdoc/classes/RandomHash.html +16 -16
- data/data/rushcheck/rdoc/classes/RandomHash.src/M000049.html +18 -0
- data/data/rushcheck/rdoc/classes/RandomHash.src/M000050.html +26 -0
- data/data/rushcheck/rdoc/classes/RandomHash.src/M000051.html +22 -0
- data/data/rushcheck/rdoc/classes/RandomProc.html +20 -20
- data/data/rushcheck/rdoc/classes/RandomProc.src/M000060.html +18 -0
- data/data/rushcheck/rdoc/classes/RandomProc.src/M000061.html +30 -0
- data/data/rushcheck/rdoc/classes/RandomProc.src/M000062.html +26 -0
- data/data/rushcheck/rdoc/classes/RandomProc.src/M000063.html +20 -0
- data/data/rushcheck/rdoc/classes/Result.html +19 -19
- data/data/rushcheck/rdoc/classes/Result.src/M000056.html +18 -0
- data/data/rushcheck/rdoc/classes/Result.src/M000057.html +18 -0
- data/data/rushcheck/rdoc/classes/Result.src/M000058.html +18 -0
- data/data/rushcheck/rdoc/classes/RushCheckConfig.html +41 -11
- data/data/rushcheck/rdoc/classes/RushCheckConfig.src/M000001.html +1 -1
- data/data/rushcheck/rdoc/classes/RushCheckConfig.src/M000002.html +1 -1
- data/data/rushcheck/rdoc/classes/RushCheckConfig.src/M000003.html +8 -4
- data/data/rushcheck/rdoc/classes/RushCheckConfig.src/M000004.html +4 -24
- data/data/rushcheck/rdoc/classes/RushCheckConfig.src/M000005.html +22 -39
- data/data/rushcheck/rdoc/classes/RushCheckConfig.src/M000006.html +55 -0
- data/data/rushcheck/rdoc/classes/RushCheckConfig.src/M000007.html +50 -0
- data/data/rushcheck/rdoc/classes/SpecialString.html +5 -5
- data/data/rushcheck/rdoc/classes/SpecialString.src/M000021.html +37 -0
- data/data/rushcheck/rdoc/classes/StdGen.html +27 -27
- data/data/rushcheck/rdoc/classes/StdGen.src/M000014.html +9 -4
- data/data/rushcheck/rdoc/classes/StdGen.src/M000015.html +21 -0
- data/data/rushcheck/rdoc/classes/StdGen.src/M000016.html +21 -0
- data/data/rushcheck/rdoc/classes/StdGen.src/M000017.html +18 -0
- data/data/rushcheck/rdoc/classes/StdGen.src/M000018.html +18 -0
- data/data/rushcheck/rdoc/classes/String.html +20 -20
- data/data/rushcheck/rdoc/classes/String.src/M000073.html +24 -0
- data/data/rushcheck/rdoc/classes/String.src/M000074.html +18 -0
- data/data/rushcheck/rdoc/classes/String.src/M000075.html +25 -0
- data/data/rushcheck/rdoc/classes/String.src/M000076.html +22 -0
- data/data/rushcheck/rdoc/classes/TestExausted.html +113 -0
- data/data/rushcheck/rdoc/classes/TestFailed.html +155 -0
- data/data/rushcheck/rdoc/classes/TestFailed.src/M000067.html +18 -0
- data/data/rushcheck/rdoc/classes/TestOk.html +113 -0
- data/data/rushcheck/rdoc/classes/TestOptions.html +180 -0
- data/data/rushcheck/rdoc/classes/TestOptions.src/M000008.html +18 -0
- data/data/rushcheck/rdoc/classes/TestOptions.src/M000009.html +18 -0
- data/data/rushcheck/rdoc/classes/TestResult.html +164 -0
- data/data/rushcheck/rdoc/classes/TestResult.src/M000068.html +18 -0
- data/data/rushcheck/rdoc/classes/Testable.html +96 -51
- data/data/rushcheck/rdoc/classes/Testable.src/M000091.html +4 -4
- data/data/rushcheck/rdoc/classes/Testable.src/M000092.html +4 -4
- data/data/rushcheck/rdoc/classes/Testable.src/M000095.html +4 -4
- data/data/rushcheck/rdoc/classes/Testable.src/M000096.html +18 -0
- data/data/rushcheck/rdoc/classes/Testable.src/M000097.html +18 -0
- data/data/rushcheck/rdoc/classes/Testable.src/M000099.html +19 -0
- data/data/rushcheck/rdoc/classes/Testable.src/M000100.html +21 -0
- data/data/rushcheck/rdoc/classes/Testable.src/M000101.html +18 -0
- data/data/rushcheck/rdoc/classes/Testable.src/M000103.html +18 -0
- data/data/rushcheck/rdoc/classes/Testable.src/M000104.html +18 -0
- data/data/rushcheck/rdoc/classes/TheStdGen.html +20 -20
- data/data/rushcheck/rdoc/classes/TheStdGen.src/M000069.html +18 -0
- data/data/rushcheck/rdoc/classes/TheStdGen.src/M000070.html +20 -0
- data/data/rushcheck/rdoc/classes/TheStdGen.src/M000071.html +21 -0
- data/data/rushcheck/rdoc/classes/TheStdGen.src/M000072.html +18 -0
- data/data/rushcheck/rdoc/classes/TrueClass.html +15 -15
- data/data/rushcheck/rdoc/classes/TrueClass.src/M000079.html +18 -0
- data/data/rushcheck/rdoc/classes/TrueClass.src/M000080.html +18 -0
- data/data/rushcheck/rdoc/classes/TrueClass.src/M000081.html +18 -0
- data/data/rushcheck/rdoc/created.rid +1 -1
- data/data/rushcheck/rdoc/files/rushcheck/config_rb.html +8 -1
- data/data/rushcheck/rdoc/files/rushcheck/testable_rb.html +3 -1
- data/data/rushcheck/rdoc/files/rushcheck/testoptions_rb.html +109 -0
- data/data/rushcheck/rdoc/files/rushcheck/testresult_rb.html +105 -0
- data/data/rushcheck/rdoc/files/rushcheck_rb.html +127 -0
- data/data/rushcheck/rdoc/fr_class_index.html +5 -0
- data/data/rushcheck/rdoc/fr_file_index.html +3 -1
- data/data/rushcheck/rdoc/fr_method_index.html +103 -94
- data/lib/rushcheck/arbitrary.rb +16 -12
- data/lib/rushcheck/array.rb +7 -6
- data/lib/rushcheck/assertion.rb +53 -50
- data/lib/rushcheck/bool.rb +26 -24
- data/lib/rushcheck/config.rb +131 -81
- data/lib/rushcheck/float.rb +4 -4
- data/lib/rushcheck/gen.rb +194 -182
- data/lib/rushcheck/guard.rb +9 -10
- data/lib/rushcheck/hash.rb +4 -3
- data/lib/rushcheck/integer.rb +11 -6
- data/lib/rushcheck/proc.rb +7 -7
- data/lib/rushcheck/property.rb +19 -15
- data/lib/rushcheck/random.rb +162 -159
- data/lib/rushcheck/result.rb +16 -12
- data/lib/rushcheck/string.rb +12 -12
- data/lib/rushcheck/testable.rb +99 -25
- data/lib/rushcheck/testoptions.rb +20 -0
- data/lib/rushcheck/testresult.rb +25 -0
- data/lib/{rushcheck/rushcheck.rb → rushcheck.rb} +2 -1
- metadata +93 -4
- data/data/rushcheck/doc/rushcheck.txt +0 -670
@@ -1,670 +0,0 @@
|
|
1
|
-
RushCheck - a random testing tool for Ruby
|
2
|
-
Tutorial for version 0.3
|
3
|
-
2006-08-08 Daisuke IKEGAMI <ikegami@madscientist.jp>
|
4
|
-
|
5
|
-
This document is a draft and may be obsoleted in future.
|
6
|
-
Check also the web page
|
7
|
-
http://rushcheck.rubyforge.org
|
8
|
-
|
9
|
-
= Getting started
|
10
|
-
|
11
|
-
== Install
|
12
|
-
|
13
|
-
% ruby setup.rb
|
14
|
-
|
15
|
-
at the root directory of the source code. The installation should be
|
16
|
-
easy. If not, unfortunately, please let me know. The author want to
|
17
|
-
distribute the library as a rubygem in future.
|
18
|
-
|
19
|
-
== Tutorial for writing testcase
|
20
|
-
|
21
|
-
1. add the following declaration to load RushCheck library
|
22
|
-
|
23
|
-
require 'rushcheck/rushcheck'
|
24
|
-
|
25
|
-
2. and require also your library file if the class you want to test is
|
26
|
-
included in it.
|
27
|
-
|
28
|
-
require 'your_library'
|
29
|
-
|
30
|
-
3. How to write assertions:
|
31
|
-
|
32
|
-
OK, then we can start to write test codes. RushCheck requires to
|
33
|
-
write /assertion based/ testcases. An assertion of function (or
|
34
|
-
method) consists of triple where inputs, guard conditions and a
|
35
|
-
testing property block. Here is a templete of assertion:
|
36
|
-
|
37
|
-
Assertion.new(Class1, Class2, ...) do |x, y, z, ..., g1, g2, ...|
|
38
|
-
# testing property block
|
39
|
-
# this block should return boolean value (true, false or nil)
|
40
|
-
# in Ruby
|
41
|
-
end
|
42
|
-
|
43
|
-
For example, assume that we want to test the method 'sort' in
|
44
|
-
Array. The following assertion is a simple testcase:
|
45
|
-
|
46
|
-
ast_zero =
|
47
|
-
Assertion.new() do
|
48
|
-
[].sort == []
|
49
|
-
end
|
50
|
-
|
51
|
-
whether if we sort empty array the result is also empty.
|
52
|
-
This assertion does not have any inputs and guards but only have
|
53
|
-
the property block.
|
54
|
-
|
55
|
-
Let's see another example:
|
56
|
-
|
57
|
-
ast_one =
|
58
|
-
Assertion.new(Integer) do |x|
|
59
|
-
[x].sort == [x]
|
60
|
-
end
|
61
|
-
|
62
|
-
This assertion defines that we claim for any integer 'x', an array
|
63
|
-
[x] is not changed after sorting. We can test the property 100 times:
|
64
|
-
|
65
|
-
irb> ast_one.verbose_check
|
66
|
-
1:
|
67
|
-
[-1]
|
68
|
-
2:
|
69
|
-
[-2]
|
70
|
-
... (snip) ...
|
71
|
-
99:
|
72
|
-
[6]
|
73
|
-
100:
|
74
|
-
[1]
|
75
|
-
OK, passed 100 tests.
|
76
|
-
true
|
77
|
-
irb> ast_one.check
|
78
|
-
OK, passed 100 tests.
|
79
|
-
true
|
80
|
-
irb>
|
81
|
-
|
82
|
-
RushCheck supports random testing such as above. We will see later
|
83
|
-
how to change the number of test, how to change the distribution,
|
84
|
-
etc. Here we learned two testing methods, verbose_check and check.
|
85
|
-
'verbose_check' displays every instances of test, on the other hand
|
86
|
-
'check' does not display inputs but only the result.
|
87
|
-
|
88
|
-
Next example shows how RushCheck displays the counter example. If
|
89
|
-
an assertion is failed, then there is a counter example of the
|
90
|
-
assertion.
|
91
|
-
|
92
|
-
ast_two =
|
93
|
-
Assertion.new(Integer, Integer) do |x, y|
|
94
|
-
[x, y].sort == [x, y]
|
95
|
-
end
|
96
|
-
|
97
|
-
The above test is failed sometimes and we can find the result after
|
98
|
-
checking.
|
99
|
-
|
100
|
-
irb> ast_two.check
|
101
|
-
Falsifiable, after 3 tests:
|
102
|
-
[2, -1]
|
103
|
-
false
|
104
|
-
irb>
|
105
|
-
|
106
|
-
Here, the counter example [2, -1] of the assertion is appeared. In
|
107
|
-
fact, [2, -1].sort equals [-1, 2] and does not equal [2, -1].
|
108
|
-
|
109
|
-
Sometimes, we need several pre-conditions for tests. For example,
|
110
|
-
if we have a pre-condition 'x <= y' in the previous assertion, then
|
111
|
-
the assertion should be succeeded. We can write pre-conditions as
|
112
|
-
guards in the property block:
|
113
|
-
|
114
|
-
ast_two_sorted =
|
115
|
-
Assertion.new(Integer, Integer) do |x, y, g|
|
116
|
-
g.guard { x <= y }
|
117
|
-
[x, y].sort == [x, y]
|
118
|
-
end
|
119
|
-
|
120
|
-
irb> ast_two_sorted.check
|
121
|
-
OK, passed 100 tests.
|
122
|
-
true
|
123
|
-
irb>
|
124
|
-
|
125
|
-
In this example, the number of arguments of Assertion.new (equals
|
126
|
-
2) is less than the number of variables of the property block
|
127
|
-
(equals 3). It is always assumed that the number of arguments of
|
128
|
-
Assertion.new is equal or less than the number of variables of the
|
129
|
-
block. The arguments of Assertion.new corresponds to the variables
|
130
|
-
of block one to one. Then the rest of variables is assumed as guard
|
131
|
-
conditions (in this example, the variable g is not corresponded and
|
132
|
-
assumed as a guard). We can have any number of guards in the
|
133
|
-
property block. If the guard property does not hold in random
|
134
|
-
testing, then the test is abort and try to take another random
|
135
|
-
instances. We can imagine the following test sequences in
|
136
|
-
ast_two_sorted.
|
137
|
-
|
138
|
-
1: x, y = 1, 2
|
139
|
-
--> guard g is passed
|
140
|
-
--> check the test block and succeed
|
141
|
-
2: x, y = 3, 1
|
142
|
-
--> guard g is failed and abort
|
143
|
-
(not counted)
|
144
|
-
3: x, y = 2, 3
|
145
|
-
--> ...
|
146
|
-
... (whether passed 100 tests or not)
|
147
|
-
|
148
|
-
4. Watching the statistics:
|
149
|
-
|
150
|
-
In the previous section, we saw two methods 'check' and
|
151
|
-
'verbose_check'. Sometimes, we want to watch the statistics of
|
152
|
-
random testing. However 'check' shows less information, and
|
153
|
-
'verbose_check' is too noisy. RushCheck has several options to
|
154
|
-
watch the statistics.
|
155
|
-
|
156
|
-
4.1 'trivial'
|
157
|
-
|
158
|
-
You may not want to check the trivial case in random test. As we
|
159
|
-
have seen, we can ignore the trivial case using the guard
|
160
|
-
condition. However, on the other hand, we can watch how many
|
161
|
-
trivial cases are appeared in random test.
|
162
|
-
|
163
|
-
ast_sort_triv =
|
164
|
-
Assertion.new(Integer, Integer) do |x, y, g|
|
165
|
-
g.guard {x <= y}
|
166
|
-
([x, y].sort == [x, y]).trivial{ x == y }
|
167
|
-
end
|
168
|
-
|
169
|
-
irb> ast_sort_triv.check
|
170
|
-
OK, passed 100 tests(14%, trivial).
|
171
|
-
true
|
172
|
-
|
173
|
-
Here, we have 14% (i.e. 14 times) trivial (x == y) cases in the
|
174
|
-
test.
|
175
|
-
|
176
|
-
4.2 'classify'
|
177
|
-
|
178
|
-
In addition, we can give another names to watching statistics.
|
179
|
-
ast_sort_classify =
|
180
|
-
Assertion.new(Integer, Integer) do |x, y, g|
|
181
|
-
g.guard {x <= y}
|
182
|
-
test = ([x, y].sort == [x, y])
|
183
|
-
test.classify('same'){ x == y }.
|
184
|
-
classify('bit diff'){ (x - y).abs == 1 }
|
185
|
-
end
|
186
|
-
|
187
|
-
irb> ast_sort_classify.check
|
188
|
-
OK, passed 100 tests.
|
189
|
-
18%, same.
|
190
|
-
11%, bit diff.
|
191
|
-
true
|
192
|
-
irb>
|
193
|
-
|
194
|
-
5. With another basic classes for assertions
|
195
|
-
|
196
|
-
In previous sections, we have seen how to check assertions for any
|
197
|
-
integers. In similar way, we can define the assertions for any float
|
198
|
-
numbers or any string.
|
199
|
-
|
200
|
-
Assertion.new(Float, String, ...) do |x, y,..., g, ...|
|
201
|
-
# ...
|
202
|
-
end
|
203
|
-
|
204
|
-
RushCheck has default random generators for the following basic classes:
|
205
|
-
- Integer
|
206
|
-
- Float
|
207
|
-
- String
|
208
|
-
If you want to change the distribution of randomness, then you have
|
209
|
-
to write a code for generator. There are some examples for writing
|
210
|
-
generators. In the next section, I will introduce SpecialString
|
211
|
-
whose distribution differs to the default implementation of String.
|
212
|
-
|
213
|
-
Even Array, Hash and Proc are also primitive classes, however the
|
214
|
-
randomness of them should be changed in testing codes. Therefore,
|
215
|
-
RushCheck provides an abstract generators for them. Programmer need
|
216
|
-
to write a subclass of the abstract generators. In section 5.2 and
|
217
|
-
later I will show you how to write a subclass of RandomArray, etc.
|
218
|
-
|
219
|
-
5.1 SpecialString
|
220
|
-
|
221
|
-
Sometimes, we want to check special characters, for example the
|
222
|
-
backslash '\' or unprinted characters 'ESC', 'NUL' and so on.
|
223
|
-
SpecialString is a subclass of String which is defined in
|
224
|
-
'rushcheck/string'. This library is already required in
|
225
|
-
'rushcheck/rushcheck' and don't need to require again.
|
226
|
-
|
227
|
-
The output of random generator of SpecialString has different
|
228
|
-
distribution of its of String. At String, the distribution of
|
229
|
-
characters are normalized. On the other hand, the distribution in
|
230
|
-
SpecialString is weighted and the generator provides special
|
231
|
-
characters more often than alphabets. In detail, the distribution is
|
232
|
-
as follows:
|
233
|
-
|
234
|
-
the distribution of SpecialString
|
235
|
-
---------------------------------------
|
236
|
-
Alphabet ... 15 %
|
237
|
-
Control characters ... 50 % (such as NUL)
|
238
|
-
Number ... 10 %
|
239
|
-
Special characters ... 25 % (such as '\')
|
240
|
-
|
241
|
-
Using SpecialString in assertions, it is more likely to find the
|
242
|
-
counter example. The following example is the famous bug which is
|
243
|
-
called 'malformed format bug'.
|
244
|
-
|
245
|
-
malformed_format_string =
|
246
|
-
Assertion.new(String) { |s| sprintf(s); true}
|
247
|
-
|
248
|
-
malformed_format_string2 =
|
249
|
-
Assertion.new(SpecialString) { |s| sprintf(s); true}
|
250
|
-
|
251
|
-
irb> malformed_format_string.check
|
252
|
-
Falsifiable, after 86 tests:
|
253
|
-
Unexpected exception: #<ArgumentError: malformed format string - %&>
|
254
|
-
... snip error traces ...
|
255
|
-
["\n&'e!]hr(%&\031Vi\003 }ss"]
|
256
|
-
false
|
257
|
-
irb> malformed_format_string2.check
|
258
|
-
Falsifiable, after 15 tests:
|
259
|
-
Unexpected exception: #<ArgumentError: malformed format string>
|
260
|
-
["%\037-R"]
|
261
|
-
false
|
262
|
-
|
263
|
-
In these results, we can see RushCheck find the counter example
|
264
|
-
after 86 tests with String, on the other hand, find it quickly after 15
|
265
|
-
tests with SpecialString.
|
266
|
-
|
267
|
-
It is easy to change the distribution in SpecialString. You can
|
268
|
-
define your subclass of SpecialString as follows:
|
269
|
-
|
270
|
-
class YourSpecialString < SpecialString
|
271
|
-
@@frequency = { 'alphabet' => 1,
|
272
|
-
'control' => 0,
|
273
|
-
'number' => 0,
|
274
|
-
'special' => 9 }
|
275
|
-
end
|
276
|
-
|
277
|
-
5.2 Array and RandomArray
|
278
|
-
|
279
|
-
The meaning of randomness for Array must be changed in testing
|
280
|
-
codes. Sometimes you needs a random array of Integer, and another a
|
281
|
-
random array of String. So what is random array?
|
282
|
-
|
283
|
-
RushCheck provides an abstract class RandomArray for abstract random
|
284
|
-
generator. Programmer have to write a subclass of RandomArray as
|
285
|
-
follows:
|
286
|
-
|
287
|
-
# for random array of integers
|
288
|
-
class MyRandomArray < RandomArray; end
|
289
|
-
MyRandomArray.set_pattern(Integer) {|ary, i| Integer}
|
290
|
-
|
291
|
-
(I don't like the name set_pattern and may change it in future)
|
292
|
-
|
293
|
-
The class method set_pattern takes a variable and a block.
|
294
|
-
Because array is /inductive/ structure, it can be defined by the
|
295
|
-
basecase and the inductive step.
|
296
|
-
|
297
|
-
For example, let's consider a random array
|
298
|
-
[Integer, String, Integer, String, ...]
|
299
|
-
where it has Integer at the odd index and String at the even index.
|
300
|
-
Then we can write a random array with this pattern:
|
301
|
-
|
302
|
-
MyRandomArray.set_pattern(Integer) do |ary, i|
|
303
|
-
if i % 2 == 0
|
304
|
-
then String
|
305
|
-
else Integer
|
306
|
-
end
|
307
|
-
|
308
|
-
More complecated example? OK, let's consider a stream:
|
309
|
-
the first component is Integer
|
310
|
-
if the i-th component is positive integer
|
311
|
-
then (i+1)-th component is String
|
312
|
-
otherwise the (i+1)-th component is Integer
|
313
|
-
|
314
|
-
MyRandomArray.set_pattern(Integer) do |ary, i|
|
315
|
-
if ary[i].kind_of?(Integer) && ary[i] >= 0
|
316
|
-
then String
|
317
|
-
else Integer
|
318
|
-
end
|
319
|
-
end
|
320
|
-
|
321
|
-
In this way, we can define any random array with any pattern.
|
322
|
-
|
323
|
-
5.3 Hash and RandomHash
|
324
|
-
|
325
|
-
Hash is also primitive and not so clear what is a random hash, like
|
326
|
-
array. RushCheck provides an abstract random generator RandomHash,
|
327
|
-
and programmer can write a subclass of RandomHash.
|
328
|
-
|
329
|
-
class MyRandomHash < RandomHash; end
|
330
|
-
pat = { 'key1' => Integer, 'key2' => String }
|
331
|
-
MyRandomHash.set_pattern(pat)
|
332
|
-
|
333
|
-
In this example, we can get a random Hash with two keys ('key1' and
|
334
|
-
'key2'). Here the keys are String, but we can give any object as
|
335
|
-
usual in Hash. Is it clear to define random hash? I think so.
|
336
|
-
(If not it is clear, then the interface may be changed in future)
|
337
|
-
|
338
|
-
5.4 Proc and RandomProc
|
339
|
-
|
340
|
-
It is not difficult to create a random Proc object.
|
341
|
-
As we saw in the previous sections, we have to write a subclass of
|
342
|
-
RandomProc.
|
343
|
-
|
344
|
-
class MyRandomProc < RandomProc; end
|
345
|
-
MyRandomProc.set_pattern([Integer], [Integer])
|
346
|
-
|
347
|
-
Here, we define a random procedure which takes an integer and
|
348
|
-
returns an integer also. In general, Ruby's function and method can
|
349
|
-
be regarded as a relation. (not a function in mathematics!)
|
350
|
-
Therefore I think random procedures can be generated by a pair of
|
351
|
-
inputs and outputs.
|
352
|
-
|
353
|
-
Let's consider a simple example. In general, any functions f, g, and
|
354
|
-
h should satisfy the associativity property:
|
355
|
-
for all x, f(g(h(x)) === (f . g)(h (x))
|
356
|
-
where f . g is a composition of functions in mathematical way
|
357
|
-
|
358
|
-
class Proc
|
359
|
-
# application
|
360
|
-
def **(other)
|
361
|
-
Proc.new do |*args|
|
362
|
-
res = other.call(*args)
|
363
|
-
call(*res)
|
364
|
-
end
|
365
|
-
end
|
366
|
-
end
|
367
|
-
|
368
|
-
class MyRandomProc < RandomProc; end
|
369
|
-
|
370
|
-
def associativity_integer
|
371
|
-
MyRandomProc.set_pattern([Integer], [Integer])
|
372
|
-
Assertion.new(MyRandomProc, MyRandomProc, MyRandomProc, Integer) do
|
373
|
-
|a, b, c, x|
|
374
|
-
(a ** (b ** c)).call(x) == ((a ** b) ** c).call(x)
|
375
|
-
end.check
|
376
|
-
end
|
377
|
-
|
378
|
-
P.S.
|
379
|
-
The arbitrary method is used to create a random object for test
|
380
|
-
instance. Then you may wonder what is the coarbitrary method?
|
381
|
-
The coarbitrary method is used to generate a random procedure
|
382
|
-
(lambda), which is one of central idea in QuickCheck.
|
383
|
-
|
384
|
-
6. How to define random generators for user defined class
|
385
|
-
|
386
|
-
6.1 Understand your class. What is a random object?
|
387
|
-
|
388
|
-
To use your class in the assertion, like follows
|
389
|
-
Assertion.new(YourClass) {...}
|
390
|
-
you have to write a code which generates a random object in your
|
391
|
-
class. Therefore, at first we have to consider
|
392
|
-
what is a random object in YourClass?
|
393
|
-
|
394
|
-
Sometimes the answer is not unique; there may be several ways for
|
395
|
-
generating random objects. Like SpecialString in RushCheck, you can
|
396
|
-
also write an abstract random generator.
|
397
|
-
|
398
|
-
OK, after thinking about the question, we have to write a code.
|
399
|
-
To define random generators, we have to add a class method arbitrary
|
400
|
-
in YourClass.
|
401
|
-
|
402
|
-
require 'rushcheck/rushcheck'
|
403
|
-
class YourClass
|
404
|
-
extend Arbitrary
|
405
|
-
|
406
|
-
def self.arbitrary
|
407
|
-
...
|
408
|
-
end
|
409
|
-
end
|
410
|
-
|
411
|
-
If you need to define a random proc which returns a object in
|
412
|
-
YourClass, you have to include Coarbitrary, also.
|
413
|
-
|
414
|
-
class YourClass
|
415
|
-
extend Arbitrary
|
416
|
-
include Coarbitrary
|
417
|
-
|
418
|
-
def self.arbitrary
|
419
|
-
...
|
420
|
-
end
|
421
|
-
|
422
|
-
def coarbitrary(g)
|
423
|
-
...
|
424
|
-
end
|
425
|
-
end
|
426
|
-
|
427
|
-
However, because it is little complecated to implement both
|
428
|
-
arbitrary and coarbitrary, let's focus how to implement arbitrary
|
429
|
-
first.
|
430
|
-
|
431
|
-
Let's consider the first example:
|
432
|
-
|
433
|
-
class Candy
|
434
|
-
|
435
|
-
def initialize(name, price)
|
436
|
-
raise unless price >= 0
|
437
|
-
@name, @price = name, price
|
438
|
-
end
|
439
|
-
|
440
|
-
def foo
|
441
|
-
...
|
442
|
-
end
|
443
|
-
|
444
|
-
def bar
|
445
|
-
...
|
446
|
-
end
|
447
|
-
|
448
|
-
end
|
449
|
-
|
450
|
-
To write random generator, we have to look up 'initialize'.
|
451
|
-
Here, assume that @name belongs String and @price belongs Integer.
|
452
|
-
It is natural that we assumes @price should be positive.
|
453
|
-
|
454
|
-
One simple random generator for Candy:
|
455
|
-
|
456
|
-
(1) using Gen.create
|
457
|
-
class Candy
|
458
|
-
extend Arbitrary
|
459
|
-
|
460
|
-
def self.arbitrary
|
461
|
-
xs = [String, Integer].map {|c| c.arbitrary}
|
462
|
-
Gen.create(xs) do |name, price, g|
|
463
|
-
g.guard { price >= 0 }
|
464
|
-
new(name, price)
|
465
|
-
end
|
466
|
-
end
|
467
|
-
end
|
468
|
-
|
469
|
-
Gen.create takes an array of Gen object (here, [Integer.arbitrary,
|
470
|
-
String.arbitrary]) and a block. The block takes variables which is
|
471
|
-
corresponded to the array, as Assertion.new. If guard is failed,
|
472
|
-
then RushCheck retry to create another random instance.
|
473
|
-
|
474
|
-
Note that we can use a trick instead of the guard property.
|
475
|
-
price = - price if price < 0 # g.guard { price >= 0 }
|
476
|
-
In this case, this is more efficient to generate random instance
|
477
|
-
than with the guard. However, sometimes we don't have this kind
|
478
|
-
trick and we can use some guards.
|
479
|
-
|
480
|
-
(2) using Gen#bind and Gen.unit
|
481
|
-
class Candy
|
482
|
-
extend Arbitrary
|
483
|
-
|
484
|
-
def self.arbitrary
|
485
|
-
String.arbitrary.bind do |name|
|
486
|
-
Integer.arbitrary.bind do |price|
|
487
|
-
price = - price if price < 0 # trick
|
488
|
-
Gen.unit(new(name, price))
|
489
|
-
end
|
490
|
-
end
|
491
|
-
end
|
492
|
-
end
|
493
|
-
|
494
|
-
Puzzled? OK, they can be readed as follows:
|
495
|
-
|
496
|
-
take a random (arbitrary) string and call it 'name'
|
497
|
-
take a random integer and call it 'price'
|
498
|
-
return a Gen object which has a new Candy(name, price)
|
499
|
-
|
500
|
-
In general, the class method arbitrary should return a Gen object.
|
501
|
-
Check also gen.rb in RushCheck. There are several combinators to
|
502
|
-
create random generators.
|
503
|
-
|
504
|
-
There are several way to implement random generators. The next
|
505
|
-
example is to use Gen.new without bind and unit.
|
506
|
-
|
507
|
-
(3) using Gen.new
|
508
|
-
class Candy
|
509
|
-
extend Arbitrary
|
510
|
-
|
511
|
-
def self.arbitrary
|
512
|
-
Gen.new do |n, r|
|
513
|
-
r2 = r
|
514
|
-
name, price = [String, Integer].map do |c|
|
515
|
-
r1, r2 = r2.split
|
516
|
-
c.arbitrary.value(n. r1)
|
517
|
-
end
|
518
|
-
price = - price if price < 0 # trick
|
519
|
-
new(name, price)
|
520
|
-
end
|
521
|
-
end
|
522
|
-
end
|
523
|
-
|
524
|
-
This pattern is useful if your class has many valiables for
|
525
|
-
initialize. Because binding patterns needs much depth of method call
|
526
|
-
chains, it may be failed. This technique is used to avoid the stack
|
527
|
-
overflow. This is one difference between Haskell and Ruby.
|
528
|
-
|
529
|
-
The implementation can be understanded as follows:
|
530
|
-
self.arbitrary returns a new Gen object.
|
531
|
-
let n be an integer and r be a random generator.
|
532
|
-
let r2 equal r
|
533
|
-
name and price are both random objects where
|
534
|
-
get new two random generator r1 and r2 from old r2
|
535
|
-
assign a random value by generating 'arbitrary.value(n, r1)'
|
536
|
-
and discard the random generator r1
|
537
|
-
...
|
538
|
-
|
539
|
-
Note that we need new random generator for each random object.
|
540
|
-
Here we create new random generator by spliting. If you use same
|
541
|
-
random generator for generating different objects, then the
|
542
|
-
distribution of objects are same (not generated randomly).
|
543
|
-
|
544
|
-
Because sometimes the initialize of YourClass is complecated,
|
545
|
-
then the random generator self.arbitrary turns to be complicated
|
546
|
-
also.
|
547
|
-
|
548
|
-
In next sections, I will introduce several generators in gen.rb.
|
549
|
-
They may be useful to create your own random genrator. See also rdoc
|
550
|
-
of Gen.
|
551
|
-
|
552
|
-
Appendix: expensive candy.
|
553
|
-
|
554
|
-
Using Gen.create to generate random instance, if we gives another
|
555
|
-
guard such as
|
556
|
-
g.guard { price >= 100000 } # very expensive candy!
|
557
|
-
then it consumes much time to generate random instance because the
|
558
|
-
guard fails so many times. When RushCheck creates random instances,
|
559
|
-
it starts smallest as possible, then the size becomes larger in
|
560
|
-
repeating tests. Therefore, if the guard instance seems failed so
|
561
|
-
often, then we need another seeds of generators. Here is another
|
562
|
-
generators for expensive candy instance.
|
563
|
-
|
564
|
-
lo = 100000
|
565
|
-
g = Gen.sized { |n| Gen.choose(lo, n + lo)}
|
566
|
-
xs = [String.arbitrary, g]
|
567
|
-
|
568
|
-
See gen.rb and the following sections in details for how to get
|
569
|
-
another generator.
|
570
|
-
|
571
|
-
6.2 Gen.choose
|
572
|
-
|
573
|
-
Gen.choose(lo, hi) returns a Gen object which generates a random
|
574
|
-
value in the bound.
|
575
|
-
|
576
|
-
example.
|
577
|
-
Gen.choose(0, 10) # a generator of Integer in (0..10)
|
578
|
-
|
579
|
-
6.3 Gen.frequency
|
580
|
-
|
581
|
-
Gen.frequency requires an array of pair of Integer and Gen objects,
|
582
|
-
and returns a Gen object. Gen.frequency is used to define the
|
583
|
-
distribution of randomness.
|
584
|
-
|
585
|
-
example.
|
586
|
-
Gen.frequency([[3, Integer.arbitrary], [7, String.arbitrary]])
|
587
|
-
# return a random integer or a random string (by choosing
|
588
|
-
# randomly) Integer:30% String: 70%
|
589
|
-
|
590
|
-
See also SpecialString.
|
591
|
-
|
592
|
-
6.4 Gen.lift_array
|
593
|
-
(I don't like the name and may be changed in future)
|
594
|
-
|
595
|
-
Gen.lift_array takes an array and a block which has a variable.
|
596
|
-
The block should return a Gen object. lift_array returns a Gen
|
597
|
-
object which generates an array of the result of given block for
|
598
|
-
applying each member of given array.
|
599
|
-
|
600
|
-
example.
|
601
|
-
|
602
|
-
class Candy
|
603
|
-
extend Arbitrary
|
604
|
-
|
605
|
-
def self.arbitrary
|
606
|
-
Gen.lift_array([Integer, String]) do |c|
|
607
|
-
c.arbitrary
|
608
|
-
end.bind do |args|
|
609
|
-
new(*args)
|
610
|
-
end
|
611
|
-
end
|
612
|
-
end
|
613
|
-
|
614
|
-
6.5 Gen.oneof
|
615
|
-
|
616
|
-
Gen.oneof requires an array of Gen objects returns a Gen object.
|
617
|
-
|
618
|
-
example.
|
619
|
-
Gen.oneof([Integer.arbitrary, String.arbitrary])
|
620
|
-
# return a random integer or a random string (by choosing
|
621
|
-
# randomly)
|
622
|
-
|
623
|
-
6.6 Gen.promote
|
624
|
-
|
625
|
-
Gen.promote is used to generate a random Proc object.
|
626
|
-
See also proc.rb in RushCheck
|
627
|
-
|
628
|
-
6.7 Gen.rand
|
629
|
-
|
630
|
-
Gen.rand is a random number generator.
|
631
|
-
|
632
|
-
6.8 Gen.sized
|
633
|
-
|
634
|
-
Gen.sized is used to change the size of random object.
|
635
|
-
See also some implementation in RushCheck (grep the source!)
|
636
|
-
|
637
|
-
6.9 Gen.unit
|
638
|
-
|
639
|
-
return Gen object.
|
640
|
-
|
641
|
-
6.10 Gen.vector
|
642
|
-
|
643
|
-
get a vector of Gen.
|
644
|
-
|
645
|
-
Gen.vector(Integer, 3) # => Gen [Integer, Integer, Integer]
|
646
|
-
|
647
|
-
7. how to write random Proc which returns objects in YourClass
|
648
|
-
|
649
|
-
It is complecated, and see some examples.
|
650
|
-
- rushcheck/bool.rb
|
651
|
-
- rushcheck/integer.rb
|
652
|
-
- rushcheck/string.rb
|
653
|
-
so on, grep 'coarbitrary'
|
654
|
-
|
655
|
-
FIXME: how to write coarbitrary
|
656
|
-
|
657
|
-
8. Further information
|
658
|
-
|
659
|
-
Webpage should have another useful information:
|
660
|
-
http://rushcheck.rubyforge.org/
|
661
|
-
The project page has a bug tracker and webboard.
|
662
|
-
http://rubyforge.org/projects/rushcheck/
|
663
|
-
|
664
|
-
There is no mailing list for RushCheck (now at 2006-08-08),
|
665
|
-
but don't hesitate to contact to the author!
|
666
|
-
|
667
|
-
Happy hacking and testing!
|
668
|
-
|
669
|
-
|
670
|
-
|