diff --git a/tools/wave/configuration_loader.py b/tools/wave/configuration_loader.py
index 2e2aa331511741a..5741ba3cf65780e 100644
--- a/tools/wave/configuration_loader.py
+++ b/tools/wave/configuration_loader.py
@@ -1,7 +1,8 @@
-# mypy: allow-untyped-defs
-
+from __future__ import absolute_import
+from __future__ import unicode_literals
import json
import os
+from io import open
from tools.wpt import wpt
@@ -91,7 +92,7 @@ def load_configuration_file(path):
return {}
configuration = None
- with open(path) as configuration_file:
+ with open(path, "r") as configuration_file:
configuration_file_content = configuration_file.read()
configuration = json.loads(configuration_file_content)
return configuration
diff --git a/tools/wave/data/client.py b/tools/wave/data/client.py
index d5643a5660173e6..ab6851ab34b91a3 100644
--- a/tools/wave/data/client.py
+++ b/tools/wave/data/client.py
@@ -1,6 +1,4 @@
-# mypy: allow-untyped-defs
-
-class Client:
+class Client(object):
def __init__(self, session_token):
self.session_token = session_token
diff --git a/tools/wave/data/device.py b/tools/wave/data/device.py
index b1d06cbf30d62a8..3b2ccdf4075b9ca 100644
--- a/tools/wave/data/device.py
+++ b/tools/wave/data/device.py
@@ -1,6 +1,4 @@
-# mypy: allow-untyped-defs
-
-class Device:
+class Device(object):
def __init__(self, token, user_agent, name, last_active):
self.token = token
self.user_agent = user_agent
diff --git a/tools/wave/data/event_listener.py b/tools/wave/data/event_listener.py
index c4b98653e1883de..2695df83cbab59a 100644
--- a/tools/wave/data/event_listener.py
+++ b/tools/wave/data/event_listener.py
@@ -1,8 +1,6 @@
-# mypy: allow-untyped-defs
-
-class EventListener:
+class EventListener(object):
def __init__(self, dispatcher_token):
- super().__init__()
+ super(EventListener, self).__init__()
self.dispatcher_token = dispatcher_token
self.token = None
diff --git a/tools/wave/data/http_polling_client.py b/tools/wave/data/http_polling_client.py
index 3235569a9891a73..740f547c9584931 100644
--- a/tools/wave/data/http_polling_client.py
+++ b/tools/wave/data/http_polling_client.py
@@ -1,11 +1,9 @@
-# mypy: allow-untyped-defs
-
from .client import Client
class HttpPollingClient(Client):
def __init__(self, session_token, event):
- super().__init__(session_token)
+ super(HttpPollingClient, self).__init__(session_token)
self.event = event
def send_message(self, message):
diff --git a/tools/wave/data/http_polling_event_listener.py b/tools/wave/data/http_polling_event_listener.py
index b1e46edd36d853d..df731d4856c4f9b 100644
--- a/tools/wave/data/http_polling_event_listener.py
+++ b/tools/wave/data/http_polling_event_listener.py
@@ -1,10 +1,8 @@
-# mypy: allow-untyped-defs
-
from .event_listener import EventListener
class HttpPollingEventListener(EventListener):
def __init__(self, dispatcher_token, event):
- super().__init__(dispatcher_token)
+ super(HttpPollingEventListener, self).__init__(dispatcher_token)
self.event = event
self.message = None
diff --git a/tools/wave/data/session.py b/tools/wave/data/session.py
index bb1b932dae60b15..f80b4719d0d3f09 100644
--- a/tools/wave/data/session.py
+++ b/tools/wave/data/session.py
@@ -1,5 +1,5 @@
-# mypy: allow-untyped-defs
-
+from __future__ import absolute_import
+from __future__ import unicode_literals
from ..testing.test_loader import MANUAL, AUTOMATIC
PAUSED = "paused"
@@ -9,8 +9,11 @@
PENDING = "pending"
UNKNOWN = "unknown"
+WMAS = "wmas"
+DPCTF = "dpctf"
+
-class Session:
+class Session(object):
def __init__(
self,
token=None,
@@ -32,7 +35,6 @@ def __init__(
reference_tokens=None,
browser=None,
expiration_date=None,
- type=None,
malfunctioning_tests=None
):
if token is None:
@@ -72,7 +74,6 @@ def __init__(
self.reference_tokens = reference_tokens
self.browser = browser
self.expiration_date = expiration_date
- self.type = type
if malfunctioning_tests is None:
malfunctioning_tests = []
self.malfunctioning_tests = malfunctioning_tests
diff --git a/tools/wave/ecmascript/README.MD b/tools/wave/ecmascript/README.MD
new file mode 100644
index 000000000000000..96d51477fb7952d
--- /dev/null
+++ b/tools/wave/ecmascript/README.MD
@@ -0,0 +1,51 @@
+# WMAS2017 ECMA Integration
+
+## Generating Tests
+
+Clone the ECMAScript 5 tests into the working directory
+
+```
+$ git clone git@github.com:tc39/test262.git -b es5-tests
+```
+
+Working directory should look like this
+
+```
+generate-tests.js
+test262
+test-template.html
+webplatform-adapter.js
+```
+
+Generate the tests by running
+
+```
+$ node generate-tests.js
+```
+
+Generated tests are placed in `ecmascript` directory. Copy this
+directory into the top level directory of the Web Platform Tests
+hierarchy in order for the Web Platform Test Runner to run them.
+
+## Test generation parameters
+
+```
+$ node generate-tests.js < test262-repo-dir > < output-dir >
+```
+
+You can specify where the test262 repository is located and where the
+generated tests should be put in by passing the paths to the
+generator script as shown above.
+
+## Excluded tests
+
+The following tests are automatically excluded, because they are
+causing the browser to freeze.
+
+```
+ch15/15.4/15.4.4/15.4.4.15/15.4.4.15-3-14.js
+ch15/15.4/15.4.4/15.4.4.18/15.4.4.18-3-14.js
+ch15/15.4/15.4.4/15.4.4.20/15.4.4.20-3-14.js
+ch15/15.4/15.4.4/15.4.4.21/15.4.4.21-3-14.js
+ch15/15.4/15.4.4/15.4.4.22/15.4.4.22-3-14.js
+```
diff --git a/tools/wave/ecmascript/generate-tests.js b/tools/wave/ecmascript/generate-tests.js
new file mode 100644
index 000000000000000..3a6d54929a1bfd1
--- /dev/null
+++ b/tools/wave/ecmascript/generate-tests.js
@@ -0,0 +1,309 @@
+const fs = require("fs-extra");
+const path = require("path");
+
+const readDirectory = async directoryPath => {
+ return new Promise((resolve, reject) => {
+ fs.readdir(directoryPath, (error, files) => {
+ if (error) {
+ reject(error);
+ }
+ resolve(files);
+ });
+ });
+};
+
+const makeDirectory = async directoryPath => {
+ return new Promise((resolve, reject) => {
+ fs.mkdir(directoryPath, error => {
+ if (error) {
+ reject(error);
+ }
+ resolve();
+ });
+ });
+};
+
+const readStats = async path => {
+ return new Promise((resolve, reject) => {
+ fs.stat(path, (error, stats) => {
+ if (error) {
+ resolve(null);
+ }
+ resolve(stats);
+ });
+ });
+};
+
+const readFile = async path => {
+ return new Promise((resolve, reject) => {
+ fs.readFile(
+ path,
+ {
+ encoding: "UTF-8"
+ },
+ (error, data) => {
+ if (error) {
+ reject(error);
+ }
+ resolve(data);
+ }
+ );
+ });
+};
+
+const writeFile = async (path, data) => {
+ return new Promise((resolve, reject) => {
+ fs.writeFile(path, data, error => {
+ if (error) {
+ reject(error);
+ }
+ resolve();
+ });
+ });
+};
+
+const parseFrontmatter = src => {
+ var start = src.indexOf("/*---");
+ var end = src.indexOf("---*/");
+ if (start === -1 || end === -1) return null;
+
+ var match,
+ includes = [],
+ flags = {},
+ negative = null;
+ var frontmatter = src.substring(start + 5, end);
+
+ match = frontmatter.match(/(?:^|\n)\s*includes:\s*\[([^\]]*)\]/);
+ if (match) {
+ includes = match[1].split(",").map(function f(s) {
+ return s.replace(/^\s+|\s+$/g, "");
+ });
+ } else {
+ match = frontmatter.match(/(?:^|\n)\s*includes:\s*\n(\s+-.*\n)/);
+ if (match) {
+ includes = match[1].split(",").map(function f(s) {
+ return s.replace(/^[\s\-]+|\s+$/g, "");
+ });
+ }
+ }
+
+ match = frontmatter.match(/(?:^|\n)\s*flags:\s*\[([^\]]*)\]/);
+ if (match) {
+ match[1]
+ .split(",")
+ .map(function f(s) {
+ return s.replace(/^\s+|\s+$/g, "");
+ })
+ .forEach(function(flag) {
+ switch (flag) {
+ case "onlyStrict":
+ if (flags.strict) {
+ console.error("flag conflict", src);
+ }
+ flags.strict = "always";
+ break;
+ case "noStrict":
+ if (flags.strict) {
+ console.error("flag conflict");
+ }
+ flags.strict = "never";
+ break;
+ case "module":
+ flags.module = true;
+ break;
+ case "raw":
+ flags.raw = true;
+ break;
+ case "async":
+ flags.async = true;
+ break;
+ case "generated":
+ case "non-deterministic":
+ case "CanBlockIsTrue":
+ case "CanBlockIsFalse":
+ break;
+ default:
+ console.error("unrecocognized flag: " + flag, frontmatter);
+ break;
+ }
+ });
+ }
+
+ match = frontmatter.match(/(?:^|\n)\s*negative:/);
+ if (match) {
+ var phase, type;
+ frontmatter
+ .substr(match.index + 9)
+ .split("\n")
+ .forEach(function(line) {
+ var match = line.match(/\s+phase:\s*(\S+)/);
+ if (match) {
+ phase = match[1];
+ }
+ match = line.match(/\s+type:\s*(\S+)/);
+ if (match) {
+ type = match[1];
+ }
+ });
+ if (!phase || !type) return null;
+ negative = {
+ phase: phase,
+ type: type
+ };
+ }
+
+ return {
+ includes: includes,
+ flags: flags,
+ negative: negative,
+ isDynamic: /dynamic-import/.test(frontmatter)
+ }; // lol, do better
+};
+
+const getOutputPath = ({ testsPath, currentPath, outputPath }) => {
+ return path.join(outputPath, path.relative(testsPath, currentPath));
+};
+
+// Tests that will freeze the runner
+// ch15/15.4/15.4.4/15.4.4.15/15.4.4.15-3-14.js
+// ch15/15.4/15.4.4/15.4.4.18/15.4.4.18-3-14.js
+// ch15/15.4/15.4.4/15.4.4.20/15.4.4.20-3-14.js
+// ch15/15.4/15.4.4/15.4.4.21/15.4.4.21-3-14.js
+// ch15/15.4/15.4.4/15.4.4.22/15.4.4.22-3-14.js
+const excludedTests = [
+ /15\.4\.4\.15-3-14\.js/,
+ /15\.4\.4\.18-3-14\.js/,
+ /15\.4\.4\.20-3-14\.js/,
+ /15\.4\.4\.21-3-14\.js/,
+ /15\.4\.4\.22-3-14\.js/
+];
+
+let testCount = 0;
+
+const generateTest = async ({
+ testsPath,
+ outputPath,
+ currentPath,
+ templateContent,
+ iframeTemplateContent
+}) => {
+ if (!currentPath) currentPath = testsPath;
+ let stats = await readStats(currentPath);
+ if (stats.isDirectory()) {
+ const outputDir = getOutputPath({
+ testsPath,
+ outputPath,
+ currentPath
+ });
+ if (!(await readStats(outputDir))) await makeDirectory(outputDir);
+ let files = await readDirectory(currentPath);
+ for (let file of files) {
+ await generateTest({
+ currentPath: path.join(currentPath, file),
+ outputPath,
+ testsPath,
+ templateContent,
+ iframeTemplateContent
+ });
+ }
+ } else {
+ if (
+ currentPath.indexOf(".js") === -1 ||
+ excludedTests.some(regex => regex.test(currentPath))
+ ) {
+ return;
+ }
+
+ const jsRelativePath = path.relative(testsPath, currentPath);
+ // console.log(jsRelativePath.replace('.js', ''))
+ const jsOutputPath = path.join(outputPath, jsRelativePath);
+ const htmlOutputPath = jsOutputPath.replace(".js", ".html");
+ const iframeHtmlOutputPath = jsOutputPath.replace(".js", ".iframe.html");
+ const jsSrc = await readFile(currentPath);
+ const meta = parseFrontmatter(jsSrc);
+ const includes = (meta && meta.includes) || [];
+ //console.log(includes);
+ const testContent = replacePlaceholders(templateContent, {
+ jsRelativePath,
+ includes,
+ iframeTestPath: `/${iframeHtmlOutputPath}`
+ });
+
+ const iframeTestContent = replacePlaceholders(iframeTemplateContent, {
+ jsRelativePath,
+ includes,
+ iframeTestPath: `/${iframeHtmlOutputPath}`
+ });
+
+ await writeFile(htmlOutputPath, testContent);
+ await writeFile(iframeHtmlOutputPath, iframeTestContent);
+ await fs.copy(currentPath, jsOutputPath);
+ testCount++;
+ }
+};
+
+function replacePlaceholders(
+ content,
+ { jsRelativePath, includes, iframeTestPath }
+) {
+ content = content.replace(
+ "{{ TEST_URL }}",
+ "/ecmascript/tests/" + jsRelativePath
+ );
+ content = content.replace("{{ IFRAME_TEST_URL }}", iframeTestPath);
+ content = content.replace(
+ "{{ TEST_TITLE }}",
+ jsRelativePath.split("/").pop()
+ );
+ content = content.replace(
+ "{{ INCLUDES }}",
+ includes
+ .map(function(src) {
+ return "";
+ })
+ .join("\n")
+ );
+ return content;
+}
+
+(async () => {
+ const ADAPTER_SCRIPT_NAME = "webplatform-adapter.js";
+ const HTML_TEMPLATE_NAME = path.join(__dirname, "test-template.html");
+ const IFRAME_HTML_TEMPLATE_NAME = path.join(
+ __dirname,
+ "test-template.iframe.html"
+ );
+ const DEFAULT_TEST_DIR = "./test262";
+ const DEFAULT_OUTPUT_DIR = ".";
+ const SUB_DIR_NAME = "ecmascript";
+
+ const testDir = process.argv[2] || DEFAULT_TEST_DIR;
+ const testsPath = path.join(testDir, "test");
+ const harnessDir = path.join(testDir, "harness");
+ let outputPath = process.argv[3] || DEFAULT_OUTPUT_DIR;
+ outputPath = path.join(outputPath, SUB_DIR_NAME);
+ const testsOutputPath = path.join(outputPath, "tests");
+ const harnessOutputDir = path.join(outputPath, "harness");
+ const adapterSourcePath = path.join(__dirname, ADAPTER_SCRIPT_NAME);
+ const adapterDestinationPath = path.join(outputPath, ADAPTER_SCRIPT_NAME);
+
+ if (!(await readStats(outputPath))) await makeDirectory(outputPath);
+
+ console.log("Reading test templates ...");
+ const templateContent = await readFile(HTML_TEMPLATE_NAME);
+ const iframeTemplateContent = await readFile(IFRAME_HTML_TEMPLATE_NAME);
+ console.log("Generating tests ...");
+ await generateTest({
+ testsPath,
+ outputPath: testsOutputPath,
+ templateContent,
+ iframeTemplateContent
+ });
+ await fs.copy(adapterSourcePath, adapterDestinationPath);
+ await fs.copy(harnessDir, harnessOutputDir);
+ console.log(
+ `Generated ${testCount} tests in directory ${outputPath} (${path.resolve(
+ outputPath
+ )})`
+ );
+})();
diff --git a/tools/wave/ecmascript/test-template.html b/tools/wave/ecmascript/test-template.html
new file mode 100644
index 000000000000000..24a7a09e7d84588
--- /dev/null
+++ b/tools/wave/ecmascript/test-template.html
@@ -0,0 +1,13 @@
+
+
{{ TEST_TITLE }}
+
+
+
+{{ INCLUDES }}
+
+
+
+
+
diff --git a/tools/wave/ecmascript/test-template.iframe.html b/tools/wave/ecmascript/test-template.iframe.html
new file mode 100644
index 000000000000000..9a22eabc7243136
--- /dev/null
+++ b/tools/wave/ecmascript/test-template.iframe.html
@@ -0,0 +1,16 @@
+
+{{ TEST_TITLE }}
+
+
+
+
+
+
+
+{{ INCLUDES }}
+
+
+
+
diff --git a/tools/wave/ecmascript/webplatform-adapter.js b/tools/wave/ecmascript/webplatform-adapter.js
new file mode 100644
index 000000000000000..21668861eac797b
--- /dev/null
+++ b/tools/wave/ecmascript/webplatform-adapter.js
@@ -0,0 +1,109 @@
+setup(function() { }, {
+ allow_uncaught_exception: true
+});
+
+var evaluated = false;
+
+function $TEST_COMPLETED() {
+ evaluate();
+}
+
+function $ERROR(error) {
+ evaluate(error);
+}
+
+function evaluate(error) {
+ if (evaluated) {
+ return;
+ }
+ evaluated = true;
+ getSource(function(source) {
+ var meta = parseMetadata(source);
+ console.log(meta);
+ test(function() {
+ var negative = null;
+ if (meta.hasOwnProperty("negative")) {
+ negative = {};
+ if (meta["negative"] !== "") {
+ negative.regex = new RegExp(meta["negative"]);
+ }
+ }
+
+ if (negative) {
+ if (negative.regex) {
+ assert_regexp_match(error, negative.regex, meta.description);
+ } else {
+ if (error) {
+ assert_true(true, meta.description);
+ } else {
+ throw new Error("Expected an error to be thrown.");
+ }
+ }
+ } else {
+ if (error) {
+ throw error;
+ } else {
+ assert_true(true, meta.description);
+ }
+ }
+ done();
+ }, meta.description);
+ });
+}
+
+function getSource(loadedCallback) {
+ var path = testUrl;
+ var xhr = new XMLHttpRequest();
+ xhr.addEventListener("load", function(content) {
+ loadedCallback(content.srcElement.response);
+ });
+ xhr.open("GET", path);
+ xhr.send();
+}
+
+function parseMetadata(src) {
+ var meta = {};
+ var inMeta = false;
+ var lines = src.split("\n");
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i];
+ if (inMeta) {
+ if (/\*\//.test(line)) {
+ break;
+ }
+ if (/@.+/.test(line)) {
+ var key = "";
+ var value = "";
+ var parts = line.split(" ");
+ for (var j = 0; j < parts.length; j++) {
+ var part = parts[j];
+ if (key === "") {
+ if (/^@/.test(part)) key = part.replace("@", "");
+ } else {
+ value += part + " ";
+ }
+ }
+ value = value.trim();
+ meta[key] = value;
+ }
+ } else {
+ inMeta = /\/\*\*/.test(line);
+ }
+ }
+ return meta;
+}
+
+var errorEventListener = function(error) {
+ evaluate(error.message);
+ window.removeEventListener("error", errorEventListener);
+ document.getElementById("iframe").contentWindow.removeEventListener("error", errorEventListener);
+};
+
+window.addEventListener("error", errorEventListener);
+document.getElementById("iframe").contentWindow.addEventListener("error", errorEventListener);
+document.getElementById("iframe").contentWindow.$ERROR = $ERROR;
+
+// /ecmascript/tests/built-ins/RegExp/prototype/Symbol.match/builtin-coerce-global.html "Aw Snap"
+// /ecmascript/tests/built-ins/RegExp/prototype/Symbol.match/coerce-global.html "Aw Snap"
+// /ecmascript/tests/built-ins/RegExp/prototype/Symbol.replace/coerce-global.html "Aw Snap"
+// /ecmascript/tests/language/statements/for-of/iterator-next-reference.html "Website unresponsive"
diff --git a/tools/wave/network/api/api_handler.py b/tools/wave/network/api/api_handler.py
index 9c67e6c0cda1e0c..5f1fedd07bb86ae 100644
--- a/tools/wave/network/api/api_handler.py
+++ b/tools/wave/network/api/api_handler.py
@@ -1,17 +1,20 @@
-# mypy: allow-untyped-defs
-
+from __future__ import absolute_import
+from __future__ import unicode_literals
import json
import sys
import traceback
import logging
-from urllib.parse import parse_qsl
+try:
+ from urllib.parse import parse_qsl
+except ImportError:
+ from urlparse import parse_qsl
global logger
logger = logging.getLogger("wave-api-handler")
-class ApiHandler:
+class ApiHandler(object):
def __init__(self, web_root):
self._web_root = web_root
@@ -53,7 +56,7 @@ def parse_query_parameters(self, request):
def handle_exception(self, message):
info = sys.exc_info()
traceback.print_tb(info[2])
- logger.error(f"{message}: {info[0].__name__}: {info[1].args[0]}")
+ logger.error("{}: {}: {}".format(message, info[0].__name__, info[1].args[0]))
def create_hal_list(self, items, uris, index, count, total):
hal_list = {}
@@ -68,21 +71,21 @@ def create_hal_list(self, items, uris, index, count, total):
if "self" in uris:
self_uri = uris["self"]
- self_uri += f"?index={index}&count={count}"
+ self_uri += "?index={}&count={}".format(index, count)
links["self"] = {"href": self_uri}
first_uri = uris["self"]
- first_uri += f"?index={0}&count={count}"
+ first_uri += "?index={}&count={}".format(0, count)
links["first"] = {"href": first_uri}
last_uri = uris["self"]
- last_uri += f"?index={total - (total % count)}&count={count}"
+ last_uri += "?index={}&count={}".format(total - (total % count), count)
links["last"] = {"href": last_uri}
if index + count <= total:
next_index = index + count
next_uri = uris["self"]
- next_uri += f"?index={next_index}&count={count}"
+ next_uri += "?index={}&count={}".format(next_index, count)
links["next"] = {"href": next_uri}
if index != 0:
@@ -90,7 +93,7 @@ def create_hal_list(self, items, uris, index, count, total):
if previous_index < 0:
previous_index = 0
previous_uri = uris["self"]
- previous_uri += f"?index={previous_index}&count={count}"
+ previous_uri += "?index={}&count={}".format(previous_index, count)
links["previous"] = {"href": previous_uri}
hal_list["_links"] = links
diff --git a/tools/wave/network/api/devices_api_handler.py b/tools/wave/network/api/devices_api_handler.py
index ecd9a9677088e7d..5e8b02bca1f37a7 100644
--- a/tools/wave/network/api/devices_api_handler.py
+++ b/tools/wave/network/api/devices_api_handler.py
@@ -1,5 +1,3 @@
-# mypy: allow-untyped-defs
-
import json
import threading
@@ -13,7 +11,7 @@
class DevicesApiHandler(ApiHandler):
def __init__(self, devices_manager, event_dispatcher, web_root):
- super().__init__(web_root)
+ super(DevicesApiHandler, self).__init__(web_root)
self._devices_manager = devices_manager
self._event_dispatcher = event_dispatcher
@@ -65,7 +63,7 @@ def register_event_listener(self, request, response):
token = uri_parts[2]
query = self.parse_query_parameters(request)
- if "device_token" in query:
+ if u"device_token" in query:
self._devices_manager.refresh_device(query["device_token"])
event = threading.Event()
@@ -91,7 +89,7 @@ def register_global_event_listener(self, request, response):
try:
query = self.parse_query_parameters(request)
- if "device_token" in query:
+ if u"device_token" in query:
self._devices_manager.refresh_device(query["device_token"])
event = threading.Event()
@@ -110,7 +108,7 @@ def register_global_event_listener(self, request, response):
self.send_json(data=message, response=response)
self._event_dispatcher.remove_event_listener(event_listener_token)
except Exception:
- self.handle_exception("Failed to register global event listener")
+ self.handle_exception(u"Failed to register global event listener")
response.status = 500
def post_global_event(self, request, response):
@@ -121,7 +119,7 @@ def post_global_event(self, request, response):
event = json.loads(body)
query = self.parse_query_parameters(request)
- if "device_token" in query:
+ if u"device_token" in query:
self._devices_manager.refresh_device(query["device_token"])
event_type = None
@@ -147,7 +145,7 @@ def post_event(self, request, response):
event = json.loads(body)
query = self.parse_query_parameters(request)
- if "device_token" in query:
+ if u"device_token" in query:
self._devices_manager.refresh_device(query["device_token"])
event_type = None
@@ -168,18 +166,18 @@ def handle_request(self, request, response):
# /api/devices
if len(uri_parts) == 2:
- if method == "POST":
+ if method == u"POST":
self.create_device(request, response)
return
- if method == "GET":
+ if method == u"GET":
self.read_devices(request, response)
return
# /api/devices/
if len(uri_parts) == 3:
function = uri_parts[2]
- if method == "GET":
- if function == "events":
+ if method == u"GET":
+ if function == u"events":
self.register_global_event_listener(request, response)
return
self.read_device(request, response)
@@ -192,8 +190,8 @@ def handle_request(self, request, response):
# /api/devices//
if len(uri_parts) == 4:
function = uri_parts[3]
- if method == "GET":
- if function == "events":
+ if method == u"GET":
+ if function == u"events":
self.register_event_listener(request, response)
return
if method == "POST":
diff --git a/tools/wave/network/api/general_api_handler.py b/tools/wave/network/api/general_api_handler.py
index 65883a9b75abfd7..bd588d13e85e5a7 100644
--- a/tools/wave/network/api/general_api_handler.py
+++ b/tools/wave/network/api/general_api_handler.py
@@ -1,4 +1,5 @@
-# mypy: allow-untyped-defs
+from __future__ import absolute_import
+from __future__ import unicode_literals
from .api_handler import ApiHandler
@@ -16,7 +17,7 @@ def __init__(
test_type_selection_enabled,
test_file_selection_enabled
):
- super().__init__(web_root)
+ super(GeneralApiHandler, self).__init__(web_root)
self.read_sessions_enabled = read_sessions_enabled
self.import_results_enabled = import_results_enabled
self.reports_enabled = reports_enabled
diff --git a/tools/wave/network/api/results_api_handler.py b/tools/wave/network/api/results_api_handler.py
index a9da0df10f3f7a5..276ee023892a08d 100644
--- a/tools/wave/network/api/results_api_handler.py
+++ b/tools/wave/network/api/results_api_handler.py
@@ -1,5 +1,5 @@
-# mypy: allow-untyped-defs
-
+from __future__ import absolute_import
+from __future__ import unicode_literals
import json
from .api_handler import ApiHandler
@@ -9,7 +9,7 @@
class ResultsApiHandler(ApiHandler):
def __init__(self, results_manager, session_manager, web_root):
- super().__init__(web_root)
+ super(ResultsApiHandler, self).__init__(web_root)
self._results_manager = results_manager
self._sessions_manager = session_manager
@@ -75,7 +75,7 @@ def read_results_api_wpt_report_url(self, request, response):
def read_results_api_wpt_multi_report_uri(self, request, response):
try:
uri_parts = self.parse_uri(request)
- api = uri_parts[2]
+ api = uri_parts[3]
query = self.parse_query_parameters(request)
tokens = query["tokens"].split(",")
uri = self._results_manager.read_results_wpt_multi_report_uri(
diff --git a/tools/wave/network/api/sessions_api_handler.py b/tools/wave/network/api/sessions_api_handler.py
index 9eb896b80702e99..61b291998ce3ff7 100644
--- a/tools/wave/network/api/sessions_api_handler.py
+++ b/tools/wave/network/api/sessions_api_handler.py
@@ -1,5 +1,5 @@
-# mypy: allow-untyped-defs
-
+from __future__ import absolute_import
+from __future__ import unicode_literals
import json
import threading
@@ -20,9 +20,9 @@ def __init__(
results_manager,
event_dispatcher,
web_root,
- read_sessions_enabled
+ read_sessions_enabled,
):
- super().__init__(web_root)
+ super(SessionsApiHandler, self).__init__(web_root)
self._sessions_manager = sessions_manager
self._results_manager = results_manager
self._event_dispatcher = event_dispatcher
@@ -53,9 +53,6 @@ def create_session(self, body, headers):
expiration_date = None
if "expiration_date" in config:
expiration_date = config["expiration_date"]
- type = None
- if "type" in config:
- type = config["type"]
session = self._sessions_manager.create_session(
tests,
@@ -65,20 +62,16 @@ def create_session(self, body, headers):
user_agent,
labels,
expiration_date,
- type
)
- return {
- "format": "application/json",
- "data": {"token": session.token}
- }
+ return {"format": "application/json", "data": {"token": session.token}}
except InvalidDataException:
self.handle_exception("Failed to create session")
return {
"format": "application/json",
"data": {"error": "Invalid input data!"},
- "status": 400
+ "status": 400,
}
except Exception:
@@ -106,8 +99,8 @@ def read_session(self, token):
"browser": data["browser"],
"is_public": data["is_public"],
"date_created": data["date_created"],
- "labels": data["labels"]
- }
+ "labels": data["labels"],
+ },
}
except Exception:
self.handle_exception("Failed to read session")
@@ -125,7 +118,9 @@ def read_sessions(self, query_parameters, uri_path):
if "expand" in query_parameters:
expand = query_parameters["expand"].split(",")
- session_tokens = self._sessions_manager.read_sessions(index=index, count=count)
+ session_tokens = self._sessions_manager.read_sessions(
+ index=index, count=count
+ )
total_sessions = self._sessions_manager.get_total_sessions()
embedded = {}
@@ -152,18 +147,17 @@ def read_sessions(self, query_parameters, uri_path):
uris = {
"self": uri_path,
"configuration": self._web_root + "api/sessions/{token}",
- "status": self._web_root + "api/sessions/{token}/status"
+ "status": self._web_root + "api/sessions/{token}/status",
}
- data = self.create_hal_list(session_tokens, uris, index, count, total=total_sessions)
+ data = self.create_hal_list(
+ session_tokens, uris, index, count, total=total_sessions
+ )
if len(embedded) > 0:
data["_embedded"] = embedded
- return {
- "format": "application/json",
- "data": data
- }
+ return {"format": "application/json", "data": data}
except Exception:
self.handle_exception("Failed to read session")
return {"status": 500}
@@ -183,8 +177,8 @@ def read_session_status(self, token):
"status": data["status"],
"date_started": data["date_started"],
"date_finished": data["date_finished"],
- "expiration_date": data["expiration_date"]
- }
+ "expiration_date": data["expiration_date"],
+ },
}
except Exception:
self.handle_exception("Failed to read session status")
@@ -221,17 +215,9 @@ def update_session_configuration(self, request, response):
reference_tokens = []
if "reference_tokens" in config:
reference_tokens = config["reference_tokens"]
- type = None
- if "type" in config:
- type = config["type"]
self._sessions_manager.update_session_configuration(
- token,
- tests,
- test_types,
- timeouts,
- reference_tokens,
- type
+ token, tests, test_types, timeouts, reference_tokens
)
except NotFoundException:
self.handle_exception("Failed to update session configuration")
@@ -338,12 +324,14 @@ def register_event_listener(self, request, response):
query_parameters = self.parse_query_parameters(request)
last_event_number = None
- if ("last_event" in query_parameters):
+ if "last_event" in query_parameters:
last_event_number = int(query_parameters["last_event"])
event = threading.Event()
http_polling_event_listener = HttpPollingEventListener(token, event)
- event_listener_token = self._event_dispatcher.add_event_listener(http_polling_event_listener, last_event_number)
+ event_listener_token = self._event_dispatcher.add_event_listener(
+ http_polling_event_listener, last_event_number
+ )
event.wait()
@@ -364,9 +352,8 @@ def push_event(self, request, response):
message = json.loads(body)
self._event_dispatcher.dispatch_event(
- token,
- message["type"],
- message["data"])
+ token, message["type"], message["data"]
+ )
except Exception:
self.handle_exception("Failed to push session event")
diff --git a/tools/wave/network/api/tests_api_handler.py b/tools/wave/network/api/tests_api_handler.py
index 38035837711ab65..c6126d92a79d51e 100644
--- a/tools/wave/network/api/tests_api_handler.py
+++ b/tools/wave/network/api/tests_api_handler.py
@@ -1,8 +1,11 @@
-# mypy: allow-untyped-defs
-
+from __future__ import absolute_import
+from __future__ import unicode_literals
import json
-from urllib.parse import urlunsplit
+try:
+ from urllib.parse import urlunsplit
+except ImportError:
+ from urlparse import urlunsplit
from .api_handler import ApiHandler
from ...utils.serializer import serialize_session
@@ -27,7 +30,7 @@ def __init__(
web_root,
test_loader
):
- super().__init__(web_root)
+ super(TestsApiHandler, self).__init__(web_root)
self._tests_manager = tests_manager
self._sessions_manager = sessions_manager
self._wpt_port = wpt_port
@@ -111,7 +114,8 @@ def read_next_test(self, request, response):
test_timeout = self._tests_manager.get_test_timeout(
test=test, session=session)
- test = self._sessions_manager.get_test_path_with_query(test, session)
+ test = self._sessions_manager.get_test_path_with_query(
+ test, session)
url = self._generate_test_url(
test=test,
token=token,
@@ -295,4 +299,4 @@ def _generate_url(self,
query = ""
if protocol is None:
protocol = "http"
- return urlunsplit([protocol, f"{hostname}:{port}", uri, query, ''])
+ return urlunsplit([protocol, "{}:{}".format(hostname, port), uri, query, ''])
diff --git a/tools/wave/network/http_handler.py b/tools/wave/network/http_handler.py
index b76f711cf1d7c65..5951e1e409dbd6e 100644
--- a/tools/wave/network/http_handler.py
+++ b/tools/wave/network/http_handler.py
@@ -1,6 +1,8 @@
-# mypy: allow-untyped-defs
-
-import http.client as httplib
+from __future__ import unicode_literals
+try:
+ import http.client as httplib
+except ImportError:
+ import httplib
import sys
import logging
import traceback
@@ -9,17 +11,17 @@
global logger
logger = logging.getLogger("wave-api-handler")
-class HttpHandler:
+class HttpHandler(object):
def __init__(
self,
- static_handler,
- sessions_api_handler,
- tests_api_handler,
- results_api_handler,
- devices_api_handler,
- general_api_handler,
- http_port,
- web_root
+ static_handler=None,
+ sessions_api_handler=None,
+ tests_api_handler=None,
+ results_api_handler=None,
+ devices_api_handler=None,
+ general_api_handler=None,
+ http_port=None,
+ web_root=None
):
self.static_handler = static_handler
self.sessions_api_handler = sessions_api_handler
@@ -89,10 +91,9 @@ def _remove_web_root(self, path):
path = path[len(self._web_root):]
return path
-
def _proxy(self, request, response):
host = 'localhost'
- port = int(self._http_port)
+ port = str(self._http_port)
uri = request.url_parts.path
uri = uri + "?" + request.url_parts.query
content_length = request.headers.get('Content-Length')
@@ -100,11 +101,10 @@ def _proxy(self, request, response):
if content_length is not None:
data = request.raw_input.read(int(content_length))
method = request.method
-
headers = {}
- for key in request.headers:
- value = request.headers[key]
- headers[key.decode("utf-8")] = value.decode("utf-8")
+
+ for header in request.headers:
+ headers[header] = request.headers[header]
try:
proxy_connection = httplib.HTTPConnection(host, port)
@@ -114,9 +114,9 @@ def _proxy(self, request, response):
response.headers = proxy_response.getheaders()
response.status = proxy_response.status
- except OSError:
+ except IOError:
message = "Failed to perform proxy request"
info = sys.exc_info()
traceback.print_tb(info[2])
- logger.error(f"{message}: {info[0].__name__}: {info[1].args[0]}")
+ logger.error("{}: {}: {}".format(message, info[0].__name__, info[1].args[0]))
response.status = 500
diff --git a/tools/wave/network/static_handler.py b/tools/wave/network/static_handler.py
index 230af8da1a41cb3..f5b7d7a5f6fa652 100644
--- a/tools/wave/network/static_handler.py
+++ b/tools/wave/network/static_handler.py
@@ -1,9 +1,11 @@
-# mypy: allow-untyped-defs
-
+from __future__ import with_statement
+from __future__ import absolute_import
+from __future__ import unicode_literals
import os
+from io import open
-class StaticHandler:
+class StaticHandler(object):
def __init__(self, web_root, http_port, https_port):
self.static_dir = os.path.join(
os.getcwd(), "tools/wave/www")
diff --git a/tools/wave/requirements.txt b/tools/wave/requirements.txt
index 3bb476fd968b2d7..ea9f2810db419e5 100644
--- a/tools/wave/requirements.txt
+++ b/tools/wave/requirements.txt
@@ -1,2 +1,2 @@
-ua-parser==0.18.0
-python-dateutil==2.9.0.post0
+ua-parser==0.8.0
+python-dateutil==2.8.1
diff --git a/tools/wave/testing/devices_manager.py b/tools/wave/testing/devices_manager.py
index 935782d1372b678..c03572e25903977 100644
--- a/tools/wave/testing/devices_manager.py
+++ b/tools/wave/testing/devices_manager.py
@@ -1,5 +1,3 @@
-# mypy: allow-untyped-defs
-
import time
import uuid
@@ -15,7 +13,7 @@
DEVICE_TIMEOUT = 60000 # 60sec
RECONNECT_TIME = 5000 # 5sec
-class DevicesManager:
+class DevicesManager(object):
def initialize(self, event_dispatcher):
self.devices = {}
self._event_dispatcher = event_dispatcher
@@ -41,7 +39,7 @@ def create_device(self, user_agent):
def read_device(self, token):
if token not in self.devices:
- raise NotFoundException(f"Could not find device '{token}'")
+ raise NotFoundException("Could not find device '{}'".format(token))
return self.devices[token]
def read_devices(self):
diff --git a/tools/wave/testing/event_dispatcher.py b/tools/wave/testing/event_dispatcher.py
index 9bfb6ed712a034f..0ded9c6e553ffc3 100644
--- a/tools/wave/testing/event_dispatcher.py
+++ b/tools/wave/testing/event_dispatcher.py
@@ -1,5 +1,4 @@
-# mypy: allow-untyped-defs
-
+from __future__ import unicode_literals
import uuid
import time
from threading import Timer
@@ -13,7 +12,7 @@
DEVICE_ADDED_EVENT = "device_added"
DEVICE_REMOVED_EVENT = "device_removed"
-class EventDispatcher:
+class EventDispatcher(object):
def __init__(self, event_cache_duration):
self._listeners = {}
self._events = {}
diff --git a/tools/wave/testing/results_manager.py b/tools/wave/testing/results_manager.py
index 13dfcbad7d8515a..fa1c0e92faa81bf 100644
--- a/tools/wave/testing/results_manager.py
+++ b/tools/wave/testing/results_manager.py
@@ -1,5 +1,5 @@
-# mypy: allow-untyped-defs
-
+from __future__ import absolute_import
+from __future__ import unicode_literals
import os
import shutil
import re
@@ -7,7 +7,6 @@
import hashlib
import zipfile
import time
-from threading import Timer
from ..utils.user_agent_parser import parse_user_agent, abbreviate_browser_name
from ..utils.serializer import serialize_session
@@ -20,12 +19,11 @@
from ..data.session import COMPLETED
WAVE_SRC_DIR = "./tools/wave"
-RESULTS_FILE_REGEX = r"^\w\w\d\d\d?\.json$"
+RESULTS_FILE_REGEX = "^\w\w\d\d\d?\.json$"
RESULTS_FILE_PATTERN = re.compile(RESULTS_FILE_REGEX)
-SESSION_RESULTS_TIMEOUT = 60*30 # 30min
-class ResultsManager:
+class ResultsManager(object):
def initialize(
self,
results_directory_path,
@@ -42,7 +40,6 @@ def initialize(
self._reports_enabled = reports_enabled
self._results = {}
self._persisting_interval = persisting_interval
- self._timeouts = {}
def create_result(self, token, data):
result = self.prepare_result(data)
@@ -89,10 +86,10 @@ def read_results(self, token, filter_path=None):
if filter_path is not None:
filter_api = next((p for p in filter_path.split("/")
if p is not None), None)
- results = self._read_from_cache(token)
- if results == []:
- results = self.load_results(token)
- self._set_session_cache(token, results)
+ cached_results = self._read_from_cache(token)
+ persisted_results = self.load_results(token)
+ results = self._combine_results_by_api(cached_results,
+ persisted_results)
filtered_results = {}
@@ -216,13 +213,12 @@ def read_common_passed_tests(self, tokens=None):
if test in failed_tests[api]:
continue
failed_tests[api].append(test)
- return passed_tests
def read_results_wpt_report_uri(self, token, api):
api_directory = os.path.join(self._results_directory_path, token, api)
if not os.path.isdir(api_directory):
return None
- return f"/results/{token}/{api}/all.html"
+ return "/results/{}/{}/all.html".format(token, api)
def read_results_wpt_multi_report_uri(self, tokens, api):
comparison_directory_name = self.get_comparison_identifier(tokens)
@@ -238,7 +234,7 @@ def read_results_wpt_multi_report_uri(self, tokens, api):
if not os.path.isdir(api_directory_path):
self.generate_multi_report(tokens, api)
- return f"/results/{relative_api_directory_path}/all.html"
+ return "/results/{}/all.html".format(relative_api_directory_path)
def delete_results(self, token):
results_directory = os.path.join(self._results_directory_path, token)
@@ -252,7 +248,8 @@ def persist_session(self, session):
return
for api in list(self._results[token].keys())[:]:
self.save_api_results(token, api)
- self.create_info_file(session)
+ self.create_info_file(session)
+ self._clear_cache_api(token, api)
session.recent_completed_count = 0
self._sessions_manager.update_session(session)
@@ -272,7 +269,7 @@ def load_results(self, token):
continue
file_path = os.path.join(api_directory, file_name)
data = None
- with open(file_path) as file:
+ with open(file_path, "r") as file:
data = file.read()
result = json.loads(data)
results[api] = result["results"]
@@ -289,28 +286,22 @@ def _push_to_cache(self, token, result):
if api not in self._results[token]:
self._results[token][api] = []
self._results[token][api].append(result)
- self._set_timeout(token)
-
- def _set_session_cache(self, token, results):
- if token is None:
- return
- self._results[token] = results
- self._set_timeout(token)
def _read_from_cache(self, token):
if token is None:
return []
if token not in self._results:
return []
- self._set_timeout(token)
return self._results[token]
- def _clear_session_cache(self, token):
+ def _clear_cache_api(self, token, api):
if token is None:
return
if token not in self._results:
return
- del self._results[token]
+ if api not in self._results[token]:
+ return
+ del self._results[token][api]
def _combine_results_by_api(self, result_a, result_b):
combined_result = {}
@@ -488,7 +479,7 @@ def export_results_api_json(self, token, api):
if not os.path.isfile(file_path):
return None
- with open(file_path) as file:
+ with open(file_path, "r") as file:
blob = file.read()
return blob
@@ -547,7 +538,7 @@ def export_results(self, token):
zip.write(file_path, file_name, zipfile.ZIP_DEFLATED)
zip.close()
- with open(zip_file_name) as file:
+ with open(zip_file_name, "rb") as file:
blob = file.read()
os.remove(zip_file_name)
@@ -556,7 +547,7 @@ def export_results(self, token):
def export_results_overview(self, token):
session = self._sessions_manager.read_session(token)
if session is None:
- raise NotFoundException(f"Could not find session {token}")
+ raise NotFoundException("Could not find session {}".format(token))
tmp_file_name = str(time.time()) + ".zip"
zip = zipfile.ZipFile(tmp_file_name, "w")
@@ -598,7 +589,7 @@ def load_session_from_info_file(self, info_file_path):
if not os.path.isfile(info_file_path):
return None
- with open(info_file_path) as info_file:
+ with open(info_file_path, "r") as info_file:
data = info_file.read()
info_file.close()
info = json.loads(str(data))
@@ -607,7 +598,7 @@ def load_session_from_info_file(self, info_file_path):
def import_results(self, blob):
if not self.is_import_results_enabled:
raise PermissionDeniedException()
- tmp_file_name = f"{str(time.time())}.zip"
+ tmp_file_name = "{}.zip".format(str(time.time()))
with open(tmp_file_name, "w") as file:
file.write(blob)
@@ -627,7 +618,7 @@ def import_results(self, blob):
os.makedirs(destination_path)
zip.extractall(destination_path)
self.remove_tmp_files()
- self.load_results(token)
+ self.load_results()
return token
def import_results_api_json(self, token, api, blob):
@@ -663,12 +654,3 @@ def remove_tmp_files(self):
if re.match(r"\d{10}\.\d{2}\.zip", file) is None:
continue
os.remove(file)
-
- def _set_timeout(self, token):
- if token in self._timeouts:
- self._timeouts[token].cancel()
-
- def handler(self, token):
- self._clear_session_cache(token)
-
- self._timeouts[token] = Timer(SESSION_RESULTS_TIMEOUT, handler, [self, token])
diff --git a/tools/wave/testing/sessions_manager.py b/tools/wave/testing/sessions_manager.py
index 093c3cffe8507a8..5e70848823bd66a 100644
--- a/tools/wave/testing/sessions_manager.py
+++ b/tools/wave/testing/sessions_manager.py
@@ -1,5 +1,6 @@
-# mypy: allow-untyped-defs
-
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
import uuid
import time
import os
@@ -22,14 +23,16 @@
DEFAULT_TEST_MANUAL_TIMEOUT = 300000
-class SessionsManager:
- def initialize(self,
- test_loader,
- event_dispatcher,
- tests_manager,
- results_directory,
- results_manager,
- configuration):
+class SessionsManager(object):
+ def initialize(
+ self,
+ test_loader,
+ event_dispatcher,
+ tests_manager,
+ results_directory,
+ results_manager,
+ configuration,
+ ):
self._test_loader = test_loader
self._sessions = {}
self._expiration_timeout = None
@@ -48,7 +51,6 @@ def create_session(
user_agent=None,
labels=None,
expiration_date=None,
- type=None
):
if tests is None:
tests = {}
@@ -74,20 +76,26 @@ def create_session(
for test_type in test_types:
if test_type != "automatic" and test_type != "manual":
- raise InvalidDataException(f"Unknown type '{test_type}'")
+ raise InvalidDataException("Unknown type '{}'".format(test_type))
+
+ if expiration_date is not None and type(expiration_date) != int:
+ expiration_date = iso_to_millis(expiration_date)
+ if type(expiration_date) != int:
+ raise InvalidDataException(
+ "Expected ISO string for expiration date: {}", expiration_date
+ )
token = str(uuid.uuid1())
pending_tests = self._test_loader.get_tests(
test_types,
include_list=tests["include"],
exclude_list=tests["exclude"],
- reference_tokens=reference_tokens)
+ reference_tokens=reference_tokens,
+ )
browser = parse_user_agent(user_agent)
- test_files_count = self._tests_manager.calculate_test_files_count(
- pending_tests
- )
+ test_files_count = self._tests_manager.calculate_test_files_count(pending_tests)
test_state = {}
for api in test_files_count:
@@ -97,7 +105,8 @@ def create_session(
"timeout": 0,
"not_run": 0,
"total": test_files_count[api],
- "complete": 0}
+ "complete": 0,
+ }
date_created = int(time.time() * 1000)
@@ -114,9 +123,8 @@ def create_session(
status=PENDING,
reference_tokens=reference_tokens,
labels=labels,
- type=type,
expiration_date=expiration_date,
- date_created=date_created
+ date_created=date_created,
)
self._push_to_cache(session)
@@ -130,7 +138,6 @@ def read_session(self, token):
return None
session = self._read_from_cache(token)
if session is None or session.test_state is None:
- print("loading session from file system")
session = self.load_session(token)
if session is not None:
self._push_to_cache(session)
@@ -180,7 +187,7 @@ def update_session(self, session):
self._push_to_cache(session)
def update_session_configuration(
- self, token, tests, test_types, timeouts, reference_tokens, type
+ self, token, tests, test_types, timeouts, reference_tokens
):
session = self.read_session(token)
if session is None:
@@ -202,12 +209,13 @@ def update_session_configuration(
include_list=tests["include"],
exclude_list=tests["exclude"],
reference_tokens=reference_tokens,
- test_types=test_types
+ test_types=test_types,
)
session.pending_tests = pending_tests
session.tests = tests
test_files_count = self._tests_manager.calculate_test_files_count(
- pending_tests)
+ pending_tests
+ )
test_state = {}
for api in test_files_count:
test_state[api] = {
@@ -230,8 +238,6 @@ def update_session_configuration(
session.timeouts = timeouts
if reference_tokens is not None:
session.reference_tokens = reference_tokens
- if type is not None:
- session.type = type
self._push_to_cache(session)
return session
@@ -299,7 +305,7 @@ def load_session_info(self, token):
return None
info_data = None
- with open(info_file) as file:
+ with open(info_file, "r") as file:
info_data = file.read()
parsed_info_data = json.loads(info_data)
@@ -373,9 +379,7 @@ def start_session(self, token):
self.update_session(session)
self._event_dispatcher.dispatch_event(
- token,
- event_type=STATUS_EVENT,
- data=session.status
+ token, event_type=STATUS_EVENT, data=session.status
)
def pause_session(self, token):
@@ -385,9 +389,7 @@ def pause_session(self, token):
session.status = PAUSED
self.update_session(session)
self._event_dispatcher.dispatch_event(
- token,
- event_type=STATUS_EVENT,
- data=session.status
+ token, event_type=STATUS_EVENT, data=session.status
)
self._results_manager.persist_session(session)
@@ -399,9 +401,7 @@ def stop_session(self, token):
session.date_finished = int(time.time() * 1000)
self.update_session(session)
self._event_dispatcher.dispatch_event(
- token,
- event_type=STATUS_EVENT,
- data=session.status
+ token, event_type=STATUS_EVENT, data=session.status
)
def resume_session(self, token, resume_token):
@@ -409,9 +409,7 @@ def resume_session(self, token, resume_token):
if session.status != PENDING:
return
self._event_dispatcher.dispatch_event(
- token,
- event_type=RESUME_EVENT,
- data=resume_token
+ token, event_type=RESUME_EVENT, data=resume_token
)
self.delete_session(token)
@@ -423,18 +421,18 @@ def complete_session(self, token):
session.date_finished = int(time.time() * 1000)
self.update_session(session)
self._event_dispatcher.dispatch_event(
- token,
- event_type=STATUS_EVENT,
- data=session.status
+ token, event_type=STATUS_EVENT, data=session.status
)
def test_in_session(self, test, session):
- return self._test_list_contains_test(test, session.pending_tests) \
- or self._test_list_contains_test(test, session.running_tests)
+ return self._test_list_contains_test(
+ test, session.pending_tests
+ ) or self._test_list_contains_test(test, session.running_tests)
def is_test_complete(self, test, session):
- return not self._test_list_contains_test(test, session.pending_tests) \
- and not self._test_list_contains_test(test, session.running_tests)
+ return not self._test_list_contains_test(
+ test, session.pending_tests
+ ) and not self._test_list_contains_test(test, session.running_tests)
def is_test_running(self, test, session):
return self._test_list_contains_test(test, session.running_tests)
@@ -446,8 +444,7 @@ def _test_list_contains_test(self, test, test_list):
return False
def is_api_complete(self, api, session):
- return api not in session.pending_tests \
- and api not in session.running_tests
+ return api not in session.pending_tests and api not in session.running_tests
def get_test_path_with_query(self, test, session):
query_string = ""
@@ -461,7 +458,7 @@ def get_test_path_with_query(self, test, session):
pattern = re.compile("^" + include_test)
if pattern.match(test) is not None:
query_string += query + "&"
- return f"{test}?{query_string}"
+ return "{}?{}".format(test, query_string)
def find_token(self, fragment):
if len(fragment) < 8:
diff --git a/tools/wave/testing/test_loader.py b/tools/wave/testing/test_loader.py
index 8d751260d976567..d2664bb5dc587b7 100644
--- a/tools/wave/testing/test_loader.py
+++ b/tools/wave/testing/test_loader.py
@@ -1,5 +1,5 @@
-# mypy: allow-untyped-defs
-
+from __future__ import absolute_import
+from __future__ import unicode_literals
import os
import re
@@ -9,7 +9,7 @@
TEST_TYPES = [AUTOMATIC, MANUAL]
-class TestLoader:
+class TestLoader(object):
def initialize(
self,
exclude_list_file_path,
@@ -48,7 +48,7 @@ def load_tests(self, tests):
self._tests[AUTOMATIC][api].remove(test_path)
if not self._is_valid_test(test_path,
- include_list=include_list):
+ include_regex_list=include_list):
continue
if api not in self._tests[MANUAL]:
@@ -94,14 +94,23 @@ def _parse_api_name(self, test_path):
continue
return part
- def _is_valid_test(self, test_path, exclude_list=None, include_list=None):
+ def _convert_list_to_regex(self, test_list):
+ regex_patterns = []
+
+ if test_list is not None and len(test_list) > 0:
+ is_valid = False
+ for test in test_list:
+ test = test.split("?")[0]
+ pattern = re.compile("^" + test)
+ regex_patterns.append(pattern)
+ return regex_patterns
+
+ def _is_valid_test(self, test_path, exclude_regex_list=None, include_regex_list=None):
is_valid = True
- if include_list is not None and len(include_list) > 0:
+ if include_regex_list is not None and len(include_regex_list) > 0:
is_valid = False
- for include_test in include_list:
- include_test = include_test.split("?")[0]
- pattern = re.compile("^" + include_test)
+ for pattern in include_regex_list:
if pattern.match(test_path) is not None:
is_valid = True
break
@@ -109,11 +118,9 @@ def _is_valid_test(self, test_path, exclude_list=None, include_list=None):
if not is_valid:
return is_valid
- if exclude_list is not None and len(exclude_list) > 0:
+ if exclude_regex_list is not None and len(exclude_regex_list) > 0:
is_valid = True
- for exclude_test in exclude_list:
- exclude_test = exclude_test.split("?")[0]
- pattern = re.compile("^" + exclude_test)
+ for pattern in exclude_regex_list:
if pattern.match(test_path) is not None:
is_valid = False
break
@@ -154,6 +161,9 @@ def get_tests(
if reference_tokens is None:
reference_tokens = []
+ exclude_regex_list = self._convert_list_to_regex(exclude_list)
+ include_regex_list = self._convert_list_to_regex(include_list)
+
loaded_tests = {}
reference_results = self._results_manager.read_common_passed_tests(
@@ -164,16 +174,17 @@ def get_tests(
continue
for api in self._tests[test_type]:
for test_path in self._tests[test_type][api]:
- if not self._is_valid_test(test_path, exclude_list,
- include_list):
+ if not self._is_valid_test(test_path, exclude_regex_list,
+ include_regex_list):
continue
if reference_results is not None and \
(api not in reference_results or
- (api in reference_results and test_path not in reference_results[api])):
+ (api in reference_results and test_path not in reference_results[api])):
continue
if api not in loaded_tests:
loaded_tests[api] = []
loaded_tests[api].append(test_path)
+
return loaded_tests
def get_apis(self):
diff --git a/tools/wave/testing/tests_manager.py b/tools/wave/testing/tests_manager.py
index 8187eb4a7db4f1b..c4dd695c6f3912f 100644
--- a/tools/wave/testing/tests_manager.py
+++ b/tools/wave/testing/tests_manager.py
@@ -1,7 +1,9 @@
-# mypy: allow-untyped-defs
-
+from __future__ import division
+from __future__ import absolute_import
+from __future__ import unicode_literals
import re
from threading import Timer
+import functools
from .event_dispatcher import TEST_COMPLETED_EVENT
@@ -9,13 +11,9 @@
from ..data.session import COMPLETED, ABORTED
-class TestsManager:
+class TestsManager(object):
def initialize(
- self,
- test_loader,
- sessions_manager,
- results_manager,
- event_dispatcher
+ self, test_loader, sessions_manager, results_manager, event_dispatcher
):
self._test_loader = test_loader
self._sessions_manager = sessions_manager
@@ -23,6 +21,7 @@ def initialize(
self._event_dispatcher = event_dispatcher
self._timeouts = []
+ self._logs = {}
def next_test(self, session):
if session.status == COMPLETED or session.status == ABORTED:
@@ -53,10 +52,7 @@ def handler(self, token, test):
self._on_test_timeout(token, test)
timer = Timer(test_timeout, handler, [self, token, test])
- self._timeouts.append({
- "test": test,
- "timeout": timer
- })
+ self._timeouts.append({"test": test, "timeout": timer})
session.pending_tests = pending_tests
session.running_tests = running_tests
@@ -110,8 +106,11 @@ def read_last_completed_tests(self, token, count):
tests["pass"].append(result["test"])
if not passes and len(tests["fail"]) < count:
tests["fail"].append(result["test"])
- if len(tests["pass"]) == count and len(tests["fail"]) == count \
- and len(tests["timeout"]) == count:
+ if (
+ len(tests["pass"]) == count
+ and len(tests["fail"]) == count
+ and len(tests["timeout"]) == count
+ ):
return tests
return tests
@@ -122,34 +121,33 @@ def _sort_tests_by_execution(self, tests):
for test in tests[api]:
sorted_tests.append(test)
- class compare:
- def __init__(self, tests_manager, test):
- self.test = test
- self.tests_manager = tests_manager
-
- def __lt__(self, test_b):
- test_a = self.test
- test_b = test_b.test
- micro_test_list = {}
- api_a = ""
- for part in test_a.split("/"):
- if part != "":
- api_a = part
- break
- api_b = ""
- for part in test_b.split("/"):
- if part != "":
- api_b = part
- break
- if api_a == api_b:
- micro_test_list[api_a] = [test_a, test_b]
- else:
- micro_test_list[api_a] = [test_a]
- micro_test_list[api_b] = [test_b]
- next_test = self.tests_manager._get_next_test_from_list(micro_test_list)
- return next_test == test_b
-
- sorted_tests.sort(key=lambda test: compare(self, test))
+ def compare(tests_manager, test_a, test_b):
+ micro_test_list = {}
+ api_a = ""
+ for part in test_a.split("/"):
+ if part != "":
+ api_a = part
+ break
+ api_b = ""
+ for part in test_b.split("/"):
+ if part != "":
+ api_b = part
+ break
+ if api_a == api_b:
+ micro_test_list[api_a] = [test_a, test_b]
+ else:
+ micro_test_list[api_a] = [test_a]
+ micro_test_list[api_b] = [test_b]
+ next_test = tests_manager._get_next_test_from_list(micro_test_list)
+ if next_test == test_a:
+ return -1
+ return 1
+
+ sorted_tests.sort(
+ key=functools.cmp_to_key(
+ lambda test_a, test_b: compare(self, test_a, test_b)
+ )
+ )
return sorted_tests
def _get_next_test_from_list(self, tests):
@@ -164,7 +162,7 @@ def _get_next_test_from_list(self, tests):
apis.sort(key=lambda api: api.lower())
for api in apis:
- tests[api].sort(key=lambda test: test.replace("/", "").lower())
+ tests[api].sort(key=lambda api: api.replace("/", "").lower())
while test is None:
if len(apis) <= current_api:
@@ -220,14 +218,14 @@ def skip_to(self, test_list, test):
if test not in sorted_tests:
return test_list
index = sorted_tests.index(test)
- remaining_tests = sorted_tests[index + 1:]
+ remaining_tests = sorted_tests[index + 1 :]
remaining_tests_by_api = {}
current_api = "___"
for test in remaining_tests:
- if not test.startswith("/" + current_api) and \
- not test.startswith(current_api):
- current_api = next((p for p in test.split("/") if p != ""),
- None)
+ if not test.startswith("/" + current_api) and not test.startswith(
+ current_api
+ ):
+ current_api = next((p for p in test.split("/") if p != ""), None)
if current_api not in remaining_tests_by_api:
remaining_tests_by_api[current_api] = []
remaining_tests_by_api[current_api].append(test)
@@ -283,16 +281,15 @@ def get_test_timeout(self, test, session):
return test_timeout
def _on_test_timeout(self, token, test):
+ logs = []
+ if token in self._logs:
+ logs = self._logs[token]
data = {
"test": test,
"status": "TIMEOUT",
"message": None,
- "subtests": [
- {
- "status": "TIMEOUT",
- "xstatus": "SERVERTIMEOUT"
- }
- ]
+ "subtests": [{"status": "TIMEOUT", "xstatus": "SERVERTIMEOUT"}],
+ "logs": logs,
}
self._results_manager.create_result(token, data)
@@ -310,23 +307,13 @@ def complete_test(self, test, session):
timeout["timeout"].cancel()
self._timeouts.remove(timeout)
- self.update_tests(
- running_tests=running_tests,
- session=session
- )
+ self.update_tests(running_tests=running_tests, session=session)
self._event_dispatcher.dispatch_event(
- dispatcher_token=session.token,
- event_type=TEST_COMPLETED_EVENT,
- data=test
+ dispatcher_token=session.token, event_type=TEST_COMPLETED_EVENT, data=test
)
- def update_tests(
- self,
- pending_tests=None,
- running_tests=None,
- session=None
- ):
+ def update_tests(self, pending_tests=None, running_tests=None, session=None):
if pending_tests is not None:
session.pending_tests = pending_tests
@@ -364,7 +351,7 @@ def load_tests(self, session):
session.test_types,
include_list=session.tests["include"],
exclude_list=session.tests["exclude"],
- reference_tokens=session.reference_tokens
+ reference_tokens=session.reference_tokens,
)
last_completed_test = session.last_completed_test
@@ -372,3 +359,13 @@ def load_tests(self, session):
pending_tests = self.skip_to(pending_tests, last_completed_test)
return pending_tests
+
+ def add_logs(self, token, logs):
+ if token not in self._logs:
+ self._logs[token] = []
+ self._logs[token] = self._logs[token] + logs
+
+ def get_logs(self, token):
+ if token not in self._logs:
+ return []
+ return self._logs[token]
diff --git a/tools/wave/testing/wpt_report.py b/tools/wave/testing/wpt_report.py
index b84119ed8526665..a7b8351cc1e7e85 100644
--- a/tools/wave/testing/wpt_report.py
+++ b/tools/wave/testing/wpt_report.py
@@ -1,5 +1,4 @@
-# mypy: allow-untyped-defs
-
+from __future__ import unicode_literals
import subprocess
import os
import ntpath
@@ -12,7 +11,10 @@ def generate_report(
output_html_directory_path=None,
spec_name=None,
is_multi=None,
- reference_dir=None):
+ reference_dir=None,
+ tests_base_url=None):
+ if not is_wptreport_installed():
+ return
if is_multi is None:
is_multi = False
try:
@@ -25,14 +27,15 @@ def generate_report(
"--failures", "true",
"--tokenFileName", "true" if is_multi else "false",
"--pass", "100",
- "--ref", reference_dir if reference_dir is not None else ""]
- whole_command = ""
- for command_part in command:
- whole_command += command_part + " "
+ "--ref", reference_dir if reference_dir is not None else "",
+ "--testsBaseUrl", tests_base_url
+ ]
subprocess.call(command, shell=False)
except subprocess.CalledProcessError as e:
info = sys.exc_info()
raise Exception("Failed to execute wptreport: " + str(info[0].__name__) + ": " + e.output)
+ except FileNotFoundError as e:
+ raise Exception("Failed to execute wptreport: " + " ".join(command))
def generate_multi_report(
@@ -40,6 +43,8 @@ def generate_multi_report(
spec_name=None,
result_json_files=None,
reference_dir=None):
+ if not is_wptreport_installed():
+ return
for file in result_json_files:
if not os.path.isfile(file["path"]):
continue
@@ -55,3 +60,10 @@ def generate_multi_report(
spec_name=spec_name,
is_multi=True,
reference_dir=reference_dir)
+
+def is_wptreport_installed():
+ try:
+ subprocess.check_output(["wptreport", "--help"])
+ return True
+ except Exception:
+ return False
\ No newline at end of file
diff --git a/tools/wave/tests/test_api.py b/tools/wave/tests/test_api.py
new file mode 100644
index 000000000000000..1391febd3687258
--- /dev/null
+++ b/tools/wave/tests/test_api.py
@@ -0,0 +1,164 @@
+import pytest
+import requests
+import json
+import copy
+
+def test_import_results():
+ # Arrange
+ token = create_session()
+ print(token)
+ tests = read_available_tests()
+ apis = list(tests.keys())
+ api = apis[0]
+ test = tests[api][0]
+ set_session_tests(token, [test])
+ print(api)
+ print(test)
+ start_session(token)
+ next_test = read_next_test(token)
+ create_positive_result(token, test)
+ next_test = read_next_test(token)
+
+ # Act
+ url = "/".join([get_url(), "api/results", token, api, "json"])
+ r = requests.get(url)
+ old_results = copy.deepcopy(r.json())
+
+ new_results = copy.deepcopy(old_results)
+ new_results["results"][0]["subtests"].append({"name": "Subtest ab", "status": "FAIL", "message": None})
+ url = "/".join([get_url(), "api/results", token, api, "json"])
+ r = requests.post(url, data=json.dumps(new_results))
+ status_code = r.status_code
+
+ url = "/".join([get_url(), "api/results", token, api, "json"])
+ r = requests.get(url)
+ updated_results = r.json()
+
+ # Assert
+ assert status_code == 200
+ assert updated_results == new_results
+ assert updated_results != old_results
+
+ # CleanUp
+ delete_session(token)
+
+def test_last_completed_tests():
+ # Arrange
+ token = create_session()
+ print(token)
+ tests = read_available_tests()
+ apis = list(tests.keys())
+ api = apis[0]
+ test = tests[api][0]
+ set_session_tests(token, [test])
+ print(api)
+ print(test)
+ start_session(token)
+ next_test = read_next_test(token)
+ create_timed_out_result(token, test)
+ next_test = read_next_test(token)
+
+ # Act
+ url = "/".join([get_url(), "api/tests/", token, "/last_completed"])
+ r = requests.get(url)
+ status_code = r.status_code
+
+ url = "/".join([get_url(), "api/results", token, api, "json"])
+ r = requests.get(url)
+ updated_results = r.json()
+
+ # Assert
+ assert status_code == 200
+
+ # CleanUp
+ delete_session(token)
+
+def create_session():
+ url = "/".join([get_url(), "api/sessions"])
+ r = requests.post(url)
+ response_payload = r.json()
+ token = response_payload["token"]
+ return token
+
+def read_session(token):
+ url = "/".join([get_url(), "api/sessions", token])
+ r = requests.get(url)
+ session = r.json()
+ return session
+
+def read_session_status(token):
+ url = "/".join([get_url(), "api/sessions", token, "status"])
+ r = requests.get(url)
+ status = r.json()
+ return status
+
+def read_available_tests():
+ url = "/".join([get_url(), "api/tests"])
+ r = requests.get(url)
+ tests = r.json()
+ return tests
+
+def set_session_tests(token, tests):
+ url = "/".join([get_url(), "api/sessions", token])
+ config = {
+ "tests": {
+ "include": tests
+ }
+ }
+
+ r = requests.put(url, data=json.dumps(config))
+
+def read_next_test(token):
+ url = "/".join([get_url(), "api/tests", token, "next"])
+ r = requests.get(url)
+ next_test = r.json()["next_test"]
+ return next_test
+
+
+def start_session(token):
+ url = "/".join([get_url(), "api/sessions", token, "start"])
+ r = requests.post(url)
+
+def create_result(token, test, result):
+ url = "/".join([get_url(), "api/results", token])
+ r = requests.post(url, data=json.dumps(result))
+ print(r.status_code)
+ print(result)
+
+def create_positive_result(token, test):
+ result = {
+ "test": test,
+ "status": "OK",
+ "message": None,
+ "subtests": [
+ {"name": "Subtest xy", "status": "PASS", "message": None}
+ ]
+ }
+
+ create_result(token, test, result)
+
+def create_timed_out_result(token, test):
+ result = {
+ "test": test,
+ "status": "TIMEOUT",
+ "message": None,
+ "subtests": []
+ }
+
+ create_result(token, test, result)
+
+def read_session_tests(token):
+ url = "/".join([get_url(), "api/tests", token])
+ r = requests.get(url)
+ tests = r.json()
+ return tests
+
+def delete_session(token):
+ url = "/".join([get_url(), "api/sessions", token])
+ r = requests.delete(url)
+
+
+def get_url():
+ host = "127.0.0.1:8000"
+ base_url = "_wave"
+ return "/".join(["http:/", host, base_url])
\ No newline at end of file
diff --git a/tools/wave/tests/test_wave.py b/tools/wave/tests/test_wave.py
index a7d87a38e1b01f9..cfb84df6564c41a 100644
--- a/tools/wave/tests/test_wave.py
+++ b/tools/wave/tests/test_wave.py
@@ -1,13 +1,14 @@
-# mypy: allow-untyped-defs
-
import errno
import os
import socket
import subprocess
import time
-from urllib.request import urlopen
-from urllib.error import URLError
+try:
+ from urllib.request import urlopen
+ from urllib.error import URLError
+except ImportError:
+ from urllib2 import urlopen, URLError
from tools.wpt import wpt
@@ -15,7 +16,7 @@ def is_port_8080_in_use():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.bind(("127.0.0.1", 8080))
- except OSError as e:
+ except socket.error as e:
if e.errno == errno.EADDRINUSE:
return True
else:
@@ -52,4 +53,3 @@ def test_serve():
break
finally:
os.killpg(p.pid, 15)
- p.wait(10)
diff --git a/tools/wave/tox.ini b/tools/wave/tox.ini
index 88c76096f459839..b868686452f88a1 100644
--- a/tools/wave/tox.ini
+++ b/tools/wave/tox.ini
@@ -1,14 +1,13 @@
[tox]
-envlist = py38,py39,py310,py311,py312
+envlist = py36,py37,py38,py39
skipsdist=True
skip_missing_interpreters = False
[testenv]
deps =
-r{toxinidir}/../requirements_pytest.txt
- -r{toxinidir}/requirements.txt
-r{toxinidir}/../wptrunner/requirements.txt
- -r{toxinidir}/../wptrunner/requirements_chromium.txt
+ -r{toxinidir}/../wptrunner/requirements_chrome.txt
-r{toxinidir}/../wptrunner/requirements_firefox.txt
commands =
diff --git a/tools/wave/utils/deserializer.py b/tools/wave/utils/deserializer.py
index 28d1054f6ba77aa..ddf77a99d58da3d 100644
--- a/tools/wave/utils/deserializer.py
+++ b/tools/wave/utils/deserializer.py
@@ -1,5 +1,5 @@
-# mypy: allow-untyped-defs
-
+from __future__ import absolute_import
+from __future__ import unicode_literals
from ..data.session import Session, UNKNOWN
from datetime import datetime
import dateutil.parser
@@ -76,9 +76,6 @@ def deserialize_session(session_dict):
if "expiration_date" in session_dict:
expiration_date = session_dict["expiration_date"]
expiration_date = iso_to_millis(expiration_date)
- type = None
- if "type" in session_dict:
- type = session_dict["type"]
malfunctioning_tests = []
if "malfunctioning_tests" in session_dict:
malfunctioning_tests = session_dict["malfunctioning_tests"]
@@ -102,7 +99,6 @@ def deserialize_session(session_dict):
reference_tokens=reference_tokens,
browser=browser,
expiration_date=expiration_date,
- type=type,
malfunctioning_tests=malfunctioning_tests
)
diff --git a/tools/wave/utils/serializer.py b/tools/wave/utils/serializer.py
index 995365081db0ce2..72fb55d3293ddb0 100644
--- a/tools/wave/utils/serializer.py
+++ b/tools/wave/utils/serializer.py
@@ -1,5 +1,4 @@
-# mypy: allow-untyped-defs
-
+from __future__ import unicode_literals
from datetime import datetime
@@ -23,7 +22,6 @@ def serialize_session(session):
"is_public": session.is_public,
"reference_tokens": session.reference_tokens,
"expiration_date": millis_to_iso(session.expiration_date),
- "type": session.type,
"malfunctioning_tests": session.malfunctioning_tests
}
diff --git a/tools/wave/utils/user_agent_parser.py b/tools/wave/utils/user_agent_parser.py
index d1da820b530ed5e..7c0727e1f3cd844 100644
--- a/tools/wave/utils/user_agent_parser.py
+++ b/tools/wave/utils/user_agent_parser.py
@@ -1,5 +1,5 @@
-# mypy: allow-untyped-defs
-
+from __future__ import absolute_import
+from __future__ import unicode_literals
from ua_parser import user_agent_parser
diff --git a/tools/wave/wave-cli.py b/tools/wave/wave-cli.py
new file mode 100644
index 000000000000000..f920515c66153df
--- /dev/null
+++ b/tools/wave/wave-cli.py
@@ -0,0 +1,113 @@
+import sys
+import os
+import urllib2
+import zipfile
+
+START = "start"
+DOWNLOAD_REFERENCE_BROWSERS = "download-reference-results"
+
+REFERENCE_BROWSERS = {
+ "chrome": {
+ "name": "Chromium 73.0.3640.0",
+ "url": "https://s3.us-east-2.amazonaws.com/wave-browser-snapshots/wave-reference-browser-results/WMAS+2018/Chromium73-a50c6db0-6a94-11e9-8d1b-e23fc4555885.zip"
+ },
+ "edge": {
+ "name": "Edge 44.17763",
+ "url": "https://s3.us-east-2.amazonaws.com/wave-browser-snapshots/wave-reference-browser-results/WMAS+2018/Edge44-b2924d20-6a93-11e9-98b4-a11fb92a6d1c.zip"
+ },
+ "firefox": {
+ "name": "Firefox 64.0",
+ "url":
+ "https://s3.us-east-2.amazonaws.com/wave-browser-snapshots/wave-reference-browser-results/WMAS+2018/Firefox64-bb7aafa0-6a92-11e9-8ec2-04f58dad2e4f.zip"
+ },
+ "webkit": {
+ "name": "WebKit r239158",
+ "url":
+ "https://s3.us-east-2.amazonaws.com/wave-browser-snapshots/wave-reference-browser-results/WMAS+2018/WebKitr239158-caf823e0-6a92-11e9-b732-3188d0065ebc.zip"
+ }
+}
+
+
+def main():
+ parameters = get_run_parameters()
+ configuration_file_path = None
+ if ("configuration_file_path" in parameters):
+ configuration_file_path = parameters["configuration_file_path"]
+
+ if (parameters["operation"] == DOWNLOAD_REFERENCE_BROWSERS):
+ download_reference_browsers()
+
+
+def get_run_parameters():
+ arguments = sys.argv
+ parameters = {}
+
+ operation = arguments[1].lower()
+
+ if operation != START and operation != DOWNLOAD_REFERENCE_BROWSERS:
+ raise Exception("Unknown operation {}".format(operation))
+
+ parameters["operation"] = operation
+
+ iterator = iter(arguments)
+ next(iterator)
+ next(iterator)
+ for argument in iterator:
+ if (argument.lower() == "--config"):
+ path = next(iterator)
+ if not path.startswith("/"):
+ path = os.path.join(os.getcwd(), path)
+ parameters["configuration_file_path"] = path
+ continue
+
+ raise Exception("Unknown option {}".format(argument))
+
+ if "configuration_file_path" not in parameters:
+ parameters["configuration_file_path"] = os.path.join(
+ os.getcwd(), "config.json")
+
+ return parameters
+
+
+def download_file(url, file_path):
+ response = urllib2.urlopen(url)
+ data = response.read()
+ file = open(file_path, "wb")
+ file.write(data)
+ file.close()
+
+
+def printt(text):
+ sys.stdout.write(text)
+ sys.stdout.flush()
+
+
+def download_reference_browsers():
+ result_directory = os.path.abspath("./results")
+
+ if not os.path.isdir(result_directory):
+ os.mkdir(result_directory)
+
+ for id in REFERENCE_BROWSERS:
+ browser = REFERENCE_BROWSERS[id]
+ browser["zip"] = browser["url"].split("/")[-1]
+ printt("Downloading {} results ...".format(browser["name"]))
+ download_file(browser["url"], os.path.join(
+ result_directory, browser["zip"]))
+ print(" done.")
+
+ for id in REFERENCE_BROWSERS:
+ browser = REFERENCE_BROWSERS[id]
+ printt("Extracting {} results ...".format(browser["name"]))
+ zip_file = zipfile.ZipFile(os.path.join(
+ result_directory, browser["zip"]))
+ zip_file.extractall(result_directory)
+ print(" done.")
+
+ print("Cleaning ...")
+ for id in REFERENCE_BROWSERS:
+ browser = REFERENCE_BROWSERS[id]
+ os.remove(os.path.join(
+ result_directory, browser["zip"]))
+
+main()
diff --git a/tools/wave/wave_server.py b/tools/wave/wave_server.py
index 1439c0b7cae39c1..8f1998b7af7d676 100644
--- a/tools/wave/wave_server.py
+++ b/tools/wave/wave_server.py
@@ -1,5 +1,4 @@
-# mypy: allow-untyped-defs
-
+from __future__ import unicode_literals
import os
import logging
@@ -23,7 +22,7 @@
VERSION_STRING = "v3.3.0"
-class WaveServer:
+class WaveServer(object):
def initialize(self,
tests,
configuration_file_path=None,
diff --git a/tools/wave/webgl/prepare-tests.js b/tools/wave/webgl/prepare-tests.js
new file mode 100644
index 000000000000000..9ac8012ad70d674
--- /dev/null
+++ b/tools/wave/webgl/prepare-tests.js
@@ -0,0 +1,82 @@
+const fs = require("fs");
+const fse = require('fs-extra');
+const path = require("path");
+
+
+const makeDirectory = async directoryPath => {
+ return new Promise((resolve, reject) => {
+ fs.mkdir(directoryPath, error => {
+ if (error) {
+ reject(error);
+ }
+ resolve();
+ });
+ });
+};
+
+const readStats = async path => {
+ return new Promise((resolve, reject) => {
+ fs.stat(path, (error, stats) => {
+ if (error) {
+ resolve(null);
+ }
+ resolve(stats);
+ });
+ });
+};
+
+
+const addHarnessToTestsHeader = async(testsPath,testsListPath) =>{
+ var files = fs.readFileSync(testsListPath).toString().split("\n");
+ var numberOfTestFiles = 0;
+ for(var i=0; i", ' \n \n \n');
+ var file = fs.openSync(filename,'r+');
+ fs.writeSync(file, content);
+ numberOfTestFiles += 1;
+ }
+ }
+ }
+ return numberOfTestFiles;
+}
+
+
+(async () => {
+
+ const testDir = process.argv[2] || DEFAULT_TEST_DIR;
+
+ // Files that will be overwritten in the original webgl test suite
+ const PRE_TEST_NAME = "js-test-pre.js";
+ const UNIT_TEST_NAME = "unit.js";
+
+ const RESOURCES = path.join( __dirname ,"resources");
+ const DEFAULT_TEST_DIR = "/webgl/";
+ const DEFAULT_OUTPUT_DIR = ".";
+ const SUB_DIR_NAME = "webgl";
+
+ const testsPath = path.join(testDir, "conformance-suites");
+ const v1_0_3_harnessDir = path.join(testsPath, "1.0.3");
+ const preTestsPath = path.join(RESOURCES, PRE_TEST_NAME);
+ const unitTestPath = path.join(RESOURCES, UNIT_TEST_NAME);
+ let outputPath = process.argv[3] || DEFAULT_OUTPUT_DIR;
+ outputPath = path.join(outputPath, SUB_DIR_NAME);
+ const testsOutputPath = path.join(outputPath, "conformance-suite");
+ const resourcesPath = path.join(testsOutputPath, "resources");
+ const presTestDestinationPath = path.join(resourcesPath, "js-test-pre.js");
+ const unitTestDestinationputPath = path.join(testsOutputPath, "conformance", "more", "unit.js");
+
+ const testsListPath = path.join(RESOURCES, "list_all_tests")
+
+ if (!(await readStats(SUB_DIR_NAME))) await makeDirectory(SUB_DIR_NAME);
+
+ await fse.copy(v1_0_3_harnessDir, testsOutputPath);
+ await fse.copy(preTestsPath, presTestDestinationPath);
+ await fse.copy(unitTestPath, unitTestDestinationputPath);
+ const numberOfTestFiles = await addHarnessToTestsHeader(testsOutputPath,testsListPath);
+ console.log(`Total of ${numberOfTestFiles} webGl tests integrated`, testsListPath);
+})();
diff --git a/tools/wave/webgl/resources/js-test-pre.js b/tools/wave/webgl/resources/js-test-pre.js
new file mode 100644
index 000000000000000..47ef89f737aa8c4
--- /dev/null
+++ b/tools/wave/webgl/resources/js-test-pre.js
@@ -0,0 +1,531 @@
+/*
+** Copyright (c) 2012 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+// Array containing the runned subtests.
+// All the runned tests will be set to done once
+// notifyFinishedToHarness is called.
+var subTests = [];
+
+(function() {
+ var testHarnessInitialized = false;
+
+ setup({explicit_timeout: true});
+
+ var initNonKhronosFramework = function() {
+ if (testHarnessInitialized) {
+ return;
+ }
+ testHarnessInitialized = true;
+
+ /* -- plaform specific code -- */
+
+ // WebKit Specific code. Add your code here.
+ if (window.testRunner && !window.layoutTestController) {
+ window.layoutTestController = window.testRunner;
+ }
+
+ if (window.layoutTestController) {
+ layoutTestController.overridePreference("WebKitWebGLEnabled", "1");
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ }
+ if (window.internals) {
+ // The WebKit testing system compares console output.
+ // Because the output of the WebGL Tests is GPU dependent
+ // we turn off console messages.
+ window.console.log = function() { };
+ window.console.error = function() { };
+ window.internals.settings.setWebGLErrorsToConsoleEnabled(false);
+ }
+
+ /* -- end platform specific code --*/
+ }
+
+ this.initTestingHarness = function() {
+ initNonKhronosFramework();
+ }
+}());
+
+function nonKhronosFrameworkNotifyDone() {
+ // WebKit Specific code. Add your code here.
+ if (window.layoutTestController) {
+ layoutTestController.notifyDone();
+ }
+}
+
+function reportTestResultsToHarness(success, msg) {
+ var testTitle = "["+ subTests.length + "] " + msg;
+ var test = async_test(testTitle);
+ test.step(function() {
+ assert_true(success, testTitle + " should be true");
+ });
+ subTests.push(test);
+ if (window.parent.webglTestHarness) {
+ window.parent.webglTestHarness.reportResults(window.location.pathname, success, msg);
+ }
+}
+
+function notifyFinishedToHarness() {
+ for(var i=0; i < subTests.length ; i++){
+ subTests[i].done();
+ }
+ if (window.parent.webglTestHarness) {
+ window.parent.webglTestHarness.notifyFinished(window.location.pathname);
+ }
+ if (window.nonKhronosFrameworkNotifyDone) {
+ window.nonKhronosFrameworkNotifyDone();
+ }
+}
+
+function _logToConsole(msg)
+{
+ if (window.console)
+ window.console.log(msg);
+}
+
+var _jsTestPreVerboseLogging = false;
+
+function enableJSTestPreVerboseLogging()
+{
+ _jsTestPreVerboseLogging = true;
+}
+
+function description(msg)
+{
+ initTestingHarness();
+ if (msg === undefined) {
+ msg = document.title;
+ }
+ // For MSIE 6 compatibility
+ var span = document.createElement("span");
+ span.innerHTML = '' + msg + '
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
';
+ var description = document.getElementById("description");
+ if (description.firstChild)
+ description.replaceChild(span, description.firstChild);
+ else
+ description.appendChild(span);
+ if (_jsTestPreVerboseLogging) {
+ _logToConsole(msg);
+ }
+}
+
+function _addSpan(contents)
+{
+ var span = document.createElement("span");
+ document.getElementById("console").appendChild(span); // insert it first so XHTML knows the namespace
+ span.innerHTML = contents + '
';
+}
+
+function debug(msg)
+{
+ _addSpan(msg);
+ if (_jsTestPreVerboseLogging) {
+ _logToConsole(msg);
+ }
+}
+
+function escapeHTML(text)
+{
+ return text.replace(/&/g, "&").replace(/PASS ' + escapeHTML(msg) + '');
+ if (_jsTestPreVerboseLogging) {
+ _logToConsole('PASS ' + msg);
+ }
+}
+
+function testFailed(msg)
+{
+ reportTestResultsToHarness(false, msg);
+ _addSpan('FAIL ' + escapeHTML(msg) + '');
+ _logToConsole('FAIL ' + msg);
+}
+
+function areArraysEqual(_a, _b)
+{
+ try {
+ if (_a.length !== _b.length)
+ return false;
+ for (var i = 0; i < _a.length; i++)
+ if (_a[i] !== _b[i])
+ return false;
+ } catch (ex) {
+ return false;
+ }
+ return true;
+}
+
+function isMinusZero(n)
+{
+ // the only way to tell 0 from -0 in JS is the fact that 1/-0 is
+ // -Infinity instead of Infinity
+ return n === 0 && 1/n < 0;
+}
+
+function isResultCorrect(_actual, _expected)
+{
+ if (_expected === 0)
+ return _actual === _expected && (1/_actual) === (1/_expected);
+ if (_actual === _expected)
+ return true;
+ if (typeof(_expected) == "number" && isNaN(_expected))
+ return typeof(_actual) == "number" && isNaN(_actual);
+ if (Object.prototype.toString.call(_expected) == Object.prototype.toString.call([]))
+ return areArraysEqual(_actual, _expected);
+ return false;
+}
+
+function stringify(v)
+{
+ if (v === 0 && 1/v < 0)
+ return "-0";
+ else return "" + v;
+}
+
+function evalAndLog(_a)
+{
+ if (typeof _a != "string")
+ debug("WARN: tryAndLog() expects a string argument");
+
+ // Log first in case things go horribly wrong or this causes a sync event.
+ debug(_a);
+
+ var _av;
+ try {
+ _av = eval(_a);
+ } catch (e) {
+ testFailed(_a + " threw exception " + e);
+ }
+ return _av;
+}
+
+function shouldBe(_a, _b, quiet)
+{
+ if (typeof _a != "string" || typeof _b != "string")
+ debug("WARN: shouldBe() expects string arguments");
+ var exception;
+ var _av;
+ try {
+ _av = eval(_a);
+ } catch (e) {
+ exception = e;
+ }
+ var _bv = eval(_b);
+
+ if (exception)
+ testFailed(_a + " should be " + _bv + ". Threw exception " + exception);
+ else if (isResultCorrect(_av, _bv)) {
+ if (!quiet) {
+ testPassed(_a + " is " + _b);
+ }
+ } else if (typeof(_av) == typeof(_bv))
+ testFailed(_a + " should be " + _bv + ". Was " + stringify(_av) + ".");
+ else
+ testFailed(_a + " should be " + _bv + " (of type " + typeof _bv + "). Was " + _av + " (of type " + typeof _av + ").");
+}
+
+function shouldNotBe(_a, _b, quiet)
+{
+ if (typeof _a != "string" || typeof _b != "string")
+ debug("WARN: shouldNotBe() expects string arguments");
+ var exception;
+ var _av;
+ try {
+ _av = eval(_a);
+ } catch (e) {
+ exception = e;
+ }
+ var _bv = eval(_b);
+
+ if (exception)
+ testFailed(_a + " should not be " + _bv + ". Threw exception " + exception);
+ else if (!isResultCorrect(_av, _bv)) {
+ if (!quiet) {
+ testPassed(_a + " is not " + _b);
+ }
+ } else
+ testFailed(_a + " should not be " + _bv + ".");
+}
+
+function shouldBeTrue(_a) { shouldBe(_a, "true"); }
+function shouldBeFalse(_a) { shouldBe(_a, "false"); }
+function shouldBeNaN(_a) { shouldBe(_a, "NaN"); }
+function shouldBeNull(_a) { shouldBe(_a, "null"); }
+
+function shouldBeEqualToString(a, b)
+{
+ var unevaledString = '"' + b.replace(/"/g, "\"") + '"';
+ shouldBe(a, unevaledString);
+}
+
+function shouldEvaluateTo(actual, expected) {
+ // A general-purpose comparator. 'actual' should be a string to be
+ // evaluated, as for shouldBe(). 'expected' may be any type and will be
+ // used without being eval'ed.
+ if (expected == null) {
+ // Do this before the object test, since null is of type 'object'.
+ shouldBeNull(actual);
+ } else if (typeof expected == "undefined") {
+ shouldBeUndefined(actual);
+ } else if (typeof expected == "function") {
+ // All this fuss is to avoid the string-arg warning from shouldBe().
+ try {
+ actualValue = eval(actual);
+ } catch (e) {
+ testFailed("Evaluating " + actual + ": Threw exception " + e);
+ return;
+ }
+ shouldBe("'" + actualValue.toString().replace(/\n/g, "") + "'",
+ "'" + expected.toString().replace(/\n/g, "") + "'");
+ } else if (typeof expected == "object") {
+ shouldBeTrue(actual + " == '" + expected + "'");
+ } else if (typeof expected == "string") {
+ shouldBe(actual, expected);
+ } else if (typeof expected == "boolean") {
+ shouldBe("typeof " + actual, "'boolean'");
+ if (expected)
+ shouldBeTrue(actual);
+ else
+ shouldBeFalse(actual);
+ } else if (typeof expected == "number") {
+ shouldBe(actual, stringify(expected));
+ } else {
+ debug(expected + " is unknown type " + typeof expected);
+ shouldBeTrue(actual, "'" +expected.toString() + "'");
+ }
+}
+
+function shouldBeNonZero(_a)
+{
+ var exception;
+ var _av;
+ try {
+ _av = eval(_a);
+ } catch (e) {
+ exception = e;
+ }
+
+ if (exception)
+ testFailed(_a + " should be non-zero. Threw exception " + exception);
+ else if (_av != 0)
+ testPassed(_a + " is non-zero.");
+ else
+ testFailed(_a + " should be non-zero. Was " + _av);
+}
+
+function shouldBeNonNull(_a)
+{
+ var exception;
+ var _av;
+ try {
+ _av = eval(_a);
+ } catch (e) {
+ exception = e;
+ }
+
+ if (exception)
+ testFailed(_a + " should be non-null. Threw exception " + exception);
+ else if (_av != null)
+ testPassed(_a + " is non-null.");
+ else
+ testFailed(_a + " should be non-null. Was " + _av);
+}
+
+function shouldBeUndefined(_a)
+{
+ var exception;
+ var _av;
+ try {
+ _av = eval(_a);
+ } catch (e) {
+ exception = e;
+ }
+
+ if (exception)
+ testFailed(_a + " should be undefined. Threw exception " + exception);
+ else if (typeof _av == "undefined")
+ testPassed(_a + " is undefined.");
+ else
+ testFailed(_a + " should be undefined. Was " + _av);
+}
+
+function shouldBeDefined(_a)
+{
+ var exception;
+ var _av;
+ try {
+ _av = eval(_a);
+ } catch (e) {
+ exception = e;
+ }
+
+ if (exception)
+ testFailed(_a + " should be defined. Threw exception " + exception);
+ else if (_av !== undefined)
+ testPassed(_a + " is defined.");
+ else
+ testFailed(_a + " should be defined. Was " + _av);
+}
+
+function shouldBeGreaterThanOrEqual(_a, _b) {
+ if (typeof _a != "string" || typeof _b != "string")
+ debug("WARN: shouldBeGreaterThanOrEqual expects string arguments");
+
+ var exception;
+ var _av;
+ try {
+ _av = eval(_a);
+ } catch (e) {
+ exception = e;
+ }
+ var _bv = eval(_b);
+
+ if (exception)
+ testFailed(_a + " should be >= " + _b + ". Threw exception " + exception);
+ else if (typeof _av == "undefined" || _av < _bv)
+ testFailed(_a + " should be >= " + _b + ". Was " + _av + " (of type " + typeof _av + ").");
+ else
+ testPassed(_a + " is >= " + _b);
+}
+
+function expectTrue(v, msg) {
+ if (v) {
+ testPassed(msg);
+ } else {
+ testFailed(msg);
+ }
+}
+
+function shouldThrow(_a, _e)
+{
+ var exception;
+ var _av;
+ try {
+ _av = eval(_a);
+ } catch (e) {
+ exception = e;
+ }
+
+ var _ev;
+ if (_e)
+ _ev = eval(_e);
+
+ if (exception) {
+ if (typeof _e == "undefined" || exception == _ev)
+ testPassed(_a + " threw exception " + exception + ".");
+ else
+ testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Threw exception " + exception + ".");
+ } else if (typeof _av == "undefined")
+ testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Was undefined.");
+ else
+ testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Was " + _av + ".");
+}
+
+function shouldBeType(_a, _type) {
+ var exception;
+ var _av;
+ try {
+ _av = eval(_a);
+ } catch (e) {
+ exception = e;
+ }
+
+ var _typev = eval(_type);
+
+ if(_typev === Number){
+ if(_av instanceof Number){
+ testPassed(_a + " is an instance of Number");
+ }
+ else if(typeof(_av) === 'number'){
+ testPassed(_a + " is an instance of Number");
+ }
+ else{
+ testFailed(_a + " is not an instance of Number");
+ }
+ }
+ else if (_av instanceof _typev) {
+ testPassed(_a + " is an instance of " + _type);
+ } else {
+ testFailed(_a + " is not an instance of " + _type);
+ }
+}
+
+function assertMsg(assertion, msg) {
+ if (assertion) {
+ testPassed(msg);
+ } else {
+ testFailed(msg);
+ }
+}
+
+function gc() {
+ if (window.GCController) {
+ window.GCController.collect();
+ return;
+ }
+
+ if (window.opera && window.opera.collect) {
+ window.opera.collect();
+ return;
+ }
+
+ try {
+ window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIDOMWindowUtils)
+ .garbageCollect();
+ return;
+ } catch(e) {}
+
+ function gcRec(n) {
+ if (n < 1)
+ return {};
+ var temp = {i: "ab" + i + (i / 100000)};
+ temp += "foo";
+ gcRec(n-1);
+ }
+ for (var i = 0; i < 1000; i++)
+ gcRec(10);
+}
+
+function finishTest() {
+ successfullyParsed = true;
+ var epilogue = document.createElement("script");
+ var basePath = "";
+ var expectedBase = "js-test-pre.js";
+ var scripts = document.getElementsByTagName('script');
+ for (var script, i = 0; script = scripts[i]; i++) {
+ var src = script.src;
+ var l = src.length;
+ if (src.substr(l - expectedBase.length) == expectedBase) {
+ basePath = src.substr(0, l - expectedBase.length);
+ break;
+ }
+ }
+ epilogue.src = basePath + "js-test-post.js";
+ document.body.appendChild(epilogue);
+}
+
diff --git a/tools/wave/webgl/resources/list_all_tests b/tools/wave/webgl/resources/list_all_tests
new file mode 100644
index 000000000000000..1bfc60bd9d81f63
--- /dev/null
+++ b/tools/wave/webgl/resources/list_all_tests
@@ -0,0 +1,677 @@
+conformance/attribs/gl-bindAttribLocation-aliasing.html
+conformance/attribs/gl-bindAttribLocation-matrix.html
+conformance/attribs/gl-disabled-vertex-attrib.html
+conformance/attribs/gl-enable-vertex-attrib.html
+conformance/attribs/gl-matrix-attributes.html
+conformance/attribs/gl-vertex-attrib.html
+conformance/attribs/gl-vertexattribpointer.html
+conformance/attribs/gl-vertexattribpointer-offsets.html
+conformance/attribs/gl-vertex-attrib-render.html
+conformance/attribs/gl-vertex-attrib-zero-issues.html
+conformance/buffers/buffer-bind-test.html
+conformance/buffers/buffer-data-array-buffer.html
+conformance/buffers/buffer-data-array-buffer-delete.html
+conformance/buffers/element-array-buffer-delete-recreate.html
+conformance/buffers/index-validation-copies-indices.html
+conformance/buffers/index-validation-crash-with-buffer-sub-data.html
+conformance/buffers/index-validation-large-buffer.html
+conformance/buffers/index-validation-verifies-too-many-indices.html
+conformance/buffers/index-validation-with-resized-buffer.html
+conformance/buffers/index-validation.html
+conformance/canvas/buffer-offscreen-test.html
+conformance/canvas/buffer-preserve-test.html
+conformance/canvas/canvas-test.html
+conformance/canvas/canvas-zero-size.html
+conformance/canvas/drawingbuffer-static-canvas-test.html
+conformance/canvas/drawingbuffer-hd-dpi-test.html
+conformance/canvas/drawingbuffer-test.html
+conformance/canvas/draw-webgl-to-canvas-test.html
+conformance/canvas/draw-static-webgl-to-multiple-canvas-test.html
+conformance/canvas/framebuffer-bindings-unaffected-on-resize.html
+conformance/canvas/rapid-resizing.html
+conformance/canvas/texture-bindings-unaffected-on-resize.html
+conformance/canvas/to-data-url-test.html
+conformance/canvas/viewport-unchanged-upon-resize.html
+conformance/context/constants-and-properties.html
+conformance/context/context-attribute-preserve-drawing-buffer.html
+conformance/context/context-attributes-alpha-depth-stencil-antialias.html
+conformance/context/context-creation-and-destruction.html
+conformance/context/context-creation.html
+conformance/context/context-eviction-with-garbage-collection.html
+conformance/context/context-hidden-alpha.html
+conformance/context/context-release-upon-reload.html
+conformance/context/context-release-with-workers.html
+conformance/context/context-lost-restored.html
+conformance/context/context-lost.html
+conformance/context/context-type-test.html
+conformance/context/incorrect-context-object-behaviour.html
+conformance/context/methods.html
+conformance/context/premultiplyalpha-test.html
+conformance/context/resource-sharing-test.html
+conformance/extensions/angle-instanced-arrays.html
+conformance/extensions/angle-instanced-arrays-out-of-bounds.html
+conformance/extensions/ext-blend-minmax.html
+conformance/extensions/ext-frag-depth.html
+conformance/extensions/ext-shader-texture-lod.html
+conformance/extensions/ext-sRGB.html
+conformance/extensions/ext-texture-filter-anisotropic.html
+conformance/extensions/get-extension.html
+conformance/extensions/oes-standard-derivatives.html
+conformance/extensions/oes-texture-float-with-canvas.html
+conformance/extensions/oes-texture-float-with-image-data.html
+conformance/extensions/oes-texture-float-with-image.html
+conformance/extensions/oes-texture-float-with-video.html
+conformance/extensions/oes-texture-float.html
+conformance/extensions/oes-vertex-array-object.html
+conformance/extensions/oes-vertex-array-object-bufferData.html
+conformance/extensions/oes-texture-half-float.html
+conformance/extensions/oes-texture-float-linear.html
+conformance/extensions/oes-texture-half-float-linear.html
+conformance/extensions/oes-texture-half-float-with-canvas.html
+conformance/extensions/oes-texture-half-float-with-image-data.html
+conformance/extensions/oes-texture-half-float-with-image.html
+conformance/extensions/oes-texture-half-float-with-video.html
+conformance/extensions/oes-element-index-uint.html
+conformance/extensions/webgl-debug-renderer-info.html
+conformance/extensions/webgl-debug-shaders.html
+conformance/extensions/webgl-compressed-texture-atc.html
+conformance/extensions/webgl-compressed-texture-pvrtc.html
+conformance/extensions/webgl-compressed-texture-s3tc.html
+conformance/extensions/webgl-compressed-texture-size-limit.html
+conformance/extensions/webgl-depth-texture.html
+conformance/extensions/webgl-draw-buffers.html
+conformance/extensions/webgl-shared-resources.html
+conformance/glsl/bugs/angle-d3d11-compiler-error.html
+conformance/glsl/bugs/angle-dx-variable-bug.html
+conformance/glsl/bugs/array-of-struct-with-int-first-position.html
+conformance/glsl/bugs/compare-loop-index-to-uniform.html
+conformance/glsl/bugs/complex-glsl-does-not-crash.html
+conformance/glsl/bugs/conditional-discard-optimization.html
+conformance/glsl/bugs/constant-precision-qualifier.html
+conformance/glsl/bugs/floored-division-accuracy.html
+conformance/glsl/bugs/fragcoord-linking-bug.html
+conformance/glsl/bugs/long-expressions-should-not-crash.html
+conformance/glsl/bugs/modulo-arithmetic-accuracy.html
+conformance/glsl/bugs/multiplication-assignment.html
+conformance/glsl/bugs/nested-functions-should-not-crash.html
+conformance/glsl/bugs/sampler-array-using-loop-index.html
+conformance/glsl/bugs/temp-expressions-should-not-crash.html
+conformance/glsl/bugs/uniforms-should-not-lose-values.html
+conformance/glsl/bugs/conditional-discard-in-loop.html
+conformance/glsl/bugs/essl3-shaders-with-webgl1.html
+conformance/glsl/constructors/glsl-construct-vec2.html
+conformance/glsl/constructors/glsl-construct-vec3.html
+conformance/glsl/constructors/glsl-construct-vec4.html
+conformance/glsl/constructors/glsl-construct-ivec2.html
+conformance/glsl/constructors/glsl-construct-ivec3.html
+conformance/glsl/constructors/glsl-construct-ivec4.html
+conformance/glsl/constructors/glsl-construct-bvec2.html
+conformance/glsl/constructors/glsl-construct-bvec3.html
+conformance/glsl/constructors/glsl-construct-bvec4.html
+conformance/glsl/constructors/glsl-construct-mat2.html
+conformance/glsl/constructors/glsl-construct-mat3.html
+conformance/glsl/constructors/glsl-construct-mat4.html
+conformance/glsl/constructors/glsl-construct-vec-mat-corner-cases.html
+conformance/glsl/constructors/glsl-construct-vec-mat-index.html
+conformance/glsl/functions/glsl-function.html
+conformance/glsl/functions/glsl-function-abs.html
+conformance/glsl/functions/glsl-function-acos.html
+conformance/glsl/functions/glsl-function-asin.html
+conformance/glsl/functions/glsl-function-atan.html
+conformance/glsl/functions/glsl-function-atan-xy.html
+conformance/glsl/functions/glsl-function-ceil.html
+conformance/glsl/functions/glsl-function-clamp-float.html
+conformance/glsl/functions/glsl-function-clamp-gentype.html
+conformance/glsl/functions/glsl-function-cos.html
+conformance/glsl/functions/glsl-function-cross.html
+conformance/glsl/functions/glsl-function-distance.html
+conformance/glsl/functions/glsl-function-dot.html
+conformance/glsl/functions/glsl-function-faceforward.html
+conformance/glsl/functions/glsl-function-floor.html
+conformance/glsl/functions/glsl-function-fract.html
+conformance/glsl/functions/glsl-function-length.html
+conformance/glsl/functions/glsl-function-max-float.html
+conformance/glsl/functions/glsl-function-max-gentype.html
+conformance/glsl/functions/glsl-function-min-float.html
+conformance/glsl/functions/glsl-function-min-gentype.html
+conformance/glsl/functions/glsl-function-mix-float.html
+conformance/glsl/functions/glsl-function-mix-gentype.html
+conformance/glsl/functions/glsl-function-mod-float.html
+conformance/glsl/functions/glsl-function-mod-gentype.html
+conformance/glsl/functions/glsl-function-normalize.html
+conformance/glsl/functions/glsl-function-reflect.html
+conformance/glsl/functions/glsl-function-sign.html
+conformance/glsl/functions/glsl-function-sin.html
+conformance/glsl/functions/glsl-function-step-float.html
+conformance/glsl/functions/glsl-function-step-gentype.html
+conformance/glsl/functions/glsl-function-smoothstep-float.html
+conformance/glsl/functions/glsl-function-smoothstep-gentype.html
+conformance/glsl/implicit/add_int_float.vert.html
+conformance/glsl/implicit/add_int_mat2.vert.html
+conformance/glsl/implicit/add_int_mat3.vert.html
+conformance/glsl/implicit/add_int_mat4.vert.html
+conformance/glsl/implicit/add_int_vec2.vert.html
+conformance/glsl/implicit/add_int_vec3.vert.html
+conformance/glsl/implicit/add_int_vec4.vert.html
+conformance/glsl/implicit/add_ivec2_vec2.vert.html
+conformance/glsl/implicit/add_ivec3_vec3.vert.html
+conformance/glsl/implicit/add_ivec4_vec4.vert.html
+conformance/glsl/implicit/assign_int_to_float.vert.html
+conformance/glsl/implicit/assign_ivec2_to_vec2.vert.html
+conformance/glsl/implicit/assign_ivec3_to_vec3.vert.html
+conformance/glsl/implicit/assign_ivec4_to_vec4.vert.html
+conformance/glsl/implicit/construct_struct.vert.html
+conformance/glsl/implicit/divide_int_float.vert.html
+conformance/glsl/implicit/divide_int_mat2.vert.html
+conformance/glsl/implicit/divide_int_mat3.vert.html
+conformance/glsl/implicit/divide_int_mat4.vert.html
+conformance/glsl/implicit/divide_int_vec2.vert.html
+conformance/glsl/implicit/divide_int_vec3.vert.html
+conformance/glsl/implicit/divide_int_vec4.vert.html
+conformance/glsl/implicit/divide_ivec2_vec2.vert.html
+conformance/glsl/implicit/divide_ivec3_vec3.vert.html
+conformance/glsl/implicit/divide_ivec4_vec4.vert.html
+conformance/glsl/implicit/equal_int_float.vert.html
+conformance/glsl/implicit/equal_ivec2_vec2.vert.html
+conformance/glsl/implicit/equal_ivec3_vec3.vert.html
+conformance/glsl/implicit/equal_ivec4_vec4.vert.html
+conformance/glsl/implicit/function_int_float.vert.html
+conformance/glsl/implicit/function_ivec2_vec2.vert.html
+conformance/glsl/implicit/function_ivec3_vec3.vert.html
+conformance/glsl/implicit/function_ivec4_vec4.vert.html
+conformance/glsl/implicit/greater_than.vert.html
+conformance/glsl/implicit/greater_than_equal.vert.html
+conformance/glsl/implicit/less_than.vert.html
+conformance/glsl/implicit/less_than_equal.vert.html
+conformance/glsl/implicit/multiply_int_float.vert.html
+conformance/glsl/implicit/multiply_int_mat2.vert.html
+conformance/glsl/implicit/multiply_int_mat3.vert.html
+conformance/glsl/implicit/multiply_int_mat4.vert.html
+conformance/glsl/implicit/multiply_int_vec2.vert.html
+conformance/glsl/implicit/multiply_int_vec3.vert.html
+conformance/glsl/implicit/multiply_int_vec4.vert.html
+conformance/glsl/implicit/multiply_ivec2_vec2.vert.html
+conformance/glsl/implicit/multiply_ivec3_vec3.vert.html
+conformance/glsl/implicit/multiply_ivec4_vec4.vert.html
+conformance/glsl/implicit/not_equal_int_float.vert.html
+conformance/glsl/implicit/not_equal_ivec2_vec2.vert.html
+conformance/glsl/implicit/not_equal_ivec3_vec3.vert.html
+conformance/glsl/implicit/not_equal_ivec4_vec4.vert.html
+conformance/glsl/implicit/subtract_int_float.vert.html
+conformance/glsl/implicit/subtract_int_mat2.vert.html
+conformance/glsl/implicit/subtract_int_mat3.vert.html
+conformance/glsl/implicit/subtract_int_mat4.vert.html
+conformance/glsl/implicit/subtract_int_vec2.vert.html
+conformance/glsl/implicit/subtract_int_vec3.vert.html
+conformance/glsl/implicit/subtract_int_vec4.vert.html
+conformance/glsl/implicit/subtract_ivec2_vec2.vert.html
+conformance/glsl/implicit/subtract_ivec3_vec3.vert.html
+conformance/glsl/implicit/subtract_ivec4_vec4.vert.html
+conformance/glsl/implicit/ternary_int_float.vert.html
+conformance/glsl/implicit/ternary_ivec2_vec2.vert.html
+conformance/glsl/implicit/ternary_ivec3_vec3.vert.html
+conformance/glsl/implicit/ternary_ivec4_vec4.vert.html
+conformance/glsl/literals/float_literal.vert.html
+conformance/glsl/literals/literal_precision.html
+conformance/glsl/literals/overflow_leak.vert.html
+conformance/glsl/matrices/glsl-mat4-to-mat3.html
+conformance/glsl/matrices/glsl-mat3-construction.html
+conformance/glsl/misc/attrib-location-length-limits.html
+conformance/glsl/misc/boolean_precision.html
+conformance/glsl/misc/embedded-struct-definitions-forbidden.html
+conformance/glsl/misc/empty_main.vert.html
+conformance/glsl/misc/expression-list-in-declarator-initializer.html
+conformance/glsl/misc/gl_position_unset.vert.html
+conformance/glsl/misc/glsl-function-nodes.html
+conformance/glsl/misc/glsl-vertex-branch.html
+conformance/glsl/misc/glsl-long-variable-names.html
+conformance/glsl/misc/non-ascii-comments.vert.html
+conformance/glsl/misc/non-ascii.vert.html
+conformance/glsl/misc/re-compile-re-link.html
+conformance/glsl/misc/shader-precision-format-obeyed.html
+conformance/glsl/misc/shader-struct-scope.html
+conformance/glsl/misc/shader-uniform-packing-restrictions.html
+conformance/glsl/misc/shader-varying-packing-restrictions.html
+conformance/glsl/misc/shader-with-256-character-define.html
+conformance/glsl/misc/shader-with-256-character-identifier.frag.html
+conformance/glsl/misc/shader-with-257-character-define.html
+conformance/glsl/misc/shader-with-257-character-identifier.frag.html
+conformance/glsl/misc/shader-with-_webgl-identifier.vert.html
+conformance/glsl/misc/shader-with-arbitrary-indexing.frag.html
+conformance/glsl/misc/shader-with-arbitrary-indexing.vert.html
+conformance/glsl/misc/shader-with-array-of-structs-containing-arrays.html
+conformance/glsl/misc/shader-with-array-of-structs-uniform.html
+conformance/glsl/misc/shader-with-attrib-array.vert.html
+conformance/glsl/misc/shader-with-attrib-struct.vert.html
+conformance/glsl/misc/shader-with-clipvertex.vert.html
+conformance/glsl/misc/shader-with-conditional-scoping.html
+conformance/glsl/misc/shader-with-conditional-scoping-negative.html
+conformance/glsl/misc/shader-with-default-precision.frag.html
+conformance/glsl/misc/shader-with-default-precision.vert.html
+conformance/glsl/misc/shader-with-define-line-continuation.frag.html
+conformance/glsl/misc/shader-with-dfdx-no-ext.frag.html
+conformance/glsl/misc/shader-with-dfdx.frag.html
+conformance/glsl/misc/shader-with-do-loop.html
+conformance/glsl/misc/shader-with-error-directive.html
+conformance/glsl/misc/shader-with-explicit-int-cast.vert.html
+conformance/glsl/misc/shader-with-float-return-value.frag.html
+conformance/glsl/misc/shader-with-for-scoping.html
+conformance/glsl/misc/shader-with-for-loop.html
+conformance/glsl/misc/shader-with-frag-depth.frag.html
+conformance/glsl/misc/shader-with-function-recursion.frag.html
+conformance/glsl/misc/shader-with-function-scoped-struct.html
+conformance/glsl/misc/shader-with-functional-scoping.html
+conformance/glsl/misc/shader-with-comma-assignment.html
+conformance/glsl/misc/shader-with-comma-conditional-assignment.html
+conformance/glsl/misc/shader-with-glcolor.vert.html
+conformance/glsl/misc/shader-with-gles-1.frag.html
+conformance/glsl/misc/shader-with-gles-symbol.frag.html
+conformance/glsl/misc/shader-with-glprojectionmatrix.vert.html
+conformance/glsl/misc/shader-with-implicit-vec3-to-vec4-cast.vert.html
+conformance/glsl/misc/shader-with-include.vert.html
+conformance/glsl/misc/shader-with-int-return-value.frag.html
+conformance/glsl/misc/shader-with-invalid-identifier.frag.html
+conformance/glsl/misc/shader-with-ivec2-return-value.frag.html
+conformance/glsl/misc/shader-with-ivec3-return-value.frag.html
+conformance/glsl/misc/shader-with-ivec4-return-value.frag.html
+conformance/glsl/misc/shader-with-limited-indexing.frag.html
+conformance/glsl/misc/shader-with-hex-int-constant-macro.html
+conformance/glsl/misc/shader-with-long-line.html
+conformance/glsl/misc/shader-with-non-ascii-error.frag.html
+conformance/glsl/misc/shader-with-non-reserved-words.html
+conformance/glsl/misc/shader-with-precision.frag.html
+conformance/glsl/misc/shader-with-preprocessor-whitespace.html
+conformance/glsl/misc/shader-with-quoted-error.frag.html
+conformance/glsl/misc/shader-with-reserved-words.html
+conformance/glsl/misc/shader-with-similar-uniform-array-names.html
+conformance/glsl/misc/shader-with-too-many-uniforms.html
+conformance/glsl/misc/shader-with-undefined-preprocessor-symbol.frag.html
+conformance/glsl/misc/shader-with-uniform-in-loop-condition.vert.html
+conformance/glsl/misc/shader-with-vec2-return-value.frag.html
+conformance/glsl/misc/shader-with-vec3-return-value.frag.html
+conformance/glsl/misc/shader-with-vec4-return-value.frag.html
+conformance/glsl/misc/shader-with-vec4-vec3-vec4-conditional.html
+conformance/glsl/misc/shader-with-version-100.frag.html
+conformance/glsl/misc/shader-with-version-100.vert.html
+conformance/glsl/misc/shader-with-version-120.vert.html
+conformance/glsl/misc/shader-with-version-130.vert.html
+conformance/glsl/misc/shader-with-webgl-identifier.vert.html
+conformance/glsl/misc/shader-with-while-loop.html
+conformance/glsl/misc/shader-without-precision.frag.html
+conformance/glsl/misc/shaders-with-constant-expression-loop-conditions.html
+conformance/glsl/misc/shaders-with-invariance.html
+conformance/glsl/misc/shaders-with-name-conflicts.html
+conformance/glsl/misc/shaders-with-mis-matching-uniforms.html
+conformance/glsl/misc/shaders-with-mis-matching-varyings.html
+conformance/glsl/misc/shaders-with-missing-varyings.html
+conformance/glsl/misc/shaders-with-uniform-structs.html
+conformance/glsl/misc/shaders-with-varyings.html
+conformance/glsl/misc/shared.html
+conformance/glsl/misc/struct-nesting-exceeds-maximum.html
+conformance/glsl/misc/struct-nesting-under-maximum.html
+conformance/glsl/misc/uniform-location-length-limits.html
+conformance/glsl/misc/shader-with-short-circuiting-operators.html
+conformance/glsl/misc/shader-with-global-variable-precision-mismatch.html
+conformance/glsl/misc/large-loop-compile.html
+conformance/glsl/misc/struct-equals.html
+conformance/glsl/misc/struct-mixed-array-declarators.html
+conformance/glsl/misc/struct-nesting-of-variable-names.html
+conformance/glsl/misc/struct-specifiers-in-uniforms.html
+conformance/glsl/misc/struct-unary-operators.html
+conformance/glsl/misc/ternary-operators-in-global-initializers.html
+conformance/glsl/misc/ternary-operators-in-initializers.html
+conformance/glsl/reserved/_webgl_field.vert.html
+conformance/glsl/reserved/_webgl_function.vert.html
+conformance/glsl/reserved/_webgl_struct.vert.html
+conformance/glsl/reserved/_webgl_variable.vert.html
+conformance/glsl/reserved/webgl_field.vert.html
+conformance/glsl/reserved/webgl_function.vert.html
+conformance/glsl/reserved/webgl_struct.vert.html
+conformance/glsl/reserved/webgl_variable.vert.html
+conformance/glsl/samplers/glsl-function-texture2d-bias.html
+conformance/glsl/samplers/glsl-function-texture2dlod.html
+conformance/glsl/samplers/glsl-function-texture2dproj.html
+conformance/glsl/samplers/glsl-function-texture2dprojlod.html
+conformance/glsl/variables/gl-fragcoord.html
+conformance/glsl/variables/gl-frontfacing.html
+conformance/glsl/variables/gl-pointcoord.html
+conformance/glsl/variables/glsl-built-ins.html
+conformance/glsl/variables/gl-fragcoord-xy-values.html
+conformance/glsl/variables/gl-fragdata-and-fragcolor.html
+conformance/limits/gl-min-attribs.html
+conformance/limits/gl-max-texture-dimensions.html
+conformance/limits/gl-min-textures.html
+conformance/limits/gl-min-uniforms.html
+conformance/misc/bad-arguments-test.html
+conformance/misc/boolean-argument-conversion.html
+conformance/misc/delayed-drawing.html
+conformance/misc/error-reporting.html
+conformance/misc/instanceof-test.html
+conformance/misc/invalid-passed-params.html
+conformance/misc/is-object.html
+conformance/misc/null-object-behaviour.html
+conformance/misc/functions-returning-strings.html
+conformance/misc/object-deletion-behaviour.html
+conformance/misc/shader-precision-format.html
+conformance/misc/type-conversion-test.html
+conformance/misc/uninitialized-test.html
+conformance/misc/webgl-specific.html
+conformance/ogles/GL/abs/abs_001_to_006.html
+conformance/ogles/GL/acos/acos_001_to_006.html
+conformance/ogles/GL/all/all_001_to_004.html
+conformance/ogles/GL/any/any_001_to_004.html
+conformance/ogles/GL/array/array_001_to_006.html
+conformance/ogles/GL/asin/asin_001_to_006.html
+conformance/ogles/GL/atan/atan_001_to_008.html
+conformance/ogles/GL/atan/atan_009_to_012.html
+conformance/ogles/GL/biConstants/biConstants_001_to_008.html
+conformance/ogles/GL/biConstants/biConstants_009_to_016.html
+conformance/ogles/GL/biuDepthRange/biuDepthRange_001_to_002.html
+conformance/ogles/GL/build/build_001_to_008.html
+conformance/ogles/GL/build/build_009_to_016.html
+conformance/ogles/GL/build/build_017_to_024.html
+conformance/ogles/GL/build/build_025_to_032.html
+conformance/ogles/GL/build/build_033_to_040.html
+conformance/ogles/GL/build/build_041_to_048.html
+conformance/ogles/GL/build/build_049_to_056.html
+conformance/ogles/GL/build/build_057_to_064.html
+conformance/ogles/GL/build/build_065_to_072.html
+conformance/ogles/GL/build/build_073_to_080.html
+conformance/ogles/GL/build/build_081_to_088.html
+conformance/ogles/GL/build/build_089_to_096.html
+conformance/ogles/GL/build/build_097_to_104.html
+conformance/ogles/GL/build/build_105_to_112.html
+conformance/ogles/GL/build/build_113_to_120.html
+conformance/ogles/GL/build/build_121_to_128.html
+conformance/ogles/GL/build/build_129_to_136.html
+conformance/ogles/GL/build/build_137_to_144.html
+conformance/ogles/GL/build/build_145_to_152.html
+conformance/ogles/GL/build/build_153_to_160.html
+conformance/ogles/GL/build/build_161_to_168.html
+conformance/ogles/GL/build/build_169_to_176.html
+conformance/ogles/GL/build/build_177_to_178.html
+conformance/ogles/GL/built_in_varying_array_out_of_bounds/built_in_varying_array_out_of_bounds_001_to_001.html
+conformance/ogles/GL/ceil/ceil_001_to_006.html
+conformance/ogles/GL/clamp/clamp_001_to_006.html
+conformance/ogles/GL/control_flow/control_flow_001_to_008.html
+conformance/ogles/GL/control_flow/control_flow_009_to_010.html
+conformance/ogles/GL/cos/cos_001_to_006.html
+conformance/ogles/GL/cross/cross_001_to_002.html
+conformance/ogles/GL/default/default_001_to_001.html
+conformance/ogles/GL/degrees/degrees_001_to_006.html
+conformance/ogles/GL/discard/discard_001_to_002.html
+conformance/ogles/GL/distance/distance_001_to_006.html
+conformance/ogles/GL/dot/dot_001_to_006.html
+conformance/ogles/GL/equal/equal_001_to_008.html
+conformance/ogles/GL/equal/equal_009_to_012.html
+conformance/ogles/GL/exp/exp_001_to_008.html
+conformance/ogles/GL/exp/exp_009_to_012.html
+conformance/ogles/GL/exp2/exp2_001_to_008.html
+conformance/ogles/GL/exp2/exp2_009_to_012.html
+conformance/ogles/GL/faceforward/faceforward_001_to_006.html
+conformance/ogles/GL/floor/floor_001_to_006.html
+conformance/ogles/GL/fract/fract_001_to_006.html
+conformance/ogles/GL/functions/functions_001_to_008.html
+conformance/ogles/GL/functions/functions_009_to_016.html
+conformance/ogles/GL/functions/functions_017_to_024.html
+conformance/ogles/GL/functions/functions_025_to_032.html
+conformance/ogles/GL/functions/functions_033_to_040.html
+conformance/ogles/GL/functions/functions_041_to_048.html
+conformance/ogles/GL/functions/functions_049_to_056.html
+conformance/ogles/GL/functions/functions_057_to_064.html
+conformance/ogles/GL/functions/functions_065_to_072.html
+conformance/ogles/GL/functions/functions_073_to_080.html
+conformance/ogles/GL/functions/functions_081_to_088.html
+conformance/ogles/GL/functions/functions_089_to_096.html
+conformance/ogles/GL/functions/functions_097_to_104.html
+conformance/ogles/GL/functions/functions_105_to_112.html
+conformance/ogles/GL/functions/functions_113_to_120.html
+conformance/ogles/GL/functions/functions_121_to_126.html
+conformance/ogles/GL/gl_FragCoord/gl_FragCoord_001_to_003.html
+conformance/ogles/GL/gl_FrontFacing/gl_FrontFacing_001_to_001.html
+conformance/ogles/GL/greaterThan/greaterThan_001_to_008.html
+conformance/ogles/GL/greaterThanEqual/greaterThanEqual_001_to_008.html
+conformance/ogles/GL/inversesqrt/inversesqrt_001_to_006.html
+conformance/ogles/GL/length/length_001_to_006.html
+conformance/ogles/GL/lessThan/lessThan_001_to_008.html
+conformance/ogles/GL/lessThanEqual/lessThanEqual_001_to_008.html
+conformance/ogles/GL/log/log_001_to_008.html
+conformance/ogles/GL/log/log_009_to_012.html
+conformance/ogles/GL/log2/log2_001_to_008.html
+conformance/ogles/GL/log2/log2_009_to_012.html
+conformance/ogles/GL/mat/mat_001_to_008.html
+conformance/ogles/GL/mat/mat_009_to_016.html
+conformance/ogles/GL/mat/mat_017_to_024.html
+conformance/ogles/GL/mat/mat_025_to_032.html
+conformance/ogles/GL/mat/mat_033_to_040.html
+conformance/ogles/GL/mat/mat_041_to_046.html
+conformance/ogles/GL/mat3/mat3_001_to_006.html
+conformance/ogles/GL/matrixCompMult/matrixCompMult_001_to_004.html
+conformance/ogles/GL/max/max_001_to_006.html
+conformance/ogles/GL/min/min_001_to_006.html
+conformance/ogles/GL/mix/mix_001_to_006.html
+conformance/ogles/GL/mod/mod_001_to_008.html
+conformance/ogles/GL/normalize/normalize_001_to_006.html
+conformance/ogles/GL/not/not_001_to_004.html
+conformance/ogles/GL/notEqual/notEqual_001_to_008.html
+conformance/ogles/GL/notEqual/notEqual_009_to_012.html
+conformance/ogles/GL/operators/operators_001_to_008.html
+conformance/ogles/GL/operators/operators_009_to_016.html
+conformance/ogles/GL/operators/operators_017_to_024.html
+conformance/ogles/GL/operators/operators_025_to_026.html
+conformance/ogles/GL/pow/pow_001_to_008.html
+conformance/ogles/GL/pow/pow_009_to_016.html
+conformance/ogles/GL/pow/pow_017_to_024.html
+conformance/ogles/GL/radians/radians_001_to_006.html
+conformance/ogles/GL/reflect/reflect_001_to_006.html
+conformance/ogles/GL/refract/refract_001_to_006.html
+conformance/ogles/GL/sign/sign_001_to_006.html
+conformance/ogles/GL/sin/sin_001_to_006.html
+conformance/ogles/GL/smoothstep/smoothstep_001_to_006.html
+conformance/ogles/GL/sqrt/sqrt_001_to_006.html
+conformance/ogles/GL/step/step_001_to_006.html
+conformance/ogles/GL/struct/struct_001_to_008.html
+conformance/ogles/GL/struct/struct_009_to_016.html
+conformance/ogles/GL/struct/struct_017_to_024.html
+conformance/ogles/GL/struct/struct_025_to_032.html
+conformance/ogles/GL/struct/struct_033_to_040.html
+conformance/ogles/GL/struct/struct_041_to_048.html
+conformance/ogles/GL/struct/struct_049_to_056.html
+conformance/ogles/GL/swizzlers/swizzlers_001_to_008.html
+conformance/ogles/GL/swizzlers/swizzlers_009_to_016.html
+conformance/ogles/GL/swizzlers/swizzlers_017_to_024.html
+conformance/ogles/GL/swizzlers/swizzlers_025_to_032.html
+conformance/ogles/GL/swizzlers/swizzlers_033_to_040.html
+conformance/ogles/GL/swizzlers/swizzlers_041_to_048.html
+conformance/ogles/GL/swizzlers/swizzlers_049_to_056.html
+conformance/ogles/GL/swizzlers/swizzlers_057_to_064.html
+conformance/ogles/GL/swizzlers/swizzlers_065_to_072.html
+conformance/ogles/GL/swizzlers/swizzlers_073_to_080.html
+conformance/ogles/GL/swizzlers/swizzlers_081_to_088.html
+conformance/ogles/GL/swizzlers/swizzlers_089_to_096.html
+conformance/ogles/GL/swizzlers/swizzlers_097_to_104.html
+conformance/ogles/GL/swizzlers/swizzlers_105_to_112.html
+conformance/ogles/GL/swizzlers/swizzlers_113_to_120.html
+conformance/ogles/GL/tan/tan_001_to_006.html
+conformance/ogles/GL/vec/vec_001_to_008.html
+conformance/ogles/GL/vec/vec_009_to_016.html
+conformance/ogles/GL/vec/vec_017_to_018.html
+conformance/ogles/GL/vec3/vec3_001_to_008.html
+conformance/programs/get-active-test.html
+conformance/programs/gl-bind-attrib-location-test.html
+conformance/programs/gl-bind-attrib-location-long-names-test.html
+conformance/programs/gl-get-active-attribute.html
+conformance/programs/gl-get-active-uniform.html
+conformance/programs/gl-getshadersource.html
+conformance/programs/gl-shader-test.html
+conformance/programs/invalid-UTF-16.html
+conformance/programs/program-test.html
+conformance/programs/use-program-crash-with-discard-in-fragment-shader.html
+conformance/reading/read-pixels-pack-alignment.html
+conformance/reading/read-pixels-test.html
+conformance/renderbuffers/feedback-loop.html
+conformance/renderbuffers/framebuffer-object-attachment.html
+conformance/renderbuffers/framebuffer-state-restoration.html
+conformance/renderbuffers/framebuffer-test.html
+conformance/renderbuffers/renderbuffer-initialization.html
+conformance/rendering/culling.html
+conformance/rendering/draw-arrays-out-of-bounds.html
+conformance/rendering/draw-elements-out-of-bounds.html
+conformance/rendering/framebuffer-switch.html
+conformance/rendering/framebuffer-texture-switch.html
+conformance/rendering/gl-clear.html
+conformance/rendering/gl-drawarrays.html
+conformance/rendering/gl-drawelements.html
+conformance/rendering/gl-scissor-test.html
+conformance/rendering/gl-scissor-fbo-test.html
+conformance/rendering/gl-scissor-canvas-dimensions.html
+conformance/rendering/gl-viewport-test.html
+conformance/rendering/many-draw-calls.html
+conformance/rendering/more-than-65536-indices.html
+conformance/rendering/multisample-corruption.html
+conformance/rendering/negative-one-index.html
+conformance/rendering/point-no-attributes.html
+conformance/rendering/point-size.html
+conformance/rendering/point-with-gl-pointcoord-in-fragment-shader.html
+conformance/rendering/polygon-offset.html
+conformance/rendering/simple.html
+conformance/rendering/triangle.html
+conformance/rendering/line-loop-tri-fan.html
+conformance/state/gl-enable-enum-test.html
+conformance/state/gl-enum-tests.html
+conformance/state/gl-get-calls.html
+conformance/state/gl-geterror.html
+conformance/state/gl-getstring.html
+conformance/state/gl-object-get-calls.html
+conformance/state/state-uneffected-after-compositing.html
+conformance/textures/compressed-tex-image.html
+conformance/textures/copy-tex-image-and-sub-image-2d.html
+conformance/textures/copy-tex-image-2d-formats.html
+conformance/textures/default-texture.html
+conformance/textures/gl-get-tex-parameter.html
+conformance/textures/gl-pixelstorei.html
+conformance/textures/gl-teximage.html
+conformance/textures/origin-clean-conformance.html
+conformance/textures/tex-image-and-sub-image-2d-with-array-buffer-view.html
+conformance/textures/tex-image-and-sub-image-2d-with-canvas.html
+conformance/textures/tex-image-and-sub-image-2d-with-canvas-rgb565.html
+conformance/textures/tex-image-and-sub-image-2d-with-canvas-rgba4444.html
+conformance/textures/tex-image-and-sub-image-2d-with-canvas-rgba5551.html
+conformance/textures/tex-image-and-sub-image-2d-with-image-data.html
+conformance/textures/tex-image-and-sub-image-2d-with-image-data-rgb565.html
+conformance/textures/tex-image-and-sub-image-2d-with-image-data-rgba4444.html
+conformance/textures/tex-image-and-sub-image-2d-with-image-data-rgba5551.html
+conformance/textures/tex-image-and-sub-image-2d-with-image.html
+conformance/textures/tex-image-and-sub-image-2d-with-image-rgb565.html
+conformance/textures/tex-image-and-sub-image-2d-with-image-rgba4444.html
+conformance/textures/tex-image-and-sub-image-2d-with-image-rgba5551.html
+conformance/textures/tex-image-and-sub-image-2d-with-svg-image.html
+conformance/textures/tex-image-and-sub-image-2d-with-video.html
+conformance/textures/tex-image-and-sub-image-2d-with-video-rgb565.html
+conformance/textures/tex-image-and-sub-image-2d-with-video-rgba4444.html
+conformance/textures/tex-image-and-sub-image-2d-with-video-rgba5551.html
+conformance/textures/tex-image-and-sub-image-2d-with-webgl-canvas.html
+conformance/textures/tex-image-and-sub-image-2d-with-webgl-canvas-rgb565.html
+conformance/textures/tex-image-and-sub-image-2d-with-webgl-canvas-rgba4444.html
+conformance/textures/tex-image-and-sub-image-2d-with-webgl-canvas-rgba5551.html
+conformance/textures/tex-image-and-uniform-binding-bugs.html
+conformance/textures/tex-image-canvas-corruption.html
+conformance/textures/tex-image-webgl.html
+conformance/textures/tex-image-with-format-and-type.html
+conformance/textures/tex-image-with-invalid-data.html
+conformance/textures/tex-input-validation.html
+conformance/textures/tex-sub-image-2d-bad-args.html
+conformance/textures/tex-sub-image-2d.html
+conformance/textures/texparameter-test.html
+conformance/textures/texture-active-bind-2.html
+conformance/textures/texture-active-bind.html
+conformance/textures/texture-attachment-formats.html
+conformance/textures/texture-clear.html
+conformance/textures/texture-complete.html
+conformance/textures/texture-copying-feedback-loops.html
+conformance/textures/texture-hd-dpi.html
+conformance/textures/texture-formats-test.html
+conformance/textures/texture-mips.html
+conformance/textures/texture-npot-video.html
+conformance/textures/texture-npot.html
+conformance/textures/texture-size.html
+conformance/textures/texture-size-cube-maps.html
+conformance/textures/texture-size-limit.html
+conformance/textures/texture-sub-image-cube-maps.html
+conformance/textures/texture-transparent-pixels-initialized.html
+conformance/textures/texture-upload-cube-maps.html
+conformance/textures/texture-upload-size.html
+conformance/textures/mipmap-fbo.html
+conformance/textures/texture-fakeblack.html
+conformance/textures/texture-draw-with-2d-and-cube.html
+conformance/typedarrays/array-buffer-crash.html
+conformance/typedarrays/array-buffer-view-crash.html
+conformance/typedarrays/array-unit-tests.html
+conformance/typedarrays/data-view-crash.html
+conformance/typedarrays/data-view-test.html
+conformance/typedarrays/typed-arrays-in-workers.html
+conformance/typedarrays/array-large-array-tests.html
+conformance/uniforms/gl-uniform-arrays.html
+conformance/uniforms/gl-uniform-bool.html
+conformance/uniforms/gl-uniformmatrix4fv.html
+conformance/uniforms/gl-unknown-uniform.html
+conformance/uniforms/null-uniform-location.html
+conformance/uniforms/out-of-bounds-uniform-array-access.html
+conformance/uniforms/uniform-default-values.html
+conformance/uniforms/uniform-values-per-program.html
+conformance/uniforms/uniform-location.html
+conformance/uniforms/uniform-samplers-test.html
+conformance/more/conformance/constants.html
+conformance/more/conformance/getContext.html
+conformance/more/conformance/methods.html
+conformance/more/conformance/quickCheckAPI-A.html
+conformance/more/conformance/quickCheckAPI-B1.html
+conformance/more/conformance/quickCheckAPI-B2.html
+conformance/more/conformance/quickCheckAPI-B3.html
+conformance/more/conformance/quickCheckAPI-B4.html
+conformance/more/conformance/quickCheckAPI-C.html
+conformance/more/conformance/quickCheckAPI-D_G.html
+conformance/more/conformance/quickCheckAPI-G_I.html
+conformance/more/conformance/quickCheckAPI-L_S.html
+conformance/more/conformance/quickCheckAPI-S_V.html
+conformance/more/conformance/webGLArrays.html
+conformance/more/functions/bindBuffer.html
+conformance/more/functions/bindBufferBadArgs.html
+conformance/more/functions/bindFramebufferLeaveNonZero.html
+conformance/more/functions/bufferData.html
+conformance/more/functions/bufferDataBadArgs.html
+conformance/more/functions/bufferSubData.html
+conformance/more/functions/bufferSubDataBadArgs.html
+conformance/more/functions/copyTexImage2D.html
+conformance/more/functions/copyTexImage2DBadArgs.html
+conformance/more/functions/copyTexSubImage2D.html
+conformance/more/functions/copyTexSubImage2DBadArgs.html
+conformance/more/functions/deleteBufferBadArgs.html
+conformance/more/functions/drawArrays.html
+conformance/more/functions/drawArraysOutOfBounds.html
+conformance/more/functions/drawElements.html
+conformance/more/functions/isTests.html
+conformance/more/functions/isTestsBadArgs.html
+conformance/more/functions/readPixels.html
+conformance/more/functions/readPixelsBadArgs.html
+conformance/more/functions/texImage2D.html
+conformance/more/functions/texImage2DBadArgs.html
+conformance/more/functions/texImage2DHTML.html
+conformance/more/functions/texImage2DHTMLBadArgs.html
+conformance/more/functions/texSubImage2D.html
+conformance/more/functions/texSubImage2DBadArgs.html
+conformance/more/functions/texSubImage2DHTML.html
+conformance/more/functions/texSubImage2DHTMLBadArgs.html
+conformance/more/functions/uniformf.html
+conformance/more/functions/uniformfBadArgs.html
+conformance/more/functions/uniformfArrayLen1.html
+conformance/more/functions/uniformi.html
+conformance/more/functions/uniformiBadArgs.html
+conformance/more/functions/uniformMatrix.html
+conformance/more/functions/uniformMatrixBadArgs.html
+conformance/more/functions/vertexAttrib.html
+conformance/more/functions/vertexAttribBadArgs.html
+conformance/more/functions/vertexAttribPointer.html
+conformance/more/functions/vertexAttribPointerBadArgs.html
+conformance/more/glsl/arrayOutOfBounds.html
+conformance/more/glsl/uniformOutOfBounds.html
\ No newline at end of file
diff --git a/tools/wave/webgl/resources/unit.js b/tools/wave/webgl/resources/unit.js
new file mode 100644
index 000000000000000..ee667ee83d0cc41
--- /dev/null
+++ b/tools/wave/webgl/resources/unit.js
@@ -0,0 +1,960 @@
+/*
+Unit testing library for the OpenGL ES 2.0 HTML Canvas context
+*/
+
+/*
+** Copyright (c) 2012 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be included
+** in all copies or substantial portions of the Materials.
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+// Array containing the runned subtests.
+// All the runned tests will be set to done once
+// notifyFinishedToHarness is called.
+var subTests = [];
+
+/* -- plaform specific code -- */
+
+// WebKit
+if (window.testRunner && !window.layoutTestController) {
+ window.layoutTestController = window.testRunner;
+}
+
+if (window.layoutTestController) {
+ layoutTestController.overridePreference("WebKitWebGLEnabled", "1");
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+
+ // The WebKit testing system compares console output.
+ // Because the output of the WebGL Tests is GPU dependent
+ // we turn off console messages.
+ window.console.log = function() { };
+ window.console.error = function() { };
+
+ // RAF doesn't work in LayoutTests. Disable it so the tests will
+ // use setTimeout instead.
+ window.requestAnimationFrame = undefined;
+ window.webkitRequestAnimationFrame = undefined;
+}
+
+if (window.internals) {
+ window.internals.settings.setWebGLErrorsToConsoleEnabled(false);
+}
+
+/* -- end platform specific code --*/
+Tests = {
+ autorun : true,
+ message : null,
+ delay : 0,
+ autoinit: true,
+
+ startUnit : function(){ return []; },
+ setup : function() { return arguments; },
+ teardown : function() {},
+ endUnit : function() {}
+}
+
+var __testSuccess__ = true;
+var __testFailCount__ = 0;
+var __testLog__;
+var __backlog__ = [];
+
+Object.toSource = function(a, seen){
+ if (a == null) return "null";
+ if (typeof a == 'boolean') return a ? "true" : "false";
+ if (typeof a == 'string') return '"' + a.replace(/"/g, '\\"') + '"';
+ if (a instanceof HTMLElement) return a.toString();
+ if (a.width && a.height && a.data) return "[ImageData]";
+ if (a instanceof Array) {
+ if (!seen) seen = [];
+ var idx = seen.indexOf(a);
+ if (idx != -1) return '#'+(idx+1)+'#';
+ seen.unshift(a);
+ var srcs = a.map(function(o){ return Object.toSource(o,seen) });
+ var prefix = '';
+ idx = seen.indexOf(a);
+ if (idx != -1) prefix = '#'+(idx+1)+'=';
+ return prefix + '[' + srcs.join(", ") + ']';
+ }
+ if (typeof a == 'object') {
+ if (!seen) seen = [];
+ var idx = seen.indexOf(a);
+ if (idx != -1) return '#'+(idx+1)+'#';
+ seen.unshift(a);
+ var members = [];
+ var name;
+ try {
+ for (var i in a) {
+ if (i.search(/^[a-zA-Z0-9]+$/) != -1)
+ name = i;
+ else
+ name = '"' + i.replace(/"/g, '\\"') + '"';
+ var ai;
+ try { ai = a[i]; }
+ catch(e) { ai = 'null /*ERROR_ACCESSING*/'; }
+ var s = name + ':' + Object.toSource(ai, seen);
+ members.push(s);
+ }
+ } catch (e) {}
+ var prefix = '';
+ idx = seen.indexOf(a);
+ if (idx != -1) prefix = '#'+(idx+1)+'=';
+ return prefix + '{' + members.join(", ") + '}'
+ }
+ if (typeof a == 'function')
+ return '('+a.toString().replace(/\n/g, " ").replace(/\s+/g, " ")+')';
+ return a.toString();
+}
+
+function formatError(e) {
+ if (window.console) console.log(e);
+ var pathSegs = location.href.toString().split("/");
+ var currentDoc = e.lineNumber != null ? pathSegs[pathSegs.length - 1] : null;
+ var trace = (e.filename || currentDoc) + ":" + e.lineNumber + (e.trace ? "\n"+e.trace : "");
+ return e.message + "\n" + trace;
+}
+
+function runTests() {
+ var h = document.getElementById('test-status');
+ if (h == null) {
+ h = document.createElement('h1');
+ h.id = 'test-status';
+ document.body.appendChild(h);
+ }
+ h.textContent = "";
+ var log = document.getElementById('test-log');
+ if (log == null) {
+ log = document.createElement('div');
+ log.id = 'test-log';
+ document.body.appendChild(log);
+ }
+ while (log.childNodes.length > 0)
+ log.removeChild(log.firstChild);
+
+ var setup_args = [];
+
+ if (Tests.startUnit != null) {
+ __testLog__ = document.createElement('div');
+ try {
+ setup_args = Tests.startUnit();
+ if (__testLog__.childNodes.length > 0)
+ log.appendChild(__testLog__);
+ } catch(e) {
+ testFailed("startUnit", formatError(e));
+ log.appendChild(__testLog__);
+ printTestStatus();
+ return;
+ }
+ }
+
+ var testsRun = false;
+ var allTestsSuccessful = true;
+
+ for (var i in Tests) {
+ if (i.substring(0,4) != "test") continue;
+ __testLog__ = document.createElement('div');
+ __testSuccess__ = true;
+ try {
+ doTestNotify (i);
+ var args = setup_args;
+ if (Tests.setup != null)
+ args = Tests.setup.apply(Tests, setup_args);
+ Tests[i].apply(Tests, args);
+ if (Tests.teardown != null)
+ Tests.teardown.apply(Tests, args);
+ }
+ catch (e) {
+ testFailed(i, e.name, formatError(e));
+ }
+ if (__testSuccess__ == false) {
+ ++__testFailCount__;
+ }
+ var h = document.createElement('h2');
+ h.textContent = i;
+ __testLog__.insertBefore(h, __testLog__.firstChild);
+ log.appendChild(__testLog__);
+ allTestsSuccessful = allTestsSuccessful && __testSuccess__ == true;
+ reportTestResultsToHarness(__testSuccess__, i);
+ doTestNotify (i+"--"+(__testSuccess__?"OK":"FAIL"));
+ testsRun = true;
+ }
+
+ printTestStatus(testsRun);
+ if (Tests.endUnit != null) {
+ __testLog__ = document.createElement('div');
+ try {
+ Tests.endUnit.apply(Tests, setup_args);
+ if (__testLog__.childNodes.length > 0)
+ log.appendChild(__testLog__);
+ } catch(e) {
+ testFailed("endUnit", e.name, formatError(e));
+ log.appendChild(__testLog__);
+ }
+ }
+ notifyFinishedToHarness(allTestsSuccessful, "finished tests");
+}
+
+function doTestNotify(name) {
+ //try {
+ // var xhr = new XMLHttpRequest();
+ // xhr.open("GET", "http://localhost:8888/"+name, true);
+ // xhr.send(null);
+ //} catch(e) {}
+}
+
+function testFailed(assertName, name) {
+ var d = document.createElement('div');
+ var h = document.createElement('h3');
+ var d1 = document.createElement("span");
+ h.appendChild(d1);
+ d1.appendChild(document.createTextNode("FAIL: "));
+ d1.style.color = "red";
+ h.appendChild(document.createTextNode(
+ name==null ? assertName : name + " (in " + assertName + ")"));
+ d.appendChild(h);
+ var args = []
+ for (var i=2; il[ii]) {
+ testFailed("assertArrayEqualsWithEpsilon", name, v, p, l);
+ return false;
+ }
+ }
+ testPassed("assertArrayEqualsWithEpsilon", name, v, p, l);
+ return true;
+}
+
+function assertNotEquals(name, v, p) {
+ if (p == null) { p = v; v = name; name = null; }
+ if (compare(v, p)) {
+ testFailed("assertNotEquals", name, v, p)
+ return false;
+ } else {
+ testPassed("assertNotEquals", name, v, p)
+ return true;
+ }
+}
+
+function time(elementId, f) {
+ var s = document.getElementById(elementId);
+ var t0 = new Date().getTime();
+ f();
+ var t1 = new Date().getTime();
+ s.textContent = 'Elapsed: '+(t1-t0)+' ms';
+}
+
+function randomFloat () {
+ // note that in fuzz-testing, this can used as the size of a buffer to allocate.
+ // so it shouldn't return astronomic values. The maximum value 10000000 is already quite big.
+ var fac = 1.0;
+ var r = Math.random();
+ if (r < 0.25)
+ fac = 10;
+ else if (r < 0.4)
+ fac = 100;
+ else if (r < 0.5)
+ fac = 1000;
+ else if (r < 0.6)
+ fac = 100000;
+ else if (r < 0.7)
+ fac = 10000000;
+ else if (r < 0.8)
+ fac = NaN;
+ return -0.5*fac + Math.random() * fac;
+}
+function randomFloatFromRange(lo, hi) {
+ var r = Math.random();
+ if (r < 0.05)
+ return lo;
+ else if (r > 0.95)
+ return hi;
+ else
+ return lo + Math.random()*(hi-lo);
+}
+function randomInt (sz) {
+ if (sz != null)
+ return Math.floor(Math.random()*sz);
+ else
+ return Math.floor(randomFloat());
+}
+function randomIntFromRange(lo, hi) {
+ return Math.floor(randomFloatFromRange(lo, hi));
+}
+function randomLength () {
+ var l = Math.floor(Math.random() * 256);
+ if (Math.random < 0.5) l = l / 10;
+ if (Math.random < 0.3) l = l / 10;
+ return l;
+}
+function randomSmallIntArray () {
+ var l = randomLength();
+ var s = new Array(l);
+ for (var i=0; i
+