-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathplugin.py
71 lines (55 loc) · 2.51 KB
/
plugin.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
from __future__ import print_function
import os
import pytest
from collections import OrderedDict
# Reference:
# https://docs.pytest.org/en/latest/writing_plugins.html#hookwrapper-executing-around-other-hooks
# https://docs.pytest.org/en/latest/writing_plugins.html#hook-function-ordering-call-example
# https://docs.pytest.org/en/stable/reference.html#pytest.hookspec.pytest_runtest_makereport
#
# Inspired by:
# https://github.com/pytest-dev/pytest/blob/master/src/_pytest/terminal.py
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
# execute all other hooks to obtain the report object
outcome = yield
report = outcome.get_result()
# enable only in a workflow of GitHub Actions
# ref: https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables
if os.environ.get('GITHUB_ACTIONS') != 'true':
return
if report.when == "call" and report.failed:
# collect information to be annotated
filesystempath, lineno, _ = report.location
# try to convert to absolute path in GitHub Actions
workspace = os.environ.get('GITHUB_WORKSPACE')
if workspace:
full_path = os.path.abspath(filesystempath)
rel_path = os.path.relpath(full_path, workspace)
if not rel_path.startswith('..'):
filesystempath = rel_path
# 0-index to 1-index
lineno += 1
# get the name of the current failed test, with parametrize info
longrepr = report.head_line or item.name
# get the error message and line number from the actual error
try:
longrepr += "\n\n" + report.longrepr.reprcrash.message
lineno = report.longrepr.reprcrash.lineno
except AttributeError:
pass
print(_error_workflow_command(filesystempath, lineno, longrepr))
def _error_workflow_command(filesystempath, lineno, longrepr):
# Build collection of arguments. Ordering is strict for easy testing
details_dict = OrderedDict()
details_dict["file"] = filesystempath
if lineno is not None:
details_dict["line"] = lineno
details = ",".join("{}={}".format(k,v) for k,v in details_dict.items())
if longrepr is None:
return '\n::error {}'.format(details)
else:
longrepr = _escape(longrepr)
return '\n::error {}::{}'.format(details, longrepr)
def _escape(s):
return s.replace('%', '%25').replace('\r', '%0D').replace('\n', '%0A')