1function description(msg)
2{
3    print(msg);
4    print("\nOn success, you will see a series of \"PASS\" messages, followed by \"TEST COMPLETE\".\n");
5    print();
6}
7
8function debug(msg)
9{
10    print(msg);
11}
12
13function escapeString(text)
14{
15    return text.replace(/\0/g, "");
16}
17
18function testPassed(msg)
19{
20    print("PASS", escapeString(msg));
21}
22
23function testFailed(msg)
24{
25    print("FAIL", escapeString(msg));
26}
27
28function areArraysEqual(_a, _b)
29{
30    if (Object.prototype.toString.call(_a) != Object.prototype.toString.call([]))
31        return false;
32    if (_a.length !== _b.length)
33        return false;
34    for (var i = 0; i < _a.length; i++)
35        if (_a[i] !== _b[i])
36            return false;
37    return true;
38}
39
40function isMinusZero(n)
41{
42    // the only way to tell 0 from -0 in JS is the fact that 1/-0 is
43    // -Infinity instead of Infinity
44    return n === 0 && 1/n < 0;
45}
46
47function isResultCorrect(_actual, _expected)
48{
49    if (_expected === 0)
50        return _actual === _expected && (1/_actual) === (1/_expected);
51    if (_actual === _expected)
52        return true;
53    if (typeof(_expected) == "number" && isNaN(_expected))
54        return typeof(_actual) == "number" && isNaN(_actual);
55    if (Object.prototype.toString.call(_expected) == Object.prototype.toString.call([]))
56        return areArraysEqual(_actual, _expected);
57    return false;
58}
59
60function stringify(v)
61{
62    if (v === 0 && 1/v < 0)
63        return "-0";
64    else return "" + v;
65}
66
67function shouldBe(_a, _b)
68{
69  if (typeof _a != "string" || typeof _b != "string")
70    debug("WARN: shouldBe() expects string arguments");
71  var exception;
72  var _av;
73  try {
74     _av = eval(_a);
75  } catch (e) {
76     exception = e;
77  }
78  var _bv = eval(_b);
79
80  if (exception)
81    testFailed(_a + " should be " + _bv + ". Threw exception " + exception);
82  else if (isResultCorrect(_av, _bv))
83    testPassed(_a + " is " + _b);
84  else if (typeof(_av) == typeof(_bv))
85    testFailed(_a + " should be " + _bv + ". Was " + stringify(_av) + ".");
86  else
87    testFailed(_a + " should be " + _bv + " (of type " + typeof _bv + "). Was " + _av + " (of type " + typeof _av + ").");
88}
89
90function shouldBeTrue(_a) { shouldBe(_a, "true"); }
91function shouldBeFalse(_a) { shouldBe(_a, "false"); }
92function shouldBeNaN(_a) { shouldBe(_a, "NaN"); }
93function shouldBeNull(_a) { shouldBe(_a, "null"); }
94
95function shouldBeEqualToString(a, b)
96{
97  var unevaledString = '"' + b.replace(/"/g, "\"") + '"';
98  shouldBe(a, unevaledString);
99}
100
101function shouldBeUndefined(_a)
102{
103  var exception;
104  var _av;
105  try {
106     _av = eval(_a);
107  } catch (e) {
108     exception = e;
109  }
110
111  if (exception)
112    testFailed(_a + " should be undefined. Threw exception " + exception);
113  else if (typeof _av == "undefined")
114    testPassed(_a + " is undefined.");
115  else
116    testFailed(_a + " should be undefined. Was " + _av);
117}
118
119
120function shouldThrow(_a, _e)
121{
122  var exception;
123  var _av;
124  try {
125     _av = eval(_a);
126  } catch (e) {
127     exception = e;
128  }
129
130  var _ev;
131  if (_e)
132      _ev =  eval(_e);
133
134  if (exception) {
135    if (typeof _e == "undefined" || exception == _ev)
136      testPassed(_a + " threw exception " + exception + ".");
137    else
138      testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Threw exception " + exception + ".");
139  } else if (typeof _av == "undefined")
140    testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Was undefined.");
141  else
142    testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Was " + _av + ".");
143}
144