+
diff --git a/tests/wpt/web-platform-tests/css/css-syntax/escaped-eof.html b/tests/wpt/web-platform-tests/css/css-syntax/escaped-eof.html
index 66ac8e9442e..5d47c34ac51 100644
--- a/tests/wpt/web-platform-tests/css/css-syntax/escaped-eof.html
+++ b/tests/wpt/web-platform-tests/css/css-syntax/escaped-eof.html
@@ -15,7 +15,7 @@
+
+
+
+
diff --git a/tests/wpt/web-platform-tests/css/css-values/calc-letter-spacing.html b/tests/wpt/web-platform-tests/css/css-values/calc-letter-spacing.html
new file mode 100644
index 00000000000..444785ba14c
--- /dev/null
+++ b/tests/wpt/web-platform-tests/css/css-values/calc-letter-spacing.html
@@ -0,0 +1,75 @@
+
+
+
+
+ CSS Values and Units Test: computed value of 'letter-spacing' when specified with calc() function
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/wpt/web-platform-tests/css/css-values/reference/viewport-unit-011-ref.html b/tests/wpt/web-platform-tests/css/css-values/reference/viewport-unit-011-ref.html
new file mode 100644
index 00000000000..e56c6ec8451
--- /dev/null
+++ b/tests/wpt/web-platform-tests/css/css-values/reference/viewport-unit-011-ref.html
@@ -0,0 +1,19 @@
+
+
+
+
+ CSS Values and Units Test Reference File
+
+
+
+
diff --git a/tests/wpt/web-platform-tests/css/css-values/viewport-unit-011.html b/tests/wpt/web-platform-tests/css/css-values/viewport-unit-011.html
new file mode 100644
index 00000000000..055f3d1fd2d
--- /dev/null
+++ b/tests/wpt/web-platform-tests/css/css-values/viewport-unit-011.html
@@ -0,0 +1,29 @@
+
+
+
+
+ CSS Values and Units Test: vh unit and vw unit (basic)
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/wpt/web-platform-tests/dom/events/EventListener-handleEvent.html b/tests/wpt/web-platform-tests/dom/events/EventListener-handleEvent.html
index b33b030a641..6630f273fff 100644
--- a/tests/wpt/web-platform-tests/dom/events/EventListener-handleEvent.html
+++ b/tests/wpt/web-platform-tests/dom/events/EventListener-handleEvent.html
@@ -3,40 +3,83 @@
EventListener::handleEvent()
+
-
-
-
-
Shady Grove
-
Aeolian
-
-
-
Over the river, Charlie
-
Dorian
-
-
-
+
diff --git a/tests/wpt/web-platform-tests/html/webappapis/scripting/events/event-handler-attributes-frameset-window.html b/tests/wpt/web-platform-tests/html/webappapis/scripting/events/event-handler-attributes-frameset-window.html
index ecfe90e88ea..b583eca52da 100644
--- a/tests/wpt/web-platform-tests/html/webappapis/scripting/events/event-handler-attributes-frameset-window.html
+++ b/tests/wpt/web-platform-tests/html/webappapis/scripting/events/event-handler-attributes-frameset-window.html
@@ -17,7 +17,9 @@ handlersListPromise.then(({ shadowedHandlers, notShadowedHandlers }) => {
add_completion_callback(() => {
const log_elem = document.getElementById("log");
const frame_elem = document.querySelector("frame");
- frame_elem.contentDocument.body.innerHTML = log_elem.innerHTML;
+ if (log_elem) {
+ frame_elem.contentDocument.body.innerHTML = log_elem.innerHTML;
+ }
});
done();
diff --git a/tests/wpt/web-platform-tests/interfaces/webxr.idl b/tests/wpt/web-platform-tests/interfaces/webxr.idl
index 3054500f590..63643a11b6b 100644
--- a/tests/wpt/web-platform-tests/interfaces/webxr.idl
+++ b/tests/wpt/web-platform-tests/interfaces/webxr.idl
@@ -28,12 +28,10 @@ enum XREnvironmentBlendMode {
readonly attribute XRSessionMode mode;
readonly attribute XRPresentationContext? outputContext;
readonly attribute XREnvironmentBlendMode environmentBlendMode;
-
- attribute double depthNear;
- attribute double depthFar;
- attribute XRLayer? baseLayer;
+ readonly attribute XRRenderState renderState;
// Methods
+ void updateRenderState(optional XRRenderStateInit state);
Promise requestReferenceSpace(XRReferenceSpaceOptions options);
FrozenArray getInputSources();
@@ -64,6 +62,18 @@ dictionary XRSessionCreationOptions {
XRPresentationContext? outputContext = null;
};
+dictionary XRRenderStateInit {
+ double depthNear;
+ double depthFar;
+ XRLayer? baseLayer;
+};
+
+[SecureContext, Exposed=Window] interface XRRenderState {
+ readonly attribute double depthNear;
+ readonly attribute double depthFar;
+ readonly attribute XRLayer? baseLayer;
+};
+
callback XRFrameRequestCallback = void (DOMHighResTimeStamp time, XRFrame frame);
[SecureContext, Exposed=Window] interface XRFrame {
diff --git a/tests/wpt/web-platform-tests/preload/link-header-preload-nonce.html b/tests/wpt/web-platform-tests/preload/link-header-preload-nonce.html
index 51b2224864d..240d6f11dd5 100644
--- a/tests/wpt/web-platform-tests/preload/link-header-preload-nonce.html
+++ b/tests/wpt/web-platform-tests/preload/link-header-preload-nonce.html
@@ -9,8 +9,8 @@
diff --git a/tests/wpt/web-platform-tests/preload/link-header-preload-srcset.tentative.html b/tests/wpt/web-platform-tests/preload/link-header-preload-srcset.tentative.html
index 9eb8ac4e00d..024da965796 100644
--- a/tests/wpt/web-platform-tests/preload/link-header-preload-srcset.tentative.html
+++ b/tests/wpt/web-platform-tests/preload/link-header-preload-srcset.tentative.html
@@ -11,16 +11,16 @@
diff --git a/tests/wpt/web-platform-tests/preload/resources/preload_helper.js b/tests/wpt/web-platform-tests/preload/resources/preload_helper.js
index 5a23be74b8d..b2cf8323db0 100644
--- a/tests/wpt/web-platform-tests/preload/resources/preload_helper.js
+++ b/tests/wpt/web-platform-tests/preload/resources/preload_helper.js
@@ -20,3 +20,9 @@ function verifyNumberOfDownloads(url, number)
});
assert_equals(numDownloads, number, url);
}
+
+function verifyNumberOfResourceTimingEntries(url, number)
+{
+ var numEntries = performance.getEntriesByName(getAbsoluteURL(url)).length;
+ assert_equals(numEntries, number, url);
+}
diff --git a/tests/wpt/web-platform-tests/tools/ci/taskcluster-run.py b/tests/wpt/web-platform-tests/tools/ci/taskcluster-run.py
index f443903786e..2b3270c17aa 100755
--- a/tests/wpt/web-platform-tests/tools/ci/taskcluster-run.py
+++ b/tests/wpt/web-platform-tests/tools/ci/taskcluster-run.py
@@ -56,7 +56,8 @@ def main(product, commit_range, wpt_args):
"--no-pause",
"--no-restart-on-unexpected",
"--install-fonts",
- "--no-headless"
+ "--no-headless",
+ "--verify-log-full"
]
wpt_args += browser_specific_args.get(product, [])
diff --git a/tests/wpt/web-platform-tests/tools/docker/Dockerfile b/tests/wpt/web-platform-tests/tools/docker/Dockerfile
index 53564ac135c..0cb2352e5fd 100644
--- a/tests/wpt/web-platform-tests/tools/docker/Dockerfile
+++ b/tests/wpt/web-platform-tests/tools/docker/Dockerfile
@@ -66,9 +66,13 @@ WORKDIR /home/test
RUN sudo echo ""
RUN mkdir -p /home/test/artifacts
+RUN mkdir -p /home/test/bin
+
+ENV PATH="/home/test/bin:${PATH}"
WORKDIR /home/test/
COPY .bashrc /home/test/.bashrc
COPY start.sh /home/test/start.sh
+COPY retry.py /home/test/bin/retry
diff --git a/tests/wpt/web-platform-tests/tools/docker/retry.py b/tests/wpt/web-platform-tests/tools/docker/retry.py
new file mode 100755
index 00000000000..6126b781bfa
--- /dev/null
+++ b/tests/wpt/web-platform-tests/tools/docker/retry.py
@@ -0,0 +1,58 @@
+#! /usr/bin/env python
+import argparse
+import subprocess
+import time
+import sys
+
+
+def get_args():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--delay", action="store", type=float, default=3, help="Initial delay before retry, in seconds")
+ parser.add_argument("--count", action="store", type=int, default=5, help="Total number of tries")
+ parser.add_argument("--factor", action="store", type=float, default=2, help="Exponential backoff factor")
+ parser.add_argument("cmd", nargs=argparse.REMAINDER)
+ return parser
+
+
+def iter_range(n):
+ i = 0
+ while i < n:
+ yield i
+ i += 1
+
+
+def main():
+ args = get_args().parse_args()
+
+ if not args.cmd:
+ print("No command supplied")
+ sys.exit(1)
+
+ retcode = None
+
+ for n in iter_range(args.count):
+ try:
+ print("Running %s [try %d/%d]" % (" ".join(args.cmd), (n+1), args.count))
+ subprocess.check_call(args.cmd)
+ except subprocess.CalledProcessError as e:
+ retcode = e.returncode
+ else:
+ print("Command succeeded")
+ retcode = 0
+ break
+
+ if args.factor == 0:
+ wait_time = (n+1) * args.delay
+ else:
+ wait_time = args.factor**n * args.delay
+ if n < args.count - 1:
+ print("Command failed, waiting %s seconds to retry" % wait_time)
+ time.sleep(wait_time)
+ else:
+ print("Command failed, out of retries")
+
+ sys.exit(retcode)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/wpt/web-platform-tests/tools/docker/start.sh b/tests/wpt/web-platform-tests/tools/docker/start.sh
index 52a5127892c..bfc7e9960ab 100755
--- a/tests/wpt/web-platform-tests/tools/docker/start.sh
+++ b/tests/wpt/web-platform-tests/tools/docker/start.sh
@@ -26,13 +26,13 @@ git init
git remote add origin ${REMOTE}
# Initially we just fetch 50 commits in order to save several minutes of fetching
-git fetch --quiet --depth=50 --tags origin ${REF}
+retry git fetch --quiet --depth=50 --tags origin ${REF}
if [[ ! `git rev-parse --verify -q ${REVISION}` ]];
then
# But if for some reason the commit under test isn't in that range, we give in and
# fetch everything
- git fetch -q --unshallow ${REMOTE}
+ retry git fetch -q --unshallow ${REMOTE}
git rev-parse --verify ${REVISION}
fi
git checkout -b build ${REVISION}
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/browsers/firefox.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/browsers/firefox.py
index 67d5b6ec2f5..ec317b8bed2 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/browsers/firefox.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/browsers/firefox.py
@@ -82,7 +82,7 @@ def browser_kwargs(test_type, run_info_data, config, **kwargs):
"timeout_multiplier": get_timeout_multiplier(test_type,
run_info_data,
**kwargs),
- "leak_check": kwargs["leak_check"],
+ "leak_check": run_info_data["debug"] and (kwargs["leak_check"] is not False),
"asan": run_info_data.get("asan"),
"stylo_threads": kwargs["stylo_threads"],
"chaos_mode_flags": kwargs["chaos_mode_flags"],
@@ -214,6 +214,8 @@ class FirefoxBrowser(Browser):
self.lsan_dir = lsan_dir
self.lsan_allowed = None
self.lsan_max_stack_depth = None
+ self.mozleak_allowed = None
+ self.mozleak_thresholds = None
self.leak_check = leak_check
self.leak_report_file = None
self.lsan_handler = None
@@ -222,21 +224,27 @@ class FirefoxBrowser(Browser):
self.headless = headless
def settings(self, test):
- self.lsan_allowed = test.lsan_allowed
- self.lsan_max_stack_depth = test.lsan_max_stack_depth
return {"check_leaks": self.leak_check and not test.leaks,
- "lsan_allowed": test.lsan_allowed}
+ "lsan_allowed": test.lsan_allowed,
+ "lsan_max_stack_depth": test.lsan_max_stack_depth,
+ "mozleak_allowed": self.leak_check and test.mozleak_allowed,
+ "mozleak_thresholds": self.leak_check and test.mozleak_threshold}
def start(self, group_metadata=None, **kwargs):
if group_metadata is None:
group_metadata = {}
+ self.group_metadata = group_metadata
+ self.lsan_allowed = kwargs.get("lsan_allowed")
+ self.lsan_max_stack_depth = kwargs.get("lsan_max_stack_depth")
+ self.mozleak_allowed = kwargs.get("mozleak_allowed")
+ self.mozleak_thresholds = kwargs.get("mozleak_thresholds")
+
if self.marionette_port is None:
self.marionette_port = get_free_port(2828, exclude=self.used_ports)
self.used_ports.add(self.marionette_port)
if self.asan:
- print "Setting up LSAN"
self.lsan_handler = mozleak.LSANLeaks(self.logger,
scope=group_metadata.get("scope", "/"),
allowed=self.lsan_allowed,
@@ -357,21 +365,18 @@ class FirefoxBrowser(Browser):
self.logger.debug("stopped")
def process_leaks(self):
- self.logger.debug("PROCESS LEAKS %s" % self.leak_report_file)
+ self.logger.info("PROCESS LEAKS %s" % self.leak_report_file)
if self.lsan_handler:
self.lsan_handler.process()
if self.leak_report_file is not None:
mozleak.process_leak_log(
self.leak_report_file,
- leak_thresholds={
- "default": 0,
- "tab": 10000, # See dependencies of bug 1051230.
- # GMP rarely gets a log, but when it does, it leaks a little.
- "geckomediaplugin": 20000,
- },
- ignore_missing_leaks=["geckomediaplugin"],
+ leak_thresholds=self.mozleak_thresholds,
+ ignore_missing_leaks=["gmplugin"],
log=self.logger,
- stack_fixer=self.stack_fixer
+ stack_fixer=self.stack_fixer,
+ scope=self.group_metadata.get("scope"),
+ allowed=self.mozleak_allowed
)
def pid(self):
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/executormarionette.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/executormarionette.py
index d69e998e2c3..d5692af77d3 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/executormarionette.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/executors/executormarionette.py
@@ -595,6 +595,7 @@ class ExecuteAsyncScriptRun(object):
if self.protocol.is_alive:
self.result = False, ("INTERNAL-ERROR", None)
else:
+ self.logger.info("Browser not responding, setting status to CRASH")
self.result = False, ("CRASH", None)
return self.result
@@ -608,15 +609,21 @@ class ExecuteAsyncScriptRun(object):
# This can happen on a crash
# Also, should check after the test if the firefox process is still running
# and otherwise ignore any other result and set it to crash
+ self.logger.info("IOError on command, setting status to CRASH")
+ self.result = False, ("CRASH", None)
+ except errors.NoSuchWindowException:
+ self.logger.info("NoSuchWindowException on command, setting status to CRASH")
self.result = False, ("CRASH", None)
except Exception as e:
- message = getattr(e, "message", "")
- if message:
- message += "\n"
- message += traceback.format_exc(e)
- self.logger.warning(message)
- self.result = False, ("INTERNAL-ERROR", None)
-
+ if isinstance(e, errors.JavascriptException) and e.message.startswith("Document was unloaded"):
+ message = "Document unloaded; maybe test navigated the top-level-browsing context?"
+ else:
+ message = getattr(e, "message", "")
+ if message:
+ message += "\n"
+ message += traceback.format_exc(e)
+ self.logger.warning(traceback.format_exc())
+ self.result = False, ("INTERNAL-ERROR", message)
finally:
self.result_flag.set()
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/formatters.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/formatters.py
index 0a54624b7b4..284868522ea 100755
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/formatters.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/formatters.py
@@ -123,3 +123,32 @@ class WptreportFormatter(BaseFormatter):
"min": data["min_expected"],
"max": data["max_expected"]
}
+
+ def lsan_leak(self, data):
+ if "lsan_leaks" not in self.results:
+ self.results["lsan_leaks"] = []
+ lsan_leaks = self.results["lsan_leaks"]
+ lsan_leaks.append({"frames": data["frames"],
+ "scope": data["scope"],
+ "allowed_match": data.get("allowed_match")})
+
+ def find_or_create_mozleak(self, data):
+ if "mozleak" not in self.results:
+ self.results["mozleak"] = {}
+ scope = data["scope"]
+ if scope not in self.results["mozleak"]:
+ self.results["mozleak"][scope] = {"objects": [], "total": []}
+ return self.results["mozleak"][scope]
+
+ def mozleak_object(self, data):
+ scope_data = self.find_or_create_mozleak(data)
+ scope_data["objects"].append({"process": data["process"],
+ "name": data["name"],
+ "allowed": data.get("allowed", False),
+ "bytes": data["bytes"]})
+
+ def mozleak_total(self, data):
+ scope_data = self.find_or_create_mozleak(data)
+ scope_data["total"].append({"bytes": data["bytes"],
+ "threshold": data.get("threshold", 0),
+ "process": data["process"]})
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/manifestexpected.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/manifestexpected.py
index 7ecef4b0b24..f4fddc7a2a1 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/manifestexpected.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/manifestexpected.py
@@ -70,9 +70,9 @@ def prefs(node):
return rv
-def lsan_allowed(node):
+def set_prop(name, node):
try:
- node_items = node.get("lsan-allowed")
+ node_items = node.get(name)
if isinstance(node_items, (str, unicode)):
rv = {node_items}
else:
@@ -82,6 +82,20 @@ def lsan_allowed(node):
return rv
+def leak_threshold(node):
+ rv = {}
+ try:
+ node_items = node.get("leak-threshold")
+ if isinstance(node_items, (str, unicode)):
+ node_items = [node_items]
+ for item in node_items:
+ process, value = item.rsplit(":", 1)
+ rv[process.strip()] = int(value.strip())
+ except KeyError:
+ pass
+ return rv
+
+
class ExpectedManifest(ManifestItem):
def __init__(self, name, test_path, url_base):
"""Object representing all the tests in a particular manifest
@@ -154,7 +168,15 @@ class ExpectedManifest(ManifestItem):
@property
def lsan_allowed(self):
- return lsan_allowed(self)
+ return set_prop("lsan-allowed", self)
+
+ @property
+ def leak_allowed(self):
+ return set_prop("leak-allowed", self)
+
+ @property
+ def leak_threshold(self):
+ return leak_threshold(self)
@property
def lsan_max_stack_depth(self):
@@ -192,7 +214,15 @@ class DirectoryManifest(ManifestItem):
@property
def lsan_allowed(self):
- return lsan_allowed(self)
+ return set_prop("lsan-allowed", self)
+
+ @property
+ def leak_allowed(self):
+ return set_prop("leak-allowed", self)
+
+ @property
+ def leak_threshold(self):
+ return leak_threshold(self)
@property
def lsan_max_stack_depth(self):
@@ -256,7 +286,15 @@ class TestNode(ManifestItem):
@property
def lsan_allowed(self):
- return lsan_allowed(self)
+ return set_prop("lsan-allowed", self)
+
+ @property
+ def leak_allowed(self):
+ return set_prop("leak-allowed", self)
+
+ @property
+ def leak_threshold(self):
+ return leak_threshold(self)
@property
def lsan_max_stack_depth(self):
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/manifestupdate.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/manifestupdate.py
index 1f8ba77e8f9..90bcd593ede 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/manifestupdate.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/manifestupdate.py
@@ -2,6 +2,7 @@ import itertools
import os
import urlparse
from collections import namedtuple, defaultdict
+from math import ceil
from wptmanifest.node import (DataNode, ConditionalNode, BinaryExpressionNode,
BinaryOperatorNode, VariableNode, StringNode, NumberNode,
@@ -81,6 +82,8 @@ class ExpectedManifest(ManifestItem):
self.property_order = property_order
self.update_properties = {
"lsan": LsanUpdate(self),
+ "leak-object": LeakObjectUpdate(self),
+ "leak-threshold": LeakThresholdUpdate(self),
}
def append(self, child):
@@ -122,6 +125,24 @@ class ExpectedManifest(ManifestItem):
self.update_properties["lsan"].set(run_info, result)
+ def set_leak_object(self, run_info, result):
+ """Set the result of the test in a particular run
+
+ :param run_info: Dictionary of run_info parameters corresponding
+ to this run
+ :param result: Leaked objects deletec"""
+
+ self.update_properties["leak-object"].set(run_info, result)
+
+ def set_leak_threshold(self, run_info, result):
+ """Set the result of the test in a particular run
+
+ :param run_info: Dictionary of run_info parameters corresponding
+ to this run
+ :param result: Total number of bytes leaked"""
+
+ self.update_properties["leak-threshold"].set(run_info, result)
+
def coalesce_properties(self, stability):
for prop_update in self.update_properties.itervalues():
prop_update.coalesce(stability)
@@ -432,6 +453,10 @@ class ExpectedUpdate(PropertyUpdate):
class MaxAssertsUpdate(PropertyUpdate):
+ """For asserts we always update the default value and never add new conditionals.
+ The value we set as the default is the maximum the current default or one more than the
+ number of asserts we saw in any configuration."""
+
property_name = "max-asserts"
cls_default_value = 0
value_type = int
@@ -447,9 +472,6 @@ class MaxAssertsUpdate(PropertyUpdate):
return old_value
def update_default(self):
- """For asserts we always update the default value and never add new conditionals.
- The value we set as the default is the maximum the current default or one more than the
- number of asserts we saw in any configuration."""
# Current values
values = []
current_default = None
@@ -501,20 +523,11 @@ class MinAssertsUpdate(PropertyUpdate):
return True, new_value
-class LsanUpdate(PropertyUpdate):
- property_name = "lsan-allowed"
+class AppendOnlyListUpdate(PropertyUpdate):
cls_default_value = None
def get_value(self, result):
- # If we have an allowed_match that matched, return None
- # This value is ignored later (because it matches the default)
- # We do that because then if we allow a failure in foo/__dir__.ini
- # we don't want to update foo/bar/__dir__.ini with the same rule
- if result[1]:
- return None
- # Otherwise return the topmost stack frame
- # TODO: there is probably some improvement to be made by looking for a "better" stack frame
- return result[0][0]
+ raise NotImplementedError
def update_value(self, old_value, new_value):
if isinstance(new_value, (str, unicode)):
@@ -539,6 +552,96 @@ class LsanUpdate(PropertyUpdate):
return True, new_value if new_value else None
+class LsanUpdate(AppendOnlyListUpdate):
+ property_name = "lsan-allowed"
+
+ def get_value(self, result):
+ # If we have an allowed_match that matched, return None
+ # This value is ignored later (because it matches the default)
+ # We do that because then if we allow a failure in foo/__dir__.ini
+ # we don't want to update foo/bar/__dir__.ini with the same rule
+ if result[1]:
+ return None
+ # Otherwise return the topmost stack frame
+ # TODO: there is probably some improvement to be made by looking for a "better" stack frame
+ return result[0][0]
+
+
+class LeakObjectUpdate(AppendOnlyListUpdate):
+ property_name = "leak-allowed"
+
+ def get_value(self, result):
+ # If we have an allowed_match that matched, return None
+ if result[1]:
+ return None
+ # Otherwise return the process/object name
+ return result[0]
+
+
+class LeakThresholdUpdate(PropertyUpdate):
+ property_name = "leak-threshold"
+ cls_default_value = []
+
+ def __init__(self, node):
+ PropertyUpdate.__init__(self, node)
+ self.thresholds = {}
+
+ def get_value(self, value):
+ threshold = value[2]
+ key = value[0]
+ self.thresholds[key] = threshold
+ return value[:2]
+
+ def value_type(self, data):
+ if all(isinstance(item, tuple) for item in data):
+ return data
+ values = [item.rsplit(":", 1) for item in data]
+ return [(key, int(float(value))) for key, value in values]
+
+ def update_value(self, old_value, new_value, allow_buffer=True):
+ rv = []
+ old_value = dict(old_value)
+ new_value = dict(self.value_type(new_value))
+ for key in set(new_value.keys()) | set(old_value.keys()):
+ old = old_value.get(key, 0)
+ new = new_value.get(key, 0)
+ threshold = self.thresholds.get(key, 0)
+ # If the value is less than the threshold but there isn't
+ # an old value we must have inherited the threshold from
+ # a parent ini file so don't any anything to this one
+ if not old and new < threshold:
+ continue
+ if old >= new:
+ updated = old
+ else:
+ if allow_buffer:
+ # Round up to nearest 50 kb
+ boundary = 50 * 1024
+ updated = int(boundary * ceil(float(new) / boundary))
+ else:
+ updated = new
+ rv.append((key, updated))
+ return ["%s:%s" % item for item in sorted(rv)]
+
+ def update_default(self):
+ # Current values
+ current_default = []
+ if self.property_name in self.node._data:
+ current_default = [item for item in
+ self.node._data[self.property_name]
+ if item.condition_node is None]
+ current_default = current_default[0].value_as(self.value_type)
+ max_new = {}
+ for item in self.new:
+ key, value = item.value
+ if value > max_new.get(key, 0):
+ max_new[key] = value
+ new_value = self.update_value(current_default,
+ max_new.items(),
+ allow_buffer=False)
+ return True, new_value
+
+
def group_conditionals(values, property_order=None, boolean_properties=None):
"""Given a list of Value objects, return a list of
(conditional_node, status) pairs representing the conditional
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/metadata.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/metadata.py
index b170fc66316..26dc71e64e4 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/metadata.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/metadata.py
@@ -289,7 +289,9 @@ class ExpectedUpdater(object):
"test_status": self.test_status,
"test_end": self.test_end,
"assertion_count": self.assertion_count,
- "lsan_leak": self.lsan_leak}
+ "lsan_leak": self.lsan_leak,
+ "mozleak_object": self.mozleak_object,
+ "mozleak_total": self.mozleak_total}
self.tests_visited = {}
def update_from_log(self, log_file):
@@ -340,6 +342,15 @@ class ExpectedUpdater(object):
for item in data.get("lsan_leaks", []):
action_map["lsan_leak"](item)
+ mozleak_data = data.get("mozleak", {})
+ for scope, scope_data in mozleak_data.iteritems():
+ for key, action in [("objects", "mozleak_object"),
+ ("total", "mozleak_total")]:
+ for item in scope_data.get(key, []):
+ item_data = {"scope": scope}
+ item_data.update(item)
+ action_map[action](item_data)
+
def suite_start(self, data):
self.run_info = run_info_intern.store(data["run_info"])
@@ -397,17 +408,36 @@ class ExpectedUpdater(object):
if data["count"] < data["min_expected"] or data["count"] > data["max_expected"]:
test_data.set_requires_update()
- def lsan_leak(self, data):
+ def test_for_scope(self, data):
dir_path = data.get("scope", "/")
dir_id = intern(os.path.join(dir_path, "__dir__").replace(os.path.sep, "/").encode("utf8"))
if dir_id.startswith("/"):
dir_id = dir_id[1:]
- test_data = self.id_test_map[dir_id]
+ return dir_id, self.id_test_map[dir_id]
+
+ def lsan_leak(self, data):
+ dir_id, test_data = self.test_for_scope(data)
test_data.set(dir_id, None, "lsan",
self.run_info, (data["frames"], data.get("allowed_match")))
if not data.get("allowed_match"):
test_data.set_requires_update()
+ def mozleak_object(self, data):
+ dir_id, test_data = self.test_for_scope(data)
+ test_data.set(dir_id, None, "leak-object",
+ self.run_info, ("%s:%s", (data["process"], data["name"]),
+ data.get("allowed")))
+ if not data.get("allowed"):
+ test_data.set_requires_update()
+
+ def mozleak_total(self, data):
+ if data["bytes"]:
+ dir_id, test_data = self.test_for_scope(data)
+ test_data.set(dir_id, None, "leak-threshold",
+ self.run_info, (data["process"], data["bytes"], data["threshold"]))
+ if data["bytes"] > data["threshold"] or data["bytes"] < 0:
+ test_data.set_requires_update()
+
def create_test_tree(metadata_path, test_manifest):
"""Create a map of test_id to TestFileData for that test.
@@ -552,6 +582,10 @@ class TestFileData(object):
if subtest_id is None and test_id.endswith("__dir__"):
if prop == "lsan":
expected.set_lsan(run_info, value)
+ elif prop == "leak-object":
+ expected.set_leak_object(run_info, value)
+ elif prop == "leak-threshold":
+ expected.set_leak_threshold(run_info, value)
continue
if prop == "status":
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/tests/test_update.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/tests/test_update.py
index 36facdc9520..032ac82dff6 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/tests/test_update.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/tests/test_update.py
@@ -606,3 +606,98 @@ def test_update_wptreport_1():
assert len(updated) == 1
assert updated[0][1].get("lsan-allowed") == ["baz"]
+
+
+def test_update_leak_total_0():
+ test_id = "/path/to/test.htm"
+ dir_id = "path/to/__dir__"
+ tests = [("path/to/test.htm", [test_id], "testharness", ""),
+ ("path/to/__dir__", [dir_id], None, "")]
+
+ log_0 = suite_log([("mozleak_total", {"scope": "path/to/",
+ "process": "default",
+ "bytes": 100,
+ "threshold": 0,
+ "objects": []})])
+
+ updated = update(tests, log_0)
+ new_manifest = updated[0][1]
+
+ assert not new_manifest.is_empty
+ assert new_manifest.get("leak-threshold") == ['default:51200']
+
+
+def test_update_leak_total_1():
+ test_id = "/path/to/test.htm"
+ dir_id = "path/to/__dir__"
+ tests = [("path/to/test.htm", [test_id], "testharness", ""),
+ ("path/to/__dir__", [dir_id], None, "")]
+
+ log_0 = suite_log([("mozleak_total", {"scope": "path/to/",
+ "process": "default",
+ "bytes": 100,
+ "threshold": 1000,
+ "objects": []})])
+
+ updated = update(tests, log_0)
+ assert not updated
+
+
+def test_update_leak_total_2():
+ test_id = "/path/to/test.htm"
+ dir_id = "path/to/__dir__"
+ tests = [("path/to/test.htm", [test_id], "testharness", ""),
+ ("path/to/__dir__", [dir_id], None, """
+leak-total: 110""")]
+
+ log_0 = suite_log([("mozleak_total", {"scope": "path/to/",
+ "process": "default",
+ "bytes": 100,
+ "threshold": 110,
+ "objects": []})])
+
+ updated = update(tests, log_0)
+ assert not updated
+
+
+def test_update_leak_total_3():
+ test_id = "/path/to/test.htm"
+ dir_id = "path/to/__dir__"
+ tests = [("path/to/test.htm", [test_id], "testharness", ""),
+ ("path/to/__dir__", [dir_id], None, """
+leak-total: 100""")]
+
+ log_0 = suite_log([("mozleak_total", {"scope": "path/to/",
+ "process": "default",
+ "bytes": 1000,
+ "threshold": 100,
+ "objects": []})])
+
+ updated = update(tests, log_0)
+ new_manifest = updated[0][1]
+
+ assert not new_manifest.is_empty
+ assert new_manifest.get("leak-threshold") == ['default:51200']
+
+
+def test_update_leak_total_4():
+ test_id = "/path/to/test.htm"
+ dir_id = "path/to/__dir__"
+ tests = [("path/to/test.htm", [test_id], "testharness", ""),
+ ("path/to/__dir__", [dir_id], None, """
+leak-total: 110""")]
+
+ log_0 = suite_log([
+ ("lsan_leak", {"scope": "path/to/",
+ "frames": ["foo", "bar"]}),
+ ("mozleak_total", {"scope": "path/to/",
+ "process": "default",
+ "bytes": 100,
+ "threshold": 110,
+ "objects": []})])
+
+ updated = update(tests, log_0)
+ new_manifest = updated[0][1]
+
+ assert not new_manifest.is_empty
+ assert new_manifest.has_key("leak-threshold") is False
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptcommandline.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptcommandline.py
index 0a3a8c2b6f7..1d6909daaca 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptcommandline.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wptcommandline.py
@@ -261,8 +261,11 @@ scheme host and port.""")
gecko_group.add_argument("--setpref", dest="extra_prefs", action='append',
default=[], metavar="PREF=VALUE",
help="Defines an extra user preference (overrides those in prefs_root)")
- gecko_group.add_argument("--leak-check", dest="leak_check", action="store_true",
- help="Enable leak checking")
+ gecko_group.add_argument("--leak-check", dest="leak_check", action="store_true", default=None,
+ help="Enable leak checking (enabled by default for debug builds, "
+ "silently ignored for opt)")
+ gecko_group.add_argument("--no-leak-check", dest="leak_check", action="store_false", default=None,
+ help="Disable leak checking")
gecko_group.add_argument("--stylo-threads", action="store", type=int, default=1,
help="Number of parallel threads to use for stylo")
gecko_group.add_argument("--reftest-internal", dest="reftest_internal", action="store_true",
@@ -270,7 +273,7 @@ scheme host and port.""")
gecko_group.add_argument("--reftest-external", dest="reftest_internal", action="store_false",
help="Disable reftest runner implemented inside Marionette")
gecko_group.add_argument("--reftest-screenshot", dest="reftest_screenshot", action="store",
- choices=["always", "fail", "unexpected"], default="unexpected",
+ choices=["always", "fail", "unexpected"], default=None,
help="With --reftest-internal, when to take a screenshot")
gecko_group.add_argument("--chaos", dest="chaos_mode_flags", action="store",
nargs="?", const=0xFFFFFFFF, type=int,
@@ -532,6 +535,9 @@ def check_args(kwargs):
if kwargs["lsan_dir"] is None:
kwargs["lsan_dir"] = kwargs["prefs_root"]
+ if kwargs["reftest_screenshot"] is None:
+ kwargs["reftest_screenshot"] = "unexpected"
+
return kwargs
diff --git a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wpttest.py b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wpttest.py
index 767a25de8c1..7fa39be4de1 100644
--- a/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wpttest.py
+++ b/tests/wpt/web-platform-tests/tools/wptrunner/wptrunner/wpttest.py
@@ -242,6 +242,26 @@ class Test(object):
return depth
return None
+ @property
+ def mozleak_allowed(self):
+ mozleak_allowed = set()
+ for meta in self.itermeta():
+ mozleak_allowed |= meta.leak_allowed
+ if atom_reset in mozleak_allowed:
+ mozleak_allowed.remove(atom_reset)
+ break
+ return mozleak_allowed
+
+ @property
+ def mozleak_threshold(self):
+ rv = {}
+ for meta in self.itermeta(None):
+ threshold = meta.leak_threshold
+ for key, value in threshold.iteritems():
+ if key not in rv:
+ rv[key] = value
+ return rv
+
@property
def tags(self):
tags = set()
diff --git a/tests/wpt/web-platform-tests/wasm/jsapi/wasm-constants.js b/tests/wpt/web-platform-tests/wasm/jsapi/wasm-constants.js
index 18748e6cf54..e3846386b8d 100644
--- a/tests/wpt/web-platform-tests/wasm/jsapi/wasm-constants.js
+++ b/tests/wpt/web-platform-tests/wasm/jsapi/wasm-constants.js
@@ -28,6 +28,7 @@ var kWasmV3 = 0;
var kHeaderSize = 8;
var kPageSize = 65536;
+var kSpecMaxPages = 65535;
function bytesWithHeader() {
var buffer = new ArrayBuffer(kHeaderSize + arguments.length);
@@ -52,18 +53,17 @@ let kDeclNoLocals = 0;
// Section declaration constants
let kUnknownSectionCode = 0;
-let kTypeSectionCode = 1; // Function signature declarations
-let kImportSectionCode = 2; // Import declarations
-let kFunctionSectionCode = 3; // Function declarations
-let kTableSectionCode = 4; // Indirect function table and other tables
-let kMemorySectionCode = 5; // Memory attributes
-let kGlobalSectionCode = 6; // Global declarations
-let kExportSectionCode = 7; // Exports
-let kStartSectionCode = 8; // Start function declaration
-let kElementSectionCode = 9; // Elements section
-let kCodeSectionCode = 10; // Function code
-let kDataSectionCode = 11; // Data segments
-let kNameSectionCode = 12; // Name section (encoded as string)
+let kTypeSectionCode = 1; // Function signature declarations
+let kImportSectionCode = 2; // Import declarations
+let kFunctionSectionCode = 3; // Function declarations
+let kTableSectionCode = 4; // Indirect function table and other tables
+let kMemorySectionCode = 5; // Memory attributes
+let kGlobalSectionCode = 6; // Global declarations
+let kExportSectionCode = 7; // Exports
+let kStartSectionCode = 8; // Start function declaration
+let kElementSectionCode = 9; // Elements section
+let kCodeSectionCode = 10; // Function code
+let kDataSectionCode = 11; // Data segments
// Name section types
let kModuleNameCode = 0;
@@ -74,6 +74,7 @@ let kWasmFunctionTypeForm = 0x60;
let kWasmAnyFunctionTypeForm = 0x70;
let kHasMaximumFlag = 1;
+let kResizableMaximumFlag = 1;
// Function declaration flags
let kDeclFunctionName = 0x01;
@@ -87,6 +88,7 @@ let kWasmI32 = 0x7f;
let kWasmI64 = 0x7e;
let kWasmF32 = 0x7d;
let kWasmF64 = 0x7c;
+let kWasmS128 = 0x7b;
let kExternalFunction = 0;
let kExternalTable = 1;
@@ -102,6 +104,8 @@ let kSig_l_l = makeSig([kWasmI64], [kWasmI64]);
let kSig_i_l = makeSig([kWasmI64], [kWasmI32]);
let kSig_i_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32]);
let kSig_i_iii = makeSig([kWasmI32, kWasmI32, kWasmI32], [kWasmI32]);
+let kSig_v_iiii = makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32], []);
+let kSig_f_ff = makeSig([kWasmF32, kWasmF32], [kWasmF32]);
let kSig_d_dd = makeSig([kWasmF64, kWasmF64], [kWasmF64]);
let kSig_l_ll = makeSig([kWasmI64, kWasmI64], [kWasmI64]);
let kSig_i_dd = makeSig([kWasmF64, kWasmF64], [kWasmI32]);
@@ -118,6 +122,11 @@ let kSig_v_d = makeSig([kWasmF64], []);
let kSig_v_dd = makeSig([kWasmF64, kWasmF64], []);
let kSig_v_ddi = makeSig([kWasmF64, kWasmF64, kWasmI32], []);
+let kSig_v_f = makeSig([kWasmF32], []);
+let kSig_f_f = makeSig([kWasmF32], [kWasmF32]);
+let kSig_f_d = makeSig([kWasmF64], [kWasmF32]);
+let kSig_d_d = makeSig([kWasmF64], [kWasmF64]);
+
function makeSig(params, results) {
return {params: params, results: results};
}
@@ -357,19 +366,19 @@ function assertTraps(trap, code) {
throw new MjsUnitAssertionError('Did not trap, expected: ' + kTrapMsgs[trap]);
}
-function assertWasmThrows(value, code) {
- assertEquals('number', typeof value);
- try {
- if (typeof code === 'function') {
- code();
- } else {
- eval(code);
- }
- } catch (e) {
- assertEquals('number', typeof e);
- assertEquals(value, e);
- // Success.
- return;
+function wasmI32Const(val) {
+ let bytes = [kExprI32Const];
+ for (let i = 0; i < 4; ++i) {
+ bytes.push(0x80 | ((val >> (7 * i)) & 0x7f));
}
- throw new MjsUnitAssertionError('Did not throw, expected: ' + value);
+ bytes.push((val >> (7 * 4)) & 0x7f);
+ return bytes;
+}
+
+function wasmF32Const(f) {
+ return [kExprF32Const].concat(Array.from(new Uint8Array((new Float32Array([f])).buffer)));
+}
+
+function wasmF64Const(f) {
+ return [kExprF64Const].concat(Array.from(new Uint8Array((new Float64Array([f])).buffer)));
}
diff --git a/tests/wpt/web-platform-tests/wasm/jsapi/wasm-module-builder.js b/tests/wpt/web-platform-tests/wasm/jsapi/wasm-module-builder.js
index c01b71733d6..e13f4157873 100644
--- a/tests/wpt/web-platform-tests/wasm/jsapi/wasm-module-builder.js
+++ b/tests/wpt/web-platform-tests/wasm/jsapi/wasm-module-builder.js
@@ -69,14 +69,13 @@ class Binary extends Array {
// Emit section name.
this.emit_u8(section_code);
// Emit the section to a temporary buffer: its full length isn't know yet.
- const section = new Binary;
+ let section = new Binary;
content_generator(section);
// Emit section length.
this.emit_u32v(section.length);
// Copy the temporary buffer.
- for (const b of section) {
- this.push(b);
- }
+ // Avoid spread because {section} can be huge.
+ for (let b of section) this.push(b);
}
}
@@ -88,15 +87,6 @@ class WasmFunctionBuilder {
this.body = [];
}
- numLocalNames() {
- if (this.local_names === undefined) return 0;
- let num_local_names = 0;
- for (let loc_name of this.local_names) {
- if (loc_name !== undefined) ++num_local_names;
- }
- return num_local_names;
- }
-
exportAs(name) {
this.module.addExport(name, this.index);
return this;
@@ -108,40 +98,12 @@ class WasmFunctionBuilder {
}
addBody(body) {
- for (let b of body) {
- if (typeof b !== 'number' || (b & (~0xFF)) !== 0 )
- throw new Error('invalid body (entries must be 8 bit numbers): ' + body);
- }
- this.body = body.slice();
- // Automatically add the end for the function block to the body.
- this.body.push(kExprEnd);
- return this;
- }
-
- addBodyWithEnd(body) {
this.body = body;
return this;
}
- getNumLocals() {
- let total_locals = 0;
- for (let l of this.locals || []) {
- for (let type of ["i32", "i64", "f32", "f64"]) {
- total_locals += l[type + "_count"] || 0;
- }
- }
- return total_locals;
- }
-
- addLocals(locals, names) {
- const old_num_locals = this.getNumLocals();
- if (!this.locals) this.locals = []
- this.locals.push(locals);
- if (names) {
- if (!this.local_names) this.local_names = [];
- const missing_names = old_num_locals - this.local_names.length;
- this.local_names.push(...new Array(missing_names), ...names);
- }
+ addLocals(locals) {
+ this.locals = locals;
return this;
}
@@ -176,6 +138,7 @@ class WasmModuleBuilder {
this.table_length_max = undefined;
this.element_segments = [];
this.data_segments = [];
+ this.segments = [];
this.explicit = [];
this.num_imported_funcs = 0;
this.num_imported_globals = 0;
@@ -197,22 +160,6 @@ class WasmModuleBuilder {
return this;
}
- stringToBytes(name) {
- var result = new Binary();
- result.emit_u32v(name.length);
- for (var i = 0; i < name.length; i++) {
- result.emit_u8(name.charCodeAt(i));
- }
- return result;
- }
-
- addCustomSection(name, bytes) {
- name = this.stringToBytes(name);
- var length = new Binary();
- length.emit_u32v(name.length + bytes.length);
- this.explicit.push([0, ...length, ...name, ...bytes]);
- }
-
addType(type) {
// TODO: canonicalize types?
this.types.push(type);
@@ -235,21 +182,15 @@ class WasmModuleBuilder {
}
addImport(module = "", name, type) {
- if (this.functions.length != 0) {
- throw new Error('Imported functions must be declared before local ones');
- }
let type_index = (typeof type) == "number" ? type : this.addType(type);
this.imports.push({module: module, name: name, kind: kExternalFunction,
type: type_index});
return this.num_imported_funcs++;
}
- addImportedGlobal(module = "", name, type, mutable = false) {
- if (this.globals.length != 0) {
- throw new Error('Imported globals must be declared before local ones');
- }
+ addImportedGlobal(module = "", name, type) {
let o = {module: module, name: name, kind: kExternalGlobal, type: type,
- mutable: mutable};
+ mutable: false}
this.imports.push(o);
return this.num_imported_globals++;
}
@@ -278,7 +219,8 @@ class WasmModuleBuilder {
}
addDataSegment(addr, data, is_global = false) {
- this.data_segments.push({addr: addr, data: data, is_global: is_global});
+ this.data_segments.push(
+ {addr: addr, data: data, is_global: is_global});
return this.data_segments.length - 1;
}
@@ -309,21 +251,15 @@ class WasmModuleBuilder {
return this.addElementSegment(this.table_length_min, false, array);
}
- setTableBounds(min, max) {
+ setTableBounds(min, max = undefined) {
this.table_length_min = min;
this.table_length_max = max;
return this;
}
- setTableLength(length) {
- this.table_length_min = length;
- this.table_length_max = length;
- return this;
- }
-
- setName(name) {
- this.name = name;
- return this;
+ // TODO(ssauleau): legacy, remove this
+ setFunctionTableLength(length) {
+ return this.setTableBounds(length);
}
toArray(debug = false) {
@@ -385,11 +321,15 @@ class WasmModuleBuilder {
}
// Add functions declarations
+ let has_names = false;
+ let names = false;
if (wasm.functions.length > 0) {
if (debug) print("emitting function decls @ " + binary.length);
binary.emit_section(kFunctionSectionCode, section => {
section.emit_u32v(wasm.functions.length);
for (let func of wasm.functions) {
+ has_names = has_names || (func.name != undefined &&
+ func.name.length > 0);
section.emit_u32v(func.type_index);
}
});
@@ -415,7 +355,7 @@ class WasmModuleBuilder {
binary.emit_section(kMemorySectionCode, section => {
section.emit_u8(1); // one memory entry
const has_max = wasm.memory.max !== undefined;
- section.emit_u8(has_max ? kHasMaximumFlag : 0);
+ section.emit_u8(has_max ? 1 : 0);
section.emit_u32v(wasm.memory.min);
if (has_max) section.emit_u32v(wasm.memory.max);
});
@@ -438,7 +378,7 @@ class WasmModuleBuilder {
break;
case kWasmI64:
section.emit_u8(kExprI64Const);
- section.emit_u32v(global.init);
+ section.emit_u8(global.init);
break;
case kWasmF32:
section.emit_u8(kExprF32Const);
@@ -492,7 +432,7 @@ class WasmModuleBuilder {
}
// Add start function section.
- if (wasm.start_index !== undefined) {
+ if (wasm.start_index != undefined) {
if (debug) print("emitting start function @ " + binary.length);
binary.emit_section(kStartSectionCode, section => {
section.emit_u32v(wasm.start_index);
@@ -507,7 +447,7 @@ class WasmModuleBuilder {
section.emit_u32v(inits.length);
for (let init of inits) {
- section.emit_u8(0); // table index
+ section.emit_u8(0); // table index / flags
if (init.is_global) {
section.emit_u8(kExprGetGlobal);
} else {
@@ -532,7 +472,9 @@ class WasmModuleBuilder {
for (let func of wasm.functions) {
// Function body length will be patched later.
let local_decls = [];
- for (let l of func.locals || []) {
+ let l = func.locals;
+ if (l != undefined) {
+ let local_decls_count = 0;
if (l.i32_count > 0) {
local_decls.push({count: l.i32_count, type: kWasmI32});
}
@@ -567,7 +509,7 @@ class WasmModuleBuilder {
binary.emit_section(kDataSectionCode, section => {
section.emit_u32v(wasm.data_segments.length);
for (let seg of wasm.data_segments) {
- section.emit_u8(0); // linear memory index 0
+ section.emit_u8(0); // linear memory index 0 / flags
if (seg.is_global) {
// initializer is a global variable
section.emit_u8(kExprGetGlobal);
@@ -590,50 +532,21 @@ class WasmModuleBuilder {
binary.emit_bytes(exp);
}
- // Add names.
- let num_function_names = 0;
- let num_functions_with_local_names = 0;
- for (let func of wasm.functions) {
- if (func.name !== undefined) ++num_function_names;
- if (func.numLocalNames() > 0) ++num_functions_with_local_names;
- }
- if (num_function_names > 0 || num_functions_with_local_names > 0 ||
- wasm.name !== undefined) {
- if (debug) print('emitting names @ ' + binary.length);
+ // Add function names.
+ if (has_names) {
+ if (debug) print("emitting names @ " + binary.length);
binary.emit_section(kUnknownSectionCode, section => {
- section.emit_string('name');
- // Emit module name.
- if (wasm.name !== undefined) {
- section.emit_section(kModuleNameCode, name_section => {
- name_section.emit_string(wasm.name);
- });
+ section.emit_string("name");
+ var count = wasm.functions.length + wasm.num_imported_funcs;
+ section.emit_u32v(count);
+ for (var i = 0; i < wasm.num_imported_funcs; i++) {
+ section.emit_u8(0); // empty string
+ section.emit_u8(0); // local names count == 0
}
- // Emit function names.
- if (num_function_names > 0) {
- section.emit_section(kFunctionNamesCode, name_section => {
- name_section.emit_u32v(num_function_names);
- for (let func of wasm.functions) {
- if (func.name === undefined) continue;
- name_section.emit_u32v(func.index);
- name_section.emit_string(func.name);
- }
- });
- }
- // Emit local names.
- if (num_functions_with_local_names > 0) {
- section.emit_section(kLocalNamesCode, name_section => {
- name_section.emit_u32v(num_functions_with_local_names);
- for (let func of wasm.functions) {
- if (func.numLocalNames() == 0) continue;
- name_section.emit_u32v(func.index);
- name_section.emit_u32v(func.numLocalNames());
- for (let i = 0; i < func.local_names.length; ++i) {
- if (func.local_names[i] === undefined) continue;
- name_section.emit_u32v(i);
- name_section.emit_string(func.local_names[i]);
- }
- }
- });
+ for (let func of wasm.functions) {
+ var name = func.name == undefined ? "" : func.name;
+ section.emit_string(name);
+ section.emit_u8(0); // local names count == 0
}
});
}
@@ -650,21 +563,12 @@ class WasmModuleBuilder {
if ((typeof val) == "string") val = val.charCodeAt(0);
view[i] = val | 0;
}
- return buffer;
+ return new Uint8Array(buffer);
}
- instantiate(ffi) {
+ instantiate(...args) {
let module = new WebAssembly.Module(this.toBuffer());
- let instance = new WebAssembly.Instance(module, ffi);
+ let instance = new WebAssembly.Instance(module, ...args);
return instance;
}
-
- asyncInstantiate(ffi) {
- return WebAssembly.instantiate(this.toBuffer(), ffi)
- .then(({module, instance}) => instance);
- }
-
- toModule(debug = false) {
- return new WebAssembly.Module(this.toBuffer(debug));
- }
}
diff --git a/tests/wpt/web-platform-tests/webaudio/resources/biquad-testing.js b/tests/wpt/web-platform-tests/webaudio/resources/biquad-testing.js
index 7a0b6e6c1f8..7f90a1f72be 100644
--- a/tests/wpt/web-platform-tests/webaudio/resources/biquad-testing.js
+++ b/tests/wpt/web-platform-tests/webaudio/resources/biquad-testing.js
@@ -5,15 +5,16 @@ let signal;
let renderedBuffer;
let renderedData;
-let sampleRate = 44100.0;
+// Use a power of two to eliminate round-off in converting frame to time
+let sampleRate = 32768;
let pulseLengthFrames = .1 * sampleRate;
// Maximum allowed error for the test to succeed. Experimentally determined.
let maxAllowedError = 5.9e-8;
-// This must be large enough so that the filtered result is
-// essentially zero. See comments for createTestAndRun.
-let timeStep = .1;
+// This must be large enough so that the filtered result is essentially zero.
+// See comments for createTestAndRun. This must be a whole number of frames.
+let timeStep = Math.ceil(.1 * sampleRate) / sampleRate;
// Maximum number of filters we can process (mostly for setting the
// render length correctly.)
diff --git a/tests/wpt/web-platform-tests/webaudio/resources/distance-model-testing.js b/tests/wpt/web-platform-tests/webaudio/resources/distance-model-testing.js
index 1b9adde403e..f8a6cf940a9 100644
--- a/tests/wpt/web-platform-tests/webaudio/resources/distance-model-testing.js
+++ b/tests/wpt/web-platform-tests/webaudio/resources/distance-model-testing.js
@@ -1,10 +1,13 @@
-let sampleRate = 44100.0;
+// Use a power of two to eliminate round-off when converting frames to time and
+// vice versa.
+let sampleRate = 32768;
// How many panner nodes to create for the test.
let nodesToCreate = 100;
-// Time step when each panner node starts.
-let timeStep = 0.001;
+// Time step when each panner node starts. Make sure it starts on a frame
+// boundary.
+let timeStep = Math.floor(0.001 * sampleRate) / sampleRate;
// Make sure we render long enough to get all of our nodes.
let renderLengthSeconds = timeStep * (nodesToCreate + 1);
@@ -134,7 +137,7 @@ function checkDistanceResult(renderedBuffer, model, should) {
// The max allowed error between the actual gain and the expected
// value. This is determined experimentally. Set to 0 to see
// what the actual errors are.
- let maxAllowedError = 3.3e-6;
+ let maxAllowedError = 2.2720e-6;
let success = true;
diff --git a/tests/wpt/web-platform-tests/webaudio/resources/note-grain-on-testing.js b/tests/wpt/web-platform-tests/webaudio/resources/note-grain-on-testing.js
index 1e941897161..ad0631670df 100644
--- a/tests/wpt/web-platform-tests/webaudio/resources/note-grain-on-testing.js
+++ b/tests/wpt/web-platform-tests/webaudio/resources/note-grain-on-testing.js
@@ -1,17 +1,23 @@
-let sampleRate = 44100.0;
+// Use a power of two to eliminate round-off converting from frames to time.
+let sampleRate = 32768;
// How many grains to play.
let numberOfTests = 100;
-// Duration of each grain to be played
-let duration = 0.01;
+// Duration of each grain to be played. Make a whole number of frames
+let duration = Math.floor(0.01 * sampleRate) / sampleRate;
+
+// A little extra bit of silence between grain boundaries. Must be a whole
+// number of frames.
+let grainGap = Math.floor(0.005 * sampleRate) / sampleRate;
// Time step between the start of each grain. We need to add a little
// bit of silence so we can detect grain boundaries
-let timeStep = duration + .005;
+let timeStep = duration + grainGap;
-// Time step between the start for each grain.
-let grainOffsetStep = 0.001;
+// Time step between the start for each grain. Must be a whole number of
+// frames.
+let grainOffsetStep = Math.floor(0.001 * sampleRate) / sampleRate;
// How long to render to cover all of the grains.
let renderTime = (numberOfTests + 1) * timeStep;
diff --git a/tests/wpt/web-platform-tests/webaudio/resources/panner-model-testing.js b/tests/wpt/web-platform-tests/webaudio/resources/panner-model-testing.js
index 662fb1d68c5..4df3e178134 100644
--- a/tests/wpt/web-platform-tests/webaudio/resources/panner-model-testing.js
+++ b/tests/wpt/web-platform-tests/webaudio/resources/panner-model-testing.js
@@ -1,9 +1,12 @@
-let sampleRate = 44100.0;
+// Use a power of two to eliminate round-off when converting frames to time and
+// vice versa.
+let sampleRate = 32768;
let numberOfChannels = 1;
-// Time step when each panner node starts.
-let timeStep = 0.001;
+// Time step when each panner node starts. Make sure it starts on a frame
+// boundary.
+let timeStep = Math.floor(0.001 * sampleRate) / sampleRate;
// Length of the impulse signal.
let pulseLengthFrames = Math.round(timeStep * sampleRate);
@@ -114,7 +117,7 @@ function checkResult(renderedBuffer, should) {
// The max error we allow between the rendered impulse and the
// expected value. This value is experimentally determined. Set
// to 0 to make the test fail to see what the actual error is.
- let maxAllowedError = 1.3e-6;
+ let maxAllowedError = 1.1597e-6;
let success = true;
diff --git a/tests/wpt/web-platform-tests/webaudio/resources/stereopanner-testing.js b/tests/wpt/web-platform-tests/webaudio/resources/stereopanner-testing.js
index d6238a9cd36..2778493e3b6 100644
--- a/tests/wpt/web-platform-tests/webaudio/resources/stereopanner-testing.js
+++ b/tests/wpt/web-platform-tests/webaudio/resources/stereopanner-testing.js
@@ -3,10 +3,12 @@ let StereoPannerTest = (function() {
// Constants
let PI_OVER_TWO = Math.PI * 0.5;
- let gSampleRate = 44100;
+ // Use a power of two to eliminate any round-off when converting frames to
+ // time.
+ let gSampleRate = 32768;
- // Time step when each panner node starts.
- let gTimeStep = 0.001;
+ // Time step when each panner node starts. Make sure this is on a frame boundary.
+ let gTimeStep = Math.floor(0.001 * gSampleRate) / gSampleRate;
// How many panner nodes to create for the test
let gNodesToCreate = 100;
@@ -77,7 +79,7 @@ let StereoPannerTest = (function() {
// The max error we allow between the rendered impulse and the
// expected value. This value is experimentally determined. Set
// to 0 to make the test fail to see what the actual error is.
- this.maxAllowedError = 1.3e-6;
+ this.maxAllowedError = 9.8015e-8;
// Max (absolute) error and the index of the maxima for the left
// and right channels.
diff --git a/tests/wpt/web-platform-tests/webaudio/the-audio-api/the-audiobuffersourcenode-interface/audiobuffersource-playbackrate-zero.html b/tests/wpt/web-platform-tests/webaudio/the-audio-api/the-audiobuffersourcenode-interface/audiobuffersource-playbackrate-zero.html
index 58ee49e42d2..5624054e328 100644
--- a/tests/wpt/web-platform-tests/webaudio/the-audio-api/the-audiobuffersourcenode-interface/audiobuffersource-playbackrate-zero.html
+++ b/tests/wpt/web-platform-tests/webaudio/the-audio-api/the-audiobuffersourcenode-interface/audiobuffersource-playbackrate-zero.html
@@ -74,6 +74,42 @@
.then(() => task.done());
});
+ audit.define('subsample start with playback rate 0', (task, should) => {
+ let context = new OfflineAudioContext(1, renderLength, sampleRate);
+ let rampBuffer = new AudioBuffer(
+ {length: renderLength, sampleRate: context.sampleRate});
+ let data = new Float32Array(renderLength);
+ let startValue = 5;
+ for (let k = 0; k < data.length; ++k) {
+ data[k] = k + startValue;
+ }
+ rampBuffer.copyToChannel(data, 0);
+
+ let src = new AudioBufferSourceNode(
+ context, {buffer: rampBuffer, playbackRate: 0});
+
+ src.connect(context.destination);
+
+ // Purposely start the source between frame boundaries
+ let startFrame = 27.3;
+ src.start(startFrame / context.sampleRate);
+
+ context.startRendering()
+ .then(audioBuffer => {
+ let actualStartFrame = Math.ceil(startFrame);
+ let audio = audioBuffer.getChannelData(0);
+
+ should(
+ audio.slice(0, actualStartFrame),
+ `output[0:${actualStartFrame - 1}]`)
+ .beConstantValueOf(0);
+ should(
+ audio.slice(actualStartFrame), `output[${actualStartFrame}:]`)
+ .beConstantValueOf(startValue);
+ })
+ .then(() => task.done());
+ });
+
audit.run();