From 759c52d7eb62f69f31258927558eb5cb985ef47c Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Sun, 9 Aug 2015 15:30:02 -0400 Subject: [PATCH 1/4] Upgrade wptrunner (tests/wpt/harness) to latest version --- tests/wpt/harness/docs/usage.rst | 4 +- tests/wpt/harness/requirements.txt | 2 +- tests/wpt/harness/test/test.cfg.example | 8 ++- tests/wpt/harness/test/test.py | 6 +- .../wpt/harness/wptrunner/browsers/firefox.py | 13 +++- tests/wpt/harness/wptrunner/environment.py | 65 +++++-------------- tests/wpt/harness/wptrunner/executors/base.py | 12 ++-- .../wptrunner/executors/executormarionette.py | 6 ++ .../wptrunner/executors/executorservo.py | 49 +++++++++----- .../executors/testharness_marionette.js | 15 +++-- .../executors/testharness_webdriver.js | 15 +++-- .../wpt/harness/wptrunner/manifestinclude.py | 3 +- tests/wpt/harness/wptrunner/manifestupdate.py | 4 +- tests/wpt/harness/wptrunner/metadata.py | 4 +- tests/wpt/harness/wptrunner/products.py | 4 +- tests/wpt/harness/wptrunner/reduce.py | 2 +- .../wptrunner/testharnessreport-servo.js | 16 +++-- .../testharnessreport-servodriver.js | 14 ++-- .../harness/wptrunner/testharnessreport.js | 13 ---- tests/wpt/harness/wptrunner/testloader.py | 2 +- tests/wpt/harness/wptrunner/testrunner.py | 2 +- .../harness/wptrunner/tests/test_update.py | 2 +- tests/wpt/harness/wptrunner/update/sync.py | 17 +++-- tests/wpt/harness/wptrunner/vcs.py | 2 +- tests/wpt/harness/wptrunner/wptcommandline.py | 6 +- tests/wpt/harness/wptrunner/wptlogging.py | 2 +- tests/wpt/harness/wptrunner/wptrunner.py | 18 +++-- tests/wpt/harness/wptrunner/wpttest.py | 4 +- 28 files changed, 164 insertions(+), 146 deletions(-) diff --git a/tests/wpt/harness/docs/usage.rst b/tests/wpt/harness/docs/usage.rst index ba1375dbcc8..279f4f2c590 100644 --- a/tests/wpt/harness/docs/usage.rst +++ b/tests/wpt/harness/docs/usage.rst @@ -154,11 +154,11 @@ metadata files in a subdirectory of the current directory named ``meta``. Output ------ -wptrunner uses the :py:mod:`mozlog.structured` package for output. This +wptrunner uses the :py:mod:`mozlog` package for output. This structures events such as test results or log messages as JSON objects that can then be fed to other tools for interpretation. More details about the message format are given in the -:py:mod:`mozlog.structured` documentation. +:py:mod:`mozlog` documentation. By default the raw JSON messages are dumped to stdout. This is convenient for piping into other tools, but not ideal for humans diff --git a/tests/wpt/harness/requirements.txt b/tests/wpt/harness/requirements.txt index 0981fb8fe96..9553db44910 100644 --- a/tests/wpt/harness/requirements.txt +++ b/tests/wpt/harness/requirements.txt @@ -1,4 +1,4 @@ html5lib >= 0.99 mozinfo >= 0.7 -mozlog >= 2.8 +mozlog >= 3.0 mozdebug >= 0.1 diff --git a/tests/wpt/harness/test/test.cfg.example b/tests/wpt/harness/test/test.cfg.example index 6a4057e2200..db48226216c 100644 --- a/tests/wpt/harness/test/test.cfg.example +++ b/tests/wpt/harness/test/test.cfg.example @@ -8,9 +8,13 @@ ssl-type=none # prefs-root=/path/to/gecko-src/testing/profiles/ # [servo] -# binary=/path/to/servo-src/components/servo/target/servo +# binary=/path/to/servo-src/target/release/servo +# exclude=testharness # Because it needs a special testharness.js + +# [servodriver] +# binary=/path/to/servo-src/target/release/servo # exclude=testharness # Because it needs a special testharness.js # [chrome] # binary=/path/to/chrome -# webdriver-binary=/path/to/chromedriver \ No newline at end of file +# webdriver-binary=/path/to/chromedriver diff --git a/tests/wpt/harness/test/test.py b/tests/wpt/harness/test/test.py index 6d90ba93555..034e317bd52 100644 --- a/tests/wpt/harness/test/test.py +++ b/tests/wpt/harness/test/test.py @@ -8,9 +8,9 @@ import threading import time from StringIO import StringIO -from mozlog.structured import structuredlog, reader -from mozlog.structured.handlers import BaseHandler, StreamHandler, StatusHandler -from mozlog.structured.formatters import MachFormatter +from mozlog import structuredlog, reader +from mozlog.handlers import BaseHandler, StreamHandler, StatusHandler +from mozlog.formatters import MachFormatter from wptrunner import wptcommandline, wptrunner here = os.path.abspath(os.path.dirname(__file__)) diff --git a/tests/wpt/harness/wptrunner/browsers/firefox.py b/tests/wpt/harness/wptrunner/browsers/firefox.py index 127308a7080..6bbe282d3e8 100644 --- a/tests/wpt/harness/wptrunner/browsers/firefox.py +++ b/tests/wpt/harness/wptrunner/browsers/firefox.py @@ -27,7 +27,8 @@ __wptrunner__ = {"product": "firefox", "reftest": "MarionetteRefTestExecutor"}, "browser_kwargs": "browser_kwargs", "executor_kwargs": "executor_kwargs", - "env_options": "env_options"} + "env_options": "env_options", + "run_info_extras": "run_info_extras"} def check_args(**kwargs): @@ -43,7 +44,8 @@ def browser_kwargs(**kwargs): "symbols_path": kwargs["symbols_path"], "stackwalk_binary": kwargs["stackwalk_binary"], "certutil_binary": kwargs["certutil_binary"], - "ca_certificate_path": kwargs["ssl_env"].ca_cert_path()} + "ca_certificate_path": kwargs["ssl_env"].ca_cert_path(), + "e10s": kwargs["gecko_e10s"]} def executor_kwargs(test_type, server_config, cache_manager, run_info_data, @@ -63,13 +65,15 @@ def env_options(): "certificate_domain": "web-platform.test", "supports_debugger": True} +def run_info_extras(**kwargs): + return {"e10s": kwargs["gecko_e10s"]} class FirefoxBrowser(Browser): used_ports = set() def __init__(self, logger, binary, prefs_root, debug_info=None, symbols_path=None, stackwalk_binary=None, certutil_binary=None, - ca_certificate_path=None): + ca_certificate_path=None, e10s=False): Browser.__init__(self, logger) self.binary = binary self.prefs_root = prefs_root @@ -81,6 +85,7 @@ class FirefoxBrowser(Browser): self.stackwalk_binary = stackwalk_binary self.ca_certificate_path = ca_certificate_path self.certutil_binary = certutil_binary + self.e10s = e10s def start(self): self.marionette_port = get_free_port(2828, exclude=self.used_ports) @@ -99,6 +104,8 @@ class FirefoxBrowser(Browser): "marionette.defaultPrefs.port": self.marionette_port, "dom.disable_open_during_load": False, "network.dns.localDomains": ",".join(hostnames)}) + if self.e10s: + self.profile.set_preferences({"browser.tabs.remote.autostart": True}) if self.ca_certificate_path is not None: self.setup_ssl() diff --git a/tests/wpt/harness/wptrunner/environment.py b/tests/wpt/harness/wptrunner/environment.py index 5d0436cbf5a..b3d0e301fd3 100644 --- a/tests/wpt/harness/wptrunner/environment.py +++ b/tests/wpt/harness/wptrunner/environment.py @@ -10,7 +10,7 @@ import socket import sys import time -from mozlog.structured import get_default_logger, handlers +from mozlog import get_default_logger, handlers from wptlogging import LogLevelRewriter @@ -80,25 +80,6 @@ class TestEnvironmentError(Exception): pass -class StaticHandler(object): - def __init__(self, path, format_args, content_type, **headers): - with open(path) as f: - self.data = f.read() % format_args - - self.resp_headers = [("Content-Type", content_type)] - for k, v in headers.iteritems(): - resp_headers.append((k.replace("_", "-"), v)) - - self.handler = serve.handlers.handler(self.handle_request) - - def handle_request(self, request, response): - return self.resp_headers, self.data - - def __call__(self, request, response): - rv = self.handler(request, response) - return rv - - class TestEnvironment(object): def __init__(self, test_paths, ssl_env, pause_after_test, debug_info, options): """Context manager that owns the test environment i.e. the http and @@ -114,29 +95,30 @@ class TestEnvironment(object): self.options = options if options is not None else {} self.cache_manager = multiprocessing.Manager() - self.routes = self.get_routes() + self.stash = serve.stash.StashServer() def __enter__(self): + self.stash.__enter__() self.ssl_env.__enter__() self.cache_manager.__enter__() self.setup_server_logging() self.config = self.load_config() serve.set_computed_defaults(self.config) self.external_config, self.servers = serve.start(self.config, self.ssl_env, - self.routes) + self.get_routes()) if self.options.get("supports_debugger") and self.debug_info and self.debug_info.interactive: self.ignore_interrupts() return self def __exit__(self, exc_type, exc_val, exc_tb): self.process_interrupts() - self.cache_manager.__exit__(exc_type, exc_val, exc_tb) - self.ssl_env.__exit__(exc_type, exc_val, exc_tb) - for scheme, servers in self.servers.iteritems(): for port, server in servers: server.kill() + self.cache_manager.__exit__(exc_type, exc_val, exc_tb) + self.ssl_env.__exit__(exc_type, exc_val, exc_tb) + self.stash.__exit__() def ignore_interrupts(self): signal.signal(signal.SIGINT, signal.SIG_IGN) @@ -193,40 +175,25 @@ class TestEnvironment(object): pass def get_routes(self): - routes = serve.default_routes() + route_builder = serve.RoutesBuilder() + for path, format_args, content_type, route in [ ("testharness_runner.html", {}, "text/html", "/testharness_runner.html"), (self.options.get("testharnessreport", "testharnessreport.js"), {"output": self.pause_after_test}, "text/javascript", "/resources/testharnessreport.js")]: - handler = StaticHandler(os.path.join(here, path), format_args, content_type) - routes.insert(0, (b"GET", str(route), handler)) + path = os.path.normpath(os.path.join(here, path)) + route_builder.add_static(path, format_args, content_type, route) - for url, paths in self.test_paths.iteritems(): - if url == "/": + for url_base, paths in self.test_paths.iteritems(): + if url_base == "/": continue - - path = paths["tests_path"] - url = "/%s/" % url.strip("/") - - for (method, - suffix, - handler_cls) in [(b"*", - b"*.py", - serve.handlers.PythonScriptHandler), - (b"GET", - "*.asis", - serve.handlers.AsIsHandler), - (b"GET", - "*", - serve.handlers.FileHandler)]: - route = (method, b"%s%s" % (str(url), str(suffix)), handler_cls(path, url_base=url)) - routes.insert(-3, route) + route_builder.add_mount_point(url_base, paths["tests_path"]) if "/" not in self.test_paths: - routes = routes[:-3] + del route_builder.mountpoint_routes["/"] - return routes + return route_builder.get_routes() def ensure_started(self): # Pause for a while to ensure that the server has a chance to start diff --git a/tests/wpt/harness/wptrunner/executors/base.py b/tests/wpt/harness/wptrunner/executors/base.py index 4e983fb663f..1ff09a5f030 100644 --- a/tests/wpt/harness/wptrunner/executors/base.py +++ b/tests/wpt/harness/wptrunner/executors/base.py @@ -55,12 +55,14 @@ class TestharnessResultConverter(object): def __call__(self, test, result): """Convert a JSON result into a (TestResult, [SubtestResult]) tuple""" - assert result["test"] == test.url, ("Got results from %s, expected %s" % - (result["test"], test.url)) - harness_result = test.result_cls(self.harness_codes[result["status"]], result["message"]) + result_url, status, message, stack, subtest_results = result + assert result_url == test.url, ("Got results from %s, expected %s" % + (result_url, test.url)) + harness_result = test.result_cls(self.harness_codes[status], message) return (harness_result, - [test.subtest_result_cls(subtest["name"], self.test_codes[subtest["status"]], - subtest["message"], subtest.get("stack", None)) for subtest in result["tests"]]) + [test.subtest_result_cls(name, self.test_codes[status], message, stack) + for name, status, message, stack in subtest_results]) + testharness_result_converter = TestharnessResultConverter() diff --git a/tests/wpt/harness/wptrunner/executors/executormarionette.py b/tests/wpt/harness/wptrunner/executors/executormarionette.py index 898e279413e..90c76759faf 100644 --- a/tests/wpt/harness/wptrunner/executors/executormarionette.py +++ b/tests/wpt/harness/wptrunner/executors/executormarionette.py @@ -107,6 +107,12 @@ class MarionetteProtocol(Protocol): return True def after_connect(self): + # Turn off debug-level logging by default since this is so verbose + with self.marionette.using_context("chrome"): + self.marionette.execute_script(""" + Components.utils.import("resource://gre/modules/Log.jsm"); + Log.repository.getLogger("Marionette").level = Log.Level.Info; + """) self.load_runner("http") def load_runner(self, protocol): diff --git a/tests/wpt/harness/wptrunner/executors/executorservo.py b/tests/wpt/harness/wptrunner/executors/executorservo.py index b0e0eaeda27..649e4e0ba18 100644 --- a/tests/wpt/harness/wptrunner/executors/executorservo.py +++ b/tests/wpt/harness/wptrunner/executors/executorservo.py @@ -65,6 +65,8 @@ class ServoTestharnessExecutor(ProcessTestExecutor): args = ["--cpu", "--hard-fail", "-u", "Servo/wptrunner", "-z", self.test_url(test)] for stylesheet in self.browser.user_stylesheets: args += ["--user-stylesheet", stylesheet] + for pref in test.environment.get('prefs', {}): + args += ["--pref", pref] debug_args, command = browser_command(self.binary, args, self.debug_info) self.command = command @@ -104,7 +106,6 @@ class ServoTestharnessExecutor(ProcessTestExecutor): if self.result_flag.is_set(): if self.result_data is not None: - self.result_data["test"] = test.url result = self.convert_result(test, self.result_data) else: self.proc.wait() @@ -190,26 +191,44 @@ class ServoRefTestExecutor(ProcessTestExecutor): full_url = self.test_url(test) with TempFilename(self.tempdir) as output_path: - self.command = [self.binary, "--cpu", "--hard-fail", "--exit", - "-u", "Servo/wptrunner", "-Z", "disable-text-aa", - "--output=%s" % output_path, full_url] + debug_args, command = browser_command( + self.binary, + ["--cpu", "--hard-fail", "--exit", "-u", "Servo/wptrunner", + "-Z", "disable-text-aa", "--output=%s" % output_path, full_url], + self.debug_info) + for stylesheet in self.browser.user_stylesheets: - self.command += ["--user-stylesheet", stylesheet] + command += ["--user-stylesheet", stylesheet] + + for pref in test.environment.get('prefs', {}): + command += ["--pref", pref] + + self.command = debug_args + command env = os.environ.copy() env["HOST_FILE"] = self.hosts_path - self.proc = ProcessHandler(self.command, - processOutputLine=[self.on_output], - env=env) + if not self.interactive: + self.proc = ProcessHandler(self.command, + processOutputLine=[self.on_output], + env=env) - try: - self.proc.run() - timeout = test.timeout * self.timeout_multiplier + 5 - rv = self.proc.wait(timeout=timeout) - except KeyboardInterrupt: - self.proc.kill() - raise + + try: + self.proc.run() + timeout = test.timeout * self.timeout_multiplier + 5 + rv = self.proc.wait(timeout=timeout) + except KeyboardInterrupt: + self.proc.kill() + raise + else: + self.proc = subprocess.Popen(self.command, + env=env) + try: + rv = self.proc.wait() + except KeyboardInterrupt: + self.proc.kill() + raise if rv is None: self.proc.kill() diff --git a/tests/wpt/harness/wptrunner/executors/testharness_marionette.js b/tests/wpt/harness/wptrunner/executors/testharness_marionette.js index f5eb9fa920a..1a1b685fefa 100644 --- a/tests/wpt/harness/wptrunner/executors/testharness_marionette.js +++ b/tests/wpt/harness/wptrunner/executors/testharness_marionette.js @@ -13,11 +13,16 @@ window.wrappedJSObject.addEventListener("message", function listener(event) { clearTimeout(timer); var tests = event.data.tests; var status = event.data.status; - marionetteScriptFinished({test:"%(url)s", - tests: tests, - status: status.status, - message: status.message, - stack: status.stack}); + + var subtest_results = tests.map(function(x) { + return [x.name, x.status, x.message, x.stack] + }); + + marionetteScriptFinished(["%(url)s", + status.status, + status.message, + status.stack, + subtest_results]); }, false); window.wrappedJSObject.win = window.open("%(abs_url)s", "%(window_id)s"); diff --git a/tests/wpt/harness/wptrunner/executors/testharness_webdriver.js b/tests/wpt/harness/wptrunner/executors/testharness_webdriver.js index 286c5fb2fab..7ca8d573703 100644 --- a/tests/wpt/harness/wptrunner/executors/testharness_webdriver.js +++ b/tests/wpt/harness/wptrunner/executors/testharness_webdriver.js @@ -8,12 +8,17 @@ window.timeout_multiplier = %(timeout_multiplier)d; window.addEventListener("message", function(event) { var tests = event.data[0]; var status = event.data[1]; + + var subtest_results = tests.map(function(x) { + return [x.name, x.status, x.message, x.stack] + }); + clearTimeout(timer); - callback({test:"%(url)s", - tests: tests, - status: status.status, - message: status.message, - stack: status.stack}); + callback(["%(url)s", + status.status, + status.message, + status.stack, + subtest_results]); }, false); window.win = window.open("%(abs_url)s", "%(window_id)s"); diff --git a/tests/wpt/harness/wptrunner/manifestinclude.py b/tests/wpt/harness/wptrunner/manifestinclude.py index a3cda5a1c99..6fba05128de 100644 --- a/tests/wpt/harness/wptrunner/manifestinclude.py +++ b/tests/wpt/harness/wptrunner/manifestinclude.py @@ -57,7 +57,7 @@ class IncludeManifest(ManifestItem): try: skip_value = self.get("skip", {"test_type": test.item_type}).lower() assert skip_value in ("true", "false") - return False if skip_value == "true" else True + return skip_value != "true" except KeyError: if node.parent is not None: node = node.parent @@ -107,6 +107,7 @@ class IncludeManifest(ManifestItem): if component not in node.child_map: new_node = IncludeManifest(DataNode(component)) node.append(new_node) + new_node.set("skip", node.get("skip", {})) node = node.child_map[component] diff --git a/tests/wpt/harness/wptrunner/manifestupdate.py b/tests/wpt/harness/wptrunner/manifestupdate.py index 944b35d7d93..0a5d46c0995 100644 --- a/tests/wpt/harness/wptrunner/manifestupdate.py +++ b/tests/wpt/harness/wptrunner/manifestupdate.py @@ -329,7 +329,7 @@ def group_conditionals(values): properties = set(item[0] for item in by_property.iterkeys()) - prop_order = ["debug", "os", "version", "processor", "bits"] + prop_order = ["debug", "e10s", "os", "version", "processor", "bits"] include_props = [] for prop in prop_order: @@ -356,7 +356,7 @@ def make_expr(prop_set, status): assert len(prop_set) > 0 - no_value_props = set(["debug"]) + no_value_props = set(["debug", "e10s"]) expressions = [] for prop, value in prop_set: diff --git a/tests/wpt/harness/wptrunner/metadata.py b/tests/wpt/harness/wptrunner/metadata.py index 948baaa96e7..7f0303491dc 100644 --- a/tests/wpt/harness/wptrunner/metadata.py +++ b/tests/wpt/harness/wptrunner/metadata.py @@ -10,8 +10,8 @@ import types import uuid from collections import defaultdict -from mozlog.structured import reader -from mozlog.structured import structuredlog +from mozlog import reader +from mozlog import structuredlog import expected import manifestupdate diff --git a/tests/wpt/harness/wptrunner/products.py b/tests/wpt/harness/wptrunner/products.py index ce467da6f9b..f67a51cf300 100644 --- a/tests/wpt/harness/wptrunner/products.py +++ b/tests/wpt/harness/wptrunner/products.py @@ -43,6 +43,8 @@ def load_product(config, product): browser_kwargs = getattr(module, data["browser_kwargs"]) executor_kwargs = getattr(module, data["executor_kwargs"]) env_options = getattr(module, data["env_options"])() + run_info_extras = (getattr(module, data["run_info_extras"]) + if "run_info_extras" in data else lambda **kwargs:{}) executor_classes = {} for test_type, cls_name in data["executor"].iteritems(): @@ -52,4 +54,4 @@ def load_product(config, product): return (check_args, browser_cls, browser_kwargs, executor_classes, executor_kwargs, - env_options) + env_options, run_info_extras) diff --git a/tests/wpt/harness/wptrunner/reduce.py b/tests/wpt/harness/wptrunner/reduce.py index 9f50dbec284..c4487b9d356 100644 --- a/tests/wpt/harness/wptrunner/reduce.py +++ b/tests/wpt/harness/wptrunner/reduce.py @@ -10,7 +10,7 @@ from collections import defaultdict import wptrunner import wpttest -from mozlog.structured import commandline, reader +from mozlog import commandline, reader logger = None diff --git a/tests/wpt/harness/wptrunner/testharnessreport-servo.js b/tests/wpt/harness/wptrunner/testharnessreport-servo.js index 72cafe29e5a..d1b31676170 100644 --- a/tests/wpt/harness/wptrunner/testharnessreport-servo.js +++ b/tests/wpt/harness/wptrunner/testharnessreport-servo.js @@ -7,12 +7,14 @@ var props = {output:%(output)d}; setup(props); add_completion_callback(function (tests, harness_status) { - alert("RESULT: " + JSON.stringify({ - tests: tests.map(function(t) { - return { name: t.name, status: t.status, message: t.message, stack: t.stack} + var id = location.pathname + location.search + location.hash; + alert("RESULT: " + JSON.stringify([ + id, + harness_status.status, + harness_status.message, + harness_status.stack, + tests.map(function(t) { + return [t.name, t.status, t.message, t.stack] }), - status: harness_status.status, - message: harness_status.message, - stack: harness_status.stack, - })); + ])); }); diff --git a/tests/wpt/harness/wptrunner/testharnessreport-servodriver.js b/tests/wpt/harness/wptrunner/testharnessreport-servodriver.js index 874a097ca2c..9008944ef5a 100644 --- a/tests/wpt/harness/wptrunner/testharnessreport-servodriver.js +++ b/tests/wpt/harness/wptrunner/testharnessreport-servodriver.js @@ -6,15 +6,15 @@ setup({output:%(output)d}); add_completion_callback(function() { add_completion_callback(function (tests, status) { - var test_results = tests.map(function(x) { - return {name:x.name, status:x.status, message:x.message, stack:x.stack} + var subtest_results = tests.map(function(x) { + return [x.name, x.status, x.message, x.stack] }); var id = location.pathname + location.search + location.hash; - var results = JSON.stringify({test: id, - tests:test_results, - status: status.status, - message: status.message, - stack: status.stack}); + var results = JSON.stringify([id, + status.status, + status.message, + status.stack, + subtest_results]); (function done() { if (window.__wd_results_callback__) { clearTimeout(__wd_results_timer__); diff --git a/tests/wpt/harness/wptrunner/testharnessreport.js b/tests/wpt/harness/wptrunner/testharnessreport.js index 6a79caf753a..79559773f72 100644 --- a/tests/wpt/harness/wptrunner/testharnessreport.js +++ b/tests/wpt/harness/wptrunner/testharnessreport.js @@ -15,16 +15,3 @@ if (window.opener && window.opener.explicit_timeout) { } setup(props); -add_completion_callback(function() { - add_completion_callback(function(tests, status) { - var harness_status = { - "status": status.status, - "message": status.message, - "stack": status.stack - }; - var test_results = tests.map(function(x) { - return {name:x.name, status:x.status, message:x.message, stack:x.stack} - }); - window.opener.postMessage([test_results, harness_status], "*"); - }) -}); diff --git a/tests/wpt/harness/wptrunner/testloader.py b/tests/wpt/harness/wptrunner/testloader.py index c431c840d12..29a07c45522 100644 --- a/tests/wpt/harness/wptrunner/testloader.py +++ b/tests/wpt/harness/wptrunner/testloader.py @@ -496,7 +496,7 @@ class TestLoader(object): def iter_tests(self): manifest_items = [] - for manifest in self.manifests.keys(): + for manifest in sorted(self.manifests.keys(), key=lambda x:x.url_base): manifest_iter = iterfilter(self.manifest_filters, manifest.itertypes(*self.test_types)) manifest_items.extend(manifest_iter) diff --git a/tests/wpt/harness/wptrunner/testrunner.py b/tests/wpt/harness/wptrunner/testrunner.py index e665b349eac..4c876328771 100644 --- a/tests/wpt/harness/wptrunner/testrunner.py +++ b/tests/wpt/harness/wptrunner/testrunner.py @@ -11,7 +11,7 @@ import traceback from Queue import Empty from multiprocessing import Process, current_process, Queue -from mozlog.structured import structuredlog +from mozlog import structuredlog # Special value used as a sentinal in various commands Stop = object() diff --git a/tests/wpt/harness/wptrunner/tests/test_update.py b/tests/wpt/harness/wptrunner/tests/test_update.py index d938e6659d7..2d77b326d61 100644 --- a/tests/wpt/harness/wptrunner/tests/test_update.py +++ b/tests/wpt/harness/wptrunner/tests/test_update.py @@ -6,7 +6,7 @@ import unittest import StringIO from .. import metadata, manifestupdate -from mozlog.structured import structuredlog, handlers, formatters +from mozlog import structuredlog, handlers, formatters class TestExpectedUpdater(unittest.TestCase): diff --git a/tests/wpt/harness/wptrunner/update/sync.py b/tests/wpt/harness/wptrunner/update/sync.py index 68db2d3285b..28189eb3837 100644 --- a/tests/wpt/harness/wptrunner/update/sync.py +++ b/tests/wpt/harness/wptrunner/update/sync.py @@ -124,24 +124,23 @@ class GetSyncTargetCommit(Step): class LoadManifest(Step): """Load the test manifest""" - provides = ["test_manifest"] + provides = ["manifest_path", "test_manifest", "old_manifest"] def create(self, state): - state.test_manifest = testloader.ManifestLoader(state.tests_path).load_manifest( - state.tests_path, state.metadata_path, - ) + from manifest import manifest + state.manifest_path = os.path.join(state.metadata_path, "MANIFEST.json") + # Conservatively always rebuild the manifest when doing a sync + state.old_manifest = manifest.load(state.tests_path, state.manifest_path) + state.test_manifest = manifest.Manifest(None, "/") class UpdateManifest(Step): """Update the manifest to match the tests in the sync tree checkout""" - provides = ["initial_rev"] def create(self, state): from manifest import manifest, update - test_manifest = state.test_manifest - state.initial_rev = test_manifest.rev - update.update(state.sync["path"], "/", test_manifest) - manifest.write(test_manifest, os.path.join(state.metadata_path, "MANIFEST.json")) + update.update(state.sync["path"], "/", state.test_manifest) + manifest.write(state.test_manifest, state.manifest_path) class CopyWorkTree(Step): diff --git a/tests/wpt/harness/wptrunner/vcs.py b/tests/wpt/harness/wptrunner/vcs.py index 26a81b75854..6dfb9d59277 100644 --- a/tests/wpt/harness/wptrunner/vcs.py +++ b/tests/wpt/harness/wptrunner/vcs.py @@ -5,7 +5,7 @@ import subprocess from functools import partial -from mozlog.structured import get_default_logger +from mozlog import get_default_logger logger = None diff --git a/tests/wpt/harness/wptrunner/wptcommandline.py b/tests/wpt/harness/wptrunner/wptcommandline.py index 3133550dc96..e9e42a0b01f 100644 --- a/tests/wpt/harness/wptrunner/wptcommandline.py +++ b/tests/wpt/harness/wptrunner/wptcommandline.py @@ -35,7 +35,7 @@ def require_arg(kwargs, name, value_func=None): def create_parser(product_choices=None): - from mozlog.structured import commandline + from mozlog import commandline import products @@ -154,6 +154,8 @@ def create_parser(product_choices=None): gecko_group = parser.add_argument_group("Gecko-specific") gecko_group.add_argument("--prefs-root", dest="prefs_root", action="store", type=abs_path, help="Path to the folder containing browser prefs") + gecko_group.add_argument("--e10s", dest="gecko_e10s", action="store_true", + help="Path to the folder containing browser prefs") b2g_group = parser.add_argument_group("B2G-specific") b2g_group.add_argument("--b2g-no-backup", action="store_true", default=False, @@ -290,7 +292,7 @@ def check_args(kwargs): kwargs["debugger"] = mozdebug.get_default_debugger_name() debug_info = mozdebug.get_debugger_info(kwargs["debugger"], kwargs["debugger_args"]) - if debug_info.interactive: + if debug_info and debug_info.interactive: if kwargs["processes"] != 1: kwargs["processes"] = 1 kwargs["no_capture_stdio"] = True diff --git a/tests/wpt/harness/wptrunner/wptlogging.py b/tests/wpt/harness/wptrunner/wptlogging.py index 3058a58aaeb..9e6e737d3e9 100644 --- a/tests/wpt/harness/wptrunner/wptlogging.py +++ b/tests/wpt/harness/wptrunner/wptlogging.py @@ -8,7 +8,7 @@ import threading from StringIO import StringIO from multiprocessing import Queue -from mozlog.structured import commandline, stdadapter +from mozlog import commandline, stdadapter def setup(args, defaults): logger = commandline.setup_logging("web-platform-tests", args, defaults) diff --git a/tests/wpt/harness/wptrunner/wptrunner.py b/tests/wpt/harness/wptrunner/wptrunner.py index 42923c2ae0d..29fd06a3619 100644 --- a/tests/wpt/harness/wptrunner/wptrunner.py +++ b/tests/wpt/harness/wptrunner/wptrunner.py @@ -40,8 +40,12 @@ def setup_logging(*args, **kwargs): global logger logger = wptlogging.setup(*args, **kwargs) -def get_loader(test_paths, product, ssl_env, debug=None, **kwargs): - run_info = wpttest.get_run_info(kwargs["run_info"], product, debug=debug) +def get_loader(test_paths, product, ssl_env, debug=None, run_info_extras=None, **kwargs): + if run_info_extras is None: + run_info_extras = {} + + run_info = wpttest.get_run_info(kwargs["run_info"], product, debug=debug, + extras=run_info_extras) test_manifests = testloader.ManifestLoader(test_paths, force_manifest_update=kwargs["manifest_update"]).load() @@ -111,17 +115,21 @@ def run_tests(config, test_paths, product, **kwargs): (check_args, browser_cls, get_browser_kwargs, executor_classes, get_executor_kwargs, - env_options) = products.load_product(config, product) + env_options, run_info_extras) = products.load_product(config, product) ssl_env = env.ssl_env(logger, **kwargs) check_args(**kwargs) if "test_loader" in kwargs: - run_info = wpttest.get_run_info(kwargs["run_info"], product, debug=None) + run_info = wpttest.get_run_info(kwargs["run_info"], product, debug=None, + extras=run_info_extras(**kwargs)) test_loader = kwargs["test_loader"] else: - run_info, test_loader = get_loader(test_paths, product, ssl_env, + run_info, test_loader = get_loader(test_paths, + product, + ssl_env, + run_info_extras=run_info_extras(**kwargs), **kwargs) if kwargs["run_by_dir"] is False: diff --git a/tests/wpt/harness/wptrunner/wpttest.py b/tests/wpt/harness/wptrunner/wpttest.py index 635b1a2298e..9186b9e96c2 100644 --- a/tests/wpt/harness/wptrunner/wpttest.py +++ b/tests/wpt/harness/wptrunner/wpttest.py @@ -57,7 +57,7 @@ def get_run_info(metadata_root, product, **kwargs): class RunInfo(dict): - def __init__(self, metadata_root, product, debug): + def __init__(self, metadata_root, product, debug, extras=None): self._update_mozinfo(metadata_root) self.update(mozinfo.info) self["product"] = product @@ -66,6 +66,8 @@ class RunInfo(dict): elif "debug" not in self: # Default to release self["debug"] = False + if extras is not None: + self.update(extras) def _update_mozinfo(self, metadata_root): """Add extra build information from a mozinfo.json file in a parent From e38df50bbf2d8c33aa53ce8ce1fc2b84e9b22814 Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Sun, 9 Aug 2015 13:17:26 -0400 Subject: [PATCH 2/4] Revert "Revert "Auto merge of #7103 - frewsxcv:python-venv, r=metajack" for breaking web-platform-tests." This reverts commit 47d6d958f58f5011742a18abcdd5a76bf4390966. --- .gitignore | 2 +- .../flake8-2.4.1-py2.py3-none-any.whl | Bin 30917 -> 0 bytes .../pep8-1.5.7-py2.py3-none-any.whl | Bin 37490 -> 0 bytes .../pyflakes-0.9.0-py2.py3-none-any.whl | Bin 38209 -> 0 bytes python/mach_bootstrap.py | 37 + python/mozdebug/__init__.py | 30 - python/mozdebug/mozdebug.py | 168 --- python/mozinfo/__init__.py | 56 - python/mozinfo/mozinfo.py | 233 ----- python/mozlog/mozlog/__init__.py | 26 - python/mozlog/mozlog/logger.py | 180 ---- python/mozlog/mozlog/loggingmixin.py | 41 - python/mozlog/mozlog/loglistener.py | 47 - python/mozlog/mozlog/structured/__init__.py | 7 - .../mozlog/mozlog/structured/commandline.py | 225 ---- .../mozlog/structured/formatters/__init__.py | 13 - .../mozlog/structured/formatters/base.py | 19 - .../structured/formatters/html/__init__.py | 1 - .../mozlog/structured/formatters/html/html.py | 194 ---- .../mozlog/structured/formatters/html/main.js | 172 --- .../structured/formatters/html/style.css | 154 --- .../structured/formatters/html/xmlgen.py | 267 ----- .../structured/formatters/machformatter.py | 356 ------- .../structured/formatters/tbplformatter.py | 140 --- .../mozlog/structured/formatters/unittest.py | 58 -- .../mozlog/structured/formatters/xunit.py | 100 -- .../mozlog/structured/handlers/__init__.py | 7 - .../mozlog/mozlog/structured/handlers/base.py | 104 -- .../structured/handlers/bufferhandler.py | 82 -- .../structured/handlers/statushandler.py | 52 - python/mozlog/mozlog/structured/logtypes.py | 174 ---- python/mozlog/mozlog/structured/reader.py | 73 -- .../mozlog/structured/scripts/__init__.py | 30 - .../mozlog/structured/scripts/format.py | 39 - .../mozlog/structured/scripts/logmerge.py | 82 -- .../mozlog/structured/scripts/unstable.py | 108 -- python/mozlog/mozlog/structured/stdadapter.py | 40 - .../mozlog/mozlog/structured/structuredlog.py | 425 -------- python/mozlog/setup.py | 36 - python/mozlog/tests/manifest.ini | 2 - python/mozlog/tests/test_logger.py | 259 ----- python/mozlog/tests/test_structured.py | 986 ------------------ python/requirements.txt | 12 + python/servo/testing_commands.py | 45 - python/tidy.py | 9 +- python/toml/LICENSE | 21 - python/toml/PKG-INFO | 52 - python/toml/README.rst | 42 - python/toml/setup.py | 14 - python/toml/toml.py | 643 ------------ tests/dromaeo/run_dromaeo.py | 3 - 51 files changed, 51 insertions(+), 5815 deletions(-) delete mode 100644 python/dependencies/flake8-2.4.1-py2.py3-none-any.whl delete mode 100644 python/dependencies/pep8-1.5.7-py2.py3-none-any.whl delete mode 100644 python/dependencies/pyflakes-0.9.0-py2.py3-none-any.whl delete mode 100644 python/mozdebug/__init__.py delete mode 100644 python/mozdebug/mozdebug.py delete mode 100644 python/mozinfo/__init__.py delete mode 100755 python/mozinfo/mozinfo.py delete mode 100644 python/mozlog/mozlog/__init__.py delete mode 100644 python/mozlog/mozlog/logger.py delete mode 100644 python/mozlog/mozlog/loggingmixin.py delete mode 100644 python/mozlog/mozlog/loglistener.py delete mode 100644 python/mozlog/mozlog/structured/__init__.py delete mode 100644 python/mozlog/mozlog/structured/commandline.py delete mode 100644 python/mozlog/mozlog/structured/formatters/__init__.py delete mode 100644 python/mozlog/mozlog/structured/formatters/base.py delete mode 100644 python/mozlog/mozlog/structured/formatters/html/__init__.py delete mode 100755 python/mozlog/mozlog/structured/formatters/html/html.py delete mode 100644 python/mozlog/mozlog/structured/formatters/html/main.js delete mode 100644 python/mozlog/mozlog/structured/formatters/html/style.css delete mode 100644 python/mozlog/mozlog/structured/formatters/html/xmlgen.py delete mode 100644 python/mozlog/mozlog/structured/formatters/machformatter.py delete mode 100644 python/mozlog/mozlog/structured/formatters/tbplformatter.py delete mode 100755 python/mozlog/mozlog/structured/formatters/unittest.py delete mode 100644 python/mozlog/mozlog/structured/formatters/xunit.py delete mode 100644 python/mozlog/mozlog/structured/handlers/__init__.py delete mode 100644 python/mozlog/mozlog/structured/handlers/base.py delete mode 100644 python/mozlog/mozlog/structured/handlers/bufferhandler.py delete mode 100644 python/mozlog/mozlog/structured/handlers/statushandler.py delete mode 100644 python/mozlog/mozlog/structured/logtypes.py delete mode 100644 python/mozlog/mozlog/structured/reader.py delete mode 100644 python/mozlog/mozlog/structured/scripts/__init__.py delete mode 100644 python/mozlog/mozlog/structured/scripts/format.py delete mode 100644 python/mozlog/mozlog/structured/scripts/logmerge.py delete mode 100644 python/mozlog/mozlog/structured/scripts/unstable.py delete mode 100644 python/mozlog/mozlog/structured/stdadapter.py delete mode 100644 python/mozlog/mozlog/structured/structuredlog.py delete mode 100644 python/mozlog/setup.py delete mode 100644 python/mozlog/tests/manifest.ini delete mode 100644 python/mozlog/tests/test_logger.py delete mode 100644 python/mozlog/tests/test_structured.py create mode 100644 python/requirements.txt delete mode 100644 python/toml/LICENSE delete mode 100644 python/toml/PKG-INFO delete mode 100644 python/toml/README.rst delete mode 100644 python/toml/setup.py delete mode 100644 python/toml/toml.py diff --git a/.gitignore b/.gitignore index a21c4e30a17..860c9e5073a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ /ports/android/libs /ports/android/local.properties /ports/android/obj -/tests/wpt/_virtualenv +/python/_virtualenv *~ *# *.o diff --git a/python/dependencies/flake8-2.4.1-py2.py3-none-any.whl b/python/dependencies/flake8-2.4.1-py2.py3-none-any.whl deleted file mode 100644 index 2aead94cbdf797904b6671e48d23392a16820616..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30917 zcmZU)V{~Rs*DV~|wrv|7+qq)fwvCRRbZpzUZQEAIN$2bP8Q(eg^S)<~Q9t&NTDz*o zUURLv)+|LCP%tzgARs89ij)TNt3Ok%SbyIz{tlGC!_3Ce+LVh?U*FQs(nVjN!ND^s zj!}l5W@c(ODo(u)pvbwvI?t+l3Ie3~f9*_KONnK?7DoN}J74a9+p#sYwEK@88QO_i zfaIjqlzj8tq}a z12MFLjixpza8QlLD#H&Xs3w<&_K2b%%C?`k?nOIvu45w#7`Hjzp08VPaH+Sc@s&Z7 zU-WD(I(nU%eHVweKrS9BUS%A5NeKw7NTJ9B$o3B-g>J&TF)v0tK+TiO`Y6|)`!%mn&t}_w@Df3-RzrIg! zS0pg-RJ6sD@caQPI}t{VsLlc)mv(&grFg(yyS;oKvyDRi*z89u11^<^U18}@8Cf}9 zN8DkB*4D{o)?~C%UC=Zn&j{ZA=L;ineN*E)i%$5YNXPAjyW>8XC(x+fSVZaN-SnI{ z-Dkhl^I9|;vz{PGGsn#}@x$Juc0JubLP;CNK(Nqr?%>f;iC159)ldxoj6F5dn_AG? zI$Ki=AAX+gNl3dZZ}N-s*^TLCT|pghuz**hyy~xs%D1-Zu4@;W@ct4le~Q4aKFwX z*;7}+dSG?0$1cJTw%mv&)@atd;`QbqGN6KGz0U5a3A9p6P_O;h{k+YggWIPch8#M@ zHBkb(XZF2?96C}gaHb}j?ir>+oO|RZ*lAm4nkIELsC78y*NH_Z137oHTgoy?5U396 zT=>GW{o^4|-#j&gh%~JhS0kZv3Jg36pBL>Y7KjyC7v^FK4D+4((g)T@5S4^EY{qF;MXsfX308X|HA62RX( z&tZN1qU;DAq&;4kv&RJhxrRJJA=dOS3${;-vrP%H_1rz$FNP>1#_kC2B&a`^2H(xZ zvoFQHB6E|^Q)3+_M=q;(nH!}6VuqH)OxCVs5tJRo9SjGNn+e8%0Lg9rcvX2k07wGY zHtVU$jgyJ&Ej&HCP~o9eN#G~szc6qmZ7DA6<(K~i3W(zt^ebe zDspj|Oi102wBj3}q9kTXUy>ZkwJ9`DE8P+!aC=8n(hv9YFWl_+9=|cv@qsYIfp9bfM5cfnTAKUQgtGd<+A066qiyr1HMJz zIpPb(ogw+fplTe4AwI>O5ZH8W(4M8;ENe@%h)t_h(VFHc9Ut7~06rMlNJ8XpD5AI! zr0Qr8hN?h%&_B9Vk-?7q!w7@*2=f5p9gF|EqGEA=OpD6B>{IG10bqRuX-6x^Mg+KA zD#Uh&JP(I{&~}{kHkVC-P+T5aUT*X%-yD?y^w#89`RLj%mH6&(C_HuS)ErCOUR-ZR zsaCRzzAg^VE^k`#TQ_ZO2OlbXVq8^n9rLY=WK9bn7*(mF`UJl)?R{0ppU@S(SY(o5 zz8O7}BC@fte$?#0q0g{Ap4#xb)ue+6V@qhz^@M0%LUbf@@YE64R(+q}w@BG!88PRc z7z>tN{e7rAZY`0WOn;6mNA5Bl*Rf;B|KIG`5Ff7H(9LxGT&SdlcCL`cVM^(0=% z(nAW92tM!&Xw(4Sndok{TecN%eslrfeeI4zMavrEM?92z!H&# zDqvj~>~X>@R+Yd=(6OYG+4zwddG4BzS=lWd+qFN~CL4{0+~X|GpKokUo!A?{Cu>Ra zuy$dNwcEd#TrLX($+#@6In-it(e7tt4~n2+$LXQD++I*y*(ICxwRCz_AX?-S)nk+! z`&sB;1;m{%c;?ga=XPM%@(J``U&%^t5ZAcWTQB_^zD#ib$0?ZFnOoYK{yl|iP4Bo( z4wUcR+@L!nVoPh-abQR=*w>$1?EG{NXU0jL7;6k@8wECIrpm$8-{orxR^VriY(Xf0t|K4}QP`K%URmfAn>5 zHfk0g<*?EF8retamsOaTu%#7L$kW?fY}TBgNR37n8?}w8=1si#T6>fpg;~NoVII}0 zHs;>auLt?eZtk0kGkkq*HRf)gmErIM2`rO(E3C*cdh#}V zAR(r<)IIeK^8;miRS~bD!Sj?zfe_`T&W%4EEc~M34aQrT^`Rw$k`cspZNtwplH1|8 z71E~n2ETzzJymlBUII|IGi@Ah*UiN9$~?+bmy7G+FXxmk)1eTf*ZV+Wj_N#4g|8Vz zR<&ghO`*TZqY%Rb8{eG(jg*XN>D15itJ@%6vE8RGgwTlRSWaF-Cr!uN=JaXPLKfSQ zS3A!dJn@6~4TtP(_(uSB^1kq4r02@k65vq^y<9nVaJyFQT1a66OP?cSE_Qbj^H!8I zTP3Ffr%mv3H_PLolV9^tTnL#pJ<)<`%BP9S3Dc*rulthOPRf$VamY0bsK(o(O+brw zJA>{2@=@gHO%;edBl2|#MlDZ2Pl?E?OUJTcUN&Sly+|UgQ?pY^ejs!MnsN+h3f92b zP^>o;L0e+13)T@vZ|oao{P=PC&dxkQpE-c_|bt9 zePn)@{=|p}D~-xLRu9hfhA)_;vcI!Q z=jaG;IMTCj?`L#8Q@A*ez^fS469{*h-8YZ+&?o;E+sq*sTVM_l=6u;_dIXOBZjUEJqUm)|n#8oo8K(OgS1vB| z9GZE2D+_ES%=wBA0KrHJ402S1F9or4qmBg6>LR(yfOY{kp=5)%!Gp^#J~dtvg$|uZ zJypM;{$O{XjqOMGoW+9LG>#&1f!`0ZR6vh)KQayPhKmL=#ky{XfEs)SYiD18xl{u=p z5oy3bnqe~u+UpF{%w+y}F1eg;2!v*8=tESlo9x(W#CiXMfVAX23|OWXVmQ{!liGl) zXz_Y__g_@?#7f~{FJn<+b159ZIujhZo=%`-C+Ju$2|W~5%v7V{r{?%1V z573jUfRFF~tV*oKvG1Q<=#VV#@E56jLdFCSn<6PkK zo>-3qEm!F%4`=)JQ0xyo&q__OZkTNNSi1u;qvYO-899w%H}wJ@YCR~)r$`L=LrB*0 z@_qNPS+seiR97ME!$ zfAgql1&2UCG1*-qHG)b-t+%)29;$+RfN_PocIF2srvjr!-3ayyLdi<3;oi{x;xtxw z+!m6g3C|tMc5NC*65bKTREe=;;5yq;3+m^dJQ|I+4qmqMA@-(S{TRK2Mhwkbo|p|y zbW5OsNd>KKY0xWOmJ!xakes21^~Z+SA^~qZLa<*>yaA6T>aqF60AlN2A%B*nzjD?EMwk3J(kD3ofI3j$uA^sTOfTePC9sw^|C?xfn}CPjD`c56_5$^!mJ%@P`vvRCzn#!Fl0c4{ z)hnw%yt;ets=4>remaF0{4-rt{dSbo44Sk-t2jWxA^PT4fhe~{kD;x6OPD|djnXaU z(2~>?sYk(046Ool;NT$(Zdqa3oJ|*6+x))XVherui2OyNwa3y3e6Rm&X_YPk+uu|o zrWdw!AkH#rZ7_Rn|EqT>S#KiQP%qwoEGxtxu}Aw2y zH#>EcC?lQf%t)vJh2-RZYHDt7mW%zaGu4|OrE2Qg-h-su`Kz}iVZB048+o*{=GqE- zlw?<}!fu$sp3`xWqIzqhAv;59d974ht@hfv@ishCxY4G=6i}(k5o79EO`{oh@P6^$ zpx<-8(N$+352$_+p4ss7XGVQxE>%sb^wA}ECAFTsq>s~mT$2?jX|NhbSI}K)u3-G( z%~11lEuL}^2TGfEMMYcvQ6YJAY~4u&*qpeLo)vA-IW2C2U1^AE_97PKyzYs=@0eue zF4&;(w6yH$@p(B3f8o5}jN9$*d^h9BG<70h@x--H?s$rL;4F1MQWSIx1rpic553da(0&qqr! zgs2jo7zq*!k`8F^mB&~#F&Z4zSP9>YaWi%Xht~p!IOLLw zT|((nXr}x|P##j+%6YHK$gi!T-RYh%w3aDCsXDJw!b{Y;siY{kBep3NIFZ!RNaX5d zO7gb@6{}F%(ntvsW4Xp7?x$$$BGp!+IPC>}I^On6dn4=_cYUhYg;;PacOTn5GMl)R zuw-xXdz4DVBqCus6cx)L35$h6(J4PYUl{GE#t;~LsZvj8`p1B4)0mNm<-a1 zfys5}ISPm~DcV4a?j@gq=o6;)EJCIC1$ao^q0lDivKk`8pPwN*{v5hnYoXe76Hour zR#5P8@0OzvExc1V`f~ww?x8(3FfQ8Fv#qA7XFRY&J^w6FXugZ3P->E0F|IOVxw*<4 z9qGg@zf4@UW%p~i8q?F=uy!kQ7gHZ=8k8QJuODQ;ufz#Ga%v!AMGwp9kTvO6rb90A*GyBYW#xr0V_~E=x-(B$@+Pls>IO8Mv{)08yC3&`Thx5d%sAiG znlbvAzik)}vIS?1kSUaGg9V2bC4(ND`9WwZUqR4rWlc8EM;|$wYeQ6h(ZxpSj8aAs z73<5c=&5dY>qGj=h@A>*&eWVZ2d)Jb;C>>>s(374{C9ffi<;Ml%8pk?%*jokV- zbt?^lcpq3LX7KabJO*4ufY#9?@{w^#^ME-es|L(zt=vDYTqA;}SjZQ{y^Q9_S#+nN z{cPNRfD;!gvO!Qt1Z<1aZAygjj7DVE({jstz{&{*YG(x8k%4xc^6&T)GXD_K3oaC4rJ0!a?L;u_AtyyK{=}oEb%`P^f8d zg=0ZdX-h%(LEv6yiW}ZajVvr#rIa=W8#1b4kVaU9FlK-F6L;_}sMp_GauOe%Y<=a2 z(w8MQ1!G}d#-k9qk3YeLtL6$2_Jn#$9OI0!ASpv*U=>^5FTyNFh;W6WjXYwh2Y_#T zP=HX}Jo<0F;N5(18f5$Zsomdtw2TJ-oNa(5CanQXG83^(0|!T}6*ou2bU0oPCu09b zp1aNs^b*3y_6yYTo9BIeYRZQ0D>ZtZPx1VTv)+pRW=u`9ZS|&d_S=^vbtCOz(l^Rm zo8Dhcs{zpIQ*~#ywr6I|R-#t zx0+FAE$0$Qp&A%FL3|Ff;G_}ZdgE&8VRDMdwPnopIr|P6N}1hXDtRWj`sa!qLS6bYAVYN&Et)~XO&)|d?Yd3bmZ#UJ(cke^KpW|+^9@)c_{V}EQZ=7+ zaGJY84+1g8UN>mMItBG3cnu@7o<-K9$5JoW2E)vU5jp!6(kY z=t^QbZr#wf-~EW=?gM4r)8RY~5)TlTkv@3j&aXxKR&md0;54{tv3j8p>g-9VSz&%j zrz9|{L*Eio>&++K*24E221R(Lbdnj+MbK?2GZ3Bw{mzcK{d9DP^(cR}V*rRrtsFoAYzNRE}d4v+C!s(ob3IA9{}jo({E z+R+lt2t?DA{l&+Zz@b7bi|b=l(!0I5aY=TdQShTA5{01>4N9E*k}lGWuxZk z*eQ!sxxC3m9*j3a$R;Es-f?xTYhxELJ~$ls>VV6Gc_i_B#* zHhEDF*1f4#vJ-^871Eb;zyZrDf({v|#f}t2*wRKaIAyMy$lUP7c)$F_Ap#K#mXYms zz9()^^F7>gkvZ>KiMgqa`-I-c<#E;FL%}TS$i@`4%*U}5EGkkQ{tB_Pn`A3^o~?M$ zJ=id!@JghXh-!+RS4(z9l#>j=lHlZfIFc(Eg62EzuF{$Uiqn=Ui z^#LZlK1da06}_SAYsj$yEuN*lcCqXbWy1kqn90MBZPJ z2&G5y6}9#H%S%Coy!Q53;y|l4MTv3w6FBgla|yA+N~2I%(?rv;pd38WjSm@3gcD;-Z`uMLy>sp^#119=$fSnvGtknaNM zvLfP#94{_+I6^_U_i7erI8rdSI&RFToBEvDd;76dxn#Q*n#C;3&iR-7% zmm2-;vm*RUjs9tn{;l?2t84%5R-*Z>))L{NC2C1nzo>fL1r*42f@o{GU@P`LscIIO zGN4e$tD?AT_tdOk@twJqkEnlWz%%tiv<%>244m@2RzQpYP@=59O zaesTi#$^e|N~BoK4B27FX4JYPN!(vmh-AkZ0j@^#{qcg>?_nZ(7pMsp{E$iY`FatP zB=7I_GAV|IN{pxp*Xx&d6jZB;J+eqZtq(z4FW_t7L7@b!SRqEHV=z8cuaC26ZIg2asYH(w6h| zb~hfNuR05%(3SQ~S;*xe61}f^`1#H@?_p&e1KQ;x_gNMLG;aeiPPD7x>XqR@N)@`rWU?8+&2B#(u|PSI3)&or3?VM6kR zkY)@r+Jjw$2V}H)B}_^`F>a)5*pD;{PR->3&OGll;z?*@kTn=wNfSuN1*d@MPXesm zg^G740(&Ljk#5A*Gq!mrcG}pivt9Zu|pajj+mPqesN0^PoATW)i3aCO2kuPf@ z+XvsmyDS}=N*|#XktPHlou&vmK?2;I16_PmNRE7dd=Z8iEV}eNN;)Q$2SY|VsflY4 zZ#g_xbN0`;fj0BiRAfZn^E&K(=n;}yp`=ifq8vmjA(EU#j-{z^7)ELAT&+en9q&r` z*I)?cg}babtopS2j{khr`*07nY@#gcErEm@meKuXWy2sh} z(N-dD3#{EFqKi$L!)*}1$~q! zDHXv^-u6If-z1h4 zff!W@%Ui3$>Q+xndp!qp6j;l9y5uo(8+ zHg#FiZ4Hh@UkqBJ)x=(0&orR#20-uD9NS_ymu!I=yOoJ>J3{?CPWCpBkYDSw<0bf& z89l`Hpb4y$Sxw_m&3b_?y?>kHT!ZsEWKWqGFmA=UpY!w}WSWRD#K*R&i+TsTybR@o z^Etq@pWZpvt%Og+5lm0tj`PG3-qPFFwn^NKzGgW|+?ag#vt#ETs+%76fvz6+0{;vC z|Fk^i=fW>n{(}GZ-+t$R>1>=#9qgT4Or8D)fM)g0*uQcW-&J+E1}ZX6J@QPBzLhfA zBDoex?W2XFvB4E`i>QY66seS=702LDZ*xhhR0~R5kR`-0(V3^IDPmWMQ~jJlX-ws6 z^SQQ%&qTFERu9#?jYtsb!qz?|QEt+-m?W)0hJDKJeHemAIdVYam~C~8NPYzhElT}% zBJy_9_U2H=7aj0tv-M_)Qv-(=dDSo!%Do%86{Nz~4XSZdMgm(_e^mEo-YMjpNG&1t z0A7wI`TGwmC4CBLbD@uu1+GAT9mRtyMXY|Uv) z;w}YY8uy#6eD=F{q!uTg-}O7*l}5pG^SER@o1kRH&+mdI}D-8m3x+Hz}M3bU9n2Jw=a zM$@?l)W%XKi<}Y?jds3$(Bu~;vw+(}dZEyHS6sGsn0f;@lnk8^jWOO>1`5{vR}#ow zwe{4uQZ{T11;Sk@m(>v$KvRiFI5)O{IMe-saZfMSyS*pPVk&yJoorWLikf5E+Wg<1 z1``;#*DeamL%QX$H=W9{WH6yzscH8+YrOGPJpj-(zj#a*R@Ma(E4nuGFsQbJ(xPmy zpVv+Bywffh`aJItAby(bdLA|qNfXr@7_@0YHn;R7Y?k?Pdrd5i z&gSwrfNJz{-nE!^h(3 z7jiq`3gM|uxEgakT}rMe>1f{LI%Xuj#O6ZjUp{LLxst*%hmI=L5PUsPT0ajynyV}L z3ey!23>81p765ljBKug>$5#G9mpz~?8{2VYW1mY}H+)Ewtg z0Zp#Qku*i_AS8<=!j~g8&cVf=MEosIjqpCT;!~POe|E~Ij=>D`OK7Rb#C2erT2DC#VM*e%=4nxW>rNIjmBkvbe+`4Wq>SgBYDa z;iD}|JIsPId817!MQ_^x61N}Ln0U~##$DNtAF+!wZ zPpCvVU!o6RB=pH;GB9WbR)}1~46{=#&#T8Q#7UMpqoqYqB_{zq$9RqKCg0^PNZcVt z_Nw)}VEf}kBtl|K0Q{SeEQO1zjTcdP_3#yw)T<5ogL>tk zeAU88Xr?R0kx!TAdOx}O`6C)L>Cc6G=+V0F7sR!H+jBzSxwXFSUC)pgi9L21D_@&s z81FIdC?GIqXL?o2K6|cyRB88>R>FbbBo;_;=b=OWH4WM&C!>5UfLM{jBw1Pd z^@J67yeC(53hgZ9%;NmqcRe|sr(wa4RJH9AY#UvM6)9L z8=XcFxvo|FI%rNdEghZOY8F>QaD8@veZk;}a__s91PXmUFk^PBNOO8TMDKD#ME1x= z?Mt)HeFbXcgS}9>zMjt`5n_fS7=m+M7uxbpArd8>s(1nFD+iSyjMyS&;4NO~Jg=Fn zv6_*as$4P-8<3xN!fcPs0k8DSm$mpp)9LbIY}a@LR>k6^^n?``+puLW*d1`0*(@+l)2T?>lbwZw9OwJy+QBVF4x8|Zrr z$G*rr?i}Dr*-{gmc770DKc#jW`%iT}{2jg5E|2+ka@Xws2L)YqAmZ_;$n#IG2ug9O zX9%arW>*pob<1NBgb@fuim;D@&==0NWI&;!Om30zPa@&&kUCKf>RW0NBw{Z1TMz=r zn=ObU5hPQMXox;d^NYMH?nv8g+xbwr{9!8w@K@?r{)sqcKa|WuWelI#=kl!V>}$X8 z12!QGcxCP?P*Fii`Z{=>yV@w`V)`;kKfL^Mj`^0=TZc<>SrRhr$}vZvI3{^2Jy9P| zqJCUobgBaMHuoxNrtj_ELy|)!wxjpO&TbrTB%Rrw zU`NaH)moAZZ0E;FJd`yv@ z=PI{Hh6)F&B1(!tXb4yRw#@e|iA&dx`lkG&Ioy0TKK^GygH5 z?{4U1XK82tkCw7o%Qo(?1^G`;Z_qtSUA9H@%L<3akVOEI<%`IING1mr0%TySg*LaQ zs-#u-Ozmw~d?M9)=MLIEaBG7oLOAOzAFGHbN2V_HtBJg38h%mvq?h&LiHBILe^95I zO%t+Rsi+5>ei7;dRsM6aa|P06;hO40(mT!5qgye$w{BAkiUVMhAMWQWlBsQWV(1($ zvS8f?+vkQLK{f7$k)~#lBC%cu}p>#H|-l(-I$s!mb%uesgX{*7Kf6VQ#@E5>dl=U z53SAc>iv29-r9wYRh>>J9ugh#M-IGW+n1k<=!+@hpWXw30CSuUv*@U^i3ck=OcM_t-EfaaIUEa~glT z7jYMoeTdcCKIcv0)Gh2b*HBseo%o75{{bfIgXat&f$Hv-D>##JUPnUd2v~1&CD^~t zo2Enp<6)mrmDnM-i$aPXfEw;MF0!Y&$)++0J6;a{Em{T%SY&YR+#VLKqo##)>a#!s z4X|cTTNWa?=0o>ig-o|d^t&fmhsLcQ6zE_q?nZU=&<;BM;hue3U4J%|i_Hjqd+>#f zRx!N!yE>k|P7*+s^po(&5nkTK8FnBV1Qz5zK<~EKn{lW6qp>aUH8?v>egLMd2PL$j zof@|^=8N{C?^`h`_Tpy)R=O+5EMb48gF27-So_OB2Fi*peFRZ3JU2*qWK80jh$_6? zBB(dgpM#tt_9?l(0YEV#RlTToM~l?s^h)8CNb7*aJ3H*plm zQj(-7QDpt0TPlb2{9bt6vR=%wtGqb4bN6l`EltEn-E6R3Wl*}-AeRcp1Y$Mkaq9If zDQcp`Qf-K>svgKW;uEM8QK#*ko&}gDpnDx<07#sTPnws|Yjmgz4~{s8$POx+9vhTh z;`zoDqC@t&w4r@h6oFB-rr)#w5>@`}@HQDuP&;!;Tiu5hr{zr3xxR>gvrw+T4kLMH z!mBVa0n;A!-fsLk%g<~#I3a8wO>A)$N#R!9MM#yImvSa))MlMjHXx1ML6}7GlG&2B zKFuRg2gaJL?B|wE-=slb?85>=O=Kx>R@4n%olz`bb87t0A4Fjt#F?VN!*w`EOV+pb zNTAI;P-6v8_E-qPOFjZjnwTf|Nxu|~N>?~7e%jfRTC;?OJ*{4IV3p(whgnx(C{x|D zgeNE^^90Ms7fJ5O3lJTw{OlfCmOY24r*vKw?!qgiFOFABT3aQnQQ^d%N2neb4m8%p zX3K}iQ9J;HB+9NlVC~(;8j8YJ;X?q%GQ&v_*?Td~+un!49(fq3uxjkO%nNrm_^#zC z3nXIOMNr+_hRg=4|9LvyaUIb-pR0<$|0Va1DgHduKC-jDW4CYEDG#jKdKQx@%&*bW zL0@f~(*3n~$%&87Hky1_;xSnOh$TD{FW@Bd_NEA6D}d!jcmft!R_&z zbke%|f-9^jXtD2=4!}8eiXYG!L^{J@A`m&#OvDr&XMt{KhaM&fK}=Z)ajY)|4fjcMN2$MvpDB zA%%lbb~dv0A#G3%vtkQR9i@)1(R#@T^jM(}ZAb-aM4IB}=c&mzwlQ9P5zSfl6}51Z zPFl_s=m!HL+=31y**XJKn8-yZ5t#qp6~%ku$^ukotNW(}VvY-*D>3X|-!32+NFhva zN0Ukj!cd>6iu7CV0f%(zqh7%YB7vJ~v)v71Cso(9Nus?>dm~M+=g7QO_JaOC_&F@M zL_MC}(gc-`#zDLM7Mf zF&MHiaG2zkK$;hj*IlI8I05tm0Z(VgP2Fd(G05Aem`5lN&SVU^bs$(DG+z5wY-DO(1zG9bf5rPHB~gN9VJCfq{mbGOS!%&enK$i~e8UQp*n4JkPSIZy5aGh;Affx3QgNw&rlxOAAk~v|) z##HtK=mie;D}K5Yx^Z1FKZ?2e6sGJ&=olYWT&la6L?Q$_n{2HkY%Z*yT7ED}OkAPC zW*wdp(z>*Qfey!r**f;kCU027!ToFh=HzMAE*<*Ududm%{oQNv)Jvat$C(=(TRJjd zqb;ms(!7w|kK>?OF_e4obl8LYC`JMNI~opI_iK7G{vD~jxl8@KDeL@I1ZhhFze(bq zJ0N1&Bh!ThhAlQpAn)0!`|=&3eePts`~XlkI2(ng1X`P=&Q3wA(i}+am5AH@qI_kBVIG_`tK-;z{pIlE)u@>`N8e zO|Z!!DO2T!CIv6q13%zOsKvQmDP0iQ!i7yr5%n0@w70Qgmz{rzmO%7*@gju0p(0-x zaGEk?D0~K%nOk1?Q-=k2`210|ixt7ti?S_Vt%JdT0RM^sK~N!{S-;^ld9@=U-|Kgr zpA&|yLYA$6A8(3)z?D}7%)>_pA8Pr)o@*(oUF3sg!{YxopagpAcZOv1@9IPPqVZSRT89fDh{4Lk8u-oDg?X6w6WGHbLL#q}CH7bN43ZS#bpp>>U|`po zteq@cRG`{8G%g|(N+La1k2})BQ8{(|I$>r)@?z0FVX`DUuAMs#bbl~lrnb7m8C`r? zk|K4%E3PO4_c?;BMLHc8&j^2bCb%k3@S&SfO?KhggiVc)znqil*@W!ky|4xVH*sE< z@_JYQ;?`@vCC_&8gMA#BJxl9koNe1W5-|KrI%634P{y`7(CBoxF16M;71VStpZcX{ z_Y1~#zCiiuAUbUFs0)V67AEkxTsOQFn_#l$t_caq6623s2P6*Uj;3CNZvXSc=_00F zgeO{PHO3k4~g2{GtotvL6NE*E2A-5P}()R1w>Nc#o#C`Wq=VNBkj*` z|EKP%styOMn$3-BJzcJiUb{VXqmoyQaUTQV3Hp7&uc|o8o!^ec*|7u5waAdAoKiHI z5EC`SpC==TnT!(o0!%|?B%s(ou6rmlw|_@fSas>}r2;LiW_|r+x&S`(pZQw&v&szZa)CZ>3!)ZJaGn@<${G-a2%z;#?I=HF@=X6HqF>A2gS=4(^=D<5AFkaGs z_UAULpV#Tf=hN`Q8u9P%s8fjD*WSW-EKDcN6bD*5I|k?^0kXRo=4@Ia?&)EY=aX^G zBb~=fFQzm*-Hvjt&Oz3CP!0w5&0qzI%`Y4;WUBMCAH?A^AZ+|HVa{ZO`E%S)s9eJ6 zq(*#Vk*H<$?hMD{l*F8lHkhx@0^=IKcBco<8)wje1x4IALf{791UGgp+wLVDiarI7 zd1WTj@RYJ9Mtfd1eO4Nyy``y7(3W#{VScmUIsJ??wGJ?TmRbvjYS+6E3$*brBsK%v zOxDcHHsk5PFd}|mhKP6|=b{oL2b{?!@eJ%H&tuX}aY-qOj?7c(4PVNsd`StZT+dSb zME=pTZuN2j)d^qOJ+5$Bj3vLZjx3=>Qk zZ{VK1+=}KKX#0wY%0ydUu9`Yp`7j z4Xf9DJ+DM%$;tgWpsha4yXUyoTFN9X#jJb!9!se@$pw&7#dEgI#6;d?8gy`Dm3Rv( zMR|%Oszd31UYR4;Rt60IpU)JZeQwL`Ad?)O5?XetX1_*=#^0B{h z+CSBg!vQPxf@n=bqYc;xXY* z#@IC76Ru*DQB-NeXEBjVsc|eThL*sDn}v|JASE+FNm5nfV)Ci2XA&yt3dj;=A~&n9 zHEG1sf?JO@M@IqrW5gYLQ)@3G3^r{kqWcljoX^3xGY<2mS`6-46fvhzQXOGF_@{dx zAtXp4irW$#&rQs~P zdLL8*Z0h;KYi8)|W+jFd@0G)f$%Z5voop-6`l$7VmV`UXc8AwcJihBHYVnIK_vlppN$PE5U)9>DQ_%#u#;=*@2n5%1Pvj5Dd`m}QD0@UMJ z5}n*bPzFF) zTOFu5hxWpZn7k$}T)6;F#5XVB$g%OB(q1U{UT{wTz!Hca{dIeyiFizRd0}?uD_Jed zq{cKTN=T(rE)NLI3OaMvaYUW$oJBg`CG-A@0>iE7do;CTG+rw>2f@tkjqQlLWImzr zmr7Y^m>)1hATUL?8pKo`r~&qT43arR5d+ebG%WYfULs&x9r<3M1tffp_!U6UxHUrf zWj;sZ4l{`2CaBv#?}3Qu=mbb&*Dv}kveh&GB(Gl!LX^-C#-w0&=!_z-5LWd((sgz6 z;zB)Q2HuJvm%Ptjk7`0HhGaVqkj0c+qVsX~X3fRy8q=L7cTF`?;rmB2Rhsne{#)t& zryTWtEd%`aw~7_<_nzY4Yl{D~ko6yg{6EV6m{d9YO(wXo+dt@{UUj-)5N^0IU|5Q9 z)`;5-;BFSMLe1x*QFPdeTWsHcMZHGmcC7d~!5Dqd1JurK*f;Wb)z^Ip_q}6X z13p%MZ*yPr#GU5=^TFdckL@gg6+4Krhh+3(t3i zfUpiLxr0P^lcHNsT^{8k7p*CNxs&w#R@{#jrX1vE;nvBYpJ2R3sn4Hhac*Co!7_H|2(GM!^up;P%uj_bBPLUi*lPA6M=~G88Wa@mM}0%dKyB?ayDLwaf_w3u#(ca5NqCM31yYz| zY@L~BrAx~hi2RV4UxY9Xj@TR`{fhNg%FL8Vf8yfY-pR0-Qu{mr|GX*iKAgOUZ**HV zl|&v@=@S<44t}o0Vpirnt*%i?AVoYf>!TOP`5}AlTr|S7h!K@~oY`BnH4mIG>(d(7Pr55?odk%oLLW<4vBLz|jfk_e^oNOf&B@HJPr*U_= zCRBSy-WCe420YJqMuwfO@{rm!SDI#tjfoMXIS^wyk5gTj*FlqH?(Mn?tb+eyq(UI7Vx^1y1p*88&ucv;08(0C>HBAJX8dw?Z%%CB=rm>iT zgzhF@gs=s^LfT*abpUgEHYc^4@j0vFOYUYhHr{WuF1hnSi!NWwct^N#Z)%L2UXa? z1oqmeLcNLS2rK4W=uEOVgl}3@l3#~VKX)6wz>%N>nCnK(T-GV-f2pMNK=H8I^(}DB z&ai30PA^~N{a7%~#@)ddBfMJv9E1>jXOU4X^@)&~S^>~I1HqQ_#a+c2|Ghe>r$IJf zXqzi!65?X`ng`*Km-hwtIcsH&a9;5xca8jN2b{?hcFmL!qz3c~nV&AuB=PSi`&nSp`TR#2|3r?P|)cLL}I22b`zS5Hf$?JC?ZPBk$ z;*9wM8KSKsytLYnj{x*hc9pUiI+l71S) zdcJT9I4zLg?svPL`_Fy*Jj20Dc5}44VQyw6*LXB@&c6>KXK6YB z@^;ZzeSZw?EP`Gr3-Ve$3?mha;e*nd;zh-Wdw;RLr^^ojpI<^bKc0Y;F>~k>FALe> zC1i{9#|8aQ0;gJB%a5i7Z{7KW>$xIR0JfltG)=CD3e+4)sCDF``Eexu%_(x{n-&iq z?mR5B@q9uyleDiqWMoS=V#x*DQNhv798V&2C{ebcUMJQ|(XcjKNIN37-FV4L2UI)T zWq{Q@tFNX6o?uSmEkN`hH=o~8Mt$W%b{6&U42lSi7zR4^arKp+_oG*0ZLqR;N7uXeKpuP_gQK3Qr?bo#qk%!C`@xkkROzy(`emXfT;QYD-BpU3osqe=GS;o1|y5 zKUYaYDzB6eWm{K^Lgo`I?}5v!Gsm7t20wZrbGIN7kW_z&C99NPd1pN0`m8R4+R!)w z(^PaB$lY&_qwI3gZ9>7KwSC4nV*aqW5h@pc<}v8qYph6qw46jLZToH@X*)^Q(uuyF zbfkmz2BFZhP@zX873XF`O$#_Lac^Q)7bh*!DL;eyJXFmx7%tizmP;>QcCHer%Ntr$;qZ2<=93VUa*EeGnL<7R_ROo5bOUK!p^V>9|8w z*v~WC$;i|B9tlPan9^tZsSPY8!`-@vP}ehum|~G~`~}-GLZ@dzEEih*NyLm)N`~B_ zw`S)ONhJO?C8Hgv^UQS0du)u37DeZ=w9!NYNt20R$ucyiS+FcY*_Yy@c8gtkve|K_ z1tV5z;S9e^K7IhN5=^KiG%9<|p0}a0$!EL9x2?Ntl#@WK%6{864K(5sH@vXd93#>m zGoMGrES;Fyrp66l66Qn=b<bfcaW&yk18Q~am`^fXngw!arY^+DH*>6Wi>2Vjm1 zaDR2mN*=^yQLh`bzDc0J6rfFMq1!RS7Vj(;lb_;a$kECvGJ>~d5_!T zKMbhih8!-}$8?|f_HrOC^ySVi6i$k+JLVLoW0lOM5gVqS+;onExTCv@ z%IaDcfyOlr(^twB4=5%fKMPllC`;?m?K?UF`0>>CQ@@2l3=H)E+usM`IK(9s;p|IC zi6PT(sa9SMs7_k)x%-u4ZlTmAM7k{IOu0W|KMdC05%2M3j*#Gx(wT=4nW~p4h0Qt8 zvL~rQrqChTQYoW)6Ph;0O!gMkbE7GrK97zJ`)``Z1PGp*YWNm<`ikf3dGEc3Zj|(> z2?9;GZVt;Uc7G6bnfFcfUg=1aEv=?u(-7!dUVHCDSixU76;Xk4A-7eeCLu}r5!^Dz z(c&2TLtGqHON6m4s)E3x_ZE#V5%D%EDzSGViU3;OWTnRR$_DX~kBZFjP<3Tq4Qo>9 z5c+}$MG1UeYUae$!X@qKg-EicPn%m;91|94$NOZ5GxZV^(#=rJhgKW1vlU>O3&qZN zW#LvYGJOKd${Vn4)#BTLF7f~`Rwo8;nf#(G7i(9j(wwtCHsQ{0_YY0Va(THxBwV;u zv;08%y6vz!44+RXkfYD)Om7o>HX;w`U6TT+6J?inWpH;xxaiR<;>lgH6F^ZXli{nG z=R@ELbVkhLPFzA^))CXVt|&FZ=5v+OM7k4EeX%3F>HR;{KE*s8d#q$aXTK89-HcGF z+9JMtcLZ5;`ydqnzNl%u;6W=mMBccvV9<9h7rcJ%iF>iHgU&N&Z{X|Ud|oo8S%-z# zDFvQ87d;wu;)$G-wsNly&j-9IABi>t-#4xiFVuWDXGN?S{;0fLX z*k{BF4vfSNwy1e?(K(|JOa|E&b!3z1Ia5$TywljcS6$Ec+q>+$?i<68VUj6&+YTXv zkknVQ0h~(2qRy<9Lq3izaS4_fN5>UAvJ);Zk_sih!Z*cEi+&+ zLJ4k{8IQxa431I;Br9G02!1IC4FM0)j$nBGLPHDEynNv@P@{JpTFT4?+=YcORMTar zb+f}d`E>;?YW>Vj_FVS+2nw$1P48sW9E1Qt+_>R#?)#iMQ!^isNPaRT>oi2w-Zj79HzIFEGGss7%2WjEd zYVMzyLRh5(-%#6#@tYLyYQk9cX2}v(PMfTt5ICNWe0%h>HXMA9Rg@6bAr64#r^f%xOlCkSmX?y}v7iVj7i~rFCjx zyCTY~5FzynI(2d~e_lMz4^`D{oFqL?qZ<+fpCXV z!e$3p!-1hZ<&l?Uq+8IKYTu2`PpurFzscYGJh|f zFTk!pB*L3fK4u3pUQ1px(h4;S6U=k;B*!+)rxz+-&W}Xrv~pQD=1Zj}(;H(@Y>rnU zv(xK>Kb{a`3MBA(@m%7s<*#0J*N;jD=LYYps25nrG+cf_5lRj}jBU$x?|@?x-V?hw zn3QRUTY#VC3L713wCO|w8`JyTqgU}cv>C}tYjep1UW@{}uKmm8NR7a3FalKF_QoJ% zZm)ehh~o}NGxtvNumiUTiI|F3-g|kmg_)RVIkN!5wQ4o}_-2q2h6n*$T`?tu-kRFi zBx!~vqlYj$j9*MHZ~9u@ue|CTxV6N39m8l4L%LLpYgdcfphPRyJCarj@ho@Iv8eHI zC)?Q^3@}*98r`h0^85@e$3Hrv4KgE%v&M7R)rVhpMtTjM3$KKYtacjJVP?^XxqxTq zwD{##YMHzCE9WjM-+_0wMEah=1h2MwUtXuLKQ?PTKVg-23a+-?emiI6bXf13-pdGW z9$Cw)M`=jqHVw+@DQuD!1F8C`VdL+fi|!Wc??g@I!S}K%$2@m-+Y%crPL3;KKXD*2 zzPSMe713I*AtLt;d?8M(S$3{-`PxpbKsP(;%sIqv>_T|+`6OT7yZ)-;`fY)qRbl%y zL;aiRNx?2^!jKRe{A%XgjHjW2(h6<^j8T- zjQbQZH1zUOxpQbDSG{G9&jw&H;%Po(9cp%zGp?1SefX4hK6=I~8YxK4XsM0D#uaIo zT76n#;U#dTIfto$?vksCD4MtRY5o~#;sDhE1wl(b$u^Y}s@l&j8c}d97PFF-hm)LD zjJhROXl@Bl%BdJ!xU7VeGMULco+k@LjDO^_BtLl&7bnm;Per0lu@XwaYkt$)FJVfT zZHu=b)3h#*O;8iqnCV)`kA344RG2dr-Z`{*<^}q6D1I-?O>0Vy44TzGGBBcAru#(9 zt#s+rc>i#9az5g{dfj<6yNn4S9nrLjLvJoIZqfnVZ zVH0V3qKhcW%77GByQtlbDQJc17Z>0FVSfyqIjfjq9U*B3a~50NmOUs%?9Od+cY0cU zyxWv)UP@z-d@OCrWeO*`o$R~?C+oXng^X^4lu;-56zQ1G909i)pVfg+6{-g?gQ=0d zs(?>>cIRJ*I^U}?=HkWjaOjxyZBE_w#a3s4O^G?bwY9AXf*}_vk}Ti!p@~U&Xs46n z7Qc~I@e20^nIW6T2L|X8+^^uJh9|*yZHML`POcC(yNDGg;(#tj*!isJM$+kim_%bw zhT^A$<%C90y+A>JQ_(8dDn7LKCNTbsG*VZMfknwcN|HfKKEtm|@MG%z=B z<^RZ?<4tpT5~C?g+!G~cE8(WvTbNgU$mYHR?1T``JbGm%`RMb3@Mstm;6gXzFnXSu zI+(PsVk&jTP>~f~+?B6;O+Qi~>KPT5PNFA;4QFGy#y_NBbwcxXT z2J8X3iZP$%8@@t3{kr$wX1Ef=YKOV`<-$_ey7`}c-=>N>3j6NV=9a*U&<@}`^BukU z2qNNhP$`ha6SAU{QRnS}x4|uKE2r8)Ywn;+h3~N0d)p17+U7FdeB{%N<#jn94zY3X zlUmT$-1)_>FwJc3xGanHyAyAbLwN;B@*>}n-GVxsT6q6=(h?9l=B4-7;}x_xnWomt z7UiK}(jk%c@XgzL45EkET$m9d8Tp857^J6rfw;B5_D=lqIsBYv^4{#_^LOUULG|yS z!%gj6oIDL2>|bc3Gr+~eB~oE@jBa#HI+{@dFi1~L6SFPzS1VqnO0_pk7fxub=Ve0R zSQ;)eW{%R9(q;-(`c8g!c1HTJfJ?jA^{iqjN@PGlT`oC$1sAIzWAYF}4q595)M45X z3p%$^Dfv+ahK-G1b6I~3b%EaLi{4A{VEHA)O8)OdwKa7yG%<8B1XwxS+eHJH>|VkM zj~FA049Hw5t~zEmT@kfCuP*BA?8Q--ST-%{C{p65AjO}2^N&mzm|T3TNWbGp@bL_) zs^CV?8poGx*?47M00UzK+MU;SlHD;szSm832sIwiz$Oj_slkZn9F=iPL?l=HARf3T zWpuuL!{n_=nrvks7lSz2(fUSz9C0$I+ecMR$T~cj;^W}9JWT!AP<+Y=g6kzDvpwYtPmC&(_KyRbA<1^PaD3_ssCdJN@(DQN#d|5=PqyK2uS!bX zc}%`Ch>AO}b9|?c>(r(kswJwo7HVzpi6e4cOXh{;USY86Rp?VZ(FXqA1wd7dF96c*4$!y~)?89m!R$u4 zbbsX<7c*seIz;@4uSX9^{KoVc=XcPud1=hEH*5GghO~MTcV|8syN&|!s`=a%=4*QJ zIu!~MUp<}ZXv7!VAfzL9%OiRX>gXrIfkgHaqrlFPJhLx84tU_JPHaRjhCOacPT>3T~n@p_Xm$t4y)_b4!YM1{@ zed8ON|L;FkUK}VaCJYpQVS~;~96#7#z%1`(-}_qXg{n_4nJHWfn+xQw)}K>X7%{~} zP_5%%gQ(PB&7C%45sPc7Cmaor+E9iwpf)s&ITMXTsSSv!v)qW{q#qcOLmIYSEALp5 z7pD3snYWClZ!#UM%z6SwoX9^mqBbfV$l)5g+8R+9Wv;a~Mi3?9gsUW(hvw?C5YU;t zdo6sLIjW{Kh=cfEL(NiBQnIFYaEm<3)ei{R9Q)p%?W3&Wj36>sjhwPbK55kEja{r; zxF9tgm7!pYaY(NinX3Q5mbWYR@me5S&Cqc;^sxD>JY{JK$F0=E(-{$w|LsMUd#bec zl%-6DK1Ss99;t>ng#=lKnr3E69mSk@L4+8^My`U2nvf>FPaY=i6a{*1{cN@zonK=M zCYGt?#VZ}jVcByp+o9+QWC|*hZf~t&E9$V>ReBQJGX&ZF&mElQMCG5_rc80CHg)(v z5f&&%zmN9%T7*U>51UY4>_aT6bhQNis69mgDS4xFaZzfo_B#Dew@6}PP2Q?>V$l8S z>B(4-4=%5Z=x+V?sh5Qdmm$0};oHSabjSze>T#_98Qr5$A zk~KuuAux5`VXomkKu)o#hLUQSLHfDqC9&5}2&8H;n8e39s9uKoSAWc*#td~s5!WK| zij)b|Wq+ZuwMv0eRzxbrWH^R(?b!secx1|ZWAW7EgXZYu<1FRZ_znXOT`m%`t=}r+SfIe}5xZF@q*vl_3;7 zgTIqLqN5h_5EF+;A3#upUZ4;O8+zr{Hf#Cdpd+p78S9`w(XU%bF>OP~lGRuKHIp*v zE_P#VQRKZ*4pAcUm3jQEXcR@68Bo_Uqevc2A*%Ev$@AwB8TcZQ;j7Kspwjw2lv-98o44#4S~6ZmhaTZ%f?ligL%(Sn_2#{{>)QadpowtMyud$)M+~d8rF6%hYs3DD^=nH@QPWIhWgqr&6l-fZ`vt zgN3^YBannEW~i85!tX4ayStxF<%vs>$T=YMb>m4X7D+)e(Rrr5%j%W_7Y1YpCMgoe zD{MBNJ0~SbvMB&2En^i{FO0v@X&eN7ICtR)H3=&V#L@3BQ_n%JSO;3s9>N1Oju-WF z$*<^5>LMm*3$mOkb32|h(^H>&$MC^cPfT}33q3s*a`n7^(pS9ZuomdlZU5cMC2FL* zK03gs!NPIf%U8~c6DQE+FZ31Gq(cVf)-rSAUHC^K?GIwi(PR!3P>M}q#?yKkKDF2V zq74EMK%LzZQXjc@48Zt7rSt_9U!Er2-qcakq~2>f zPIwq+64WS57_3#=Bsr6Z@qksLW+@+j3|YO%MqTRuIh*dh6>v=d4?eK$f)4M=ZD-u1 zeqdK~SESMsGg&lpxi3&3@p{;-WG;a>xr5{wCb}|l^?_sMX}2XN&{VhGR5RYwu8_G( z0NZRj^r534&=pHMo+oMm1jbSlask&yJ|ydtIjfX=L&sYOD0zHuv@%c|W$K&*2w0(P zL+X>Nhk(gD@4JQbCSGI#hzPMh_5z~;;R~<@1hezocduY-t9q-?fR1l-aiItveSOA& z*a8NH!WFGzIqtV78=GSqpIc@>E<1(^!QV|tj|;+(0#)(+i@UkFJoLs85xen5)PmlX zsOg$418fVv(?zi7Up5IbbZw)c-%C9=QlslmQ;VfTBci__RHDu0!X!$Uy6=bSZTMslj3wzrQL-NS>CaSJ{V+{=mQ%Ht#xm{AwRr z%%Cd_$!0D1uw~P!>aWwJmOASh(uP2##hj$*=>=DXZ zV)TrCG&}rVkpa_ebilN(0p=bj!#4vAXr}Lb_>FYIFXU7Ow zWAu)qFk#jgn#r&1=Gi5vi=o@$hN{-*LlC)HA-O`5?t_Hp-fUj^scz*(jU0PY`DE!F zFNG0-FNU5q6im1PE*_Tosi?P9nhRKoKO0m%Cd=AhqMNhdLd^{nzc1R4s@vjA*0sr% zqB#~(&qIDkH+RSd+Tc`d9#!0X#mO2r$bu6NkF13$@um`|{lNit+{#S$7mDx@Y~ z9DM#BAsvtw7>@d8w-Aljd}Ta*R2%Ut&-<=@tQWp&H-8zJ$7q5NIuI4}Mxl2TIWlJ1 z+vi&v#eC_|TU++0&F{%2nt^1zt>#)zw$Kj~Z%o{J7TkjbSU17mT-NM7pWi_|Gal0O z;<|JlAC=}E+c3!FRm4tO*lw;Ja+Wjm@ZH`;Q|al3wTg6`Hy~$z0+oGU3Md!@pFM8O z{oXTO+n(T=WDc&_g~%r^{Om!;S0})o3oqY}){qsTvxgbfPh(qRB;vhKimphApr)~m zryL123SHS}tV2O8^3;TRrl_e#lR(q>p`la79bK&})e8r)c^)g%t!0k?*6LoOC_hEm zW8=aJU+j^XXEitFbJ+Jrp@c}t#cv?%jL7L6K5<9@`bMuESx&EZsgzi?%mOeg7loI*3e zU4!UA`xH^IJpm2+812IQX*8)cyei{6Pgy3W-3Y{B^L86|WWa&TLDtiWc2=#_MV_q* z>?=WWXzW*7E#;c;q8?UkUI+9JPA>7~p0~f|A4wj;1cCD)s3SS4w$u zhGk$;ZF}uRc+dctjSY(<(*#mnos7qmYBKIp)~)Tk670T6vR`x`;v^iVPu~sz;4XR8FIH(wjEDus$CtWmKY`4 zCo*0AjTZarbESji_r3jT=QjyY(`ZZFL>Lj6S+ZIh<=h*yK@uQZlmL4_$@h6l$b?@OF|(C!maeg$8+e-i{m&}=t7#v053>cIzAs_udboa? zL=?cq3rJ5@<-i`Cn~^8AYEt@6m(}K;o9GtwU%tvH{ztyL{uf^*|G`&XUA}g{@Kudb z=a*6>V;7X0qLX}AZ?~(8tG7vhRz)v-)$Mn_I^#(a!IS-AB=!5={20rGtX99(!u!W0 z=KCNJ-^HO`o32I9?(#*AH$o{o6cFb+T*^g->*gAJwt-k7Nw!#3!Z7K8Y?RJC#lQKg zTl$Wf{wl!>UrkRd1*n8)j3@S-^5+$k`^y6j`llQmCd%*aCh`=QeQHMaOORfYj!?5M@S3N zXM|P6>%#-T;SH-#aRLrX5x?RKNKYs&(rkPXcKUE|>*9NTa(8kNbn3nCUM;fCSwc4& z<|idvH+BGNimd|t>Q(8ujZIn}R@Z^X2DMS-lyyWpe}L`@i73I4`7OE+?kq;K&Dnck zmQvN@vvZ~dYnI`H0ec;^No>6amx7~M5MbH!7p~g;ldGEB@7~O`3ep}B)_pqCBB(fkEqZQi_Hsz&RnB}O-JrX4V=dgQv8?@A{KXsH~VN?2*WMCW9Yhlk1O zB~q6?w9DP1B(=SU4C&N}I`3x>%|Cv8ob79@B*V@JkA)9i-ZoF^K1q%2Ntk`%s`h4! zms~c)G(~`g$^6wK`11nAI zkV|TS5q+q6anHlAx>U=ob))+Fq`hsii{Q-j%%fF~97RyNSn(+ue=MSoC`;)2$ z|Cg$M|1AFMBwQ0onQJX5gpMo!KF}5n+&sjtd3<&b;EkX|+hq&Si>sP1gT{P=E1eu`jOh`vq!31+=&xH$AwH)}2W;_S-%O~)?VL7_Gqdj4mhm)_7E zc52X|6phLIljwc1JRO_6Q_=_#d`^SWz)6~rCZN0bi9U^dl-pk!Q-F|1wPea-6C#{K z8si;qVL~9=IZ#ucoF>lq22838bPk`*Z5uURiN7Jqj~{04>q0DsCGIZDVlPtZtMrVl zaYC{e^-)m4n*vq>u<U;+#W{UF5T|^DMXci280@Tm?+ssGj*W=P#^E_YYRp(8J*vYL*BYz2HqLP!jt}8u7v@`(FSJ!M{Tl6>(8T6*1T8f%uP7=#Xo^k{9Vh z?4m3TULE8K#JQx^1Fs=*Oi_LAuD*tS{I0I?>4oT}q&{L!=uI>U`BZ2fuu2Y zlTdw371Pvv#QFS9{2&O{l*LTFwDuCQ`hv6+QXEAmQ?|>%IQ%1Ua6$VnC0TvH;&cSqz!Ea3_S4VvIo zJ)fs&A=lh2h?<5k7Nh3agt&zKv~5=)HsQp|I6o=s^j21ncCF0J8Cef;d2|Ls^DY1h zIW1=oiN=0OFiU*~^_`eTb}WF6^Hb8>?6porfEG{6s6%Nfj7rM9v(al({7~PWi&76p z1$d@H`4T>tV{cI%g7Tsw7D*z^l8CZW)Uy8SUg;)7&$l2Uk1lul*7wZ&26(WDuH173 zcWaRi9Y?`fySIjsbJAPD47DCURuvoBNtH+t)aR&@Z2bzc(Og$5da7?taJqSQf|E}B zTH~6r+On|bBI&!}`eQ8yq~JmR)Ki*3#;7d}i|w=x&r>yuQJ^#9fo!p+ae#P;_{o{k zh*ztdG>51|@tx(V1upO!?S#=B#+QYZ&<(g-jN5tk-&lp90d0n@o~|~@eP%k zF?xVr)wNU+6;*Rsi6_TJ9IY*wS&MzL@AP*i3Fr)n)l_j-N-d%&@q2AYu491Vfq^lv zq^MGRQ*e9c1~Uqa<9{{BAC2+3OdqcMosoftL~s_BO^#fqZ?s zk+-&(>}uRf5@7k-yBGXixEW?0ZONAd?hdAdW=C>0G4@M0(XIO&7T}Zj#I=`~@pNtm z`da=zCOXaM9G?yk8~%VCjtO=iKD{W)_E@LPC}I4ljwM%%boZ>j<(B&Jv!LY)V1jkf z$c!msm619or`RQ?1(d#us%sc>28iIH-uyGDszqEh@7+<=cnDa-^u0I#*OV$~_o4i+ z1*%w(9CdI~v6Ag$j$G9Z&g9^?j$QL^l`=c|UJQ`FW!jod%A>n;ORc(HH+Drv7|XEm z(X#xZVv$RskVxQYx3@<%v_ng=AIzY5srnRPlgSW@D|0#Sw@t>A`SD8wu=@Zi7g1j} zzB==)k5%2jM2i268_Q6kqPn|6IVY+sXZE@FM~L_r1k_8n{CKH2BX>W525Z zv?te3wfD<|;h)q0w%m3%xe|M?+Rr=%9zxRUrDP^bquk@eJP`_&b z)F$VrR-NI$+W%>q^Q+6BTIT$8iTfXy-y7)s>hGjC-+lc;eedw=7e=3~% zX+$LSC!;^jiXQ{|y`JQ!kCN!0eEx5B$?v23J(>Nd8;Rtf-2O1#{*(*;(}w2dHu3j# z`JZ^yZ<*o04((5I?Vp}i8vp9~dzAZk)88Y|KTUNt|JC&W#G`+8{8P~Ir{lf$zdHUW zg!rr3k8A!tDEQONLg!!2{!fVTcgNpjgFhW>{@wAfNa62JzX$VvI?3t&>)8Gi+WXz? k_qXexX3)0(YWDZLwvsHw%YFMt?FA0V#>;Objz9kTfAVzvJpcdz diff --git a/python/dependencies/pep8-1.5.7-py2.py3-none-any.whl b/python/dependencies/pep8-1.5.7-py2.py3-none-any.whl deleted file mode 100644 index 7ba4e74bcc2ec1cba3beaf77b4ae9e5a5b987d9c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37490 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
0?8hKX^4yYT`d7UWZTEjH5V4nM55O$`S<_gny< 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 BA*=uZ diff --git a/python/mach_bootstrap.py b/python/mach_bootstrap.py index 84de5b22b25..e04d1781bdd 100644 --- a/python/mach_bootstrap.py +++ b/python/mach_bootstrap.py @@ -6,7 +6,9 @@ from __future__ import print_function, unicode_literals import os import platform +import subprocess import sys +from distutils.spawn import find_executable SEARCH_PATHS = [ "python/mach", @@ -73,6 +75,39 @@ CATEGORIES = { } +def _get_exec(name, default=None): + path = find_executable(name) + if not path: + return default + return path + + +def _activate_virtualenv(topdir): + virtualenv_path = os.path.join(topdir, "python", "_virtualenv") + python = _get_exec("python2", "python") + + if not os.path.exists(virtualenv_path): + virtualenv = _get_exec("virtualenv2", "virtualenv") + subprocess.check_call([virtualenv, "-p", python, virtualenv_path]) + + activate_path = os.path.join(virtualenv_path, "bin", "activate_this.py") + execfile(activate_path, dict(__file__=activate_path)) + + # TODO: Right now, we iteratively install all the requirements by invoking + # `pip install` each time. If it were the case that there were conflicting + # requirements, we wouldn't know about them. Once + # https://github.com/pypa/pip/issues/988 is addressed, then we can just + # chain each of the requirements files into the same `pip install` call + # and it will check for conflicts. + requirements_paths = [ + os.path.join(topdir, "python", "requirements.txt"), + os.path.join(topdir, "tests", "wpt", "harness", "requirements.txt"), + os.path.join(topdir, "tests", "wpt", "harness", "requirements_servo.txt"), + ] + for path in requirements_paths: + subprocess.check_call(["pip", "install", "-q", "-r", path]) + + def bootstrap(topdir): topdir = os.path.abspath(topdir) @@ -84,6 +119,8 @@ def bootstrap(topdir): print('You are running Python', platform.python_version()) sys.exit(1) + _activate_virtualenv(topdir) + def populate_context(context, key=None): if key is None: return diff --git a/python/mozdebug/__init__.py b/python/mozdebug/__init__.py deleted file mode 100644 index 54d5b4d5de8..00000000000 --- a/python/mozdebug/__init__.py +++ /dev/null @@ -1,30 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. - -""" -This module contains a set of function to gather information about the -debugging capabilities of the platform. It allows to look for a specific -debugger or to query the system for a compatible/default debugger. - -The following simple example looks for the default debugger on the -current platform and launches a debugger process with the correct -debugger-specific arguments: - -:: - - import mozdebug - - debugger = mozdebug.get_default_debugger_name() - debuggerInfo = mozdebug.get_debugger_info(debugger) - - debuggeePath = "toDebug" - - processArgs = [self.debuggerInfo.path] + self.debuggerInfo.args - processArgs.append(debuggeePath) - - run_process(args, ...) - -""" - -from mozdebug import * diff --git a/python/mozdebug/mozdebug.py b/python/mozdebug/mozdebug.py deleted file mode 100644 index 3f3a54d6be4..00000000000 --- a/python/mozdebug/mozdebug.py +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env python - -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. - -import os -import mozinfo -from collections import namedtuple -from distutils.spawn import find_executable - -__all__ = ['get_debugger_info', - 'get_default_debugger_name', - 'DebuggerSearch'] - -''' -Map of debugging programs to information about them, like default arguments -and whether or not they are interactive. - -To add support for a new debugger, simply add the relative entry in -_DEBUGGER_INFO and optionally update the _DEBUGGER_PRIORITIES. -''' -_DEBUGGER_INFO = { - # gdb requires that you supply the '--args' flag in order to pass arguments - # after the executable name to the executable. - 'gdb': { - 'interactive': True, - 'args': ['-q', '--args'] - }, - - 'cgdb': { - 'interactive': True, - 'args': ['-q', '--args'] - }, - - 'lldb': { - 'interactive': True, - 'args': ['--'], - 'requiresEscapedArgs': True - }, - - # Visual Studio Debugger Support. - 'devenv.exe': { - 'interactive': True, - 'args': ['-debugexe'] - }, - - # Visual C++ Express Debugger Support. - 'wdexpress.exe': { - 'interactive': True, - 'args': ['-debugexe'] - }, - - # valgrind doesn't explain much about leaks unless you set the - # '--leak-check=full' flag. But there are a lot of objects that are - # semi-deliberately leaked, so we set '--show-possibly-lost=no' to avoid - # uninteresting output from those objects. We set '--smc-check==all-non-file' - # and '--vex-iropt-register-updates=allregs-at-mem-access' so that valgrind - # deals properly with JIT'd JavaScript code. - 'valgrind': { - 'interactive': False, - 'args': ['--leak-check=full', - '--show-possibly-lost=no', - '--smc-check=all-non-file', - '--vex-iropt-register-updates=allregs-at-mem-access'] - } -} - -# Maps each OS platform to the preferred debugger programs found in _DEBUGGER_INFO. -_DEBUGGER_PRIORITIES = { - 'win': ['devenv.exe', 'wdexpress.exe'], - 'linux': ['gdb', 'cgdb', 'lldb'], - 'mac': ['lldb', 'gdb'], - 'unknown': ['gdb'] -} - -def get_debugger_info(debugger, debuggerArgs = None, debuggerInteractive = False): - ''' - Get the information about the requested debugger. - - Returns a dictionary containing the |path| of the debugger executable, - if it will run in |interactive| mode, its arguments and whether it needs - to escape arguments it passes to the debugged program (|requiresEscapedArgs|). - If the debugger cannot be found in the system, returns |None|. - - :param debugger: The name of the debugger. - :param debuggerArgs: If specified, it's the arguments to pass to the debugger, - as a string. Any debugger-specific separator arguments are appended after these - arguments. - :param debuggerInteractive: If specified, forces the debugger to be interactive. - ''' - - debuggerPath = None - - if debugger: - # Append '.exe' to the debugger on Windows if it's not present, - # so things like '--debugger=devenv' work. - if (os.name == 'nt' - and not debugger.lower().endswith('.exe')): - debugger += '.exe' - - debuggerPath = find_executable(debugger) - - if not debuggerPath: - print 'Error: Could not find debugger %s.' % debugger - return None - - debuggerName = os.path.basename(debuggerPath).lower() - - def get_debugger_info(type, default): - if debuggerName in _DEBUGGER_INFO and type in _DEBUGGER_INFO[debuggerName]: - return _DEBUGGER_INFO[debuggerName][type] - return default - - # Define a namedtuple to access the debugger information from the outside world. - DebuggerInfo = namedtuple( - 'DebuggerInfo', - ['path', 'interactive', 'args', 'requiresEscapedArgs'] - ) - - debugger_arguments = [] - - if debuggerArgs: - # Append the provided debugger arguments at the end of the arguments list. - debugger_arguments += debuggerArgs.split() - - debugger_arguments += get_debugger_info('args', []) - - # Override the default debugger interactive mode if needed. - debugger_interactive = get_debugger_info('interactive', False) - if debuggerInteractive: - debugger_interactive = debuggerInteractive - - d = DebuggerInfo( - debuggerPath, - debugger_interactive, - debugger_arguments, - get_debugger_info('requiresEscapedArgs', False) - ) - - return d - -# Defines the search policies to use in get_default_debugger_name. -class DebuggerSearch: - OnlyFirst = 1 - KeepLooking = 2 - -def get_default_debugger_name(search=DebuggerSearch.OnlyFirst): - ''' - Get the debugger name for the default debugger on current platform. - - :param search: If specified, stops looking for the debugger if the - default one is not found (|DebuggerSearch.OnlyFirst|) or keeps - looking for other compatible debuggers (|DebuggerSearch.KeepLooking|). - ''' - - # Find out which debuggers are preferred for use on this platform. - debuggerPriorities = _DEBUGGER_PRIORITIES[mozinfo.os if mozinfo.os in _DEBUGGER_PRIORITIES else 'unknown'] - - # Finally get the debugger information. - for debuggerName in debuggerPriorities: - debuggerPath = find_executable(debuggerName) - if debuggerPath: - return debuggerName - elif not search == DebuggerSearch.KeepLooking: - return None - - return None diff --git a/python/mozinfo/__init__.py b/python/mozinfo/__init__.py deleted file mode 100644 index 904dfef71a7..00000000000 --- a/python/mozinfo/__init__.py +++ /dev/null @@ -1,56 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. - -""" -interface to transform introspected system information to a format palatable to -Mozilla - -Module variables: - -.. attribute:: bits - - 32 or 64 - -.. attribute:: isBsd - - Returns ``True`` if the operating system is BSD - -.. attribute:: isLinux - - Returns ``True`` if the operating system is Linux - -.. attribute:: isMac - - Returns ``True`` if the operating system is Mac - -.. attribute:: isWin - - Returns ``True`` if the operating system is Windows - -.. attribute:: os - - Operating system [``'win'``, ``'mac'``, ``'linux'``, ...] - -.. attribute:: processor - - Processor architecture [``'x86'``, ``'x86_64'``, ``'ppc'``, ...] - -.. attribute:: version - - Operating system version string. For windows, the service pack information is also included - -.. attribute:: info - - Returns information identifying the current system. - - * :attr:`bits` - * :attr:`os` - * :attr:`processor` - * :attr:`version` - -""" - -import mozinfo -from mozinfo import * -__all__ = mozinfo.__all__ diff --git a/python/mozinfo/mozinfo.py b/python/mozinfo/mozinfo.py deleted file mode 100755 index 96847fea01f..00000000000 --- a/python/mozinfo/mozinfo.py +++ /dev/null @@ -1,233 +0,0 @@ -#!/usr/bin/env python - -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. - -# TODO: it might be a good idea of adding a system name (e.g. 'Ubuntu' for -# linux) to the information; I certainly wouldn't want anyone parsing this -# information and having behaviour depend on it - -import os -import platform -import re -import sys - -# keep a copy of the os module since updating globals overrides this -_os = os - -class unknown(object): - """marker class for unknown information""" - def __nonzero__(self): - return False - def __str__(self): - return 'UNKNOWN' -unknown = unknown() # singleton - -# get system information -info = {'os': unknown, - 'processor': unknown, - 'version': unknown, - 'os_version': unknown, - 'bits': unknown, - 'has_sandbox': unknown } -(system, node, release, version, machine, processor) = platform.uname() -(bits, linkage) = platform.architecture() - -# get os information and related data -if system in ["Microsoft", "Windows"]: - info['os'] = 'win' - # There is a Python bug on Windows to determine platform values - # http://bugs.python.org/issue7860 - if "PROCESSOR_ARCHITEW6432" in os.environ: - processor = os.environ.get("PROCESSOR_ARCHITEW6432", processor) - else: - processor = os.environ.get('PROCESSOR_ARCHITECTURE', processor) - system = os.environ.get("OS", system).replace('_', ' ') - (major, minor, _, _, service_pack) = os.sys.getwindowsversion() - info['service_pack'] = service_pack - os_version = "%d.%d" % (major, minor) -elif system == "Linux": - if hasattr(platform, "linux_distribution"): - (distro, os_version, codename) = platform.linux_distribution() - else: - (distro, os_version, codename) = platform.dist() - if not processor: - processor = machine - version = "%s %s" % (distro, os_version) - info['os'] = 'linux' - info['linux_distro'] = distro -elif system in ['DragonFly', 'FreeBSD', 'NetBSD', 'OpenBSD']: - info['os'] = 'bsd' - version = os_version = sys.platform -elif system == "Darwin": - (release, versioninfo, machine) = platform.mac_ver() - version = "OS X %s" % release - versionNums = release.split('.')[:2] - os_version = "%s.%s" % (versionNums[0], versionNums[1]) - info['os'] = 'mac' -elif sys.platform in ('solaris', 'sunos5'): - info['os'] = 'unix' - os_version = version = sys.platform -else: - os_version = version = unknown - -info['version'] = version -info['os_version'] = os_version - -# processor type and bits -if processor in ["i386", "i686"]: - if bits == "32bit": - processor = "x86" - elif bits == "64bit": - processor = "x86_64" -elif processor.upper() == "AMD64": - bits = "64bit" - processor = "x86_64" -elif processor == "Power Macintosh": - processor = "ppc" -bits = re.search('(\d+)bit', bits).group(1) -info.update({'processor': processor, - 'bits': int(bits), - }) - -if info['os'] == 'linux': - import ctypes - import errno - PR_SET_SECCOMP = 22 - SECCOMP_MODE_FILTER = 2 - ctypes.CDLL("libc.so.6", use_errno=True).prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, 0) - info['has_sandbox'] = ctypes.get_errno() == errno.EFAULT -else: - info['has_sandbox'] = True - -# standard value of choices, for easy inspection -choices = {'os': ['linux', 'bsd', 'win', 'mac', 'unix'], - 'bits': [32, 64], - 'processor': ['x86', 'x86_64', 'ppc']} - - -def sanitize(info): - """Do some sanitization of input values, primarily - to handle universal Mac builds.""" - if "processor" in info and info["processor"] == "universal-x86-x86_64": - # If we're running on OS X 10.6 or newer, assume 64-bit - if release[:4] >= "10.6": # Note this is a string comparison - info["processor"] = "x86_64" - info["bits"] = 64 - else: - info["processor"] = "x86" - info["bits"] = 32 - -# method for updating information -def update(new_info): - """ - Update the info. - - :param new_info: Either a dict containing the new info or a path/url - to a json file containing the new info. - """ - - if isinstance(new_info, basestring): - # lazy import - import mozfile - import json - f = mozfile.load(new_info) - new_info = json.loads(f.read()) - f.close() - - info.update(new_info) - sanitize(info) - globals().update(info) - - # convenience data for os access - for os_name in choices['os']: - globals()['is' + os_name.title()] = info['os'] == os_name - # unix is special - if isLinux or isBsd: - globals()['isUnix'] = True - -def find_and_update_from_json(*dirs): - """ - Find a mozinfo.json file, load it, and update the info with the - contents. - - :param dirs: Directories in which to look for the file. They will be - searched after first looking in the root of the objdir - if the current script is being run from a Mozilla objdir. - - Returns the full path to mozinfo.json if it was found, or None otherwise. - """ - # First, see if we're in an objdir - try: - from mozbuild.base import MozbuildObject, BuildEnvironmentNotFoundException - build = MozbuildObject.from_environment() - json_path = _os.path.join(build.topobjdir, "mozinfo.json") - if _os.path.isfile(json_path): - update(json_path) - return json_path - except ImportError: - pass - except BuildEnvironmentNotFoundException: - pass - - for d in dirs: - d = _os.path.abspath(d) - json_path = _os.path.join(d, "mozinfo.json") - if _os.path.isfile(json_path): - update(json_path) - return json_path - - return None - -update({}) - -# exports -__all__ = info.keys() -__all__ += ['is' + os_name.title() for os_name in choices['os']] -__all__ += [ - 'info', - 'unknown', - 'main', - 'choices', - 'update', - 'find_and_update_from_json', - ] - -def main(args=None): - - # parse the command line - from optparse import OptionParser - parser = OptionParser(description=__doc__) - for key in choices: - parser.add_option('--%s' % key, dest=key, - action='store_true', default=False, - help="display choices for %s" % key) - options, args = parser.parse_args() - - # args are JSON blobs to override info - if args: - # lazy import - import json - for arg in args: - if _os.path.exists(arg): - string = file(arg).read() - else: - string = arg - update(json.loads(string)) - - # print out choices if requested - flag = False - for key, value in options.__dict__.items(): - if value is True: - print '%s choices: %s' % (key, ' '.join([str(choice) - for choice in choices[key]])) - flag = True - if flag: return - - # otherwise, print out all info - for key, value in info.items(): - print '%s: %s' % (key, value) - -if __name__ == '__main__': - main() diff --git a/python/mozlog/mozlog/__init__.py b/python/mozlog/mozlog/__init__.py deleted file mode 100644 index bfa23eae645..00000000000 --- a/python/mozlog/mozlog/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. - -""" -Mozlog aims to standardize log formatting within Mozilla. - -It simply wraps Python's logging_ module and adds a few convenience methods -for logging test results and events. - -The structured submodule takes a different approach and implements a -JSON-based logging protocol designed for recording test results.""" - -from logger import * -from loglistener import LogMessageServer -from loggingmixin import LoggingMixin - -try: - import structured -except ImportError: - # Structured logging doesn't work on python 2.6 which is still used on some - # legacy test machines; https://bugzilla.mozilla.org/show_bug.cgi?id=864866 - # Once we move away from Python 2.6, please cleanup devicemanager.py's - # exception block - pass - diff --git a/python/mozlog/mozlog/logger.py b/python/mozlog/mozlog/logger.py deleted file mode 100644 index 60bd4912fb3..00000000000 --- a/python/mozlog/mozlog/logger.py +++ /dev/null @@ -1,180 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. - -from logging import getLogger as getSysLogger -from logging import * -# Some of the build slave environments don't see the following when doing -# 'from logging import *' -# see https://bugzilla.mozilla.org/show_bug.cgi?id=700415#c35 -from logging import getLoggerClass, addLevelName, setLoggerClass, shutdown, debug, info, basicConfig -import json - -_default_level = INFO -_LoggerClass = getLoggerClass() - -# Define mozlog specific log levels -START = _default_level + 1 -END = _default_level + 2 -PASS = _default_level + 3 -KNOWN_FAIL = _default_level + 4 -FAIL = _default_level + 5 -CRASH = _default_level + 6 -# Define associated text of log levels -addLevelName(START, 'TEST-START') -addLevelName(END, 'TEST-END') -addLevelName(PASS, 'TEST-PASS') -addLevelName(KNOWN_FAIL, 'TEST-KNOWN-FAIL') -addLevelName(FAIL, 'TEST-UNEXPECTED-FAIL') -addLevelName(CRASH, 'PROCESS-CRASH') - -class MozLogger(_LoggerClass): - """ - MozLogger class which adds some convenience log levels - related to automated testing in Mozilla and ability to - output structured log messages. - """ - def testStart(self, message, *args, **kwargs): - """Logs a test start message""" - self.log(START, message, *args, **kwargs) - - def testEnd(self, message, *args, **kwargs): - """Logs a test end message""" - self.log(END, message, *args, **kwargs) - - def testPass(self, message, *args, **kwargs): - """Logs a test pass message""" - self.log(PASS, message, *args, **kwargs) - - def testFail(self, message, *args, **kwargs): - """Logs a test fail message""" - self.log(FAIL, message, *args, **kwargs) - - def testKnownFail(self, message, *args, **kwargs): - """Logs a test known fail message""" - self.log(KNOWN_FAIL, message, *args, **kwargs) - - def processCrash(self, message, *args, **kwargs): - """Logs a process crash message""" - self.log(CRASH, message, *args, **kwargs) - - def log_structured(self, action, params=None): - """Logs a structured message object.""" - if params is None: - params = {} - - level = params.get('_level', _default_level) - if isinstance(level, int): - params['_level'] = getLevelName(level) - else: - params['_level'] = level - level = getLevelName(level.upper()) - - # If the logger is fed a level number unknown to the logging - # module, getLevelName will return a string. Unfortunately, - # the logging module will raise a type error elsewhere if - # the level is not an integer. - if not isinstance(level, int): - level = _default_level - - params['action'] = action - - # The can message be None. This is expected, and shouldn't cause - # unstructured formatters to fail. - message = params.get('_message') - - self.log(level, message, extra={'params': params}) - -class JSONFormatter(Formatter): - """Log formatter for emitting structured JSON entries.""" - - def format(self, record): - # Default values determined by logger metadata - output = { - '_time': int(round(record.created * 1000, 0)), - '_namespace': record.name, - '_level': getLevelName(record.levelno), - } - - # If this message was created by a call to log_structured, - # anything specified by the caller's params should act as - # an override. - output.update(getattr(record, 'params', {})) - - if record.msg and output.get('_message') is None: - # For compatibility with callers using the printf like - # API exposed by python logging, call the default formatter. - output['_message'] = Formatter.format(self, record) - - return json.dumps(output, indent=output.get('indent')) - -class MozFormatter(Formatter): - """ - MozFormatter class used to standardize formatting - If a different format is desired, this can be explicitly - overriden with the log handler's setFormatter() method - """ - level_length = 0 - max_level_length = len('TEST-START') - - def __init__(self, include_timestamp=False): - """ - Formatter.__init__ has fmt and datefmt parameters that won't have - any affect on a MozFormatter instance. - - :param include_timestamp: if True, include formatted time at the - beginning of the message - """ - self.include_timestamp = include_timestamp - Formatter.__init__(self) - - def format(self, record): - # Handles padding so record levels align nicely - if len(record.levelname) > self.level_length: - pad = 0 - if len(record.levelname) <= self.max_level_length: - self.level_length = len(record.levelname) - else: - pad = self.level_length - len(record.levelname) + 1 - sep = '|'.rjust(pad) - fmt = '%(name)s %(levelname)s ' + sep + ' %(message)s' - if self.include_timestamp: - fmt = '%(asctime)s ' + fmt - # this protected member is used to define the format - # used by the base Formatter's method - self._fmt = fmt - return Formatter.format(self, record) - -def getLogger(name, handler=None): - """ - Returns the logger with the specified name. - If the logger doesn't exist, it is created. - If handler is specified, adds it to the logger. Otherwise a default handler - that logs to standard output will be used. - - :param name: The name of the logger to retrieve - :param handler: A handler to add to the logger. If the logger already exists, - and a handler is specified, an exception will be raised. To - add a handler to an existing logger, call that logger's - addHandler method. - """ - setLoggerClass(MozLogger) - - if name in Logger.manager.loggerDict: - if handler: - raise ValueError('The handler parameter requires ' + \ - 'that a logger by this name does ' + \ - 'not already exist') - return Logger.manager.loggerDict[name] - - logger = getSysLogger(name) - logger.setLevel(_default_level) - - if handler is None: - handler = StreamHandler() - handler.setFormatter(MozFormatter()) - - logger.addHandler(handler) - logger.propagate = False - return logger - diff --git a/python/mozlog/mozlog/loggingmixin.py b/python/mozlog/mozlog/loggingmixin.py deleted file mode 100644 index 8e958edac82..00000000000 --- a/python/mozlog/mozlog/loggingmixin.py +++ /dev/null @@ -1,41 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -import mozlog - -class LoggingMixin(object): - """Expose a subset of logging functions to an inheriting class.""" - - def set_logger(self, logger_instance=None, name=None): - """Method for setting the underlying logger instance to be used.""" - - if logger_instance and not isinstance(logger_instance, mozlog.Logger): - raise ValueError("logger_instance must be an instance of" + - "mozlog.Logger") - - if name is None: - name = ".".join([self.__module__, self.__class__.__name__]) - - self._logger = logger_instance or mozlog.getLogger(name) - - def _log_msg(self, cmd, *args, **kwargs): - if not hasattr(self, "_logger"): - self._logger = mozlog.getLogger(".".join([self.__module__, - self.__class__.__name__])) - getattr(self._logger, cmd)(*args, **kwargs) - - def log(self, *args, **kwargs): - self._log_msg("log", *args, **kwargs) - - def info(self, *args, **kwargs): - self._log_msg("info", *args, **kwargs) - - def error(self, *args, **kwargs): - self._log_msg("error", *args, **kwargs) - - def warn(self, *args, **kwargs): - self._log_msg("warn", *args, **kwargs) - - def log_structured(self, *args, **kwargs): - self._log_msg("log_structured", *args, **kwargs) diff --git a/python/mozlog/mozlog/loglistener.py b/python/mozlog/mozlog/loglistener.py deleted file mode 100644 index e4b54c3988b..00000000000 --- a/python/mozlog/mozlog/loglistener.py +++ /dev/null @@ -1,47 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this file, -# You can obtain one at http://mozilla.org/MPL/2.0/. - -import SocketServer -import socket -import json - -class LogMessageServer(SocketServer.TCPServer): - def __init__(self, server_address, logger, message_callback=None, timeout=3): - SocketServer.TCPServer.__init__(self, server_address, LogMessageHandler) - self._logger = logger - self._message_callback = message_callback - self.timeout = timeout - -class LogMessageHandler(SocketServer.BaseRequestHandler): - """Processes output from a connected log source, logging to an - existing logger upon receipt of a well-formed log messsage.""" - - def handle(self): - """Continually listens for log messages.""" - self._partial_message = '' - self.request.settimeout(self.server.timeout) - - while True: - try: - data = self.request.recv(1024) - if not data: - return - self.process_message(data) - except socket.timeout: - return - - def process_message(self, data): - """Processes data from a connected log source. Messages are assumed - to be newline delimited, and generally well-formed JSON.""" - for part in data.split('\n'): - msg_string = self._partial_message + part - try: - msg = json.loads(msg_string) - self._partial_message = '' - self.server._logger.log_structured(msg.get('action', 'UNKNOWN'), msg) - if self.server._message_callback: - self.server._message_callback() - - except ValueError: - self._partial_message = msg_string diff --git a/python/mozlog/mozlog/structured/__init__.py b/python/mozlog/mozlog/structured/__init__.py deleted file mode 100644 index 31a779108b4..00000000000 --- a/python/mozlog/mozlog/structured/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -import commandline -import structuredlog -from structuredlog import get_default_logger, set_default_logger diff --git a/python/mozlog/mozlog/structured/commandline.py b/python/mozlog/mozlog/structured/commandline.py deleted file mode 100644 index d4a993febe5..00000000000 --- a/python/mozlog/mozlog/structured/commandline.py +++ /dev/null @@ -1,225 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -import sys -import os -import optparse - -from collections import defaultdict -from structuredlog import StructuredLogger, set_default_logger -import handlers -import formatters - -log_formatters = { - 'raw': (formatters.JSONFormatter, "Raw structured log messages"), - 'unittest': (formatters.UnittestFormatter, "Unittest style output"), - 'xunit': (formatters.XUnitFormatter, "xUnit compatible XML"), - 'html': (formatters.HTMLFormatter, "HTML report"), - 'mach': (formatters.MachFormatter, "Human-readable output"), - 'tbpl': (formatters.TbplFormatter, "TBPL style log format"), -} - -TEXT_FORMATTERS = ('raw', 'mach') -"""a subset of formatters for non test harnesses related applications""" - -def level_filter_wrapper(formatter, level): - return handlers.LogLevelFilter(formatter, level) - -def verbose_wrapper(formatter, verbose): - formatter.verbose = verbose - return formatter - -def buffer_handler_wrapper(handler, buffer_limit): - if buffer_limit == "UNLIMITED": - buffer_limit = None - else: - buffer_limit = int(buffer_limit) - return handlers.BufferingLogFilter(handler, buffer_limit) - -formatter_option_defaults = { - 'verbose': False, - 'level': 'info', -} - -fmt_options = { - #