diff --git a/pythonFiles/testing_tools/adapter/discovery.py b/pythonFiles/testing_tools/adapter/discovery.py index 15196a6b0beb..3803069f314f 100644 --- a/pythonFiles/testing_tools/adapter/discovery.py +++ b/pythonFiles/testing_tools/adapter/discovery.py @@ -3,11 +3,42 @@ from __future__ import absolute_import, print_function -import os.path +import re +from .util import fix_fileid, DIRNAME, NORMCASE from .info import ParentInfo +FILE_ID_RE = re.compile(r""" + ^ + (?: + ( .* [.] (?: py | txt ) \b ) # .txt for doctest files + ( [^.] .* )? + ) + $ + """, re.VERBOSE) + + +def fix_nodeid(nodeid, kind, rootdir=None, #*, + _fix_fileid=fix_fileid, + ): + if not nodeid: + raise ValueError('missing nodeid') + if nodeid == '.': + return nodeid + + fileid = nodeid + remainder = '' + if kind not in ('folder', 'file'): + m = FILE_ID_RE.match(nodeid) + if m: + fileid, remainder = m.groups() + elif len(nodeid) > 1: + fileid = nodeid[:2] + remainder = nodeid[2:] + fileid = _fix_fileid(fileid, rootdir) + return fileid + (remainder or '') + class DiscoveredTests(object): """A container for the discovered tests and their parents.""" @@ -23,7 +54,10 @@ def __getitem__(self, index): @property def parents(self): - return sorted(self._parents.values(), key=lambda v: (v.root or v.name, v.id)) + return sorted(self._parents.values(), + key=lambda p: (NORMCASE(p.root or p.name), + p.id), + ) def reset(self): """Clear out any previously discovered tests.""" @@ -36,25 +70,31 @@ def add_test(self, test, parents): # Updating the parent ID and the test ID aren't necessary if the # provided test and parents (from the test collector) are # properly generated. However, we play it safe here. - test = test._replace(parentid=parentid) - if not test.id.startswith('.' + os.path.sep): - test = test._replace(id=os.path.join('.', test.id)) + test = test._replace( + id=fix_nodeid(test.id, 'test', test.path.root), + parentid=parentid, + ) self._tests.append(test) - def _ensure_parent(self, path, parents): + def _ensure_parent(self, path, parents, #*, + _dirname=DIRNAME, + ): rootdir = path.root + relpath = path.relfile _parents = iter(parents) nodeid, name, kind = next(_parents) # As in add_test(), the node ID *should* already be correct. - if nodeid != '.' and not nodeid.startswith('.' + os.path.sep): - nodeid = os.path.join('.', nodeid) + nodeid = fix_nodeid(nodeid, kind, rootdir) _parentid = nodeid for parentid, parentname, parentkind in _parents: # As in add_test(), the parent ID *should* already be correct. - if parentid != '.' and not parentid.startswith('.' + os.path.sep): - parentid = os.path.join('.', parentid) - info = ParentInfo(nodeid, kind, name, rootdir, parentid) + parentid = fix_nodeid(parentid, kind, rootdir) + if kind in ('folder', 'file'): + info = ParentInfo(nodeid, kind, name, rootdir, relpath, parentid) + relpath = _dirname(relpath) + else: + info = ParentInfo(nodeid, kind, name, rootdir, None, parentid) self._parents[(rootdir, nodeid)] = info nodeid, name, kind = parentid, parentname, parentkind assert nodeid == '.' diff --git a/pythonFiles/testing_tools/adapter/info.py b/pythonFiles/testing_tools/adapter/info.py index c9c14571dd6b..ae01426fe57b 100644 --- a/pythonFiles/testing_tools/adapter/info.py +++ b/pythonFiles/testing_tools/adapter/info.py @@ -26,18 +26,19 @@ def __init__(self, *args, **kwargs): # self.sub may be None. -class ParentInfo(namedtuple('ParentInfo', 'id kind name root parentid')): +class ParentInfo(namedtuple('ParentInfo', 'id kind name root relpath parentid')): KINDS = ('folder', 'file', 'suite', 'function', 'subtest') - def __new__(cls, id, kind, name, root=None, parentid=None): + def __new__(cls, id, kind, name, root=None, relpath=None, parentid=None): self = super(ParentInfo, cls).__new__( cls, - str(id) if id else None, - str(kind) if kind else None, - str(name) if name else None, - str(root) if root else None, - str(parentid) if parentid else None, + id=str(id) if id else None, + kind=str(kind) if kind else None, + name=str(name) if name else None, + root=str(root) if root else None, + relpath=str(relpath) if relpath else None, + parentid=str(parentid) if parentid else None, ) return self @@ -53,8 +54,12 @@ def __init__(self, *args, **kwargs): if self.root is None: if self.parentid is not None or self.kind != 'folder': raise TypeError('missing root') + if self.relpath is not None: + raise TypeError('unexpected relpath {}'.format(self.relpath)) elif self.parentid is None: raise TypeError('missing parentid') + elif self.relpath is None and self.kind in ('folder', 'file'): + raise TypeError('missing relpath') class TestInfo(namedtuple('TestInfo', 'id name path source markers parentid kind')): diff --git a/pythonFiles/testing_tools/adapter/pytest/_discovery.py b/pythonFiles/testing_tools/adapter/pytest/_discovery.py index aee1a1eccb98..39685067c601 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_discovery.py +++ b/pythonFiles/testing_tools/adapter/pytest/_discovery.py @@ -3,7 +3,6 @@ from __future__ import absolute_import, print_function -import os.path import sys import pytest @@ -61,8 +60,9 @@ def _adjust_pytest_args(pytestargs): class TestCollector(object): """This is a pytest plugin that collects the discovered tests.""" - NORMCASE = staticmethod(os.path.normcase) - PATHSEP = os.path.sep + @classmethod + def parse_item(cls, item): + return parse_item(item) def __init__(self, tests=None): if tests is None: @@ -77,7 +77,7 @@ def pytest_collection_modifyitems(self, session, config, items): self._started = True self._tests.reset() for item in items: - test, parents = parse_item(item, self.NORMCASE, self.PATHSEP) + test, parents = self.parse_item(item) self._tests.add_test(test, parents) # This hook is not specified in the docs, so we also provide @@ -91,5 +91,5 @@ def pytest_collection_finish(self, session): return self._tests.reset() for item in items: - test, parents = parse_item(item, self.NORMCASE, self.PATHSEP) + test, parents = self.parse_item(item) self._tests.add_test(test, parents) diff --git a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py index 9bd3d9ff45e8..56543ee8b13d 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py +++ b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py @@ -100,9 +100,10 @@ import _pytest.unittest from ..info import TestInfo, TestPath +from ..util import fix_fileid, PATH_SEP, NORMCASE -def should_never_reach_here(node, *extra): +def should_never_reach_here(item, **extra): """Indicates a code path we should never reach.""" print('The Python extension has run into an unexpected situation') print('while processing a pytest node during test discovery. Please') @@ -110,19 +111,20 @@ def should_never_reach_here(node, *extra): print(' https://github.com/microsoft/vscode-python/issues') print('and paste the following output there.') print() - for field, info in _summarize_item(node): + for field, info in _summarize_item(item): print('{}: {}'.format(field, info)) if extra: print() print('extra info:') - for info in extra: - if isinstance(line, str): - print(str) + for name, info in extra.items(): + print('{:10}'.format(name + ':'), end='') + if isinstance(info, str): + print(info) else: try: - print(*line) + print(*info) except TypeError: - print(line) + print(info) print() print('traceback:') import traceback @@ -130,11 +132,16 @@ def should_never_reach_here(node, *extra): msg = 'Unexpected pytest node (see printed output).' exc = NotImplementedError(msg) - exc.node = node + exc.item = item return exc -def parse_item(item, _normcase, _pathsep): +def parse_item(item, #*, + _get_item_kind=(lambda *a: _get_item_kind(*a)), + _parse_node_id=(lambda *a: _parse_node_id(*a)), + _split_fspath=(lambda *a: _split_fspath(*a)), + _get_location=(lambda *a: _get_location(*a)), + ): """Return (TestInfo, [suite ID]) for the given item. The suite IDs, if any, are in parent order with the item's direct @@ -146,30 +153,28 @@ def parse_item(item, _normcase, _pathsep): #_debug_item(item, showsummary=True) kind, _ = _get_item_kind(item) (nodeid, parents, fileid, testfunc, parameterized - ) = _parse_node_id(item.nodeid, kind, _pathsep, _normcase) + ) = _parse_node_id(item.nodeid, kind) # Note: testfunc does not necessarily match item.function.__name__. # This can result from importing a test function from another module. # Figure out the file. - testroot, relfile = _split_fspath(str(item.fspath), fileid, item, - _normcase) - location, fullname = _get_location(item, testroot, relfile, - _normcase, _pathsep) + testroot, relfile = _split_fspath(str(item.fspath), fileid, item) + location, fullname = _get_location(item, testroot, relfile) if kind == 'function': if testfunc and fullname != testfunc + parameterized: raise should_never_reach_here( item, - fullname, - testfunc, - parameterized, + fullname=fullname, + testfunc=testfunc, + parameterized=parameterized, ) elif kind == 'doctest': if (testfunc and fullname != testfunc and fullname != '[doctest] ' + testfunc): raise should_never_reach_here( item, - fullname, - testfunc, + fullname=fullname, + testfunc=testfunc, ) testfunc = None @@ -212,36 +217,47 @@ def parse_item(item, _normcase, _pathsep): return test, parents -def _split_fspath(fspath, fileid, item, _normcase): +def _split_fspath(fspath, fileid, item, #*, + _normcase=NORMCASE, + ): """Return (testroot, relfile) for the given fspath. "relfile" will match "fileid". """ - # "fileid" comes from nodeid, is always normcased, and is always - # relative to the testroot (with a "./" prefix. - _relsuffix = fileid[1:] # Drop (only) the "." prefix. - if not _normcase(fspath).endswith(_relsuffix): + # "fileid" comes from nodeid and is always relative to the testroot + # (with a "./" prefix). There are no guarantees about casing, so we + # normcase just be to sure. + relsuffix = fileid[1:] # Drop (only) the "." prefix. + if not _normcase(fspath).endswith(_normcase(relsuffix)): raise should_never_reach_here( item, - fspath, - fileid, + fspath=fspath, + fileid=fileid, ) testroot = fspath[:-len(fileid) + 1] # Ignore the "./" prefix. relfile = '.' + fspath[-len(fileid) + 1:] # Keep the pathsep. return testroot, relfile -def _get_location(item, testroot, relfile, _normcase, _pathsep): +def _get_location(item, testroot, relfile, #*, + _matches_relfile=(lambda *a: _matches_relfile(*a)), + _is_legacy_wrapper=(lambda *a: _is_legacy_wrapper(*a)), + _unwrap_decorator=(lambda *a: _unwrap_decorator(*a)), + _pathsep=PATH_SEP, + ): """Return (loc str, fullname) for the given item.""" + # When it comes to normcase, we favor relfile (from item.fspath) + # over item.location in this function. + srcfile, lineno, fullname = item.location - if _matches_relfile(srcfile, testroot, relfile, _normcase, _pathsep): + if _matches_relfile(srcfile, testroot, relfile): srcfile = relfile else: # pytest supports discovery of tests imported from other # modules. This is reflected by a different filename # in item.location. - if _is_legacy_wrapper(srcfile, _pathsep): + if _is_legacy_wrapper(srcfile): srcfile = relfile unwrapped = _unwrap_decorator(item.function) if unwrapped is None: @@ -250,14 +266,14 @@ def _get_location(item, testroot, relfile, _normcase, _pathsep): lineno = None else: _srcfile, lineno = unwrapped - if not _matches_relfile(_srcfile, testroot, relfile, _normcase, _pathsep): + if not _matches_relfile(_srcfile, testroot, relfile): # For legacy wrappers we really expect the wrapped # function to be in relfile. So here we ignore any # other file and just say "somewhere in relfile". lineno = None if lineno is None: lineno = -1 # i.e. "unknown" - elif _matches_relfile(srcfile, testroot, relfile, _normcase, _pathsep): + elif _matches_relfile(srcfile, testroot, relfile): srcfile = relfile # Otherwise we just return the info from item.location as-is. @@ -268,7 +284,10 @@ def _get_location(item, testroot, relfile, _normcase, _pathsep): return location, fullname -def _matches_relfile(srcfile, testroot, relfile, _normcase, _pathsep): +def _matches_relfile(srcfile, testroot, relfile, #*, + _normcase=NORMCASE, + _pathsep=PATH_SEP, + ): """Return True if "srcfile" matches the given relfile.""" testroot = _normcase(testroot) srcfile = _normcase(srcfile) @@ -283,7 +302,10 @@ def _matches_relfile(srcfile, testroot, relfile, _normcase, _pathsep): return False -def _is_legacy_wrapper(srcfile, _pathsep, _pyversion=sys.version_info): +def _is_legacy_wrapper(srcfile, #*, + _pathsep=PATH_SEP, + _pyversion=sys.version_info, + ): """Return True if the test might be wrapped. In Python 2 unittest's decorators (e.g. unittest.skip) do not wrap @@ -323,9 +345,11 @@ def _unwrap_decorator(func): return filename, lineno -def _parse_node_id(testid, kind, _pathsep, _normcase): +def _parse_node_id(testid, kind, #*, + _iter_nodes=(lambda *a: _iter_nodes(*a)), + ): """Return the components of the given node ID, in heirarchical order.""" - nodes = iter(_iter_nodes(testid, kind, _pathsep, _normcase)) + nodes = iter(_iter_nodes(testid, kind)) testid, name, kind = next(nodes) parents = [] @@ -347,7 +371,7 @@ def _parse_node_id(testid, kind, _pathsep, _normcase): else: raise should_never_reach_here( testid, - kind, + kind=kind, ) fullname = funcname @@ -365,7 +389,7 @@ def _parse_node_id(testid, kind, _pathsep, _normcase): else: raise should_never_reach_here( testid, - node, + node=node, ) else: fileid = None @@ -374,9 +398,15 @@ def _parse_node_id(testid, kind, _pathsep, _normcase): return testid, parents, fileid, fullname, parameterized or '' -def _iter_nodes(nodeid, kind, _pathsep, _normcase): +def _iter_nodes(testid, kind, #*, + _normalize_test_id=(lambda *a: _normalize_test_id(*a)), + _normcase=NORMCASE, + _pathsep=PATH_SEP, + ): """Yield (nodeid, name, kind) for the given node ID and its parents.""" - nodeid = _normalize_node_id(nodeid, kind, _pathsep, _normcase) + nodeid, testid = _normalize_test_id(testid, kind) + if len(nodeid) > len(testid): + testid = '.' + _pathsep + testid if kind == 'function' and nodeid.endswith(']'): funcid, sep, parameterized = nodeid.partition('[') @@ -408,37 +438,48 @@ def _iter_nodes(nodeid, kind, _pathsep, _normcase): # Extract the file and folders. fileid = parentid - parentid, _, filename = fileid.rpartition(_pathsep) - yield (fileid, filename, 'file') + raw = testid[:len(fileid)] + _parentid, _, filename = _normcase(fileid).rpartition(_pathsep) + parentid = fileid[:len(_parentid)] + raw, name = raw[:len(_parentid)], raw[-len(filename):] + yield (fileid, name, 'file') # We're guaranteed at least one (the test root). - while _pathsep in parentid: + while _pathsep in _normcase(parentid): folderid = parentid - parentid, _, foldername = folderid.rpartition(_pathsep) - yield (folderid, foldername, 'folder') + _parentid, _, foldername = _normcase(folderid).rpartition(_pathsep) + parentid = folderid[:len(_parentid)] + raw, name = raw[:len(parentid)], raw[-len(foldername):] + yield (folderid, name, 'folder') # We set the actual test root later at the bottom of parse_item(). testroot = None yield (parentid, testroot, 'folder') -def _normalize_node_id(nodeid, kind, _pathsep, _normcase): +def _normalize_test_id(testid, kind, #*, + _fix_fileid=fix_fileid, + _pathsep=PATH_SEP, + ): """Return the canonical form for the given node ID.""" - while '::()::' in nodeid: - nodeid = nodeid.replace('::()::', '::') + while '::()::' in testid: + testid = testid.replace('::()::', '::') if kind is None: - return nodeid - - fileid, sep, remainder = nodeid.partition('::') - if sep: - # pytest works fine even if we normalize the filename. - nodeid = _normcase(fileid) + sep + remainder - - if nodeid.startswith(_pathsep): + return testid, testid + orig = testid + + # We need to keep the testid as-is, or else pytest won't recognize + # it when we try to use it later (e.g. to run a test). The only + # exception is that we add a "./" prefix for relative paths. + # Note that pytest always uses "/" as the path separator in IDs. + fileid, sep, remainder = testid.partition('::') + fileid = _fix_fileid(fileid) + if not fileid.startswith('./'): # Absolute "paths" not expected. raise should_never_reach_here( - nodeid, + testid, + fileid=fileid, ) - if not nodeid.startswith('.' + _pathsep): - nodeid = '.' + _pathsep + nodeid - return nodeid + testid = fileid + sep + remainder + + return testid, orig def _get_item_kind(item): @@ -490,7 +531,7 @@ def _summarize_item(item): else: yield field, getattr(item, field, '') except Exception as exc: - yield field, '' + yield field, ''.format(exc) def _debug_item(item, showsummary=False): diff --git a/pythonFiles/testing_tools/adapter/report.py b/pythonFiles/testing_tools/adapter/report.py index edc33fcb8a34..395a30d89c58 100644 --- a/pythonFiles/testing_tools/adapter/report.py +++ b/pythonFiles/testing_tools/adapter/report.py @@ -36,15 +36,19 @@ def report_discovered(tests, parents, pretty=False, simple=False, root['id'] = parent.id continue root['parents'].append({ + # "id" must match what the testing framework recognizes. 'id': parent.id, 'kind': parent.kind, 'name': parent.name, 'parentid': parent.parentid, }) + if parent.relpath is not None: + root['parents'][-1]['relpath'] = parent.relpath for test in tests: # We are guaranteed that the parent was added. root = byroot[test.path.root] testdata = { + # "id" must match what the testing framework recognizes. 'id': test.id, 'name': test.name, # TODO: Add a "kind" field diff --git a/pythonFiles/testing_tools/adapter/util.py b/pythonFiles/testing_tools/adapter/util.py index d8df4eb25485..444ae44c2cae 100644 --- a/pythonFiles/testing_tools/adapter/util.py +++ b/pythonFiles/testing_tools/adapter/util.py @@ -6,6 +6,7 @@ from io import StringIO except ImportError: from StringIO import StringIO # 2.7 +import os.path import sys @@ -14,27 +15,6 @@ def noop_cm(): yield -@contextlib.contextmanager -def hide_stdio(): - """Swallow stdout and stderr.""" - ignored = StdioStream() - sys.stdout = ignored - sys.stderr = ignored - try: - yield ignored - finally: - sys.stdout = sys.__stdout__ - sys.stderr = sys.__stderr__ - - -if sys.version_info < (3,): - class StdioStream(StringIO): - def write(self, msg): - StringIO.write(self, msg.decode()) -else: - StdioStream = StringIO - - def group_attr_names(attrnames): grouped = { 'dunder': [], @@ -61,6 +41,145 @@ def group_attr_names(attrnames): return grouped +if sys.version_info < (3,): + def _str_to_lower(value): + return value.decode().lower() +else: + _str_to_lower = str.lower + + +############################# +# file paths + +_os_path = os.path +# Uncomment to test Windows behavior on non-windows OS: +#import ntpath as _os_path +PATH_SEP = _os_path.sep +NORMCASE = _os_path.normcase +DIRNAME = _os_path.dirname +BASENAME = _os_path.basename +IS_ABS_PATH = _os_path.isabs +PATH_JOIN = _os_path.join + + +def fix_path(path, #*, + _pathsep=PATH_SEP): + """Return a platform-appropriate path for the given path.""" + if not path: + return '.' + return path.replace('/', _pathsep) + + +def fix_relpath(path, #*, + _fix_path=fix_path, + _path_isabs=IS_ABS_PATH, + _pathsep=PATH_SEP + ): + """Return a ./-prefixed, platform-appropriate path for the given path.""" + path = _fix_path(path) + if path in ('.', '..'): + return path + if not _path_isabs(path): + if not path.startswith('.' + _pathsep): + path = '.' + _pathsep + path + return path + + +def _resolve_relpath(path, rootdir=None, #*, + _path_isabs=IS_ABS_PATH, + _normcase=NORMCASE, + _pathsep=PATH_SEP, + ): + # "path" is expected to use "/" for its path separator, regardless + # of the provided "_pathsep". + + if path.startswith('./'): + return path[2:] + if not _path_isabs(path): + return path + + # Deal with root-dir-as-fileid. + _, sep, relpath = path.partition('/') + if sep and not relpath.replace('/', ''): + return '' + + if rootdir is None: + return None + rootdir = _normcase(rootdir) + if not rootdir.endswith(_pathsep): + rootdir += _pathsep + + if not _normcase(path).startswith(rootdir): + return None + return path[len(rootdir):] + + +def fix_fileid(fileid, rootdir=None, #*, + normalize=False, + strictpathsep=None, + _pathsep=PATH_SEP, + **kwargs + ): + """Return a pathsep-separated file ID ("./"-prefixed) for the given value. + + The file ID may be absolute. If so and "rootdir" is + provided then make the file ID relative. If absolute but "rootdir" + is not provided then leave it absolute. + """ + if not fileid or fileid == '.': + return fileid + + # We default to "/" (forward slash) as the final path sep, since + # that gives us a consistent, cross-platform result. (Windows does + # actually support "/" as a path separator.) Most notably, node IDs + # from pytest use "/" as the path separator by default. + _fileid = fileid.replace(_pathsep, '/') + + relpath = _resolve_relpath(_fileid, rootdir, + _pathsep=_pathsep, + **kwargs + ) + if relpath: # Note that we treat "" here as an absolute path. + _fileid = './' + relpath + + if normalize: + if strictpathsep: + raise ValueError( + 'cannot normalize *and* keep strict path separator') + _fileid = _str_to_lower(_fileid) + elif strictpathsep: + # We do not use _normcase since we want to preserve capitalization. + _fileid = _fileid.replace('/', _pathsep) + return _fileid + + +############################# +# stdio + +@contextlib.contextmanager +def hide_stdio(): + """Swallow stdout and stderr.""" + ignored = StdioStream() + sys.stdout = ignored + sys.stderr = ignored + try: + yield ignored + finally: + sys.stdout = sys.__stdout__ + sys.stderr = sys.__stderr__ + + +if sys.version_info < (3,): + class StdioStream(StringIO): + def write(self, msg): + StringIO.write(self, msg.decode()) +else: + StdioStream = StringIO + + +############################# +# shell + def shlex_unsplit(argv): """Return the shell-safe string for the given arguments. diff --git a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py index a70657a2915d..91312c655276 100644 --- a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py +++ b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py @@ -7,8 +7,7 @@ from io import StringIO except ImportError: # 2.7 from StringIO import StringIO -import os -import os.path +from os import name as OS_NAME import sys import unittest @@ -16,14 +15,14 @@ import _pytest.doctest from ....util import Stub, StubProxy +from testing_tools.adapter.util import ( + fix_path, fix_relpath, fix_fileid, PATH_JOIN + ) from testing_tools.adapter.info import TestInfo, TestPath, ParentInfo +from testing_tools.adapter.pytest import _pytest_item as pytest_item from testing_tools.adapter.pytest._discovery import discover, TestCollector -def fix_path(nodeid): - return nodeid.replace('/', os.path.sep) - - class StubPyTest(StubProxy): def __init__(self, stub=None): @@ -173,6 +172,92 @@ def func(*args, **kwargs): return func +def generate_parse_item(pathsep): + if pathsep == '\\': + def normcase(path): + path = path.lower() + return path.replace('/', '\\') + else: + raise NotImplementedError + ########## + def _fix_fileid(*args): + return fix_fileid( + *args, + **dict( + _normcase=normcase, + _pathsep=pathsep, + ) + ) + def _normalize_test_id(*args): + return pytest_item._normalize_test_id( + *args, + **dict( + _fix_fileid=_fix_fileid, + _pathsep=pathsep, + ) + ) + def _iter_nodes(*args): + return pytest_item._iter_nodes( + *args, + **dict( + _normalize_test_id=_normalize_test_id, + _normcase=normcase, + _pathsep=pathsep, + ) + ) + def _parse_node_id(*args): + return pytest_item._parse_node_id( + *args, + **dict( + _iter_nodes=_iter_nodes, + ) + ) + ########## + def _split_fspath(*args): + return pytest_item._split_fspath( + *args, + **dict( + _normcase=normcase, + ) + ) + ########## + def _matches_relfile(*args): + return pytest_item._matches_relfile( + *args, + **dict( + _normcase=normcase, + _pathsep=pathsep, + ) + ) + def _is_legacy_wrapper(*args): + return pytest_item._is_legacy_wrapper( + *args, + **dict( + _pathsep=pathsep, + ) + ) + def _get_location(*args): + return pytest_item._get_location( + *args, + **dict( + _matches_relfile=_matches_relfile, + _is_legacy_wrapper=_is_legacy_wrapper, + _pathsep=pathsep, + ) + ) + ########## + def _parse_item(item): + return pytest_item.parse_item( + item, + **dict( + _parse_node_id=_parse_node_id, + _split_fspath=_split_fspath, + _get_location=_get_location, + ) + ) + return _parse_item + + ################################## # tests @@ -306,7 +391,6 @@ def test_modifyitems(self): testroot = fix_path('/a/b/c') relfile1 = fix_path('./test_spam.py') relfile2 = fix_path('x/y/z/test_eggs.py') - relfileid2 = os.path.join('.', relfile2) collector.pytest_collection_modifyitems(session, config, [ StubFunctionItem( @@ -314,7 +398,7 @@ def test_modifyitems(self): nodeid='test_spam.py::SpamTests::test_one', name='test_one', location=('test_spam.py', 12, 'SpamTests.test_one'), - fspath=os.path.join(testroot, 'test_spam.py'), + fspath=PATH_JOIN(testroot, 'test_spam.py'), function=FakeFunc('test_one'), ), StubFunctionItem( @@ -322,7 +406,7 @@ def test_modifyitems(self): nodeid='test_spam.py::SpamTests::test_other', name='test_other', location=('test_spam.py', 19, 'SpamTests.test_other'), - fspath=os.path.join(testroot, 'test_spam.py'), + fspath=PATH_JOIN(testroot, 'test_spam.py'), function=FakeFunc('test_other'), ), StubFunctionItem( @@ -330,7 +414,7 @@ def test_modifyitems(self): nodeid='test_spam.py::test_all', name='test_all', location=('test_spam.py', 144, 'test_all'), - fspath=os.path.join(testroot, 'test_spam.py'), + fspath=PATH_JOIN(testroot, 'test_spam.py'), function=FakeFunc('test_all'), ), StubFunctionItem( @@ -338,7 +422,7 @@ def test_modifyitems(self): nodeid='test_spam.py::test_each[10-10]', name='test_each[10-10]', location=('test_spam.py', 273, 'test_each[10-10]'), - fspath=os.path.join(testroot, 'test_spam.py'), + fspath=PATH_JOIN(testroot, 'test_spam.py'), function=FakeFunc('test_each'), ), StubFunctionItem( @@ -346,7 +430,7 @@ def test_modifyitems(self): nodeid=relfile2 + '::All::BasicTests::test_first', name='test_first', location=(relfile2, 31, 'All.BasicTests.test_first'), - fspath=os.path.join(testroot, relfile2), + fspath=PATH_JOIN(testroot, relfile2), function=FakeFunc('test_first'), ), StubFunctionItem( @@ -354,7 +438,7 @@ def test_modifyitems(self): nodeid=relfile2 + '::All::BasicTests::test_each[1+2-3]', name='test_each[1+2-3]', location=(relfile2, 62, 'All.BasicTests.test_each[1+2-3]'), - fspath=os.path.join(testroot, relfile2), + fspath=PATH_JOIN(testroot, relfile2), function=FakeFunc('test_each'), own_markers=[FakeMarker(v) for v in [ # supported @@ -374,12 +458,12 @@ def test_modifyitems(self): ('discovered.reset', None, None), ('discovered.add_test', None, dict( parents=[ - (relfile1 + '::SpamTests', 'SpamTests', 'suite'), - (relfile1, 'test_spam.py', 'file'), + ('./test_spam.py::SpamTests', 'SpamTests', 'suite'), + ('./test_spam.py', 'test_spam.py', 'file'), ('.', testroot, 'folder'), ], test=TestInfo( - id=relfile1 + '::SpamTests::test_one', + id='./test_spam.py::SpamTests::test_one', name='test_one', path=TestPath( root=testroot, @@ -389,17 +473,17 @@ def test_modifyitems(self): ), source='{}:{}'.format(relfile1, 13), markers=None, - parentid=relfile1 + '::SpamTests', + parentid='./test_spam.py::SpamTests', ), )), ('discovered.add_test', None, dict( parents=[ - (relfile1 + '::SpamTests', 'SpamTests', 'suite'), - (relfile1, 'test_spam.py', 'file'), + ('./test_spam.py::SpamTests', 'SpamTests', 'suite'), + ('./test_spam.py', 'test_spam.py', 'file'), ('.', testroot, 'folder'), ], test=TestInfo( - id=relfile1 + '::SpamTests::test_other', + id='./test_spam.py::SpamTests::test_other', name='test_other', path=TestPath( root=testroot, @@ -409,16 +493,16 @@ def test_modifyitems(self): ), source='{}:{}'.format(relfile1, 20), markers=None, - parentid=relfile1 + '::SpamTests', + parentid='./test_spam.py::SpamTests', ), )), ('discovered.add_test', None, dict( parents=[ - (relfile1, 'test_spam.py', 'file'), + ('./test_spam.py', 'test_spam.py', 'file'), ('.', testroot, 'folder'), ], test=TestInfo( - id=relfile1 + '::test_all', + id='./test_spam.py::test_all', name='test_all', path=TestPath( root=testroot, @@ -428,17 +512,17 @@ def test_modifyitems(self): ), source='{}:{}'.format(relfile1, 145), markers=None, - parentid=relfile1, + parentid='./test_spam.py', ), )), ('discovered.add_test', None, dict( parents=[ - (relfile1 + '::test_each', 'test_each', 'function'), - (relfile1, 'test_spam.py', 'file'), + ('./test_spam.py::test_each', 'test_each', 'function'), + ('./test_spam.py', 'test_spam.py', 'file'), ('.', testroot, 'folder'), ], test=TestInfo( - id=relfile1 + '::test_each[10-10]', + id='./test_spam.py::test_each[10-10]', name='test_each[10-10]', path=TestPath( root=testroot, @@ -448,56 +532,56 @@ def test_modifyitems(self): ), source='{}:{}'.format(relfile1, 274), markers=None, - parentid=relfile1 + '::test_each', + parentid='./test_spam.py::test_each', ), )), ('discovered.add_test', None, dict( parents=[ - (relfileid2 + '::All::BasicTests', 'BasicTests', 'suite'), - (relfileid2 + '::All', 'All', 'suite'), - (relfileid2, 'test_eggs.py', 'file'), - (fix_path('./x/y/z'), 'z', 'folder'), - (fix_path('./x/y'), 'y', 'folder'), - (fix_path('./x'), 'x', 'folder'), + ('./x/y/z/test_eggs.py::All::BasicTests', 'BasicTests', 'suite'), + ('./x/y/z/test_eggs.py::All', 'All', 'suite'), + ('./x/y/z/test_eggs.py', 'test_eggs.py', 'file'), + ('./x/y/z', 'z', 'folder'), + ('./x/y', 'y', 'folder'), + ('./x', 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id=relfileid2 + '::All::BasicTests::test_first', + id='./x/y/z/test_eggs.py::All::BasicTests::test_first', name='test_first', path=TestPath( root=testroot, - relfile=relfileid2, + relfile=fix_relpath(relfile2), func='All.BasicTests.test_first', sub=None, ), - source='{}:{}'.format(relfileid2, 32), + source='{}:{}'.format(fix_relpath(relfile2), 32), markers=None, - parentid=relfileid2 + '::All::BasicTests', + parentid='./x/y/z/test_eggs.py::All::BasicTests', ), )), ('discovered.add_test', None, dict( parents=[ - (relfileid2 + '::All::BasicTests::test_each', 'test_each', 'function'), - (relfileid2 + '::All::BasicTests', 'BasicTests', 'suite'), - (relfileid2 + '::All', 'All', 'suite'), - (relfileid2, 'test_eggs.py', 'file'), - (fix_path('./x/y/z'), 'z', 'folder'), - (fix_path('./x/y'), 'y', 'folder'), - (fix_path('./x'), 'x', 'folder'), + ('./x/y/z/test_eggs.py::All::BasicTests::test_each', 'test_each', 'function'), + ('./x/y/z/test_eggs.py::All::BasicTests', 'BasicTests', 'suite'), + ('./x/y/z/test_eggs.py::All', 'All', 'suite'), + ('./x/y/z/test_eggs.py', 'test_eggs.py', 'file'), + ('./x/y/z', 'z', 'folder'), + ('./x/y', 'y', 'folder'), + ('./x', 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id=relfileid2 + '::All::BasicTests::test_each[1+2-3]', + id='./x/y/z/test_eggs.py::All::BasicTests::test_each[1+2-3]', name='test_each[1+2-3]', path=TestPath( root=testroot, - relfile=relfileid2, + relfile=fix_relpath(relfile2), func='All.BasicTests.test_each', sub=['[1+2-3]'], ), - source='{}:{}'.format(relfileid2, 63), + source='{}:{}'.format(fix_relpath(relfile2), 63), markers=['expected-failure', 'skip', 'skip-if'], - parentid=relfileid2 + '::All::BasicTests::test_each', + parentid='./x/y/z/test_eggs.py::All::BasicTests::test_each', ), )), ]) @@ -508,14 +592,13 @@ def test_finish(self): session = StubPytestSession(stub) testroot = fix_path('/a/b/c') relfile = fix_path('x/y/z/test_eggs.py') - relfileid = os.path.join('.', relfile) session.items = [ StubFunctionItem( stub, nodeid=relfile + '::SpamTests::test_spam', name='test_spam', location=(relfile, 12, 'SpamTests.test_spam'), - fspath=os.path.join(testroot, relfile), + fspath=PATH_JOIN(testroot, relfile), function=FakeFunc('test_spam'), ), ] @@ -528,25 +611,25 @@ def test_finish(self): ('discovered.reset', None, None), ('discovered.add_test', None, dict( parents=[ - (relfileid + '::SpamTests', 'SpamTests', 'suite'), - (relfileid, 'test_eggs.py', 'file'), - (fix_path('./x/y/z'), 'z', 'folder'), - (fix_path('./x/y'), 'y', 'folder'), - (fix_path('./x'), 'x', 'folder'), + ('./x/y/z/test_eggs.py::SpamTests', 'SpamTests', 'suite'), + ('./x/y/z/test_eggs.py', 'test_eggs.py', 'file'), + ('./x/y/z', 'z', 'folder'), + ('./x/y', 'y', 'folder'), + ('./x', 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id=relfileid + '::SpamTests::test_spam', + id='./x/y/z/test_eggs.py::SpamTests::test_spam', name='test_spam', path=TestPath( root=testroot, - relfile=relfileid, + relfile=fix_relpath(relfile), func='SpamTests.test_spam', sub=None, ), - source='{}:{}'.format(relfileid, 13), + source='{}:{}'.format(fix_relpath(relfile), 13), markers=None, - parentid=relfileid + '::SpamTests', + parentid='./x/y/z/test_eggs.py::SpamTests', ), )), ]) @@ -557,16 +640,14 @@ def test_doctest(self): session = StubPytestSession(stub) testroot = fix_path('/a/b/c') doctestfile = fix_path('x/test_doctest.txt') - doctestfileid = os.path.join('.', doctestfile) relfile = fix_path('x/y/z/test_eggs.py') - relfileid = os.path.join('.', relfile) session.items = [ StubDoctestItem( stub, nodeid=doctestfile + '::test_doctest.txt', name='test_doctest.txt', location=(doctestfile, 0, '[doctest] test_doctest.txt'), - fspath=os.path.join(testroot, doctestfile), + fspath=PATH_JOIN(testroot, doctestfile), ), # With --doctest-modules StubDoctestItem( @@ -574,21 +655,21 @@ def test_doctest(self): nodeid=relfile + '::test_eggs', name='test_eggs', location=(relfile, 0, '[doctest] test_eggs'), - fspath=os.path.join(testroot, relfile), + fspath=PATH_JOIN(testroot, relfile), ), StubDoctestItem( stub, nodeid=relfile + '::test_eggs.TestSpam', name='test_eggs.TestSpam', location=(relfile, 12, '[doctest] test_eggs.TestSpam'), - fspath=os.path.join(testroot, relfile), + fspath=PATH_JOIN(testroot, relfile), ), StubDoctestItem( stub, nodeid=relfile + '::test_eggs.TestSpam.TestEggs', name='test_eggs.TestSpam.TestEggs', location=(relfile, 27, '[doctest] test_eggs.TestSpam.TestEggs'), - fspath=os.path.join(testroot, relfile), + fspath=PATH_JOIN(testroot, relfile), ), ] collector = TestCollector(tests=discovered) @@ -600,84 +681,84 @@ def test_doctest(self): ('discovered.reset', None, None), ('discovered.add_test', None, dict( parents=[ - (doctestfileid, 'test_doctest.txt', 'file'), - (fix_path('./x'), 'x', 'folder'), + ('./x/test_doctest.txt', 'test_doctest.txt', 'file'), + ('./x', 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id=doctestfileid + '::test_doctest.txt', + id='./x/test_doctest.txt::test_doctest.txt', name='test_doctest.txt', path=TestPath( root=testroot, - relfile=doctestfileid, + relfile=fix_relpath(doctestfile), func=None, ), - source='{}:{}'.format(doctestfileid, 1), + source='{}:{}'.format(fix_relpath(doctestfile), 1), markers=[], - parentid=doctestfileid, + parentid='./x/test_doctest.txt', ), )), ('discovered.add_test', None, dict( parents=[ - (relfileid, 'test_eggs.py', 'file'), - (fix_path('./x/y/z'), 'z', 'folder'), - (fix_path('./x/y'), 'y', 'folder'), - (fix_path('./x'), 'x', 'folder'), + ('./x/y/z/test_eggs.py', 'test_eggs.py', 'file'), + ('./x/y/z', 'z', 'folder'), + ('./x/y', 'y', 'folder'), + ('./x', 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id=relfileid + '::test_eggs', + id='./x/y/z/test_eggs.py::test_eggs', name='test_eggs', path=TestPath( root=testroot, - relfile=relfileid, + relfile=fix_relpath(relfile), func=None, ), - source='{}:{}'.format(relfileid, 1), + source='{}:{}'.format(fix_relpath(relfile), 1), markers=[], - parentid=relfileid, + parentid='./x/y/z/test_eggs.py', ), )), ('discovered.add_test', None, dict( parents=[ - (relfileid, 'test_eggs.py', 'file'), - (fix_path('./x/y/z'), 'z', 'folder'), - (fix_path('./x/y'), 'y', 'folder'), - (fix_path('./x'), 'x', 'folder'), + ('./x/y/z/test_eggs.py', 'test_eggs.py', 'file'), + ('./x/y/z', 'z', 'folder'), + ('./x/y', 'y', 'folder'), + ('./x', 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id=relfileid + '::test_eggs.TestSpam', + id='./x/y/z/test_eggs.py::test_eggs.TestSpam', name='test_eggs.TestSpam', path=TestPath( root=testroot, - relfile=relfileid, + relfile=fix_relpath(relfile), func=None, ), - source='{}:{}'.format(relfileid, 13), + source='{}:{}'.format(fix_relpath(relfile), 13), markers=[], - parentid=relfileid, + parentid='./x/y/z/test_eggs.py', ), )), ('discovered.add_test', None, dict( parents=[ - (relfileid, 'test_eggs.py', 'file'), - (fix_path('./x/y/z'), 'z', 'folder'), - (fix_path('./x/y'), 'y', 'folder'), - (fix_path('./x'), 'x', 'folder'), + ('./x/y/z/test_eggs.py', 'test_eggs.py', 'file'), + ('./x/y/z', 'z', 'folder'), + ('./x/y', 'y', 'folder'), + ('./x', 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id=relfileid + '::test_eggs.TestSpam.TestEggs', + id='./x/y/z/test_eggs.py::test_eggs.TestSpam.TestEggs', name='test_eggs.TestSpam.TestEggs', path=TestPath( root=testroot, - relfile=relfileid, + relfile=fix_relpath(relfile), func=None, ), - source='{}:{}'.format(relfileid, 28), + source='{}:{}'.format(fix_relpath(relfile), 28), markers=[], - parentid=relfileid, + parentid='./x/y/z/test_eggs.py', ), )), ]) @@ -688,14 +769,13 @@ def test_nested_brackets(self): session = StubPytestSession(stub) testroot = fix_path('/a/b/c') relfile = fix_path('x/y/z/test_eggs.py') - relfileid = os.path.join('.', relfile) session.items = [ StubFunctionItem( stub, nodeid=relfile + '::SpamTests::test_spam[a-[b]-c]', name='test_spam[a-[b]-c]', location=(relfile, 12, 'SpamTests.test_spam[a-[b]-c]'), - fspath=os.path.join(testroot, relfile), + fspath=PATH_JOIN(testroot, relfile), function=FakeFunc('test_spam'), ), ] @@ -708,26 +788,26 @@ def test_nested_brackets(self): ('discovered.reset', None, None), ('discovered.add_test', None, dict( parents=[ - (relfileid + '::SpamTests::test_spam', 'test_spam', 'function'), - (relfileid + '::SpamTests', 'SpamTests', 'suite'), - (relfileid, 'test_eggs.py', 'file'), - (fix_path('./x/y/z'), 'z', 'folder'), - (fix_path('./x/y'), 'y', 'folder'), - (fix_path('./x'), 'x', 'folder'), + ('./x/y/z/test_eggs.py::SpamTests::test_spam', 'test_spam', 'function'), + ('./x/y/z/test_eggs.py::SpamTests', 'SpamTests', 'suite'), + ('./x/y/z/test_eggs.py', 'test_eggs.py', 'file'), + ('./x/y/z', 'z', 'folder'), + ('./x/y', 'y', 'folder'), + ('./x', 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id=relfileid + '::SpamTests::test_spam[a-[b]-c]', + id='./x/y/z/test_eggs.py::SpamTests::test_spam[a-[b]-c]', name='test_spam[a-[b]-c]', path=TestPath( root=testroot, - relfile=relfileid, + relfile=fix_relpath(relfile), func='SpamTests.test_spam', sub=['[a-[b]-c]'], ), - source='{}:{}'.format(relfileid, 13), + source='{}:{}'.format(fix_relpath(relfile), 13), markers=None, - parentid=relfileid + '::SpamTests::test_spam', + parentid='./x/y/z/test_eggs.py::SpamTests::test_spam', ), )), ]) @@ -738,14 +818,13 @@ def test_nested_suite(self): session = StubPytestSession(stub) testroot = fix_path('/a/b/c') relfile = fix_path('x/y/z/test_eggs.py') - relfileid = os.path.join('.', relfile) session.items = [ StubFunctionItem( stub, nodeid=relfile + '::SpamTests::Ham::Eggs::test_spam', name='test_spam', location=(relfile, 12, 'SpamTests.Ham.Eggs.test_spam'), - fspath=os.path.join(testroot, relfile), + fspath=PATH_JOIN(testroot, relfile), function=FakeFunc('test_spam'), ), ] @@ -758,27 +837,27 @@ def test_nested_suite(self): ('discovered.reset', None, None), ('discovered.add_test', None, dict( parents=[ - (relfileid + '::SpamTests::Ham::Eggs', 'Eggs', 'suite'), - (relfileid + '::SpamTests::Ham', 'Ham', 'suite'), - (relfileid + '::SpamTests', 'SpamTests', 'suite'), - (relfileid, 'test_eggs.py', 'file'), - (fix_path('./x/y/z'), 'z', 'folder'), - (fix_path('./x/y'), 'y', 'folder'), - (fix_path('./x'), 'x', 'folder'), + ('./x/y/z/test_eggs.py::SpamTests::Ham::Eggs', 'Eggs', 'suite'), + ('./x/y/z/test_eggs.py::SpamTests::Ham', 'Ham', 'suite'), + ('./x/y/z/test_eggs.py::SpamTests', 'SpamTests', 'suite'), + ('./x/y/z/test_eggs.py', 'test_eggs.py', 'file'), + ('./x/y/z', 'z', 'folder'), + ('./x/y', 'y', 'folder'), + ('./x', 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id=relfileid + '::SpamTests::Ham::Eggs::test_spam', + id='./x/y/z/test_eggs.py::SpamTests::Ham::Eggs::test_spam', name='test_spam', path=TestPath( root=testroot, - relfile=relfileid, + relfile=fix_relpath(relfile), func='SpamTests.Ham.Eggs.test_spam', sub=None, ), - source='{}:{}'.format(relfileid, 13), + source='{}:{}'.format(fix_relpath(relfile), 13), markers=None, - parentid=relfileid + '::SpamTests::Ham::Eggs', + parentid='./x/y/z/test_eggs.py::SpamTests::Ham::Eggs', ), )), ]) @@ -788,24 +867,54 @@ def test_windows(self): discovered = StubDiscoveredTests(stub) session = StubPytestSession(stub) testroot = r'C:\A\B\C' - relfile = r'X\Y\Z\test_eggs.py' + altroot = testroot.replace('\\', '/') + relfile = r'X\Y\Z\test_Eggs.py' session.items = [ + # typical: StubFunctionItem( stub, - nodeid='x/y/z/test_eggs.py::SpamTests::test_spam', + # pytest always uses "/" as the path separator in node IDs: + nodeid='X/Y/Z/test_Eggs.py::SpamTests::test_spam', name='test_spam', - location=('X/Y/Z/test_eggs.py', 12, 'SpamTests.test_spam'), + # normal path separator (contrast with nodeid): + location=(relfile, 12, 'SpamTests.test_spam'), + # path separator matches location: fspath=testroot + '\\' + relfile, function=FakeFunc('test_spam'), ), ] + tests = [ + # permutations of path separators + (r'X/test_a.py', '\\', '\\'), # typical + (r'X/test_b.py', '\\', '/'), + (r'X/test_c.py', '/', '\\'), + (r'X/test_d.py', '/', '/'), + (r'X\test_e.py', '\\', '\\'), + (r'X\test_f.py', '\\', '/'), + (r'X\test_g.py', '/', '\\'), + (r'X\test_h.py', '/', '/'), + ] + for fileid, locfile, fspath in tests: + if locfile == '/': + locfile = fileid.replace('\\', '/') + elif locfile == '\\': + locfile = fileid.replace('/', '\\') + if fspath == '/': + fspath = (testroot + '/' + fileid).replace('\\', '/') + elif fspath == '\\': + fspath = (testroot + '/' + fileid).replace('/', '\\') + session.items.append( + StubFunctionItem( + stub, + nodeid=fileid + '::test_spam', + name='test_spam', + location=(locfile, 12, 'test_spam'), + fspath=fspath, + function=FakeFunc('test_spam'), + )) collector = TestCollector(tests=discovered) - if os.name != 'nt': - def normcase(path): - path = path.lower() - return path.replace('/', '\\') - collector.NORMCASE = normcase - collector.PATHSEP = '\\' + if OS_NAME != 'nt': + collector.parse_item = generate_parse_item('\\') collector.pytest_collection_finish(session) @@ -814,25 +923,198 @@ def normcase(path): ('discovered.reset', None, None), ('discovered.add_test', None, dict( parents=[ - (r'.\x\y\z\test_eggs.py::SpamTests', 'SpamTests', 'suite'), - (r'.\x\y\z\test_eggs.py', 'test_eggs.py', 'file'), - (r'.\x\y\z', 'z', 'folder'), - (r'.\x\y', 'y', 'folder'), - (r'.\x', 'x', 'folder'), + (r'./X/Y/Z/test_Eggs.py::SpamTests', 'SpamTests', 'suite'), + (r'./X/Y/Z/test_Eggs.py', 'test_Eggs.py', 'file'), + (r'./X/Y/Z', 'Z', 'folder'), + (r'./X/Y', 'Y', 'folder'), + (r'./X', 'X', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id=r'.\x\y\z\test_eggs.py::SpamTests::test_spam', + id=r'./X/Y/Z/test_Eggs.py::SpamTests::test_spam', name='test_spam', path=TestPath( root=testroot, # not normalized - relfile=r'.\X\Y\Z\test_eggs.py', # not normalized + relfile=r'.\X\Y\Z\test_Eggs.py', # not normalized func='SpamTests.test_spam', sub=None, ), - source=r'.\X\Y\Z\test_eggs.py:{}'.format(13), # not normalized + source=r'.\X\Y\Z\test_Eggs.py:13', # not normalized + markers=None, + parentid=r'./X/Y/Z/test_Eggs.py::SpamTests', + ), + )), + + # permutations + # (*all* the IDs use "/") + # (source path separator should match relfile, not location) + + # /, \, \ + ('discovered.add_test', None, dict( + parents=[ + (r'./X/test_a.py', 'test_a.py', 'file'), + (r'./X', 'X', 'folder'), + ('.', testroot, 'folder'), + ], + test=TestInfo( + id=r'./X/test_a.py::test_spam', + name='test_spam', + path=TestPath( + root=testroot, + relfile=r'.\X\test_a.py', + func='test_spam', + sub=None, + ), + source=r'.\X\test_a.py:13', + markers=None, + parentid=r'./X/test_a.py', + ), + )), + # /, \, / + ('discovered.add_test', None, dict( + parents=[ + (r'./X/test_b.py', 'test_b.py', 'file'), + (r'./X', 'X', 'folder'), + ('.', altroot, 'folder'), + ], + test=TestInfo( + id=r'./X/test_b.py::test_spam', + name='test_spam', + path=TestPath( + root=altroot, + relfile=r'./X/test_b.py', + func='test_spam', + sub=None, + ), + source=r'./X/test_b.py:13', + markers=None, + parentid=r'./X/test_b.py', + ), + )), + # /, /, \ + ('discovered.add_test', None, dict( + parents=[ + (r'./X/test_c.py', 'test_c.py', 'file'), + (r'./X', 'X', 'folder'), + ('.', testroot, 'folder'), + ], + test=TestInfo( + id=r'./X/test_c.py::test_spam', + name='test_spam', + path=TestPath( + root=testroot, + relfile=r'.\X\test_c.py', + func='test_spam', + sub=None, + ), + source=r'.\X\test_c.py:13', + markers=None, + parentid=r'./X/test_c.py', + ), + )), + # /, /, / + ('discovered.add_test', None, dict( + parents=[ + (r'./X/test_d.py', 'test_d.py', 'file'), + (r'./X', 'X', 'folder'), + ('.', altroot, 'folder'), + ], + test=TestInfo( + id=r'./X/test_d.py::test_spam', + name='test_spam', + path=TestPath( + root=altroot, + relfile=r'./X/test_d.py', + func='test_spam', + sub=None, + ), + source=r'./X/test_d.py:13', + markers=None, + parentid=r'./X/test_d.py', + ), + )), + # \, \, \ + ('discovered.add_test', None, dict( + parents=[ + (r'./X/test_e.py', 'test_e.py', 'file'), + (r'./X', 'X', 'folder'), + ('.', testroot, 'folder'), + ], + test=TestInfo( + id=r'./X/test_e.py::test_spam', + name='test_spam', + path=TestPath( + root=testroot, + relfile=r'.\X\test_e.py', + func='test_spam', + sub=None, + ), + source=r'.\X\test_e.py:13', + markers=None, + parentid=r'./X/test_e.py', + ), + )), + # \, \, / + ('discovered.add_test', None, dict( + parents=[ + (r'./X/test_f.py', 'test_f.py', 'file'), + (r'./X', 'X', 'folder'), + ('.', altroot, 'folder'), + ], + test=TestInfo( + id=r'./X/test_f.py::test_spam', + name='test_spam', + path=TestPath( + root=altroot, + relfile=r'./X/test_f.py', + func='test_spam', + sub=None, + ), + source=r'./X/test_f.py:13', + markers=None, + parentid=r'./X/test_f.py', + ), + )), + # \, /, \ + ('discovered.add_test', None, dict( + parents=[ + (r'./X/test_g.py', 'test_g.py', 'file'), + (r'./X', 'X', 'folder'), + ('.', testroot, 'folder'), + ], + test=TestInfo( + id=r'./X/test_g.py::test_spam', + name='test_spam', + path=TestPath( + root=testroot, + relfile=r'.\X\test_g.py', + func='test_spam', + sub=None, + ), + source=r'.\X\test_g.py:13', + markers=None, + parentid=r'./X/test_g.py', + ), + )), + # \, /, / + ('discovered.add_test', None, dict( + parents=[ + (r'./X/test_h.py', 'test_h.py', 'file'), + (r'./X', 'X', 'folder'), + ('.', altroot, 'folder'), + ], + test=TestInfo( + id=r'./X/test_h.py::test_spam', + name='test_spam', + path=TestPath( + root=altroot, + relfile=r'./X/test_h.py', + func='test_spam', + sub=None, + ), + source=r'./X/test_h.py:13', markers=None, - parentid=r'.\x\y\z\test_eggs.py::SpamTests', + parentid=r'./X/test_h.py', ), )), ]) @@ -843,14 +1125,13 @@ def test_mysterious_parens(self): session = StubPytestSession(stub) testroot = fix_path('/a/b/c') relfile = fix_path('x/y/z/test_eggs.py') - relfileid = os.path.join('.', relfile) session.items = [ StubFunctionItem( stub, nodeid=relfile + '::SpamTests::()::()::test_spam', name='test_spam', location=(relfile, 12, 'SpamTests.test_spam'), - fspath=os.path.join(testroot, relfile), + fspath=PATH_JOIN(testroot, relfile), function=FakeFunc('test_spam'), ), ] @@ -863,25 +1144,25 @@ def test_mysterious_parens(self): ('discovered.reset', None, None), ('discovered.add_test', None, dict( parents=[ - (relfileid + '::SpamTests', 'SpamTests', 'suite'), - (relfileid, 'test_eggs.py', 'file'), - (fix_path('./x/y/z'), 'z', 'folder'), - (fix_path('./x/y'), 'y', 'folder'), - (fix_path('./x'), 'x', 'folder'), + ('./x/y/z/test_eggs.py::SpamTests', 'SpamTests', 'suite'), + ('./x/y/z/test_eggs.py', 'test_eggs.py', 'file'), + ('./x/y/z', 'z', 'folder'), + ('./x/y', 'y', 'folder'), + ('./x', 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id=relfileid + '::SpamTests::test_spam', + id='./x/y/z/test_eggs.py::SpamTests::test_spam', name='test_spam', path=TestPath( root=testroot, - relfile=relfileid, + relfile=fix_relpath(relfile), func='SpamTests.test_spam', sub=[], ), - source='{}:{}'.format(relfileid, 13), + source='{}:{}'.format(fix_relpath(relfile), 13), markers=None, - parentid=relfileid + '::SpamTests', + parentid='./x/y/z/test_eggs.py::SpamTests', ), )), ]) @@ -894,7 +1175,6 @@ def test_imported_test(self): session = StubPytestSession(stub) testroot = fix_path('/a/b/c') relfile = fix_path('x/y/z/test_eggs.py') - relfileid = os.path.join('.', relfile) srcfile = fix_path('x/y/z/_extern.py') session.items = [ StubFunctionItem( @@ -902,7 +1182,7 @@ def test_imported_test(self): nodeid=relfile + '::SpamTests::test_spam', name='test_spam', location=(srcfile, 12, 'SpamTests.test_spam'), - fspath=os.path.join(testroot, relfile), + fspath=PATH_JOIN(testroot, relfile), function=FakeFunc('test_spam'), ), StubFunctionItem( @@ -910,7 +1190,7 @@ def test_imported_test(self): nodeid=relfile + '::test_ham', name='test_ham', location=(srcfile, 3, 'test_ham'), - fspath=os.path.join(testroot, relfile), + fspath=PATH_JOIN(testroot, relfile), function=FakeFunc('test_spam'), ), ] @@ -923,47 +1203,47 @@ def test_imported_test(self): ('discovered.reset', None, None), ('discovered.add_test', None, dict( parents=[ - (relfileid + '::SpamTests', 'SpamTests', 'suite'), - (relfileid, 'test_eggs.py', 'file'), - (fix_path('./x/y/z'), 'z', 'folder'), - (fix_path('./x/y'), 'y', 'folder'), - (fix_path('./x'), 'x', 'folder'), + ('./x/y/z/test_eggs.py::SpamTests', 'SpamTests', 'suite'), + ('./x/y/z/test_eggs.py', 'test_eggs.py', 'file'), + ('./x/y/z', 'z', 'folder'), + ('./x/y', 'y', 'folder'), + ('./x', 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id=relfileid + '::SpamTests::test_spam', + id='./x/y/z/test_eggs.py::SpamTests::test_spam', name='test_spam', path=TestPath( root=testroot, - relfile=relfileid, + relfile=fix_relpath(relfile), func='SpamTests.test_spam', sub=None, ), - source='{}:{}'.format(os.path.join('.', srcfile), 13), + source='{}:{}'.format(fix_relpath(srcfile), 13), markers=None, - parentid=relfileid + '::SpamTests', + parentid='./x/y/z/test_eggs.py::SpamTests', ), )), ('discovered.add_test', None, dict( parents=[ - (relfileid, 'test_eggs.py', 'file'), - (fix_path('./x/y/z'), 'z', 'folder'), - (fix_path('./x/y'), 'y', 'folder'), - (fix_path('./x'), 'x', 'folder'), + ('./x/y/z/test_eggs.py', 'test_eggs.py', 'file'), + ('./x/y/z', 'z', 'folder'), + ('./x/y', 'y', 'folder'), + ('./x', 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id=relfileid + '::test_ham', + id='./x/y/z/test_eggs.py::test_ham', name='test_ham', path=TestPath( root=testroot, - relfile=relfileid, + relfile=fix_relpath(relfile), func='test_ham', sub=None, ), - source='{}:{}'.format(os.path.join('.', srcfile), 4), + source='{}:{}'.format(fix_relpath(srcfile), 4), markers=None, - parentid=relfileid, + parentid='./x/y/z/test_eggs.py', ), )), ]) diff --git a/pythonFiles/tests/testing_tools/adapter/test_discovery.py b/pythonFiles/tests/testing_tools/adapter/test_discovery.py index 01475afd4b00..ce6b615c5f37 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_discovery.py +++ b/pythonFiles/tests/testing_tools/adapter/test_discovery.py @@ -3,26 +3,30 @@ from __future__ import absolute_import, print_function -import os.path import unittest +from testing_tools.adapter.util import fix_path, fix_relpath from testing_tools.adapter.info import TestInfo, TestPath, ParentInfo -from testing_tools.adapter.discovery import DiscoveredTests +from testing_tools.adapter.discovery import fix_nodeid, DiscoveredTests -def fix_path(nodeid): - return nodeid.replace('/', os.path.sep) +def _fix_nodeid(nodeid): + + nodeid = nodeid.replace('\\', '/') + if not nodeid.startswith('./'): + nodeid = './' + nodeid + return nodeid class DiscoveredTestsTests(unittest.TestCase): def test_list(self): testroot = fix_path('/a/b/c') - relfile = 'test_spam.py' - relfileid = os.path.join('.', relfile) + relfile = fix_path('./test_spam.py') tests = [ TestInfo( - id=relfile + '::test_each[10-10]', + # missing "./": + id='test_spam.py::test_each[10-10]', name='test_each[10-10]', path=TestPath( root=testroot, @@ -32,10 +36,11 @@ def test_list(self): ), source='{}:{}'.format(relfile, 10), markers=None, - parentid=relfile + '::test_each', + # missing "./": + parentid='test_spam.py::test_each', ), TestInfo( - id=relfile + '::All::BasicTests::test_first', + id='test_spam.py::All::BasicTests::test_first', name='test_first', path=TestPath( root=testroot, @@ -45,22 +50,22 @@ def test_list(self): ), source='{}:{}'.format(relfile, 62), markers=None, - parentid=relfile + '::All::BasicTests', + parentid='test_spam.py::All::BasicTests', ), ] allparents= [ - [(relfileid + '::test_each', 'test_each', 'function'), - (relfileid, relfile, 'file'), + [(fix_path('./test_spam.py::test_each'), 'test_each', 'function'), + (fix_path('./test_spam.py'), 'test_spam.py', 'file'), ('.', testroot, 'folder'), ], - [(relfileid + '::All::BasicTests', 'BasicTests', 'suite'), - (relfileid + '::All', 'All', 'suite'), - (relfileid, relfile, 'file'), + [(fix_path('./test_spam.py::All::BasicTests'), 'BasicTests', 'suite'), + (fix_path('./test_spam.py::All'), 'All', 'suite'), + (fix_path('./test_spam.py'), 'test_spam.py', 'file'), ('.', testroot, 'folder'), ], ] - expected = [test._replace(id=os.path.join('.', test.id), - parentid=os.path.join('.', test.parentid)) + expected = [test._replace(id=_fix_nodeid(test.id), + parentid=_fix_nodeid(test.parentid)) for test in tests] discovered = DiscoveredTests() for test, parents in zip(tests, allparents): @@ -86,7 +91,7 @@ def test_reset(self): relfile='test_spam.py', func='test_each', ), - source='{}:{}'.format('test_spam.py', 11), + source='test_spam.py:11', markers=[], parentid='./test_spam.py', ), @@ -104,45 +109,50 @@ def test_reset(self): def test_parents(self): testroot = fix_path('/a/b/c') relfile = fix_path('x/y/z/test_spam.py') - relfileid = os.path.join('.', relfile) tests = [ TestInfo( + # missing "./", using pathsep: id=relfile + '::test_each[10-10]', name='test_each[10-10]', path=TestPath( root=testroot, - relfile=relfile, + relfile=fix_relpath(relfile), func='test_each', sub=['[10-10]'], ), source='{}:{}'.format(relfile, 10), markers=None, + # missing "./", using pathsep: parentid=relfile + '::test_each', ), TestInfo( + # missing "./", using pathsep: id=relfile + '::All::BasicTests::test_first', name='test_first', path=TestPath( root=testroot, - relfile=relfile, + relfile=fix_relpath(relfile), func='All.BasicTests.test_first', sub=None, ), source='{}:{}'.format(relfile, 61), markers=None, + # missing "./", using pathsep: parentid=relfile + '::All::BasicTests', ), ] allparents= [ - [(relfileid + '::test_each', 'test_each', 'function'), - (relfileid, relfile, 'file'), + # missing "./", using pathsep: + [(relfile + '::test_each', 'test_each', 'function'), + (relfile, relfile, 'file'), ('.', testroot, 'folder'), ], - [(relfileid + '::All::BasicTests', 'BasicTests', 'suite'), - (relfileid + '::All', 'All', 'suite'), - (relfileid, 'test_spam.py', 'file'), - (fix_path('./x/y/z'), 'z', 'folder'), - (fix_path('./x/y'), 'y', 'folder'), + # missing "./", using pathsep: + [(relfile + '::All::BasicTests', 'BasicTests', 'suite'), + (relfile + '::All', 'All', 'suite'), + (relfile, 'test_spam.py', 'file'), + (fix_path('x/y/z'), 'z', 'folder'), + (fix_path('x/y'), 'y', 'folder'), (fix_path('./x'), 'x', 'folder'), ('.', testroot, 'folder'), ], @@ -161,74 +171,81 @@ def test_parents(self): name=testroot, ), ParentInfo( - id=fix_path('./x'), + id='./x', kind='folder', name='x', root=testroot, + relpath=fix_path('./x'), parentid='.', ), ParentInfo( - id=fix_path('./x/y'), + id='./x/y', kind='folder', name='y', root=testroot, - parentid=fix_path('./x'), + relpath=fix_path('./x/y'), + parentid='./x', ), ParentInfo( - id=fix_path('./x/y/z'), + id='./x/y/z', kind='folder', name='z', root=testroot, - parentid=fix_path('./x/y'), + relpath=fix_path('./x/y/z'), + parentid='./x/y', ), ParentInfo( - id=relfileid, + id='./x/y/z/test_spam.py', kind='file', - name=os.path.basename(relfile), + name='test_spam.py', root=testroot, - parentid=os.path.dirname(relfileid), + relpath=fix_relpath(relfile), + parentid='./x/y/z', ), ParentInfo( - id=relfileid + '::All', + id='./x/y/z/test_spam.py::All', kind='suite', name='All', root=testroot, - parentid=relfileid, + parentid='./x/y/z/test_spam.py', ), ParentInfo( - id=relfileid + '::All::BasicTests', + id='./x/y/z/test_spam.py::All::BasicTests', kind='suite', name='BasicTests', root=testroot, - parentid=relfileid + '::All', + parentid='./x/y/z/test_spam.py::All', ), ParentInfo( - id=relfileid + '::test_each', + id='./x/y/z/test_spam.py::test_each', kind='function', name='test_each', root=testroot, - parentid=relfileid, + parentid='./x/y/z/test_spam.py', ), ]) def test_add_test_simple(self): testroot = fix_path('/a/b/c') relfile = 'test_spam.py' - relfileid = os.path.join('.', relfile) test = TestInfo( + # missing "./": id=relfile + '::test_spam', name='test_spam', path=TestPath( root=testroot, + # missing "./": relfile=relfile, func='test_spam', ), + # missing "./": source='{}:{}'.format(relfile, 11), markers=[], + # missing "./": parentid=relfile, ) - expected = test._replace(id=os.path.join('.', test.id), - parentid=relfileid) + expected = test._replace(id=_fix_nodeid(test.id), + parentid=_fix_nodeid(test.parentid)) discovered = DiscoveredTests() before = list(discovered), discovered.parents @@ -247,10 +264,11 @@ def test_add_test_simple(self): name=testroot, ), ParentInfo( - id=relfileid, + id='./test_spam.py', kind='file', name=relfile, root=testroot, + relpath=relfile, parentid='.', ), ])) @@ -259,37 +277,38 @@ def test_multiroot(self): # the first root testroot1 = fix_path('/a/b/c') relfile1 = 'test_spam.py' - relfileid1 = os.path.join('.', relfile1) alltests = [ TestInfo( + # missing "./": id=relfile1 + '::test_spam', name='test_spam', path=TestPath( root=testroot1, - relfile=relfile1, + relfile=fix_relpath(relfile1), func='test_spam', ), source='{}:{}'.format(relfile1, 10), markers=[], + # missing "./": parentid=relfile1, ), ] allparents = [ - [(relfileid1, 'test_spam.py', 'file'), + # missing "./": + [(relfile1, 'test_spam.py', 'file'), ('.', testroot1, 'folder'), ], ] # the second root testroot2 = fix_path('/x/y/z') - relfile2 = 'w/test_eggs.py' - relfileid2 = os.path.join('.', relfile2) + relfile2 = fix_path('w/test_eggs.py') alltests.extend([ TestInfo( - id=relfile2 + 'BasicTests::test_first', + id=relfile2 + '::BasicTests::test_first', name='test_first', path=TestPath( root=testroot2, - relfile=relfile2, + relfile=fix_relpath(relfile2), func='BasicTests.test_first', ), source='{}:{}'.format(relfile2, 61), @@ -298,8 +317,9 @@ def test_multiroot(self): ), ]) allparents.extend([ - [(relfileid2 + '::BasicTests', 'BasicTests', 'suite'), - (relfileid2, 'test_eggs.py', 'file'), + # missing "./", using pathsep: + [(relfile2 + '::BasicTests', 'BasicTests', 'suite'), + (relfile2, 'test_eggs.py', 'file'), (fix_path('./w'), 'w', 'folder'), ('.', testroot2, 'folder'), ], @@ -315,29 +335,29 @@ def test_multiroot(self): self.assertEqual(tests, [ # the first root TestInfo( - id=relfileid1 + '::test_spam', + id='./test_spam.py::test_spam', name='test_spam', path=TestPath( root=testroot1, - relfile=relfile1, + relfile=fix_relpath(relfile1), func='test_spam', ), source='{}:{}'.format(relfile1, 10), markers=[], - parentid=relfileid1, + parentid='./test_spam.py', ), # the secondroot TestInfo( - id=relfileid2 + 'BasicTests::test_first', + id='./w/test_eggs.py::BasicTests::test_first', name='test_first', path=TestPath( root=testroot2, - relfile=relfile2, + relfile=fix_relpath(relfile2), func='BasicTests.test_first', ), source='{}:{}'.format(relfile2, 61), markers=[], - parentid=relfileid2 + '::BasicTests', + parentid='./w/test_eggs.py::BasicTests', ), ]) self.assertEqual(parents, [ @@ -348,11 +368,12 @@ def test_multiroot(self): name=testroot1, ), ParentInfo( - id=relfileid1, + id='./test_spam.py', kind='file', - name=os.path.basename(relfile1), + name='test_spam.py', root=testroot1, - parentid=os.path.dirname(relfileid1), + relpath=fix_relpath(relfile1), + parentid='.', ), # the secondroot ParentInfo( @@ -361,25 +382,27 @@ def test_multiroot(self): name=testroot2, ), ParentInfo( - id=fix_path('./w'), + id='./w', kind='folder', name='w', root=testroot2, + relpath=fix_path('./w'), parentid='.', ), ParentInfo( - id=relfileid2, + id='./w/test_eggs.py', kind='file', - name=os.path.basename(relfile2), + name='test_eggs.py', root=testroot2, - parentid=os.path.dirname(relfileid2), + relpath=fix_relpath(relfile2), + parentid='./w', ), ParentInfo( - id=relfileid2 + '::BasicTests', + id='./w/test_eggs.py::BasicTests', kind='suite', name='BasicTests', root=testroot2, - parentid=relfileid2, + parentid='./w/test_eggs.py', ), ]) @@ -462,6 +485,9 @@ def test_doctest(self): ('.', testroot, 'folder'), ], ] + expected = [test._replace(id=_fix_nodeid(test.id), + parentid=_fix_nodeid(test.parentid)) + for test in alltests] discovered = DiscoveredTests() @@ -471,7 +497,7 @@ def test_doctest(self): parents = discovered.parents self.maxDiff = None - self.assertEqual(tests, alltests) + self.assertEqual(tests, expected) self.assertEqual(parents, [ ParentInfo( id='.', @@ -479,39 +505,44 @@ def test_doctest(self): name=testroot, ), ParentInfo( - id=fix_path('./x'), + id='./x', kind='folder', name='x', root=testroot, + relpath=fix_path('./x'), parentid='.', ), ParentInfo( - id=doctestfile, + id='./x/test_doctest.txt', kind='file', - name=os.path.basename(doctestfile), + name='test_doctest.txt', root=testroot, - parentid=os.path.dirname(doctestfile), + relpath=fix_path(doctestfile), + parentid='./x', ), ParentInfo( - id=fix_path('./x/y'), + id='./x/y', kind='folder', name='y', root=testroot, - parentid=fix_path('./x'), + relpath=fix_path('./x/y'), + parentid='./x', ), ParentInfo( - id=fix_path('./x/y/z'), + id='./x/y/z', kind='folder', name='z', root=testroot, - parentid=fix_path('./x/y'), + relpath=fix_path('./x/y/z'), + parentid='./x/y', ), ParentInfo( - id=relfile, + id='./x/y/z/test_eggs.py', kind='file', - name=os.path.basename(relfile), + name='test_eggs.py', root=testroot, - parentid=os.path.dirname(relfile), + relpath=fix_relpath(relfile), + parentid='./x/y/z', ), ]) @@ -556,6 +587,9 @@ def test_nested_suite_simple(self): ('.', testroot, 'folder'), ], ] + expected = [test._replace(id=_fix_nodeid(test.id), + parentid=_fix_nodeid(test.parentid)) + for test in alltests] discovered = DiscoveredTests() for test, parents in zip(alltests, allparents): @@ -564,7 +598,7 @@ def test_nested_suite_simple(self): parents = discovered.parents self.maxDiff = None - self.assertEqual(tests, alltests) + self.assertEqual(tests, expected) self.assertEqual(parents, [ ParentInfo( id='.', @@ -572,24 +606,25 @@ def test_nested_suite_simple(self): name=testroot, ), ParentInfo( - id=relfile, + id='./test_eggs.py', kind='file', - name=os.path.basename(relfile), + name='test_eggs.py', root=testroot, - parentid=os.path.dirname(relfile), + relpath=fix_relpath(relfile), + parentid='.' ), ParentInfo( - id=relfile + '::TestOuter', + id='./test_eggs.py::TestOuter', kind='suite', name='TestOuter', root=testroot, - parentid=relfile, + parentid='./test_eggs.py', ), ParentInfo( - id=relfile + '::TestOuter::TestInner', + id='./test_eggs.py::TestOuter::TestInner', kind='suite', name='TestInner', root=testroot, - parentid=relfile + '::TestOuter', + parentid='./test_eggs.py::TestOuter', ), ]) diff --git a/pythonFiles/tests/testing_tools/adapter/test_functional.py b/pythonFiles/tests/testing_tools/adapter/test_functional.py index 209738d7f12f..871c9d605446 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_functional.py +++ b/pythonFiles/tests/testing_tools/adapter/test_functional.py @@ -13,6 +13,7 @@ import pytest from ...__main__ import TESTING_TOOLS_ROOT +from testing_tools.adapter.util import fix_path, PATH_SEP CWD = os.getcwd() @@ -42,13 +43,10 @@ def _run_adapter(cmd, tool, *cliargs, **kwargs): kwds['stderr'] = subprocess.STDOUT argv.append('--cache-clear') print('running {!r}'.format(' '.join(arg.rpartition(CWD + '/')[-1] for arg in argv))) - return subprocess.check_output(argv, - universal_newlines=True, - **kwds) - - -def fix_path(nodeid): - return nodeid.replace('/', os.path.sep) + output = subprocess.check_output(argv, + universal_newlines=True, + **kwds) + return output def fix_test_order(tests): @@ -68,7 +66,6 @@ def fix_test_order(tests): def fix_source(tests, testid, srcfile, lineno): - testid = fix_path(testid) for test in tests: if test['id'] == testid: break @@ -79,9 +76,18 @@ def fix_source(tests, testid, srcfile, lineno): test['source'] = fix_path('{}:{}'.format(srcfile, lineno)) +# Note that these tests are skipped if util.PATH_SEP is not os.path.sep. +# This is because the functional tests should reflect the actual +# operating environment. + @pytest.mark.functional class PytestTests(unittest.TestCase): + def setUp(self): + if PATH_SEP is not os.path.sep: + raise unittest.SkipTest('functional tests require unmodified env') + super(PytestTests, self).setUp() + def complex(self, testroot): results = COMPLEX.copy() results['root'] = testroot @@ -100,23 +106,25 @@ def test_discover_simple(self): 'root': projroot, 'rootid': '.', 'parents': [ - {'id': fix_path('./tests'), + {'id': './tests', 'kind': 'folder', 'name': 'tests', + 'relpath': fix_path('./tests'), 'parentid': '.', }, - {'id': fix_path('./tests/test_spam.py'), + {'id': './tests/test_spam.py', 'kind': 'file', 'name': 'test_spam.py', - 'parentid': fix_path('./tests'), + 'relpath': fix_path('./tests/test_spam.py'), + 'parentid': './tests', }, ], 'tests': [ - {'id': fix_path('./tests/test_spam.py::test_simple'), + {'id': './tests/test_spam.py::test_simple', 'name': 'test_simple', 'source': fix_path('./tests/test_spam.py:2'), 'markers': [], - 'parentid': fix_path('./tests/test_spam.py'), + 'parentid': './tests/test_spam.py', }, ], }]) @@ -148,49 +156,51 @@ def test_discover_complex_doctest(self): expected = self.complex(projroot) # add in doctests from test suite expected[0]['parents'].insert(3, { - 'id': fix_path('./tests/test_doctest.py'), + 'id': './tests/test_doctest.py', 'kind': 'file', 'name': 'test_doctest.py', - 'parentid': fix_path('./tests'), + 'relpath': fix_path('./tests/test_doctest.py'), + 'parentid': './tests', }) expected[0]['tests'].insert(2, { - 'id': fix_path('./tests/test_doctest.py::tests.test_doctest'), + 'id': './tests/test_doctest.py::tests.test_doctest', 'name': 'tests.test_doctest', 'source': fix_path('./tests/test_doctest.py:1'), 'markers': [], - 'parentid': fix_path('./tests/test_doctest.py'), + 'parentid': './tests/test_doctest.py', }) # add in doctests from non-test module expected[0]['parents'].insert(0, { - 'id': fix_path('./mod.py'), + 'id': './mod.py', 'kind': 'file', 'name': 'mod.py', + 'relpath': fix_path('./mod.py'), 'parentid': '.', }) expected[0]['tests'] = [ - {'id': fix_path('./mod.py::mod'), + {'id': './mod.py::mod', 'name': 'mod', 'source': fix_path('./mod.py:1'), 'markers': [], - 'parentid': fix_path('./mod.py'), + 'parentid': './mod.py', }, - {'id': fix_path('./mod.py::mod.Spam'), + {'id': './mod.py::mod.Spam', 'name': 'mod.Spam', 'source': fix_path('./mod.py:33'), 'markers': [], - 'parentid': fix_path('./mod.py'), + 'parentid': './mod.py', }, - {'id': fix_path('./mod.py::mod.Spam.eggs'), + {'id': './mod.py::mod.Spam.eggs', 'name': 'mod.Spam.eggs', 'source': fix_path('./mod.py:43'), 'markers': [], - 'parentid': fix_path('./mod.py'), + 'parentid': './mod.py', }, - {'id': fix_path('./mod.py::mod.square'), + {'id': './mod.py::mod.square', 'name': 'mod.square', 'source': fix_path('./mod.py:18'), 'markers': [], - 'parentid': fix_path('./mod.py'), + 'parentid': './mod.py', }, ] + expected[0]['tests'] expected[0]['tests'] = fix_test_order(expected[0]['tests']) @@ -265,43 +275,47 @@ def test_discover_normcase(self): self.maxDiff = None self.assertTrue(projroot.endswith('NormCase')) - fix_id = os.path.normcase self.assertEqual(result, [{ 'root': projroot, 'rootid': '.', 'parents': [ - {'id': fix_id('./tests'), + {'id': './tests', 'kind': 'folder', 'name': 'tests', + 'relpath': fix_path('./tests'), 'parentid': '.', }, - {'id': fix_id('./tests/A'), + {'id': './tests/A', 'kind': 'folder', - 'name': fix_id('A'), - 'parentid': fix_id('./tests'), + 'name': 'A', + 'relpath': fix_path('./tests/A'), + 'parentid': './tests', }, - {'id': fix_id('./tests/A/b'), + {'id': './tests/A/b', 'kind': 'folder', 'name': 'b', - 'parentid': fix_id('./tests/A'), + 'relpath': fix_path('./tests/A/b'), + 'parentid': './tests/A', }, - {'id': fix_id('./tests/A/b/C'), + {'id': './tests/A/b/C', 'kind': 'folder', - 'name': fix_id('C'), - 'parentid': fix_id('./tests/A/b'), + 'name': 'C', + 'relpath': fix_path('./tests/A/b/C'), + 'parentid': './tests/A/b', }, - {'id': fix_id('./tests/A/b/C/test_Spam.py'), + {'id': './tests/A/b/C/test_Spam.py', 'kind': 'file', - 'name': fix_id('test_Spam.py'), - 'parentid': fix_id('./tests/A/b/C'), + 'name': 'test_Spam.py', + 'relpath': fix_path('./tests/A/b/C/test_Spam.py'), + 'parentid': './tests/A/b/C', }, ], 'tests': [ - {'id': fix_id('./tests/A/b/C/test_Spam.py::test_okay'), + {'id': './tests/A/b/C/test_Spam.py::test_okay', 'name': 'test_okay', 'source': fix_path('./tests/A/b/C/test_Spam.py:2'), 'markers': [], - 'parentid': fix_id('./tests/A/b/C/test_Spam.py'), + 'parentid': './tests/A/b/C/test_Spam.py', }, ], }]) @@ -312,982 +326,1006 @@ def test_discover_normcase(self): 'rootid': '.', 'parents': [ # - {'id': fix_path('./tests'), + {'id': './tests', 'kind': 'folder', 'name': 'tests', + 'relpath': fix_path('./tests'), 'parentid': '.', }, # +++ - {'id': fix_path('./tests/test_42-43.py'), + {'id': './tests/test_42-43.py', 'kind': 'file', 'name': 'test_42-43.py', - 'parentid': fix_path('./tests'), + 'relpath': fix_path('./tests/test_42-43.py'), + 'parentid': './tests', }, # +++ - {'id': fix_path('./tests/test_42.py'), + {'id': './tests/test_42.py', 'kind': 'file', 'name': 'test_42.py', - 'parentid': fix_path('./tests'), + 'relpath': fix_path('./tests/test_42.py'), + 'parentid': './tests', }, # +++ - {'id': fix_path('./tests/test_doctest.txt'), + {'id': './tests/test_doctest.txt', 'kind': 'file', 'name': 'test_doctest.txt', - 'parentid': fix_path('./tests'), + 'relpath': fix_path('./tests/test_doctest.txt'), + 'parentid': './tests', }, # +++ - {'id': fix_path('./tests/test_foo.py'), + {'id': './tests/test_foo.py', 'kind': 'file', 'name': 'test_foo.py', - 'parentid': fix_path('./tests'), + 'relpath': fix_path('./tests/test_foo.py'), + 'parentid': './tests', }, # +++ - {'id': fix_path('./tests/test_mixed.py'), + {'id': './tests/test_mixed.py', 'kind': 'file', 'name': 'test_mixed.py', - 'parentid': fix_path('./tests'), + 'relpath': fix_path('./tests/test_mixed.py'), + 'parentid': './tests', }, - {'id': fix_path('./tests/test_mixed.py::MyTests'), + {'id': './tests/test_mixed.py::MyTests', 'kind': 'suite', 'name': 'MyTests', - 'parentid': fix_path('./tests/test_mixed.py'), + 'parentid': './tests/test_mixed.py', }, - {'id': fix_path('./tests/test_mixed.py::TestMySuite'), + {'id': './tests/test_mixed.py::TestMySuite', 'kind': 'suite', 'name': 'TestMySuite', - 'parentid': fix_path('./tests/test_mixed.py'), + 'parentid': './tests/test_mixed.py', }, # +++ - {'id': fix_path('./tests/test_pytest.py'), + {'id': './tests/test_pytest.py', 'kind': 'file', 'name': 'test_pytest.py', - 'parentid': fix_path('./tests'), + 'relpath': fix_path('./tests/test_pytest.py'), + 'parentid': './tests', }, - {'id': fix_path('./tests/test_pytest.py::TestEggs'), + {'id': './tests/test_pytest.py::TestEggs', 'kind': 'suite', 'name': 'TestEggs', - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::TestParam'), + {'id': './tests/test_pytest.py::TestParam', 'kind': 'suite', 'name': 'TestParam', - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::TestParam::test_param_13'), + {'id': './tests/test_pytest.py::TestParam::test_param_13', 'kind': 'function', 'name': 'test_param_13', - 'parentid': fix_path('./tests/test_pytest.py::TestParam'), + 'parentid': './tests/test_pytest.py::TestParam', }, - {'id': fix_path('./tests/test_pytest.py::TestParamAll'), + {'id': './tests/test_pytest.py::TestParamAll', 'kind': 'suite', 'name': 'TestParamAll', - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::TestParamAll::test_param_13'), + {'id': './tests/test_pytest.py::TestParamAll::test_param_13', 'kind': 'function', 'name': 'test_param_13', - 'parentid': fix_path('./tests/test_pytest.py::TestParamAll'), + 'parentid': './tests/test_pytest.py::TestParamAll', }, - {'id': fix_path('./tests/test_pytest.py::TestParamAll::test_spam_13'), + {'id': './tests/test_pytest.py::TestParamAll::test_spam_13', 'kind': 'function', 'name': 'test_spam_13', - 'parentid': fix_path('./tests/test_pytest.py::TestParamAll'), + 'parentid': './tests/test_pytest.py::TestParamAll', }, - {'id': fix_path('./tests/test_pytest.py::TestSpam'), + {'id': './tests/test_pytest.py::TestSpam', 'kind': 'suite', 'name': 'TestSpam', - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::TestSpam::TestHam'), + {'id': './tests/test_pytest.py::TestSpam::TestHam', 'kind': 'suite', 'name': 'TestHam', - 'parentid': fix_path('./tests/test_pytest.py::TestSpam'), + 'parentid': './tests/test_pytest.py::TestSpam', }, - {'id': fix_path('./tests/test_pytest.py::TestSpam::TestHam::TestEggs'), + {'id': './tests/test_pytest.py::TestSpam::TestHam::TestEggs', 'kind': 'suite', 'name': 'TestEggs', - 'parentid': fix_path('./tests/test_pytest.py::TestSpam::TestHam'), + 'parentid': './tests/test_pytest.py::TestSpam::TestHam', }, - {'id': fix_path('./tests/test_pytest.py::test_fixture_param'), + {'id': './tests/test_pytest.py::test_fixture_param', 'kind': 'function', 'name': 'test_fixture_param', - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_param_01'), + {'id': './tests/test_pytest.py::test_param_01', 'kind': 'function', 'name': 'test_param_01', - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_param_11'), + {'id': './tests/test_pytest.py::test_param_11', 'kind': 'function', 'name': 'test_param_11', - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_param_13'), + {'id': './tests/test_pytest.py::test_param_13', 'kind': 'function', 'name': 'test_param_13', - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_param_13_markers'), + {'id': './tests/test_pytest.py::test_param_13_markers', 'kind': 'function', 'name': 'test_param_13_markers', - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_param_13_repeat'), + {'id': './tests/test_pytest.py::test_param_13_repeat', 'kind': 'function', 'name': 'test_param_13_repeat', - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_param_13_skipped'), + {'id': './tests/test_pytest.py::test_param_13_skipped', 'kind': 'function', 'name': 'test_param_13_skipped', - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_param_23_13'), + {'id': './tests/test_pytest.py::test_param_23_13', 'kind': 'function', 'name': 'test_param_23_13', - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_param_23_raises'), + {'id': './tests/test_pytest.py::test_param_23_raises', 'kind': 'function', 'name': 'test_param_23_raises', - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_param_33'), + {'id': './tests/test_pytest.py::test_param_33', 'kind': 'function', 'name': 'test_param_33', - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_param_33_ids'), + {'id': './tests/test_pytest.py::test_param_33_ids', 'kind': 'function', 'name': 'test_param_33_ids', - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_param_fixture'), + {'id': './tests/test_pytest.py::test_param_fixture', 'kind': 'function', 'name': 'test_param_fixture', - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_param_mark_fixture'), + {'id': './tests/test_pytest.py::test_param_mark_fixture', 'kind': 'function', 'name': 'test_param_mark_fixture', - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, # +++ - {'id': fix_path('./tests/test_pytest_param.py'), + {'id': './tests/test_pytest_param.py', 'kind': 'file', 'name': 'test_pytest_param.py', - 'parentid': fix_path('./tests'), + 'relpath': fix_path('./tests/test_pytest_param.py'), + 'parentid': './tests', }, - {'id': fix_path('./tests/test_pytest_param.py::TestParamAll'), + {'id': './tests/test_pytest_param.py::TestParamAll', 'kind': 'suite', 'name': 'TestParamAll', - 'parentid': fix_path('./tests/test_pytest_param.py'), + 'parentid': './tests/test_pytest_param.py', }, - {'id': fix_path('./tests/test_pytest_param.py::TestParamAll::test_param_13'), + {'id': './tests/test_pytest_param.py::TestParamAll::test_param_13', 'kind': 'function', 'name': 'test_param_13', - 'parentid': fix_path('./tests/test_pytest_param.py::TestParamAll'), + 'parentid': './tests/test_pytest_param.py::TestParamAll', }, - {'id': fix_path('./tests/test_pytest_param.py::TestParamAll::test_spam_13'), + {'id': './tests/test_pytest_param.py::TestParamAll::test_spam_13', 'kind': 'function', 'name': 'test_spam_13', - 'parentid': fix_path('./tests/test_pytest_param.py::TestParamAll'), + 'parentid': './tests/test_pytest_param.py::TestParamAll', }, - {'id': fix_path('./tests/test_pytest_param.py::test_param_13'), + {'id': './tests/test_pytest_param.py::test_param_13', 'kind': 'function', 'name': 'test_param_13', - 'parentid': fix_path('./tests/test_pytest_param.py'), + 'parentid': './tests/test_pytest_param.py', }, # +++ - {'id': fix_path('./tests/test_unittest.py'), + {'id': './tests/test_unittest.py', 'kind': 'file', 'name': 'test_unittest.py', - 'parentid': fix_path('./tests'), + 'relpath': fix_path('./tests/test_unittest.py'), + 'parentid': './tests', }, - {'id': fix_path('./tests/test_unittest.py::MyTests'), + {'id': './tests/test_unittest.py::MyTests', 'kind': 'suite', 'name': 'MyTests', - 'parentid': fix_path('./tests/test_unittest.py'), + 'parentid': './tests/test_unittest.py', }, - {'id': fix_path('./tests/test_unittest.py::OtherTests'), + {'id': './tests/test_unittest.py::OtherTests', 'kind': 'suite', 'name': 'OtherTests', - 'parentid': fix_path('./tests/test_unittest.py'), + 'parentid': './tests/test_unittest.py', }, ## - {'id': fix_path('./tests/v'), + {'id': './tests/v', 'kind': 'folder', 'name': 'v', - 'parentid': fix_path('./tests'), + 'relpath': fix_path('./tests/v'), + 'parentid': './tests', }, ## +++ - {'id': fix_path('./tests/v/test_eggs.py'), + {'id': './tests/v/test_eggs.py', 'kind': 'file', 'name': 'test_eggs.py', - 'parentid': fix_path('./tests/v'), + 'relpath': fix_path('./tests/v/test_eggs.py'), + 'parentid': './tests/v', }, - {'id': fix_path('./tests/v/test_eggs.py::TestSimple'), + {'id': './tests/v/test_eggs.py::TestSimple', 'kind': 'suite', 'name': 'TestSimple', - 'parentid': fix_path('./tests/v/test_eggs.py'), + 'parentid': './tests/v/test_eggs.py', }, ## +++ - {'id': fix_path('./tests/v/test_ham.py'), + {'id': './tests/v/test_ham.py', 'kind': 'file', 'name': 'test_ham.py', - 'parentid': fix_path('./tests/v'), + 'relpath': fix_path('./tests/v/test_ham.py'), + 'parentid': './tests/v', }, ## +++ - {'id': fix_path('./tests/v/test_spam.py'), + {'id': './tests/v/test_spam.py', 'kind': 'file', 'name': 'test_spam.py', - 'parentid': fix_path('./tests/v'), + 'relpath': fix_path('./tests/v/test_spam.py'), + 'parentid': './tests/v', }, ## - {'id': fix_path('./tests/w'), + {'id': './tests/w', 'kind': 'folder', 'name': 'w', - 'parentid': fix_path('./tests'), + 'relpath': fix_path('./tests/w'), + 'parentid': './tests', }, ## +++ - {'id': fix_path('./tests/w/test_spam.py'), + {'id': './tests/w/test_spam.py', 'kind': 'file', 'name': 'test_spam.py', - 'parentid': fix_path('./tests/w'), + 'relpath': fix_path('./tests/w/test_spam.py'), + 'parentid': './tests/w', }, ## +++ - {'id': fix_path('./tests/w/test_spam_ex.py'), + {'id': './tests/w/test_spam_ex.py', 'kind': 'file', 'name': 'test_spam_ex.py', - 'parentid': fix_path('./tests/w'), + 'relpath': fix_path('./tests/w/test_spam_ex.py'), + 'parentid': './tests/w', }, ## - {'id': fix_path('./tests/x'), + {'id': './tests/x', 'kind': 'folder', 'name': 'x', - 'parentid': fix_path('./tests'), + 'relpath': fix_path('./tests/x'), + 'parentid': './tests', }, ### - {'id': fix_path('./tests/x/y'), + {'id': './tests/x/y', 'kind': 'folder', 'name': 'y', - 'parentid': fix_path('./tests/x'), + 'relpath': fix_path('./tests/x/y'), + 'parentid': './tests/x', }, #### - {'id': fix_path('./tests/x/y/z'), + {'id': './tests/x/y/z', 'kind': 'folder', 'name': 'z', - 'parentid': fix_path('./tests/x/y'), + 'relpath': fix_path('./tests/x/y/z'), + 'parentid': './tests/x/y', }, ##### - {'id': fix_path('./tests/x/y/z/a'), + {'id': './tests/x/y/z/a', 'kind': 'folder', 'name': 'a', - 'parentid': fix_path('./tests/x/y/z'), + 'relpath': fix_path('./tests/x/y/z/a'), + 'parentid': './tests/x/y/z', }, ##### +++ - {'id': fix_path('./tests/x/y/z/a/test_spam.py'), + {'id': './tests/x/y/z/a/test_spam.py', 'kind': 'file', 'name': 'test_spam.py', - 'parentid': fix_path('./tests/x/y/z/a'), + 'relpath': fix_path('./tests/x/y/z/a/test_spam.py'), + 'parentid': './tests/x/y/z/a', }, ##### - {'id': fix_path('./tests/x/y/z/b'), + {'id': './tests/x/y/z/b', 'kind': 'folder', 'name': 'b', - 'parentid': fix_path('./tests/x/y/z'), + 'relpath': fix_path('./tests/x/y/z/b'), + 'parentid': './tests/x/y/z', }, ##### +++ - {'id': fix_path('./tests/x/y/z/b/test_spam.py'), + {'id': './tests/x/y/z/b/test_spam.py', 'kind': 'file', 'name': 'test_spam.py', - 'parentid': fix_path('./tests/x/y/z/b'), + 'relpath': fix_path('./tests/x/y/z/b/test_spam.py'), + 'parentid': './tests/x/y/z/b', }, #### +++ - {'id': fix_path('./tests/x/y/z/test_ham.py'), + {'id': './tests/x/y/z/test_ham.py', 'kind': 'file', 'name': 'test_ham.py', - 'parentid': fix_path('./tests/x/y/z'), + 'relpath': fix_path('./tests/x/y/z/test_ham.py'), + 'parentid': './tests/x/y/z', }, ], 'tests': [ ########## - {'id': fix_path('./tests/test_42-43.py::test_simple'), + {'id': './tests/test_42-43.py::test_simple', 'name': 'test_simple', 'source': fix_path('./tests/test_42-43.py:2'), 'markers': [], - 'parentid': fix_path('./tests/test_42-43.py'), + 'parentid': './tests/test_42-43.py', }, ##### - {'id': fix_path('./tests/test_42.py::test_simple'), + {'id': './tests/test_42.py::test_simple', 'name': 'test_simple', 'source': fix_path('./tests/test_42.py:2'), 'markers': [], - 'parentid': fix_path('./tests/test_42.py'), + 'parentid': './tests/test_42.py', }, ##### - {'id': fix_path('./tests/test_doctest.txt::test_doctest.txt'), + {'id': './tests/test_doctest.txt::test_doctest.txt', 'name': 'test_doctest.txt', 'source': fix_path('./tests/test_doctest.txt:1'), 'markers': [], - 'parentid': fix_path('./tests/test_doctest.txt'), + 'parentid': './tests/test_doctest.txt', }, ##### - {'id': fix_path('./tests/test_foo.py::test_simple'), + {'id': './tests/test_foo.py::test_simple', 'name': 'test_simple', 'source': fix_path('./tests/test_foo.py:3'), 'markers': [], - 'parentid': fix_path('./tests/test_foo.py'), + 'parentid': './tests/test_foo.py', }, ##### - {'id': fix_path('./tests/test_mixed.py::test_top_level'), + {'id': './tests/test_mixed.py::test_top_level', 'name': 'test_top_level', 'source': fix_path('./tests/test_mixed.py:5'), 'markers': [], - 'parentid': fix_path('./tests/test_mixed.py'), + 'parentid': './tests/test_mixed.py', }, - {'id': fix_path('./tests/test_mixed.py::test_skipped'), + {'id': './tests/test_mixed.py::test_skipped', 'name': 'test_skipped', 'source': fix_path('./tests/test_mixed.py:9'), 'markers': ['skip'], - 'parentid': fix_path('./tests/test_mixed.py'), + 'parentid': './tests/test_mixed.py', }, - {'id': fix_path('./tests/test_mixed.py::TestMySuite::test_simple'), + {'id': './tests/test_mixed.py::TestMySuite::test_simple', 'name': 'test_simple', 'source': fix_path('./tests/test_mixed.py:16'), 'markers': [], - 'parentid': fix_path('./tests/test_mixed.py::TestMySuite'), + 'parentid': './tests/test_mixed.py::TestMySuite', }, - {'id': fix_path('./tests/test_mixed.py::MyTests::test_simple'), + {'id': './tests/test_mixed.py::MyTests::test_simple', 'name': 'test_simple', 'source': fix_path('./tests/test_mixed.py:22'), 'markers': [], - 'parentid': fix_path('./tests/test_mixed.py::MyTests'), + 'parentid': './tests/test_mixed.py::MyTests', }, - {'id': fix_path('./tests/test_mixed.py::MyTests::test_skipped'), + {'id': './tests/test_mixed.py::MyTests::test_skipped', 'name': 'test_skipped', 'source': fix_path('./tests/test_mixed.py:25'), 'markers': ['skip'], - 'parentid': fix_path('./tests/test_mixed.py::MyTests'), + 'parentid': './tests/test_mixed.py::MyTests', }, ##### - {'id': fix_path('./tests/test_pytest.py::test_simple'), + {'id': './tests/test_pytest.py::test_simple', 'name': 'test_simple', 'source': fix_path('./tests/test_pytest.py:6'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_failure'), + {'id': './tests/test_pytest.py::test_failure', 'name': 'test_failure', 'source': fix_path('./tests/test_pytest.py:10'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_runtime_skipped'), + {'id': './tests/test_pytest.py::test_runtime_skipped', 'name': 'test_runtime_skipped', 'source': fix_path('./tests/test_pytest.py:14'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_runtime_failed'), + {'id': './tests/test_pytest.py::test_runtime_failed', 'name': 'test_runtime_failed', 'source': fix_path('./tests/test_pytest.py:18'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_raises'), + {'id': './tests/test_pytest.py::test_raises', 'name': 'test_raises', 'source': fix_path('./tests/test_pytest.py:22'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_skipped'), + {'id': './tests/test_pytest.py::test_skipped', 'name': 'test_skipped', 'source': fix_path('./tests/test_pytest.py:26'), 'markers': ['skip'], - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_maybe_skipped'), + {'id': './tests/test_pytest.py::test_maybe_skipped', 'name': 'test_maybe_skipped', 'source': fix_path('./tests/test_pytest.py:31'), 'markers': ['skip-if'], - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_known_failure'), + {'id': './tests/test_pytest.py::test_known_failure', 'name': 'test_known_failure', 'source': fix_path('./tests/test_pytest.py:36'), 'markers': ['expected-failure'], - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_warned'), + {'id': './tests/test_pytest.py::test_warned', 'name': 'test_warned', 'source': fix_path('./tests/test_pytest.py:41'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_custom_marker'), + {'id': './tests/test_pytest.py::test_custom_marker', 'name': 'test_custom_marker', 'source': fix_path('./tests/test_pytest.py:46'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_multiple_markers'), + {'id': './tests/test_pytest.py::test_multiple_markers', 'name': 'test_multiple_markers', 'source': fix_path('./tests/test_pytest.py:51'), 'markers': ['expected-failure', 'skip', 'skip-if'], - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_dynamic_1'), + {'id': './tests/test_pytest.py::test_dynamic_1', 'name': 'test_dynamic_1', 'source': fix_path('./tests/test_pytest.py:62'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_dynamic_2'), + {'id': './tests/test_pytest.py::test_dynamic_2', 'name': 'test_dynamic_2', 'source': fix_path('./tests/test_pytest.py:62'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_dynamic_3'), + {'id': './tests/test_pytest.py::test_dynamic_3', 'name': 'test_dynamic_3', 'source': fix_path('./tests/test_pytest.py:62'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::TestSpam::test_simple'), + {'id': './tests/test_pytest.py::TestSpam::test_simple', 'name': 'test_simple', 'source': fix_path('./tests/test_pytest.py:70'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::TestSpam'), + 'parentid': './tests/test_pytest.py::TestSpam', }, - {'id': fix_path('./tests/test_pytest.py::TestSpam::test_skipped'), + {'id': './tests/test_pytest.py::TestSpam::test_skipped', 'name': 'test_skipped', 'source': fix_path('./tests/test_pytest.py:73'), 'markers': ['skip'], - 'parentid': fix_path('./tests/test_pytest.py::TestSpam'), + 'parentid': './tests/test_pytest.py::TestSpam', }, - {'id': fix_path('./tests/test_pytest.py::TestSpam::TestHam::TestEggs::test_simple'), + {'id': './tests/test_pytest.py::TestSpam::TestHam::TestEggs::test_simple', 'name': 'test_simple', 'source': fix_path('./tests/test_pytest.py:81'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::TestSpam::TestHam::TestEggs'), + 'parentid': './tests/test_pytest.py::TestSpam::TestHam::TestEggs', }, - {'id': fix_path('./tests/test_pytest.py::TestEggs::test_simple'), + {'id': './tests/test_pytest.py::TestEggs::test_simple', 'name': 'test_simple', 'source': fix_path('./tests/test_pytest.py:93'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::TestEggs'), + 'parentid': './tests/test_pytest.py::TestEggs', }, - {'id': fix_path('./tests/test_pytest.py::test_param_01[]'), + {'id': './tests/test_pytest.py::test_param_01[]', 'name': 'test_param_01[]', 'source': fix_path('./tests/test_pytest.py:103'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_01'), + 'parentid': './tests/test_pytest.py::test_param_01', }, - {'id': fix_path('./tests/test_pytest.py::test_param_11[x0]'), + {'id': './tests/test_pytest.py::test_param_11[x0]', 'name': 'test_param_11[x0]', 'source': fix_path('./tests/test_pytest.py:108'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_11'), + 'parentid': './tests/test_pytest.py::test_param_11', }, - {'id': fix_path('./tests/test_pytest.py::test_param_13[x0]'), + {'id': './tests/test_pytest.py::test_param_13[x0]', 'name': 'test_param_13[x0]', 'source': fix_path('./tests/test_pytest.py:113'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_13'), + 'parentid': './tests/test_pytest.py::test_param_13', }, - {'id': fix_path('./tests/test_pytest.py::test_param_13[x1]'), + {'id': './tests/test_pytest.py::test_param_13[x1]', 'name': 'test_param_13[x1]', 'source': fix_path('./tests/test_pytest.py:113'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_13'), + 'parentid': './tests/test_pytest.py::test_param_13', }, - {'id': fix_path('./tests/test_pytest.py::test_param_13[x2]'), + {'id': './tests/test_pytest.py::test_param_13[x2]', 'name': 'test_param_13[x2]', 'source': fix_path('./tests/test_pytest.py:113'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_13'), + 'parentid': './tests/test_pytest.py::test_param_13', }, - {'id': fix_path('./tests/test_pytest.py::test_param_13_repeat[x0]'), + {'id': './tests/test_pytest.py::test_param_13_repeat[x0]', 'name': 'test_param_13_repeat[x0]', 'source': fix_path('./tests/test_pytest.py:118'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_13_repeat'), + 'parentid': './tests/test_pytest.py::test_param_13_repeat', }, - {'id': fix_path('./tests/test_pytest.py::test_param_13_repeat[x1]'), + {'id': './tests/test_pytest.py::test_param_13_repeat[x1]', 'name': 'test_param_13_repeat[x1]', 'source': fix_path('./tests/test_pytest.py:118'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_13_repeat'), + 'parentid': './tests/test_pytest.py::test_param_13_repeat', }, - {'id': fix_path('./tests/test_pytest.py::test_param_13_repeat[x2]'), + {'id': './tests/test_pytest.py::test_param_13_repeat[x2]', 'name': 'test_param_13_repeat[x2]', 'source': fix_path('./tests/test_pytest.py:118'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_13_repeat'), + 'parentid': './tests/test_pytest.py::test_param_13_repeat', }, - {'id': fix_path('./tests/test_pytest.py::test_param_33[1-1-1]'), + {'id': './tests/test_pytest.py::test_param_33[1-1-1]', 'name': 'test_param_33[1-1-1]', 'source': fix_path('./tests/test_pytest.py:123'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_33'), + 'parentid': './tests/test_pytest.py::test_param_33', }, - {'id': fix_path('./tests/test_pytest.py::test_param_33[3-4-5]'), + {'id': './tests/test_pytest.py::test_param_33[3-4-5]', 'name': 'test_param_33[3-4-5]', 'source': fix_path('./tests/test_pytest.py:123'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_33'), + 'parentid': './tests/test_pytest.py::test_param_33', }, - {'id': fix_path('./tests/test_pytest.py::test_param_33[0-0-0]'), + {'id': './tests/test_pytest.py::test_param_33[0-0-0]', 'name': 'test_param_33[0-0-0]', 'source': fix_path('./tests/test_pytest.py:123'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_33'), + 'parentid': './tests/test_pytest.py::test_param_33', }, - {'id': fix_path('./tests/test_pytest.py::test_param_33_ids[v1]'), + {'id': './tests/test_pytest.py::test_param_33_ids[v1]', 'name': 'test_param_33_ids[v1]', 'source': fix_path('./tests/test_pytest.py:128'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_33_ids'), + 'parentid': './tests/test_pytest.py::test_param_33_ids', }, - {'id': fix_path('./tests/test_pytest.py::test_param_33_ids[v2]'), + {'id': './tests/test_pytest.py::test_param_33_ids[v2]', 'name': 'test_param_33_ids[v2]', 'source': fix_path('./tests/test_pytest.py:128'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_33_ids'), + 'parentid': './tests/test_pytest.py::test_param_33_ids', }, - {'id': fix_path('./tests/test_pytest.py::test_param_33_ids[v3]'), + {'id': './tests/test_pytest.py::test_param_33_ids[v3]', 'name': 'test_param_33_ids[v3]', 'source': fix_path('./tests/test_pytest.py:128'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_33_ids'), + 'parentid': './tests/test_pytest.py::test_param_33_ids', }, - {'id': fix_path('./tests/test_pytest.py::test_param_23_13[1-1-z0]'), + {'id': './tests/test_pytest.py::test_param_23_13[1-1-z0]', 'name': 'test_param_23_13[1-1-z0]', 'source': fix_path('./tests/test_pytest.py:134'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_23_13'), + 'parentid': './tests/test_pytest.py::test_param_23_13', }, - {'id': fix_path('./tests/test_pytest.py::test_param_23_13[1-1-z1]'), + {'id': './tests/test_pytest.py::test_param_23_13[1-1-z1]', 'name': 'test_param_23_13[1-1-z1]', 'source': fix_path('./tests/test_pytest.py:134'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_23_13'), + 'parentid': './tests/test_pytest.py::test_param_23_13', }, - {'id': fix_path('./tests/test_pytest.py::test_param_23_13[1-1-z2]'), + {'id': './tests/test_pytest.py::test_param_23_13[1-1-z2]', 'name': 'test_param_23_13[1-1-z2]', 'source': fix_path('./tests/test_pytest.py:134'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_23_13'), + 'parentid': './tests/test_pytest.py::test_param_23_13', }, - {'id': fix_path('./tests/test_pytest.py::test_param_23_13[3-4-z0]'), + {'id': './tests/test_pytest.py::test_param_23_13[3-4-z0]', 'name': 'test_param_23_13[3-4-z0]', 'source': fix_path('./tests/test_pytest.py:134'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_23_13'), + 'parentid': './tests/test_pytest.py::test_param_23_13', }, - {'id': fix_path('./tests/test_pytest.py::test_param_23_13[3-4-z1]'), + {'id': './tests/test_pytest.py::test_param_23_13[3-4-z1]', 'name': 'test_param_23_13[3-4-z1]', 'source': fix_path('./tests/test_pytest.py:134'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_23_13'), + 'parentid': './tests/test_pytest.py::test_param_23_13', }, - {'id': fix_path('./tests/test_pytest.py::test_param_23_13[3-4-z2]'), + {'id': './tests/test_pytest.py::test_param_23_13[3-4-z2]', 'name': 'test_param_23_13[3-4-z2]', 'source': fix_path('./tests/test_pytest.py:134'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_23_13'), + 'parentid': './tests/test_pytest.py::test_param_23_13', }, - {'id': fix_path('./tests/test_pytest.py::test_param_23_13[0-0-z0]'), + {'id': './tests/test_pytest.py::test_param_23_13[0-0-z0]', 'name': 'test_param_23_13[0-0-z0]', 'source': fix_path('./tests/test_pytest.py:134'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_23_13'), + 'parentid': './tests/test_pytest.py::test_param_23_13', }, - {'id': fix_path('./tests/test_pytest.py::test_param_23_13[0-0-z1]'), + {'id': './tests/test_pytest.py::test_param_23_13[0-0-z1]', 'name': 'test_param_23_13[0-0-z1]', 'source': fix_path('./tests/test_pytest.py:134'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_23_13'), + 'parentid': './tests/test_pytest.py::test_param_23_13', }, - {'id': fix_path('./tests/test_pytest.py::test_param_23_13[0-0-z2]'), + {'id': './tests/test_pytest.py::test_param_23_13[0-0-z2]', 'name': 'test_param_23_13[0-0-z2]', 'source': fix_path('./tests/test_pytest.py:134'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_23_13'), + 'parentid': './tests/test_pytest.py::test_param_23_13', }, - {'id': fix_path('./tests/test_pytest.py::test_param_13_markers[x0]'), + {'id': './tests/test_pytest.py::test_param_13_markers[x0]', 'name': 'test_param_13_markers[x0]', 'source': fix_path('./tests/test_pytest.py:140'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_13_markers'), + 'parentid': './tests/test_pytest.py::test_param_13_markers', }, - {'id': fix_path('./tests/test_pytest.py::test_param_13_markers[???]'), + {'id': './tests/test_pytest.py::test_param_13_markers[???]', 'name': 'test_param_13_markers[???]', 'source': fix_path('./tests/test_pytest.py:140'), 'markers': ['skip'], - 'parentid': fix_path('./tests/test_pytest.py::test_param_13_markers'), + 'parentid': './tests/test_pytest.py::test_param_13_markers', }, - {'id': fix_path('./tests/test_pytest.py::test_param_13_markers[2]'), + {'id': './tests/test_pytest.py::test_param_13_markers[2]', 'name': 'test_param_13_markers[2]', 'source': fix_path('./tests/test_pytest.py:140'), 'markers': ['expected-failure'], - 'parentid': fix_path('./tests/test_pytest.py::test_param_13_markers'), + 'parentid': './tests/test_pytest.py::test_param_13_markers', }, - {'id': fix_path('./tests/test_pytest.py::test_param_13_skipped[x0]'), + {'id': './tests/test_pytest.py::test_param_13_skipped[x0]', 'name': 'test_param_13_skipped[x0]', 'source': fix_path('./tests/test_pytest.py:149'), 'markers': ['skip'], - 'parentid': fix_path('./tests/test_pytest.py::test_param_13_skipped'), + 'parentid': './tests/test_pytest.py::test_param_13_skipped', }, - {'id': fix_path('./tests/test_pytest.py::test_param_13_skipped[x1]'), + {'id': './tests/test_pytest.py::test_param_13_skipped[x1]', 'name': 'test_param_13_skipped[x1]', 'source': fix_path('./tests/test_pytest.py:149'), 'markers': ['skip'], - 'parentid': fix_path('./tests/test_pytest.py::test_param_13_skipped'), + 'parentid': './tests/test_pytest.py::test_param_13_skipped', }, - {'id': fix_path('./tests/test_pytest.py::test_param_13_skipped[x2]'), + {'id': './tests/test_pytest.py::test_param_13_skipped[x2]', 'name': 'test_param_13_skipped[x2]', 'source': fix_path('./tests/test_pytest.py:149'), 'markers': ['skip'], - 'parentid': fix_path('./tests/test_pytest.py::test_param_13_skipped'), + 'parentid': './tests/test_pytest.py::test_param_13_skipped', }, - {'id': fix_path('./tests/test_pytest.py::test_param_23_raises[1-None]'), + {'id': './tests/test_pytest.py::test_param_23_raises[1-None]', 'name': 'test_param_23_raises[1-None]', 'source': fix_path('./tests/test_pytest.py:155'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_23_raises'), + 'parentid': './tests/test_pytest.py::test_param_23_raises', }, - {'id': fix_path('./tests/test_pytest.py::test_param_23_raises[1.0-None]'), + {'id': './tests/test_pytest.py::test_param_23_raises[1.0-None]', 'name': 'test_param_23_raises[1.0-None]', 'source': fix_path('./tests/test_pytest.py:155'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_23_raises'), + 'parentid': './tests/test_pytest.py::test_param_23_raises', }, - {'id': fix_path('./tests/test_pytest.py::test_param_23_raises[2-catch2]'), + {'id': './tests/test_pytest.py::test_param_23_raises[2-catch2]', 'name': 'test_param_23_raises[2-catch2]', 'source': fix_path('./tests/test_pytest.py:155'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_23_raises'), + 'parentid': './tests/test_pytest.py::test_param_23_raises', }, - {'id': fix_path('./tests/test_pytest.py::TestParam::test_simple'), + {'id': './tests/test_pytest.py::TestParam::test_simple', 'name': 'test_simple', 'source': fix_path('./tests/test_pytest.py:164'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::TestParam'), + 'parentid': './tests/test_pytest.py::TestParam', }, - {'id': fix_path('./tests/test_pytest.py::TestParam::test_param_13[x0]'), + {'id': './tests/test_pytest.py::TestParam::test_param_13[x0]', 'name': 'test_param_13[x0]', 'source': fix_path('./tests/test_pytest.py:167'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::TestParam::test_param_13'), + 'parentid': './tests/test_pytest.py::TestParam::test_param_13', }, - {'id': fix_path('./tests/test_pytest.py::TestParam::test_param_13[x1]'), + {'id': './tests/test_pytest.py::TestParam::test_param_13[x1]', 'name': 'test_param_13[x1]', 'source': fix_path('./tests/test_pytest.py:167'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::TestParam::test_param_13'), + 'parentid': './tests/test_pytest.py::TestParam::test_param_13', }, - {'id': fix_path('./tests/test_pytest.py::TestParam::test_param_13[x2]'), + {'id': './tests/test_pytest.py::TestParam::test_param_13[x2]', 'name': 'test_param_13[x2]', 'source': fix_path('./tests/test_pytest.py:167'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::TestParam::test_param_13'), + 'parentid': './tests/test_pytest.py::TestParam::test_param_13', }, - {'id': fix_path('./tests/test_pytest.py::TestParamAll::test_param_13[x0]'), + {'id': './tests/test_pytest.py::TestParamAll::test_param_13[x0]', 'name': 'test_param_13[x0]', 'source': fix_path('./tests/test_pytest.py:175'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::TestParamAll::test_param_13'), + 'parentid': './tests/test_pytest.py::TestParamAll::test_param_13', }, - {'id': fix_path('./tests/test_pytest.py::TestParamAll::test_param_13[x1]'), + {'id': './tests/test_pytest.py::TestParamAll::test_param_13[x1]', 'name': 'test_param_13[x1]', 'source': fix_path('./tests/test_pytest.py:175'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::TestParamAll::test_param_13'), + 'parentid': './tests/test_pytest.py::TestParamAll::test_param_13', }, - {'id': fix_path('./tests/test_pytest.py::TestParamAll::test_param_13[x2]'), + {'id': './tests/test_pytest.py::TestParamAll::test_param_13[x2]', 'name': 'test_param_13[x2]', 'source': fix_path('./tests/test_pytest.py:175'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::TestParamAll::test_param_13'), + 'parentid': './tests/test_pytest.py::TestParamAll::test_param_13', }, - {'id': fix_path('./tests/test_pytest.py::TestParamAll::test_spam_13[x0]'), + {'id': './tests/test_pytest.py::TestParamAll::test_spam_13[x0]', 'name': 'test_spam_13[x0]', 'source': fix_path('./tests/test_pytest.py:178'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::TestParamAll::test_spam_13'), + 'parentid': './tests/test_pytest.py::TestParamAll::test_spam_13', }, - {'id': fix_path('./tests/test_pytest.py::TestParamAll::test_spam_13[x1]'), + {'id': './tests/test_pytest.py::TestParamAll::test_spam_13[x1]', 'name': 'test_spam_13[x1]', 'source': fix_path('./tests/test_pytest.py:178'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::TestParamAll::test_spam_13'), + 'parentid': './tests/test_pytest.py::TestParamAll::test_spam_13', }, - {'id': fix_path('./tests/test_pytest.py::TestParamAll::test_spam_13[x2]'), + {'id': './tests/test_pytest.py::TestParamAll::test_spam_13[x2]', 'name': 'test_spam_13[x2]', 'source': fix_path('./tests/test_pytest.py:178'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::TestParamAll::test_spam_13'), + 'parentid': './tests/test_pytest.py::TestParamAll::test_spam_13', }, - {'id': fix_path('./tests/test_pytest.py::test_fixture'), + {'id': './tests/test_pytest.py::test_fixture', 'name': 'test_fixture', 'source': fix_path('./tests/test_pytest.py:192'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_mark_fixture'), + {'id': './tests/test_pytest.py::test_mark_fixture', 'name': 'test_mark_fixture', 'source': fix_path('./tests/test_pytest.py:196'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py'), + 'parentid': './tests/test_pytest.py', }, - {'id': fix_path('./tests/test_pytest.py::test_param_fixture[x0]'), + {'id': './tests/test_pytest.py::test_param_fixture[x0]', 'name': 'test_param_fixture[x0]', 'source': fix_path('./tests/test_pytest.py:201'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_fixture'), + 'parentid': './tests/test_pytest.py::test_param_fixture', }, - {'id': fix_path('./tests/test_pytest.py::test_param_fixture[x1]'), + {'id': './tests/test_pytest.py::test_param_fixture[x1]', 'name': 'test_param_fixture[x1]', 'source': fix_path('./tests/test_pytest.py:201'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_fixture'), + 'parentid': './tests/test_pytest.py::test_param_fixture', }, - {'id': fix_path('./tests/test_pytest.py::test_param_fixture[x2]'), + {'id': './tests/test_pytest.py::test_param_fixture[x2]', 'name': 'test_param_fixture[x2]', 'source': fix_path('./tests/test_pytest.py:201'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_fixture'), + 'parentid': './tests/test_pytest.py::test_param_fixture', }, - {'id': fix_path('./tests/test_pytest.py::test_param_mark_fixture[x0]'), + {'id': './tests/test_pytest.py::test_param_mark_fixture[x0]', 'name': 'test_param_mark_fixture[x0]', 'source': fix_path('./tests/test_pytest.py:207'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_mark_fixture'), + 'parentid': './tests/test_pytest.py::test_param_mark_fixture', }, - {'id': fix_path('./tests/test_pytest.py::test_param_mark_fixture[x1]'), + {'id': './tests/test_pytest.py::test_param_mark_fixture[x1]', 'name': 'test_param_mark_fixture[x1]', 'source': fix_path('./tests/test_pytest.py:207'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_mark_fixture'), + 'parentid': './tests/test_pytest.py::test_param_mark_fixture', }, - {'id': fix_path('./tests/test_pytest.py::test_param_mark_fixture[x2]'), + {'id': './tests/test_pytest.py::test_param_mark_fixture[x2]', 'name': 'test_param_mark_fixture[x2]', 'source': fix_path('./tests/test_pytest.py:207'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_param_mark_fixture'), + 'parentid': './tests/test_pytest.py::test_param_mark_fixture', }, - {'id': fix_path('./tests/test_pytest.py::test_fixture_param[spam]'), + {'id': './tests/test_pytest.py::test_fixture_param[spam]', 'name': 'test_fixture_param[spam]', 'source': fix_path('./tests/test_pytest.py:216'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_fixture_param'), + 'parentid': './tests/test_pytest.py::test_fixture_param', }, - {'id': fix_path('./tests/test_pytest.py::test_fixture_param[eggs]'), + {'id': './tests/test_pytest.py::test_fixture_param[eggs]', 'name': 'test_fixture_param[eggs]', 'source': fix_path('./tests/test_pytest.py:216'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest.py::test_fixture_param'), + 'parentid': './tests/test_pytest.py::test_fixture_param', }, ###### - {'id': fix_path('./tests/test_pytest_param.py::test_param_13[x0]'), + {'id': './tests/test_pytest_param.py::test_param_13[x0]', 'name': 'test_param_13[x0]', 'source': fix_path('./tests/test_pytest_param.py:8'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest_param.py::test_param_13'), + 'parentid': './tests/test_pytest_param.py::test_param_13', }, - {'id': fix_path('./tests/test_pytest_param.py::test_param_13[x1]'), + {'id': './tests/test_pytest_param.py::test_param_13[x1]', 'name': 'test_param_13[x1]', 'source': fix_path('./tests/test_pytest_param.py:8'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest_param.py::test_param_13'), + 'parentid': './tests/test_pytest_param.py::test_param_13', }, - {'id': fix_path('./tests/test_pytest_param.py::test_param_13[x2]'), + {'id': './tests/test_pytest_param.py::test_param_13[x2]', 'name': 'test_param_13[x2]', 'source': fix_path('./tests/test_pytest_param.py:8'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest_param.py::test_param_13'), + 'parentid': './tests/test_pytest_param.py::test_param_13', }, - {'id': fix_path('./tests/test_pytest_param.py::TestParamAll::test_param_13[x0]'), + {'id': './tests/test_pytest_param.py::TestParamAll::test_param_13[x0]', 'name': 'test_param_13[x0]', 'source': fix_path('./tests/test_pytest_param.py:14'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest_param.py::TestParamAll::test_param_13'), + 'parentid': './tests/test_pytest_param.py::TestParamAll::test_param_13', }, - {'id': fix_path('./tests/test_pytest_param.py::TestParamAll::test_param_13[x1]'), + {'id': './tests/test_pytest_param.py::TestParamAll::test_param_13[x1]', 'name': 'test_param_13[x1]', 'source': fix_path('./tests/test_pytest_param.py:14'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest_param.py::TestParamAll::test_param_13'), + 'parentid': './tests/test_pytest_param.py::TestParamAll::test_param_13', }, - {'id': fix_path('./tests/test_pytest_param.py::TestParamAll::test_param_13[x2]'), + {'id': './tests/test_pytest_param.py::TestParamAll::test_param_13[x2]', 'name': 'test_param_13[x2]', 'source': fix_path('./tests/test_pytest_param.py:14'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest_param.py::TestParamAll::test_param_13'), + 'parentid': './tests/test_pytest_param.py::TestParamAll::test_param_13', }, - {'id': fix_path('./tests/test_pytest_param.py::TestParamAll::test_spam_13[x0]'), + {'id': './tests/test_pytest_param.py::TestParamAll::test_spam_13[x0]', 'name': 'test_spam_13[x0]', 'source': fix_path('./tests/test_pytest_param.py:17'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest_param.py::TestParamAll::test_spam_13'), + 'parentid': './tests/test_pytest_param.py::TestParamAll::test_spam_13', }, - {'id': fix_path('./tests/test_pytest_param.py::TestParamAll::test_spam_13[x1]'), + {'id': './tests/test_pytest_param.py::TestParamAll::test_spam_13[x1]', 'name': 'test_spam_13[x1]', 'source': fix_path('./tests/test_pytest_param.py:17'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest_param.py::TestParamAll::test_spam_13'), + 'parentid': './tests/test_pytest_param.py::TestParamAll::test_spam_13', }, - {'id': fix_path('./tests/test_pytest_param.py::TestParamAll::test_spam_13[x2]'), + {'id': './tests/test_pytest_param.py::TestParamAll::test_spam_13[x2]', 'name': 'test_spam_13[x2]', 'source': fix_path('./tests/test_pytest_param.py:17'), 'markers': [], - 'parentid': fix_path('./tests/test_pytest_param.py::TestParamAll::test_spam_13'), + 'parentid': './tests/test_pytest_param.py::TestParamAll::test_spam_13', }, ###### - {'id': fix_path('./tests/test_unittest.py::MyTests::test_dynamic_'), + {'id': './tests/test_unittest.py::MyTests::test_dynamic_', 'name': 'test_dynamic_', 'source': fix_path('./tests/test_unittest.py:54'), 'markers': [], - 'parentid': fix_path('./tests/test_unittest.py::MyTests'), + 'parentid': './tests/test_unittest.py::MyTests', }, - {'id': fix_path('./tests/test_unittest.py::MyTests::test_failure'), + {'id': './tests/test_unittest.py::MyTests::test_failure', 'name': 'test_failure', 'source': fix_path('./tests/test_unittest.py:34'), 'markers': [], - 'parentid': fix_path('./tests/test_unittest.py::MyTests'), + 'parentid': './tests/test_unittest.py::MyTests', }, - {'id': fix_path('./tests/test_unittest.py::MyTests::test_known_failure'), + {'id': './tests/test_unittest.py::MyTests::test_known_failure', 'name': 'test_known_failure', 'source': fix_path('./tests/test_unittest.py:37'), 'markers': [], - 'parentid': fix_path('./tests/test_unittest.py::MyTests'), + 'parentid': './tests/test_unittest.py::MyTests', }, - {'id': fix_path('./tests/test_unittest.py::MyTests::test_maybe_not_skipped'), + {'id': './tests/test_unittest.py::MyTests::test_maybe_not_skipped', 'name': 'test_maybe_not_skipped', 'source': fix_path('./tests/test_unittest.py:17'), 'markers': [], - 'parentid': fix_path('./tests/test_unittest.py::MyTests'), + 'parentid': './tests/test_unittest.py::MyTests', }, - {'id': fix_path('./tests/test_unittest.py::MyTests::test_maybe_skipped'), + {'id': './tests/test_unittest.py::MyTests::test_maybe_skipped', 'name': 'test_maybe_skipped', 'source': fix_path('./tests/test_unittest.py:13'), 'markers': [], - 'parentid': fix_path('./tests/test_unittest.py::MyTests'), + 'parentid': './tests/test_unittest.py::MyTests', }, - {'id': fix_path('./tests/test_unittest.py::MyTests::test_simple'), + {'id': './tests/test_unittest.py::MyTests::test_simple', 'name': 'test_simple', 'source': fix_path('./tests/test_unittest.py:6'), 'markers': [], - 'parentid': fix_path('./tests/test_unittest.py::MyTests'), + 'parentid': './tests/test_unittest.py::MyTests', }, - {'id': fix_path('./tests/test_unittest.py::MyTests::test_skipped'), + {'id': './tests/test_unittest.py::MyTests::test_skipped', 'name': 'test_skipped', 'source': fix_path('./tests/test_unittest.py:9'), 'markers': [], - 'parentid': fix_path('./tests/test_unittest.py::MyTests'), + 'parentid': './tests/test_unittest.py::MyTests', }, - {'id': fix_path('./tests/test_unittest.py::MyTests::test_skipped_inside'), + {'id': './tests/test_unittest.py::MyTests::test_skipped_inside', 'name': 'test_skipped_inside', 'source': fix_path('./tests/test_unittest.py:21'), 'markers': [], - 'parentid': fix_path('./tests/test_unittest.py::MyTests'), + 'parentid': './tests/test_unittest.py::MyTests', }, - {'id': fix_path('./tests/test_unittest.py::MyTests::test_with_nested_subtests'), + {'id': './tests/test_unittest.py::MyTests::test_with_nested_subtests', 'name': 'test_with_nested_subtests', 'source': fix_path('./tests/test_unittest.py:46'), 'markers': [], - 'parentid': fix_path('./tests/test_unittest.py::MyTests'), + 'parentid': './tests/test_unittest.py::MyTests', }, - {'id': fix_path('./tests/test_unittest.py::MyTests::test_with_subtests'), + {'id': './tests/test_unittest.py::MyTests::test_with_subtests', 'name': 'test_with_subtests', 'source': fix_path('./tests/test_unittest.py:41'), 'markers': [], - 'parentid': fix_path('./tests/test_unittest.py::MyTests'), + 'parentid': './tests/test_unittest.py::MyTests', }, - {'id': fix_path('./tests/test_unittest.py::OtherTests::test_simple'), + {'id': './tests/test_unittest.py::OtherTests::test_simple', 'name': 'test_simple', 'source': fix_path('./tests/test_unittest.py:61'), 'markers': [], - 'parentid': fix_path('./tests/test_unittest.py::OtherTests'), + 'parentid': './tests/test_unittest.py::OtherTests', }, ########### - {'id': fix_path('./tests/v/test_eggs.py::test_simple'), + {'id': './tests/v/test_eggs.py::test_simple', 'name': 'test_simple', 'source': fix_path('./tests/v/spam.py:2'), 'markers': [], - 'parentid': fix_path('./tests/v/test_eggs.py'), + 'parentid': './tests/v/test_eggs.py', }, - {'id': fix_path('./tests/v/test_eggs.py::TestSimple::test_simple'), + {'id': './tests/v/test_eggs.py::TestSimple::test_simple', 'name': 'test_simple', 'source': fix_path('./tests/v/spam.py:8'), 'markers': [], - 'parentid': fix_path('./tests/v/test_eggs.py::TestSimple'), + 'parentid': './tests/v/test_eggs.py::TestSimple', }, ###### - {'id': fix_path('./tests/v/test_ham.py::test_simple'), + {'id': './tests/v/test_ham.py::test_simple', 'name': 'test_simple', 'source': fix_path('./tests/v/spam.py:2'), 'markers': [], - 'parentid': fix_path('./tests/v/test_ham.py'), + 'parentid': './tests/v/test_ham.py', }, - {'id': fix_path('./tests/v/test_ham.py::test_not_hard'), + {'id': './tests/v/test_ham.py::test_not_hard', 'name': 'test_not_hard', 'source': fix_path('./tests/v/spam.py:2'), 'markers': [], - 'parentid': fix_path('./tests/v/test_ham.py'), + 'parentid': './tests/v/test_ham.py', }, ###### - {'id': fix_path('./tests/v/test_spam.py::test_simple'), + {'id': './tests/v/test_spam.py::test_simple', 'name': 'test_simple', 'source': fix_path('./tests/v/spam.py:2'), 'markers': [], - 'parentid': fix_path('./tests/v/test_spam.py'), + 'parentid': './tests/v/test_spam.py', }, - {'id': fix_path('./tests/v/test_spam.py::test_simpler'), + {'id': './tests/v/test_spam.py::test_simpler', 'name': 'test_simpler', 'source': fix_path('./tests/v/test_spam.py:4'), 'markers': [], - 'parentid': fix_path('./tests/v/test_spam.py'), + 'parentid': './tests/v/test_spam.py', }, ########### - {'id': fix_path('./tests/w/test_spam.py::test_simple'), + {'id': './tests/w/test_spam.py::test_simple', 'name': 'test_simple', 'source': fix_path('./tests/w/test_spam.py:4'), 'markers': [], - 'parentid': fix_path('./tests/w/test_spam.py'), + 'parentid': './tests/w/test_spam.py', }, - {'id': fix_path('./tests/w/test_spam_ex.py::test_simple'), + {'id': './tests/w/test_spam_ex.py::test_simple', 'name': 'test_simple', 'source': fix_path('./tests/w/test_spam_ex.py:4'), 'markers': [], - 'parentid': fix_path('./tests/w/test_spam_ex.py'), + 'parentid': './tests/w/test_spam_ex.py', }, ########### - {'id': fix_path('./tests/x/y/z/test_ham.py::test_simple'), + {'id': './tests/x/y/z/test_ham.py::test_simple', 'name': 'test_simple', 'source': fix_path('./tests/x/y/z/test_ham.py:2'), 'markers': [], - 'parentid': fix_path('./tests/x/y/z/test_ham.py'), + 'parentid': './tests/x/y/z/test_ham.py', }, ###### - {'id': fix_path('./tests/x/y/z/a/test_spam.py::test_simple'), + {'id': './tests/x/y/z/a/test_spam.py::test_simple', 'name': 'test_simple', 'source': fix_path('./tests/x/y/z/a/test_spam.py:11'), 'markers': [], - 'parentid': fix_path('./tests/x/y/z/a/test_spam.py'), + 'parentid': './tests/x/y/z/a/test_spam.py', }, - {'id': fix_path('./tests/x/y/z/b/test_spam.py::test_simple'), + {'id': './tests/x/y/z/b/test_spam.py::test_simple', 'name': 'test_simple', 'source': fix_path('./tests/x/y/z/b/test_spam.py:7'), 'markers': [], - 'parentid': fix_path('./tests/x/y/z/b/test_spam.py'), + 'parentid': './tests/x/y/z/b/test_spam.py', }, ], } diff --git a/pythonFiles/tests/testing_tools/adapter/test_report.py b/pythonFiles/tests/testing_tools/adapter/test_report.py index 4628c0719729..b3ecbad425b0 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_report.py +++ b/pythonFiles/tests/testing_tools/adapter/test_report.py @@ -2,10 +2,10 @@ # Licensed under the MIT License. import json -import os.path import unittest from ...util import StubProxy +from testing_tools.adapter.util import fix_path, fix_relpath from testing_tools.adapter.info import TestInfo, TestPath, ParentInfo from testing_tools.adapter.report import report_discovered @@ -23,8 +23,9 @@ class ReportDiscoveredTests(unittest.TestCase): def test_basic(self): stub = StubSender() - testroot = '/a/b/c'.replace('/', os.path.sep) + testroot = fix_path('/a/b/c') relfile = 'test_spam.py' + relpath = fix_relpath(relfile) tests = [ TestInfo( id='test#1', @@ -50,6 +51,7 @@ def test_basic(self): kind='file', name=relfile, root=testroot, + relpath=relpath, parentid='', ), ] @@ -60,6 +62,7 @@ def test_basic(self): {'id': 'file#1', 'kind': 'file', 'name': relfile, + 'relpath': relpath, 'parentid': '', }, ], @@ -82,9 +85,10 @@ def test_basic(self): def test_multiroot(self): stub = StubSender() # the first root - testroot1 = '/a/b/c'.replace('/', os.path.sep) - relfile1 = 'test_spam.py' - relfileid1 = os.path.join('.', relfile1) + testroot1 = fix_path('/a/b/c') + relfileid1 = './test_spam.py' + relpath1 = fix_path(relfileid1) + relfile1 = relpath1[2:] tests = [ TestInfo( id=relfileid1 + '::test_spam', @@ -108,9 +112,10 @@ def test_multiroot(self): ParentInfo( id=relfileid1, kind='file', - name=os.path.basename(relfile1), + name='test_spam.py', root=testroot1, - parentid=os.path.dirname(relfileid1), + relpath=relpath1, + parentid='.', ), ] expected = [ @@ -119,7 +124,8 @@ def test_multiroot(self): 'parents': [ {'id': relfileid1, 'kind': 'file', - 'name': relfile1, + 'name': 'test_spam.py', + 'relpath': relpath1, 'parentid': '.', }, ], @@ -133,9 +139,10 @@ def test_multiroot(self): }, ] # the second root - testroot2 = '/x/y/z'.replace('/', os.path.sep) - relfile2 = 'w/test_eggs.py' - relfileid2 = os.path.join('.', relfile2) + testroot2 = fix_path('/x/y/z') + relfileid2 = './w/test_eggs.py' + relpath2 = fix_path(relfileid2) + relfile2 = relpath2[2:] tests.extend([ TestInfo( id=relfileid2 + '::BasicTests::test_first', @@ -157,18 +164,20 @@ def test_multiroot(self): name=testroot2, ), ParentInfo( - id='./w'.replace('/', os.path.sep), + id='./w', kind='folder', name='w', root=testroot2, + relpath=fix_path('./w'), parentid='.', ), ParentInfo( id=relfileid2, kind='file', - name=os.path.basename(relfile2), + name='test_eggs.py', root=testroot2, - parentid=os.path.dirname(relfileid2), + relpath=relpath2, + parentid='./w', ), ParentInfo( id=relfileid2 + '::BasicTests', @@ -182,15 +191,17 @@ def test_multiroot(self): {'rootid': '.', 'root': testroot2, 'parents': [ - {'id': os.path.dirname(relfileid2), + {'id': './w', 'kind': 'folder', 'name': 'w', + 'relpath': fix_path('./w'), 'parentid': '.', }, {'id': relfileid2, 'kind': 'file', - 'name': os.path.basename(relfile2), - 'parentid': os.path.dirname(relfileid2), + 'name': 'test_eggs.py', + 'relpath': relpath2, + 'parentid': './w', }, {'id': relfileid2 + '::BasicTests', 'kind': 'suite', @@ -252,147 +263,147 @@ def test_complex(self): test_okay """ stub = StubSender() - testroot = '/a/b/c'.replace('/', os.path.sep) - relfile1 = './test_ham.py'.replace('/', os.path.sep) - relfile2 = './test_spam.py'.replace('/', os.path.sep) - relfile3 = './w/test_ham.py'.replace('/', os.path.sep) - relfile4 = './w/test_eggs.py'.replace('/', os.path.sep) - relfile5 = './x/y/a/test_spam.py'.replace('/', os.path.sep) - relfile6 = './x/y/b/test_spam.py'.replace('/', os.path.sep) + testroot = fix_path('/a/b/c') + relfileid1 = './test_ham.py' + relfileid2 = './test_spam.py' + relfileid3 = './w/test_ham.py' + relfileid4 = './w/test_eggs.py' + relfileid5 = './x/y/a/test_spam.py' + relfileid6 = './x/y/b/test_spam.py' tests = [ TestInfo( - id=relfile1 + '::MySuite::test_x1', + id=relfileid1 + '::MySuite::test_x1', name='test_x1', path=TestPath( root=testroot, - relfile=relfile1, + relfile=fix_path(relfileid1), func='MySuite.test_x1', ), - source='{}:{}'.format(relfile1, 10), + source='{}:{}'.format(fix_path(relfileid1), 10), markers=None, - parentid=relfile1 + '::MySuite', + parentid=relfileid1 + '::MySuite', ), TestInfo( - id=relfile1 + '::MySuite::test_x2', + id=relfileid1 + '::MySuite::test_x2', name='test_x2', path=TestPath( root=testroot, - relfile=relfile1, + relfile=fix_path(relfileid1), func='MySuite.test_x2', ), - source='{}:{}'.format(relfile1, 21), + source='{}:{}'.format(fix_path(relfileid1), 21), markers=None, - parentid=relfile1 + '::MySuite', + parentid=relfileid1 + '::MySuite', ), TestInfo( - id=relfile2 + '::SpamTests::test_okay', + id=relfileid2 + '::SpamTests::test_okay', name='test_okay', path=TestPath( root=testroot, - relfile=relfile2, + relfile=fix_path(relfileid2), func='SpamTests.test_okay', ), - source='{}:{}'.format(relfile2, 17), + source='{}:{}'.format(fix_path(relfileid2), 17), markers=None, - parentid=relfile2 + '::SpamTests', + parentid=relfileid2 + '::SpamTests', ), TestInfo( - id=relfile3 + '::test_ham1', + id=relfileid3 + '::test_ham1', name='test_ham1', path=TestPath( root=testroot, - relfile=relfile3, + relfile=fix_path(relfileid3), func='test_ham1', ), - source='{}:{}'.format(relfile3, 8), + source='{}:{}'.format(fix_path(relfileid3), 8), markers=None, - parentid=relfile3, + parentid=relfileid3, ), TestInfo( - id=relfile3 + '::HamTests::test_uh_oh', + id=relfileid3 + '::HamTests::test_uh_oh', name='test_uh_oh', path=TestPath( root=testroot, - relfile=relfile3, + relfile=fix_path(relfileid3), func='HamTests.test_uh_oh', ), - source='{}:{}'.format(relfile3, 19), + source='{}:{}'.format(fix_path(relfileid3), 19), markers=['expected-failure'], - parentid=relfile3 + '::HamTests', + parentid=relfileid3 + '::HamTests', ), TestInfo( - id=relfile3 + '::HamTests::test_whoa', + id=relfileid3 + '::HamTests::test_whoa', name='test_whoa', path=TestPath( root=testroot, - relfile=relfile3, + relfile=fix_path(relfileid3), func='HamTests.test_whoa', ), - source='{}:{}'.format(relfile3, 35), + source='{}:{}'.format(fix_path(relfileid3), 35), markers=None, - parentid=relfile3 + '::HamTests', + parentid=relfileid3 + '::HamTests', ), TestInfo( - id=relfile3 + '::MoreHam::test_yay[1-2]', + id=relfileid3 + '::MoreHam::test_yay[1-2]', name='test_yay[1-2]', path=TestPath( root=testroot, - relfile=relfile3, + relfile=fix_path(relfileid3), func='MoreHam.test_yay', sub=['[1-2]'], ), - source='{}:{}'.format(relfile3, 57), + source='{}:{}'.format(fix_path(relfileid3), 57), markers=None, - parentid=relfile3 + '::MoreHam::test_yay', + parentid=relfileid3 + '::MoreHam::test_yay', ), TestInfo( - id=relfile3 + '::MoreHam::test_yay[1-2][3-4]', + id=relfileid3 + '::MoreHam::test_yay[1-2][3-4]', name='test_yay[1-2][3-4]', path=TestPath( root=testroot, - relfile=relfile3, + relfile=fix_path(relfileid3), func='MoreHam.test_yay', sub=['[1-2]', '[3=4]'], ), - source='{}:{}'.format(relfile3, 72), + source='{}:{}'.format(fix_path(relfileid3), 72), markers=None, - parentid=relfile3 + '::MoreHam::test_yay[1-2]', + parentid=relfileid3 + '::MoreHam::test_yay[1-2]', ), TestInfo( - id=relfile4 + '::SpamTests::test_okay', + id=relfileid4 + '::SpamTests::test_okay', name='test_okay', path=TestPath( root=testroot, - relfile=relfile4, + relfile=fix_path(relfileid4), func='SpamTests.test_okay', ), - source='{}:{}'.format(relfile4, 15), + source='{}:{}'.format(fix_path(relfileid4), 15), markers=None, - parentid=relfile4 + '::SpamTests', + parentid=relfileid4 + '::SpamTests', ), TestInfo( - id=relfile5 + '::SpamTests::test_okay', + id=relfileid5 + '::SpamTests::test_okay', name='test_okay', path=TestPath( root=testroot, - relfile=relfile5, + relfile=fix_path(relfileid5), func='SpamTests.test_okay', ), - source='{}:{}'.format(relfile5, 12), + source='{}:{}'.format(fix_path(relfileid5), 12), markers=None, - parentid=relfile5 + '::SpamTests', + parentid=relfileid5 + '::SpamTests', ), TestInfo( - id=relfile6 + '::SpamTests::test_okay', + id=relfileid6 + '::SpamTests::test_okay', name='test_okay', path=TestPath( root=testroot, - relfile=relfile6, + relfile=fix_path(relfileid6), func='SpamTests.test_okay', ), - source='{}:{}'.format(relfile6, 27), + source='{}:{}'.format(fix_path(relfileid6), 27), markers=None, - parentid=relfile6 + '::SpamTests', + parentid=relfileid6 + '::SpamTests', ), ] parents = [ @@ -403,327 +414,349 @@ def test_complex(self): ), ParentInfo( - id=relfile1, + id=relfileid1, kind='file', - name=os.path.basename(relfile1), + name='test_ham.py', root=testroot, + relpath=fix_path(relfileid1), parentid='.', ), ParentInfo( - id=relfile1 + '::MySuite', + id=relfileid1 + '::MySuite', kind='suite', name='MySuite', root=testroot, - parentid=relfile1, + parentid=relfileid1, ), ParentInfo( - id=relfile2, + id=relfileid2, kind='file', - name=os.path.basename(relfile2), + name='test_spam.py', root=testroot, + relpath=fix_path(relfileid2), parentid='.', ), ParentInfo( - id=relfile2 + '::SpamTests', + id=relfileid2 + '::SpamTests', kind='suite', name='SpamTests', root=testroot, - parentid=relfile2, + parentid=relfileid2, ), ParentInfo( - id='./w'.replace('/', os.path.sep), + id='./w', kind='folder', name='w', root=testroot, + relpath=fix_path('./w'), parentid='.', ), ParentInfo( - id=relfile3, + id=relfileid3, kind='file', - name=os.path.basename(relfile3), + name='test_ham.py', root=testroot, - parentid=os.path.dirname(relfile3), + relpath=fix_path(relfileid3), + parentid='./w', ), ParentInfo( - id=relfile3 + '::HamTests', + id=relfileid3 + '::HamTests', kind='suite', name='HamTests', root=testroot, - parentid=relfile3, + parentid=relfileid3, ), ParentInfo( - id=relfile3 + '::MoreHam', + id=relfileid3 + '::MoreHam', kind='suite', name='MoreHam', root=testroot, - parentid=relfile3, + parentid=relfileid3, ), ParentInfo( - id=relfile3 + '::MoreHam::test_yay', + id=relfileid3 + '::MoreHam::test_yay', kind='function', name='test_yay', root=testroot, - parentid=relfile3 + '::MoreHam', + parentid=relfileid3 + '::MoreHam', ), ParentInfo( - id=relfile3 + '::MoreHam::test_yay[1-2]', + id=relfileid3 + '::MoreHam::test_yay[1-2]', kind='subtest', name='test_yay[1-2]', root=testroot, - parentid=relfile3 + '::MoreHam::test_yay', + parentid=relfileid3 + '::MoreHam::test_yay', ), ParentInfo( - id=relfile4, + id=relfileid4, kind='file', - name=os.path.basename(relfile4), + name='test_eggs.py', root=testroot, - parentid=os.path.dirname(relfile4), + relpath=fix_path(relfileid4), + parentid='./w', ), ParentInfo( - id=relfile4 + '::SpamTests', + id=relfileid4 + '::SpamTests', kind='suite', name='SpamTests', root=testroot, - parentid=relfile4, + parentid=relfileid4, ), ParentInfo( - id='./x'.replace('/', os.path.sep), + id='./x', kind='folder', name='x', root=testroot, + relpath=fix_path('./x'), parentid='.', ), ParentInfo( - id='./x/y'.replace('/', os.path.sep), + id='./x/y', kind='folder', name='y', root=testroot, - parentid='./x'.replace('/', os.path.sep), + relpath=fix_path('./x/y'), + parentid='./x', ), ParentInfo( - id='./x/y/a'.replace('/', os.path.sep), + id='./x/y/a', kind='folder', name='a', root=testroot, - parentid='./x/y'.replace('/', os.path.sep), + relpath=fix_path('./x/y/a'), + parentid='./x/y', ), ParentInfo( - id=relfile5, + id=relfileid5, kind='file', - name=os.path.basename(relfile5), + name='test_spam.py', root=testroot, - parentid=os.path.dirname(relfile5), + relpath=fix_path(relfileid5), + parentid='./x/y/a', ), ParentInfo( - id=relfile5 + '::SpamTests', + id=relfileid5 + '::SpamTests', kind='suite', name='SpamTests', root=testroot, - parentid=relfile5, + parentid=relfileid5, ), ParentInfo( - id='./x/y/b'.replace('/', os.path.sep), + id='./x/y/b', kind='folder', name='b', root=testroot, - parentid='./x/y'.replace('/', os.path.sep), + relpath=fix_path('./x/y/b'), + parentid='./x/y', ), ParentInfo( - id=relfile6, + id=relfileid6, kind='file', - name=os.path.basename(relfile6), + name='test_spam.py', root=testroot, - parentid=os.path.dirname(relfile6), + relpath=fix_path(relfileid6), + parentid='./x/y/b', ), ParentInfo( - id=relfile6 + '::SpamTests', + id=relfileid6 + '::SpamTests', kind='suite', name='SpamTests', root=testroot, - parentid=relfile6, + parentid=relfileid6, ), ] expected = [{ 'rootid': '.', 'root': testroot, 'parents': [ - {'id': relfile1, + {'id': relfileid1, 'kind': 'file', - 'name': os.path.basename(relfile1), + 'name': 'test_ham.py', + 'relpath': fix_path(relfileid1), 'parentid': '.', }, - {'id': relfile1 + '::MySuite', + {'id': relfileid1 + '::MySuite', 'kind': 'suite', 'name': 'MySuite', - 'parentid': relfile1, + 'parentid': relfileid1, }, - {'id': relfile2, + {'id': relfileid2, 'kind': 'file', - 'name': os.path.basename(relfile2), + 'name': 'test_spam.py', + 'relpath': fix_path(relfileid2), 'parentid': '.', }, - {'id': relfile2 + '::SpamTests', + {'id': relfileid2 + '::SpamTests', 'kind': 'suite', 'name': 'SpamTests', - 'parentid': relfile2, + 'parentid': relfileid2, }, - {'id': './w'.replace('/', os.path.sep), + {'id': './w', 'kind': 'folder', 'name': 'w', + 'relpath': fix_path('./w'), 'parentid': '.', }, - {'id': relfile3, + {'id': relfileid3, 'kind': 'file', - 'name': os.path.basename(relfile3), - 'parentid': os.path.dirname(relfile3), + 'name': 'test_ham.py', + 'relpath': fix_path(relfileid3), + 'parentid': './w', }, - {'id': relfile3 + '::HamTests', + {'id': relfileid3 + '::HamTests', 'kind': 'suite', 'name': 'HamTests', - 'parentid': relfile3, + 'parentid': relfileid3, }, - {'id': relfile3 + '::MoreHam', + {'id': relfileid3 + '::MoreHam', 'kind': 'suite', 'name': 'MoreHam', - 'parentid': relfile3, + 'parentid': relfileid3, }, - {'id': relfile3 + '::MoreHam::test_yay', + {'id': relfileid3 + '::MoreHam::test_yay', 'kind': 'function', 'name': 'test_yay', - 'parentid': relfile3 + '::MoreHam', + 'parentid': relfileid3 + '::MoreHam', }, - {'id': relfile3 + '::MoreHam::test_yay[1-2]', + {'id': relfileid3 + '::MoreHam::test_yay[1-2]', 'kind': 'subtest', 'name': 'test_yay[1-2]', - 'parentid': relfile3 + '::MoreHam::test_yay', + 'parentid': relfileid3 + '::MoreHam::test_yay', }, - {'id': relfile4, + {'id': relfileid4, 'kind': 'file', - 'name': os.path.basename(relfile4), - 'parentid': os.path.dirname(relfile4), + 'name': 'test_eggs.py', + 'relpath': fix_path(relfileid4), + 'parentid': './w', }, - {'id': relfile4 + '::SpamTests', + {'id': relfileid4 + '::SpamTests', 'kind': 'suite', 'name': 'SpamTests', - 'parentid': relfile4, + 'parentid': relfileid4, }, - {'id': './x'.replace('/', os.path.sep), + {'id': './x', 'kind': 'folder', 'name': 'x', + 'relpath': fix_path('./x'), 'parentid': '.', }, - {'id': './x/y'.replace('/', os.path.sep), + {'id': './x/y', 'kind': 'folder', 'name': 'y', - 'parentid': './x'.replace('/', os.path.sep), + 'relpath': fix_path('./x/y'), + 'parentid': './x', }, - {'id': './x/y/a'.replace('/', os.path.sep), + {'id': './x/y/a', 'kind': 'folder', 'name': 'a', - 'parentid': './x/y'.replace('/', os.path.sep), + 'relpath': fix_path('./x/y/a'), + 'parentid': './x/y', }, - {'id': relfile5, + {'id': relfileid5, 'kind': 'file', - 'name': os.path.basename(relfile5), - 'parentid': os.path.dirname(relfile5), + 'name': 'test_spam.py', + 'relpath': fix_path(relfileid5), + 'parentid': './x/y/a', }, - {'id': relfile5 + '::SpamTests', + {'id': relfileid5 + '::SpamTests', 'kind': 'suite', 'name': 'SpamTests', - 'parentid': relfile5, + 'parentid': relfileid5, }, - {'id': './x/y/b'.replace('/', os.path.sep), + {'id': './x/y/b', 'kind': 'folder', 'name': 'b', - 'parentid': './x/y'.replace('/', os.path.sep), + 'relpath': fix_path('./x/y/b'), + 'parentid': './x/y', }, - {'id': relfile6, + {'id': relfileid6, 'kind': 'file', - 'name': os.path.basename(relfile6), - 'parentid': os.path.dirname(relfile6), + 'name': 'test_spam.py', + 'relpath': fix_path(relfileid6), + 'parentid': './x/y/b', }, - {'id': relfile6 + '::SpamTests', + {'id': relfileid6 + '::SpamTests', 'kind': 'suite', 'name': 'SpamTests', - 'parentid': relfile6, + 'parentid': relfileid6, }, ], 'tests': [ - {'id': relfile1 + '::MySuite::test_x1', + {'id': relfileid1 + '::MySuite::test_x1', 'name': 'test_x1', - 'source': '{}:{}'.format(relfile1, 10), + 'source': '{}:{}'.format(fix_path(relfileid1), 10), 'markers': [], - 'parentid': relfile1 + '::MySuite', + 'parentid': relfileid1 + '::MySuite', }, - {'id': relfile1 + '::MySuite::test_x2', + {'id': relfileid1 + '::MySuite::test_x2', 'name': 'test_x2', - 'source': '{}:{}'.format(relfile1, 21), + 'source': '{}:{}'.format(fix_path(relfileid1), 21), 'markers': [], - 'parentid': relfile1 + '::MySuite', + 'parentid': relfileid1 + '::MySuite', }, - {'id': relfile2 + '::SpamTests::test_okay', + {'id': relfileid2 + '::SpamTests::test_okay', 'name': 'test_okay', - 'source': '{}:{}'.format(relfile2, 17), + 'source': '{}:{}'.format(fix_path(relfileid2), 17), 'markers': [], - 'parentid': relfile2 + '::SpamTests', + 'parentid': relfileid2 + '::SpamTests', }, - {'id': relfile3 + '::test_ham1', + {'id': relfileid3 + '::test_ham1', 'name': 'test_ham1', - 'source': '{}:{}'.format(relfile3, 8), + 'source': '{}:{}'.format(fix_path(relfileid3), 8), 'markers': [], - 'parentid': relfile3, + 'parentid': relfileid3, }, - {'id': relfile3 + '::HamTests::test_uh_oh', + {'id': relfileid3 + '::HamTests::test_uh_oh', 'name': 'test_uh_oh', - 'source': '{}:{}'.format(relfile3, 19), + 'source': '{}:{}'.format(fix_path(relfileid3), 19), 'markers': ['expected-failure'], - 'parentid': relfile3 + '::HamTests', + 'parentid': relfileid3 + '::HamTests', }, - {'id': relfile3 + '::HamTests::test_whoa', + {'id': relfileid3 + '::HamTests::test_whoa', 'name': 'test_whoa', - 'source': '{}:{}'.format(relfile3, 35), + 'source': '{}:{}'.format(fix_path(relfileid3), 35), 'markers': [], - 'parentid': relfile3 + '::HamTests', + 'parentid': relfileid3 + '::HamTests', }, - {'id': relfile3 + '::MoreHam::test_yay[1-2]', + {'id': relfileid3 + '::MoreHam::test_yay[1-2]', 'name': 'test_yay[1-2]', - 'source': '{}:{}'.format(relfile3, 57), + 'source': '{}:{}'.format(fix_path(relfileid3), 57), 'markers': [], - 'parentid': relfile3 + '::MoreHam::test_yay', + 'parentid': relfileid3 + '::MoreHam::test_yay', }, - {'id': relfile3 + '::MoreHam::test_yay[1-2][3-4]', + {'id': relfileid3 + '::MoreHam::test_yay[1-2][3-4]', 'name': 'test_yay[1-2][3-4]', - 'source': '{}:{}'.format(relfile3, 72), + 'source': '{}:{}'.format(fix_path(relfileid3), 72), 'markers': [], - 'parentid': relfile3 + '::MoreHam::test_yay[1-2]', + 'parentid': relfileid3 + '::MoreHam::test_yay[1-2]', }, - {'id': relfile4 + '::SpamTests::test_okay', + {'id': relfileid4 + '::SpamTests::test_okay', 'name': 'test_okay', - 'source': '{}:{}'.format(relfile4, 15), + 'source': '{}:{}'.format(fix_path(relfileid4), 15), 'markers': [], - 'parentid': relfile4 + '::SpamTests', + 'parentid': relfileid4 + '::SpamTests', }, - {'id': relfile5 + '::SpamTests::test_okay', + {'id': relfileid5 + '::SpamTests::test_okay', 'name': 'test_okay', - 'source': '{}:{}'.format(relfile5, 12), + 'source': '{}:{}'.format(fix_path(relfileid5), 12), 'markers': [], - 'parentid': relfile5 + '::SpamTests', + 'parentid': relfileid5 + '::SpamTests', }, - {'id': relfile6 + '::SpamTests::test_okay', + {'id': relfileid6 + '::SpamTests::test_okay', 'name': 'test_okay', - 'source': '{}:{}'.format(relfile6, 27), + 'source': '{}:{}'.format(fix_path(relfileid6), 27), 'markers': [], - 'parentid': relfile6 + '::SpamTests', + 'parentid': relfileid6 + '::SpamTests', }, ], }] @@ -737,8 +770,8 @@ def test_complex(self): def test_simple_basic(self): stub = StubSender() - testroot = '/a/b/c'.replace('/', os.path.sep) - relfile = 'x/y/z/test_spam.py'.replace('/', os.path.sep) + testroot = fix_path('/a/b/c') + relfile = fix_path('x/y/z/test_spam.py') tests = [ TestInfo( id='test#1', @@ -811,14 +844,14 @@ def test_simple_complex(self): test_okay """ stub = StubSender() - testroot1 = '/a/b/c'.replace('/', os.path.sep) - relfile1 = './test_ham.py'.replace('/', os.path.sep) - testroot2 = '/a/b/e/f/g'.replace('/', os.path.sep) - relfile2 = './test_spam.py'.replace('/', os.path.sep) - relfile3 = 'w/test_ham.py'.replace('/', os.path.sep) - relfile4 = 'w/test_eggs.py'.replace('/', os.path.sep) - relfile5 = 'x/y/a/test_spam.py'.replace('/', os.path.sep) - relfile6 = 'x/y/b/test_spam.py'.replace('/', os.path.sep) + testroot1 = fix_path('/a/b/c') + relfile1 = fix_path('./test_ham.py') + testroot2 = fix_path('/a/b/e/f/g') + relfile2 = fix_path('./test_spam.py') + relfile3 = fix_path('w/test_ham.py') + relfile4 = fix_path('w/test_eggs.py') + relfile5 = fix_path('x/y/a/test_spam.py') + relfile6 = fix_path('x/y/b/test_spam.py') tests = [ # under first root folder TestInfo( diff --git a/pythonFiles/tests/testing_tools/adapter/test_util.py b/pythonFiles/tests/testing_tools/adapter/test_util.py index eabca9cdd475..309ab1e0d1e7 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_util.py +++ b/pythonFiles/tests/testing_tools/adapter/test_util.py @@ -1,10 +1,248 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. +from __future__ import absolute_import, print_function + +import ntpath +import os +import os.path +import posixpath import shlex +import sys import unittest -from testing_tools.adapter.util import shlex_unsplit +import pytest + +from testing_tools.adapter.util import ( + fix_path, fix_relpath, fix_fileid, + shlex_unsplit, + ) + + +@unittest.skipIf(sys.version_info < (3,), 'Python 2 does not have subTest') +class FilePathTests(unittest.TestCase): + + @pytest.mark.functional + def test_isolated_imports(self): + import testing_tools.adapter + from testing_tools.adapter import util + from . import test_functional + ignored = { + os.path.abspath(__file__), + os.path.abspath(util.__file__), + os.path.abspath(test_functional.__file__), + } + adapter = os.path.abspath( + os.path.dirname(testing_tools.adapter.__file__)) + tests = os.path.join( + os.path.abspath( + os.path.dirname( + os.path.dirname(testing_tools.__file__))), + 'tests', + 'testing_tools', + 'adapter', + ) + found = [] + for root in [adapter, tests]: + for dirname, _, files in os.walk(root): + if '.data' in dirname: + continue + for basename in files: + if not basename.endswith('.py'): + continue + filename = os.path.join(dirname, basename) + if filename in ignored: + continue + with open(filename) as srcfile: + for line in srcfile: + if line.strip() == 'import os.path': + found.append(filename) + break + + if found: + self.fail(os.linesep.join([ + '', + 'Please only use path-related API from testing_tools.adapter.util.', + 'Found use of "os.path" in the following files:', + ] + [' ' + file for file in found])) + + def test_fix_path(self): + tests = [ + ('./spam.py', r'.\spam.py'), + ('./some-dir', r'.\some-dir'), + ('./some-dir/', '.\\some-dir\\'), + ('./some-dir/eggs', r'.\some-dir\eggs'), + ('./some-dir/eggs/spam.py', r'.\some-dir\eggs\spam.py'), + ('X/y/Z/a.B.c.PY', r'X\y\Z\a.B.c.PY'), + ('/', '\\'), + ('/spam', r'\spam'), + ('C:/spam', r'C:\spam'), + ] + for path, expected in tests: + pathsep = ntpath.sep + with self.subTest(r'fixed for \: {!r}'.format(path)): + fixed = fix_path(path, _pathsep=pathsep) + self.assertEqual(fixed, expected) + + pathsep = posixpath.sep + with self.subTest('unchanged for /: {!r}'.format(path)): + unchanged = fix_path(path, _pathsep=pathsep) + self.assertEqual(unchanged, path) + + # no path -> "." + for path in ['', None]: + for pathsep in [ntpath.sep, posixpath.sep]: + with self.subTest(r'fixed for {}: {!r}'.format(pathsep, path)): + fixed = fix_path(path, _pathsep=pathsep) + self.assertEqual(fixed, '.') + + # no-op paths + paths = [path for _, path in tests] + paths.extend([ + '.', + '..', + 'some-dir', + 'spam.py', + ]) + for path in paths: + for pathsep in [ntpath.sep, posixpath.sep]: + with self.subTest(r'unchanged for {}: {!r}'.format(pathsep, path)): + unchanged = fix_path(path, _pathsep=pathsep) + self.assertEqual(unchanged, path) + + def test_fix_relpath(self): + tests = [ + ('spam.py', posixpath, './spam.py'), + ('eggs/spam.py', posixpath, './eggs/spam.py'), + ('eggs/spam/', posixpath, './eggs/spam/'), + (r'\spam.py', posixpath, r'./\spam.py'), + ('spam.py', ntpath, r'.\spam.py'), + (r'eggs\spam.py', ntpath, '.\eggs\spam.py'), + ('eggs\\spam\\', ntpath, '.\\eggs\\spam\\'), + ('/spam.py', ntpath, r'\spam.py'), # Note the fixed "/". + # absolute + ('/', posixpath, '/'), + ('/spam.py', posixpath, '/spam.py'), + ('\\', ntpath, '\\'), + (r'\spam.py', ntpath, r'\spam.py'), + (r'C:\spam.py', ntpath, r'C:\spam.py'), + # no-op + ('./spam.py', posixpath, './spam.py'), + (r'.\spam.py', ntpath, r'.\spam.py'), + ] + # no-op + for path in ['.', '..']: + tests.extend([ + (path, posixpath, path), + (path, ntpath, path), + ]) + for path, _os_path, expected in tests: + with self.subTest((path, _os_path.sep)): + fixed = fix_relpath(path, + _fix_path=(lambda p: fix_path(p, _pathsep=_os_path.sep)), + _path_isabs=_os_path.isabs, + _pathsep=_os_path.sep, + ) + self.assertEqual(fixed, expected) + + def test_fix_fileid(self): + common = [ + ('spam.py', './spam.py'), + ('eggs/spam.py', './eggs/spam.py'), + ('eggs/spam/', './eggs/spam/'), + # absolute (no-op) + ('/', '/'), + ('//', '//'), + ('/spam.py', '/spam.py'), + # no-op + (None, None), + ('', ''), + ('.', '.'), + ('./spam.py', './spam.py'), + ] + tests = [(p, posixpath, e) for p, e in common] + tests.extend((p, posixpath, e) for p, e in [ + (r'\spam.py', r'./\spam.py'), + ]) + tests.extend((p, ntpath, e) for p, e in common) + tests.extend((p, ntpath, e) for p, e in [ + (r'eggs\spam.py', './eggs/spam.py'), + ('eggs\\spam\\', './eggs/spam/'), + (r'.\spam.py', r'./spam.py'), + # absolute + (r'\spam.py', '/spam.py'), + (r'C:\spam.py', 'C:/spam.py'), + ('\\', '/'), + ('\\\\', '//'), + ('C:\\\\', 'C://'), + ('C:/', 'C:/'), + ('C://', 'C://'), + ('C:/spam.py', 'C:/spam.py'), + ]) + for fileid, _os_path, expected in tests: + pathsep = _os_path.sep + with self.subTest(r'for {}: {!r}'.format(pathsep, fileid)): + fixed = fix_fileid(fileid, + _path_isabs=_os_path.isabs, + _normcase=_os_path.normcase, + _pathsep=pathsep, + ) + self.assertEqual(fixed, expected) + + # with rootdir + common = [ + ('spam.py', '/eggs', './spam.py'), + ('spam.py', '\eggs', './spam.py'), + # absolute + ('/spam.py', '/', './spam.py'), + ('/eggs/spam.py', '/eggs', './spam.py'), + ('/eggs/spam.py', '/eggs/', './spam.py'), + # no-op + ('/spam.py', '/eggs', '/spam.py'), + ('/spam.py', '/eggs/', '/spam.py'), + # root-only (no-op) + ('/', '/', '/'), + ('/', '/spam', '/'), + ('//', '/', '//'), + ('//', '//', '//'), + ('//', '//spam', '//'), + ] + tests = [(p, r, posixpath, e) for p, r, e in common] + tests = [(p, r, ntpath, e) for p, r, e in common] + tests.extend((p, r, ntpath, e) for p, r, e in [ + ('spam.py', r'\eggs', './spam.py'), + # absolute + (r'\spam.py', '\\', r'./spam.py'), + (r'C:\spam.py', 'C:\\', r'./spam.py'), + (r'\eggs\spam.py', r'\eggs', r'./spam.py'), + (r'\eggs\spam.py', '\\eggs\\', r'./spam.py'), + # normcase + (r'C:\spam.py', 'c:\\', r'./spam.py'), + (r'\Eggs\Spam.py', '\\eggs', r'./Spam.py'), + (r'\eggs\spam.py', '\\Eggs', r'./spam.py'), + (r'\eggs\Spam.py', '\\Eggs', r'./Spam.py'), + # no-op + (r'\spam.py', r'\eggs', r'/spam.py'), + (r'C:\spam.py', r'C:\eggs', r'C:/spam.py'), + # TODO: Should these be supported. + (r'C:\spam.py', '\\', r'C:/spam.py'), + (r'\spam.py', 'C:\\', r'/spam.py'), + # root-only + ('\\', '\\', '/'), + ('\\\\', '\\', '//'), + ('C:\\', 'C:\\eggs', 'C:/'), + ('C:\\', 'C:\\', 'C:/'), + (r'C:\spam.py', 'D:\\', r'C:/spam.py'), + ]) + for fileid, rootdir, _os_path, expected in tests: + pathsep = _os_path.sep + with self.subTest(r'for {} (with rootdir {!r}): {!r}'.format(pathsep, rootdir, fileid)): + fixed = fix_fileid(fileid, rootdir, + _path_isabs=_os_path.isabs, + _normcase=_os_path.normcase, + _pathsep=pathsep, + ) + self.assertEqual(fixed, expected) class ShlexUnsplitTests(unittest.TestCase): diff --git a/src/client/testing/common/services/discoveredTestParser.ts b/src/client/testing/common/services/discoveredTestParser.ts index 7772a35fc894..308ce17c5507 100644 --- a/src/client/testing/common/services/discoveredTestParser.ts +++ b/src/client/testing/common/services/discoveredTestParser.ts @@ -10,14 +10,20 @@ import { IWorkspaceService } from '../../../common/application/types'; import { traceError } from '../../../common/logger'; import { TestDataItem } from '../../types'; import { getParentFile, getParentSuite, getTestType } from '../testUtils'; -import { FlattenedTestFunction, FlattenedTestSuite, SubtestParent, TestFile, TestFolder, TestFunction, Tests, TestSuite, TestType } from '../types'; -import { DiscoveredTests, ITestDiscoveredTestParser, TestContainer, TestItem } from './types'; +import * as testing from '../types'; +import * as discovery from './types'; @injectable() -export class TestDiscoveredTestParser implements ITestDiscoveredTestParser { - constructor(@inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService) { } - public parse(resource: Uri, discoveredTests: DiscoveredTests[]): Tests { - const tests: Tests = { +export class TestDiscoveredTestParser implements discovery.ITestDiscoveredTestParser { + constructor( + @inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService + ) { } + + public parse( + resource: Uri, + discoveredTests: discovery.DiscoveredTests[] + ): testing.Tests { + const tests: testing.Tests = { rootTestFolders: [], summary: { errors: 0, failures: 0, passed: 0, skipped: 0 }, testFiles: [], @@ -32,11 +38,14 @@ export class TestDiscoveredTestParser implements ITestDiscoveredTestParser { return tests; } - // If the root is the workspace folder, then ignore that. for (const data of discoveredTests) { const rootFolder = { - name: data.root, folders: [], time: 0, - testFiles: [], resource: resource, nameToRun: data.rootid + name: data.root, + folders: [], + time: 0, + testFiles: [], + resource: resource, + nameToRun: data.rootid }; tests.rootTestFolders.push(rootFolder); tests.testFolders.push(rootFolder); @@ -45,6 +54,7 @@ export class TestDiscoveredTestParser implements ITestDiscoveredTestParser { return tests; } + /** * Not the best solution to use `case statements`, but it keeps the code simple and easy to read in one place. * Could go with separate classes for each type and use stratergies, but that just ends up a class for @@ -58,25 +68,31 @@ export class TestDiscoveredTestParser implements ITestDiscoveredTestParser { * @param {Tests} tests * @memberof TestsDiscovery */ - public buildChildren(rootFolder: TestFolder, parent: TestDataItem, discoveredTests: DiscoveredTests, tests: Tests) { + public buildChildren( + rootFolder: testing.TestFolder, + parent: TestDataItem, + discoveredTests: discovery.DiscoveredTests, + tests: testing.Tests + ) { const parentType = getTestType(parent); switch (parentType) { - case TestType.testFolder: { - this.processFolder(rootFolder, parent as TestFolder, discoveredTests, tests); + case testing.TestType.testFolder: { + this.processFolder(rootFolder, parent as testing.TestFolder, discoveredTests, tests); break; } - case TestType.testFile: { - this.processFile(rootFolder, parent as TestFile, discoveredTests, tests); + case testing.TestType.testFile: { + this.processFile(rootFolder, parent as testing.TestFile, discoveredTests, tests); break; } - case TestType.testSuite: { - this.processSuite(rootFolder, parent as TestSuite, discoveredTests, tests); + case testing.TestType.testSuite: { + this.processSuite(rootFolder, parent as testing.TestSuite, discoveredTests, tests); break; } default: break; } } + /** * Process the children of a folder. * A folder can only contain other folders and files. @@ -89,21 +105,47 @@ export class TestDiscoveredTestParser implements ITestDiscoveredTestParser { * @param {Tests} tests * @memberof TestDiscoveredTestParser */ - protected processFolder(rootFolder: TestFolder, parentFolder: TestFolder, discoveredTests: DiscoveredTests, tests: Tests) { + protected processFolder( + rootFolder: testing.TestFolder, + parentFolder: testing.TestFolder, + discoveredTests: discovery.DiscoveredTests, + tests: testing.Tests + ) { const folders = discoveredTests.parents .filter(child => child.kind === 'folder' && child.parentid === parentFolder.nameToRun) - .map(folder => createTestFolder(rootFolder, folder)); + .map(folder => createTestFolder( + rootFolder, + folder as discovery.TestFolder + )); + folders.forEach(folder => { + parentFolder.folders.push(folder); + tests.testFolders.push(folder); + this.buildChildren( + rootFolder, + folder, + discoveredTests, + tests + ); + }); const files = discoveredTests.parents .filter(child => child.kind === 'file' && child.parentid === parentFolder.nameToRun) - .map(file => createTestFile(rootFolder, file)); - - parentFolder.folders.push(...folders); - parentFolder.testFiles.push(...files); - tests.testFolders.push(...folders); - tests.testFiles.push(...files); - [...folders, ...files].forEach(item => this.buildChildren(rootFolder, item, discoveredTests, tests)); + .map(file => createTestFile( + rootFolder, + file as discovery.TestFile + )); + files.forEach(file => { + parentFolder.testFiles.push(file); + tests.testFiles.push(file); + this.buildChildren( + rootFolder, + file, + discoveredTests, + tests + ); + }); } + /** * Process the children of a file. * A file can only contain suites, functions and paramerterized functions. @@ -116,26 +158,57 @@ export class TestDiscoveredTestParser implements ITestDiscoveredTestParser { * @param {Tests} tests * @memberof TestDiscoveredTestParser */ - protected processFile(rootFolder: TestFolder, parentFile: TestFile, discoveredTests: DiscoveredTests, tests: Tests) { + protected processFile( + rootFolder: testing.TestFolder, + parentFile: testing.TestFile, + discoveredTests: discovery.DiscoveredTests, + tests: testing.Tests + ) { const suites = discoveredTests.parents .filter(child => child.kind === 'suite' && child.parentid === parentFile.nameToRun) - .map(suite => createTestSuite(parentFile, rootFolder.resource, suite)); + .map(suite => createTestSuite( + parentFile, + rootFolder.resource, + suite as discovery.TestSuite + )); + suites.forEach(suite => { + parentFile.suites.push(suite); + tests.testSuites.push( + createFlattenedSuite(tests, suite) + ); + this.buildChildren( + rootFolder, + suite, + discoveredTests, + tests + ); + }); const functions = discoveredTests.tests - .filter(func => func.parentid === parentFile.nameToRun) - .map(func => createTestFunction(rootFolder, func)); - - parentFile.suites.push(...suites); - parentFile.functions.push(...functions); - tests.testSuites.push(...suites.map(suite => createFlattenedSuite(tests, suite))); - tests.testFunctions.push(...functions.map(func => createFlattenedFunction(tests, func))); - suites.forEach(item => this.buildChildren(rootFolder, item, discoveredTests, tests)); + .filter(test => test.parentid === parentFile.nameToRun) + .map(test => createTestFunction(rootFolder, test)); + functions.forEach(func => { + parentFile.functions.push(func); + tests.testFunctions.push( + createFlattenedFunction(tests, func) + ); + }); const parameterizedFunctions = discoveredTests.parents .filter(child => child.kind === 'function' && child.parentid === parentFile.nameToRun) - .map(func => createParameterizedTestFunction(rootFolder, func)); - parameterizedFunctions.forEach(func => this.processParameterizedFunction(rootFolder, parentFile, func, discoveredTests, tests)); + .map(func => createParameterizedTestFunction( + rootFolder, + func as discovery.TestFunction + )); + parameterizedFunctions.forEach(func => this.processParameterizedFunction( + rootFolder, + parentFile, + func, + discoveredTests, + tests + )); } + /** * Process the children of a suite. * A suite can only contain suites, functions and paramerterized functions. @@ -148,26 +221,57 @@ export class TestDiscoveredTestParser implements ITestDiscoveredTestParser { * @param {Tests} tests * @memberof TestDiscoveredTestParser */ - protected processSuite(rootFolder: TestFolder, parentSuite: TestSuite, discoveredTests: DiscoveredTests, tests: Tests) { + protected processSuite( + rootFolder: testing.TestFolder, + parentSuite: testing.TestSuite, + discoveredTests: discovery.DiscoveredTests, + tests: testing.Tests + ) { const suites = discoveredTests.parents .filter(child => child.kind === 'suite' && child.parentid === parentSuite.nameToRun) - .map(suite => createTestSuite(parentSuite, rootFolder.resource, suite)); + .map(suite => createTestSuite( + parentSuite, + rootFolder.resource, + suite as discovery.TestSuite + )); + suites.forEach(suite => { + parentSuite.suites.push(suite); + tests.testSuites.push( + createFlattenedSuite(tests, suite) + ); + this.buildChildren( + rootFolder, + suite, + discoveredTests, + tests + ); + }); const functions = discoveredTests.tests - .filter(func => func.parentid === parentSuite.nameToRun) - .map(func => createTestFunction(rootFolder, func)); - - parentSuite.suites.push(...suites); - parentSuite.functions.push(...functions); - tests.testSuites.push(...suites.map(suite => createFlattenedSuite(tests, suite))); - tests.testFunctions.push(...functions.map(func => createFlattenedFunction(tests, func))); - suites.forEach(item => this.buildChildren(rootFolder, item, discoveredTests, tests)); + .filter(test => test.parentid === parentSuite.nameToRun) + .map(test => createTestFunction(rootFolder, test)); + functions.forEach(func => { + parentSuite.functions.push(func); + tests.testFunctions.push( + createFlattenedFunction(tests, func) + ); + }); const parameterizedFunctions = discoveredTests.parents .filter(child => child.kind === 'function' && child.parentid === parentSuite.nameToRun) - .map(func => createParameterizedTestFunction(rootFolder, func)); - parameterizedFunctions.forEach(func => this.processParameterizedFunction(rootFolder, parentSuite, func, discoveredTests, tests)); + .map(func => createParameterizedTestFunction( + rootFolder, + func as discovery.TestFunction + )); + parameterizedFunctions.forEach(func => this.processParameterizedFunction( + rootFolder, + parentSuite, + func, + discoveredTests, + tests + )); } + /** * Process the children of a parameterized function. * A parameterized function can only contain functions (in tests). @@ -181,81 +285,173 @@ export class TestDiscoveredTestParser implements ITestDiscoveredTestParser { * @returns * @memberof TestDiscoveredTestParser */ - protected processParameterizedFunction(rootFolder: TestFolder, parent: TestFile | TestSuite, parentFunction: SubtestParent, discoveredTests: DiscoveredTests, tests: Tests) { + protected processParameterizedFunction( + rootFolder: testing.TestFolder, + parent: testing.TestFile | testing.TestSuite, + parentFunction: testing.SubtestParent, + discoveredTests: discovery.DiscoveredTests, + tests: testing.Tests + ) { if (!parentFunction.asSuite) { return; } const functions = discoveredTests.tests - .filter(func => func.parentid === parentFunction.nameToRun) - .map(func => createTestFunction(rootFolder, func)); - functions.map(func => func.subtestParent = parentFunction); - parentFunction.asSuite.functions.push(...functions); - parent.functions.push(...functions); - tests.testFunctions.push(...functions.map(func => createFlattenedParameterizedFunction(tests, func, parent))); + .filter(test => test.parentid === parentFunction.nameToRun) + .map(test => createTestFunction(rootFolder, test)); + functions.forEach(func => { + func.subtestParent = parentFunction; + parentFunction.asSuite.functions.push(func); + parent.functions.push(func); + tests.testFunctions.push( + createFlattenedParameterizedFunction( + tests, + func, + parent + ) + ); + }); } } -function createTestFolder(root: TestFolder, item: TestContainer): TestFolder { +function createTestFolder( + root: testing.TestFolder, + item: discovery.TestFolder +): testing.TestFolder { return { - name: item.name, nameToRun: item.id, resource: root.resource, time: 0, folders: [], testFiles: [] + name: item.name, + nameToRun: item.id, + resource: root.resource, + time: 0, + folders: [], + testFiles: [] }; } -function createTestFile(root: TestFolder, item: TestContainer): TestFile { - const fullyQualifiedName = path.isAbsolute(item.id) ? item.id : path.resolve(root.name, item.id); + +function createTestFile( + root: testing.TestFolder, + item: discovery.TestFile +): testing.TestFile { + const fullpath = path.isAbsolute(item.relpath) ? + item.relpath : + path.resolve(root.name, item.relpath); return { - fullPath: fullyQualifiedName, functions: [], name: item.name, - nameToRun: item.id, resource: root.resource, suites: [], time: 0, xmlName: createXmlName(item.id) + fullPath: fullpath, + functions: [], + name: item.name, + nameToRun: item.id, + resource: root.resource, + suites: [], + time: 0, + xmlName: createXmlName(item.id) }; } -function createTestSuite(parentSuiteFile: TestFile | TestSuite, resource: Uri, item: TestContainer): TestSuite { + +function createTestSuite( + parentSuiteFile: testing.TestFile | testing.TestSuite, + resource: Uri, + item: discovery.TestSuite +): testing.TestSuite { const suite = { - functions: [], name: item.name, nameToRun: item.id, resource: resource, - suites: [], time: 0, xmlName: '', isInstance: false, isUnitTest: false + functions: [], + name: item.name, + nameToRun: item.id, + resource: resource, + suites: [], + time: 0, + xmlName: '', + isInstance: false, + isUnitTest: false }; suite.xmlName = `${parentSuiteFile.xmlName}.${item.name}`; return suite; } -function createFlattenedSuite(tests: Tests, suite: TestSuite): FlattenedTestSuite { + +function createFlattenedSuite( + tests: testing.Tests, + suite: testing.TestSuite +): testing.FlattenedTestSuite { const parentFile = getParentFile(tests, suite); return { - parentTestFile: parentFile, testSuite: suite, xmlClassName: parentFile.xmlName + parentTestFile: parentFile, + testSuite: suite, + xmlClassName: parentFile.xmlName }; } -function createFlattenedParameterizedFunction(tests: Tests, func: TestFunction, parent: TestFile | TestSuite): FlattenedTestFunction { + +function createFlattenedParameterizedFunction( + tests: testing.Tests, + func: testing.TestFunction, + parent: testing.TestFile | testing.TestSuite +): testing.FlattenedTestFunction { const type = getTestType(parent); - const parentFile = (type && type === TestType.testSuite) ? getParentFile(tests, func) : parent as TestFile; - const parentSuite = (type && type === TestType.testSuite) ? parent as TestSuite : undefined; + const parentFile = (type && type === testing.TestType.testSuite) ? + getParentFile(tests, func) : + parent as testing.TestFile; + const parentSuite = (type && type === testing.TestType.testSuite) ? + parent as testing.TestSuite : + undefined; return { - parentTestFile: parentFile, parentTestSuite: parentSuite, - xmlClassName: parentSuite ? parentSuite.xmlName : parentFile.xmlName, testFunction: func + parentTestFile: parentFile, + parentTestSuite: parentSuite, + xmlClassName: parentSuite ? parentSuite.xmlName : parentFile.xmlName, + testFunction: func }; } -function createFlattenedFunction(tests: Tests, func: TestFunction): FlattenedTestFunction { + +function createFlattenedFunction( + tests: testing.Tests, + func: testing.TestFunction +): testing.FlattenedTestFunction { const parent = getParentFile(tests, func); const type = parent ? getTestType(parent) : undefined; - const parentFile = (type && type === TestType.testSuite) ? getParentFile(tests, func) : parent as TestFile; + const parentFile = (type && type === testing.TestType.testSuite) ? + getParentFile(tests, func) : + parent as testing.TestFile; const parentSuite = getParentSuite(tests, func); return { - parentTestFile: parentFile, parentTestSuite: parentSuite, - xmlClassName: parentSuite ? parentSuite.xmlName : parentFile.xmlName, testFunction: func + parentTestFile: parentFile, + parentTestSuite: parentSuite, + xmlClassName: parentSuite ? parentSuite.xmlName : parentFile.xmlName, + testFunction: func }; } -function createParameterizedTestFunction(root: TestFolder, item: TestContainer): SubtestParent { - const suite: TestSuite = { - functions: [], isInstance: false, isUnitTest: false, - name: item.name, nameToRun: item.id, resource: root.resource, - time: 0, suites: [], xmlName: '' + +function createParameterizedTestFunction( + root: testing.TestFolder, + item: discovery.TestFunction +): testing.SubtestParent { + const suite: testing.TestSuite = { + functions: [], + isInstance: false, + isUnitTest: false, + name: item.name, + nameToRun: item.id, + resource: root.resource, + time: 0, + suites: [], + xmlName: '' }; return { - asSuite: suite, name: item.name, nameToRun: item.id, time: 0 + asSuite: suite, + name: item.name, + nameToRun: item.id, + time: 0 }; } -function createTestFunction(root: TestFolder, item: TestItem): TestFunction { + +function createTestFunction( + root: testing.TestFolder, + item: discovery.Test +): testing.TestFunction { return { - name: item.name, nameToRun: item.id, resource: root.resource, - time: 0, file: item.source.substr(0, item.source.lastIndexOf(':')) + name: item.name, + nameToRun: item.id, + resource: root.resource, + time: 0, + file: item.source.substr(0, item.source.lastIndexOf(':')) }; } + /** * Creates something known as an Xml Name, used to identify items * from an xunit test result. @@ -264,9 +460,13 @@ function createTestFunction(root: TestFolder, item: TestItem): TestFunction { * @returns */ function createXmlName(fileId: string) { - let name = path.join(path.dirname(fileId), path.basename(fileId, path.extname(fileId))); + let name = path.join( + path.dirname(fileId), + path.basename(fileId, path.extname(fileId)) + ); + // Replace all path separators with ".". name = name.replace(/\\/g, '.').replace(/\//g, '.'); - // Remove leading . & / & \ + // Remove leading "." and path separators. while (name.startsWith('.') || name.startsWith('/') || name.startsWith('\\')) { name = name.substring(1); } diff --git a/src/client/testing/common/services/types.ts b/src/client/testing/common/services/types.ts index 4bf14344eae8..b6ae34eccb0f 100644 --- a/src/client/testing/common/services/types.ts +++ b/src/client/testing/common/services/types.ts @@ -6,23 +6,39 @@ import { Uri } from 'vscode'; import { Tests } from '../types'; -export type TestContainer = { +export type TestNode = { id: string; - kind: 'file' | 'folder' | 'suite' | 'function'; name: string; parentid: string; }; -export type TestItem = { - id: string; - name: string; +export type TestParent = TestNode & { + kind: 'folder' | 'file' | 'suite' | 'function'; +}; +export type TestFSNode = TestParent & { + kind: 'folder' | 'file'; + relpath: string; +}; +export type TestFolder = TestFSNode & { + kind: 'folder'; +}; +export type TestFile = TestFSNode & { + kind: 'file'; +}; +export type TestSuite = TestParent & { + kind: 'suite'; +}; +// function-as-a-container is for parameterized ("sub") tests. +export type TestFunction = TestParent & { + kind: 'function'; +}; +export type Test = TestNode & { source: string; - parentid: string; }; export type DiscoveredTests = { rootid: string; root: string; - parents: TestContainer[]; - tests: TestItem[]; + parents: TestParent[]; + tests: Test[]; }; export const ITestDiscoveredTestParser = Symbol('ITestDiscoveredTestParser');