Files
sousa-gecko/testing/web-platform/mach_commands_base.py
T
James Graham b4375e8936 Bug 2047805 - Make logger owner call shutdown(), r=Sasha
Instead of providing setup_logging functions that return a logger but
don't handle any cleanup, provide context managers that clean up the
logging configuration.

In particular wptlogging.LoggingManager will call shutdown on the
logger iff it created the logger in the first place.

Where we store the logger as a global variable we wrap in a
GlobalLogger class that knows how to set the global state and unsets
it at the end. Rather inelegantly, the interaction with globals means
we end up copying a lot of code around.

Differential Revision: https://phabricator.services.mozilla.com/D306960
2026-06-23 11:03:03 +00:00

131 lines
3.9 KiB
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 sys
from mozlog import handlers
from mozlog.structuredlog import log_levels
here = os.path.abspath(os.path.dirname(__file__))
def setup_environment(tests_root=None):
if tests_root is None:
tests_root = os.path.join(here, "tests")
if tests_root not in sys.path:
sys.path.insert(0, tests_root)
from tools import localpaths # noqa: F401
def create_parser_wpt():
setup_environment()
from wptrunner import wptcommandline
result = wptcommandline.create_parser()
result.add_argument(
"--no-install",
action="store_true",
default=False,
help="Do not install test runner application",
)
return result
class CriticalLogBuffer(handlers.BaseHandler):
"""
Buffer critical log entries
"""
def __init__(self):
self.log_entries = []
def __call__(self, data):
if (
data["action"] == "log"
and log_levels[data["level"]] <= log_levels["CRITICAL"]
):
self.log_entries.append(data)
class WebPlatformTestsRunner:
"""Run web platform tests."""
def __init__(self, setup):
self.setup = setup
def logging_manager(self, **kwargs):
from tools.wpt import run
return run.GlobalLogger(
kwargs,
{self.setup.default_log_type: sys.stdout},
formatter_defaults={"screenshot": True},
)
def run(self, logger, **kwargs):
from mozbuild.base import BinaryNotFoundException
from wptrunner import wptrunner
if kwargs["manifest_update"] is not False:
self.update_manifest(logger)
kwargs["manifest_update"] = False
if kwargs["product"] == "firefox":
try:
kwargs = self.setup.kwargs_firefox(kwargs)
except BinaryNotFoundException as e:
logger.error(e)
logger.info(e.help())
return 1
elif kwargs["product"] == "firefox_android":
kwargs = self.setup.kwargs_firefox_android(kwargs)
else:
kwargs = self.setup.kwargs_wptrun(kwargs)
log_buffer = CriticalLogBuffer()
logger.add_handler(log_buffer)
# wptrunner.start() unconditionally calls logger.shutdown() in its
# finally block. When invoked from `mach test`, the logger is shared
# across multiple WPT invocations (one per subsuite bucket), so
# shutting it down after the first run kills it for all subsequent
# runs. Temporarily replace shutdown with a no-op so the caller
# retains control over the logger lifetime.
original_shutdown = logger.shutdown
logger.shutdown = lambda *args, **kwargs: None
result = 1
try:
result = wptrunner.start(**kwargs)
finally:
logger.shutdown = original_shutdown
if int(result) != 0:
self._process_log_errors(logger, log_buffer, kwargs)
logger.remove_handler(log_buffer)
return int(result)
def _process_log_errors(self, logger, log_buffer, kwargs):
for item in log_buffer.log_entries:
if (
kwargs["webdriver_binary"] is None
and "webdriver" in item["message"]
and self.setup.topobjdir
):
print(
"ERROR: Couldn't find geckodriver binary required to run wdspec tests, consider `ac_add_options --enable-geckodriver` in your build configuration"
)
def update_manifest(self, logger, **kwargs):
import manifestupdate
return manifestupdate.run(
logger=logger,
src_root=self.setup.topsrcdir,
obj_root=self.setup.topobjdir,
**kwargs,
)