| #!/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') |
| handler.setFormatter(formatter) |
| logger.setLevel(logging.INFO) |
| logger.addHandler(handler) |
| |
| def iter_dir_recusive(path, callback): |
| if os.path.isfile(path) and os.path.splitext(path)[1] == '.js': |
| logger.info("Processing {} ...".format(path)) |
| callback(path) |
| else: |
| for root, dirs, files in os.walk(path): |
| for name in files: |
| iter_dir_recusive(os.path.join(root, name), callback) |
| logger.info("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 mark-jsc-stress-test.py" |
| |
| def skip(self): |
| for path in self._test_pathes: |
| logger.info("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: |
| logger.info("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))) |
| res.append(word) |
| else: |
| values.append(word) |
| 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 = filter(lambda variable: variable in self._conditions, JSCTestModifier.variables) |
| 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() |
| f.seek(0) |
| for line in lines: |
| if not self._skip_line_postfix in line: |
| f.write(line) |
| f.truncate() |
| |
| |
| def _skip_test_file(self, test_file): |
| # remove the exisiting skip line, so that we can apply the new one |
| self._enable_test_file(test_file) |
| skip_line = self._generate_skip_annotation_line() |
| with open(test_file, 'r+') as f: |
| original_content = f.read() |
| f.seek(0) |
| 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:]) |
| else: |
| 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): |
| files.append(whole_path) |
| failure_test_files_set.add(failure_test_file) |
| |
| 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__': |
| sys.exit(main()) |