forked from DonJayamanne/pythonVSCode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpytest.py
More file actions
150 lines (130 loc) · 4.5 KB
/
pytest.py
File metadata and controls
150 lines (130 loc) · 4.5 KB
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import os.path
import pytest
from .errors import UnsupportedCommandError
from .info import TestInfo, TestPath
def add_cli_subparser(cmd, name, parent):
"""Add a new subparser to the given parent and add args to it."""
parser = parent.add_parser(name)
if cmd == 'discover':
# For now we don't have any tool-specific CLI options to add.
pass
else:
raise UnsupportedCommandError(cmd)
return parser
def discover(pytestargs=None,
_pytest_main=pytest.main, _plugin=None):
"""Return the results of test discovery."""
if _plugin is None:
_plugin = TestCollector()
pytestargs = _adjust_pytest_args(pytestargs)
ec = _pytest_main(pytestargs, [_plugin])
if ec != 0:
raise Exception('pytest discovery failed (exit code {})'.format(ec))
if _plugin.discovered is None:
raise Exception('pytest discovery did not start')
return _plugin.discovered
def _adjust_pytest_args(pytestargs):
pytestargs = list(pytestargs) if pytestargs else []
# Duplicate entries should be okay.
pytestargs.insert(0, '--collect-only')
pytestargs.insert(0, '-pno:terminal')
# TODO: pull in code from:
# src/client/unittests/pytest/services/discoveryService.ts
# src/client/unittests/pytest/services/argsService.ts
return pytestargs
class TestCollector(object):
"""This is a pytest plugin that collects the discovered tests."""
discovered = None
# Relevant plugin hooks:
# https://docs.pytest.org/en/latest/reference.html#collection-hooks
def pytest_collection_modifyitems(self, session, config, items):
self.discovered = []
for item in items:
info = _parse_item(item)
self.discovered.append(info)
# This hook is not specified in the docs, so we also provide
# the "modifyitems" hook just in case.
def pytest_collection_finish(self, session):
try:
items = session.items
except AttributeError:
# TODO: Is there an alternative?
return
# print(', '.join(k for k in dir(items[0]) if k[0].islower()))
self.discovered = []
for item in items:
# print(' ', item.user_properties)
# print(' ', item.own_markers)
# print(' ', list(item.iter_markers()))
# print()
info = _parse_item(item)
self.discovered.append(info)
def _parse_item(item):
"""
(pytest.Collector)
pytest.Session
pytest.Package
pytest.Module
pytest.Class
(pytest.File)
(pytest.Item)
pytest.Function
"""
# Figure out the file.
filename, lineno, fullname = item.location
if not str(item.fspath).endswith(os.path.sep + filename):
raise NotImplementedError
testroot = str(item.fspath)[:-len(filename)].rstrip(os.path.sep)
if os.path.sep in filename:
relfile = filename
else:
relfile = os.path.join('.', filename)
# Figure out the func (and subs).
funcname = item.function.__name__
parts = item.nodeid.split('::')
if parts.pop(0) != filename:
# TODO: What to do?
raise NotImplementedError
suites = []
while len(parts) > 1:
suites.append(parts.pop(0))
parameterized = ''
if '[' in parts[0]:
_func, sep, parameterized = parts[0].partition('[')
parameterized = sep + parameterized
if _func != funcname:
# TODO: What to do?
raise NotImplementedError
if suites:
testfunc = '.'.join(suites) + '.' + funcname
else:
testfunc = funcname
if fullname != testfunc + parameterized:
# TODO: What to do?
raise NotImplementedError
# Sort out markers.
# See: https://docs.pytest.org/en/latest/reference.html#marks
markers = set()
for marker in item.own_markers:
if marker.name == 'parameterize':
# We've already covered these.
continue
elif marker.name == 'skip':
markers.add('skip')
elif marker.name == 'skipif':
markers.add('skip-if')
elif marker.name == 'xfail':
markers.add('expected-failure')
# TODO: Support other markers?
return TestInfo(
id=item.nodeid,
name=item.name,
path=TestPath(
root=testroot,
relfile=relfile,
func=testfunc,
sub=[parameterized] if parameterized else None,
),
lineno=lineno,
markers=sorted(markers) if markers else None,
)