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

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/canvas/test/webgl-conformance/resources/webgl-test-harness.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,444 @@
     1.4 +// Copyright (c) 2009 The Chromium Authors. All rights reserved.
     1.5 +// Use of this source code is governed by a BSD-style license that can be
     1.6 +// found in the LICENSE file.
     1.7 +
     1.8 +// This is a test harness for running javascript tests in the browser.
     1.9 +// The only identifier exposed by this harness is WebGLTestHarnessModule.
    1.10 +//
    1.11 +// To use it make an HTML page with an iframe. Then call the harness like this
    1.12 +//
    1.13 +//    function reportResults(type, msg, success) {
    1.14 +//      ...
    1.15 +//      return true;
    1.16 +//    }
    1.17 +//
    1.18 +//    var fileListURL = '00_test_list.txt';
    1.19 +//    var testHarness = new WebGLTestHarnessModule.TestHarness(
    1.20 +//        iframe,
    1.21 +//        fileListURL,
    1.22 +//        reportResults);
    1.23 +//
    1.24 +// The harness will load the fileListURL and parse it for the URLs, one URL
    1.25 +// per line. URLs should be on the same domain and at the same folder level
    1.26 +// or below the main html file.  If any URL ends in .txt it will be parsed
    1.27 +// as well so you can nest .txt files. URLs inside a .txt file should be
    1.28 +// relative to that text file.
    1.29 +//
    1.30 +// During startup, for each page found the reportFunction will be called with
    1.31 +// WebGLTestHarnessModule.TestHarness.reportType.ADD_PAGE and msg will be
    1.32 +// the URL of the test.
    1.33 +//
    1.34 +// Each test is required to call testHarness.reportResults. This is most easily
    1.35 +// accomplished by storing that value on the main window with
    1.36 +//
    1.37 +//     window.webglTestHarness = testHarness
    1.38 +//
    1.39 +// and then adding these to functions to your tests.
    1.40 +//
    1.41 +//     function reportTestResultsToHarness(success, msg) {
    1.42 +//       if (window.parent.webglTestHarness) {
    1.43 +//         window.parent.webglTestHarness.reportResults(success, msg);
    1.44 +//       }
    1.45 +//     }
    1.46 +//
    1.47 +//     function notifyFinishedToHarness() {
    1.48 +//       if (window.parent.webglTestHarness) {
    1.49 +//         window.parent.webglTestHarness.notifyFinished();
    1.50 +//       }
    1.51 +//     }
    1.52 +//
    1.53 +// This way your tests will still run without the harness and you can use
    1.54 +// any testing framework you want.
    1.55 +//
    1.56 +// Each test should call reportTestResultsToHarness with true for success if it
    1.57 +// succeeded and false if it fail followed and any message it wants to
    1.58 +// associate with the test. If your testing framework supports checking for
    1.59 +// timeout you can call it with success equal to undefined in that case.
    1.60 +//
    1.61 +// To run the tests, call testHarness.runTests();
    1.62 +//
    1.63 +// For each test run, before the page is loaded the reportFunction will be
    1.64 +// called with WebGLTestHarnessModule.TestHarness.reportType.START_PAGE and msg
    1.65 +// will be the URL of the test. You may return false if you want the test to be
    1.66 +// skipped.
    1.67 +//
    1.68 +// For each test completed the reportFunction will be called with
    1.69 +// with WebGLTestHarnessModule.TestHarness.reportType.TEST_RESULT,
    1.70 +// success = true on success, false on failure, undefined on timeout
    1.71 +// and msg is any message the test choose to pass on.
    1.72 +//
    1.73 +// When all the tests on the page have finished your page must call
    1.74 +// notifyFinishedToHarness.  If notifyFinishedToHarness is not called
    1.75 +// the harness will assume the test timed out.
    1.76 +//
    1.77 +// When all the tests on a page have finished OR the page as timed out the
    1.78 +// reportFunction will be called with
    1.79 +// WebGLTestHarnessModule.TestHarness.reportType.FINISH_PAGE
    1.80 +// where success = true if the page has completed or undefined if the page timed
    1.81 +// out.
    1.82 +//
    1.83 +// Finally, when all the tests have completed the reportFunction will be called
    1.84 +// with WebGLTestHarnessModule.TestHarness.reportType.FINISHED_ALL_TESTS.
    1.85 +//
    1.86 +
    1.87 +WebGLTestHarnessModule = function() {
    1.88 +
    1.89 +/**
    1.90 + * Wrapped logging function.
    1.91 + */
    1.92 +var log = function(msg) {
    1.93 +  if (window.console && window.console.log) {
    1.94 +    window.console.log(msg);
    1.95 +  }
    1.96 +};
    1.97 +
    1.98 +/**
    1.99 + * Loads text from an external file. This function is synchronous.
   1.100 + * @param {string} url The url of the external file.
   1.101 + * @param {!function(bool, string): void} callback that is sent a bool for
   1.102 + *     success and the string.
   1.103 + */
   1.104 +var loadTextFileAsynchronous = function(url, callback) {
   1.105 +  log ("loading: " + url);
   1.106 +  var error = 'loadTextFileSynchronous failed to load url "' + url + '"';
   1.107 +  var request;
   1.108 +  if (window.XMLHttpRequest) {
   1.109 +    request = new XMLHttpRequest();
   1.110 +    if (request.overrideMimeType) {
   1.111 +      request.overrideMimeType('text/plain');
   1.112 +    }
   1.113 +  } else {
   1.114 +    throw 'XMLHttpRequest is disabled';
   1.115 +  }
   1.116 +  try {
   1.117 +    request.open('GET', url, true);
   1.118 +    request.onreadystatechange = function() {
   1.119 +      if (request.readyState == 4) {
   1.120 +        var text = '';
   1.121 +        // HTTP reports success with a 200 status. The file protocol reports
   1.122 +        // success with zero. HTTP does not use zero as a status code (they
   1.123 +        // start at 100).
   1.124 +        // https://developer.mozilla.org/En/Using_XMLHttpRequest
   1.125 +        var success = request.status == 200 || request.status == 0;
   1.126 +        if (success) {
   1.127 +          text = request.responseText;
   1.128 +        }
   1.129 +        log("loaded: " + url);
   1.130 +        callback(success, text);
   1.131 +      }
   1.132 +    };
   1.133 +    request.send(null);
   1.134 +  } catch (e) {
   1.135 +    log("failed to load: " + url);
   1.136 +    callback(false, '');
   1.137 +  }
   1.138 +};
   1.139 +
   1.140 +/**
   1.141 + * Compare version strings.
   1.142 + */
   1.143 +var greaterThanOrEqualToVersion = function(have, want) {
   1.144 +  have = have.split(" ")[0].split(".");
   1.145 +  want = want.split(" ")[0].split(".");
   1.146 +
   1.147 +  //have 1.2.3   want  1.1
   1.148 +  //have 1.1.1   want  1.1
   1.149 +  //have 1.0.9   want  1.1
   1.150 +  //have 1.1     want  1.1.1
   1.151 +
   1.152 +  for (var ii = 0; ii < want.length; ++ii) {
   1.153 +    var wantNum = parseInt(want[ii]);
   1.154 +    var haveNum = have[ii] ? parseInt(have[ii]) : 0
   1.155 +    if (haveNum < wantNum) {
   1.156 +      return false;
   1.157 +    }
   1.158 +  }
   1.159 +  return true;
   1.160 +};
   1.161 +
   1.162 +/**
   1.163 + * Reads a file, recursively adding files referenced inside.
   1.164 + *
   1.165 + * Each line of URL is parsed, comments starting with '#' or ';'
   1.166 + * or '//' are stripped.
   1.167 + *
   1.168 + * arguments beginning with -- are extracted
   1.169 + *
   1.170 + * lines that end in .txt are recursively scanned for more files
   1.171 + * other lines are added to the list of files.
   1.172 + *
   1.173 + * @param {string} url The url of the file to read.
   1.174 + * @param {void function(boolean, !Array.<string>)} callback.
   1.175 + *      Callback that is called with true for success and an
   1.176 + *      array of filenames.
   1.177 + * @param {Object} options. Optional options
   1.178 + *
   1.179 + * Options:
   1.180 + *    version: {string} The version of the conformance test.
   1.181 + *    Tests with the argument --min-version <version> will
   1.182 + *    be ignored version is less then <version>
   1.183 + *
   1.184 + */
   1.185 +var getFileList = function(url, callback, options) {
   1.186 +  var files = [];
   1.187 +
   1.188 +  var copyObject = function(obj) {
   1.189 +    return JSON.parse(JSON.stringify(obj));
   1.190 +  };
   1.191 +
   1.192 +  var toCamelCase = function(str) {
   1.193 +    return str.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase() });
   1.194 +  };
   1.195 +
   1.196 +  var globalOptions = copyObject(options);
   1.197 +  globalOptions.defaultVersion = "1.0";
   1.198 +
   1.199 +  var getFileListImpl = function(prefix, line, hierarchicalOptions, callback) {
   1.200 +    var files = [];
   1.201 +
   1.202 +    var args = line.split(/\s+/);
   1.203 +    var nonOptions = [];
   1.204 +    var useTest = true;
   1.205 +    var testOptions = {};
   1.206 +    for (var jj = 0; jj < args.length; ++jj) {
   1.207 +      var arg = args[jj];
   1.208 +      if (arg[0] == '-') {
   1.209 +        if (arg[1] != '-') {
   1.210 +          throw ("bad option at in " + url + ":" + (ii + 1) + ": " + str);
   1.211 +        }
   1.212 +        var option = arg.substring(2);
   1.213 +        switch (option) {
   1.214 +          case 'min-version':
   1.215 +            ++jj;
   1.216 +            testOptions[toCamelCase(option)] = args[jj];
   1.217 +            break;
   1.218 +          default:
   1.219 +            throw ("bad unknown option '" + option + "' at in " + url + ":" + (ii + 1) + ": " + str);
   1.220 +        }
   1.221 +      } else {
   1.222 +        nonOptions.push(arg);
   1.223 +      }
   1.224 +    }
   1.225 +    var url = prefix + nonOptions.join(" ");
   1.226 +
   1.227 +    if (url.substr(url.length - 4) != '.txt') {
   1.228 +      var minVersion = testOptions.minVersion;
   1.229 +      if (!minVersion) {
   1.230 +        minVersion = hierarchicalOptions.defaultVersion;
   1.231 +      }
   1.232 +
   1.233 +      if (globalOptions.minVersion) {
   1.234 +        useTest = greaterThanOrEqualToVersion(minVersion, globalOptions.minVersion);
   1.235 +      } else {
   1.236 +        useTest = greaterThanOrEqualToVersion(globalOptions.version, minVersion);
   1.237 +      }
   1.238 +    }
   1.239 +
   1.240 +    if (!useTest) {
   1.241 +      callback(true, []);
   1.242 +      return;
   1.243 +    }
   1.244 +
   1.245 +    if (url.substr(url.length - 4) == '.txt') {
   1.246 +      // If a version was explicity specified pass it down.
   1.247 +      if (testOptions.minVersion) {
   1.248 +        hierarchicalOptions.defaultVersion = testOptions.minVersion;
   1.249 +      }
   1.250 +      loadTextFileAsynchronous(url, function() {
   1.251 +        return function(success, text) {
   1.252 +          if (!success) {
   1.253 +            callback(false, '');
   1.254 +            return;
   1.255 +          }
   1.256 +          var lines = text.split('\n');
   1.257 +          var prefix = '';
   1.258 +          var lastSlash = url.lastIndexOf('/');
   1.259 +          if (lastSlash >= 0) {
   1.260 +            prefix = url.substr(0, lastSlash + 1);
   1.261 +          }
   1.262 +          var fail = false;
   1.263 +          var count = 1;
   1.264 +          var index = 0;
   1.265 +          for (var ii = 0; ii < lines.length; ++ii) {
   1.266 +            var str = lines[ii].replace(/^\s\s*/, '').replace(/\s\s*$/, '');
   1.267 +            if (str.length > 4 &&
   1.268 +                str[0] != '#' &&
   1.269 +                str[0] != ";" &&
   1.270 +                str.substr(0, 2) != "//") {
   1.271 +              ++count;
   1.272 +              getFileListImpl(prefix, str, copyObject(hierarchicalOptions), function(index) {
   1.273 +                return function(success, new_files) {
   1.274 +                  log("got files: " + new_files.length);
   1.275 +                  if (success) {
   1.276 +                    files[index] = new_files;
   1.277 +                  }
   1.278 +                  finish(success);
   1.279 +                };
   1.280 +              }(index++));
   1.281 +            }
   1.282 +          }
   1.283 +          finish(true);
   1.284 +
   1.285 +          function finish(success) {
   1.286 +            if (!success) {
   1.287 +              fail = true;
   1.288 +            }
   1.289 +            --count;
   1.290 +            log("count: " + count);
   1.291 +            if (!count) {
   1.292 +              callback(!fail, files);
   1.293 +            }
   1.294 +          }
   1.295 +        }
   1.296 +      }());
   1.297 +    } else {
   1.298 +      files.push(url);
   1.299 +      callback(true, files);
   1.300 +    }
   1.301 +  };
   1.302 +
   1.303 +  getFileListImpl('', url, globalOptions, function(success, files) {
   1.304 +    // flatten
   1.305 +    var flat = [];
   1.306 +    flatten(files);
   1.307 +    function flatten(files) {
   1.308 +      for (var ii = 0; ii < files.length; ++ii) {
   1.309 +        var value = files[ii];
   1.310 +        if (typeof(value) == "string") {
   1.311 +          flat.push(value);
   1.312 +        } else {
   1.313 +          flatten(value);
   1.314 +        }
   1.315 +      }
   1.316 +    }
   1.317 +    callback(success, flat);
   1.318 +  });
   1.319 +};
   1.320 +
   1.321 +var TestFile = function(url) {
   1.322 +  this.url = url;
   1.323 +};
   1.324 +
   1.325 +var TestHarness = function(iframe, filelistUrl, reportFunc, options) {
   1.326 +  this.window = window;
   1.327 +  this.iframe = iframe;
   1.328 +  this.reportFunc = reportFunc;
   1.329 +  this.timeoutDelay = 20000;
   1.330 +  this.files = [];
   1.331 +
   1.332 +  var that = this;
   1.333 +  getFileList(filelistUrl, function() {
   1.334 +    return function(success, files) {
   1.335 +      that.addFiles_(success, files);
   1.336 +    };
   1.337 +  }(), options);
   1.338 +
   1.339 +};
   1.340 +
   1.341 +TestHarness.reportType = {
   1.342 +  ADD_PAGE: 1,
   1.343 +  READY: 2,
   1.344 +  START_PAGE: 3,
   1.345 +  TEST_RESULT: 4,
   1.346 +  FINISH_PAGE: 5,
   1.347 +  FINISHED_ALL_TESTS: 6
   1.348 +};
   1.349 +
   1.350 +TestHarness.prototype.addFiles_ = function(success, files) {
   1.351 +  if (!success) {
   1.352 +    this.reportFunc(
   1.353 +        TestHarness.reportType.FINISHED_ALL_TESTS,
   1.354 +        'Unable to load tests. Are you running locally?\n' +
   1.355 +        'You need to run from a server or configure your\n' +
   1.356 +        'browser to allow access to local files (not recommended).\n\n' +
   1.357 +        'Note: An easy way to run from a server:\n\n' +
   1.358 +        '\tcd path_to_tests\n' +
   1.359 +        '\tpython -m SimpleHTTPServer\n\n' +
   1.360 +        'then point your browser to ' +
   1.361 +          '<a href="http://localhost:8000/webgl-conformance-tests.html">' +
   1.362 +          'http://localhost:8000/webgl-conformance-tests.html</a>',
   1.363 +        false)
   1.364 +    return;
   1.365 +  }
   1.366 +  log("total files: " + files.length);
   1.367 +  for (var ii = 0; ii < files.length; ++ii) {
   1.368 +    log("" + ii + ": " + files[ii]);
   1.369 +    this.files.push(new TestFile(files[ii]));
   1.370 +    this.reportFunc(TestHarness.reportType.ADD_PAGE, files[ii], undefined);
   1.371 +  }
   1.372 +  this.reportFunc(TestHarness.reportType.READY, undefined, undefined);
   1.373 +}
   1.374 +
   1.375 +TestHarness.prototype.runTests = function(opt_start, opt_count) {
   1.376 +  var count = opt_count || this.files.length;
   1.377 +  this.nextFileIndex = opt_start || 0;
   1.378 +  this.lastFileIndex = this.nextFileIndex + count;
   1.379 +  this.startNextFile();
   1.380 +};
   1.381 +
   1.382 +TestHarness.prototype.setTimeout = function() {
   1.383 +  var that = this;
   1.384 +  this.timeoutId = this.window.setTimeout(function() {
   1.385 +      that.timeout();
   1.386 +    }, this.timeoutDelay);
   1.387 +};
   1.388 +
   1.389 +TestHarness.prototype.clearTimeout = function() {
   1.390 +  this.window.clearTimeout(this.timeoutId);
   1.391 +};
   1.392 +
   1.393 +TestHarness.prototype.startNextFile = function() {
   1.394 +  if (this.nextFileIndex >= this.lastFileIndex) {
   1.395 +    log("done");
   1.396 +    this.reportFunc(TestHarness.reportType.FINISHED_ALL_TESTS,
   1.397 +                    '', true);
   1.398 +  } else {
   1.399 +    this.currentFile = this.files[this.nextFileIndex++];
   1.400 +    log("loading: " + this.currentFile.url);
   1.401 +    if (this.reportFunc(TestHarness.reportType.START_PAGE,
   1.402 +                        this.currentFile.url, undefined)) {
   1.403 +      this.iframe.src = this.currentFile.url;
   1.404 +      this.setTimeout();
   1.405 +    } else {
   1.406 +      this.reportResults(false, "skipped");
   1.407 +      this.notifyFinished();
   1.408 +    }
   1.409 +  }
   1.410 +};
   1.411 +
   1.412 +TestHarness.prototype.reportResults = function (success, msg) {
   1.413 +  this.clearTimeout();
   1.414 +  log(success ? "PASS" : "FAIL", msg);
   1.415 +  this.reportFunc(TestHarness.reportType.TEST_RESULT, msg, success);
   1.416 +  // For each result we get, reset the timeout
   1.417 +  this.setTimeout();
   1.418 +};
   1.419 +
   1.420 +TestHarness.prototype.notifyFinished = function () {
   1.421 +  this.clearTimeout();
   1.422 +  var url = this.currentFile ? this.currentFile.url : 'unknown';
   1.423 +  log(url + ": finished");
   1.424 +  this.reportFunc(TestHarness.reportType.FINISH_PAGE, url, true);
   1.425 +  this.startNextFile();
   1.426 +};
   1.427 +
   1.428 +TestHarness.prototype.timeout = function() {
   1.429 +  this.clearTimeout();
   1.430 +  var url = this.currentFile ? this.currentFile.url : 'unknown';
   1.431 +  log(url + ": timeout");
   1.432 +  this.reportFunc(TestHarness.reportType.FINISH_PAGE, url, undefined);
   1.433 +  this.startNextFile();
   1.434 +};
   1.435 +
   1.436 +TestHarness.prototype.setTimeoutDelay = function(x) {
   1.437 +  this.timeoutDelay = x;
   1.438 +};
   1.439 +
   1.440 +return {
   1.441 +    'TestHarness': TestHarness
   1.442 +  };
   1.443 +
   1.444 +}();
   1.445 +
   1.446 +
   1.447 +

mercurial