|
4 | 4 | from __future__ import absolute_import |
5 | 5 |
|
6 | 6 | import os.path |
| 7 | +import sys |
7 | 8 |
|
8 | 9 | import pytest |
9 | 10 |
|
@@ -141,6 +142,7 @@ def _ensure_parent(self, path, parentid, suiteids): |
141 | 142 | rootdir, parent) |
142 | 143 | self._parents[(rootdir, parentid)] = funcinfo |
143 | 144 | elif parent != parentid: |
| 145 | + print(parent, parentid) |
144 | 146 | # TODO: What to do? |
145 | 147 | raise NotImplementedError |
146 | 148 | return parentid |
@@ -168,10 +170,12 @@ def _ensure_file(self, rootdir, relfile): |
168 | 170 | def _ensure_suites(self, fullsuite, rootdir, fileid, suiteids): |
169 | 171 | if not fullsuite: |
170 | 172 | if suiteids: |
| 173 | + print(suiteids) |
171 | 174 | # TODO: What to do? |
172 | 175 | raise NotImplementedError |
173 | 176 | return None |
174 | 177 | if len(suiteids) != fullsuite.count('.') + 1: |
| 178 | + print(suiteids) |
175 | 179 | # TODO: What to do? |
176 | 180 | raise NotImplementedError |
177 | 181 |
|
@@ -210,13 +214,12 @@ def _parse_item(item, _normcase, _pathsep): |
210 | 214 | #_debug_item(item, showsummary=True) |
211 | 215 | kind, _ = _get_item_kind(item) |
212 | 216 | # Figure out the func, suites, and subs. |
213 | | - (fileid, suiteids, suites, funcid, basename, parameterized |
214 | | - ) = _parse_node_id(item.nodeid, kind) |
| 217 | + (nodeid, fileid, suiteids, suites, funcid, basename, parameterized |
| 218 | + ) = _parse_node_id(item.nodeid, kind, _pathsep, _normcase) |
215 | 219 | if kind == 'function': |
216 | 220 | funcname = basename |
217 | | - if funcid and item.function.__name__ != funcname: |
218 | | - # TODO: What to do? |
219 | | - raise NotImplementedError |
| 221 | + # Note: funcname does not necessarily match item.function.__name__. |
| 222 | + # This can result from importing a test function from another module. |
220 | 223 | if suites: |
221 | 224 | testfunc = '.'.join(suites) + '.' + funcname |
222 | 225 | else: |
@@ -226,32 +229,23 @@ def _parse_item(item, _normcase, _pathsep): |
226 | 229 | funcname = None |
227 | 230 |
|
228 | 231 | # Figure out the file. |
| 232 | + relfile = _normcase(fileid) |
229 | 233 | fspath = str(item.fspath) |
230 | | - if not fspath.endswith(_pathsep + fileid): |
| 234 | + if not _normcase(fspath).endswith(relfile[1:]): |
| 235 | + print(fspath) |
| 236 | + print(relfile) |
231 | 237 | raise NotImplementedError |
232 | | - filename = fspath[-len(fileid):] |
233 | | - testroot = str(item.fspath)[:-len(fileid)].rstrip(_pathsep) |
234 | | - if _pathsep in filename: |
235 | | - relfile = filename |
236 | | - else: |
237 | | - relfile = '.' + _pathsep + filename |
238 | | - srcfile, lineno, fullname = item.location |
239 | | - if srcfile != fileid: |
240 | | - # pytest supports discovery of tests imported from other |
241 | | - # modules. This is reflected by a different filename |
242 | | - # in item.location. |
243 | | - if _normcase(fileid) == _normcase(srcfile): |
244 | | - srcfile = fileid |
245 | | - else: |
246 | | - srcfile = relfile |
247 | | - location = '{}:{}'.format(srcfile, lineno) |
| 238 | + testroot = str(item.fspath)[:-len(relfile) + 1] |
| 239 | + location, fullname = _get_location(item, relfile, _normcase, _pathsep) |
248 | 240 | if kind == 'function': |
249 | 241 | if testfunc and fullname != testfunc + parameterized: |
250 | | - print(fullname, testfunc) |
| 242 | + print(item.nodeid) |
| 243 | + print(fullname, suites, testfunc) |
251 | 244 | # TODO: What to do? |
252 | 245 | raise NotImplementedError |
253 | 246 | elif kind == 'doctest': |
254 | 247 | if testfunc and fullname != testfunc + parameterized: |
| 248 | + print(item.nodeid) |
255 | 249 | print(fullname, testfunc) |
256 | 250 | # TODO: What to do? |
257 | 251 | raise NotImplementedError |
@@ -280,7 +274,7 @@ def _parse_item(item, _normcase, _pathsep): |
280 | 274 | # TODO: Support other markers? |
281 | 275 |
|
282 | 276 | test = TestInfo( |
283 | | - id=item.nodeid, |
| 277 | + id=nodeid, |
284 | 278 | name=item.name, |
285 | 279 | path=TestPath( |
286 | 280 | root=testroot, |
@@ -295,11 +289,62 @@ def _parse_item(item, _normcase, _pathsep): |
295 | 289 | return test, suiteids |
296 | 290 |
|
297 | 291 |
|
298 | | -def _parse_node_id(nodeid, kind='function'): |
| 292 | +def _get_location(item, relfile, _normcase, _pathsep): |
| 293 | + srcfile, lineno, fullname = item.location |
| 294 | + srcfile = _normcase(srcfile) |
| 295 | + if srcfile in (relfile, relfile[len(_pathsep) + 1:]): |
| 296 | + srcfile = relfile |
| 297 | + else: |
| 298 | + # pytest supports discovery of tests imported from other |
| 299 | + # modules. This is reflected by a different filename |
| 300 | + # in item.location. |
| 301 | + srcfile, lineno = _find_location( |
| 302 | + srcfile, lineno, relfile, item.function, _pathsep) |
| 303 | + if not srcfile.startswith('.' + _pathsep): |
| 304 | + srcfile = '.' + _pathsep + srcfile |
| 305 | + # from pytest, line numbers are 0-based |
| 306 | + location = '{}:{}'.format(srcfile, int(lineno) + 1) |
| 307 | + return location, fullname |
| 308 | + |
| 309 | + |
| 310 | +def _find_location(srcfile, lineno, relfile, func, _pathsep): |
| 311 | + if sys.version_info > (3,): |
| 312 | + return srcfile, lineno |
| 313 | + if (_pathsep + 'unittest' + _pathsep + 'case.py') not in srcfile: |
| 314 | + return srcfile, lineno |
| 315 | + |
| 316 | + # Unwrap the decorator (e.g. unittest.skip). |
| 317 | + srcfile = relfile |
| 318 | + lineno = -1 |
| 319 | + try: |
| 320 | + func = func.__closure__[0].cell_contents |
| 321 | + except (IndexError, AttributeError): |
| 322 | + return srcfile, lineno |
| 323 | + else: |
| 324 | + if callable(func) and func.__code__.co_filename.endswith(relfile[1:]): |
| 325 | + lineno = func.__code__.co_firstlineno - 1 |
| 326 | + return srcfile, lineno |
| 327 | + |
| 328 | + |
| 329 | +def _parse_node_id(nodeid, kind, _pathsep, _normcase): |
| 330 | + if not nodeid.startswith('.' + _pathsep): |
| 331 | + nodeid = '.' + _pathsep + nodeid |
| 332 | + while '::()::' in nodeid: |
| 333 | + nodeid = nodeid.replace('::()::', '::') |
| 334 | + |
| 335 | + fileid, _, remainder = nodeid.partition('::') |
| 336 | + if not fileid or not remainder: |
| 337 | + print(nodeid) |
| 338 | + # TODO: Unexpected! What to do? |
| 339 | + raise NotImplementedError |
| 340 | + fileid = _normcase(fileid) |
| 341 | + nodeid = fileid + '::' + remainder |
| 342 | + |
299 | 343 | if kind == 'doctest': |
300 | 344 | try: |
301 | 345 | parentid, name = nodeid.split('::') |
302 | 346 | except ValueError: |
| 347 | + print(nodeid) |
303 | 348 | # TODO: Unexpected! What to do? |
304 | 349 | raise NotImplementedError |
305 | 350 | funcid = None |
@@ -309,26 +354,30 @@ def _parse_node_id(nodeid, kind='function'): |
309 | 354 | if nodeid.endswith(']'): |
310 | 355 | funcid, sep, parameterized = nodeid.partition('[') |
311 | 356 | if not sep: |
| 357 | + print(nodeid) |
312 | 358 | # TODO: Unexpected! What to do? |
313 | 359 | raise NotImplementedError |
314 | 360 | parameterized = sep + parameterized |
315 | 361 | else: |
316 | 362 | funcid = nodeid |
317 | | - |
318 | 363 | parentid, _, name = funcid.rpartition('::') |
319 | | - if not name: |
| 364 | + if not parentid or not name: |
| 365 | + print(parentid, name) |
320 | 366 | # TODO: What to do? We expect at least a filename and a function |
321 | 367 | raise NotImplementedError |
322 | 368 |
|
323 | 369 | suites = [] |
324 | 370 | suiteids = [] |
325 | 371 | while '::' in parentid: |
326 | | - suiteids.insert(0, parentid) |
327 | | - parentid, _, suitename = parentid.rpartition('::') |
| 372 | + fullid = parentid |
| 373 | + parentid, _, suitename = fullid.rpartition('::') |
| 374 | + suiteids.insert(0, fullid) |
328 | 375 | suites.insert(0, suitename) |
329 | | - fileid = parentid |
| 376 | + if parentid != fileid: |
| 377 | + print(nodeid) |
| 378 | + print(parentid, fileid) |
330 | 379 |
|
331 | | - return fileid, suiteids, suites, funcid, name, parameterized |
| 380 | + return nodeid, fileid, suiteids, suites, funcid, name, parameterized |
332 | 381 |
|
333 | 382 |
|
334 | 383 | def _get_item_kind(item): |
|