rushcheck 0.2

Sign up to get free protection for your applications and to get access to all the features.
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