Skip to content

Commit f96880b

Browse files
committed
feat(output): provide cwd to parsers
1 parent 375f9b8 commit f96880b

File tree

5 files changed

+120
-17
lines changed

5 files changed

+120
-17
lines changed

rplugin/python3/ultest/handler/parsers/output/__init__.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
from ....logging import get_logger
66
from .base import ParseResult
77
from .parsec import ParseError
8-
from .python.pytest import pytest_output
9-
from .python.unittest import unittest_output
8+
from .python.pytest import parse_pytest
9+
from .python.unittest import parse_unittest
1010

1111

1212
@dataclass
@@ -41,8 +41,8 @@ class OutputPatterns:
4141
class OutputParser:
4242
def __init__(self, disable_patterns: List[str]) -> None:
4343
self._parsers = {
44-
"python#pytest": pytest_output,
45-
"python#pyunit": unittest_output,
44+
"python#pytest": parse_pytest,
45+
"python#pyunit": parse_unittest,
4646
}
4747
self._patterns = {
4848
runner: patterns
@@ -53,10 +53,12 @@ def __init__(self, disable_patterns: List[str]) -> None:
5353
def can_parse(self, runner: str) -> bool:
5454
return runner in self._patterns or runner in self._parsers
5555

56-
def parse_failed(self, runner: str, output: str) -> Iterable[ParseResult]:
56+
def parse_failed(self, runner: str, output: str, cwd=None) -> Iterable[ParseResult]:
5757
if runner in self._parsers:
5858
try:
59-
return self._parsers[runner].parse(_ANSI_ESCAPE.sub("", output)).results
59+
return self._parsers[runner](
60+
_ANSI_ESCAPE.sub("", output), cwd=cwd
61+
).results
6062
except ParseError:
6163
return []
6264
return self._regex_parse_failed(runner, output.splitlines())

rplugin/python3/ultest/handler/parsers/output/python/pytest.py

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import os.path as path
12
from dataclasses import dataclass
23
from logging import getLogger
4+
from pathlib import Path
35
from typing import List, Optional
46

57
from .. import parsec as p
@@ -18,30 +20,42 @@ class PytestCodeTrace:
1820
message: Optional[List[str]] = None
1921

2022

21-
@generate
22-
def pytest_output():
23-
yield pytest_test_results_summary
24-
parsed_outputs = yield p.many1(failed_test_section)
25-
parsed_summary = yield pytest_summary_info
23+
def parse_pytest(output: str, cwd: str = None):
24+
parsed_outputs, parsed_summary = pytest_output.parse(output)
25+
26+
def convert_to_relative(file: str):
27+
if not Path(file).is_absolute():
28+
return file
29+
return path.relpath(file, cwd)
30+
2631
parsed_results = {
27-
(r.file, r.name, *r.namespaces): r
32+
(convert_to_relative(r.file), r.name, *r.namespaces): r
2833
for r in [*parsed_summary, *parsed_outputs]
2934
if r
3035
}
3136
return ParsedOutput(results=list(parsed_results.values()))
3237

3338

39+
@generate
40+
def pytest_output():
41+
yield pytest_test_results_summary
42+
parsed_outputs = yield p.many1(failed_test_section)
43+
parsed_summary = yield pytest_summary_info
44+
return parsed_outputs, parsed_summary
45+
46+
3447
@generate
3548
def failed_test_section():
3649
namespaces, test_name = yield failed_test_section_title
37-
yield until_eol
3850
raw_output_lines = yield p.many1(
3951
p.exclude(until_eol, failed_test_section_title ^ pytest_summary_info_title)
4052
)
4153
output_text = "\n".join(raw_output_lines) + "\n"
4254
try:
4355

44-
file, err_msg, err_line = failed_test_section_output.parse(output_text)
56+
file, err_msg, err_line = (
57+
failed_test_section_output ^ failed_test_section_collection_error
58+
).parse(output_text)
4559
return ParseResult(
4660
name=test_name,
4761
namespaces=namespaces,
@@ -76,11 +90,34 @@ def failed_test_section_output():
7690
)
7791

7892

93+
@generate
94+
def failed_test_section_collection_error():
95+
yield p.string("Traceback") >> until_eol
96+
test_file = (
97+
yield p.many1(p.string(" "))
98+
>> p.string('File "')
99+
>> p.many1(p.none_of('"')).parsecmap(join_chars)
100+
<< p.string('", ')
101+
)
102+
test_line_no = (
103+
yield p.string("line ")
104+
>> p.many1(p.digit()).parsecmap(join_chars).parsecmap(int)
105+
<< until_eol
106+
)
107+
yield p.many1(p.string(" ") >> until_eol)
108+
error_message = yield until_eol
109+
return (
110+
test_file,
111+
[error_message],
112+
test_line_no,
113+
)
114+
115+
79116
@generate
80117
def failed_test_section_title():
81118
yield p.string("_") >> p.many1(p.string("_")) >> p.space()
82119
name_elements = (
83-
yield p.many1(p.none_of(" "))
120+
yield p.many1(p.none_of(" ["))
84121
.parsecmap(join_chars)
85122
.parsecmap(lambda elems: elems.split("."))
86123
)

rplugin/python3/ultest/handler/parsers/output/python/unittest.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ class ErroredTestError(Exception):
88
...
99

1010

11+
def parse_unittest(output: str, cwd: str = None):
12+
return unittest_output.parse(output).results
13+
14+
1115
@generate
1216
def unittest_output():
1317
try:

rplugin/python3/ultest/handler/runner/__init__.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,9 @@ async def run(cmd=cmd):
188188
(code, output_path) = await self._processes.run(
189189
cmd, tree.data.file, tree.data.id, cwd=root, env=env
190190
)
191-
self._process_results(tree, file_tree, code, output_path, runner, on_finish)
191+
self._process_results(
192+
tree, file_tree, code, output_path, runner, on_finish, root
193+
)
192194

193195
self._vim.launch(run(), tree.data.id)
194196

@@ -200,6 +202,7 @@ def _process_results(
200202
output_path: str,
201203
runner: str,
202204
on_finish: Callable[[List[Tuple[Position, Result]]], None],
205+
cwd: Optional[str] = None,
203206
):
204207

205208
namespaces = {
@@ -213,7 +216,7 @@ def _process_results(
213216
output = cmd_out.read()
214217

215218
parsed_failures = (
216-
self._output_parser.parse_failed(runner, output) if code else []
219+
self._output_parser.parse_failed(runner, output, cwd) if code else []
217220
)
218221
failed = {
219222
(failed.name, *(failed.namespaces)): failed for failed in parsed_failures

tests/unit/handler/parsers/output/python/test_pytest.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,14 @@ def test_parse_file(self):
8282
output=None,
8383
line=23,
8484
),
85+
ParseResult(
86+
name="test_parametrize[5]",
87+
namespaces=[],
88+
file="test_a.py",
89+
message=None,
90+
output=None,
91+
line=None,
92+
),
8593
],
8694
)
8795

@@ -336,3 +344,52 @@ def warn(msg, *args, **kwargs):
336344
line=35,
337345
),
338346
)
347+
348+
def test_parse_collection_error(self):
349+
raw = """================================================= test session starts ==================================================
350+
platform linux -- Python 3.8.12, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
351+
rootdir: /home/ronan/Dev/repos/hypothesis, configfile: pytest.ini
352+
plugins: xdist-2.4.0, forked-1.3.0, hypothesis-6.24.0
353+
collected 1 item
354+
355+
examples/example_hypothesis_entrypoint/test_entrypoint.py F [100%]
356+
357+
======================================================= FAILURES =======================================================
358+
___________________________________________ test_registered_from_entrypoint ____________________________________________
359+
Traceback (most recent call last):
360+
File "/home/ronan/Dev/repos/hypothesis/hypothesis-python/examples/example_hypothesis_entrypoint/test_entrypoint.py", line 22, in test_registered_from_entrypoint
361+
def test_registered_from_entrypoint(x):
362+
File "/home/ronan/Dev/repos/hypothesis/hypothesis-python/src/hypothesis/core.py", line 1199, in wrapped_test
363+
raise the_error_hypothesis_found
364+
File "/home/ronan/Dev/repos/hypothesis/hypothesis-python/examples/example_hypothesis_entrypoint/example_hypothesis_entrypoint.py", line 25, in __init__
365+
assert x >= 0, f"got {x}, but only positive numbers are allowed"
366+
AssertionError: got -1, but only positive numbers are allowed
367+
================================================= slowest 20 durations =================================================
368+
0.07s call hypothesis-python/examples/example_hypothesis_entrypoint/test_entrypoint.py::test_registered_from_entrypoint
369+
370+
(2 durations < 0.005s hidden. Use -vv to show these durations.)
371+
=============================================== short test summary info ================================================
372+
FAILED examples/example_hypothesis_entrypoint/test_entrypoint.py::test_registered_from_entrypoint - AssertionError: g...
373+
================================================== 1 failed in 0.28s ===================================================
374+
375+
"""
376+
result = OutputParser([]).parse_failed(
377+
"python#pytest",
378+
raw,
379+
cwd="/home/ronan/Dev/repos/hypothesis/hypothesis-python",
380+
)
381+
self.assertEqual(
382+
[
383+
ParseResult(
384+
name="test_registered_from_entrypoint",
385+
namespaces=[],
386+
file="/home/ronan/Dev/repos/hypothesis/hypothesis-python/examples/example_hypothesis_entrypoint/test_entrypoint.py",
387+
message=[
388+
"AssertionError: got -1, but only positive numbers are allowed"
389+
],
390+
output=None,
391+
line=22,
392+
)
393+
],
394+
result,
395+
)

0 commit comments

Comments
 (0)