rushcheck 0.2
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/COPYING +515 -0
- data/INSTALL +14 -0
- data/LICENSE +5 -0
- data/README +49 -0
- data/Rakefile +39 -0
- data/copying.txt +23 -0
- data/data/rushcheck/doc/policy.txt +520 -0
- data/data/rushcheck/doc/rushcheck.txt +612 -0
- data/data/rushcheck/examples/printf.rb +22 -0
- data/data/rushcheck/examples/proc.rb +33 -0
- data/data/rushcheck/examples/roguetile.rb +153 -0
- data/data/rushcheck/examples/sample.rb +52 -0
- data/data/rushcheck/rdoc/classes/Arbitrary.html +148 -0
- data/data/rushcheck/rdoc/classes/Arbitrary.src/M000075.html +18 -0
- data/data/rushcheck/rdoc/classes/Assertion.html +183 -0
- data/data/rushcheck/rdoc/classes/Assertion.src/M000015.html +20 -0
- data/data/rushcheck/rdoc/classes/Assertion.src/M000016.html +45 -0
- data/data/rushcheck/rdoc/classes/Coarbitrary.html +135 -0
- data/data/rushcheck/rdoc/classes/Coarbitrary.src/M000095.html +18 -0
- data/data/rushcheck/rdoc/classes/FalseClass.html +177 -0
- data/data/rushcheck/rdoc/classes/FalseClass.src/M000018.html +18 -0
- data/data/rushcheck/rdoc/classes/FalseClass.src/M000019.html +18 -0
- data/data/rushcheck/rdoc/classes/FalseClass.src/M000020.html +18 -0
- data/data/rushcheck/rdoc/classes/Float.html +191 -0
- data/data/rushcheck/rdoc/classes/Float.src/M000047.html +21 -0
- data/data/rushcheck/rdoc/classes/Float.src/M000048.html +18 -0
- data/data/rushcheck/rdoc/classes/Float.src/M000049.html +22 -0
- data/data/rushcheck/rdoc/classes/Float.src/M000050.html +20 -0
- data/data/rushcheck/rdoc/classes/Gen.html +515 -0
- data/data/rushcheck/rdoc/classes/Gen.src/M000026.html +18 -0
- data/data/rushcheck/rdoc/classes/Gen.src/M000027.html +20 -0
- data/data/rushcheck/rdoc/classes/Gen.src/M000028.html +28 -0
- data/data/rushcheck/rdoc/classes/Gen.src/M000029.html +24 -0
- data/data/rushcheck/rdoc/classes/Gen.src/M000030.html +18 -0
- data/data/rushcheck/rdoc/classes/Gen.src/M000031.html +18 -0
- data/data/rushcheck/rdoc/classes/Gen.src/M000032.html +18 -0
- data/data/rushcheck/rdoc/classes/Gen.src/M000033.html +18 -0
- data/data/rushcheck/rdoc/classes/Gen.src/M000034.html +18 -0
- data/data/rushcheck/rdoc/classes/Gen.src/M000035.html +24 -0
- data/data/rushcheck/rdoc/classes/Gen.src/M000036.html +18 -0
- data/data/rushcheck/rdoc/classes/Gen.src/M000037.html +21 -0
- data/data/rushcheck/rdoc/classes/Gen.src/M000038.html +18 -0
- data/data/rushcheck/rdoc/classes/Gen.src/M000039.html +18 -0
- data/data/rushcheck/rdoc/classes/Gen.src/M000040.html +23 -0
- data/data/rushcheck/rdoc/classes/Gen.src/M000041.html +19 -0
- data/data/rushcheck/rdoc/classes/Gen.src/M000042.html +18 -0
- data/data/rushcheck/rdoc/classes/Gen.src/M000043.html +24 -0
- data/data/rushcheck/rdoc/classes/Guard.html +159 -0
- data/data/rushcheck/rdoc/classes/Guard.src/M000024.html +27 -0
- data/data/rushcheck/rdoc/classes/Guard.src/M000025.html +18 -0
- data/data/rushcheck/rdoc/classes/HsRandom.html +201 -0
- data/data/rushcheck/rdoc/classes/HsRandom.src/M000081.html +22 -0
- data/data/rushcheck/rdoc/classes/HsRandom.src/M000082.html +32 -0
- data/data/rushcheck/rdoc/classes/HsRandom.src/M000083.html +18 -0
- data/data/rushcheck/rdoc/classes/Integer.html +212 -0
- data/data/rushcheck/rdoc/classes/Integer.src/M000006.html +18 -0
- data/data/rushcheck/rdoc/classes/Integer.src/M000007.html +18 -0
- data/data/rushcheck/rdoc/classes/Integer.src/M000008.html +21 -0
- data/data/rushcheck/rdoc/classes/Integer.src/M000009.html +19 -0
- data/data/rushcheck/rdoc/classes/NilClass.html +177 -0
- data/data/rushcheck/rdoc/classes/NilClass.src/M000059.html +18 -0
- data/data/rushcheck/rdoc/classes/NilClass.src/M000060.html +18 -0
- data/data/rushcheck/rdoc/classes/NilClass.src/M000061.html +18 -0
- data/data/rushcheck/rdoc/classes/Property.html +174 -0
- data/data/rushcheck/rdoc/classes/Property.src/M000070.html +26 -0
- data/data/rushcheck/rdoc/classes/Property.src/M000071.html +18 -0
- data/data/rushcheck/rdoc/classes/RandomArray.html +184 -0
- data/data/rushcheck/rdoc/classes/RandomArray.src/M000021.html +18 -0
- data/data/rushcheck/rdoc/classes/RandomArray.src/M000022.html +35 -0
- data/data/rushcheck/rdoc/classes/RandomArray.src/M000023.html +22 -0
- data/data/rushcheck/rdoc/classes/RandomBool.html +146 -0
- data/data/rushcheck/rdoc/classes/RandomBool.src/M000079.html +18 -0
- data/data/rushcheck/rdoc/classes/RandomBool.src/M000080.html +19 -0
- data/data/rushcheck/rdoc/classes/RandomGen.html +196 -0
- data/data/rushcheck/rdoc/classes/RandomGen.src/M000076.html +18 -0
- data/data/rushcheck/rdoc/classes/RandomGen.src/M000077.html +18 -0
- data/data/rushcheck/rdoc/classes/RandomGen.src/M000078.html +18 -0
- data/data/rushcheck/rdoc/classes/RandomHash.html +197 -0
- data/data/rushcheck/rdoc/classes/RandomHash.src/M000044.html +18 -0
- data/data/rushcheck/rdoc/classes/RandomHash.src/M000045.html +26 -0
- data/data/rushcheck/rdoc/classes/RandomHash.src/M000046.html +22 -0
- data/data/rushcheck/rdoc/classes/RandomProc.html +192 -0
- data/data/rushcheck/rdoc/classes/RandomProc.src/M000055.html +18 -0
- data/data/rushcheck/rdoc/classes/RandomProc.src/M000056.html +30 -0
- data/data/rushcheck/rdoc/classes/RandomProc.src/M000057.html +26 -0
- data/data/rushcheck/rdoc/classes/RandomProc.src/M000058.html +20 -0
- data/data/rushcheck/rdoc/classes/Result.html +214 -0
- data/data/rushcheck/rdoc/classes/Result.src/M000051.html +18 -0
- data/data/rushcheck/rdoc/classes/Result.src/M000052.html +18 -0
- data/data/rushcheck/rdoc/classes/Result.src/M000053.html +18 -0
- data/data/rushcheck/rdoc/classes/RushCheckConfig.html +240 -0
- data/data/rushcheck/rdoc/classes/RushCheckConfig.src/M000001.html +23 -0
- data/data/rushcheck/rdoc/classes/RushCheckConfig.src/M000002.html +22 -0
- data/data/rushcheck/rdoc/classes/RushCheckConfig.src/M000003.html +18 -0
- data/data/rushcheck/rdoc/classes/RushCheckConfig.src/M000004.html +38 -0
- data/data/rushcheck/rdoc/classes/RushCheckConfig.src/M000005.html +55 -0
- data/data/rushcheck/rdoc/classes/RushCheckGuard.html +118 -0
- data/data/rushcheck/rdoc/classes/SpecialString.html +151 -0
- data/data/rushcheck/rdoc/classes/SpecialString.src/M000017.html +37 -0
- data/data/rushcheck/rdoc/classes/StdGen.html +252 -0
- data/data/rushcheck/rdoc/classes/StdGen.src/M000010.html +23 -0
- data/data/rushcheck/rdoc/classes/StdGen.src/M000011.html +21 -0
- data/data/rushcheck/rdoc/classes/StdGen.src/M000012.html +21 -0
- data/data/rushcheck/rdoc/classes/StdGen.src/M000013.html +18 -0
- data/data/rushcheck/rdoc/classes/StdGen.src/M000014.html +18 -0
- data/data/rushcheck/rdoc/classes/String.html +191 -0
- data/data/rushcheck/rdoc/classes/String.src/M000066.html +24 -0
- data/data/rushcheck/rdoc/classes/String.src/M000067.html +18 -0
- data/data/rushcheck/rdoc/classes/String.src/M000068.html +25 -0
- data/data/rushcheck/rdoc/classes/String.src/M000069.html +22 -0
- data/data/rushcheck/rdoc/classes/Testable.html +281 -0
- data/data/rushcheck/rdoc/classes/Testable.src/M000084.html +18 -0
- data/data/rushcheck/rdoc/classes/Testable.src/M000085.html +18 -0
- data/data/rushcheck/rdoc/classes/Testable.src/M000088.html +18 -0
- data/data/rushcheck/rdoc/classes/Testable.src/M000089.html +18 -0
- data/data/rushcheck/rdoc/classes/Testable.src/M000090.html +18 -0
- data/data/rushcheck/rdoc/classes/Testable.src/M000092.html +18 -0
- data/data/rushcheck/rdoc/classes/Testable.src/M000094.html +18 -0
- data/data/rushcheck/rdoc/classes/TheStdGen.html +200 -0
- data/data/rushcheck/rdoc/classes/TheStdGen.src/M000062.html +18 -0
- data/data/rushcheck/rdoc/classes/TheStdGen.src/M000063.html +20 -0
- data/data/rushcheck/rdoc/classes/TheStdGen.src/M000064.html +21 -0
- data/data/rushcheck/rdoc/classes/TheStdGen.src/M000065.html +18 -0
- data/data/rushcheck/rdoc/classes/TrueClass.html +177 -0
- data/data/rushcheck/rdoc/classes/TrueClass.src/M000072.html +18 -0
- data/data/rushcheck/rdoc/classes/TrueClass.src/M000073.html +18 -0
- data/data/rushcheck/rdoc/classes/TrueClass.src/M000074.html +18 -0
- data/data/rushcheck/rdoc/created.rid +1 -0
- data/data/rushcheck/rdoc/files/rushcheck/arbitrary_rb.html +114 -0
- data/data/rushcheck/rdoc/files/rushcheck/array_rb.html +117 -0
- data/data/rushcheck/rdoc/files/rushcheck/assertion_rb.html +120 -0
- data/data/rushcheck/rdoc/files/rushcheck/bool_rb.html +120 -0
- data/data/rushcheck/rdoc/files/rushcheck/config_rb.html +109 -0
- data/data/rushcheck/rdoc/files/rushcheck/float_rb.html +118 -0
- data/data/rushcheck/rdoc/files/rushcheck/gen_rb.html +111 -0
- data/data/rushcheck/rdoc/files/rushcheck/guard_rb.html +108 -0
- data/data/rushcheck/rdoc/files/rushcheck/hash_rb.html +117 -0
- data/data/rushcheck/rdoc/files/rushcheck/integer_rb.html +118 -0
- data/data/rushcheck/rdoc/files/rushcheck/proc_rb.html +118 -0
- data/data/rushcheck/rdoc/files/rushcheck/property_rb.html +119 -0
- data/data/rushcheck/rdoc/files/rushcheck/random_rb.html +126 -0
- data/data/rushcheck/rdoc/files/rushcheck/result_rb.html +118 -0
- data/data/rushcheck/rdoc/files/rushcheck/rushcheck_rb.html +123 -0
- data/data/rushcheck/rdoc/files/rushcheck/string_rb.html +121 -0
- data/data/rushcheck/rdoc/files/rushcheck/testable_rb.html +117 -0
- data/data/rushcheck/rdoc/fr_class_index.html +51 -0
- data/data/rushcheck/rdoc/fr_file_index.html +43 -0
- data/data/rushcheck/rdoc/fr_method_index.html +121 -0
- data/data/rushcheck/rdoc/index.html +24 -0
- data/data/rushcheck/rdoc/rdoc-style.css +208 -0
- data/lib/rushcheck/arbitrary.rb +28 -0
- data/lib/rushcheck/array.rb +49 -0
- data/lib/rushcheck/assertion.rb +67 -0
- data/lib/rushcheck/bool.rb +90 -0
- data/lib/rushcheck/config.rb +98 -0
- data/lib/rushcheck/float.rb +43 -0
- data/lib/rushcheck/gen.rb +189 -0
- data/lib/rushcheck/guard.rb +27 -0
- data/lib/rushcheck/hash.rb +45 -0
- data/lib/rushcheck/integer.rb +43 -0
- data/lib/rushcheck/proc.rb +53 -0
- data/lib/rushcheck/property.rb +29 -0
- data/lib/rushcheck/random.rb +215 -0
- data/lib/rushcheck/result.rb +26 -0
- data/lib/rushcheck/rushcheck.rb +17 -0
- data/lib/rushcheck/string.rb +100 -0
- data/lib/rushcheck/testable.rb +41 -0
- metadata +238 -0
|
@@ -0,0 +1,612 @@
|
|
|
1
|
+
RushCheck - a random testing tool for Ruby
|
|
2
|
+
Tutorial for version 0.2
|
|
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
|
+
@name, @price = name, price
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
def foo
|
|
440
|
+
...
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
def bar
|
|
444
|
+
...
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
To write random generator, we have to look up 'initialize'.
|
|
450
|
+
Here, assume that @name belongs String and @price belongs Integer.
|
|
451
|
+
|
|
452
|
+
One simple random generator for Candy:
|
|
453
|
+
|
|
454
|
+
(1) using Gen#bind and Gen.unit
|
|
455
|
+
class Candy
|
|
456
|
+
extend Arbitrary
|
|
457
|
+
|
|
458
|
+
def self.arbitrary
|
|
459
|
+
Integer.arbitrary.bind do |name|
|
|
460
|
+
String.arbitrary.bind do |price|
|
|
461
|
+
Gen.unit(new(name, price))
|
|
462
|
+
end
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
Puzzled? OK, they can be readed as follows:
|
|
468
|
+
|
|
469
|
+
take a random (arbitrary) integer and call it 'name'
|
|
470
|
+
take a random string and call it 'price'
|
|
471
|
+
return a Gen object which has a new Candy(name, price)
|
|
472
|
+
|
|
473
|
+
In general, the class method arbitrary should return a Gen object.
|
|
474
|
+
Check also gen.rb in RushCheck. There are several combinators to
|
|
475
|
+
create random generators.
|
|
476
|
+
|
|
477
|
+
There are several way to implement random generators. The next
|
|
478
|
+
example is to use Gen.new without bind and unit.
|
|
479
|
+
|
|
480
|
+
(2) using Gen.new
|
|
481
|
+
class Candy
|
|
482
|
+
extend Arbitrary
|
|
483
|
+
|
|
484
|
+
def self.arbitrary
|
|
485
|
+
Gen.new do |n, r|
|
|
486
|
+
r2 = r
|
|
487
|
+
name, price = [Integer, String].map do |c|
|
|
488
|
+
r1, r2 = r2.split
|
|
489
|
+
c.arbitrary.value(n. r1)
|
|
490
|
+
end
|
|
491
|
+
new(name, price)
|
|
492
|
+
end
|
|
493
|
+
end
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
This pattern is useful if your class has many valiables for
|
|
497
|
+
initialize. Because binding patterns needs much depth of method call
|
|
498
|
+
chains, it may be failed. This technique is used to avoid the stack
|
|
499
|
+
overflow. This is one difference between Haskell and Ruby.
|
|
500
|
+
|
|
501
|
+
The implementation can be understanded as follows:
|
|
502
|
+
self.arbitrary returns a new Gen object.
|
|
503
|
+
let n be an integer and r be a random generator.
|
|
504
|
+
let r2 equal r
|
|
505
|
+
name and price are both random objects where
|
|
506
|
+
get new two random generator r1 and r2 from old r2
|
|
507
|
+
assign a random value by generating 'arbitrary.value(n, r1)'
|
|
508
|
+
and discard the random generator r1
|
|
509
|
+
|
|
510
|
+
Note that we need new random generator for each random object.
|
|
511
|
+
Here we create new random generator by spliting. If you use same
|
|
512
|
+
random generator for generating different objects, then the
|
|
513
|
+
distribution of objects are same (not generated randomly).
|
|
514
|
+
|
|
515
|
+
Because sometimes the initialize of YourClass is complecated,
|
|
516
|
+
then the random generator self.arbitrary turns to be complecated
|
|
517
|
+
also.
|
|
518
|
+
|
|
519
|
+
In next sections, I will introduce several generators in gen.rb.
|
|
520
|
+
They may be useful to create your own random genrator. See also rdoc
|
|
521
|
+
of Gen.
|
|
522
|
+
|
|
523
|
+
6.2 Gen.choose
|
|
524
|
+
|
|
525
|
+
Gen.choose(lo, hi) returns a Gen object which generates a random
|
|
526
|
+
value in the bound.
|
|
527
|
+
|
|
528
|
+
example.
|
|
529
|
+
Gen.choose(0, 10) # a generator of Integer in (0..10)
|
|
530
|
+
|
|
531
|
+
6.3 Gen.frequency
|
|
532
|
+
|
|
533
|
+
Gen.frequency requires an array of pair of Integer and Gen objects,
|
|
534
|
+
and returns a Gen object. Gen.frequency is used to define the
|
|
535
|
+
distribution of randomness.
|
|
536
|
+
|
|
537
|
+
example.
|
|
538
|
+
Gen.frequency([[3, Integer.arbitrary], [7, String.arbitrary]])
|
|
539
|
+
# return a random integer or a random string (by choosing
|
|
540
|
+
# randomly) Integer:30% String: 70%
|
|
541
|
+
|
|
542
|
+
See also SpecialString.
|
|
543
|
+
|
|
544
|
+
6.4 Gen.lift_array
|
|
545
|
+
(I don't like the name and may be changed in future)
|
|
546
|
+
|
|
547
|
+
Gen.lift_array takes an array and a block which has a variable.
|
|
548
|
+
The block should return a Gen object. lift_array returns a Gen
|
|
549
|
+
object which generates an array of the result of given block for
|
|
550
|
+
applying each member of given array.
|
|
551
|
+
|
|
552
|
+
example.
|
|
553
|
+
|
|
554
|
+
class Candy
|
|
555
|
+
extend Arbitrary
|
|
556
|
+
|
|
557
|
+
def self.arbitrary
|
|
558
|
+
Gen.lift_array([Integer, String]) do |c|
|
|
559
|
+
c.arbitrary
|
|
560
|
+
end.bind do |args|
|
|
561
|
+
new(*args)
|
|
562
|
+
end
|
|
563
|
+
end
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
6.5 Gen.oneof
|
|
567
|
+
|
|
568
|
+
Gen.oneof requires an array of Gen objects returns a Gen object.
|
|
569
|
+
|
|
570
|
+
example.
|
|
571
|
+
Gen.oneof([Integer.arbitrary, String.arbitrary])
|
|
572
|
+
# return a random integer or a random string (by choosing
|
|
573
|
+
# randomly)
|
|
574
|
+
|
|
575
|
+
6.6 Gen.promote
|
|
576
|
+
|
|
577
|
+
Gen.promote is used to generate a random Proc object.
|
|
578
|
+
See also proc.rb in RushCheck
|
|
579
|
+
|
|
580
|
+
6.7 Gen.rand
|
|
581
|
+
|
|
582
|
+
Gen.rand is a random number generator.
|
|
583
|
+
|
|
584
|
+
6.8 Gen.sized
|
|
585
|
+
|
|
586
|
+
Gen.sized is used to change the size of random object.
|
|
587
|
+
See also some implementation in RushCheck (grep the source!)
|
|
588
|
+
|
|
589
|
+
6.9 Gen.unit
|
|
590
|
+
|
|
591
|
+
return Gen object.
|
|
592
|
+
|
|
593
|
+
6.10 Gen.vector
|
|
594
|
+
|
|
595
|
+
get a vector of Gen.
|
|
596
|
+
|
|
597
|
+
Gen.vector(Integer, 3) # => Gen [Integer, Integer, Integer]
|
|
598
|
+
|
|
599
|
+
7. Further information
|
|
600
|
+
|
|
601
|
+
Webpage should have another useful information:
|
|
602
|
+
http://rushcheck.rubyforge.org/
|
|
603
|
+
The project page has a bug tracker and webboard.
|
|
604
|
+
http://rubyforge.org/projects/rushcheck/
|
|
605
|
+
|
|
606
|
+
There is no mailing list for RushCheck (now at 2006-08-08),
|
|
607
|
+
but don't hesitate to contact to the author!
|
|
608
|
+
|
|
609
|
+
Happy hacking and testing!
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# malformed format string bug for sprintf
|
|
2
|
+
# see also a recent news
|
|
3
|
+
# http://k-tai.impress.co.jp/cda/article/news_toppage/30484.html
|
|
4
|
+
# this news tells that a japanese handy-phone hungs when it receives
|
|
5
|
+
# an email which contains the special format string "%s".
|
|
6
|
+
|
|
7
|
+
require 'rushcheck/rushcheck'
|
|
8
|
+
|
|
9
|
+
def malformed_format_string
|
|
10
|
+
Assertion.new(String) { |s|
|
|
11
|
+
sprintf(s)
|
|
12
|
+
true
|
|
13
|
+
}.check
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def malformed_format_string2
|
|
17
|
+
# SpecialString is used to find special format more likely
|
|
18
|
+
Assertion.new(SpecialString) { |s|
|
|
19
|
+
sprintf(s)
|
|
20
|
+
true
|
|
21
|
+
}.check
|
|
22
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# proc.rb
|
|
2
|
+
# a sample of RushCheck
|
|
3
|
+
|
|
4
|
+
require 'rushcheck/rushcheck'
|
|
5
|
+
|
|
6
|
+
class Proc
|
|
7
|
+
# application
|
|
8
|
+
def **(other)
|
|
9
|
+
Proc.new do |*args|
|
|
10
|
+
res = other.call(*args)
|
|
11
|
+
call(*res)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class MyRandomProc < RandomProc; end
|
|
17
|
+
|
|
18
|
+
def associativity_integer
|
|
19
|
+
MyRandomProc.set_pattern([Integer], [Integer])
|
|
20
|
+
Assertion.new(MyRandomProc, MyRandomProc, MyRandomProc, Integer) do
|
|
21
|
+
|a, b, c, x|
|
|
22
|
+
(a ** (b ** c)).call(x) == ((a ** b) ** c).call(x)
|
|
23
|
+
end.check
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# this test takes much time than associativity_integer
|
|
27
|
+
def associativity_string
|
|
28
|
+
MyRandomProc.set_pattern([String], [String])
|
|
29
|
+
Assertion.new(MyRandomProc, MyRandomProc, MyRandomProc, String) do
|
|
30
|
+
|a, b, c, x|
|
|
31
|
+
(a ** (b ** c)).call(x) == ((a ** b) ** c).call(x)
|
|
32
|
+
end.check
|
|
33
|
+
end
|