Skip to content

Commit 6af799d

Browse files
ericsnowcurrentlyDonJayamanne
authored andcommitted
Fix some problems in the pytest adapter. (#5648)
* Factor out _normalize_node_id(). * Find and handle item kind properly. * Factor out _iter_nodes(). * Return the parents from _parse_node_id(). * Return the parents from parse_item(). * Add a fix_path() testing helper. * Pass the parents to add_test(). * Pass the parents to _ensure_parents(). * Move DiscoveredTests out of the pytest code. * Fix case on Windows. * Add ShouldNeverReachHere exception type. * Resolve all the TODO comments. * Make ShouldNeverReachHere an informative function instead.
1 parent a27e0c4 commit 6af799d

File tree

6 files changed

+1078
-820
lines changed

6 files changed

+1078
-820
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from __future__ import absolute_import, print_function
5+
6+
import os.path
7+
8+
from .info import ParentInfo
9+
10+
11+
12+
class DiscoveredTests(object):
13+
"""A container for the discovered tests and their parents."""
14+
15+
def __init__(self):
16+
self.reset()
17+
18+
def __len__(self):
19+
return len(self._tests)
20+
21+
def __getitem__(self, index):
22+
return self._tests[index]
23+
24+
@property
25+
def parents(self):
26+
return sorted(self._parents.values(), key=lambda v: (v.root or v.name, v.id))
27+
28+
def reset(self):
29+
"""Clear out any previously discovered tests."""
30+
self._parents = {}
31+
self._tests = []
32+
33+
def add_test(self, test, parents):
34+
"""Add the given test and its parents."""
35+
parentid = self._ensure_parent(test.path, parents)
36+
# Updating the parent ID and the test ID aren't necessary if the
37+
# provided test and parents (from the test collector) are
38+
# properly generated. However, we play it safe here.
39+
test = test._replace(parentid=parentid)
40+
if not test.id.startswith('.' + os.path.sep):
41+
test = test._replace(id=os.path.join('.', test.id))
42+
self._tests.append(test)
43+
44+
def _ensure_parent(self, path, parents):
45+
rootdir = path.root
46+
47+
_parents = iter(parents)
48+
nodeid, name, kind = next(_parents)
49+
# As in add_test(), the node ID *should* already be correct.
50+
if nodeid != '.' and not nodeid.startswith('.' + os.path.sep):
51+
nodeid = os.path.join('.', nodeid)
52+
_parentid = nodeid
53+
for parentid, parentname, parentkind in _parents:
54+
# As in add_test(), the parent ID *should* already be correct.
55+
if parentid != '.' and not parentid.startswith('.' + os.path.sep):
56+
parentid = os.path.join('.', parentid)
57+
info = ParentInfo(nodeid, kind, name, rootdir, parentid)
58+
self._parents[(rootdir, nodeid)] = info
59+
nodeid, name, kind = parentid, parentname, parentkind
60+
assert nodeid == '.'
61+
info = ParentInfo(nodeid, kind, name=rootdir)
62+
self._parents[(rootdir, nodeid)] = info
63+
64+
return _parentid

pythonFiles/testing_tools/adapter/pytest/_discovery.py

Lines changed: 6 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88

99
import pytest
1010

11-
from .. import util
12-
from ..info import ParentInfo
11+
from .. import util, discovery
1312
from ._pytest_item import parse_item
1413

1514

@@ -63,7 +62,7 @@ class TestCollector(object):
6362

6463
def __init__(self, tests=None):
6564
if tests is None:
66-
tests = DiscoveredTests()
65+
tests = discovery.DiscoveredTests()
6766
self._tests = tests
6867
self._started = False
6968

@@ -74,8 +73,8 @@ def pytest_collection_modifyitems(self, session, config, items):
7473
self._started = True
7574
self._tests.reset()
7675
for item in items:
77-
test, suiteids = parse_item(item, self.NORMCASE, self.PATHSEP)
78-
self._tests.add_test(test, suiteids)
76+
test, parents = parse_item(item, self.NORMCASE, self.PATHSEP)
77+
self._tests.add_test(test, parents)
7978

8079
# This hook is not specified in the docs, so we also provide
8180
# the "modifyitems" hook just in case.
@@ -88,111 +87,5 @@ def pytest_collection_finish(self, session):
8887
return
8988
self._tests.reset()
9089
for item in items:
91-
test, suiteids = parse_item(item, self.NORMCASE, self.PATHSEP)
92-
self._tests.add_test(test, suiteids)
93-
94-
95-
class DiscoveredTests(object):
96-
"""A container for the discovered tests and their parents."""
97-
98-
def __init__(self):
99-
self.reset()
100-
101-
def __len__(self):
102-
return len(self._tests)
103-
104-
def __getitem__(self, index):
105-
return self._tests[index]
106-
107-
@property
108-
def parents(self):
109-
return sorted(self._parents.values(), key=lambda v: (v.root or v.name, v.id))
110-
111-
def reset(self):
112-
"""Clear out any previously discovered tests."""
113-
self._parents = {}
114-
self._tests = []
115-
116-
def add_test(self, test, suiteids):
117-
"""Add the given test and its parents."""
118-
parentid = self._ensure_parent(test.path, test.parentid, suiteids)
119-
test = test._replace(parentid=parentid)
120-
if not test.id.startswith('.' + os.path.sep):
121-
test = test._replace(id=os.path.join('.', test.id))
122-
self._tests.append(test)
123-
124-
def _ensure_parent(self, path, parentid, suiteids):
125-
if not parentid.startswith('.' + os.path.sep):
126-
parentid = os.path.join('.', parentid)
127-
fileid = self._ensure_file(path.root, path.relfile)
128-
rootdir = path.root
129-
130-
if not path.func:
131-
return parentid
132-
133-
fullsuite, _, funcname = path.func.rpartition('.')
134-
suiteid = self._ensure_suites(fullsuite, rootdir, fileid, suiteids)
135-
parent = suiteid if suiteid else fileid
136-
137-
if path.sub:
138-
if (rootdir, parentid) not in self._parents:
139-
funcinfo = ParentInfo(parentid, 'function', funcname,
140-
rootdir, parent)
141-
self._parents[(rootdir, parentid)] = funcinfo
142-
elif parent != parentid:
143-
print(parent, parentid)
144-
# TODO: What to do?
145-
raise NotImplementedError
146-
return parentid
147-
148-
def _ensure_file(self, rootdir, relfile):
149-
if (rootdir, '.') not in self._parents:
150-
self._parents[(rootdir, '.')] = ParentInfo('.', 'folder', rootdir)
151-
if relfile.startswith('.' + os.path.sep):
152-
fileid = relfile
153-
else:
154-
fileid = relfile = os.path.join('.', relfile)
155-
156-
if (rootdir, fileid) not in self._parents:
157-
folderid, filebase = os.path.split(fileid)
158-
fileinfo = ParentInfo(fileid, 'file', filebase, rootdir, folderid)
159-
self._parents[(rootdir, fileid)] = fileinfo
160-
161-
while folderid != '.' and (rootdir, folderid) not in self._parents:
162-
parentid, name = os.path.split(folderid)
163-
folderinfo = ParentInfo(folderid, 'folder', name, rootdir, parentid)
164-
self._parents[(rootdir, folderid)] = folderinfo
165-
folderid = parentid
166-
return relfile
167-
168-
def _ensure_suites(self, fullsuite, rootdir, fileid, suiteids):
169-
if not fullsuite:
170-
if suiteids:
171-
print(suiteids)
172-
# TODO: What to do?
173-
raise NotImplementedError
174-
return None
175-
if len(suiteids) != fullsuite.count('.') + 1:
176-
print(suiteids)
177-
# TODO: What to do?
178-
raise NotImplementedError
179-
180-
suiteid = suiteids.pop()
181-
if not suiteid.startswith('.' + os.path.sep):
182-
suiteid = os.path.join('.', suiteid)
183-
final = suiteid
184-
while '.' in fullsuite and (rootdir, suiteid) not in self._parents:
185-
parentid = suiteids.pop()
186-
if not parentid.startswith('.' + os.path.sep):
187-
parentid = os.path.join('.', parentid)
188-
fullsuite, _, name = fullsuite.rpartition('.')
189-
suiteinfo = ParentInfo(suiteid, 'suite', name, rootdir, parentid)
190-
self._parents[(rootdir, suiteid)] = suiteinfo
191-
192-
suiteid = parentid
193-
else:
194-
name = fullsuite
195-
suiteinfo = ParentInfo(suiteid, 'suite', name, rootdir, fileid)
196-
if (rootdir, suiteid) not in self._parents:
197-
self._parents[(rootdir, suiteid)] = suiteinfo
198-
return final
90+
test, parents = parse_item(item, self.NORMCASE, self.PATHSEP)
91+
self._tests.add_test(test, parents)

0 commit comments

Comments
 (0)