Source code
Revision control
Copy as Markdown
Other Tools
# Library for JSTest tests.
#
# This contains classes that represent an individual test, including
# metadata, and know how to run the tests and determine failures.
import os
import sys
from contextlib import contextmanager
# When run on tbpl, we run each test multiple times with the following
# arguments.
JITFLAGS = {
"all": [
[], # no flags, normal baseline and ion
[
"--ion-eager",
"--ion-offthread-compile=off", # implies --baseline-eager
"--more-compartments",
],
[
"--ion-eager",
"--ion-offthread-compile=off",
"--ion-check-range-analysis",
"--ion-extra-checks",
"--no-sse3",
"--no-threads",
],
["--baseline-eager", "--write-protect-code=off"],
["--no-blinterp", "--no-baseline", "--no-ion", "--more-compartments"],
["--blinterp-eager"],
],
# Like 'all' above but for jstests. This has fewer jit-specific
# configurations.
"jstests": [
[], # no flags, normal baseline and ion
[
"--ion-eager",
"--ion-offthread-compile=off", # implies --baseline-eager
"--more-compartments",
],
["--baseline-eager", "--write-protect-code=off"],
["--no-blinterp", "--no-baseline", "--no-ion", "--more-compartments"],
],
# used by jit_test.py
"ion": [
["--baseline-eager", "--write-protect-code=off"],
["--ion-eager", "--ion-offthread-compile=off", "--more-compartments"],
],
# Run reduced variants on debug builds, since they take longer time.
"debug": [
[], # no flags, normal baseline and ion
[
"--ion-eager",
"--ion-offthread-compile=off", # implies --baseline-eager
"--more-compartments",
],
["--baseline-eager", "--write-protect-code=off"],
],
# Cover cases useful for tsan. Note that we test --ion-eager without
# --ion-offthread-compile=off here, because it helps catch races.
"tsan": [
[],
[
"--ion-eager",
"--ion-check-range-analysis",
"--ion-extra-checks",
"--no-sse3",
],
["--no-blinterp", "--no-baseline", "--no-ion"],
],
"baseline": [
["--no-ion"],
],
# Interpreter-only, for tools that cannot handle binary code generation.
"interp": [
[
"--no-blinterp",
"--no-baseline",
"--no-asmjs",
"--wasm-compiler=none",
"--no-native-regexp",
]
],
"none": [[]], # no flags, normal baseline and ion
}
def get_jitflags(variant, **kwargs):
if variant not in JITFLAGS:
print('Invalid jitflag: "{}"'.format(variant))
sys.exit(1)
if variant == "none" and "none" in kwargs:
return kwargs["none"]
return JITFLAGS[variant]
def valid_jitflags():
return JITFLAGS.keys()
def get_environment_overlay(js_shell, gc_zeal):
"""
Build a dict of additional environment variables that must be set to run
tests successfully.
"""
# When updating this also update |buildBrowserEnv| in layout/tools/reftest/runreftest.py.
env = {
# Force Pacific time zone to avoid failures in Date tests.
"TZ": "PST8PDT",
# Force date strings to English.
"LC_ALL": "en_US.UTF-8",
# Tell the shell to disable crash dialogs on windows.
"XRE_NO_WINDOWS_CRASH_DIALOG": "1",
}
# Add the binary's directory to the library search path so that we find the
# nspr and icu we built, instead of the platform supplied ones (or none at
# all on windows).
if sys.platform.startswith("linux"):
env["LD_LIBRARY_PATH"] = os.path.dirname(js_shell)
elif sys.platform.startswith("darwin"):
env["DYLD_LIBRARY_PATH"] = os.path.dirname(js_shell)
elif sys.platform.startswith("win"):
env["PATH"] = os.path.dirname(js_shell)
if gc_zeal:
env["JS_GC_ZEAL"] = gc_zeal
return env
@contextmanager
def change_env(env_overlay):
# Apply the overlaid environment and record the current state.
prior_env = {}
for key, val in env_overlay.items():
prior_env[key] = os.environ.get(key, None)
if "PATH" in key and key in os.environ:
os.environ[key] = "{}{}{}".format(val, os.pathsep, os.environ[key])
else:
os.environ[key] = val
try:
# Execute with the new environment.
yield
finally:
# Restore the prior environment.
for key, val in prior_env.items():
if val is not None:
os.environ[key] = val
else:
del os.environ[key]
def get_cpu_count():
"""
Guess at a reasonable parallelism count to set as the default for the
current machine and run.
"""
# Python 2.6+
try:
import multiprocessing
return multiprocessing.cpu_count()
except (ImportError, NotImplementedError):
pass
# POSIX
try:
res = int(os.sysconf("SC_NPROCESSORS_ONLN"))
if res > 0:
return res
except (AttributeError, ValueError):
pass
# Windows
try:
res = int(os.environ["NUMBER_OF_PROCESSORS"])
if res > 0:
return res
except (KeyError, ValueError):
pass
return 1
class RefTestCase(object):
"""A test case consisting of a test and an expected result."""
def __init__(self, root, path, extra_helper_paths=None, wpt=None):
# str: path of the tests root dir
self.root = root
# str: path of JS file relative to tests root dir
self.path = path
# [str]: Extra options to pass to the shell
self.options = []
# [str]: JIT flags to pass to the shell
self.jitflags = []
# [str]: flags to never pass to the shell for this test
self.ignoredflags = []
# str or None: path to reflect-stringify.js file to test
# instead of actually running tests
self.test_reflect_stringify = None
# bool: True => test is module code
self.is_module = False
# bool: True => test is asynchronous and runs additional code after completing the first
# turn of the event loop.
self.is_async = False
# bool: True => run test, False => don't run
self.enable = True
# str?: Optional error type
self.error = None
# bool: expected result, True => pass
self.expect = True
# bool: True => ignore output as 'random'
self.random = False
# bool: True => test may run slowly
self.slow = False
# bool: True => test will not run alongside any other heavy tests
self.heavy = False
# bool: True => test is test262 testcase with raw flag, that turns off
# running shell.js files inside test262
self.is_test262_raw = False
# Use self-hosted XDR instead of parsing the source stored in the binary.
# str?: Path computed when generating the command
self.selfhosted_xdr_path = None
# str: XDR mode (= "off", "encode", "decode") to use with the
# self-hosted code.
self.selfhosted_xdr_mode = "off"
# The terms parsed to produce the above properties.
self.terms = None
# The tag between |...| in the test header.
self.tag = None
# Anything occuring after -- in the test header.
self.comment = None
self.extra_helper_paths = extra_helper_paths or []
self.wpt = wpt
def prefix_command(self):
"""Return the '-f' options needed to run a test with the given path."""
path = self.path
prefix = []
while path != "":
assert path != "/"
path = os.path.dirname(path)
if self.is_test262_raw and path != "":
# Skip running shell.js under test262 if the test has raw flag.
# Top-level shell.js is still necessary to define reportCompare.
continue
shell_path = os.path.join(self.root, path, "shell.js")
if os.path.exists(shell_path):
prefix.append(shell_path)
prefix.append("-f")
prefix.reverse()
for extra_path in self.extra_helper_paths:
prefix.append("-f")
prefix.append(extra_path)
return prefix
def abs_path(self):
return os.path.join(self.root, self.path)
def get_command(self, prefix, tempdir):
cmd = prefix + self.jitflags + self.options + self.prefix_command()
# Note: The tempdir provided as argument is managed by the caller
# should remain alive as long as the test harness. Therefore, the XDR
# content of the self-hosted code would be accessible to all JS Shell
# instances.
if self.selfhosted_xdr_mode != "off":
self.selfhosted_xdr_path = os.path.join(tempdir, "shell.xdr")
cmd += [
"--selfhosted-xdr-path",
self.selfhosted_xdr_path,
"--selfhosted-xdr-mode",
self.selfhosted_xdr_mode,
]
if self.test_reflect_stringify is not None:
cmd += [self.test_reflect_stringify, "--check", self.abs_path()]
elif self.is_module:
cmd += ["--module", self.abs_path()]
else:
cmd += ["-f", self.abs_path()]
for flag in self.ignoredflags:
if flag in cmd:
cmd.remove(flag)
return cmd
def __str__(self):
ans = self.path
if not self.enable:
ans += ", skip"
if self.error is not None:
ans += ", error=" + self.error
if not self.expect:
ans += ", fails"
if self.random:
ans += ", random"
if self.slow:
ans += ", slow"
if self.heavy:
ans += ", heavy"
if "-d" in self.options:
ans += ", debugMode"
return ans
@staticmethod
def build_js_cmd_prefix(js_path, js_args, debugger_prefix):
parts = []
if debugger_prefix:
parts += debugger_prefix
parts.append(js_path)
if js_args:
parts += js_args
return parts
def __cmp__(self, other):
if self.path == other.path:
return 0
elif self.path < other.path:
return -1
return 1
def __hash__(self):
return self.path.__hash__()
def __repr__(self):
return "<lib.tests.RefTestCase %s>" % (self.path,)