summaryrefslogtreecommitdiffstats
path: root/dom/canvas/test/webgl-conf/checkout/js/webgl-test-harness.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/canvas/test/webgl-conf/checkout/js/webgl-test-harness.js642
1 files changed, 642 insertions, 0 deletions
diff --git a/dom/canvas/test/webgl-conf/checkout/js/webgl-test-harness.js b/dom/canvas/test/webgl-conf/checkout/js/webgl-test-harness.js
new file mode 100644
index 0000000000..f48d9d2ad7
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/js/webgl-test-harness.js
@@ -0,0 +1,642 @@
+/*
+Copyright (c) 2019 The Khronos Group Inc.
+Use of this source code is governed by an MIT-style license that can be
+found in the LICENSE.txt file.
+*/
+
+// This is a test harness for running javascript tests in the browser.
+// The only identifier exposed by this harness is WebGLTestHarnessModule.
+//
+// To use it make an HTML page with an iframe. Then call the harness like this
+//
+// function reportResults(type, msg, success) {
+// ...
+// return true;
+// }
+//
+// var fileListURL = '00_test_list.txt';
+// var testHarness = new WebGLTestHarnessModule.TestHarness(
+// iframe,
+// fileListURL,
+// reportResults,
+// options);
+//
+// The harness will load the fileListURL and parse it for the URLs, one URL
+// per line preceded by options, see below. URLs should be on the same domain
+// and at the same folder level or below the main html file. If any URL ends
+// in .txt it will be parsed as well so you can nest .txt files. URLs inside a
+// .txt file should be relative to that text file.
+//
+// During startup, for each page found the reportFunction will be called with
+// WebGLTestHarnessModule.TestHarness.reportType.ADD_PAGE and msg will be
+// the URL of the test.
+//
+// Each test is required to call testHarness.reportResults. This is most easily
+// accomplished by storing that value on the main window with
+//
+// window.webglTestHarness = testHarness
+//
+// and then adding these to functions to your tests.
+//
+// function reportTestResultsToHarness(success, msg) {
+// if (window.parent.webglTestHarness) {
+// window.parent.webglTestHarness.reportResults(success, msg);
+// }
+// }
+//
+// function notifyFinishedToHarness() {
+// if (window.parent.webglTestHarness) {
+// window.parent.webglTestHarness.notifyFinished();
+// }
+// }
+//
+// This way your tests will still run without the harness and you can use
+// any testing framework you want.
+//
+// Each test should call reportTestResultsToHarness with true for success if it
+// succeeded and false if it fail followed and any message it wants to
+// associate with the test. If your testing framework supports checking for
+// timeout you can call it with success equal to undefined in that case.
+//
+// To run the tests, call testHarness.runTests(options);
+//
+// For each test run, before the page is loaded the reportFunction will be
+// called with WebGLTestHarnessModule.TestHarness.reportType.START_PAGE and msg
+// will be the URL of the test. You may return false if you want the test to be
+// skipped.
+//
+// For each test completed the reportFunction will be called with
+// with WebGLTestHarnessModule.TestHarness.reportType.TEST_RESULT,
+// success = true on success, false on failure, undefined on timeout
+// and msg is any message the test choose to pass on.
+//
+// When all the tests on the page have finished your page must call
+// notifyFinishedToHarness. If notifyFinishedToHarness is not called
+// the harness will assume the test timed out.
+//
+// When all the tests on a page have finished OR the page as timed out the
+// reportFunction will be called with
+// WebGLTestHarnessModule.TestHarness.reportType.FINISH_PAGE
+// where success = true if the page has completed or undefined if the page timed
+// out.
+//
+// Finally, when all the tests have completed the reportFunction will be called
+// with WebGLTestHarnessModule.TestHarness.reportType.FINISHED_ALL_TESTS.
+//
+// Harness Options
+//
+// These are passed in to the TestHarness as a JavaScript object
+//
+// version: (required!)
+//
+// Specifies a version used to filter tests. Tests marked as requiring
+// a version greater than this version will not be included.
+//
+// example: new TestHarness(...., {version: "3.1.2"});
+//
+// minVersion:
+//
+// Specifies the minimum version a test must require to be included.
+// This basically flips the filter so that only tests marked with
+// --min-version will be included if they are at this minVersion or
+// greater.
+//
+// example: new TestHarness(...., {minVersion: "2.3.1"});
+//
+// maxVersion:
+//
+// Specifies the maximum version a test must require to be included.
+// This basically flips the filter so that only tests marked with
+// --max-version will be included if they are at this maxVersion or
+// less.
+//
+// example: new TestHarness(...., {maxVersion: "2.3.1"});
+//
+// fast:
+//
+// Specifies to skip any tests marked as slow.
+//
+// example: new TestHarness(..., {fast: true});
+//
+// Test Options:
+//
+// Any test URL or .txt file can be prefixed by the following options
+//
+// min-version:
+//
+// Sets the minimum version required to include this test. A version is
+// passed into the harness options. Any test marked as requiring a
+// min-version greater than the version passed to the harness is skipped.
+// This allows you to add new tests to a suite of tests for a future
+// version of the suite without including the test in the current version.
+// If no -min-version is specified it is inheriited from the .txt file
+// including it. The default is 1.0.0
+//
+// example: --min-version 2.1.3 sometest.html
+//
+// max-version:
+//
+// Sets the maximum version required to include this test. A version is
+// passed into the harness options. Any test marked as requiring a
+// max-version less than the version passed to the harness is skipped.
+// This allows you to test functionality that has been removed from later
+// versions of the suite.
+// If no -max-version is specified it is inherited from the .txt file
+// including it.
+//
+// example: --max-version 1.9.9 sometest.html
+//
+// slow:
+//
+// Marks a test as slow. Slow tests can be skipped by passing fastOnly: true
+// to the TestHarness. Of course you need to pass all tests but sometimes
+// you'd like to test quickly and run only the fast subset of tests.
+//
+// example: --slow some-test-that-takes-2-mins.html
+//
+
+WebGLTestHarnessModule = function() {
+
+/**
+ * Wrapped logging function.
+ */
+var log = function(msg) {
+ if (window.console && window.console.log) {
+ window.console.log(msg);
+ }
+};
+
+/**
+ * Loads text from an external file. This function is synchronous.
+ * @param {string} url The url of the external file.
+ * @param {!function(bool, string): void} callback that is sent a bool for
+ * success and the string.
+ */
+var loadTextFileAsynchronous = function(url, callback) {
+ log ("loading: " + url);
+ var error = 'loadTextFileSynchronous failed to load url "' + url + '"';
+ var request;
+ if (window.XMLHttpRequest) {
+ request = new XMLHttpRequest();
+ if (request.overrideMimeType) {
+ request.overrideMimeType('text/plain');
+ }
+ } else {
+ throw 'XMLHttpRequest is disabled';
+ }
+ try {
+ request.open('GET', url, true);
+ request.onreadystatechange = function() {
+ if (request.readyState == 4) {
+ var text = '';
+ // HTTP reports success with a 200 status. The file protocol reports
+ // success with zero. HTTP does not use zero as a status code (they
+ // start at 100).
+ // https://developer.mozilla.org/En/Using_XMLHttpRequest
+ var success = request.status == 200 || request.status == 0;
+ if (success) {
+ text = request.responseText;
+ }
+ log("loaded: " + url);
+ callback(success, text);
+ }
+ };
+ request.send(null);
+ } catch (e) {
+ log("failed to load: " + url);
+ callback(false, '');
+ }
+};
+
+/**
+ * @param {string} versionString WebGL version string.
+ * @return {number} Integer containing the WebGL major version.
+ */
+var getMajorVersion = function(versionString) {
+ if (!versionString) {
+ return 1;
+ }
+ return parseInt(versionString.split(" ")[0].split(".")[0], 10);
+};
+
+/**
+ * @param {string} url Base URL of the test.
+ * @param {map} options Map of options to append to the URL's query string.
+ * @return {string} URL that will run the test with the given WebGL version.
+ */
+var getURLWithOptions = function(url, options) {
+ var queryArgs = 0;
+
+ for (i in options) {
+ url += queryArgs ? "&" : "?";
+ url += i + "=" + options[i];
+ queryArgs++;
+ }
+
+ return url;
+};
+
+/**
+ * Compare version strings.
+ */
+var greaterThanOrEqualToVersion = function(have, want) {
+ have = have.split(" ")[0].split(".");
+ want = want.split(" ")[0].split(".");
+
+ //have 1.2.3 want 1.1
+ //have 1.1.1 want 1.1
+ //have 1.0.9 want 1.1
+ //have 1.1 want 1.1.1
+
+ for (var ii = 0; ii < want.length; ++ii) {
+ var wantNum = parseInt(want[ii]);
+ var haveNum = have[ii] ? parseInt(have[ii]) : 0
+ if (haveNum > wantNum) {
+ return true; // 2.0.0 is greater than 1.2.3
+ }
+ if (haveNum < wantNum) {
+ return false;
+ }
+ }
+ return true;
+};
+
+/**
+ * Reads a file, recursively adding files referenced inside.
+ *
+ * Each line of URL is parsed, comments starting with '#' or ';'
+ * or '//' are stripped.
+ *
+ * arguments beginning with -- are extracted
+ *
+ * lines that end in .txt are recursively scanned for more files
+ * other lines are added to the list of files.
+ *
+ * @param {string} url The url of the file to read.
+ * @param {function(boolean, !Array.<string>):void} callback
+ * Callback that is called with true for success and an
+ * array of filenames.
+ * @param {Object} options Optional options
+ *
+ * Options:
+ * version: {string} The version of the conformance test.
+ * Tests with the argument --min-version <version> will
+ * be ignored version is less then <version>
+ *
+ */
+var getFileList = function(url, callback, options) {
+ var files = [];
+
+ var copyObject = function(obj) {
+ return JSON.parse(JSON.stringify(obj));
+ };
+
+ var toCamelCase = function(str) {
+ return str.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase() });
+ };
+
+ var globalOptions = copyObject(options);
+ globalOptions.defaultVersion = "1.0";
+ globalOptions.defaultMaxVersion = null;
+
+ var getFileListImpl = function(prefix, line, lineNum, hierarchicalOptions, callback) {
+ var files = [];
+
+ var args = line.split(/\s+/);
+ var nonOptions = [];
+ var useTest = true;
+ var testOptions = {};
+ for (var jj = 0; jj < args.length; ++jj) {
+ var arg = args[jj];
+ if (arg[0] == '-') {
+ if (arg[1] != '-') {
+ throw ("bad option at in " + url + ":" + lineNum + ": " + arg);
+ }
+ var option = arg.substring(2);
+ switch (option) {
+ // no argument options.
+ case 'slow':
+ testOptions[toCamelCase(option)] = true;
+ break;
+ // one argument options.
+ case 'min-version':
+ case 'max-version':
+ ++jj;
+ testOptions[toCamelCase(option)] = args[jj];
+ break;
+ default:
+ throw ("bad unknown option '" + option + "' at in " + url + ":" + lineNum + ": " + arg);
+ }
+ } else {
+ nonOptions.push(arg);
+ }
+ }
+ var url = prefix + nonOptions.join(" ");
+
+ if (url.substr(url.length - 4) != '.txt') {
+ var minVersion = testOptions.minVersion;
+ if (!minVersion) {
+ minVersion = hierarchicalOptions.defaultVersion;
+ }
+ var maxVersion = testOptions.maxVersion;
+ if (!maxVersion) {
+ maxVersion = hierarchicalOptions.defaultMaxVersion;
+ }
+ var slow = testOptions.slow;
+ if (!slow) {
+ slow = hierarchicalOptions.defaultSlow;
+ }
+
+ if (globalOptions.fast && slow) {
+ useTest = false;
+ } else if (globalOptions.minVersion) {
+ useTest = greaterThanOrEqualToVersion(minVersion, globalOptions.minVersion);
+ } else if (globalOptions.maxVersion && maxVersion) {
+ useTest = greaterThanOrEqualToVersion(globalOptions.maxVersion, maxVersion);
+ } else {
+ useTest = greaterThanOrEqualToVersion(globalOptions.version, minVersion);
+ if (maxVersion) {
+ useTest = useTest && greaterThanOrEqualToVersion(maxVersion, globalOptions.version);
+ }
+ }
+ }
+
+ if (!useTest) {
+ callback(true, []);
+ return;
+ }
+
+ if (url.substr(url.length - 4) == '.txt') {
+ // If a version was explicity specified pass it down.
+ if (testOptions.minVersion) {
+ hierarchicalOptions.defaultVersion = testOptions.minVersion;
+ }
+ if (testOptions.maxVersion) {
+ hierarchicalOptions.defaultMaxVersion = testOptions.maxVersion;
+ }
+ if (testOptions.slow) {
+ hierarchicalOptions.defaultSlow = testOptions.slow;
+ }
+ loadTextFileAsynchronous(url, function() {
+ return function(success, text) {
+ if (!success) {
+ callback(false, '');
+ return;
+ }
+ var lines = text.split('\n');
+ var prefix = '';
+ var lastSlash = url.lastIndexOf('/');
+ if (lastSlash >= 0) {
+ prefix = url.substr(0, lastSlash + 1);
+ }
+ var fail = false;
+ var count = 1;
+ var index = 0;
+ for (var ii = 0; ii < lines.length; ++ii) {
+ var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, '');
+ if (str.length > 4 &&
+ str[0] != '#' &&
+ str[0] != ";" &&
+ str.substr(0, 2) != "//") {
+ ++count;
+ getFileListImpl(prefix, str, ii + 1, copyObject(hierarchicalOptions), function(index) {
+ return function(success, new_files) {
+ //log("got files: " + new_files.length);
+ if (success) {
+ files[index] = new_files;
+ }
+ finish(success);
+ };
+ }(index++));
+ }
+ }
+ finish(true);
+
+ function finish(success) {
+ if (!success) {
+ fail = true;
+ }
+ --count;
+ //log("count: " + count);
+ if (!count) {
+ callback(!fail, files);
+ }
+ }
+ }
+ }());
+ } else {
+ files.push(url);
+ callback(true, files);
+ }
+ };
+
+ getFileListImpl('', url, 1, globalOptions, function(success, files) {
+ // flatten
+ var flat = [];
+ flatten(files);
+ function flatten(files) {
+ for (var ii = 0; ii < files.length; ++ii) {
+ var value = files[ii];
+ if (typeof(value) == "string") {
+ flat.push(value);
+ } else {
+ flatten(value);
+ }
+ }
+ }
+ callback(success, flat);
+ });
+};
+
+var FilterURL = (function() {
+ var prefix = window.location.pathname;
+ prefix = prefix.substring(0, prefix.lastIndexOf("/") + 1);
+ return function(url) {
+ if (url.substring(0, prefix.length) == prefix) {
+ url = url.substring(prefix.length);
+ }
+ return url;
+ };
+}());
+
+var TestFile = function(url) {
+ this.url = url;
+};
+
+var Test = function(file) {
+ this.file = file;
+};
+
+var TestHarness = function(iframe, filelistUrl, reportFunc, options) {
+ this.window = window;
+ this.iframes = iframe.length ? iframe : [iframe];
+ this.reportFunc = reportFunc;
+ this.timeoutDelay = 20000;
+ this.files = [];
+ this.allowSkip = options.allowSkip;
+ this.webglVersion = getMajorVersion(options.version);
+ this.dumpShaders = options.dumpShaders;
+ this.quiet = options.quiet;
+
+ var that = this;
+ getFileList(filelistUrl, function() {
+ return function(success, files) {
+ that.addFiles_(success, files);
+ };
+ }(), options);
+
+};
+
+TestHarness.reportType = {
+ ADD_PAGE: 1,
+ READY: 2,
+ START_PAGE: 3,
+ TEST_RESULT: 4,
+ FINISH_PAGE: 5,
+ FINISHED_ALL_TESTS: 6
+};
+
+TestHarness.prototype.addFiles_ = function(success, files) {
+ if (!success) {
+ this.reportFunc(
+ TestHarness.reportType.FINISHED_ALL_TESTS,
+ '',
+ 'Unable to load tests. Are you running locally?\n' +
+ 'You need to run from a server or configure your\n' +
+ 'browser to allow access to local files (not recommended).\n\n' +
+ 'Note: An easy way to run from a server:\n\n' +
+ '\tcd path_to_tests\n' +
+ '\tpython -m SimpleHTTPServer\n\n' +
+ 'then point your browser to ' +
+ '<a href="http://localhost:8000/webgl-conformance-tests.html">' +
+ 'http://localhost:8000/webgl-conformance-tests.html</a>',
+ false)
+ return;
+ }
+ log("total files: " + files.length);
+ for (var ii = 0; ii < files.length; ++ii) {
+ log("" + ii + ": " + files[ii]);
+ this.files.push(new TestFile(files[ii]));
+ this.reportFunc(TestHarness.reportType.ADD_PAGE, '', files[ii], undefined);
+ }
+ this.reportFunc(TestHarness.reportType.READY, '', undefined, undefined);
+}
+
+TestHarness.prototype.runTests = function(opt_options) {
+ var options = opt_options || { };
+ options.start = options.start || 0;
+ options.count = options.count || this.files.length;
+
+ this.idleIFrames = this.iframes.slice(0);
+ this.runningTests = {};
+ var testsToRun = [];
+ for (var ii = 0; ii < options.count; ++ii) {
+ testsToRun.push(ii + options.start);
+ }
+ this.numTestsRemaining = options.count;
+ this.testsToRun = testsToRun;
+ this.startNextTest();
+};
+
+TestHarness.prototype.setTimeout = function(test) {
+ var that = this;
+ test.timeoutId = this.window.setTimeout(function() {
+ that.timeout(test);
+ }, this.timeoutDelay);
+};
+
+TestHarness.prototype.clearTimeout = function(test) {
+ this.window.clearTimeout(test.timeoutId);
+};
+
+TestHarness.prototype.startNextTest = function() {
+ if (this.numTestsRemaining == 0) {
+ log("done");
+ this.reportFunc(TestHarness.reportType.FINISHED_ALL_TESTS,
+ '', '', true);
+ } else {
+ while (this.testsToRun.length > 0 && this.idleIFrames.length > 0) {
+ var testId = this.testsToRun.shift();
+ var iframe = this.idleIFrames.shift();
+ this.startTest(iframe, this.files[testId], this.webglVersion);
+ }
+ }
+};
+
+TestHarness.prototype.startTest = function(iframe, testFile, webglVersion) {
+ var test = {
+ iframe: iframe,
+ testFile: testFile
+ };
+ var url = testFile.url;
+ this.runningTests[url] = test;
+ log("loading: " + url);
+ if (this.reportFunc(TestHarness.reportType.START_PAGE, url, url, undefined)) {
+ iframe.src = getURLWithOptions(url, {
+ "webglVersion": webglVersion,
+ "dumpShaders": this.dumpShaders,
+ "quiet": this.quiet
+ });
+ this.setTimeout(test);
+ } else {
+ this.reportResults(url, !!this.allowSkip, "skipped", true);
+ this.notifyFinished(url);
+ }
+};
+
+TestHarness.prototype.getTest = function(url) {
+ var test = this.runningTests[FilterURL(url)];
+ if (!test) {
+ throw("unknown test:" + url);
+ }
+ return test;
+};
+
+TestHarness.prototype.reportResults = function(url, success, msg, skipped) {
+ url = FilterURL(url);
+ var test = this.getTest(url);
+ this.clearTimeout(test);
+ log((success ? "PASS" : "FAIL") + ": " + msg);
+ this.reportFunc(TestHarness.reportType.TEST_RESULT, url, msg, success, skipped);
+ // For each result we get, reset the timeout
+ this.setTimeout(test);
+};
+
+TestHarness.prototype.dequeTest = function(test) {
+ this.clearTimeout(test);
+ this.idleIFrames.push(test.iframe);
+ delete this.runningTests[test.testFile.url];
+ --this.numTestsRemaining;
+}
+
+TestHarness.prototype.notifyFinished = function(url) {
+ url = FilterURL(url);
+ var test = this.getTest(url);
+ log(url + ": finished");
+ this.dequeTest(test);
+ this.reportFunc(TestHarness.reportType.FINISH_PAGE, url, url, true);
+ this.startNextTest();
+};
+
+TestHarness.prototype.timeout = function(test) {
+ this.dequeTest(test);
+ var url = test.testFile.url;
+ log(url + ": timeout");
+ this.reportFunc(TestHarness.reportType.FINISH_PAGE, url, url, undefined);
+ this.startNextTest();
+};
+
+TestHarness.prototype.setTimeoutDelay = function(x) {
+ this.timeoutDelay = x;
+};
+
+return {
+ 'TestHarness': TestHarness,
+ 'getMajorVersion': getMajorVersion,
+ 'getURLWithOptions': getURLWithOptions
+ };
+
+}();
+
+
+