blob: 6cff5bad0d30f6ff594d93d82543d0f9e2e70586 [file] [log] [blame]
#!/usr/bin/env python -u
import os
import sys
import getopt
import argparse
import re
import logging
import json
logger = logging.getLogger()
handler = logging.StreamHandler(sys.stdout)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
def iter_dir_recusive(path, callback):
if os.path.isfile(path) and os.path.splitext(path)[1] == '.js':"Processing {} ...".format(path))
for root, dirs, files in os.walk(path):
for name in files:
iter_dir_recusive(os.path.join(root, name), callback)"Done.")
class JSCTestModifier(object):
variables = [
"$hostOs", "$model", "$architecture"
def __init__(self, test_pathes, conditions={}, match="all"):
self._conditions = conditions
self._test_pathes = test_pathes
self._match = match
self._skip_line_postfix = "# added by"
def skip(self):
for path in self._test_pathes:"Mark {} skip".format(path))
iter_dir_recusive(path, lambda file_path: self._skip_test_file(file_path))
def enable(self):
for path in self._test_pathes:"Mark {} enable".format(path))
iter_dir_recusive(path, lambda file_path: self._enable_test_file(file_path))
def _generate_condition_op(self, value):
op = "=="
if ("!" == value[0]):
op = "!="
value = value[1:]
return op, value
# Condition grammer is like !A or B or C and D
# Translate it to ruby: $hostOs != A or $hostOs == B or $hostOs == C and $hostOs == D
def _parse_condition(self, variable, condition):
res = []
values = []
for word in re.split(r'\s+', condition):
if word == "or" or word == "and":
value = " ".join(values).strip()
values = []
res.append('{} {} "{}"'.format(variable, *self._generate_condition_op(value)))
if values:
value = " ".join(values).strip()
res.append('{} {} "{}"'.format(variable, *self._generate_condition_op(value)))
return " ".join(res)
def _generate_skip_annotation_line(self):
skip_line_prefix = "//@ skip if"
skip_conditions = []
skip_line = "{} {} {}"
supported_variables = [variable for variable in JSCTestModifier.variables if variable in self._conditions]
condition_template = "{}" if len(supported_variables) == 1 else "({})"
for variable in supported_variables:
skip_conditions.append(condition_template.format(self._parse_condition(variable, self._conditions[variable])))
if not skip_conditions:
# No conditions, always skip
skip_conditions = ["true"]
if self._match == "any":
skip_line = skip_line.format(skip_line_prefix, " or ".join(skip_conditions), self._skip_line_postfix)
elif self._match == "all":
skip_line = skip_line.format(skip_line_prefix, " and ".join(skip_conditions), self._skip_line_postfix)
return skip_line
# This can only remove the skip annotation generated by this script
def _enable_test_file(self, test_file):
with open(test_file, 'r+') as f:
lines = f.readlines()
for line in lines:
if not self._skip_line_postfix in line:
def _skip_test_file(self, test_file):
# remove the exisiting skip line, so that we can apply the new one
skip_line = self._generate_skip_annotation_line()
with open(test_file, 'r+') as f:
original_content =
f.write("{}\n{}".format(skip_line, original_content))
opensource_root = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../../")
jsc_test_search_path = [
os.path.join(opensource_root, "JSTests"),
os.path.join(opensource_root, "LayoutTests")
def main():
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest="action")
file_list_help = "Files/directories list; use ',' to separate each item. Example: a.js, b.js, c.js Use '-' if you are using --jsc-json-output argument"
parser_enable = subparsers.add_parser("enable", help="Enable the tests which are marked as skipped by this script")
parser_enable.add_argument("files", help=file_list_help)
parser_enable.add_argument("--jsc-json-output", help="Pass the json output of run-javascriptcore-tests to unskip all failed tests")
parser_skip = subparsers.add_parser("skip", help="Insert skip condition to given files/directories")
parser_skip.add_argument("files", help=file_list_help)
parser_skip.add_argument("--jsc-json-output", help="Pass the json output of run-javascriptcore-tests to skip all failed tests")
parser_skip.add_argument("--platform", "--host-os", help="Skip if host os matches given value, Examples: 'windows or linux' '!windows and !linux'")
parser_skip.add_argument("--model", help="Skip if hardware model matches given value, Examples: 'Apple Watch Series 3 or Apple Watch Series 4' '!Apple Watch Series 3 and !Apple Watch Series 4'")
parser_skip.add_argument("--architecture", help="Skip if architecture matches given value, Examples: 'arm or x86' '!arm and !x86'")
parser_skip.add_argument("--match", default="all", help="Match all or any above conditions")
args = vars(parser.parse_args())
conditions = {}
files = []
if not args["files"] and not args["--log-file"]:
logger.error("Please speicify a list of file, or use --log-file to give a JSC test log")
return 1
if args["files"] and not args["files"] == "-":
files += args["files"].split(",")
if args["jsc_json_output"]:
with open(args["jsc_json_output"]) as f:
jsc_json_output = json.load(f)
failures = jsc_json_output["stressTestFailures"]
failure_test_files_set = set()
for failure_test in failures:
path_parts = failure_test.split(os.path.sep)
if 'yaml' in path_parts[0]:
failure_test_path = os.path.join(*path_parts[1:])
failure_test_path = failure_test
failure_test_file = os.path.splitext(failure_test_path)[0]
for search_path in jsc_test_search_path:
whole_path = os.path.join(search_path, failure_test_file)
if failure_test_file not in failure_test_files_set and os.path.isfile(whole_path):
if "host_os" in args and args["host_os"]:
conditions["$hostOs"] = args["host_os"]
if "platform" in args and args["platform"]:
conditions["$hostOs"] = args["platform"]
if "architecture" in args and args["architecture"]:
conditions["$architecture"] = args["architecture"]
if "model" in args and args["model"]:
conditions["$model"] = args["model"]
modifer = JSCTestModifier(files, conditions, args["match"] if "match" in args else None)
getattr(modifer, args["action"])()
return 0
if __name__ == '__main__':