1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
|
/** -*-C-*-ish
Kaya standard library
Copyright (C) 2004-2006 Edwin Brady
This file is distributed under the terms of the GNU Lesser General
Public Licence. See COPYING for licence.
*/
/// Support for unit test creation.
"<summary>Unit testing support library</summary>
<prose>This module contains functions for development of unit testing libraries. Tests are defined with <functionref>add</functionref>, and then the test set is run with <functionref>run</functionref></prose>
<example>program test;
Bool predicate(Int a) = (a%2 == 0);
Void() test1() {
xs = randomIntArray(10,1000);
ys = filter(predicate,xs);
assert(all(predicate,ys));
}
Void main() {
init();
add(@test1,10,\"Filtering\");
run();
}</example>
<prose>Note that random values in this module are generated using <functionref>Builtins::rand</functionref>, a generator with high speed but poor quality. For higher quality random values, consider using <moduleref>Lfrand</moduleref> or another generator. For most testing, this will not be important.</prose>"
module Testing;
import Prelude;
import IO;
import Time;
globals {
[(Void(), Int, String)] tests = [];
}
"<argument>The error message</argument>
<summary>An assertion failed</summary>
<prose>This Exception is thrown when an assertion fails.</prose>"
Exception AssertionFailure(String message);
"<argument name='test'>The test result</argument>
<argument name='msg'>The failure message, if any.</argument>
<summary>Check the result of a test.</summary>
<prose>Check the result of a test. On failure, throw an exception with the given message.</prose>
<related><functionref>add</functionref></related>
<related><functionref>run</functionref></related>"
public Void assert(Bool test, String msg = "Unspecified Assertion Failure")
{
if (!test) {
throw(AssertionFailure(msg));
}
}
// Functions for generating a variety of random test cases.
"<summary>An arbitrary union type.</summary>
<prose>This data type is used to create completely random union values for testing polymorphic functions.</prose>"
public data Arbitrary
= AnInt(Int num)
| AString(String str)
| AChar(Char ch)
| AFloat(Float float)
| AnArray([Arbitrary] array)
| Recursive(Arbitrary value)
| RecPair(Arbitrary value1, Arbitrary value2);
"<argument name='depth'>This parameter gives the maximum depth of recursion
to guarantee that the value has finite size. The default value is 10 and can usually be omitted.</argument>
<summary>Return a random value</summary>
<prose>Return a random value of arbitrary type.</prose>
<related><functionref>randomArray</functionref></related>
<related><functionref>randomChar</functionref></related>
<related><functionref>randomFloat</functionref></related>
<related><functionref>randomInt</functionref></related>
<related><functionref>randomString</functionref></related>"
public Arbitrary randomValue(Int depth = 10)
{
depth--;
if (depth <= 0) {
opts = 4;
} else {
opts = 7;
}
case rand()%opts of {
0 -> return AnInt(randomInt);
| 1 -> return AString(randomString());
| 2 -> return AChar(randomChar);
| 3 -> return AFloat(randomFloat);
// keep it quite small, don't want to spend ages generating a value!
| 4 -> return AnArray(randomArray(randomValue@(depth),0,10));
| 5 -> return Recursive(randomValue(depth));
| 6 -> return RecPair(randomValue(depth), randomValue(depth));
}
}
"<summary>Return a random integer</summary>
<prose>Return a random integer in the range -2**31 to 2**31.</prose>
<related><functionref>randomChar</functionref></related>
<related><functionref>randomFloat</functionref></related>
<related><functionref>randomIntArray</functionref></related>
<related><functionref>randomNegativeInt</functionref></related>
<related><functionref>randomPositiveInt</functionref></related>
<related><functionref>randomSignOfInt</functionref></related>
<related><functionref>randomString</functionref></related>
<related><functionref>randomValue</functionref></related>"
public Int randomInt()
{
if (rand()%2 == 0) {
return randomPositiveInt();
} else {
return randomNegativeInt();
}
}
"<summary>Return a random positive integer</summary>
<prose>Return a random integer in the range 0 to 2**31.</prose>
<related><functionref>randomInt</functionref></related>
<related><functionref>randomNegativeInt</functionref></related>
<related><functionref>randomSignOfInt</functionref></related>"
public Int randomPositiveInt()
{
return abs(rand());
}
"<summary>Return a random negative integer</summary>
<prose>Return a random integer in the range -2**31 to 0.</prose>
<related><functionref>randomInt</functionref></related>
<related><functionref>randomPositiveInt</functionref></related>
<related><functionref>randomSignOfInt</functionref></related>"
public Int randomNegativeInt()
{
return -abs(rand());
}
"<summary>Return a distribution of integers</summary>
<prose>Return an even distribution of negative, positive and zero integers</prose>
<related><functionref>randomInt</functionref></related>
<related><functionref>randomNegativeInt</functionref></related>
<related><functionref>randomPositiveInt</functionref></related>"
public Int randomSignOfInt()
{
case rand()%3 of {
0 -> return randomPositiveInt();
| 1 -> return randomNegativeInt();
| 2 -> return 0;
}
}
"<summary>Return a random floating point number</summary>
<prose>Return a random floating point number</prose>
<related><functionref>randomChar</functionref></related>
<related><functionref>randomFloatArray</functionref></related>
<related><functionref>randomInt</functionref></related>
<related><functionref>randomNegativeFloat</functionref></related>
<related><functionref>randomPositiveFloat</functionref></related>
<related><functionref>randomString</functionref></related>
<related><functionref>randomValue</functionref></related>"
public Float randomFloat()
{
if (rand()%2 == 0) {
return randomPositiveFloat();
} else {
return randomNegativeFloat();
}
}
"<summary>Return a random positive floating point number</summary>
<prose>Return a random positive floating point number</prose>
<related><functionref>randomFloat</functionref></related>
<related><functionref>randomNegativeFloat</functionref></related>"
public Float randomPositiveFloat()
{
return abs(Float(rand)/Float(rand+1)); // No, I'm not convinced either. It'll do.
}
"<summary>Return a random negative floating point number</summary>
<prose>Return a random negative floating point number</prose>
<related><functionref>randomFloat</functionref></related>
<related><functionref>randomPositiveFloat</functionref></related>"
public Float randomNegativeFloat()
{
return -abs(Float(rand)/Float(rand+1)); // No, I'm not convinced either. It'll do.
}
"<summary>Return a random character</summary>
<prose>Return a random character (half of returned characters will be ASCII, the other half will be multibyte unicode).</prose>
<related><functionref>randomCharArray</functionref></related>
<related><functionref>randomFloat</functionref></related>
<related><functionref>randomInt</functionref></related>
<related><functionref>randomString</functionref></related>
<related><functionref>randomValue</functionref></related>"
public Char randomChar()
{
return Char(1+(rand()%256));
}
"<argument name='minsize'>The minimum length of the string (may be omitted for a default of zero)</argument>
<argument name='maxsize'>The maximum length of the string (may be omitted for a default of 160)</argument>
<summary>Return a random string</summary>
<prose>Return a random string including multibyte characters.</prose>
<related><functionref>randomChar</functionref></related>
<related><functionref>randomFloat</functionref></related>
<related><functionref>randomInt</functionref></related>
<related><functionref>randomStringArray</functionref></related>
<related><functionref>randomValue</functionref></related>"
public String randomString(Int minsize = 0, Int maxsize = 160)
{
num = (rand()%(maxsize-minsize))+minsize;
str = "";
for x in [1..num] {
str += randomChar();
}
return str;
}
"<argument name='generator'>A function that returns random elements of the given type.</argument>
<argument name='minsize'>The minimum size of the array (optional, defaults to zero)</argument>
<argument name='maxsize'>The minimum size of the array (optional, defaults to 1000)</argument>
<summary>Return a random array</summary>
<prose>Return a random array, with elements given by the generator function.</prose>
<related><functionref>randomCharArray</functionref></related>
<related><functionref>randomFloatArray</functionref></related>
<related><functionref>randomIntArray</functionref></related>
<related><functionref>randomStringArray</functionref></related>
<related><functionref>randomValue</functionref></related>"
public [a] randomArray(a() generator, Int minsize = 0, Int maxsize = 1000)
{
num = (rand()%(maxsize-minsize))+minsize;
arr = [];
for x in [1..num] {
push(arr,generator());
}
return arr;
}
"<argument name='minsize'>The minimum size of the array (optional, defaults to zero)</argument>
<argument name='maxsize'>The minimum size of the array (optional, defaults to 1000)</argument>
<summary>Return a random list of integers</summary>
<prose>Return a random list of integers.</prose>
<related><functionref>randomArray</functionref></related>
<related><functionref>randomCharArray</functionref></related>
<related><functionref>randomFloatArray</functionref></related>
<related><functionref>randomInt</functionref></related>
<related><functionref>randomStringArray</functionref></related>"
public [Int] randomIntArray(Int minsize = 0, Int maxsize = 1000)
= randomArray(@randomInt, minsize, maxsize);
"<argument name='minsize'>The minimum size of the array (optional, defaults to zero)</argument>
<argument name='maxsize'>The minimum size of the array (optional, defaults to 1000)</argument>
<summary>Return a random list of floating-point numbers</summary>
<prose>Return a random list of floating-point numbers.</prose>
<related><functionref>randomArray</functionref></related>
<related><functionref>randomCharArray</functionref></related>
<related><functionref>randomFloat</functionref></related>
<related><functionref>randomIntArray</functionref></related>
<related><functionref>randomStringArray</functionref></related>"
public [Float] randomFloatArray(Int minsize = 0, Int maxsize = 1000)
= randomArray(@randomFloat, minsize, maxsize);
"<argument name='minsize'>The minimum size of the array (optional, defaults to zero)</argument>
<argument name='maxsize'>The minimum size of the array (optional, defaults to 1000)</argument>
<summary>Return a random list of characters</summary>
<prose>Return a random list of characters.</prose>
<related><functionref>randomArray</functionref></related>
<related><functionref>randomChar</functionref></related>
<related><functionref>randomFloatArray</functionref></related>
<related><functionref>randomIntArray</functionref></related>
<related><functionref>randomStringArray</functionref></related>"
public [Char] randomCharArray(Int minsize = 0, Int maxsize = 1000)
= randomArray(@randomChar, minsize, maxsize);
"<argument name='minsize'>The minimum size of the array (optional, defaults to zero)</argument>
<argument name='maxsize'>The minimum size of the array (optional, defaults to 1000)</argument>
<summary>Return a random list of strings</summary>
<prose>Return a random list of strings.</prose>
<related><functionref>randomArray</functionref></related>
<related><functionref>randomCharArray</functionref></related>
<related><functionref>randomFloatArray</functionref></related>
<related><functionref>randomIntArray</functionref></related>
<related><functionref>randomString</functionref></related>"
public [String] randomStringArray(Int minsize = 0, Int maxsize = 10000)
= randomArray(randomString@(minsize,maxsize), minsize, maxsize);
"<summary>Re-initialise the test framework.</summary>
<prose>Re-initialise the test framework, clearing any existing tests.</prose>
<related><functionref>add</functionref></related>
<related><functionref>run</functionref></related>"
public Void init() {
tests = [];
}
"<argument name='test'>A test function. Test functions must return nothing and take no parameters. They should finish with a call to <functionref>assert</functionref> for the test conditions.</argument>
<argument name='num'>The number of times to run the test (useful if random values are being used). Defaults to 1.</argument>
<argument name='name'>A descriptive name for the test, which defaults to the empty string.</argument>
<summary>Add a test function.</summary>
<prose>Add a test function that runs a test case, finishing with a call to 'assert'</prose>
<related><functionref>init</functionref></related>
<related><functionref>run</functionref></related>"
public Void add(Void() test, Int num = 1, String name = "") {
push(tests, (@test, num, name));
}
"<summary>Run the tests.</summary>
<prose>Run the tests. Returns true if all tests pass, or false otherwise.</prose>
<related><functionref>add</functionref></related>
<related><functionref>init</functionref></related>"
public Bool run() {
srand(now);
failure = 0;
success = 0;
for t@testnum in tests {
(test, num, name) = t;
Prelude::putStr("Test "+(testnum+1)+" ");
if (name!="") {
Prelude::putStr("("+name+") ");
}
try {
for x in [1..num] {
test();
}
putStrLn("success");
success++;
}
catch(AssertionFailure(msg)) {
putStrLn("FAILURE: "+msg);
failure++;
} catch(e) {
putStrLn("FAILURE: Uncaught exception "+exceptionMessage(e));
failure++;
}
}
if (failure==0) {
putStrLn("\nAll tests pass");
return true;
}
else {
putStrLn("\n"+failure+" test failure"+(if (failure!=1) "s" else ""));
}
return false;
}
|