from __future__ import print_function import ConfigParser import argparse import os import sys from mozlog import structuredlog from mozlog.handlers import BaseHandler, StreamHandler from mozlog.formatters import MachFormatter from wptrunner import wptcommandline, wptrunner here = os.path.abspath(os.path.dirname(__file__)) def setup_wptrunner_logging(logger): structuredlog.set_default_logger(logger) wptrunner.logger = logger wptrunner.wptlogging.setup_stdlib_logger() class ResultHandler(BaseHandler): def __init__(self, verbose=False, logger=None): self.inner = StreamHandler(sys.stdout, MachFormatter()) BaseHandler.__init__(self, self.inner) self.product = None self.verbose = verbose self.logger = logger self.register_message_handlers("wptrunner-test", {"set-product": self.set_product}) def set_product(self, product): self.product = product def __call__(self, data): if self.product is not None and data["action"] in ["suite_start", "suite_end"]: # Hack: mozlog sets some internal state to prevent multiple suite_start or # suite_end messages. We actually want that here (one from the metaharness # and one from the individual test type harness), so override that internal # state (a better solution might be to not share loggers, but this works well # enough) self.logger._state.suite_started = True return if (not self.verbose and (data["action"] == "process_output" or data["action"] == "log" and data["level"] not in ["error", "critical"])): return if "test" in data: data = data.copy() data["test"] = "%s: %s" % (self.product, data["test"]) return self.inner(data) def test_settings(): return { "include": "_test", "manifest-update": "", "no-capture-stdio": "" } def read_config(): parser = ConfigParser.ConfigParser() parser.read("test.cfg") rv = {"general":{}, "products":{}} rv["general"].update(dict(parser.items("general"))) # This only allows one product per whatever for now for product in parser.sections(): if product != "general": rv["products"][product] = {} for key, value in parser.items(product): rv["products"][product][key] = value return rv def run_tests(product, kwargs): kwargs["test_paths"]["/_test/"] = {"tests_path": os.path.join(here, "testdata"), "metadata_path": os.path.join(here, "metadata")} wptrunner.run_tests(**kwargs) def settings_to_argv(settings): rv = [] for name, value in settings.iteritems(): key = "--%s" % name if not value: rv.append(key) elif isinstance(value, list): for item in value: rv.extend([key, item]) else: rv.extend([key, value]) return rv def set_from_args(settings, args): if args.test: settings["include"] = args.test if args.tags: settings["tags"] = args.tags def run(config, args): logger = structuredlog.StructuredLogger("web-platform-tests") logger.add_handler(ResultHandler(logger=logger, verbose=args.verbose)) setup_wptrunner_logging(logger) parser = wptcommandline.create_parser() logger.suite_start(tests=[]) for product, product_settings in config["products"].iteritems(): if args.product and product not in args.product: continue settings = test_settings() settings.update(config["general"]) settings.update(product_settings) settings["product"] = product set_from_args(settings, args) kwargs = vars(parser.parse_args(settings_to_argv(settings))) wptcommandline.check_args(kwargs) logger.send_message("wptrunner-test", "set-product", product) run_tests(product, kwargs) logger.send_message("wptrunner-test", "set-product", None) logger.suite_end() def get_parser(): parser = argparse.ArgumentParser() parser.add_argument("-v", "--verbose", action="store_true", default=False, help="verbose log output") parser.add_argument("--product", action="append", help="Specific product to include in test run") parser.add_argument("--pdb", action="store_true", help="Invoke pdb on uncaught exception") parser.add_argument("--tag", action="append", dest="tags", help="tags to select tests") parser.add_argument("test", nargs="*", help="Specific tests to include in test run") return parser def main(): config = read_config() args = get_parser().parse_args() try: run(config, args) except Exception: if args.pdb: import pdb import traceback print(traceback.format_exc()) pdb.post_mortem() else: raise if __name__ == "__main__": main()