1 // Copyright 2008 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 function MjsUnitAssertionError(message) { 29 this.message = message; 30 // This allows fetching the stack trace using TryCatch::StackTrace. 31 this.stack = new Error("").stack; 32 } 33 34 MjsUnitAssertionError.prototype.toString = function () { 35 return this.message; 36 } 37 38 /* 39 * This file is included in all mini jsunit test cases. The test 40 * framework expects lines that signal failed tests to start with 41 * the f-word and ignore all other lines. 42 */ 43 44 function MjsUnitToString(value) { 45 switch (typeof value) { 46 case "string": 47 return JSON.stringify(value); 48 case "number": 49 if (value === 0 && (1 / value) < 0) return "-0"; 50 case "boolean": 51 case "null": 52 case "undefined": 53 case "function": 54 return String(value); 55 case "object": 56 if (value === null) return "null"; 57 var clazz = Object.prototype.toString.call(value); 58 clazz = clazz.substring(8, clazz.length - 1); 59 switch (clazz) { 60 case "Number": 61 case "String": 62 case "Boolean": 63 case "Date": 64 return clazz + "(" + MjsUnitToString(value.valueOf()) + ")"; 65 case "RegExp": 66 return value.toString(); 67 case "Array": 68 return "[" + value.map(MjsUnitArrayElementToString).join(",") + "]"; 69 case "Object": 70 break; 71 default: 72 return clazz + "()"; 73 } 74 // [[Class]] is "Object". 75 var constructor = value.constructor.name; 76 if (name) return name + "()"; 77 return "Object()"; 78 default: 79 return "-- unknown value --"; 80 } 81 } 82 83 84 function MjsUnitArrayElementToString(value, index, array) { 85 if (value === undefined && !(index in array)) return ""; 86 return MjsUnitToString(value); 87 } 88 89 90 function fail(expected, found, name_opt) { 91 var message = "Fail" + "ure"; 92 if (name_opt) { 93 // Fix this when we ditch the old test runner. 94 message += " (" + name_opt + ")"; 95 } 96 97 message += ": expected <" + MjsUnitToString(expected) + 98 "> found <" + MjsUnitToString(found) + ">"; 99 throw new MjsUnitAssertionError(message); 100 } 101 102 103 function deepObjectEquals(a, b) { 104 var aProps = []; 105 for (var key in a) 106 aProps.push(key); 107 var bProps = []; 108 for (var key in b) 109 bProps.push(key); 110 aProps.sort(); 111 bProps.sort(); 112 if (!deepEquals(aProps, bProps)) 113 return false; 114 for (var i = 0; i < aProps.length; i++) { 115 if (!deepEquals(a[aProps[i]], b[aProps[i]])) 116 return false; 117 } 118 return true; 119 } 120 121 122 function deepEquals(a, b) { 123 if (a == b) { 124 // Check for -0. 125 if (a === 0 && b === 0) return (1 / a) === (1 / b); 126 return true; 127 } 128 if (typeof a == "number" && typeof b == "number" && isNaN(a) && isNaN(b)) { 129 return true; 130 } 131 if (a == null || b == null) return false; 132 if (a.constructor === RegExp || b.constructor === RegExp) { 133 return (a.constructor === b.constructor) && (a.toString() === b.toString()); 134 } 135 if ((typeof a) !== 'object' || (typeof b) !== 'object' || 136 (a === null) || (b === null)) 137 return false; 138 if (a.constructor === Array) { 139 if (b.constructor !== Array) 140 return false; 141 if (a.length != b.length) 142 return false; 143 for (var i = 0; i < a.length; i++) { 144 if (i in a) { 145 if (!(i in b) || !(deepEquals(a[i], b[i]))) 146 return false; 147 } else if (i in b) { 148 return false; 149 } 150 } 151 return true; 152 } else { 153 return deepObjectEquals(a, b); 154 } 155 } 156 157 158 function assertSame(expected, found, name_opt) { 159 if (found !== expected) { 160 fail(expected, found, name_opt); 161 } 162 } 163 164 165 function assertEquals(expected, found, name_opt) { 166 if (!deepEquals(found, expected)) { 167 fail(expected, found, name_opt); 168 } 169 } 170 171 172 function assertArrayEquals(expected, found, name_opt) { 173 var start = ""; 174 if (name_opt) { 175 start = name_opt + " - "; 176 } 177 assertEquals(expected.length, found.length, start + "array length"); 178 if (expected.length == found.length) { 179 for (var i = 0; i < expected.length; ++i) { 180 assertEquals(expected[i], found[i], start + "array element at index " + i); 181 } 182 } 183 } 184 185 186 function assertTrue(value, name_opt) { 187 assertEquals(true, value, name_opt); 188 } 189 190 191 function assertFalse(value, name_opt) { 192 assertEquals(false, value, name_opt); 193 } 194 195 196 function assertNaN(value, name_opt) { 197 if (!isNaN(value)) { 198 fail("NaN", value, name_opt); 199 } 200 } 201 202 203 function assertNull(value, name_opt) { 204 if (value !== null) { 205 fail("null", value, name_opt); 206 } 207 } 208 209 210 function assertNotNull(value, name_opt) { 211 if (value === null) { 212 fail("not null", value, name_opt); 213 } 214 } 215 216 217 function assertThrows(code, type_opt, cause_opt) { 218 var threwException = true; 219 try { 220 if (typeof code == 'function') { 221 code(); 222 } else { 223 eval(code); 224 } 225 threwException = false; 226 } catch (e) { 227 if (typeof type_opt == 'function') 228 assertInstanceof(e, type_opt); 229 if (arguments.length >= 3) 230 assertEquals(e.type, cause_opt); 231 // Do nothing. 232 } 233 if (!threwException) assertTrue(false, "did not throw exception"); 234 } 235 236 237 function assertInstanceof(obj, type) { 238 if (!(obj instanceof type)) { 239 assertTrue(false, "Object <" + obj + "> is not an instance of <" + type + ">"); 240 } 241 } 242 243 244 function assertDoesNotThrow(code) { 245 try { 246 if (typeof code == 'function') { 247 code(); 248 } else { 249 eval(code); 250 } 251 } catch (e) { 252 assertTrue(false, "threw an exception: " + (e.message || e)); 253 } 254 } 255 256 257 function assertUnreachable(name_opt) { 258 // Fix this when we ditch the old test runner. 259 var message = "Fail" + "ure: unreachable"; 260 if (name_opt) { 261 message += " - " + name_opt; 262 } 263 throw new MjsUnitAssertionError(message); 264 } 265