diff --git a/.python-version b/.python-version
new file mode 100644
index 00000000000000..cc1923a40b1a5e
--- /dev/null
+++ b/.python-version
@@ -0,0 +1 @@
+3.8
diff --git a/tools/ci/ci_tools_integration_test.sh b/tools/ci/ci_tools_integration_test.sh
index 76f682e254d313..2eae4303f13d04 100755
--- a/tools/ci/ci_tools_integration_test.sh
+++ b/tools/ci/ci_tools_integration_test.sh
@@ -13,11 +13,6 @@ main() {
cd tools/wpt
tox
cd $WPT_ROOT
-
- # WMAS test runner integration tests
- cd tools/wave
- tox
- cd $WPT_ROOT
}
main
diff --git a/tools/wave/configuration_loader.py b/tools/wave/configuration_loader.py
index 2e2aa331511741..ea406bb738bc3d 100644
--- a/tools/wave/configuration_loader.py
+++ b/tools/wave/configuration_loader.py
@@ -91,7 +91,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/session.py b/tools/wave/data/session.py
index bb1b932dae60b1..365cc400d5df5f 100644
--- a/tools/wave/data/session.py
+++ b/tools/wave/data/session.py
@@ -9,6 +9,9 @@
PENDING = "pending"
UNKNOWN = "unknown"
+WMAS = "wmas"
+DPCTF = "dpctf"
+
class Session:
def __init__(
@@ -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 00000000000000..96d51477fb7952
--- /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 00000000000000..43e7509fe2288b
--- /dev/null
+++ b/tools/wave/ecmascript/generate-tests.js
@@ -0,0 +1,307 @@
+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);
+ 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) || [];
+ 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 00000000000000..679bd83bc70926
--- /dev/null
+++ b/tools/wave/ecmascript/test-template.html
@@ -0,0 +1,12 @@
+
+
{{ 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 00000000000000..67c8d375f6c0b8
--- /dev/null
+++ b/tools/wave/ecmascript/test-template.iframe.html
@@ -0,0 +1,15 @@
+
+{{ TEST_TITLE }}
+
+
+
+
+
+
+{{ INCLUDES }}
+
+
+
+
diff --git a/tools/wave/ecmascript/webplatform-adapter.js b/tools/wave/ecmascript/webplatform-adapter.js
new file mode 100644
index 00000000000000..bfa6e9fe7f78c9
--- /dev/null
+++ b/tools/wave/ecmascript/webplatform-adapter.js
@@ -0,0 +1,104 @@
+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;
diff --git a/tools/wave/network/api/results_api_handler.py b/tools/wave/network/api/results_api_handler.py
index a9da0df10f3f7a..1c619f6927f7ee 100644
--- a/tools/wave/network/api/results_api_handler.py
+++ b/tools/wave/network/api/results_api_handler.py
@@ -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.query
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 9eb896b80702e9..c9c5d2a9e687a2 100644
--- a/tools/wave/network/api/sessions_api_handler.py
+++ b/tools/wave/network/api/sessions_api_handler.py
@@ -20,7 +20,7 @@ def __init__(
results_manager,
event_dispatcher,
web_root,
- read_sessions_enabled
+ read_sessions_enabled,
):
super().__init__(web_root)
self._sessions_manager = sessions_manager
@@ -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 38035837711ab6..5126be99230ed0 100644
--- a/tools/wave/network/api/tests_api_handler.py
+++ b/tools/wave/network/api/tests_api_handler.py
@@ -111,7 +111,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,
@@ -265,13 +266,7 @@ def _generate_test_url(self, hostname, test, token, test_timeout):
test = split[0]
test_query = split[1]
- query = "token={}&timeout={}&https_port={}&web_root={}&{}".format(
- token,
- test_timeout,
- self._wpt_ssl_port,
- self._web_root,
- test_query
- )
+ query = f"token={token}&timeout={test_timeout}&https_port={self._wpt_ssl_port}&web_root={self._web_root}&{test_query}"
return self._generate_url(
protocol=protocol,
diff --git a/tools/wave/network/http_handler.py b/tools/wave/network/http_handler.py
index b76f711cf1d7c6..446d0641d2002a 100644
--- a/tools/wave/network/http_handler.py
+++ b/tools/wave/network/http_handler.py
@@ -12,14 +12,14 @@
class HttpHandler:
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 +89,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 +99,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,7 +112,7 @@ 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])
diff --git a/tools/wave/requirements.txt b/tools/wave/requirements.txt
index 5404a6916bd6c3..3bb476fd968b2d 100644
--- a/tools/wave/requirements.txt
+++ b/tools/wave/requirements.txt
@@ -1,3 +1,2 @@
ua-parser==0.18.0
python-dateutil==2.9.0.post0
-types-python-dateutil==2.9.0.20241206
diff --git a/tools/wave/testing/devices_manager.py b/tools/wave/testing/devices_manager.py
index 935782d1372b67..fb0323e01ace76 100644
--- a/tools/wave/testing/devices_manager.py
+++ b/tools/wave/testing/devices_manager.py
@@ -23,7 +23,7 @@ def initialize(self, event_dispatcher):
def create_device(self, user_agent):
browser = parse_user_agent(user_agent)
- name = "{} {}".format(browser["name"], browser["version"])
+ name = f"{browser['name']} {browser['version']}"
token = str(uuid.uuid1())
last_active = int(time.time() * 1000)
diff --git a/tools/wave/testing/results_manager.py b/tools/wave/testing/results_manager.py
index 13dfcbad7d8515..a36e19262feeb9 100644
--- a/tools/wave/testing/results_manager.py
+++ b/tools/wave/testing/results_manager.py
@@ -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
@@ -22,7 +21,6 @@
WAVE_SRC_DIR = "./tools/wave"
RESULTS_FILE_REGEX = r"^\w\w\d\d\d?\.json$"
RESULTS_FILE_PATTERN = re.compile(RESULTS_FILE_REGEX)
-SESSION_RESULTS_TIMEOUT = 60*30 # 30min
class ResultsManager:
@@ -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,7 +213,6 @@ 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)
@@ -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)
@@ -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))
@@ -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 093c3cffe8507a..a4992632cabe2d 100644
--- a/tools/wave/testing/sessions_manager.py
+++ b/tools/wave/testing/sessions_manager.py
@@ -15,6 +15,7 @@
from ..data.exceptions.not_found_exception import NotFoundException
from ..data.exceptions.invalid_data_exception import InvalidDataException
from ..utils.deserializer import deserialize_session
+from ..utils.deserializer import iso_to_millis
DEFAULT_TEST_TYPES = [AUTOMATIC, MANUAL]
DEFAULT_TEST_PATHS = ["/"]
@@ -23,13 +24,15 @@
class SessionsManager:
- def initialize(self,
- test_loader,
- event_dispatcher,
- tests_manager,
- results_directory,
- results_manager,
- configuration):
+ 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 = {}
@@ -76,18 +78,24 @@ def create_session(
if test_type != "automatic" and test_type != "manual":
raise InvalidDataException(f"Unknown type '{test_type}'")
+ if expiration_date is not None and not isinstance(expiration_date, int):
+ expiration_date = iso_to_millis(expiration_date)
+ if not isinstance(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 = ""
diff --git a/tools/wave/testing/test_loader.py b/tools/wave/testing/test_loader.py
index 8d751260d97656..0d3a7d0fb197c7 100644
--- a/tools/wave/testing/test_loader.py
+++ b/tools/wave/testing/test_loader.py
@@ -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,22 @@ 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:
+ 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 +117,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 +160,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 +173,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 8187eb4a7db4f1..69ce96105135c1 100644
--- a/tools/wave/testing/tests_manager.py
+++ b/tools/wave/testing/tests_manager.py
@@ -2,6 +2,7 @@
import re
from threading import Timer
+import functools
from .event_dispatcher import TEST_COMPLETED_EVENT
@@ -10,19 +11,14 @@
class TestsManager:
- def initialize(
- self,
- test_loader,
- sessions_manager,
- results_manager,
- event_dispatcher
- ):
+ def initialize(self, test_loader, sessions_manager, results_manager, event_dispatcher):
self._test_loader = test_loader
self._sessions_manager = sessions_manager
self._results_manager = results_manager
self._event_dispatcher = event_dispatcher
self._timeouts = []
+ self._logs = {}
def next_test(self, session):
if session.status == COMPLETED or session.status == ABORTED:
@@ -53,10 +49,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 +103,7 @@ 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 +114,29 @@ 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 +151,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:
@@ -224,10 +211,8 @@ def skip_to(self, test_list, test):
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 +268,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 +294,11 @@ 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
- )
+ self._event_dispatcher.dispatch_event(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 +336,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 +344,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 b84119ed852666..acf57a2e7c157d 100644
--- a/tools/wave/testing/wpt_report.py
+++ b/tools/wave/testing/wpt_report.py
@@ -12,7 +12,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 +28,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:
+ raise Exception("Failed to execute wptreport: " + " ".join(command))
def generate_multi_report(
@@ -40,6 +44,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 +61,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
diff --git a/tools/wave/tests/test_wave.py b/tools/wave/tests/test_wave.py
deleted file mode 100644
index a7d87a38e1b01f..00000000000000
--- a/tools/wave/tests/test_wave.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# mypy: allow-untyped-defs
-
-import errno
-import os
-import socket
-import subprocess
-import time
-
-from urllib.request import urlopen
-from urllib.error import URLError
-
-from tools.wpt import wpt
-
-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:
- if e.errno == errno.EADDRINUSE:
- return True
- else:
- raise e
- finally:
- s.close()
- return False
-
-def test_serve():
- if is_port_8080_in_use():
- assert False, "WAVE Test Runner failed: Port 8080 already in use."
-
- p = subprocess.Popen([os.path.join(wpt.localpaths.repo_root, "wpt"),
- "serve-wave",
- "--config",
- os.path.join(wpt.localpaths.repo_root, "tools/wave/tests/config.json")],
- preexec_fn=os.setsid)
-
- start = time.time()
- try:
- while True:
- if p.poll() is not None:
- assert False, "WAVE Test Runner failed: Server not running."
- if time.time() - start > 60:
- assert False, "WAVE Test Runner failed: Server did not start responding within 60s."
- try:
- resp = urlopen("http://web-platform.test:8080/_wave/api/sessions/public")
- print(resp)
- except URLError:
- print("URLError")
- time.sleep(1)
- else:
- assert resp.code == 200
- break
- finally:
- os.killpg(p.pid, 15)
- p.wait(10)
diff --git a/tools/wave/utils/deserializer.py b/tools/wave/utils/deserializer.py
index 28d1054f6ba77a..c1352a40733ab5 100644
--- a/tools/wave/utils/deserializer.py
+++ b/tools/wave/utils/deserializer.py
@@ -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 995365081db0ce..91d832f79e1830 100644
--- a/tools/wave/utils/serializer.py
+++ b/tools/wave/utils/serializer.py
@@ -23,7 +23,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/wave-cli.py b/tools/wave/wave-cli.py
new file mode 100644
index 00000000000000..3119b0feab04c9
--- /dev/null
+++ b/tools/wave/wave-cli.py
@@ -0,0 +1,122 @@
+# mypy: allow-untyped-defs
+# mypy: allow-untyped-calls
+
+import sys
+import os
+import urllib
+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(f"Unknown operation {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(f"Unknown option {argument}")
+
+ if "configuration_file_path" not in parameters:
+ configuration_file_path = os.path.join(os.getcwd(), "config.json")
+ parameters["configuration_file_path"] = configuration_file_path
+
+ return parameters
+
+
+def download_file(url, file_path):
+ response = urllib.request.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(f"Downloading {browser['name']} results ...")
+ dest_path = os.path.join(result_directory, browser["zip"])
+ download_file(browser["url"], dest_path)
+ print(" done.")
+
+ for id in REFERENCE_BROWSERS:
+ browser = REFERENCE_BROWSERS[id]
+ printt(f"Extracting {browser['name']} results ...")
+ dest_path = os.path.join(result_directory, browser["name"])
+ zip_file = zipfile.ZipFile(dest_path)
+ 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/webgl/prepare-tests.js b/tools/wave/webgl/prepare-tests.js
new file mode 100644
index 00000000000000..9ac8012ad70d67
--- /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 00000000000000..47ef89f737aa8c
--- /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 00000000000000..1bfc60bd9d81f6
--- /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 00000000000000..ee667ee83d0cc4
--- /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
+