rushcheck 0.3 → 0.4

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