content/canvas/test/webgl-conformance/resources/webgl-test-harness.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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

mercurial