Skip to content

Commit 9264fb4

Browse files
committed
feat: better annotation structure
1 parent 3e7600b commit 9264fb4

File tree

2 files changed

+117
-20
lines changed

2 files changed

+117
-20
lines changed

plugin_test.py

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def test_fail():
4141
testdir.monkeypatch.setenv('GITHUB_ACTIONS', 'true')
4242
result = testdir.runpytest_subprocess()
4343
result.stdout.fnmatch_lines([
44-
'::error file=test_annotation_fail.py,line=4::def test_fail():*',
44+
'::error file=test_annotation_fail.py,line=5::test_fail*assert 0*',
4545
])
4646

4747
def test_annotation_exception(testdir):
@@ -58,7 +58,7 @@ def test_fail():
5858
testdir.monkeypatch.setenv('GITHUB_ACTIONS', 'true')
5959
result = testdir.runpytest_subprocess()
6060
result.stdout.fnmatch_lines([
61-
'::error file=test_annotation_exception.py,line=4::def test_fail():*',
61+
'::error file=test_annotation_exception.py,line=5::test_fail*oops*',
6262
])
6363

6464
def test_annotation_fail_disabled_outside_workflow(testdir):
@@ -73,7 +73,7 @@ def test_fail():
7373
)
7474
testdir.monkeypatch.setenv('GITHUB_ACTIONS', '')
7575
result = testdir.runpytest_subprocess()
76-
no_fnmatch_line(result, '::error file=test_annotation_fail_disabled_outside_workflow.py')
76+
no_fnmatch_line(result, '::error file=test_annotation_fail_disabled_outside_workflow.py*')
7777

7878
def test_annotation_fail_cwd(testdir):
7979
testdir.makepyfile(
@@ -91,5 +91,87 @@ def test_fail():
9191
testdir.makefile('.ini', pytest='[pytest]\ntestpaths=..')
9292
result = testdir.runpytest_subprocess('--rootdir=foo')
9393
result.stdout.fnmatch_lines([
94-
'::error file=test_annotation_fail_cwd.py,line=4::def test_fail():*',
94+
'::error file=test_annotation_fail_cwd.py,line=5::test_fail*assert 0*',
9595
])
96+
97+
def test_annotation_long(testdir):
98+
testdir.makepyfile(
99+
'''
100+
import pytest
101+
pytest_plugins = 'pytest_github_actions_annotate_failures'
102+
103+
def f(x):
104+
return x
105+
106+
def test_fail():
107+
x = 1
108+
x += 1
109+
x += 1
110+
x += 1
111+
x += 1
112+
x += 1
113+
x += 1
114+
x += 1
115+
116+
assert f(x) == 3
117+
'''
118+
)
119+
testdir.monkeypatch.setenv('GITHUB_ACTIONS', 'true')
120+
result = testdir.runpytest_subprocess()
121+
result.stdout.fnmatch_lines([
122+
'::error file=test_annotation_long.py,line=17::test_annotation_fail*assert 8 == 3*where 8 = f(8)*',
123+
])
124+
no_fnmatch_line(result, '::*assert x += 1*')
125+
126+
def test_class_method(testdir):
127+
testdir.makepyfile(
128+
'''
129+
import pytest
130+
pytest_plugins = 'pytest_github_actions_annotate_failures'
131+
132+
class TestClass(object):
133+
def test_method(self):
134+
x = 1
135+
assert x == 2
136+
'''
137+
)
138+
testdir.monkeypatch.setenv('GITHUB_ACTIONS', 'true')
139+
result = testdir.runpytest_subprocess()
140+
result.stdout.fnmatch_lines([
141+
'::error file=test_class_method.py,line=7::TestClass.test_method*assert 1 == 2*',
142+
])
143+
no_fnmatch_line(result, '::*x = 1*')
144+
145+
146+
147+
148+
def test_annotation_long(testdir):
149+
testdir.makepyfile(
150+
'''
151+
import pytest
152+
pytest_plugins = 'pytest_github_actions_annotate_failures'
153+
154+
@pytest.mark.parametrize("a", [1])
155+
@pytest.mark.parametrize("b", [2], ids=["other"])
156+
def test_param(a, b):
157+
158+
a += 1
159+
b += 1
160+
161+
assert a == b
162+
'''
163+
)
164+
testdir.monkeypatch.setenv('GITHUB_ACTIONS', 'true')
165+
result = testdir.runpytest_subprocess()
166+
result.stdout.fnmatch_lines([
167+
'::error file=test_annotation_long.py,line=11::test_param?other?1*assert 2 == 3*',
168+
])
169+
170+
# Debugging / development tip:
171+
# Add a breakpoint() to the place you are going to check,
172+
# uncomment this example, and run it with:
173+
# GITHUB_ACTIONS=true pytest -k test_example
174+
# def test_example():
175+
# x = 3
176+
# y = 4
177+
# assert x == y

pytest_github_actions_annotate_failures/plugin.py

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,47 @@
11
from __future__ import print_function
22
import os
3+
import pytest
4+
5+
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
6+
def pytest_runtest_makereport(item, call):
7+
# execute all other hooks to obtain the report object
8+
outcome = yield
9+
report = outcome.get_result()
310

4-
def pytest_runtest_logreport(report):
511
# enable only in a workflow of GitHub Actions
612
# ref: https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables
713
if os.environ.get('GITHUB_ACTIONS') != 'true':
814
return
915

10-
if report.outcome != 'failed':
11-
return
16+
if report.when == "call" and report.failed:
17+
# collect information to be annotated
18+
filesystempath, lineno, _ = report.location
19+
20+
# try to convert to absolute path in GitHub Actions
21+
workspace = os.environ.get('GITHUB_WORKSPACE')
22+
if workspace:
23+
full_path = os.path.abspath(filesystempath)
24+
rel_path = os.path.relpath(full_path, workspace)
25+
if not rel_path.startswith('..'):
26+
filesystempath = rel_path
27+
28+
# 0-index to 1-index
29+
lineno += 1
30+
1231

13-
# collect information to be annotated
14-
filesystempath, lineno, _ = report.location
32+
# get the name of the current failed test, with parametrize info
33+
longrepr = report.head_line or item.name
1534

16-
# try to convert to absolute path in GitHub Actions
17-
workspace = os.environ.get('GITHUB_WORKSPACE')
18-
if workspace:
19-
full_path = os.path.abspath(filesystempath)
20-
rel_path = os.path.relpath(full_path, workspace)
21-
if not rel_path.startswith('..'):
22-
filesystempath = rel_path
35+
# get the error message and line number from the actual error
36+
try:
37+
longrepr += "\n\n" + report.longrepr.reprcrash.message
38+
lineno = report.longrepr.reprcrash.lineno
2339

24-
# 0-index to 1-index
25-
lineno += 1
40+
except AttributeError:
41+
pass
2642

27-
longrepr = str(report.longrepr)
43+
print(_error_workflow_command(filesystempath, lineno, longrepr))
2844

29-
print(_error_workflow_command(filesystempath, lineno, longrepr))
3045

3146
def _error_workflow_command(filesystempath, lineno, longrepr):
3247
if lineno is None:

0 commit comments

Comments
 (0)