shell.js (10364B)
1 /* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /*--- 7 defines: [testRegExp, clone_object_check] 8 allow_unused: True 9 ---*/ 10 11 (function(global) { 12 /* 13 * Date: 07 February 2001 14 * 15 * Functionality common to RegExp testing - 16 */ 17 //----------------------------------------------------------------------------- 18 19 var MSG_PATTERN = '\nregexp = '; 20 var MSG_STRING = '\nstring = '; 21 var MSG_EXPECT = '\nExpect: '; 22 var MSG_ACTUAL = '\nActual: '; 23 var ERR_LENGTH = '\nERROR !!! match arrays have different lengths:'; 24 var ERR_MATCH = '\nERROR !!! regexp failed to give expected match array:'; 25 var ERR_NO_MATCH = '\nERROR !!! regexp FAILED to match anything !!!'; 26 var ERR_UNEXP_MATCH = '\nERROR !!! regexp MATCHED when we expected it to fail !!!'; 27 var CHAR_LBRACKET = '['; 28 var CHAR_RBRACKET = ']'; 29 var CHAR_QT_DBL = '"'; 30 var CHAR_QT = "'"; 31 var CHAR_NL = '\n'; 32 var CHAR_COMMA = ','; 33 var CHAR_SPACE = ' '; 34 var TYPE_STRING = typeof 'abc'; 35 36 37 38 function testRegExp(statuses, patterns, strings, actualmatches, expectedmatches) 39 { 40 var status = ''; 41 var pattern = new RegExp(); 42 var string = ''; 43 var actualmatch = new Array(); 44 var expectedmatch = new Array(); 45 var state = ''; 46 var lActual = -1; 47 var lExpect = -1; 48 49 50 for (var i=0; i != patterns.length; i++) 51 { 52 status = statuses[i]; 53 pattern = patterns[i]; 54 string = strings[i]; 55 actualmatch=actualmatches[i]; 56 expectedmatch=expectedmatches[i]; 57 state = getState(status, pattern, string); 58 59 description = status; 60 61 if(actualmatch) 62 { 63 actual = formatArray(actualmatch); 64 if(expectedmatch) 65 { 66 // expectedmatch and actualmatch are arrays - 67 lExpect = expectedmatch.length; 68 lActual = actualmatch.length; 69 70 var expected = formatArray(expectedmatch); 71 72 if (lActual != lExpect) 73 { 74 reportCompare(lExpect, lActual, 75 state + ERR_LENGTH + 76 MSG_EXPECT + expected + 77 MSG_ACTUAL + actual + 78 CHAR_NL 79 ); 80 continue; 81 } 82 83 // OK, the arrays have same length - 84 if (expected != actual) 85 { 86 reportCompare(expected, actual, 87 state + ERR_MATCH + 88 MSG_EXPECT + expected + 89 MSG_ACTUAL + actual + 90 CHAR_NL 91 ); 92 } 93 else 94 { 95 reportCompare(expected, actual, state) 96 } 97 98 } 99 else //expectedmatch is null - that is, we did not expect a match - 100 { 101 expected = expectedmatch; 102 reportCompare(expected, actual, 103 state + ERR_UNEXP_MATCH + 104 MSG_EXPECT + expectedmatch + 105 MSG_ACTUAL + actual + 106 CHAR_NL 107 ); 108 } 109 110 } 111 else // actualmatch is null 112 { 113 if (expectedmatch) 114 { 115 actual = actualmatch; 116 reportCompare(expected, actual, 117 state + ERR_NO_MATCH + 118 MSG_EXPECT + expectedmatch + 119 MSG_ACTUAL + actualmatch + 120 CHAR_NL 121 ); 122 } 123 else // we did not expect a match 124 { 125 // Being ultra-cautious. Presumably expectedmatch===actualmatch===null 126 expected = expectedmatch; 127 actual = actualmatch; 128 reportCompare (expectedmatch, actualmatch, state); 129 } 130 } 131 } 132 } 133 134 global.testRegExp = testRegExp; 135 136 function getState(status, pattern, string) 137 { 138 /* 139 * Escape \n's, etc. to make them LITERAL in the presentation string. 140 * We don't have to worry about this in |pattern|; such escaping is 141 * done automatically by pattern.toString(), invoked implicitly below. 142 * 143 * One would like to simply do: string = string.replace(/(\s)/g, '\$1'). 144 * However, the backreference $1 is not a literal string value, 145 * so this method doesn't work. 146 * 147 * Also tried string = string.replace(/(\s)/g, escape('$1')); 148 * but this just inserts the escape of the literal '$1', i.e. '%241'. 149 */ 150 string = string.replace(/\n/g, '\\n'); 151 string = string.replace(/\r/g, '\\r'); 152 string = string.replace(/\t/g, '\\t'); 153 string = string.replace(/\v/g, '\\v'); 154 string = string.replace(/\f/g, '\\f'); 155 156 return (status + MSG_PATTERN + pattern + MSG_STRING + singleQuote(string)); 157 } 158 159 160 /* 161 * If available, arr.toSource() gives more detail than arr.toString() 162 * 163 * var arr = Array(1,2,'3'); 164 * 165 * arr.toSource() 166 * [1, 2, "3"] 167 * 168 * arr.toString() 169 * 1,2,3 170 * 171 * But toSource() doesn't exist in Rhino, so use our own imitation, below - 172 * 173 */ 174 function formatArray(arr) 175 { 176 try 177 { 178 return arr.toSource(); 179 } 180 catch(e) 181 { 182 return toSource(arr); 183 } 184 } 185 186 187 /* 188 * Imitate SpiderMonkey's arr.toSource() method: 189 * 190 * a) Double-quote each array element that is of string type 191 * b) Represent |undefined| and |null| by empty strings 192 * c) Delimit elements by a comma + single space 193 * d) Do not add delimiter at the end UNLESS the last element is |undefined| 194 * e) Add square brackets to the beginning and end of the string 195 */ 196 function toSource(arr) 197 { 198 var delim = CHAR_COMMA + CHAR_SPACE; 199 var elt = ''; 200 var ret = ''; 201 var len = arr.length; 202 203 for (i=0; i<len; i++) 204 { 205 elt = arr[i]; 206 207 switch(true) 208 { 209 case (typeof elt === TYPE_STRING) : 210 ret += doubleQuote(elt); 211 break; 212 213 case (elt === undefined || elt === null) : 214 break; // add nothing but the delimiter, below - 215 216 default: 217 ret += elt.toString(); 218 } 219 220 if ((i < len-1) || (elt === undefined)) 221 ret += delim; 222 } 223 224 return CHAR_LBRACKET + ret + CHAR_RBRACKET; 225 } 226 227 228 function doubleQuote(text) 229 { 230 return CHAR_QT_DBL + text + CHAR_QT_DBL; 231 } 232 233 234 function singleQuote(text) 235 { 236 return CHAR_QT + text + CHAR_QT; 237 } 238 239 })(this); 240 241 242 (function(global) { 243 244 // The Worker constructor can take a relative URL, but different test runners 245 // run in different enough environments that it doesn't all just automatically 246 // work. For the shell, we use just a filename; for the browser, see browser.js. 247 var workerDir = ''; 248 249 // Assert that cloning b does the right thing as far as we can tell. 250 // Caveat: getters in b must produce the same value each time they're 251 // called. We may call them several times. 252 // 253 // If desc is provided, then the very first thing we do to b is clone it. 254 // (The self-modifying object test counts on this.) 255 // 256 function clone_object_check(b, desc) { 257 function classOf(obj) { 258 return Object.prototype.toString.call(obj); 259 } 260 261 function ownProperties(obj) { 262 return Object.getOwnPropertyNames(obj). 263 map(function (p) { return [p, Object.getOwnPropertyDescriptor(obj, p)]; }); 264 } 265 266 function isArrayLength(obj, pair) { 267 return Array.isArray(obj) && pair[0] == "length"; 268 } 269 270 function isCloneable(obj, pair) { 271 return isArrayLength(obj, pair) || (typeof pair[0] === 'string' && pair[1].enumerable); 272 } 273 274 function notIndex(p) { 275 var u = p >>> 0; 276 return !("" + u == p && u != 0xffffffff); 277 } 278 279 function assertIsCloneOf(a, b, path) { 280 assertEq(a === b, false); 281 282 var ca = classOf(a); 283 assertEq(ca, classOf(b), path); 284 285 assertEq(Object.getPrototypeOf(a), 286 ca == "[object Object]" ? Object.prototype : Array.prototype, 287 path); 288 289 // 'b', the original object, may have non-enumerable or XMLName 290 // properties; ignore them (except .length, if it's an Array). 291 // 'a', the clone, should not have any non-enumerable properties 292 // (except .length, if it's an Array) or XMLName properties. 293 var pb = ownProperties(b).filter(function(element) { 294 return isCloneable(b, element); 295 }); 296 var pa = ownProperties(a); 297 for (var i = 0; i < pa.length; i++) { 298 assertEq(typeof pa[i][0], "string", "clone should not have E4X properties " + path); 299 if (!isCloneable(a, pa[i])) { 300 throw new Error("non-cloneable clone property " + pa[i][0] + " " + path); 301 } 302 } 303 304 // Check that, apart from properties whose names are array indexes, 305 // the enumerable properties appear in the same order. 306 var aNames = pa.map(function (pair) { return pair[1]; }).filter(notIndex); 307 var bNames = pa.map(function (pair) { return pair[1]; }).filter(notIndex); 308 assertEq(aNames.join(","), bNames.join(","), path); 309 310 // Check that the lists are the same when including array indexes. 311 function byName(a, b) { a = a[0]; b = b[0]; return a < b ? -1 : a === b ? 0 : 1; } 312 pa.sort(byName); 313 pb.sort(byName); 314 assertEq(pa.length, pb.length, "should see the same number of properties " + path); 315 for (var i = 0; i < pa.length; i++) { 316 var aName = pa[i][0]; 317 var bName = pb[i][0]; 318 assertEq(aName, bName, path); 319 320 var path2 = path + "." + aName; 321 var da = pa[i][1]; 322 var db = pb[i][1]; 323 if (!isArrayLength(a, pa[i])) { 324 assertEq(da.configurable, true, path2); 325 } 326 assertEq(da.writable, true, path2); 327 assertEq("value" in da, true, path2); 328 var va = da.value; 329 var vb = b[pb[i][0]]; 330 if (typeof va === "object" && va !== null) 331 queue.push([va, vb, path2]); 332 else 333 assertEq(va, vb, path2); 334 } 335 } 336 337 var banner = "while testing clone of " + (desc || JSON.stringify(b)); 338 var a = deserialize(serialize(b)); 339 var queue = [[a, b, banner]]; 340 while (queue.length) { 341 var triple = queue.shift(); 342 assertIsCloneOf(triple[0], triple[1], triple[2]); 343 } 344 345 return a; // for further testing 346 } 347 global.clone_object_check = clone_object_check; 348 349 })(this);