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.
Files changed (168) hide show
  1. data/COPYING +515 -0
  2. data/INSTALL +14 -0
  3. data/LICENSE +5 -0
  4. data/README +49 -0
  5. data/Rakefile +39 -0
  6. data/copying.txt +23 -0
  7. data/data/rushcheck/doc/policy.txt +520 -0
  8. data/data/rushcheck/doc/rushcheck.txt +612 -0
  9. data/data/rushcheck/examples/printf.rb +22 -0
  10. data/data/rushcheck/examples/proc.rb +33 -0
  11. data/data/rushcheck/examples/roguetile.rb +153 -0
  12. data/data/rushcheck/examples/sample.rb +52 -0
  13. data/data/rushcheck/rdoc/classes/Arbitrary.html +148 -0
  14. data/data/rushcheck/rdoc/classes/Arbitrary.src/M000075.html +18 -0
  15. data/data/rushcheck/rdoc/classes/Assertion.html +183 -0
  16. data/data/rushcheck/rdoc/classes/Assertion.src/M000015.html +20 -0
  17. data/data/rushcheck/rdoc/classes/Assertion.src/M000016.html +45 -0
  18. data/data/rushcheck/rdoc/classes/Coarbitrary.html +135 -0
  19. data/data/rushcheck/rdoc/classes/Coarbitrary.src/M000095.html +18 -0
  20. data/data/rushcheck/rdoc/classes/FalseClass.html +177 -0
  21. data/data/rushcheck/rdoc/classes/FalseClass.src/M000018.html +18 -0
  22. data/data/rushcheck/rdoc/classes/FalseClass.src/M000019.html +18 -0
  23. data/data/rushcheck/rdoc/classes/FalseClass.src/M000020.html +18 -0
  24. data/data/rushcheck/rdoc/classes/Float.html +191 -0
  25. data/data/rushcheck/rdoc/classes/Float.src/M000047.html +21 -0
  26. data/data/rushcheck/rdoc/classes/Float.src/M000048.html +18 -0
  27. data/data/rushcheck/rdoc/classes/Float.src/M000049.html +22 -0
  28. data/data/rushcheck/rdoc/classes/Float.src/M000050.html +20 -0
  29. data/data/rushcheck/rdoc/classes/Gen.html +515 -0
  30. data/data/rushcheck/rdoc/classes/Gen.src/M000026.html +18 -0
  31. data/data/rushcheck/rdoc/classes/Gen.src/M000027.html +20 -0
  32. data/data/rushcheck/rdoc/classes/Gen.src/M000028.html +28 -0
  33. data/data/rushcheck/rdoc/classes/Gen.src/M000029.html +24 -0
  34. data/data/rushcheck/rdoc/classes/Gen.src/M000030.html +18 -0
  35. data/data/rushcheck/rdoc/classes/Gen.src/M000031.html +18 -0
  36. data/data/rushcheck/rdoc/classes/Gen.src/M000032.html +18 -0
  37. data/data/rushcheck/rdoc/classes/Gen.src/M000033.html +18 -0
  38. data/data/rushcheck/rdoc/classes/Gen.src/M000034.html +18 -0
  39. data/data/rushcheck/rdoc/classes/Gen.src/M000035.html +24 -0
  40. data/data/rushcheck/rdoc/classes/Gen.src/M000036.html +18 -0
  41. data/data/rushcheck/rdoc/classes/Gen.src/M000037.html +21 -0
  42. data/data/rushcheck/rdoc/classes/Gen.src/M000038.html +18 -0
  43. data/data/rushcheck/rdoc/classes/Gen.src/M000039.html +18 -0
  44. data/data/rushcheck/rdoc/classes/Gen.src/M000040.html +23 -0
  45. data/data/rushcheck/rdoc/classes/Gen.src/M000041.html +19 -0
  46. data/data/rushcheck/rdoc/classes/Gen.src/M000042.html +18 -0
  47. data/data/rushcheck/rdoc/classes/Gen.src/M000043.html +24 -0
  48. data/data/rushcheck/rdoc/classes/Guard.html +159 -0
  49. data/data/rushcheck/rdoc/classes/Guard.src/M000024.html +27 -0
  50. data/data/rushcheck/rdoc/classes/Guard.src/M000025.html +18 -0
  51. data/data/rushcheck/rdoc/classes/HsRandom.html +201 -0
  52. data/data/rushcheck/rdoc/classes/HsRandom.src/M000081.html +22 -0
  53. data/data/rushcheck/rdoc/classes/HsRandom.src/M000082.html +32 -0
  54. data/data/rushcheck/rdoc/classes/HsRandom.src/M000083.html +18 -0
  55. data/data/rushcheck/rdoc/classes/Integer.html +212 -0
  56. data/data/rushcheck/rdoc/classes/Integer.src/M000006.html +18 -0
  57. data/data/rushcheck/rdoc/classes/Integer.src/M000007.html +18 -0
  58. data/data/rushcheck/rdoc/classes/Integer.src/M000008.html +21 -0
  59. data/data/rushcheck/rdoc/classes/Integer.src/M000009.html +19 -0
  60. data/data/rushcheck/rdoc/classes/NilClass.html +177 -0
  61. data/data/rushcheck/rdoc/classes/NilClass.src/M000059.html +18 -0
  62. data/data/rushcheck/rdoc/classes/NilClass.src/M000060.html +18 -0
  63. data/data/rushcheck/rdoc/classes/NilClass.src/M000061.html +18 -0
  64. data/data/rushcheck/rdoc/classes/Property.html +174 -0
  65. data/data/rushcheck/rdoc/classes/Property.src/M000070.html +26 -0
  66. data/data/rushcheck/rdoc/classes/Property.src/M000071.html +18 -0
  67. data/data/rushcheck/rdoc/classes/RandomArray.html +184 -0
  68. data/data/rushcheck/rdoc/classes/RandomArray.src/M000021.html +18 -0
  69. data/data/rushcheck/rdoc/classes/RandomArray.src/M000022.html +35 -0
  70. data/data/rushcheck/rdoc/classes/RandomArray.src/M000023.html +22 -0
  71. data/data/rushcheck/rdoc/classes/RandomBool.html +146 -0
  72. data/data/rushcheck/rdoc/classes/RandomBool.src/M000079.html +18 -0
  73. data/data/rushcheck/rdoc/classes/RandomBool.src/M000080.html +19 -0
  74. data/data/rushcheck/rdoc/classes/RandomGen.html +196 -0
  75. data/data/rushcheck/rdoc/classes/RandomGen.src/M000076.html +18 -0
  76. data/data/rushcheck/rdoc/classes/RandomGen.src/M000077.html +18 -0
  77. data/data/rushcheck/rdoc/classes/RandomGen.src/M000078.html +18 -0
  78. data/data/rushcheck/rdoc/classes/RandomHash.html +197 -0
  79. data/data/rushcheck/rdoc/classes/RandomHash.src/M000044.html +18 -0
  80. data/data/rushcheck/rdoc/classes/RandomHash.src/M000045.html +26 -0
  81. data/data/rushcheck/rdoc/classes/RandomHash.src/M000046.html +22 -0
  82. data/data/rushcheck/rdoc/classes/RandomProc.html +192 -0
  83. data/data/rushcheck/rdoc/classes/RandomProc.src/M000055.html +18 -0
  84. data/data/rushcheck/rdoc/classes/RandomProc.src/M000056.html +30 -0
  85. data/data/rushcheck/rdoc/classes/RandomProc.src/M000057.html +26 -0
  86. data/data/rushcheck/rdoc/classes/RandomProc.src/M000058.html +20 -0
  87. data/data/rushcheck/rdoc/classes/Result.html +214 -0
  88. data/data/rushcheck/rdoc/classes/Result.src/M000051.html +18 -0
  89. data/data/rushcheck/rdoc/classes/Result.src/M000052.html +18 -0
  90. data/data/rushcheck/rdoc/classes/Result.src/M000053.html +18 -0
  91. data/data/rushcheck/rdoc/classes/RushCheckConfig.html +240 -0
  92. data/data/rushcheck/rdoc/classes/RushCheckConfig.src/M000001.html +23 -0
  93. data/data/rushcheck/rdoc/classes/RushCheckConfig.src/M000002.html +22 -0
  94. data/data/rushcheck/rdoc/classes/RushCheckConfig.src/M000003.html +18 -0
  95. data/data/rushcheck/rdoc/classes/RushCheckConfig.src/M000004.html +38 -0
  96. data/data/rushcheck/rdoc/classes/RushCheckConfig.src/M000005.html +55 -0
  97. data/data/rushcheck/rdoc/classes/RushCheckGuard.html +118 -0
  98. data/data/rushcheck/rdoc/classes/SpecialString.html +151 -0
  99. data/data/rushcheck/rdoc/classes/SpecialString.src/M000017.html +37 -0
  100. data/data/rushcheck/rdoc/classes/StdGen.html +252 -0
  101. data/data/rushcheck/rdoc/classes/StdGen.src/M000010.html +23 -0
  102. data/data/rushcheck/rdoc/classes/StdGen.src/M000011.html +21 -0
  103. data/data/rushcheck/rdoc/classes/StdGen.src/M000012.html +21 -0
  104. data/data/rushcheck/rdoc/classes/StdGen.src/M000013.html +18 -0
  105. data/data/rushcheck/rdoc/classes/StdGen.src/M000014.html +18 -0
  106. data/data/rushcheck/rdoc/classes/String.html +191 -0
  107. data/data/rushcheck/rdoc/classes/String.src/M000066.html +24 -0
  108. data/data/rushcheck/rdoc/classes/String.src/M000067.html +18 -0
  109. data/data/rushcheck/rdoc/classes/String.src/M000068.html +25 -0
  110. data/data/rushcheck/rdoc/classes/String.src/M000069.html +22 -0
  111. data/data/rushcheck/rdoc/classes/Testable.html +281 -0
  112. data/data/rushcheck/rdoc/classes/Testable.src/M000084.html +18 -0
  113. data/data/rushcheck/rdoc/classes/Testable.src/M000085.html +18 -0
  114. data/data/rushcheck/rdoc/classes/Testable.src/M000088.html +18 -0
  115. data/data/rushcheck/rdoc/classes/Testable.src/M000089.html +18 -0
  116. data/data/rushcheck/rdoc/classes/Testable.src/M000090.html +18 -0
  117. data/data/rushcheck/rdoc/classes/Testable.src/M000092.html +18 -0
  118. data/data/rushcheck/rdoc/classes/Testable.src/M000094.html +18 -0
  119. data/data/rushcheck/rdoc/classes/TheStdGen.html +200 -0
  120. data/data/rushcheck/rdoc/classes/TheStdGen.src/M000062.html +18 -0
  121. data/data/rushcheck/rdoc/classes/TheStdGen.src/M000063.html +20 -0
  122. data/data/rushcheck/rdoc/classes/TheStdGen.src/M000064.html +21 -0
  123. data/data/rushcheck/rdoc/classes/TheStdGen.src/M000065.html +18 -0
  124. data/data/rushcheck/rdoc/classes/TrueClass.html +177 -0
  125. data/data/rushcheck/rdoc/classes/TrueClass.src/M000072.html +18 -0
  126. data/data/rushcheck/rdoc/classes/TrueClass.src/M000073.html +18 -0
  127. data/data/rushcheck/rdoc/classes/TrueClass.src/M000074.html +18 -0
  128. data/data/rushcheck/rdoc/created.rid +1 -0
  129. data/data/rushcheck/rdoc/files/rushcheck/arbitrary_rb.html +114 -0
  130. data/data/rushcheck/rdoc/files/rushcheck/array_rb.html +117 -0
  131. data/data/rushcheck/rdoc/files/rushcheck/assertion_rb.html +120 -0
  132. data/data/rushcheck/rdoc/files/rushcheck/bool_rb.html +120 -0
  133. data/data/rushcheck/rdoc/files/rushcheck/config_rb.html +109 -0
  134. data/data/rushcheck/rdoc/files/rushcheck/float_rb.html +118 -0
  135. data/data/rushcheck/rdoc/files/rushcheck/gen_rb.html +111 -0
  136. data/data/rushcheck/rdoc/files/rushcheck/guard_rb.html +108 -0
  137. data/data/rushcheck/rdoc/files/rushcheck/hash_rb.html +117 -0
  138. data/data/rushcheck/rdoc/files/rushcheck/integer_rb.html +118 -0
  139. data/data/rushcheck/rdoc/files/rushcheck/proc_rb.html +118 -0
  140. data/data/rushcheck/rdoc/files/rushcheck/property_rb.html +119 -0
  141. data/data/rushcheck/rdoc/files/rushcheck/random_rb.html +126 -0
  142. data/data/rushcheck/rdoc/files/rushcheck/result_rb.html +118 -0
  143. data/data/rushcheck/rdoc/files/rushcheck/rushcheck_rb.html +123 -0
  144. data/data/rushcheck/rdoc/files/rushcheck/string_rb.html +121 -0
  145. data/data/rushcheck/rdoc/files/rushcheck/testable_rb.html +117 -0
  146. data/data/rushcheck/rdoc/fr_class_index.html +51 -0
  147. data/data/rushcheck/rdoc/fr_file_index.html +43 -0
  148. data/data/rushcheck/rdoc/fr_method_index.html +121 -0
  149. data/data/rushcheck/rdoc/index.html +24 -0
  150. data/data/rushcheck/rdoc/rdoc-style.css +208 -0
  151. data/lib/rushcheck/arbitrary.rb +28 -0
  152. data/lib/rushcheck/array.rb +49 -0
  153. data/lib/rushcheck/assertion.rb +67 -0
  154. data/lib/rushcheck/bool.rb +90 -0
  155. data/lib/rushcheck/config.rb +98 -0
  156. data/lib/rushcheck/float.rb +43 -0
  157. data/lib/rushcheck/gen.rb +189 -0
  158. data/lib/rushcheck/guard.rb +27 -0
  159. data/lib/rushcheck/hash.rb +45 -0
  160. data/lib/rushcheck/integer.rb +43 -0
  161. data/lib/rushcheck/proc.rb +53 -0
  162. data/lib/rushcheck/property.rb +29 -0
  163. data/lib/rushcheck/random.rb +215 -0
  164. data/lib/rushcheck/result.rb +26 -0
  165. data/lib/rushcheck/rushcheck.rb +17 -0
  166. data/lib/rushcheck/string.rb +100 -0
  167. data/lib/rushcheck/testable.rb +41 -0
  168. 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