From 267e681a7e7b70ed71d25300d85975e57cf708a8 Mon Sep 17 00:00:00 2001 From: Aleksandr Mezin Date: Fri, 3 Jan 2025 02:43:24 +0200 Subject: [PATCH] tests: GitHub Actions annotations --- test/conftest.py | 8 +++- test/github_annotations.py | 90 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 test/github_annotations.py diff --git a/test/conftest.py b/test/conftest.py index 57855df0..60d4eecb 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -16,7 +16,13 @@ from . import procutil -pytest_plugins = ('test.screenshot', 'test.syslog', 'test.logsync', 'test.gtest_output_compat') +pytest_plugins = ( + 'test.screenshot', + 'test.syslog', + 'test.logsync', + 'test.gtest_output_compat', + 'test.github_annotations', +) LOGGER = logging.getLogger(__name__) diff --git a/test/github_annotations.py b/test/github_annotations.py new file mode 100644 index 00000000..60b2ecbf --- /dev/null +++ b/test/github_annotations.py @@ -0,0 +1,90 @@ +# SPDX-FileCopyrightText: 2025 Aleksandr Mezin +# +# SPDX-License-Identifier: GPL-3.0-or-later + +import os +import urllib.parse + + +try: + from xdist import is_xdist_worker + +except ImportError: + def is_xdist_worker(request_or_session): + return hasattr(request_or_session.config, 'workerinput') + + +def command(command, message='', **properties): + properties = ','.join( + f'{k}={urllib.parse.quote(v)}' + for k, v in properties.items() + ) + + if properties: + return f'::{command} {properties}::{urllib.parse.quote(message)}' + else: + return f'::{command}::{urllib.parse.quote(message)}' + + +class FailureReporter: + def __init__(self, terminal_reporter): + self.terminal_reporter = terminal_reporter + + def pytest_runtest_logreport(self, report): + if not report.failed: + return + + filesystempath, lineno, _ = report.location + + properties = dict( + title=report.nodeid, + file=filesystempath, + ) + + if lineno is not None: + properties['line'] = lineno + 1 + + self.terminal_reporter.write_line(command('error', report.longreprtext, **properties)) + + +class WarningReporter: + def __init__(self, terminal_reporter): + self.terminal_reporter = terminal_reporter + + def pytest_warning_recorded(self, warning_message, when, nodeid, location): + properties = dict(title=nodeid) + + if location: + filename, linenumber, _ = location + + if filename: + properties['file'] = filename + + if linenumber is not None: + properties['line'] = linenumber + 1 + + self.terminal_reporter.write_line(command('warning', warning_message, **properties)) + + +def pytest_addoption(parser): + env_value = os.environ.get('GITHUB_ACTIONS', None) + + parser.addoption( + '--github-actions-annotations', + type=bool, + default=env_value == 'true', + help='Generate GitHub Actions annotations for failures and warnings', + ) + + +def pytest_sessionstart(session): + if not session.config.option.github_actions_annotations: + return + + if is_xdist_worker(session): + return + + terminal_reporter = session.config.pluginmanager.getplugin('terminalreporter') + + session.config.pluginmanager.register(FailureReporter(terminal_reporter)) + session.config.pluginmanager.register(WarningReporter(terminal_reporter))