Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
michael@0 | 2 | // Use of this source code is governed by a BSD-style license that can be |
michael@0 | 3 | // found in the LICENSE file. |
michael@0 | 4 | |
michael@0 | 5 | // This is a test harness for running javascript tests in the browser. |
michael@0 | 6 | // The only identifier exposed by this harness is WebGLTestHarnessModule. |
michael@0 | 7 | // |
michael@0 | 8 | // To use it make an HTML page with an iframe. Then call the harness like this |
michael@0 | 9 | // |
michael@0 | 10 | // function reportResults(type, msg, success) { |
michael@0 | 11 | // ... |
michael@0 | 12 | // return true; |
michael@0 | 13 | // } |
michael@0 | 14 | // |
michael@0 | 15 | // var fileListURL = '00_test_list.txt'; |
michael@0 | 16 | // var testHarness = new WebGLTestHarnessModule.TestHarness( |
michael@0 | 17 | // iframe, |
michael@0 | 18 | // fileListURL, |
michael@0 | 19 | // reportResults); |
michael@0 | 20 | // |
michael@0 | 21 | // The harness will load the fileListURL and parse it for the URLs, one URL |
michael@0 | 22 | // per line. URLs should be on the same domain and at the same folder level |
michael@0 | 23 | // or below the main html file. If any URL ends in .txt it will be parsed |
michael@0 | 24 | // as well so you can nest .txt files. URLs inside a .txt file should be |
michael@0 | 25 | // relative to that text file. |
michael@0 | 26 | // |
michael@0 | 27 | // During startup, for each page found the reportFunction will be called with |
michael@0 | 28 | // WebGLTestHarnessModule.TestHarness.reportType.ADD_PAGE and msg will be |
michael@0 | 29 | // the URL of the test. |
michael@0 | 30 | // |
michael@0 | 31 | // Each test is required to call testHarness.reportResults. This is most easily |
michael@0 | 32 | // accomplished by storing that value on the main window with |
michael@0 | 33 | // |
michael@0 | 34 | // window.webglTestHarness = testHarness |
michael@0 | 35 | // |
michael@0 | 36 | // and then adding these to functions to your tests. |
michael@0 | 37 | // |
michael@0 | 38 | // function reportTestResultsToHarness(success, msg) { |
michael@0 | 39 | // if (window.parent.webglTestHarness) { |
michael@0 | 40 | // window.parent.webglTestHarness.reportResults(success, msg); |
michael@0 | 41 | // } |
michael@0 | 42 | // } |
michael@0 | 43 | // |
michael@0 | 44 | // function notifyFinishedToHarness() { |
michael@0 | 45 | // if (window.parent.webglTestHarness) { |
michael@0 | 46 | // window.parent.webglTestHarness.notifyFinished(); |
michael@0 | 47 | // } |
michael@0 | 48 | // } |
michael@0 | 49 | // |
michael@0 | 50 | // This way your tests will still run without the harness and you can use |
michael@0 | 51 | // any testing framework you want. |
michael@0 | 52 | // |
michael@0 | 53 | // Each test should call reportTestResultsToHarness with true for success if it |
michael@0 | 54 | // succeeded and false if it fail followed and any message it wants to |
michael@0 | 55 | // associate with the test. If your testing framework supports checking for |
michael@0 | 56 | // timeout you can call it with success equal to undefined in that case. |
michael@0 | 57 | // |
michael@0 | 58 | // To run the tests, call testHarness.runTests(); |
michael@0 | 59 | // |
michael@0 | 60 | // For each test run, before the page is loaded the reportFunction will be |
michael@0 | 61 | // called with WebGLTestHarnessModule.TestHarness.reportType.START_PAGE and msg |
michael@0 | 62 | // will be the URL of the test. You may return false if you want the test to be |
michael@0 | 63 | // skipped. |
michael@0 | 64 | // |
michael@0 | 65 | // For each test completed the reportFunction will be called with |
michael@0 | 66 | // with WebGLTestHarnessModule.TestHarness.reportType.TEST_RESULT, |
michael@0 | 67 | // success = true on success, false on failure, undefined on timeout |
michael@0 | 68 | // and msg is any message the test choose to pass on. |
michael@0 | 69 | // |
michael@0 | 70 | // When all the tests on the page have finished your page must call |
michael@0 | 71 | // notifyFinishedToHarness. If notifyFinishedToHarness is not called |
michael@0 | 72 | // the harness will assume the test timed out. |
michael@0 | 73 | // |
michael@0 | 74 | // When all the tests on a page have finished OR the page as timed out the |
michael@0 | 75 | // reportFunction will be called with |
michael@0 | 76 | // WebGLTestHarnessModule.TestHarness.reportType.FINISH_PAGE |
michael@0 | 77 | // where success = true if the page has completed or undefined if the page timed |
michael@0 | 78 | // out. |
michael@0 | 79 | // |
michael@0 | 80 | // Finally, when all the tests have completed the reportFunction will be called |
michael@0 | 81 | // with WebGLTestHarnessModule.TestHarness.reportType.FINISHED_ALL_TESTS. |
michael@0 | 82 | // |
michael@0 | 83 | |
michael@0 | 84 | WebGLTestHarnessModule = function() { |
michael@0 | 85 | |
michael@0 | 86 | /** |
michael@0 | 87 | * Wrapped logging function. |
michael@0 | 88 | */ |
michael@0 | 89 | var log = function(msg) { |
michael@0 | 90 | if (window.console && window.console.log) { |
michael@0 | 91 | window.console.log(msg); |
michael@0 | 92 | } |
michael@0 | 93 | }; |
michael@0 | 94 | |
michael@0 | 95 | /** |
michael@0 | 96 | * Loads text from an external file. This function is synchronous. |
michael@0 | 97 | * @param {string} url The url of the external file. |
michael@0 | 98 | * @param {!function(bool, string): void} callback that is sent a bool for |
michael@0 | 99 | * success and the string. |
michael@0 | 100 | */ |
michael@0 | 101 | var loadTextFileAsynchronous = function(url, callback) { |
michael@0 | 102 | log ("loading: " + url); |
michael@0 | 103 | var error = 'loadTextFileSynchronous failed to load url "' + url + '"'; |
michael@0 | 104 | var request; |
michael@0 | 105 | if (window.XMLHttpRequest) { |
michael@0 | 106 | request = new XMLHttpRequest(); |
michael@0 | 107 | if (request.overrideMimeType) { |
michael@0 | 108 | request.overrideMimeType('text/plain'); |
michael@0 | 109 | } |
michael@0 | 110 | } else { |
michael@0 | 111 | throw 'XMLHttpRequest is disabled'; |
michael@0 | 112 | } |
michael@0 | 113 | try { |
michael@0 | 114 | request.open('GET', url, true); |
michael@0 | 115 | request.onreadystatechange = function() { |
michael@0 | 116 | if (request.readyState == 4) { |
michael@0 | 117 | var text = ''; |
michael@0 | 118 | // HTTP reports success with a 200 status. The file protocol reports |
michael@0 | 119 | // success with zero. HTTP does not use zero as a status code (they |
michael@0 | 120 | // start at 100). |
michael@0 | 121 | // https://developer.mozilla.org/En/Using_XMLHttpRequest |
michael@0 | 122 | var success = request.status == 200 || request.status == 0; |
michael@0 | 123 | if (success) { |
michael@0 | 124 | text = request.responseText; |
michael@0 | 125 | } |
michael@0 | 126 | log("loaded: " + url); |
michael@0 | 127 | callback(success, text); |
michael@0 | 128 | } |
michael@0 | 129 | }; |
michael@0 | 130 | request.send(null); |
michael@0 | 131 | } catch (e) { |
michael@0 | 132 | log("failed to load: " + url); |
michael@0 | 133 | callback(false, ''); |
michael@0 | 134 | } |
michael@0 | 135 | }; |
michael@0 | 136 | |
michael@0 | 137 | /** |
michael@0 | 138 | * Compare version strings. |
michael@0 | 139 | */ |
michael@0 | 140 | var greaterThanOrEqualToVersion = function(have, want) { |
michael@0 | 141 | have = have.split(" ")[0].split("."); |
michael@0 | 142 | want = want.split(" ")[0].split("."); |
michael@0 | 143 | |
michael@0 | 144 | //have 1.2.3 want 1.1 |
michael@0 | 145 | //have 1.1.1 want 1.1 |
michael@0 | 146 | //have 1.0.9 want 1.1 |
michael@0 | 147 | //have 1.1 want 1.1.1 |
michael@0 | 148 | |
michael@0 | 149 | for (var ii = 0; ii < want.length; ++ii) { |
michael@0 | 150 | var wantNum = parseInt(want[ii]); |
michael@0 | 151 | var haveNum = have[ii] ? parseInt(have[ii]) : 0 |
michael@0 | 152 | if (haveNum < wantNum) { |
michael@0 | 153 | return false; |
michael@0 | 154 | } |
michael@0 | 155 | } |
michael@0 | 156 | return true; |
michael@0 | 157 | }; |
michael@0 | 158 | |
michael@0 | 159 | /** |
michael@0 | 160 | * Reads a file, recursively adding files referenced inside. |
michael@0 | 161 | * |
michael@0 | 162 | * Each line of URL is parsed, comments starting with '#' or ';' |
michael@0 | 163 | * or '//' are stripped. |
michael@0 | 164 | * |
michael@0 | 165 | * arguments beginning with -- are extracted |
michael@0 | 166 | * |
michael@0 | 167 | * lines that end in .txt are recursively scanned for more files |
michael@0 | 168 | * other lines are added to the list of files. |
michael@0 | 169 | * |
michael@0 | 170 | * @param {string} url The url of the file to read. |
michael@0 | 171 | * @param {void function(boolean, !Array.<string>)} callback. |
michael@0 | 172 | * Callback that is called with true for success and an |
michael@0 | 173 | * array of filenames. |
michael@0 | 174 | * @param {Object} options. Optional options |
michael@0 | 175 | * |
michael@0 | 176 | * Options: |
michael@0 | 177 | * version: {string} The version of the conformance test. |
michael@0 | 178 | * Tests with the argument --min-version <version> will |
michael@0 | 179 | * be ignored version is less then <version> |
michael@0 | 180 | * |
michael@0 | 181 | */ |
michael@0 | 182 | var getFileList = function(url, callback, options) { |
michael@0 | 183 | var files = []; |
michael@0 | 184 | |
michael@0 | 185 | var copyObject = function(obj) { |
michael@0 | 186 | return JSON.parse(JSON.stringify(obj)); |
michael@0 | 187 | }; |
michael@0 | 188 | |
michael@0 | 189 | var toCamelCase = function(str) { |
michael@0 | 190 | return str.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase() }); |
michael@0 | 191 | }; |
michael@0 | 192 | |
michael@0 | 193 | var globalOptions = copyObject(options); |
michael@0 | 194 | globalOptions.defaultVersion = "1.0"; |
michael@0 | 195 | |
michael@0 | 196 | var getFileListImpl = function(prefix, line, hierarchicalOptions, callback) { |
michael@0 | 197 | var files = []; |
michael@0 | 198 | |
michael@0 | 199 | var args = line.split(/\s+/); |
michael@0 | 200 | var nonOptions = []; |
michael@0 | 201 | var useTest = true; |
michael@0 | 202 | var testOptions = {}; |
michael@0 | 203 | for (var jj = 0; jj < args.length; ++jj) { |
michael@0 | 204 | var arg = args[jj]; |
michael@0 | 205 | if (arg[0] == '-') { |
michael@0 | 206 | if (arg[1] != '-') { |
michael@0 | 207 | throw ("bad option at in " + url + ":" + (ii + 1) + ": " + str); |
michael@0 | 208 | } |
michael@0 | 209 | var option = arg.substring(2); |
michael@0 | 210 | switch (option) { |
michael@0 | 211 | case 'min-version': |
michael@0 | 212 | ++jj; |
michael@0 | 213 | testOptions[toCamelCase(option)] = args[jj]; |
michael@0 | 214 | break; |
michael@0 | 215 | default: |
michael@0 | 216 | throw ("bad unknown option '" + option + "' at in " + url + ":" + (ii + 1) + ": " + str); |
michael@0 | 217 | } |
michael@0 | 218 | } else { |
michael@0 | 219 | nonOptions.push(arg); |
michael@0 | 220 | } |
michael@0 | 221 | } |
michael@0 | 222 | var url = prefix + nonOptions.join(" "); |
michael@0 | 223 | |
michael@0 | 224 | if (url.substr(url.length - 4) != '.txt') { |
michael@0 | 225 | var minVersion = testOptions.minVersion; |
michael@0 | 226 | if (!minVersion) { |
michael@0 | 227 | minVersion = hierarchicalOptions.defaultVersion; |
michael@0 | 228 | } |
michael@0 | 229 | |
michael@0 | 230 | if (globalOptions.minVersion) { |
michael@0 | 231 | useTest = greaterThanOrEqualToVersion(minVersion, globalOptions.minVersion); |
michael@0 | 232 | } else { |
michael@0 | 233 | useTest = greaterThanOrEqualToVersion(globalOptions.version, minVersion); |
michael@0 | 234 | } |
michael@0 | 235 | } |
michael@0 | 236 | |
michael@0 | 237 | if (!useTest) { |
michael@0 | 238 | callback(true, []); |
michael@0 | 239 | return; |
michael@0 | 240 | } |
michael@0 | 241 | |
michael@0 | 242 | if (url.substr(url.length - 4) == '.txt') { |
michael@0 | 243 | // If a version was explicity specified pass it down. |
michael@0 | 244 | if (testOptions.minVersion) { |
michael@0 | 245 | hierarchicalOptions.defaultVersion = testOptions.minVersion; |
michael@0 | 246 | } |
michael@0 | 247 | loadTextFileAsynchronous(url, function() { |
michael@0 | 248 | return function(success, text) { |
michael@0 | 249 | if (!success) { |
michael@0 | 250 | callback(false, ''); |
michael@0 | 251 | return; |
michael@0 | 252 | } |
michael@0 | 253 | var lines = text.split('\n'); |
michael@0 | 254 | var prefix = ''; |
michael@0 | 255 | var lastSlash = url.lastIndexOf('/'); |
michael@0 | 256 | if (lastSlash >= 0) { |
michael@0 | 257 | prefix = url.substr(0, lastSlash + 1); |
michael@0 | 258 | } |
michael@0 | 259 | var fail = false; |
michael@0 | 260 | var count = 1; |
michael@0 | 261 | var index = 0; |
michael@0 | 262 | for (var ii = 0; ii < lines.length; ++ii) { |
michael@0 | 263 | var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, ''); |
michael@0 | 264 | if (str.length > 4 && |
michael@0 | 265 | str[0] != '#' && |
michael@0 | 266 | str[0] != ";" && |
michael@0 | 267 | str.substr(0, 2) != "//") { |
michael@0 | 268 | ++count; |
michael@0 | 269 | getFileListImpl(prefix, str, copyObject(hierarchicalOptions), function(index) { |
michael@0 | 270 | return function(success, new_files) { |
michael@0 | 271 | log("got files: " + new_files.length); |
michael@0 | 272 | if (success) { |
michael@0 | 273 | files[index] = new_files; |
michael@0 | 274 | } |
michael@0 | 275 | finish(success); |
michael@0 | 276 | }; |
michael@0 | 277 | }(index++)); |
michael@0 | 278 | } |
michael@0 | 279 | } |
michael@0 | 280 | finish(true); |
michael@0 | 281 | |
michael@0 | 282 | function finish(success) { |
michael@0 | 283 | if (!success) { |
michael@0 | 284 | fail = true; |
michael@0 | 285 | } |
michael@0 | 286 | --count; |
michael@0 | 287 | log("count: " + count); |
michael@0 | 288 | if (!count) { |
michael@0 | 289 | callback(!fail, files); |
michael@0 | 290 | } |
michael@0 | 291 | } |
michael@0 | 292 | } |
michael@0 | 293 | }()); |
michael@0 | 294 | } else { |
michael@0 | 295 | files.push(url); |
michael@0 | 296 | callback(true, files); |
michael@0 | 297 | } |
michael@0 | 298 | }; |
michael@0 | 299 | |
michael@0 | 300 | getFileListImpl('', url, globalOptions, function(success, files) { |
michael@0 | 301 | // flatten |
michael@0 | 302 | var flat = []; |
michael@0 | 303 | flatten(files); |
michael@0 | 304 | function flatten(files) { |
michael@0 | 305 | for (var ii = 0; ii < files.length; ++ii) { |
michael@0 | 306 | var value = files[ii]; |
michael@0 | 307 | if (typeof(value) == "string") { |
michael@0 | 308 | flat.push(value); |
michael@0 | 309 | } else { |
michael@0 | 310 | flatten(value); |
michael@0 | 311 | } |
michael@0 | 312 | } |
michael@0 | 313 | } |
michael@0 | 314 | callback(success, flat); |
michael@0 | 315 | }); |
michael@0 | 316 | }; |
michael@0 | 317 | |
michael@0 | 318 | var TestFile = function(url) { |
michael@0 | 319 | this.url = url; |
michael@0 | 320 | }; |
michael@0 | 321 | |
michael@0 | 322 | var TestHarness = function(iframe, filelistUrl, reportFunc, options) { |
michael@0 | 323 | this.window = window; |
michael@0 | 324 | this.iframe = iframe; |
michael@0 | 325 | this.reportFunc = reportFunc; |
michael@0 | 326 | this.timeoutDelay = 20000; |
michael@0 | 327 | this.files = []; |
michael@0 | 328 | |
michael@0 | 329 | var that = this; |
michael@0 | 330 | getFileList(filelistUrl, function() { |
michael@0 | 331 | return function(success, files) { |
michael@0 | 332 | that.addFiles_(success, files); |
michael@0 | 333 | }; |
michael@0 | 334 | }(), options); |
michael@0 | 335 | |
michael@0 | 336 | }; |
michael@0 | 337 | |
michael@0 | 338 | TestHarness.reportType = { |
michael@0 | 339 | ADD_PAGE: 1, |
michael@0 | 340 | READY: 2, |
michael@0 | 341 | START_PAGE: 3, |
michael@0 | 342 | TEST_RESULT: 4, |
michael@0 | 343 | FINISH_PAGE: 5, |
michael@0 | 344 | FINISHED_ALL_TESTS: 6 |
michael@0 | 345 | }; |
michael@0 | 346 | |
michael@0 | 347 | TestHarness.prototype.addFiles_ = function(success, files) { |
michael@0 | 348 | if (!success) { |
michael@0 | 349 | this.reportFunc( |
michael@0 | 350 | TestHarness.reportType.FINISHED_ALL_TESTS, |
michael@0 | 351 | 'Unable to load tests. Are you running locally?\n' + |
michael@0 | 352 | 'You need to run from a server or configure your\n' + |
michael@0 | 353 | 'browser to allow access to local files (not recommended).\n\n' + |
michael@0 | 354 | 'Note: An easy way to run from a server:\n\n' + |
michael@0 | 355 | '\tcd path_to_tests\n' + |
michael@0 | 356 | '\tpython -m SimpleHTTPServer\n\n' + |
michael@0 | 357 | 'then point your browser to ' + |
michael@0 | 358 | '<a href="http://localhost:8000/webgl-conformance-tests.html">' + |
michael@0 | 359 | 'http://localhost:8000/webgl-conformance-tests.html</a>', |
michael@0 | 360 | false) |
michael@0 | 361 | return; |
michael@0 | 362 | } |
michael@0 | 363 | log("total files: " + files.length); |
michael@0 | 364 | for (var ii = 0; ii < files.length; ++ii) { |
michael@0 | 365 | log("" + ii + ": " + files[ii]); |
michael@0 | 366 | this.files.push(new TestFile(files[ii])); |
michael@0 | 367 | this.reportFunc(TestHarness.reportType.ADD_PAGE, files[ii], undefined); |
michael@0 | 368 | } |
michael@0 | 369 | this.reportFunc(TestHarness.reportType.READY, undefined, undefined); |
michael@0 | 370 | } |
michael@0 | 371 | |
michael@0 | 372 | TestHarness.prototype.runTests = function(opt_start, opt_count) { |
michael@0 | 373 | var count = opt_count || this.files.length; |
michael@0 | 374 | this.nextFileIndex = opt_start || 0; |
michael@0 | 375 | this.lastFileIndex = this.nextFileIndex + count; |
michael@0 | 376 | this.startNextFile(); |
michael@0 | 377 | }; |
michael@0 | 378 | |
michael@0 | 379 | TestHarness.prototype.setTimeout = function() { |
michael@0 | 380 | var that = this; |
michael@0 | 381 | this.timeoutId = this.window.setTimeout(function() { |
michael@0 | 382 | that.timeout(); |
michael@0 | 383 | }, this.timeoutDelay); |
michael@0 | 384 | }; |
michael@0 | 385 | |
michael@0 | 386 | TestHarness.prototype.clearTimeout = function() { |
michael@0 | 387 | this.window.clearTimeout(this.timeoutId); |
michael@0 | 388 | }; |
michael@0 | 389 | |
michael@0 | 390 | TestHarness.prototype.startNextFile = function() { |
michael@0 | 391 | if (this.nextFileIndex >= this.lastFileIndex) { |
michael@0 | 392 | log("done"); |
michael@0 | 393 | this.reportFunc(TestHarness.reportType.FINISHED_ALL_TESTS, |
michael@0 | 394 | '', true); |
michael@0 | 395 | } else { |
michael@0 | 396 | this.currentFile = this.files[this.nextFileIndex++]; |
michael@0 | 397 | log("loading: " + this.currentFile.url); |
michael@0 | 398 | if (this.reportFunc(TestHarness.reportType.START_PAGE, |
michael@0 | 399 | this.currentFile.url, undefined)) { |
michael@0 | 400 | this.iframe.src = this.currentFile.url; |
michael@0 | 401 | this.setTimeout(); |
michael@0 | 402 | } else { |
michael@0 | 403 | this.reportResults(false, "skipped"); |
michael@0 | 404 | this.notifyFinished(); |
michael@0 | 405 | } |
michael@0 | 406 | } |
michael@0 | 407 | }; |
michael@0 | 408 | |
michael@0 | 409 | TestHarness.prototype.reportResults = function (success, msg) { |
michael@0 | 410 | this.clearTimeout(); |
michael@0 | 411 | log(success ? "PASS" : "FAIL", msg); |
michael@0 | 412 | this.reportFunc(TestHarness.reportType.TEST_RESULT, msg, success); |
michael@0 | 413 | // For each result we get, reset the timeout |
michael@0 | 414 | this.setTimeout(); |
michael@0 | 415 | }; |
michael@0 | 416 | |
michael@0 | 417 | TestHarness.prototype.notifyFinished = function () { |
michael@0 | 418 | this.clearTimeout(); |
michael@0 | 419 | var url = this.currentFile ? this.currentFile.url : 'unknown'; |
michael@0 | 420 | log(url + ": finished"); |
michael@0 | 421 | this.reportFunc(TestHarness.reportType.FINISH_PAGE, url, true); |
michael@0 | 422 | this.startNextFile(); |
michael@0 | 423 | }; |
michael@0 | 424 | |
michael@0 | 425 | TestHarness.prototype.timeout = function() { |
michael@0 | 426 | this.clearTimeout(); |
michael@0 | 427 | var url = this.currentFile ? this.currentFile.url : 'unknown'; |
michael@0 | 428 | log(url + ": timeout"); |
michael@0 | 429 | this.reportFunc(TestHarness.reportType.FINISH_PAGE, url, undefined); |
michael@0 | 430 | this.startNextFile(); |
michael@0 | 431 | }; |
michael@0 | 432 | |
michael@0 | 433 | TestHarness.prototype.setTimeoutDelay = function(x) { |
michael@0 | 434 | this.timeoutDelay = x; |
michael@0 | 435 | }; |
michael@0 | 436 | |
michael@0 | 437 | return { |
michael@0 | 438 | 'TestHarness': TestHarness |
michael@0 | 439 | }; |
michael@0 | 440 | |
michael@0 | 441 | }(); |
michael@0 | 442 | |
michael@0 | 443 | |
michael@0 | 444 |