From 8ddce3b25a75d2d1c65a5a703dc5e43ae7854c44 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 5 Aug 2019 16:47:32 -0600 Subject: [PATCH 01/21] Add ParentInfo.location. --- pythonFiles/testing_tools/adapter/discovery.py | 3 ++- pythonFiles/testing_tools/adapter/info.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/discovery.py b/pythonFiles/testing_tools/adapter/discovery.py index 15196a6b0beb..9d4d4efcc1b0 100644 --- a/pythonFiles/testing_tools/adapter/discovery.py +++ b/pythonFiles/testing_tools/adapter/discovery.py @@ -54,7 +54,8 @@ def _ensure_parent(self, path, 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) + loc = None + info = ParentInfo(nodeid, kind, name, loc, rootdir, 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..3c75394e8647 100644 --- a/pythonFiles/testing_tools/adapter/info.py +++ b/pythonFiles/testing_tools/adapter/info.py @@ -26,16 +26,17 @@ 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 location root parentid')): KINDS = ('folder', 'file', 'suite', 'function', 'subtest') - def __new__(cls, id, kind, name, root=None, parentid=None): + def __new__(cls, id, kind, name, location=None, root=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(location) if location else None, str(root) if root else None, str(parentid) if parentid else None, ) From 57e3028b48a59f4a693606a9abd3e50627b42c3a Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 6 Aug 2019 07:55:47 -0600 Subject: [PATCH 02/21] Add "relpath" to reported parents (when available). --- .../testing_tools/adapter/discovery.py | 8 +- pythonFiles/testing_tools/adapter/info.py | 20 +- pythonFiles/testing_tools/adapter/report.py | 2 + .../testing_tools/adapter/test_discovery.py | 52 ++- .../testing_tools/adapter/test_functional.py | 33 ++ .../testing_tools/adapter/test_report.py | 421 ++++++++++-------- 6 files changed, 318 insertions(+), 218 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/discovery.py b/pythonFiles/testing_tools/adapter/discovery.py index 9d4d4efcc1b0..cf4988d22af3 100644 --- a/pythonFiles/testing_tools/adapter/discovery.py +++ b/pythonFiles/testing_tools/adapter/discovery.py @@ -43,6 +43,7 @@ def add_test(self, test, parents): def _ensure_parent(self, path, parents): rootdir = path.root + relpath = path.relfile _parents = iter(parents) nodeid, name, kind = next(_parents) @@ -54,8 +55,11 @@ def _ensure_parent(self, path, 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) - loc = None - info = ParentInfo(nodeid, kind, name, loc, rootdir, parentid) + if kind in ('folder', 'file'): + info = ParentInfo(nodeid, kind, name, rootdir, relpath, parentid) + relpath = os.path.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 3c75394e8647..ae01426fe57b 100644 --- a/pythonFiles/testing_tools/adapter/info.py +++ b/pythonFiles/testing_tools/adapter/info.py @@ -26,19 +26,19 @@ def __init__(self, *args, **kwargs): # self.sub may be None. -class ParentInfo(namedtuple('ParentInfo', 'id kind name location root parentid')): +class ParentInfo(namedtuple('ParentInfo', 'id kind name root relpath parentid')): KINDS = ('folder', 'file', 'suite', 'function', 'subtest') - def __new__(cls, id, kind, name, location=None, 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(location) if location 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 @@ -54,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/report.py b/pythonFiles/testing_tools/adapter/report.py index edc33fcb8a34..e53320aa11eb 100644 --- a/pythonFiles/testing_tools/adapter/report.py +++ b/pythonFiles/testing_tools/adapter/report.py @@ -41,6 +41,8 @@ def report_discovered(tests, parents, pretty=False, simple=False, '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] diff --git a/pythonFiles/tests/testing_tools/adapter/test_discovery.py b/pythonFiles/tests/testing_tools/adapter/test_discovery.py index 01475afd4b00..9673b7452010 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_discovery.py +++ b/pythonFiles/tests/testing_tools/adapter/test_discovery.py @@ -18,11 +18,11 @@ class DiscoveredTestsTests(unittest.TestCase): def test_list(self): testroot = fix_path('/a/b/c') - relfile = 'test_spam.py' - relfileid = os.path.join('.', relfile) + relfileid = './test_spam.py' + relfile = fix_path(relfileid) tests = [ TestInfo( - id=relfile + '::test_each[10-10]', + id=relfileid[2:] + '::test_each[10-10]', name='test_each[10-10]', path=TestPath( root=testroot, @@ -32,10 +32,10 @@ def test_list(self): ), source='{}:{}'.format(relfile, 10), markers=None, - parentid=relfile + '::test_each', + parentid=relfileid[2:] + '::test_each', ), TestInfo( - id=relfile + '::All::BasicTests::test_first', + id=relfileid[2:] + '::All::BasicTests::test_first', name='test_first', path=TestPath( root=testroot, @@ -45,22 +45,22 @@ def test_list(self): ), source='{}:{}'.format(relfile, 62), markers=None, - parentid=relfile + '::All::BasicTests', + parentid=relfileid[2:] + '::All::BasicTests', ), ] allparents= [ [(relfileid + '::test_each', 'test_each', 'function'), - (relfileid, relfile, 'file'), + (relfileid, 'test_spam.py', 'file'), ('.', testroot, 'folder'), ], [(relfileid + '::All::BasicTests', 'BasicTests', 'suite'), (relfileid + '::All', 'All', 'suite'), - (relfileid, relfile, 'file'), + (relfileid, '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='./' + test.id, + parentid='./' + test.parentid) for test in tests] discovered = DiscoveredTests() for test, parents in zip(tests, allparents): @@ -111,7 +111,8 @@ def test_parents(self): name='test_each[10-10]', path=TestPath( root=testroot, - relfile=relfile, + #relfile=relfile, + relfile=relfileid, func='test_each', sub=['[10-10]'], ), @@ -124,7 +125,8 @@ def test_parents(self): name='test_first', path=TestPath( root=testroot, - relfile=relfile, + #relfile=relfile, + relfile=relfileid, func='All.BasicTests.test_first', sub=None, ), @@ -165,6 +167,7 @@ def test_parents(self): kind='folder', name='x', root=testroot, + relpath=fix_path('./x'), parentid='.', ), ParentInfo( @@ -172,6 +175,7 @@ def test_parents(self): kind='folder', name='y', root=testroot, + relpath=fix_path('./x/y'), parentid=fix_path('./x'), ), ParentInfo( @@ -179,6 +183,7 @@ def test_parents(self): kind='folder', name='z', root=testroot, + relpath=fix_path('./x/y/z'), parentid=fix_path('./x/y'), ), ParentInfo( @@ -186,6 +191,7 @@ def test_parents(self): kind='file', name=os.path.basename(relfile), root=testroot, + relpath=fix_path(relfileid), parentid=os.path.dirname(relfileid), ), ParentInfo( @@ -251,6 +257,7 @@ def test_add_test_simple(self): kind='file', name=relfile, root=testroot, + relpath=relfile, parentid='.', ), ])) @@ -266,7 +273,8 @@ def test_multiroot(self): name='test_spam', path=TestPath( root=testroot1, - relfile=relfile1, + #relfile=relfile1, + relfile=relfileid1, func='test_spam', ), source='{}:{}'.format(relfile1, 10), @@ -289,7 +297,8 @@ def test_multiroot(self): name='test_first', path=TestPath( root=testroot2, - relfile=relfile2, + #relfile=relfile2, + relfile=relfileid2, func='BasicTests.test_first', ), source='{}:{}'.format(relfile2, 61), @@ -319,7 +328,8 @@ def test_multiroot(self): name='test_spam', path=TestPath( root=testroot1, - relfile=relfile1, + #relfile=relfile1, + relfile=relfileid1, func='test_spam', ), source='{}:{}'.format(relfile1, 10), @@ -332,7 +342,8 @@ def test_multiroot(self): name='test_first', path=TestPath( root=testroot2, - relfile=relfile2, + #relfile=relfile2, + relfile=relfileid2, func='BasicTests.test_first', ), source='{}:{}'.format(relfile2, 61), @@ -352,6 +363,7 @@ def test_multiroot(self): kind='file', name=os.path.basename(relfile1), root=testroot1, + relpath=fix_path(relfileid1), parentid=os.path.dirname(relfileid1), ), # the secondroot @@ -365,6 +377,7 @@ def test_multiroot(self): kind='folder', name='w', root=testroot2, + relpath=fix_path('./w'), parentid='.', ), ParentInfo( @@ -372,6 +385,7 @@ def test_multiroot(self): kind='file', name=os.path.basename(relfile2), root=testroot2, + relpath=fix_path(relfileid2), parentid=os.path.dirname(relfileid2), ), ParentInfo( @@ -483,6 +497,7 @@ def test_doctest(self): kind='folder', name='x', root=testroot, + relpath=fix_path('./x'), parentid='.', ), ParentInfo( @@ -490,6 +505,7 @@ def test_doctest(self): kind='file', name=os.path.basename(doctestfile), root=testroot, + relpath=fix_path(doctestfile), parentid=os.path.dirname(doctestfile), ), ParentInfo( @@ -497,6 +513,7 @@ def test_doctest(self): kind='folder', name='y', root=testroot, + relpath=fix_path('./x/y'), parentid=fix_path('./x'), ), ParentInfo( @@ -504,6 +521,7 @@ def test_doctest(self): kind='folder', name='z', root=testroot, + relpath=fix_path('./x/y/z'), parentid=fix_path('./x/y'), ), ParentInfo( @@ -511,6 +529,7 @@ def test_doctest(self): kind='file', name=os.path.basename(relfile), root=testroot, + relpath=fix_path(relfile), parentid=os.path.dirname(relfile), ), ]) @@ -576,6 +595,7 @@ def test_nested_suite_simple(self): kind='file', name=os.path.basename(relfile), root=testroot, + relpath=fix_path(relfile), parentid=os.path.dirname(relfile), ), ParentInfo( diff --git a/pythonFiles/tests/testing_tools/adapter/test_functional.py b/pythonFiles/tests/testing_tools/adapter/test_functional.py index 209738d7f12f..c1a7c6d36102 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_functional.py +++ b/pythonFiles/tests/testing_tools/adapter/test_functional.py @@ -103,11 +103,13 @@ def test_discover_simple(self): {'id': fix_path('./tests'), 'kind': 'folder', 'name': 'tests', + 'relpath': fix_path('./tests'), 'parentid': '.', }, {'id': fix_path('./tests/test_spam.py'), 'kind': 'file', 'name': 'test_spam.py', + 'relpath': fix_path('./tests/test_spam.py'), 'parentid': fix_path('./tests'), }, ], @@ -151,6 +153,7 @@ def test_discover_complex_doctest(self): 'id': fix_path('./tests/test_doctest.py'), 'kind': 'file', 'name': 'test_doctest.py', + 'relpath': fix_path('./tests/test_doctest.py'), 'parentid': fix_path('./tests'), }) expected[0]['tests'].insert(2, { @@ -165,6 +168,7 @@ def test_discover_complex_doctest(self): 'id': fix_path('./mod.py'), 'kind': 'file', 'name': 'mod.py', + 'relpath': fix_path('./mod.py'), 'parentid': '.', }) expected[0]['tests'] = [ @@ -273,26 +277,31 @@ def test_discover_normcase(self): {'id': fix_id('./tests'), 'kind': 'folder', 'name': 'tests', + 'relpath': fix_path('./tests'), 'parentid': '.', }, {'id': fix_id('./tests/A'), 'kind': 'folder', 'name': fix_id('A'), + 'relpath': fix_path('./tests/A'), 'parentid': fix_id('./tests'), }, {'id': fix_id('./tests/A/b'), 'kind': 'folder', 'name': 'b', + 'relpath': fix_path('./tests/A/b'), 'parentid': fix_id('./tests/A'), }, {'id': fix_id('./tests/A/b/C'), 'kind': 'folder', 'name': fix_id('C'), + 'relpath': fix_path('./tests/A/b/C'), 'parentid': fix_id('./tests/A/b'), }, {'id': fix_id('./tests/A/b/C/test_Spam.py'), 'kind': 'file', 'name': fix_id('test_Spam.py'), + 'relpath': fix_path('./tests/A/b/C/test_Spam.py'), 'parentid': fix_id('./tests/A/b/C'), }, ], @@ -315,36 +324,42 @@ def test_discover_normcase(self): {'id': fix_path('./tests'), 'kind': 'folder', 'name': 'tests', + 'relpath': fix_path('./tests'), 'parentid': '.', }, # +++ {'id': fix_path('./tests/test_42-43.py'), 'kind': 'file', 'name': 'test_42-43.py', + 'relpath': fix_path('./tests/test_42-43.py'), 'parentid': fix_path('./tests'), }, # +++ {'id': fix_path('./tests/test_42.py'), 'kind': 'file', 'name': 'test_42.py', + 'relpath': fix_path('./tests/test_42.py'), 'parentid': fix_path('./tests'), }, # +++ {'id': fix_path('./tests/test_doctest.txt'), 'kind': 'file', 'name': 'test_doctest.txt', + 'relpath': fix_path('./tests/test_doctest.txt'), 'parentid': fix_path('./tests'), }, # +++ {'id': fix_path('./tests/test_foo.py'), 'kind': 'file', 'name': 'test_foo.py', + 'relpath': fix_path('./tests/test_foo.py'), 'parentid': fix_path('./tests'), }, # +++ {'id': fix_path('./tests/test_mixed.py'), 'kind': 'file', 'name': 'test_mixed.py', + 'relpath': fix_path('./tests/test_mixed.py'), 'parentid': fix_path('./tests'), }, {'id': fix_path('./tests/test_mixed.py::MyTests'), @@ -361,6 +376,7 @@ def test_discover_normcase(self): {'id': fix_path('./tests/test_pytest.py'), 'kind': 'file', 'name': 'test_pytest.py', + 'relpath': fix_path('./tests/test_pytest.py'), 'parentid': fix_path('./tests'), }, {'id': fix_path('./tests/test_pytest.py::TestEggs'), @@ -477,6 +493,7 @@ def test_discover_normcase(self): {'id': fix_path('./tests/test_pytest_param.py'), 'kind': 'file', 'name': 'test_pytest_param.py', + 'relpath': fix_path('./tests/test_pytest_param.py'), 'parentid': fix_path('./tests'), }, {'id': fix_path('./tests/test_pytest_param.py::TestParamAll'), @@ -503,6 +520,7 @@ def test_discover_normcase(self): {'id': fix_path('./tests/test_unittest.py'), 'kind': 'file', 'name': 'test_unittest.py', + 'relpath': fix_path('./tests/test_unittest.py'), 'parentid': fix_path('./tests'), }, {'id': fix_path('./tests/test_unittest.py::MyTests'), @@ -519,12 +537,14 @@ def test_discover_normcase(self): {'id': fix_path('./tests/v'), 'kind': 'folder', 'name': 'v', + 'relpath': fix_path('./tests/v'), 'parentid': fix_path('./tests'), }, ## +++ {'id': fix_path('./tests/v/test_eggs.py'), 'kind': 'file', 'name': 'test_eggs.py', + 'relpath': fix_path('./tests/v/test_eggs.py'), 'parentid': fix_path('./tests/v'), }, {'id': fix_path('./tests/v/test_eggs.py::TestSimple'), @@ -536,78 +556,91 @@ def test_discover_normcase(self): {'id': fix_path('./tests/v/test_ham.py'), 'kind': 'file', 'name': 'test_ham.py', + 'relpath': fix_path('./tests/v/test_ham.py'), 'parentid': fix_path('./tests/v'), }, ## +++ {'id': fix_path('./tests/v/test_spam.py'), 'kind': 'file', 'name': 'test_spam.py', + 'relpath': fix_path('./tests/v/test_spam.py'), 'parentid': fix_path('./tests/v'), }, ## {'id': fix_path('./tests/w'), 'kind': 'folder', 'name': 'w', + 'relpath': fix_path('./tests/w'), 'parentid': fix_path('./tests'), }, ## +++ {'id': fix_path('./tests/w/test_spam.py'), 'kind': 'file', 'name': 'test_spam.py', + 'relpath': fix_path('./tests/w/test_spam.py'), 'parentid': fix_path('./tests/w'), }, ## +++ {'id': fix_path('./tests/w/test_spam_ex.py'), 'kind': 'file', 'name': 'test_spam_ex.py', + 'relpath': fix_path('./tests/w/test_spam_ex.py'), 'parentid': fix_path('./tests/w'), }, ## {'id': fix_path('./tests/x'), 'kind': 'folder', 'name': 'x', + 'relpath': fix_path('./tests/x'), 'parentid': fix_path('./tests'), }, ### {'id': fix_path('./tests/x/y'), 'kind': 'folder', 'name': 'y', + 'relpath': fix_path('./tests/x/y'), 'parentid': fix_path('./tests/x'), }, #### {'id': fix_path('./tests/x/y/z'), 'kind': 'folder', 'name': 'z', + 'relpath': fix_path('./tests/x/y/z'), 'parentid': fix_path('./tests/x/y'), }, ##### {'id': fix_path('./tests/x/y/z/a'), 'kind': 'folder', 'name': 'a', + 'relpath': fix_path('./tests/x/y/z/a'), 'parentid': fix_path('./tests/x/y/z'), }, ##### +++ {'id': fix_path('./tests/x/y/z/a/test_spam.py'), 'kind': 'file', 'name': 'test_spam.py', + 'relpath': fix_path('./tests/x/y/z/a/test_spam.py'), 'parentid': fix_path('./tests/x/y/z/a'), }, ##### {'id': fix_path('./tests/x/y/z/b'), 'kind': 'folder', 'name': 'b', + 'relpath': fix_path('./tests/x/y/z/b'), 'parentid': fix_path('./tests/x/y/z'), }, ##### +++ {'id': fix_path('./tests/x/y/z/b/test_spam.py'), 'kind': 'file', 'name': 'test_spam.py', + 'relpath': fix_path('./tests/x/y/z/b/test_spam.py'), 'parentid': fix_path('./tests/x/y/z/b'), }, #### +++ {'id': fix_path('./tests/x/y/z/test_ham.py'), 'kind': 'file', 'name': 'test_ham.py', + 'relpath': fix_path('./tests/x/y/z/test_ham.py'), 'parentid': fix_path('./tests/x/y/z'), }, ], diff --git a/pythonFiles/tests/testing_tools/adapter/test_report.py b/pythonFiles/tests/testing_tools/adapter/test_report.py index 4628c0719729..cd831490493c 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_report.py +++ b/pythonFiles/tests/testing_tools/adapter/test_report.py @@ -16,6 +16,10 @@ def send(self, outstr): self.add_call('send', (json.loads(outstr),), None) +def fix_path(path): + return path.replace('/', os.path.sep) + + ################################## # tests @@ -23,8 +27,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 = os.path.join('.', relfile) tests = [ TestInfo( id='test#1', @@ -50,6 +55,7 @@ def test_basic(self): kind='file', name=relfile, root=testroot, + relpath=relpath, parentid='', ), ] @@ -60,6 +66,7 @@ def test_basic(self): {'id': 'file#1', 'kind': 'file', 'name': relfile, + 'relpath': relpath, 'parentid': '', }, ], @@ -82,9 +89,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,8 +116,9 @@ def test_multiroot(self): ParentInfo( id=relfileid1, kind='file', - name=os.path.basename(relfile1), + name=os.path.basename(relpath1), root=testroot1, + relpath=relpath1, parentid=os.path.dirname(relfileid1), ), ] @@ -119,7 +128,8 @@ def test_multiroot(self): 'parents': [ {'id': relfileid1, 'kind': 'file', - 'name': relfile1, + 'name': 'test_spam.py', + 'relpath': relpath1, 'parentid': '.', }, ], @@ -133,9 +143,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,10 +168,11 @@ 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( @@ -168,7 +180,8 @@ def test_multiroot(self): kind='file', name=os.path.basename(relfile2), root=testroot2, - parentid=os.path.dirname(relfileid2), + relpath=relpath2, + parentid='./w', ), ParentInfo( id=relfileid2 + '::BasicTests', @@ -182,15 +195,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 +267,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 +418,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 +774,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 +848,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( From d7edee97d031f56e56946ec07c3f253ea6c6e8be Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 6 Aug 2019 08:09:09 -0600 Subject: [PATCH 03/21] Add util.fix_path() and friends. --- pythonFiles/testing_tools/adapter/util.py | 102 ++++++++++++++---- .../adapter/pytest/test_discovery.py | 5 +- .../testing_tools/adapter/test_discovery.py | 5 +- .../testing_tools/adapter/test_functional.py | 12 +-- .../testing_tools/adapter/test_report.py | 5 +- 5 files changed, 89 insertions(+), 40 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/util.py b/pythonFiles/testing_tools/adapter/util.py index d8df4eb25485..2e4a7706f4c4 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,86 @@ def group_attr_names(attrnames): return grouped +############################# +# file paths + +def fix_path(path, *, + _pathsep=os.path.sep): + """Return a platform-appropriate path for the given path.""" + return path.replace('/', _pathsep) + + +def fix_relpath(path, *, + _fix_path=fix_path, + _path_isabs=os.path.isabs, + _pathsep=os.path.sep + ): + """Return a ./-prefixed, platform-appropriate path for the given path.""" + path = _fix_path(path) + if not _path_isabs(path): + if not path.startswith('.' + _pathsep): + path = '.' + _pathsep + path + return path + + +def fix_nodeid(nodeid, rootdir=None, *, + _normcase=os.path.normcase, + _path_isabs=os.path.isabs, + _pathsep=os.path.sep, + ): + """Return a "/" separated node ID ("./"-prefixed) for the given value. + + The contained 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 nodeid == '.': + return nodeid + nodeid = _normcase(nodeid) + isabs = False + if _path_isabs(nodeid): + isabs = True + if rootdir is not None: + rootdir = _normcase(rootdir) + if not rootdir.endswith(_pathsep): + rootdir += _pathsep + if nodeid.startswith(rootdir): + nodeid = nodeid[len(rootdir):] + isabs = False + nodeid = nodeid.replace(_pathsep, '/') + if not isabs: + if not nodeid.startswith('./'): + nodeid = './' + nodeid + return nodeid + + +############################# +# 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..7148f5a9a24c 100644 --- a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py +++ b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py @@ -16,14 +16,11 @@ import _pytest.doctest from ....util import Stub, StubProxy +from testing_tools.adapter.util import fix_path from testing_tools.adapter.info import TestInfo, TestPath, ParentInfo 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): diff --git a/pythonFiles/tests/testing_tools/adapter/test_discovery.py b/pythonFiles/tests/testing_tools/adapter/test_discovery.py index 9673b7452010..76b28477df48 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_discovery.py +++ b/pythonFiles/tests/testing_tools/adapter/test_discovery.py @@ -6,14 +6,11 @@ import os.path import unittest +from testing_tools.adapter.util import fix_path from testing_tools.adapter.info import TestInfo, TestPath, ParentInfo from testing_tools.adapter.discovery import DiscoveredTests -def fix_path(nodeid): - return nodeid.replace('/', os.path.sep) - - class DiscoveredTestsTests(unittest.TestCase): def test_list(self): diff --git a/pythonFiles/tests/testing_tools/adapter/test_functional.py b/pythonFiles/tests/testing_tools/adapter/test_functional.py index c1a7c6d36102..a81165aa516d 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 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): diff --git a/pythonFiles/tests/testing_tools/adapter/test_report.py b/pythonFiles/tests/testing_tools/adapter/test_report.py index cd831490493c..8471ed870635 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_report.py +++ b/pythonFiles/tests/testing_tools/adapter/test_report.py @@ -6,6 +6,7 @@ import unittest from ...util import StubProxy +from testing_tools.adapter.util import fix_path from testing_tools.adapter.info import TestInfo, TestPath, ParentInfo from testing_tools.adapter.report import report_discovered @@ -16,10 +17,6 @@ def send(self, outstr): self.add_call('send', (json.loads(outstr),), None) -def fix_path(path): - return path.replace('/', os.path.sep) - - ################################## # tests From 0d0017f0eb5228d6b5f1c0b15cea2d88adbdd9ce Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 6 Aug 2019 10:09:32 -0600 Subject: [PATCH 04/21] Fix should_never_reach_here(). --- .../adapter/pytest/_pytest_item.py | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py index 9bd3d9ff45e8..130f48eaea67 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py +++ b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py @@ -102,7 +102,7 @@ from ..info import TestInfo, TestPath -def should_never_reach_here(node, *extra): +def should_never_reach_here(node, **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') @@ -115,14 +115,15 @@ def should_never_reach_here(node, *extra): 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 @@ -159,17 +160,17 @@ def parse_item(item, _normcase, _pathsep): 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 @@ -223,8 +224,8 @@ def _split_fspath(fspath, fileid, item, _normcase): if not _normcase(fspath).endswith(_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. @@ -347,7 +348,7 @@ def _parse_node_id(testid, kind, _pathsep, _normcase): else: raise should_never_reach_here( testid, - kind, + kind=kind, ) fullname = funcname @@ -365,7 +366,7 @@ def _parse_node_id(testid, kind, _pathsep, _normcase): else: raise should_never_reach_here( testid, - node, + node=node, ) else: fileid = None From 89790daa57ceec4bd3eb910f493554a4c04ea40e Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 6 Aug 2019 10:21:02 -0600 Subject: [PATCH 05/21] Use fix_nodeid() instead of os.path.normcase(). --- .../testing_tools/adapter/discovery.py | 46 ++- .../adapter/pytest/_discovery.py | 10 +- .../adapter/pytest/_pytest_item.py | 79 +++-- pythonFiles/testing_tools/adapter/util.py | 27 +- .../adapter/pytest/test_discovery.py | 334 ++++++++++-------- .../testing_tools/adapter/test_discovery.py | 203 ++++++----- .../testing_tools/adapter/test_functional.py | 29 +- 7 files changed, 424 insertions(+), 304 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/discovery.py b/pythonFiles/testing_tools/adapter/discovery.py index cf4988d22af3..d3bdb809d073 100644 --- a/pythonFiles/testing_tools/adapter/discovery.py +++ b/pythonFiles/testing_tools/adapter/discovery.py @@ -4,10 +4,43 @@ from __future__ import absolute_import, print_function import os.path +import re +from .util import fix_fileid from .info import ParentInfo +FILE_ID_RE = re.compile(r""" + ^ + (?: + ( .* [.] (?: py | txt ) \b ) # .txt for doctest files + ( [^.] .* )? + ) + $ + """, re.VERBOSE) + + +def fix_nodeid(nodeid, rootdir=None, *, + _fix_fileid=fix_fileid, + _pathsep=os.path.sep, + ): + if not nodeid: + raise ValueError('missing nodeid') + if nodeid == '.': + return nodeid + + m = FILE_ID_RE.match(nodeid) + if m: + fileid, remainder = m.groups() + elif len(nodeid) == 1: + fileid = nodeid + remainder = '' + else: + 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.""" @@ -36,9 +69,10 @@ 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.path.root), + parentid=parentid, + ) self._tests.append(test) def _ensure_parent(self, path, parents): @@ -48,13 +82,11 @@ def _ensure_parent(self, path, parents): _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, 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) + parentid = fix_nodeid(parentid, rootdir) if kind in ('folder', 'file'): info = ParentInfo(nodeid, kind, name, rootdir, relpath, parentid) relpath = os.path.dirname(relpath) 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 130f48eaea67..8f31fd2cb54a 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py +++ b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py @@ -93,6 +93,7 @@ from __future__ import absolute_import, print_function +import os.path import sys import pytest @@ -100,6 +101,7 @@ import _pytest.unittest from ..info import TestInfo, TestPath +from ..util import fix_fileid def should_never_reach_here(node, **extra): @@ -135,7 +137,12 @@ def should_never_reach_here(node, **extra): 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 @@ -147,15 +154,13 @@ 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( @@ -213,7 +218,9 @@ def parse_item(item, _normcase, _pathsep): return test, parents -def _split_fspath(fspath, fileid, item, _normcase): +def _split_fspath(fspath, fileid, item, *, + _fix_fileid=fix_fileid, + ): """Return (testroot, relfile) for the given fspath. "relfile" will match "fileid". @@ -221,7 +228,7 @@ def _split_fspath(fspath, fileid, item, _normcase): # "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): + if not _fix_fileid(fspath).endswith(_relsuffix): raise should_never_reach_here( item, fspath=fspath, @@ -232,17 +239,22 @@ def _split_fspath(fspath, fileid, item, _normcase): 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=os.path.sep, + ): """Return (loc str, fullname) for the given item.""" 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: @@ -251,14 +263,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. @@ -269,7 +281,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=os.path.normcase, + _pathsep=os.path.sep, + ): """Return True if "srcfile" matches the given relfile.""" testroot = _normcase(testroot) srcfile = _normcase(srcfile) @@ -284,7 +299,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=os.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 @@ -324,9 +342,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 = [] @@ -375,9 +395,11 @@ 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(nodeid, kind, *, + _normalize_node_id=(lambda *a: _normalize_node_id(*a)), + ): """Yield (nodeid, name, kind) for the given node ID and its parents.""" - nodeid = _normalize_node_id(nodeid, kind, _pathsep, _normcase) + nodeid = _normalize_node_id(nodeid, kind) if kind == 'function' and nodeid.endswith(']'): funcid, sep, parameterized = nodeid.partition('[') @@ -409,19 +431,21 @@ def _iter_nodes(nodeid, kind, _pathsep, _normcase): # Extract the file and folders. fileid = parentid - parentid, _, filename = fileid.rpartition(_pathsep) + parentid, _, filename = fileid.rpartition('/') yield (fileid, filename, 'file') # We're guaranteed at least one (the test root). - while _pathsep in parentid: + while '/' in parentid: folderid = parentid - parentid, _, foldername = folderid.rpartition(_pathsep) + parentid, _, foldername = folderid.rpartition('/') yield (folderid, foldername, '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_node_id(nodeid, kind, *, + _fix_fileid=fix_fileid, + ): """Return the canonical form for the given node ID.""" while '::()::' in nodeid: nodeid = nodeid.replace('::()::', '::') @@ -429,16 +453,13 @@ def _normalize_node_id(nodeid, kind, _pathsep, _normcase): return nodeid fileid, sep, remainder = nodeid.partition('::') - if sep: - # pytest works fine even if we normalize the filename. - nodeid = _normcase(fileid) + sep + remainder + # pytest works fine even if we normalize the filename. + nodeid = _fix_fileid(fileid) + sep + remainder - if nodeid.startswith(_pathsep): + if not nodeid.startswith('./'): # Absolute "paths" not expected. raise should_never_reach_here( nodeid, ) - if not nodeid.startswith('.' + _pathsep): - nodeid = '.' + _pathsep + nodeid return nodeid diff --git a/pythonFiles/testing_tools/adapter/util.py b/pythonFiles/testing_tools/adapter/util.py index 2e4a7706f4c4..b61a776cfec3 100644 --- a/pythonFiles/testing_tools/adapter/util.py +++ b/pythonFiles/testing_tools/adapter/util.py @@ -63,35 +63,36 @@ def fix_relpath(path, *, return path -def fix_nodeid(nodeid, rootdir=None, *, +def fix_fileid(fileid, rootdir=None, *, _normcase=os.path.normcase, _path_isabs=os.path.isabs, _pathsep=os.path.sep, ): - """Return a "/" separated node ID ("./"-prefixed) for the given value. + """Return a "/" separated file ID ("./"-prefixed) for the given value. - The contained file ID may be absolute. If so and "rootdir" is + 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 nodeid == '.': - return nodeid - nodeid = _normcase(nodeid) + if fileid == '.': + return fileid + _fileid = _normcase(fileid) isabs = False - if _path_isabs(nodeid): + if _path_isabs(_fileid): isabs = True if rootdir is not None: rootdir = _normcase(rootdir) if not rootdir.endswith(_pathsep): rootdir += _pathsep - if nodeid.startswith(rootdir): - nodeid = nodeid[len(rootdir):] + if _fileid.startswith(rootdir): + # This assumes pathsep has length 1. + fileid = fileid[len(rootdir):] isabs = False - nodeid = nodeid.replace(_pathsep, '/') + fileid = fileid.replace(_pathsep, '/').lower() if not isabs: - if not nodeid.startswith('./'): - nodeid = './' + nodeid - return nodeid + if not fileid.startswith('./'): + fileid = './' + fileid + return fileid ############################# diff --git a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py index 7148f5a9a24c..9a0c3f061473 100644 --- a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py +++ b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py @@ -16,8 +16,9 @@ import _pytest.doctest from ....util import Stub, StubProxy -from testing_tools.adapter.util import fix_path +from testing_tools.adapter.util import fix_path, fix_relpath, fix_fileid 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 @@ -170,6 +171,71 @@ 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, + _normcase=normcase, + _pathsep=pathsep, + ) + def _normalize_node_id(*args): + return pytest_item._normalize_node_id( + *args, + _fix_fileid=_fix_fileid, + ) + def _iter_nodes(*args): + return pytest_item._iter_nodes( + *args, + _normalize_node_id=_normalize_node_id, + ) + def _parse_node_id(*args): + return pytest_item._parse_node_id( + *args, + _iter_nodes=_iter_nodes, + ) + ########## + def _split_fspath(*args): + return pytest_item._split_fspath( + *args, + _fix_fileid=_fix_fileid, + ) + ########## + def _matches_relfile(*args): + return pytest_item._matches_relfile( + *args, + _normcase=normcase, + _pathsep=pathsep, + ) + def _is_legacy_wrapper(*args): + return pytest_item._is_legacy_wrapper( + *args, + _pathsep=pathsep, + ) + def _get_location(*args): + return pytest_item._get_location( + *args, + _matches_relfile=_matches_relfile, + _is_legacy_wrapper=_is_legacy_wrapper, + _pathsep=pathsep, + ) + ########## + def _parse_item(item): + return pytest_item.parse_item( + item, + _parse_node_id=_parse_node_id, + _split_fspath=_split_fspath, + _get_location=_get_location, + ) + return _parse_item + + ################################## # tests @@ -303,7 +369,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( @@ -371,12 +436,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, @@ -386,17 +451,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, @@ -406,16 +471,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, @@ -425,17 +490,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, @@ -445,56 +510,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', ), )), ]) @@ -505,7 +570,6 @@ 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, @@ -525,25 +589,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', ), )), ]) @@ -554,9 +618,7 @@ 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, @@ -597,84 +659,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', ), )), ]) @@ -685,7 +747,6 @@ 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, @@ -705,26 +766,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', ), )), ]) @@ -735,7 +796,6 @@ 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, @@ -755,27 +815,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', ), )), ]) @@ -789,8 +849,9 @@ def test_windows(self): session.items = [ StubFunctionItem( stub, - nodeid='x/y/z/test_eggs.py::SpamTests::test_spam', + nodeid=relfile + '::SpamTests::test_spam', name='test_spam', + # wrong pathsep: location=('X/Y/Z/test_eggs.py', 12, 'SpamTests.test_spam'), fspath=testroot + '\\' + relfile, function=FakeFunc('test_spam'), @@ -798,11 +859,7 @@ def test_windows(self): ] collector = TestCollector(tests=discovered) if os.name != 'nt': - def normcase(path): - path = path.lower() - return path.replace('/', '\\') - collector.NORMCASE = normcase - collector.PATHSEP = '\\' + collector.parse_item = generate_parse_item('\\') collector.pytest_collection_finish(session) @@ -811,15 +868,15 @@ 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'), + ('./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=r'.\x\y\z\test_eggs.py::SpamTests::test_spam', + id='./x/y/z/test_eggs.py::SpamTests::test_spam', name='test_spam', path=TestPath( root=testroot, # not normalized @@ -829,7 +886,7 @@ def normcase(path): ), source=r'.\X\Y\Z\test_eggs.py:{}'.format(13), # not normalized markers=None, - parentid=r'.\x\y\z\test_eggs.py::SpamTests', + parentid='./x/y/z/test_eggs.py::SpamTests', ), )), ]) @@ -862,9 +919,9 @@ def test_mysterious_parens(self): 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', 'z', 'folder'), + ('./x/y', 'y', 'folder'), + ('./x', 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( @@ -872,11 +929,11 @@ def test_mysterious_parens(self): 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', ), @@ -891,7 +948,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( @@ -920,47 +976,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 76b28477df48..66195e66e579 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_discovery.py +++ b/pythonFiles/tests/testing_tools/adapter/test_discovery.py @@ -6,20 +6,20 @@ import os.path import unittest -from testing_tools.adapter.util import fix_path +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 class DiscoveredTestsTests(unittest.TestCase): def test_list(self): testroot = fix_path('/a/b/c') - relfileid = './test_spam.py' - relfile = fix_path(relfileid) + relfile = fix_path('./test_spam.py') tests = [ TestInfo( - id=relfileid[2:] + '::test_each[10-10]', + # missing "./": + id='test_spam.py::test_each[10-10]', name='test_each[10-10]', path=TestPath( root=testroot, @@ -29,10 +29,11 @@ def test_list(self): ), source='{}:{}'.format(relfile, 10), markers=None, - parentid=relfileid[2:] + '::test_each', + # missing "./": + parentid='test_spam.py::test_each', ), TestInfo( - id=relfileid[2:] + '::All::BasicTests::test_first', + id='test_spam.py::All::BasicTests::test_first', name='test_first', path=TestPath( root=testroot, @@ -42,17 +43,17 @@ def test_list(self): ), source='{}:{}'.format(relfile, 62), markers=None, - parentid=relfileid[2:] + '::All::BasicTests', + parentid='test_spam.py::All::BasicTests', ), ] allparents= [ - [(relfileid + '::test_each', 'test_each', 'function'), - (relfileid, 'test_spam.py', 'file'), + [('./test_spam.py::test_each', 'test_each', 'function'), + ('./test_spam.py', 'test_spam.py', 'file'), ('.', testroot, 'folder'), ], - [(relfileid + '::All::BasicTests', 'BasicTests', 'suite'), - (relfileid + '::All', 'All', 'suite'), - (relfileid, 'test_spam.py', 'file'), + [('./test_spam.py::All::BasicTests', 'BasicTests', 'suite'), + ('./test_spam.py::All', 'All', 'suite'), + ('./test_spam.py', 'test_spam.py', 'file'), ('.', testroot, 'folder'), ], ] @@ -83,7 +84,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', ), @@ -101,47 +102,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=relfileid, + 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=relfileid, + 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'), ], @@ -160,7 +164,7 @@ def test_parents(self): name=testroot, ), ParentInfo( - id=fix_path('./x'), + id='./x', kind='folder', name='x', root=testroot, @@ -168,70 +172,73 @@ def test_parents(self): parentid='.', ), ParentInfo( - id=fix_path('./x/y'), + id='./x/y', kind='folder', name='y', root=testroot, relpath=fix_path('./x/y'), - parentid=fix_path('./x'), + parentid='./x', ), ParentInfo( - id=fix_path('./x/y/z'), + id='./x/y/z', kind='folder', name='z', root=testroot, relpath=fix_path('./x/y/z'), - parentid=fix_path('./x/y'), + 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, - relpath=fix_path(relfileid), - 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='./' + test.id, + parentid='./test_spam.py') discovered = DiscoveredTests() before = list(discovered), discovered.parents @@ -250,7 +257,7 @@ def test_add_test_simple(self): name=testroot, ), ParentInfo( - id=relfileid, + id='./test_spam.py', kind='file', name=relfile, root=testroot, @@ -263,39 +270,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=relfileid1, + relfile=os.path.join('.', 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) alltests.extend([ TestInfo( - id=relfile2 + 'BasicTests::test_first', + id=relfile2 + '::BasicTests::test_first', name='test_first', path=TestPath( root=testroot2, - #relfile=relfile2, - relfile=relfileid2, + relfile=os.path.join('.', relfile2), func='BasicTests.test_first', ), source='{}:{}'.format(relfile2, 61), @@ -304,8 +310,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'), ], @@ -321,31 +328,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=relfileid1, + 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=relfileid2, + 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, [ @@ -356,12 +361,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, - relpath=fix_path(relfileid1), - parentid=os.path.dirname(relfileid1), + relpath=fix_relpath(relfile1), + parentid='.', ), # the secondroot ParentInfo( @@ -370,7 +375,7 @@ def test_multiroot(self): name=testroot2, ), ParentInfo( - id=fix_path('./w'), + id='./w', kind='folder', name='w', root=testroot2, @@ -378,19 +383,19 @@ def test_multiroot(self): parentid='.', ), ParentInfo( - id=relfileid2, + id='./w/test_eggs.py', kind='file', - name=os.path.basename(relfile2), + name='test_eggs.py', root=testroot2, - relpath=fix_path(relfileid2), - 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', ), ]) @@ -473,6 +478,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() @@ -482,7 +490,7 @@ def test_doctest(self): parents = discovered.parents self.maxDiff = None - self.assertEqual(tests, alltests) + self.assertEqual(tests, expected) self.assertEqual(parents, [ ParentInfo( id='.', @@ -490,7 +498,7 @@ def test_doctest(self): name=testroot, ), ParentInfo( - id=fix_path('./x'), + id='./x', kind='folder', name='x', root=testroot, @@ -498,36 +506,36 @@ def test_doctest(self): parentid='.', ), ParentInfo( - id=doctestfile, + id='./x/test_doctest.txt', kind='file', - name=os.path.basename(doctestfile), + name='test_doctest.txt', root=testroot, relpath=fix_path(doctestfile), - parentid=os.path.dirname(doctestfile), + parentid='./x', ), ParentInfo( - id=fix_path('./x/y'), + id='./x/y', kind='folder', name='y', root=testroot, relpath=fix_path('./x/y'), - parentid=fix_path('./x'), + parentid='./x', ), ParentInfo( - id=fix_path('./x/y/z'), + id='./x/y/z', kind='folder', name='z', root=testroot, relpath=fix_path('./x/y/z'), - parentid=fix_path('./x/y'), + 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, - relpath=fix_path(relfile), - parentid=os.path.dirname(relfile), + relpath=fix_relpath(relfile), + parentid='./x/y/z', ), ]) @@ -572,6 +580,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): @@ -580,7 +591,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='.', @@ -588,25 +599,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, - relpath=fix_path(relfile), - 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 a81165aa516d..81858e1cf12c 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_functional.py +++ b/pythonFiles/tests/testing_tools/adapter/test_functional.py @@ -267,48 +267,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'), + 'name': 'a', 'relpath': fix_path('./tests/A'), - 'parentid': fix_id('./tests'), + 'parentid': './tests', }, - {'id': fix_id('./tests/A/b'), + {'id': './tests/a/b', 'kind': 'folder', 'name': 'b', 'relpath': fix_path('./tests/A/b'), - 'parentid': fix_id('./tests/A'), + 'parentid': './tests/a', }, - {'id': fix_id('./tests/A/b/C'), + {'id': './tests/a/b/c', 'kind': 'folder', - 'name': fix_id('C'), + 'name': 'c', 'relpath': fix_path('./tests/A/b/C'), - 'parentid': fix_id('./tests/A/b'), + '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'), + 'name': 'test_spam.py', 'relpath': fix_path('./tests/A/b/C/test_Spam.py'), - 'parentid': fix_id('./tests/A/b/C'), + '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', }, ], }]) From 267ca68a8d28b616cbfa200899222921e58135c2 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 6 Aug 2019 13:10:26 -0600 Subject: [PATCH 06/21] For parents, use the unnormalized name. --- .../adapter/pytest/_pytest_item.py | 34 +++++++++++-------- .../adapter/pytest/test_discovery.py | 22 ++++++------ .../testing_tools/adapter/test_functional.py | 6 ++-- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py index 8f31fd2cb54a..8c9f516ca218 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py +++ b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py @@ -395,11 +395,13 @@ def _parse_node_id(testid, kind, *, return testid, parents, fileid, fullname, parameterized or '' -def _iter_nodes(nodeid, kind, *, - _normalize_node_id=(lambda *a: _normalize_node_id(*a)), +def _iter_nodes(testid, kind, *, + _normalize_test_id=(lambda *a: _normalize_test_id(*a)), ): """Yield (nodeid, name, kind) for the given node ID and its parents.""" - nodeid = _normalize_node_id(nodeid, kind) + nodeid, testid = _normalize_test_id(testid, kind) + if len(nodeid) > len(testid): + testid = './' + testid if kind == 'function' and nodeid.endswith(']'): funcid, sep, parameterized = nodeid.partition('[') @@ -431,36 +433,40 @@ def _iter_nodes(nodeid, kind, *, # Extract the file and folders. fileid = parentid + raw = testid[:len(fileid)] parentid, _, filename = fileid.rpartition('/') - yield (fileid, filename, 'file') + raw, name = raw[:len(parentid)], raw[-len(filename):] + yield (fileid, name, 'file') # We're guaranteed at least one (the test root). while '/' in parentid: folderid = parentid parentid, _, foldername = folderid.rpartition('/') - yield (folderid, foldername, 'folder') + 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, *, +def _normalize_test_id(testid, kind, *, _fix_fileid=fix_fileid, ): """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 + return testid, testid + orig = testid - fileid, sep, remainder = nodeid.partition('::') + fileid, sep, remainder = testid.partition('::') # pytest works fine even if we normalize the filename. - nodeid = _fix_fileid(fileid) + sep + remainder + testid = _fix_fileid(fileid) + sep + remainder - if not nodeid.startswith('./'): # Absolute "paths" not expected. + if not testid.startswith('./'): # Absolute "paths" not expected. raise should_never_reach_here( - nodeid, + testid, ) - return nodeid + return testid, orig def _get_item_kind(item): diff --git a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py index 9a0c3f061473..e612ef2debac 100644 --- a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py +++ b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py @@ -185,15 +185,15 @@ def _fix_fileid(*args): _normcase=normcase, _pathsep=pathsep, ) - def _normalize_node_id(*args): - return pytest_item._normalize_node_id( + def _normalize_test_id(*args): + return pytest_item._normalize_test_id( *args, _fix_fileid=_fix_fileid, ) def _iter_nodes(*args): return pytest_item._iter_nodes( *args, - _normalize_node_id=_normalize_node_id, + _normalize_test_id=_normalize_test_id, ) def _parse_node_id(*args): return pytest_item._parse_node_id( @@ -845,14 +845,14 @@ def test_windows(self): discovered = StubDiscoveredTests(stub) session = StubPytestSession(stub) testroot = r'C:\A\B\C' - relfile = r'X\Y\Z\test_eggs.py' + relfile = r'X\Y\Z\test_Eggs.py' session.items = [ StubFunctionItem( stub, nodeid=relfile + '::SpamTests::test_spam', name='test_spam', # wrong pathsep: - location=('X/Y/Z/test_eggs.py', 12, 'SpamTests.test_spam'), + location=('X/Y/Z/test_Eggs.py', 12, 'SpamTests.test_spam'), fspath=testroot + '\\' + relfile, function=FakeFunc('test_spam'), ), @@ -869,10 +869,10 @@ def test_windows(self): ('discovered.add_test', None, dict( parents=[ ('./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'), + ('./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( @@ -880,11 +880,11 @@ def test_windows(self): 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='./x/y/z/test_eggs.py::SpamTests', ), diff --git a/pythonFiles/tests/testing_tools/adapter/test_functional.py b/pythonFiles/tests/testing_tools/adapter/test_functional.py index 81858e1cf12c..a502cd9cb7dc 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_functional.py +++ b/pythonFiles/tests/testing_tools/adapter/test_functional.py @@ -279,7 +279,7 @@ def test_discover_normcase(self): }, {'id': './tests/a', 'kind': 'folder', - 'name': 'a', + 'name': 'A', 'relpath': fix_path('./tests/A'), 'parentid': './tests', }, @@ -291,13 +291,13 @@ def test_discover_normcase(self): }, {'id': './tests/a/b/c', 'kind': 'folder', - 'name': 'c', + 'name': 'C', 'relpath': fix_path('./tests/A/b/C'), 'parentid': './tests/a/b', }, {'id': './tests/a/b/c/test_spam.py', 'kind': 'file', - 'name': 'test_spam.py', + 'name': 'test_Spam.py', 'relpath': fix_path('./tests/A/b/C/test_Spam.py'), 'parentid': './tests/a/b/c', }, From beee582d2f264bab9e3511550c5c3294d53e8ebd Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 6 Aug 2019 13:17:18 -0600 Subject: [PATCH 07/21] Fix Python 2 support. --- .../testing_tools/adapter/discovery.py | 2 +- .../adapter/pytest/_pytest_item.py | 16 +++---- pythonFiles/testing_tools/adapter/util.py | 6 +-- .../adapter/pytest/test_discovery.py | 48 +++++++++++++------ 4 files changed, 45 insertions(+), 27 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/discovery.py b/pythonFiles/testing_tools/adapter/discovery.py index d3bdb809d073..88f0dd89f84d 100644 --- a/pythonFiles/testing_tools/adapter/discovery.py +++ b/pythonFiles/testing_tools/adapter/discovery.py @@ -20,7 +20,7 @@ """, re.VERBOSE) -def fix_nodeid(nodeid, rootdir=None, *, +def fix_nodeid(nodeid, rootdir=None, #*, _fix_fileid=fix_fileid, _pathsep=os.path.sep, ): diff --git a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py index 8c9f516ca218..68df7b1c6b90 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py +++ b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py @@ -137,7 +137,7 @@ def should_never_reach_here(node, **extra): return exc -def parse_item(item, *, +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)), @@ -218,7 +218,7 @@ def parse_item(item, *, return test, parents -def _split_fspath(fspath, fileid, item, *, +def _split_fspath(fspath, fileid, item, #*, _fix_fileid=fix_fileid, ): """Return (testroot, relfile) for the given fspath. @@ -239,7 +239,7 @@ def _split_fspath(fspath, fileid, item, *, return testroot, relfile -def _get_location(item, testroot, relfile, *, +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)), @@ -281,7 +281,7 @@ def _get_location(item, testroot, relfile, *, return location, fullname -def _matches_relfile(srcfile, testroot, relfile, *, +def _matches_relfile(srcfile, testroot, relfile, #*, _normcase=os.path.normcase, _pathsep=os.path.sep, ): @@ -299,7 +299,7 @@ def _matches_relfile(srcfile, testroot, relfile, *, return False -def _is_legacy_wrapper(srcfile, *, +def _is_legacy_wrapper(srcfile, #*, _pathsep=os.path.sep, _pyversion=sys.version_info, ): @@ -342,7 +342,7 @@ def _unwrap_decorator(func): return filename, lineno -def _parse_node_id(testid, kind, *, +def _parse_node_id(testid, kind, #*, _iter_nodes=(lambda *a: _iter_nodes(*a)), ): """Return the components of the given node ID, in heirarchical order.""" @@ -395,7 +395,7 @@ def _parse_node_id(testid, kind, *, return testid, parents, fileid, fullname, parameterized or '' -def _iter_nodes(testid, kind, *, +def _iter_nodes(testid, kind, #*, _normalize_test_id=(lambda *a: _normalize_test_id(*a)), ): """Yield (nodeid, name, kind) for the given node ID and its parents.""" @@ -448,7 +448,7 @@ def _iter_nodes(testid, kind, *, yield (parentid, testroot, 'folder') -def _normalize_test_id(testid, kind, *, +def _normalize_test_id(testid, kind, #*, _fix_fileid=fix_fileid, ): """Return the canonical form for the given node ID.""" diff --git a/pythonFiles/testing_tools/adapter/util.py b/pythonFiles/testing_tools/adapter/util.py index b61a776cfec3..33dbd927c7d4 100644 --- a/pythonFiles/testing_tools/adapter/util.py +++ b/pythonFiles/testing_tools/adapter/util.py @@ -44,13 +44,13 @@ def group_attr_names(attrnames): ############################# # file paths -def fix_path(path, *, +def fix_path(path, #*, _pathsep=os.path.sep): """Return a platform-appropriate path for the given path.""" return path.replace('/', _pathsep) -def fix_relpath(path, *, +def fix_relpath(path, #*, _fix_path=fix_path, _path_isabs=os.path.isabs, _pathsep=os.path.sep @@ -63,7 +63,7 @@ def fix_relpath(path, *, return path -def fix_fileid(fileid, rootdir=None, *, +def fix_fileid(fileid, rootdir=None, #*, _normcase=os.path.normcase, _path_isabs=os.path.isabs, _pathsep=os.path.sep, diff --git a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py index e612ef2debac..e0908478a93d 100644 --- a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py +++ b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py @@ -182,56 +182,74 @@ def normcase(path): def _fix_fileid(*args): return fix_fileid( *args, - _normcase=normcase, - _pathsep=pathsep, + **dict( + _normcase=normcase, + _pathsep=pathsep, + ) ) def _normalize_test_id(*args): return pytest_item._normalize_test_id( *args, - _fix_fileid=_fix_fileid, + **dict( + _fix_fileid=_fix_fileid, + ) ) def _iter_nodes(*args): return pytest_item._iter_nodes( *args, - _normalize_test_id=_normalize_test_id, + **dict( + _normalize_test_id=_normalize_test_id, + ) ) def _parse_node_id(*args): return pytest_item._parse_node_id( *args, - _iter_nodes=_iter_nodes, + **dict( + _iter_nodes=_iter_nodes, + ) ) ########## def _split_fspath(*args): return pytest_item._split_fspath( *args, - _fix_fileid=_fix_fileid, + **dict( + _fix_fileid=_fix_fileid, + ) ) ########## def _matches_relfile(*args): return pytest_item._matches_relfile( *args, - _normcase=normcase, - _pathsep=pathsep, + **dict( + _normcase=normcase, + _pathsep=pathsep, + ) ) def _is_legacy_wrapper(*args): return pytest_item._is_legacy_wrapper( *args, - _pathsep=pathsep, + **dict( + _pathsep=pathsep, + ) ) def _get_location(*args): return pytest_item._get_location( *args, - _matches_relfile=_matches_relfile, - _is_legacy_wrapper=_is_legacy_wrapper, - _pathsep=pathsep, + **dict( + _matches_relfile=_matches_relfile, + _is_legacy_wrapper=_is_legacy_wrapper, + _pathsep=pathsep, + ) ) ########## def _parse_item(item): return pytest_item.parse_item( item, - _parse_node_id=_parse_node_id, - _split_fspath=_split_fspath, - _get_location=_get_location, + **dict( + _parse_node_id=_parse_node_id, + _split_fspath=_split_fspath, + _get_location=_get_location, + ) ) return _parse_item From 11fbde1a953523584e7ef87c221eb4087564b86f Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 6 Aug 2019 15:38:31 -0600 Subject: [PATCH 08/21] Use "relpath" to generate "fullpath" for test files. --- .../common/services/discoveredTestParser.ts | 374 ++++++++++++++---- src/client/testing/common/services/types.ts | 32 +- 2 files changed, 311 insertions(+), 95 deletions(-) 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'); From 020a5dcd23be83b2876661de8bdd2fc30af03388 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 6 Aug 2019 16:41:06 -0600 Subject: [PATCH 09/21] Preserve the original test ID. --- .../adapter/pytest/_pytest_item.py | 38 ++++++++++++------- pythonFiles/testing_tools/adapter/report.py | 2 + pythonFiles/testing_tools/adapter/util.py | 23 +++++++---- .../adapter/pytest/test_discovery.py | 19 ++++++---- .../testing_tools/adapter/test_functional.py | 18 ++++----- 5 files changed, 61 insertions(+), 39 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py index 68df7b1c6b90..ec6e86e12424 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py +++ b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py @@ -219,16 +219,17 @@ def parse_item(item, #*, def _split_fspath(fspath, fileid, item, #*, - _fix_fileid=fix_fileid, + _normcase=os.path.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 _fix_fileid(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=fspath, @@ -397,11 +398,13 @@ def _parse_node_id(testid, kind, #*, def _iter_nodes(testid, kind, #*, _normalize_test_id=(lambda *a: _normalize_test_id(*a)), + _normcase=os.path.normcase, + _pathsep=os.path.sep, ): """Yield (nodeid, name, kind) for the given node ID and its parents.""" nodeid, testid = _normalize_test_id(testid, kind) if len(nodeid) > len(testid): - testid = './' + testid + testid = '.' + _pathsep + testid if kind == 'function' and nodeid.endswith(']'): funcid, sep, parameterized = nodeid.partition('[') @@ -434,13 +437,15 @@ def _iter_nodes(testid, kind, #*, # Extract the file and folders. fileid = parentid raw = testid[:len(fileid)] - parentid, _, filename = fileid.rpartition('/') - raw, name = raw[:len(parentid)], raw[-len(filename):] + _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 '/' in parentid: + while _pathsep in _normcase(parentid): folderid = parentid - parentid, _, foldername = folderid.rpartition('/') + _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(). @@ -450,6 +455,7 @@ def _iter_nodes(testid, kind, #*, def _normalize_test_id(testid, kind, #*, _fix_fileid=fix_fileid, + _pathsep=os.path.sep, ): """Return the canonical form for the given node ID.""" while '::()::' in testid: @@ -458,14 +464,18 @@ def _normalize_test_id(testid, kind, #*, 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. fileid, sep, remainder = testid.partition('::') - # pytest works fine even if we normalize the filename. - testid = _fix_fileid(fileid) + sep + remainder - - if not testid.startswith('./'): # Absolute "paths" not expected. + fileid = _fix_fileid(fileid) + if not fileid.startswith('.' + _pathsep): # Absolute "paths" not expected. raise should_never_reach_here( testid, + fileid=fileid, ) + testid = fileid + sep + remainder + return testid, orig diff --git a/pythonFiles/testing_tools/adapter/report.py b/pythonFiles/testing_tools/adapter/report.py index e53320aa11eb..395a30d89c58 100644 --- a/pythonFiles/testing_tools/adapter/report.py +++ b/pythonFiles/testing_tools/adapter/report.py @@ -36,6 +36,7 @@ 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, @@ -47,6 +48,7 @@ def report_discovered(tests, parents, pretty=False, simple=False, # 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 33dbd927c7d4..a1a36fda8758 100644 --- a/pythonFiles/testing_tools/adapter/util.py +++ b/pythonFiles/testing_tools/adapter/util.py @@ -64,34 +64,41 @@ def fix_relpath(path, #*, def fix_fileid(fileid, rootdir=None, #*, + normalize=False, _normcase=os.path.normcase, _path_isabs=os.path.isabs, _pathsep=os.path.sep, ): - """Return a "/" separated file ID ("./"-prefixed) for the given value. + """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 fileid == '.': + if not fileid or fileid == '.': return fileid - _fileid = _normcase(fileid) + relprefix = '.' + _pathsep + + normalized = _normcase(fileid) + if normalized.startswith(relprefix): + return fileid + isabs = False - if _path_isabs(_fileid): + if _path_isabs(normalized): isabs = True if rootdir is not None: rootdir = _normcase(rootdir) if not rootdir.endswith(_pathsep): rootdir += _pathsep - if _fileid.startswith(rootdir): + if normalized.startswith(rootdir): # This assumes pathsep has length 1. fileid = fileid[len(rootdir):] isabs = False - fileid = fileid.replace(_pathsep, '/').lower() + if normalize: + fileid = fileid.replace(_pathsep, '/').lower() + relprefix = './' if not isabs: - if not fileid.startswith('./'): - fileid = './' + fileid + fileid = relprefix + fileid return fileid diff --git a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py index e0908478a93d..8906b9a73c25 100644 --- a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py +++ b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py @@ -192,6 +192,7 @@ def _normalize_test_id(*args): *args, **dict( _fix_fileid=_fix_fileid, + _pathsep=pathsep, ) ) def _iter_nodes(*args): @@ -199,6 +200,8 @@ def _iter_nodes(*args): *args, **dict( _normalize_test_id=_normalize_test_id, + _normcase=normcase, + _pathsep=pathsep, ) ) def _parse_node_id(*args): @@ -213,7 +216,7 @@ def _split_fspath(*args): return pytest_item._split_fspath( *args, **dict( - _fix_fileid=_fix_fileid, + _normcase=normcase, ) ) ########## @@ -886,15 +889,15 @@ def test_windows(self): ('discovered.reset', None, None), ('discovered.add_test', None, dict( parents=[ - ('./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'), + (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='./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 @@ -904,7 +907,7 @@ def test_windows(self): ), source=r'.\X\Y\Z\test_Eggs.py:13', # not normalized markers=None, - parentid='./x/y/z/test_eggs.py::SpamTests', + parentid=r'.\X\Y\Z\test_Eggs.py::SpamTests', ), )), ]) diff --git a/pythonFiles/tests/testing_tools/adapter/test_functional.py b/pythonFiles/tests/testing_tools/adapter/test_functional.py index a502cd9cb7dc..d0b083326b6a 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_functional.py +++ b/pythonFiles/tests/testing_tools/adapter/test_functional.py @@ -277,37 +277,37 @@ def test_discover_normcase(self): 'relpath': fix_path('./tests'), 'parentid': '.', }, - {'id': './tests/a', + {'id': './tests/A', 'kind': 'folder', 'name': 'A', 'relpath': fix_path('./tests/A'), 'parentid': './tests', }, - {'id': './tests/a/b', + {'id': './tests/A/b', 'kind': 'folder', 'name': 'b', 'relpath': fix_path('./tests/A/b'), - 'parentid': './tests/a', + 'parentid': './tests/A', }, - {'id': './tests/a/b/c', + {'id': './tests/A/b/C', 'kind': 'folder', 'name': 'C', 'relpath': fix_path('./tests/A/b/C'), - 'parentid': './tests/a/b', + 'parentid': './tests/A/b', }, - {'id': './tests/a/b/c/test_spam.py', + {'id': './tests/A/b/C/test_Spam.py', 'kind': 'file', 'name': 'test_Spam.py', 'relpath': fix_path('./tests/A/b/C/test_Spam.py'), - 'parentid': './tests/a/b/c', + 'parentid': './tests/A/b/C', }, ], 'tests': [ - {'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': './tests/a/b/c/test_spam.py', + 'parentid': './tests/A/b/C/test_Spam.py', }, ], }]) From 9a76ec6630fae99f6cf6cd20482124b1ad66e8fe Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 7 Aug 2019 09:13:55 -0600 Subject: [PATCH 10/21] Fix tests on Windows. --- .../testing_tools/adapter/discovery.py | 11 +- .../adapter/pytest/_pytest_item.py | 19 +- pythonFiles/testing_tools/adapter/util.py | 23 +- .../adapter/pytest/test_discovery.py | 231 +++++++++--------- .../testing_tools/adapter/test_discovery.py | 114 ++++----- .../testing_tools/adapter/test_functional.py | 33 ++- .../testing_tools/adapter/test_report.py | 11 +- 7 files changed, 233 insertions(+), 209 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/discovery.py b/pythonFiles/testing_tools/adapter/discovery.py index 88f0dd89f84d..f60689cac81a 100644 --- a/pythonFiles/testing_tools/adapter/discovery.py +++ b/pythonFiles/testing_tools/adapter/discovery.py @@ -3,10 +3,9 @@ from __future__ import absolute_import, print_function -import os.path import re -from .util import fix_fileid +from .util import fix_fileid, PATH_SEP, DIRNAME from .info import ParentInfo @@ -22,7 +21,7 @@ def fix_nodeid(nodeid, rootdir=None, #*, _fix_fileid=fix_fileid, - _pathsep=os.path.sep, + _pathsep=PATH_SEP, ): if not nodeid: raise ValueError('missing nodeid') @@ -75,7 +74,9 @@ def add_test(self, test, parents): ) self._tests.append(test) - def _ensure_parent(self, path, parents): + def _ensure_parent(self, path, parents, #*, + _dirname=DIRNAME, + ): rootdir = path.root relpath = path.relfile @@ -89,7 +90,7 @@ def _ensure_parent(self, path, parents): parentid = fix_nodeid(parentid, rootdir) if kind in ('folder', 'file'): info = ParentInfo(nodeid, kind, name, rootdir, relpath, parentid) - relpath = os.path.dirname(relpath) + relpath = _dirname(relpath) else: info = ParentInfo(nodeid, kind, name, rootdir, None, parentid) self._parents[(rootdir, nodeid)] = info diff --git a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py index ec6e86e12424..eb69946626c1 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py +++ b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py @@ -93,7 +93,6 @@ from __future__ import absolute_import, print_function -import os.path import sys import pytest @@ -101,7 +100,7 @@ import _pytest.unittest from ..info import TestInfo, TestPath -from ..util import fix_fileid +from ..util import fix_fileid, PATH_SEP, NORMCASE def should_never_reach_here(node, **extra): @@ -219,7 +218,7 @@ def parse_item(item, #*, def _split_fspath(fspath, fileid, item, #*, - _normcase=os.path.normcase, + _normcase=NORMCASE, ): """Return (testroot, relfile) for the given fspath. @@ -244,7 +243,7 @@ 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=os.path.sep, + _pathsep=PATH_SEP, ): """Return (loc str, fullname) for the given item.""" srcfile, lineno, fullname = item.location @@ -283,8 +282,8 @@ def _get_location(item, testroot, relfile, #*, def _matches_relfile(srcfile, testroot, relfile, #*, - _normcase=os.path.normcase, - _pathsep=os.path.sep, + _normcase=NORMCASE, + _pathsep=PATH_SEP, ): """Return True if "srcfile" matches the given relfile.""" testroot = _normcase(testroot) @@ -301,7 +300,7 @@ def _matches_relfile(srcfile, testroot, relfile, #*, def _is_legacy_wrapper(srcfile, #*, - _pathsep=os.path.sep, + _pathsep=PATH_SEP, _pyversion=sys.version_info, ): """Return True if the test might be wrapped. @@ -398,8 +397,8 @@ def _parse_node_id(testid, kind, #*, def _iter_nodes(testid, kind, #*, _normalize_test_id=(lambda *a: _normalize_test_id(*a)), - _normcase=os.path.normcase, - _pathsep=os.path.sep, + _normcase=NORMCASE, + _pathsep=PATH_SEP, ): """Yield (nodeid, name, kind) for the given node ID and its parents.""" nodeid, testid = _normalize_test_id(testid, kind) @@ -455,7 +454,7 @@ def _iter_nodes(testid, kind, #*, def _normalize_test_id(testid, kind, #*, _fix_fileid=fix_fileid, - _pathsep=os.path.sep, + _pathsep=PATH_SEP, ): """Return the canonical form for the given node ID.""" while '::()::' in testid: diff --git a/pythonFiles/testing_tools/adapter/util.py b/pythonFiles/testing_tools/adapter/util.py index a1a36fda8758..f864341c991a 100644 --- a/pythonFiles/testing_tools/adapter/util.py +++ b/pythonFiles/testing_tools/adapter/util.py @@ -44,16 +44,27 @@ def group_attr_names(attrnames): ############################# # 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=os.path.sep): + _pathsep=PATH_SEP): """Return a platform-appropriate path for the given path.""" return path.replace('/', _pathsep) def fix_relpath(path, #*, _fix_path=fix_path, - _path_isabs=os.path.isabs, - _pathsep=os.path.sep + _path_isabs=IS_ABS_PATH, + _pathsep=PATH_SEP ): """Return a ./-prefixed, platform-appropriate path for the given path.""" path = _fix_path(path) @@ -65,9 +76,9 @@ def fix_relpath(path, #*, def fix_fileid(fileid, rootdir=None, #*, normalize=False, - _normcase=os.path.normcase, - _path_isabs=os.path.isabs, - _pathsep=os.path.sep, + _normcase=NORMCASE, + _path_isabs=IS_ABS_PATH, + _pathsep=PATH_SEP, ): """Return a pathsep-separated file ID ("./"-prefixed) for the given value. diff --git a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py index 8906b9a73c25..cb29acd21d2b 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,7 +15,9 @@ import _pytest.doctest from ....util import Stub, StubProxy -from testing_tools.adapter.util import fix_path, fix_relpath, fix_fileid +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 @@ -397,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( @@ -405,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( @@ -413,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( @@ -421,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( @@ -429,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( @@ -437,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 @@ -457,12 +458,12 @@ def test_modifyitems(self): ('discovered.reset', None, None), ('discovered.add_test', None, dict( parents=[ - ('./test_spam.py::SpamTests', 'SpamTests', 'suite'), - ('./test_spam.py', 'test_spam.py', 'file'), + (fix_path('./test_spam.py::SpamTests'), 'SpamTests', 'suite'), + (fix_path('./test_spam.py'), 'test_spam.py', 'file'), ('.', testroot, 'folder'), ], test=TestInfo( - id='./test_spam.py::SpamTests::test_one', + id=fix_path('./test_spam.py::SpamTests::test_one'), name='test_one', path=TestPath( root=testroot, @@ -472,17 +473,17 @@ def test_modifyitems(self): ), source='{}:{}'.format(relfile1, 13), markers=None, - parentid='./test_spam.py::SpamTests', + parentid=fix_path('./test_spam.py::SpamTests'), ), )), ('discovered.add_test', None, dict( parents=[ - ('./test_spam.py::SpamTests', 'SpamTests', 'suite'), - ('./test_spam.py', 'test_spam.py', 'file'), + (fix_path('./test_spam.py::SpamTests'), 'SpamTests', 'suite'), + (fix_path('./test_spam.py'), 'test_spam.py', 'file'), ('.', testroot, 'folder'), ], test=TestInfo( - id='./test_spam.py::SpamTests::test_other', + id=fix_path('./test_spam.py::SpamTests::test_other'), name='test_other', path=TestPath( root=testroot, @@ -492,16 +493,16 @@ def test_modifyitems(self): ), source='{}:{}'.format(relfile1, 20), markers=None, - parentid='./test_spam.py::SpamTests', + parentid=fix_path('./test_spam.py::SpamTests'), ), )), ('discovered.add_test', None, dict( parents=[ - ('./test_spam.py', 'test_spam.py', 'file'), + (fix_path('./test_spam.py'), 'test_spam.py', 'file'), ('.', testroot, 'folder'), ], test=TestInfo( - id='./test_spam.py::test_all', + id=fix_path('./test_spam.py::test_all'), name='test_all', path=TestPath( root=testroot, @@ -511,17 +512,17 @@ def test_modifyitems(self): ), source='{}:{}'.format(relfile1, 145), markers=None, - parentid='./test_spam.py', + parentid=fix_path('./test_spam.py'), ), )), ('discovered.add_test', None, dict( parents=[ - ('./test_spam.py::test_each', 'test_each', 'function'), - ('./test_spam.py', 'test_spam.py', 'file'), + (fix_path('./test_spam.py::test_each'), 'test_each', 'function'), + (fix_path('./test_spam.py'), 'test_spam.py', 'file'), ('.', testroot, 'folder'), ], test=TestInfo( - id='./test_spam.py::test_each[10-10]', + id=fix_path('./test_spam.py::test_each[10-10]'), name='test_each[10-10]', path=TestPath( root=testroot, @@ -531,21 +532,21 @@ def test_modifyitems(self): ), source='{}:{}'.format(relfile1, 274), markers=None, - parentid='./test_spam.py::test_each', + parentid=fix_path('./test_spam.py::test_each'), ), )), ('discovered.add_test', None, dict( parents=[ - ('./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'), + (fix_path('./x/y/z/test_eggs.py::All::BasicTests'), 'BasicTests', 'suite'), + (fix_path('./x/y/z/test_eggs.py::All'), 'All', 'suite'), + (fix_path('./x/y/z/test_eggs.py'), 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id='./x/y/z/test_eggs.py::All::BasicTests::test_first', + id=fix_path('./x/y/z/test_eggs.py::All::BasicTests::test_first'), name='test_first', path=TestPath( root=testroot, @@ -555,22 +556,22 @@ def test_modifyitems(self): ), source='{}:{}'.format(fix_relpath(relfile2), 32), markers=None, - parentid='./x/y/z/test_eggs.py::All::BasicTests', + parentid=fix_path('./x/y/z/test_eggs.py::All::BasicTests'), ), )), ('discovered.add_test', None, dict( parents=[ - ('./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'), + (fix_path('./x/y/z/test_eggs.py::All::BasicTests::test_each'), 'test_each', 'function'), + (fix_path('./x/y/z/test_eggs.py::All::BasicTests'), 'BasicTests', 'suite'), + (fix_path('./x/y/z/test_eggs.py::All'), 'All', 'suite'), + (fix_path('./x/y/z/test_eggs.py'), 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id='./x/y/z/test_eggs.py::All::BasicTests::test_each[1+2-3]', + id=fix_path('./x/y/z/test_eggs.py::All::BasicTests::test_each[1+2-3]'), name='test_each[1+2-3]', path=TestPath( root=testroot, @@ -580,7 +581,7 @@ def test_modifyitems(self): ), source='{}:{}'.format(fix_relpath(relfile2), 63), markers=['expected-failure', 'skip', 'skip-if'], - parentid='./x/y/z/test_eggs.py::All::BasicTests::test_each', + parentid=fix_path('./x/y/z/test_eggs.py::All::BasicTests::test_each'), ), )), ]) @@ -597,7 +598,7 @@ def test_finish(self): 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'), ), ] @@ -610,15 +611,15 @@ def test_finish(self): ('discovered.reset', None, None), ('discovered.add_test', None, dict( parents=[ - ('./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'), + (fix_path('./x/y/z/test_eggs.py::SpamTests'), 'SpamTests', 'suite'), + (fix_path('./x/y/z/test_eggs.py'), 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id='./x/y/z/test_eggs.py::SpamTests::test_spam', + id=fix_path('./x/y/z/test_eggs.py::SpamTests::test_spam'), name='test_spam', path=TestPath( root=testroot, @@ -628,7 +629,7 @@ def test_finish(self): ), source='{}:{}'.format(fix_relpath(relfile), 13), markers=None, - parentid='./x/y/z/test_eggs.py::SpamTests', + parentid=fix_path('./x/y/z/test_eggs.py::SpamTests'), ), )), ]) @@ -646,7 +647,7 @@ def test_doctest(self): 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( @@ -654,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) @@ -680,12 +681,12 @@ def test_doctest(self): ('discovered.reset', None, None), ('discovered.add_test', None, dict( parents=[ - ('./x/test_doctest.txt', 'test_doctest.txt', 'file'), - ('./x', 'x', 'folder'), + (fix_path('./x/test_doctest.txt'), 'test_doctest.txt', 'file'), + (fix_path('./x'), 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id='./x/test_doctest.txt::test_doctest.txt', + id=fix_path('./x/test_doctest.txt::test_doctest.txt'), name='test_doctest.txt', path=TestPath( root=testroot, @@ -694,19 +695,19 @@ def test_doctest(self): ), source='{}:{}'.format(fix_relpath(doctestfile), 1), markers=[], - parentid='./x/test_doctest.txt', + parentid=fix_path('./x/test_doctest.txt'), ), )), ('discovered.add_test', None, dict( parents=[ - ('./x/y/z/test_eggs.py', 'test_eggs.py', 'file'), - ('./x/y/z', 'z', 'folder'), - ('./x/y', 'y', 'folder'), - ('./x', 'x', 'folder'), + (fix_path('./x/y/z/test_eggs.py'), 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id='./x/y/z/test_eggs.py::test_eggs', + id=fix_path('./x/y/z/test_eggs.py::test_eggs'), name='test_eggs', path=TestPath( root=testroot, @@ -715,19 +716,19 @@ def test_doctest(self): ), source='{}:{}'.format(fix_relpath(relfile), 1), markers=[], - parentid='./x/y/z/test_eggs.py', + parentid=fix_path('./x/y/z/test_eggs.py'), ), )), ('discovered.add_test', None, dict( parents=[ - ('./x/y/z/test_eggs.py', 'test_eggs.py', 'file'), - ('./x/y/z', 'z', 'folder'), - ('./x/y', 'y', 'folder'), - ('./x', 'x', 'folder'), + (fix_path('./x/y/z/test_eggs.py'), 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id='./x/y/z/test_eggs.py::test_eggs.TestSpam', + id=fix_path('./x/y/z/test_eggs.py::test_eggs.TestSpam'), name='test_eggs.TestSpam', path=TestPath( root=testroot, @@ -736,19 +737,19 @@ def test_doctest(self): ), source='{}:{}'.format(fix_relpath(relfile), 13), markers=[], - parentid='./x/y/z/test_eggs.py', + parentid=fix_path('./x/y/z/test_eggs.py'), ), )), ('discovered.add_test', None, dict( parents=[ - ('./x/y/z/test_eggs.py', 'test_eggs.py', 'file'), - ('./x/y/z', 'z', 'folder'), - ('./x/y', 'y', 'folder'), - ('./x', 'x', 'folder'), + (fix_path('./x/y/z/test_eggs.py'), 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id='./x/y/z/test_eggs.py::test_eggs.TestSpam.TestEggs', + id=fix_path('./x/y/z/test_eggs.py::test_eggs.TestSpam.TestEggs'), name='test_eggs.TestSpam.TestEggs', path=TestPath( root=testroot, @@ -757,7 +758,7 @@ def test_doctest(self): ), source='{}:{}'.format(fix_relpath(relfile), 28), markers=[], - parentid='./x/y/z/test_eggs.py', + parentid=fix_path('./x/y/z/test_eggs.py'), ), )), ]) @@ -774,7 +775,7 @@ def test_nested_brackets(self): 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'), ), ] @@ -787,16 +788,16 @@ def test_nested_brackets(self): ('discovered.reset', None, None), ('discovered.add_test', None, dict( parents=[ - ('./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'), + (fix_path('./x/y/z/test_eggs.py::SpamTests::test_spam'), 'test_spam', 'function'), + (fix_path('./x/y/z/test_eggs.py::SpamTests'), 'SpamTests', 'suite'), + (fix_path('./x/y/z/test_eggs.py'), 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id='./x/y/z/test_eggs.py::SpamTests::test_spam[a-[b]-c]', + id=fix_path('./x/y/z/test_eggs.py::SpamTests::test_spam[a-[b]-c]'), name='test_spam[a-[b]-c]', path=TestPath( root=testroot, @@ -806,7 +807,7 @@ def test_nested_brackets(self): ), source='{}:{}'.format(fix_relpath(relfile), 13), markers=None, - parentid='./x/y/z/test_eggs.py::SpamTests::test_spam', + parentid=fix_path('./x/y/z/test_eggs.py::SpamTests::test_spam'), ), )), ]) @@ -823,7 +824,7 @@ def test_nested_suite(self): 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'), ), ] @@ -836,17 +837,17 @@ def test_nested_suite(self): ('discovered.reset', None, None), ('discovered.add_test', None, dict( parents=[ - ('./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'), + (fix_path('./x/y/z/test_eggs.py::SpamTests::Ham::Eggs'), 'Eggs', 'suite'), + (fix_path('./x/y/z/test_eggs.py::SpamTests::Ham'), 'Ham', 'suite'), + (fix_path('./x/y/z/test_eggs.py::SpamTests'), 'SpamTests', 'suite'), + (fix_path('./x/y/z/test_eggs.py'), 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id='./x/y/z/test_eggs.py::SpamTests::Ham::Eggs::test_spam', + id=fix_path('./x/y/z/test_eggs.py::SpamTests::Ham::Eggs::test_spam'), name='test_spam', path=TestPath( root=testroot, @@ -856,7 +857,7 @@ def test_nested_suite(self): ), source='{}:{}'.format(fix_relpath(relfile), 13), markers=None, - parentid='./x/y/z/test_eggs.py::SpamTests::Ham::Eggs', + parentid=fix_path('./x/y/z/test_eggs.py::SpamTests::Ham::Eggs'), ), )), ]) @@ -879,7 +880,7 @@ def test_windows(self): ), ] collector = TestCollector(tests=discovered) - if os.name != 'nt': + if OS_NAME != 'nt': collector.parse_item = generate_parse_item('\\') collector.pytest_collection_finish(session) @@ -918,14 +919,14 @@ 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) + relfileid = fix_relpath(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'), ), ] @@ -940,9 +941,9 @@ def test_mysterious_parens(self): parents=[ (relfileid + '::SpamTests', 'SpamTests', 'suite'), (relfileid, 'test_eggs.py', 'file'), - ('./x/y/z', 'z', 'folder'), - ('./x/y', 'y', 'folder'), - ('./x', 'x', 'folder'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( @@ -976,7 +977,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( @@ -984,7 +985,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'), ), ] @@ -997,15 +998,15 @@ def test_imported_test(self): ('discovered.reset', None, None), ('discovered.add_test', None, dict( parents=[ - ('./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'), + (fix_path('./x/y/z/test_eggs.py::SpamTests'), 'SpamTests', 'suite'), + (fix_path('./x/y/z/test_eggs.py'), 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id='./x/y/z/test_eggs.py::SpamTests::test_spam', + id=fix_path('./x/y/z/test_eggs.py::SpamTests::test_spam'), name='test_spam', path=TestPath( root=testroot, @@ -1015,19 +1016,19 @@ def test_imported_test(self): ), source='{}:{}'.format(fix_relpath(srcfile), 13), markers=None, - parentid='./x/y/z/test_eggs.py::SpamTests', + parentid=fix_path('./x/y/z/test_eggs.py::SpamTests'), ), )), ('discovered.add_test', None, dict( parents=[ - ('./x/y/z/test_eggs.py', 'test_eggs.py', 'file'), - ('./x/y/z', 'z', 'folder'), - ('./x/y', 'y', 'folder'), - ('./x', 'x', 'folder'), + (fix_path('./x/y/z/test_eggs.py'), 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), ('.', testroot, 'folder'), ], test=TestInfo( - id='./x/y/z/test_eggs.py::test_ham', + id=fix_path('./x/y/z/test_eggs.py::test_ham'), name='test_ham', path=TestPath( root=testroot, @@ -1037,7 +1038,7 @@ def test_imported_test(self): ), source='{}:{}'.format(fix_relpath(srcfile), 4), markers=None, - parentid='./x/y/z/test_eggs.py', + parentid=fix_path('./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 66195e66e579..9f00566f9a85 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_discovery.py +++ b/pythonFiles/tests/testing_tools/adapter/test_discovery.py @@ -3,7 +3,6 @@ from __future__ import absolute_import, print_function -import os.path import unittest from testing_tools.adapter.util import fix_path, fix_relpath @@ -11,6 +10,11 @@ from testing_tools.adapter.discovery import fix_nodeid, DiscoveredTests +def _fix_nodeid(nodeid): + nodeid = fix_path(nodeid) + return fix_nodeid(nodeid) + + class DiscoveredTestsTests(unittest.TestCase): def test_list(self): @@ -47,18 +51,18 @@ def test_list(self): ), ] allparents= [ - [('./test_spam.py::test_each', 'test_each', 'function'), - ('./test_spam.py', 'test_spam.py', 'file'), + [(fix_path('./test_spam.py::test_each'), 'test_each', 'function'), + (fix_path('./test_spam.py'), 'test_spam.py', 'file'), ('.', testroot, 'folder'), ], - [('./test_spam.py::All::BasicTests', 'BasicTests', 'suite'), - ('./test_spam.py::All', 'All', 'suite'), - ('./test_spam.py', 'test_spam.py', '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='./' + test.id, - parentid='./' + test.parentid) + expected = [test._replace(id=fix_relpath(test.id), + parentid=fix_relpath(test.parentid)) for test in tests] discovered = DiscoveredTests() for test, parents in zip(tests, allparents): @@ -164,7 +168,7 @@ def test_parents(self): name=testroot, ), ParentInfo( - id='./x', + id=_fix_nodeid('./x'), kind='folder', name='x', root=testroot, @@ -172,49 +176,49 @@ def test_parents(self): parentid='.', ), ParentInfo( - id='./x/y', + id=_fix_nodeid('./x/y'), kind='folder', name='y', root=testroot, relpath=fix_path('./x/y'), - parentid='./x', + parentid=_fix_nodeid('./x'), ), ParentInfo( - id='./x/y/z', + id=_fix_nodeid('./x/y/z'), kind='folder', name='z', root=testroot, relpath=fix_path('./x/y/z'), - parentid='./x/y', + parentid=_fix_nodeid('./x/y'), ), ParentInfo( - id='./x/y/z/test_spam.py', + id=_fix_nodeid('./x/y/z/test_spam.py'), kind='file', name='test_spam.py', root=testroot, relpath=fix_relpath(relfile), - parentid='./x/y/z', + parentid=_fix_nodeid('./x/y/z'), ), ParentInfo( - id='./x/y/z/test_spam.py::All', + id=_fix_nodeid('./x/y/z/test_spam.py::All'), kind='suite', name='All', root=testroot, - parentid='./x/y/z/test_spam.py', + parentid=_fix_nodeid('./x/y/z/test_spam.py'), ), ParentInfo( - id='./x/y/z/test_spam.py::All::BasicTests', + id=_fix_nodeid('./x/y/z/test_spam.py::All::BasicTests'), kind='suite', name='BasicTests', root=testroot, - parentid='./x/y/z/test_spam.py::All', + parentid=_fix_nodeid('./x/y/z/test_spam.py::All'), ), ParentInfo( - id='./x/y/z/test_spam.py::test_each', + id=_fix_nodeid('./x/y/z/test_spam.py::test_each'), kind='function', name='test_each', root=testroot, - parentid='./x/y/z/test_spam.py', + parentid=_fix_nodeid('./x/y/z/test_spam.py'), ), ]) @@ -237,8 +241,8 @@ def test_add_test_simple(self): # missing "./": parentid=relfile, ) - expected = test._replace(id='./' + test.id, - parentid='./test_spam.py') + expected = test._replace(id=_fix_nodeid(test.id), + parentid=_fix_nodeid(relfile)) discovered = DiscoveredTests() before = list(discovered), discovered.parents @@ -257,7 +261,7 @@ def test_add_test_simple(self): name=testroot, ), ParentInfo( - id='./test_spam.py', + id=_fix_nodeid('./test_spam.py'), kind='file', name=relfile, root=testroot, @@ -277,7 +281,7 @@ def test_multiroot(self): name='test_spam', path=TestPath( root=testroot1, - relfile=os.path.join('.', relfile1), + relfile=fix_relpath(relfile1), func='test_spam', ), source='{}:{}'.format(relfile1, 10), @@ -294,14 +298,14 @@ def test_multiroot(self): ] # the second root testroot2 = fix_path('/x/y/z') - relfile2 = 'w/test_eggs.py' + relfile2 = fix_path('w/test_eggs.py') alltests.extend([ TestInfo( id=relfile2 + '::BasicTests::test_first', name='test_first', path=TestPath( root=testroot2, - relfile=os.path.join('.', relfile2), + relfile=fix_relpath(relfile2), func='BasicTests.test_first', ), source='{}:{}'.format(relfile2, 61), @@ -328,7 +332,7 @@ def test_multiroot(self): self.assertEqual(tests, [ # the first root TestInfo( - id='./test_spam.py::test_spam', + id=_fix_nodeid('./test_spam.py::test_spam'), name='test_spam', path=TestPath( root=testroot1, @@ -337,11 +341,11 @@ def test_multiroot(self): ), source='{}:{}'.format(relfile1, 10), markers=[], - parentid='./test_spam.py', + parentid=_fix_nodeid('./test_spam.py'), ), # the secondroot TestInfo( - id='./w/test_eggs.py::BasicTests::test_first', + id=_fix_nodeid('./w/test_eggs.py::BasicTests::test_first'), name='test_first', path=TestPath( root=testroot2, @@ -350,7 +354,7 @@ def test_multiroot(self): ), source='{}:{}'.format(relfile2, 61), markers=[], - parentid='./w/test_eggs.py::BasicTests', + parentid=_fix_nodeid('./w/test_eggs.py::BasicTests'), ), ]) self.assertEqual(parents, [ @@ -361,7 +365,7 @@ def test_multiroot(self): name=testroot1, ), ParentInfo( - id='./test_spam.py', + id=_fix_nodeid('./test_spam.py'), kind='file', name='test_spam.py', root=testroot1, @@ -375,7 +379,7 @@ def test_multiroot(self): name=testroot2, ), ParentInfo( - id='./w', + id=_fix_nodeid('./w'), kind='folder', name='w', root=testroot2, @@ -383,19 +387,19 @@ def test_multiroot(self): parentid='.', ), ParentInfo( - id='./w/test_eggs.py', + id=_fix_nodeid('./w/test_eggs.py'), kind='file', name='test_eggs.py', root=testroot2, relpath=fix_relpath(relfile2), - parentid='./w', + parentid=_fix_nodeid('./w'), ), ParentInfo( - id='./w/test_eggs.py::BasicTests', + id=_fix_nodeid('./w/test_eggs.py::BasicTests'), kind='suite', name='BasicTests', root=testroot2, - parentid='./w/test_eggs.py', + parentid=_fix_nodeid('./w/test_eggs.py'), ), ]) @@ -478,8 +482,8 @@ def test_doctest(self): ('.', testroot, 'folder'), ], ] - expected = [test._replace(id=fix_nodeid(test.id), - parentid=fix_nodeid(test.parentid)) + expected = [test._replace(id=_fix_nodeid(test.id), + parentid=_fix_nodeid(test.parentid)) for test in alltests] discovered = DiscoveredTests() @@ -498,7 +502,7 @@ def test_doctest(self): name=testroot, ), ParentInfo( - id='./x', + id=_fix_nodeid('./x'), kind='folder', name='x', root=testroot, @@ -506,36 +510,36 @@ def test_doctest(self): parentid='.', ), ParentInfo( - id='./x/test_doctest.txt', + id=_fix_nodeid('./x/test_doctest.txt'), kind='file', name='test_doctest.txt', root=testroot, relpath=fix_path(doctestfile), - parentid='./x', + parentid=_fix_nodeid('./x'), ), ParentInfo( - id='./x/y', + id=_fix_nodeid('./x/y'), kind='folder', name='y', root=testroot, relpath=fix_path('./x/y'), - parentid='./x', + parentid=_fix_nodeid('./x'), ), ParentInfo( - id='./x/y/z', + id=_fix_nodeid('./x/y/z'), kind='folder', name='z', root=testroot, relpath=fix_path('./x/y/z'), - parentid='./x/y', + parentid=_fix_nodeid('./x/y'), ), ParentInfo( - id='./x/y/z/test_eggs.py', + id=_fix_nodeid('./x/y/z/test_eggs.py'), kind='file', name='test_eggs.py', root=testroot, relpath=fix_relpath(relfile), - parentid='./x/y/z', + parentid=_fix_nodeid('./x/y/z'), ), ]) @@ -580,8 +584,8 @@ def test_nested_suite_simple(self): ('.', testroot, 'folder'), ], ] - expected = [test._replace(id=fix_nodeid(test.id), - parentid=fix_nodeid(test.parentid)) + expected = [test._replace(id=_fix_nodeid(test.id), + parentid=_fix_nodeid(test.parentid)) for test in alltests] discovered = DiscoveredTests() @@ -599,7 +603,7 @@ def test_nested_suite_simple(self): name=testroot, ), ParentInfo( - id='./test_eggs.py', + id=_fix_nodeid('./test_eggs.py'), kind='file', name='test_eggs.py', root=testroot, @@ -607,17 +611,17 @@ def test_nested_suite_simple(self): parentid='.' ), ParentInfo( - id='./test_eggs.py::TestOuter', + id=_fix_nodeid('./test_eggs.py::TestOuter'), kind='suite', name='TestOuter', root=testroot, - parentid='./test_eggs.py', + parentid=_fix_nodeid('./test_eggs.py'), ), ParentInfo( - id='./test_eggs.py::TestOuter::TestInner', + id=_fix_nodeid('./test_eggs.py::TestOuter::TestInner'), kind='suite', name='TestInner', root=testroot, - parentid='./test_eggs.py::TestOuter', + parentid=_fix_nodeid('./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 d0b083326b6a..5a1c987161f8 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_functional.py +++ b/pythonFiles/tests/testing_tools/adapter/test_functional.py @@ -13,7 +13,7 @@ import pytest from ...__main__ import TESTING_TOOLS_ROOT -from testing_tools.adapter.util import fix_path +from testing_tools.adapter.util import fix_path, PATH_SEP CWD = os.getcwd() @@ -77,9 +77,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 @@ -271,43 +280,43 @@ def test_discover_normcase(self): 'root': projroot, 'rootid': '.', 'parents': [ - {'id': './tests', + {'id': fix_path('./tests'), 'kind': 'folder', 'name': 'tests', 'relpath': fix_path('./tests'), 'parentid': '.', }, - {'id': './tests/A', + {'id': fix_path('./tests/A'), 'kind': 'folder', 'name': 'A', 'relpath': fix_path('./tests/A'), - 'parentid': './tests', + 'parentid': fix_path('./tests'), }, - {'id': './tests/A/b', + {'id': fix_path('./tests/A/b'), 'kind': 'folder', 'name': 'b', 'relpath': fix_path('./tests/A/b'), - 'parentid': './tests/A', + 'parentid': fix_path('./tests/A'), }, - {'id': './tests/A/b/C', + {'id': fix_path('./tests/A/b/C'), 'kind': 'folder', 'name': 'C', 'relpath': fix_path('./tests/A/b/C'), - 'parentid': './tests/A/b', + 'parentid': fix_path('./tests/A/b'), }, - {'id': './tests/A/b/C/test_Spam.py', + {'id': fix_path('./tests/A/b/C/test_Spam.py'), 'kind': 'file', 'name': 'test_Spam.py', 'relpath': fix_path('./tests/A/b/C/test_Spam.py'), - 'parentid': './tests/A/b/C', + 'parentid': fix_path('./tests/A/b/C'), }, ], 'tests': [ - {'id': './tests/A/b/C/test_Spam.py::test_okay', + {'id': fix_path('./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': './tests/A/b/C/test_Spam.py', + 'parentid': fix_path('./tests/A/b/C/test_Spam.py'), }, ], }]) diff --git a/pythonFiles/tests/testing_tools/adapter/test_report.py b/pythonFiles/tests/testing_tools/adapter/test_report.py index 8471ed870635..b3ecbad425b0 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_report.py +++ b/pythonFiles/tests/testing_tools/adapter/test_report.py @@ -2,11 +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 +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 @@ -26,7 +25,7 @@ def test_basic(self): stub = StubSender() testroot = fix_path('/a/b/c') relfile = 'test_spam.py' - relpath = os.path.join('.', relfile) + relpath = fix_relpath(relfile) tests = [ TestInfo( id='test#1', @@ -113,10 +112,10 @@ def test_multiroot(self): ParentInfo( id=relfileid1, kind='file', - name=os.path.basename(relpath1), + name='test_spam.py', root=testroot1, relpath=relpath1, - parentid=os.path.dirname(relfileid1), + parentid='.', ), ] expected = [ @@ -175,7 +174,7 @@ def test_multiroot(self): ParentInfo( id=relfileid2, kind='file', - name=os.path.basename(relfile2), + name='test_eggs.py', root=testroot2, relpath=relpath2, parentid='./w', From f80bc90aafb8d007cfabf970ea1edad9f7de78e1 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 7 Aug 2019 10:16:33 -0600 Subject: [PATCH 11/21] Add a functional test to make sure os.path is carefully controlled. --- .../tests/testing_tools/adapter/test_util.py | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/pythonFiles/tests/testing_tools/adapter/test_util.py b/pythonFiles/tests/testing_tools/adapter/test_util.py index eabca9cdd475..5a6bbf723eec 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_util.py +++ b/pythonFiles/tests/testing_tools/adapter/test_util.py @@ -1,12 +1,63 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. +import os +import os.path import shlex import unittest +import pytest + from testing_tools.adapter.util import shlex_unsplit +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])) + + class ShlexUnsplitTests(unittest.TestCase): def test_no_args(self): From 94ede9ce75aefa7df65d23551cdf6ed7c2567bab Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 7 Aug 2019 12:45:22 -0600 Subject: [PATCH 12/21] Add tests for the file path utils. --- pythonFiles/testing_tools/adapter/util.py | 29 ++- .../tests/testing_tools/adapter/test_util.py | 176 +++++++++++++++++- 2 files changed, 198 insertions(+), 7 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/util.py b/pythonFiles/testing_tools/adapter/util.py index f864341c991a..810d4fdce089 100644 --- a/pythonFiles/testing_tools/adapter/util.py +++ b/pythonFiles/testing_tools/adapter/util.py @@ -41,6 +41,13 @@ 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 @@ -58,6 +65,8 @@ def group_attr_names(attrnames): def fix_path(path, #*, _pathsep=PATH_SEP): """Return a platform-appropriate path for the given path.""" + if not path: + return '.' return path.replace('/', _pathsep) @@ -68,6 +77,8 @@ def fix_relpath(path, #*, ): """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 @@ -76,8 +87,8 @@ def fix_relpath(path, #*, def fix_fileid(fileid, rootdir=None, #*, normalize=False, - _normcase=NORMCASE, _path_isabs=IS_ABS_PATH, + _normcase=NORMCASE, _pathsep=PATH_SEP, ): """Return a pathsep-separated file ID ("./"-prefixed) for the given value. @@ -89,24 +100,30 @@ def fix_fileid(fileid, rootdir=None, #*, if not fileid or fileid == '.': return fileid relprefix = '.' + _pathsep - - normalized = _normcase(fileid) - if normalized.startswith(relprefix): + if fileid.startswith(relprefix): return fileid + # We do not use NORMCASE because we want to leave the pathseps alone. + _normcase = _str_to_lower if _pathsep == '\\' else (lambda p: p) + isabs = False + normalized = _normcase(fileid) if _path_isabs(normalized): + # Deal with root-dir-as-fileid. + _, sep, relpath = fileid.partition(_pathsep) + if sep and not relpath.replace(_pathsep, ''): + return fileid + isabs = True if rootdir is not None: rootdir = _normcase(rootdir) if not rootdir.endswith(_pathsep): rootdir += _pathsep if normalized.startswith(rootdir): - # This assumes pathsep has length 1. fileid = fileid[len(rootdir):] isabs = False if normalize: - fileid = fileid.replace(_pathsep, '/').lower() + fileid = _str_to_lower(fileid.replace(_pathsep, '/')) relprefix = './' if not isabs: fileid = relprefix + fileid diff --git a/pythonFiles/tests/testing_tools/adapter/test_util.py b/pythonFiles/tests/testing_tools/adapter/test_util.py index 5a6bbf723eec..298e0d02e4ce 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_util.py +++ b/pythonFiles/tests/testing_tools/adapter/test_util.py @@ -1,16 +1,25 @@ # 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 import pytest -from testing_tools.adapter.util import shlex_unsplit +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 @@ -57,6 +66,171 @@ def test_isolated_imports(self): '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): + 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'), # "/" is valid on Windows. + # 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 + ('/', posixpath, '/'), + ('//', posixpath, '//'), + ('./spam.py', posixpath, './spam.py'), + ('\\', ntpath, '\\'), + ('\\\\', ntpath, '\\\\'), + ('C:\\\\', ntpath, 'C:\\\\'), + (r'.\spam.py', ntpath, r'.\spam.py'), + ] + # no-op + for path in [None, '', '.']: + tests.extend([ + (path, posixpath, path), + (path, ntpath, path), + ]) + 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, + _pathsep=pathsep, + ) + self.assertEqual(fixed, expected) + + # with rootdir + tests = [ + ('spam.py', '/eggs', posixpath, './spam.py'), + ('spam.py', r'\eggs', ntpath, r'.\spam.py'), + # absolute + ('/spam.py', '/', posixpath, './spam.py'), + ('/eggs/spam.py', '/eggs', posixpath, './spam.py'), + ('/eggs/spam.py', '/eggs/', posixpath, './spam.py'), + (r'\spam.py', '\\', ntpath, r'.\spam.py'), + (r'C:\spam.py', 'C:\\', ntpath, r'.\spam.py'), + (r'\eggs\spam.py', r'\eggs', ntpath, r'.\spam.py'), + (r'\eggs\spam.py', '\\eggs\\', ntpath, r'.\spam.py'), + # normcase + (r'C:\spam.py', 'c:\\', ntpath, r'.\spam.py'), + (r'\Eggs\Spam.py', '\\eggs', ntpath, r'.\Spam.py'), + (r'\eggs\spam.py', '\\Eggs', ntpath, r'.\spam.py'), + (r'\eggs\Spam.py', '\\Eggs', ntpath, r'.\Spam.py'), + # no change + ('/spam.py', '/eggs', posixpath, '/spam.py'), + ('/spam.py', '/eggs/', posixpath, '/spam.py'), + (r'\spam.py', r'\eggs', ntpath, r'\spam.py'), + (r'C:\spam.py', r'C:\eggs', ntpath, r'C:\spam.py'), + # TODO: Should these be supported. + (r'C:\spam.py', '\\', ntpath, r'C:\spam.py'), + (r'\spam.py', 'C:\\', ntpath, r'\spam.py'), + # root-only + ('/', '/', posixpath, '/'), + ('/', '/spam', posixpath, '/'), + ('//', '/', posixpath, '//'), + ('//', '//', posixpath, '//'), + ('//', '//spam', posixpath, '//'), + ('\\', '\\', ntpath, '\\'), + ('\\\\', '\\', ntpath, '\\\\'), + ('C:\\', 'C:\\eggs', ntpath, 'C:\\'), + ('C:\\', 'C:\\', ntpath, 'C:\\'), + (r'C:\spam.py', 'D:\\', ntpath, 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, + _pathsep=pathsep, + ) + self.assertEqual(fixed, expected) + class ShlexUnsplitTests(unittest.TestCase): From d993a19feaa4f07b7754bb3b93b34d694c696723 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 7 Aug 2019 12:45:44 -0600 Subject: [PATCH 13/21] Use NORMCASE in util.fix_fileid(). --- pythonFiles/testing_tools/adapter/util.py | 8 +++----- pythonFiles/tests/testing_tools/adapter/test_util.py | 2 ++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/util.py b/pythonFiles/testing_tools/adapter/util.py index 810d4fdce089..b06fd4da4999 100644 --- a/pythonFiles/testing_tools/adapter/util.py +++ b/pythonFiles/testing_tools/adapter/util.py @@ -100,14 +100,12 @@ def fix_fileid(fileid, rootdir=None, #*, if not fileid or fileid == '.': return fileid relprefix = '.' + _pathsep - if fileid.startswith(relprefix): - return fileid - # We do not use NORMCASE because we want to leave the pathseps alone. - _normcase = _str_to_lower if _pathsep == '\\' else (lambda p: p) + normalized = _normcase(fileid) + if normalized.startswith(relprefix): + return fileid isabs = False - normalized = _normcase(fileid) if _path_isabs(normalized): # Deal with root-dir-as-fileid. _, sep, relpath = fileid.partition(_pathsep) diff --git a/pythonFiles/tests/testing_tools/adapter/test_util.py b/pythonFiles/tests/testing_tools/adapter/test_util.py index 298e0d02e4ce..c1fba9d13674 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_util.py +++ b/pythonFiles/tests/testing_tools/adapter/test_util.py @@ -181,6 +181,7 @@ def test_fix_fileid(self): 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) @@ -227,6 +228,7 @@ def test_fix_fileid(self): 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) From b28acd8a5d01deee6683d0abc6b836cd4f4040b9 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 8 Aug 2019 13:58:34 -0600 Subject: [PATCH 14/21] Normalize path separator in file ID to "/" (but preserve case). --- .../adapter/pytest/_pytest_item.py | 6 +- pythonFiles/testing_tools/adapter/util.py | 77 ++++-- .../adapter/pytest/test_discovery.py | 226 +++++++++++++++++- .../tests/testing_tools/adapter/test_util.py | 131 +++++----- 4 files changed, 343 insertions(+), 97 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py index eb69946626c1..3a85ed3ced9a 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py +++ b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py @@ -246,6 +246,9 @@ def _get_location(item, testroot, relfile, #*, _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): srcfile = relfile @@ -466,9 +469,10 @@ def _normalize_test_id(testid, kind, #*, # 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('.' + _pathsep): # Absolute "paths" not expected. + if not fileid.startswith('./'): # Absolute "paths" not expected. raise should_never_reach_here( testid, fileid=fileid, diff --git a/pythonFiles/testing_tools/adapter/util.py b/pythonFiles/testing_tools/adapter/util.py index b06fd4da4999..444ae44c2cae 100644 --- a/pythonFiles/testing_tools/adapter/util.py +++ b/pythonFiles/testing_tools/adapter/util.py @@ -85,11 +85,40 @@ def fix_relpath(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, - _path_isabs=IS_ABS_PATH, - _normcase=NORMCASE, + strictpathsep=None, _pathsep=PATH_SEP, + **kwargs ): """Return a pathsep-separated file ID ("./"-prefixed) for the given value. @@ -99,33 +128,29 @@ def fix_fileid(fileid, rootdir=None, #*, """ if not fileid or fileid == '.': return fileid - relprefix = '.' + _pathsep - normalized = _normcase(fileid) - if normalized.startswith(relprefix): - 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 - isabs = False - if _path_isabs(normalized): - # Deal with root-dir-as-fileid. - _, sep, relpath = fileid.partition(_pathsep) - if sep and not relpath.replace(_pathsep, ''): - return fileid - - isabs = True - if rootdir is not None: - rootdir = _normcase(rootdir) - if not rootdir.endswith(_pathsep): - rootdir += _pathsep - if normalized.startswith(rootdir): - fileid = fileid[len(rootdir):] - isabs = False if normalize: - fileid = _str_to_lower(fileid.replace(_pathsep, '/')) - relprefix = './' - if not isabs: - fileid = relprefix + fileid - return fileid + 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 ############################# diff --git a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py index cb29acd21d2b..34612f01f1d6 100644 --- a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py +++ b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py @@ -867,18 +867,51 @@ def test_windows(self): discovered = StubDiscoveredTests(stub) session = StubPytestSession(stub) testroot = r'C:\A\B\C' + altroot = testroot.replace('\\', '/') relfile = r'X\Y\Z\test_Eggs.py' session.items = [ + # typical: StubFunctionItem( stub, - nodeid=relfile + '::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', - # wrong pathsep: - 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': collector.parse_item = generate_parse_item('\\') @@ -890,15 +923,15 @@ def test_windows(self): ('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 @@ -908,7 +941,180 @@ def test_windows(self): ), source=r'.\X\Y\Z\test_Eggs.py:13', # not normalized markers=None, - parentid=r'.\X\Y\Z\test_Eggs.py::SpamTests', + 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/test_h.py', ), )), ]) diff --git a/pythonFiles/tests/testing_tools/adapter/test_util.py b/pythonFiles/tests/testing_tools/adapter/test_util.py index c1fba9d13674..309ab1e0d1e7 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_util.py +++ b/pythonFiles/tests/testing_tools/adapter/test_util.py @@ -146,36 +146,39 @@ def test_fix_relpath(self): self.assertEqual(fixed, expected) def test_fix_fileid(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'), # "/" is valid on Windows. - # absolute - ('/', posixpath, '/'), - ('/spam.py', posixpath, '/spam.py'), - ('\\', ntpath, '\\'), - (r'\spam.py', ntpath, r'\spam.py'), - (r'C:\spam.py', ntpath, r'C:\spam.py'), + common = [ + ('spam.py', './spam.py'), + ('eggs/spam.py', './eggs/spam.py'), + ('eggs/spam/', './eggs/spam/'), + # absolute (no-op) + ('/', '/'), + ('//', '//'), + ('/spam.py', '/spam.py'), # no-op - ('/', posixpath, '/'), - ('//', posixpath, '//'), - ('./spam.py', posixpath, './spam.py'), - ('\\', ntpath, '\\'), - ('\\\\', ntpath, '\\\\'), - ('C:\\\\', ntpath, 'C:\\\\'), - (r'.\spam.py', ntpath, r'.\spam.py'), + (None, None), + ('', ''), + ('.', '.'), + ('./spam.py', './spam.py'), ] - # no-op - for path in [None, '', '.']: - tests.extend([ - (path, posixpath, path), - (path, ntpath, path), - ]) + 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)): @@ -187,42 +190,50 @@ def test_fix_fileid(self): self.assertEqual(fixed, expected) # with rootdir - tests = [ - ('spam.py', '/eggs', posixpath, './spam.py'), - ('spam.py', r'\eggs', ntpath, r'.\spam.py'), + 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 - ('/spam.py', '/', posixpath, './spam.py'), - ('/eggs/spam.py', '/eggs', posixpath, './spam.py'), - ('/eggs/spam.py', '/eggs/', posixpath, './spam.py'), - (r'\spam.py', '\\', ntpath, r'.\spam.py'), - (r'C:\spam.py', 'C:\\', ntpath, r'.\spam.py'), - (r'\eggs\spam.py', r'\eggs', ntpath, r'.\spam.py'), - (r'\eggs\spam.py', '\\eggs\\', ntpath, r'.\spam.py'), + (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:\\', ntpath, r'.\spam.py'), - (r'\Eggs\Spam.py', '\\eggs', ntpath, r'.\Spam.py'), - (r'\eggs\spam.py', '\\Eggs', ntpath, r'.\spam.py'), - (r'\eggs\Spam.py', '\\Eggs', ntpath, r'.\Spam.py'), - # no change - ('/spam.py', '/eggs', posixpath, '/spam.py'), - ('/spam.py', '/eggs/', posixpath, '/spam.py'), - (r'\spam.py', r'\eggs', ntpath, r'\spam.py'), - (r'C:\spam.py', r'C:\eggs', ntpath, r'C:\spam.py'), + (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', '\\', ntpath, r'C:\spam.py'), - (r'\spam.py', 'C:\\', ntpath, r'\spam.py'), + (r'C:\spam.py', '\\', r'C:/spam.py'), + (r'\spam.py', 'C:\\', r'/spam.py'), # root-only - ('/', '/', posixpath, '/'), - ('/', '/spam', posixpath, '/'), - ('//', '/', posixpath, '//'), - ('//', '//', posixpath, '//'), - ('//', '//spam', posixpath, '//'), - ('\\', '\\', ntpath, '\\'), - ('\\\\', '\\', ntpath, '\\\\'), - ('C:\\', 'C:\\eggs', ntpath, 'C:\\'), - ('C:\\', 'C:\\', ntpath, 'C:\\'), - (r'C:\spam.py', 'D:\\', ntpath, r'C:\spam.py'), - ] + ('\\', '\\', '/'), + ('\\\\', '\\', '//'), + ('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)): From 41b38c9916f9f763b654bf13bc8318f16c0f0969 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 8 Aug 2019 14:15:41 -0600 Subject: [PATCH 15/21] Fix handling of an error path. --- pythonFiles/testing_tools/adapter/pytest/_pytest_item.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py index 3a85ed3ced9a..56543ee8b13d 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py +++ b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py @@ -103,7 +103,7 @@ 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') @@ -111,7 +111,7 @@ 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() @@ -132,7 +132,7 @@ 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 @@ -531,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): From 3131528a3a7bdc19392edda789e288507454814c Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 8 Aug 2019 14:16:13 -0600 Subject: [PATCH 16/21] Fix expected IDs in the functional tests. --- .../testing_tools/adapter/test_functional.py | 716 +++++++++--------- 1 file changed, 358 insertions(+), 358 deletions(-) diff --git a/pythonFiles/tests/testing_tools/adapter/test_functional.py b/pythonFiles/tests/testing_tools/adapter/test_functional.py index 5a1c987161f8..f9623373c1f0 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_functional.py +++ b/pythonFiles/tests/testing_tools/adapter/test_functional.py @@ -107,25 +107,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', 'relpath': fix_path('./tests/test_spam.py'), - 'parentid': fix_path('./tests'), + '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', }, ], }]) @@ -157,51 +157,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', 'relpath': fix_path('./tests/test_doctest.py'), - 'parentid': fix_path('./tests'), + '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']) @@ -280,43 +280,43 @@ def test_discover_normcase(self): 'root': projroot, 'rootid': '.', 'parents': [ - {'id': fix_path('./tests'), + {'id': './tests', 'kind': 'folder', 'name': 'tests', 'relpath': fix_path('./tests'), 'parentid': '.', }, - {'id': fix_path('./tests/A'), + {'id': './tests/A', 'kind': 'folder', 'name': 'A', 'relpath': fix_path('./tests/A'), - 'parentid': fix_path('./tests'), + 'parentid': './tests', }, - {'id': fix_path('./tests/A/b'), + {'id': './tests/A/b', 'kind': 'folder', 'name': 'b', 'relpath': fix_path('./tests/A/b'), - 'parentid': fix_path('./tests/A'), + 'parentid': './tests/A', }, - {'id': fix_path('./tests/A/b/C'), + {'id': './tests/A/b/C', 'kind': 'folder', 'name': 'C', 'relpath': fix_path('./tests/A/b/C'), - 'parentid': fix_path('./tests/A/b'), + 'parentid': './tests/A/b', }, - {'id': fix_path('./tests/A/b/C/test_Spam.py'), + {'id': './tests/A/b/C/test_Spam.py', 'kind': 'file', 'name': 'test_Spam.py', 'relpath': fix_path('./tests/A/b/C/test_Spam.py'), - 'parentid': fix_path('./tests/A/b/C'), + 'parentid': './tests/A/b/C', }, ], 'tests': [ - {'id': fix_path('./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_path('./tests/A/b/C/test_Spam.py'), + 'parentid': './tests/A/b/C/test_Spam.py', }, ], }]) @@ -327,1006 +327,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', 'relpath': fix_path('./tests/test_42-43.py'), - 'parentid': fix_path('./tests'), + 'parentid': './tests', }, # +++ - {'id': fix_path('./tests/test_42.py'), + {'id': './tests/test_42.py', 'kind': 'file', 'name': 'test_42.py', 'relpath': fix_path('./tests/test_42.py'), - 'parentid': fix_path('./tests'), + 'parentid': './tests', }, # +++ - {'id': fix_path('./tests/test_doctest.txt'), + {'id': './tests/test_doctest.txt', 'kind': 'file', 'name': 'test_doctest.txt', 'relpath': fix_path('./tests/test_doctest.txt'), - 'parentid': fix_path('./tests'), + 'parentid': './tests', }, # +++ - {'id': fix_path('./tests/test_foo.py'), + {'id': './tests/test_foo.py', 'kind': 'file', 'name': 'test_foo.py', 'relpath': fix_path('./tests/test_foo.py'), - 'parentid': fix_path('./tests'), + 'parentid': './tests', }, # +++ - {'id': fix_path('./tests/test_mixed.py'), + {'id': './tests/test_mixed.py', 'kind': 'file', 'name': 'test_mixed.py', 'relpath': fix_path('./tests/test_mixed.py'), - 'parentid': fix_path('./tests'), + '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', 'relpath': fix_path('./tests/test_pytest.py'), - 'parentid': fix_path('./tests'), + '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', 'relpath': fix_path('./tests/test_pytest_param.py'), - 'parentid': fix_path('./tests'), + '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', 'relpath': fix_path('./tests/test_unittest.py'), - 'parentid': fix_path('./tests'), + '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', 'relpath': fix_path('./tests/v'), - 'parentid': fix_path('./tests'), + 'parentid': './tests', }, ## +++ - {'id': fix_path('./tests/v/test_eggs.py'), + {'id': './tests/v/test_eggs.py', 'kind': 'file', 'name': 'test_eggs.py', 'relpath': fix_path('./tests/v/test_eggs.py'), - 'parentid': fix_path('./tests/v'), + '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', 'relpath': fix_path('./tests/v/test_ham.py'), - 'parentid': fix_path('./tests/v'), + 'parentid': './tests/v', }, ## +++ - {'id': fix_path('./tests/v/test_spam.py'), + {'id': './tests/v/test_spam.py', 'kind': 'file', 'name': 'test_spam.py', 'relpath': fix_path('./tests/v/test_spam.py'), - 'parentid': fix_path('./tests/v'), + 'parentid': './tests/v', }, ## - {'id': fix_path('./tests/w'), + {'id': './tests/w', 'kind': 'folder', 'name': 'w', 'relpath': fix_path('./tests/w'), - 'parentid': fix_path('./tests'), + 'parentid': './tests', }, ## +++ - {'id': fix_path('./tests/w/test_spam.py'), + {'id': './tests/w/test_spam.py', 'kind': 'file', 'name': 'test_spam.py', 'relpath': fix_path('./tests/w/test_spam.py'), - 'parentid': fix_path('./tests/w'), + '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', 'relpath': fix_path('./tests/w/test_spam_ex.py'), - 'parentid': fix_path('./tests/w'), + 'parentid': './tests/w', }, ## - {'id': fix_path('./tests/x'), + {'id': './tests/x', 'kind': 'folder', 'name': 'x', 'relpath': fix_path('./tests/x'), - 'parentid': fix_path('./tests'), + 'parentid': './tests', }, ### - {'id': fix_path('./tests/x/y'), + {'id': './tests/x/y', 'kind': 'folder', 'name': 'y', 'relpath': fix_path('./tests/x/y'), - 'parentid': fix_path('./tests/x'), + 'parentid': './tests/x', }, #### - {'id': fix_path('./tests/x/y/z'), + {'id': './tests/x/y/z', 'kind': 'folder', 'name': 'z', 'relpath': fix_path('./tests/x/y/z'), - 'parentid': fix_path('./tests/x/y'), + 'parentid': './tests/x/y', }, ##### - {'id': fix_path('./tests/x/y/z/a'), + {'id': './tests/x/y/z/a', 'kind': 'folder', 'name': 'a', 'relpath': fix_path('./tests/x/y/z/a'), - 'parentid': fix_path('./tests/x/y/z'), + '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', 'relpath': fix_path('./tests/x/y/z/a/test_spam.py'), - 'parentid': fix_path('./tests/x/y/z/a'), + 'parentid': './tests/x/y/z/a', }, ##### - {'id': fix_path('./tests/x/y/z/b'), + {'id': './tests/x/y/z/b', 'kind': 'folder', 'name': 'b', 'relpath': fix_path('./tests/x/y/z/b'), - 'parentid': fix_path('./tests/x/y/z'), + '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', 'relpath': fix_path('./tests/x/y/z/b/test_spam.py'), - 'parentid': fix_path('./tests/x/y/z/b'), + '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', 'relpath': fix_path('./tests/x/y/z/test_ham.py'), - 'parentid': fix_path('./tests/x/y/z'), + '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', }, ], } From 56fd6201ff9c868a457b3332a4b16dfa2af41c3d Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 8 Aug 2019 14:57:41 -0600 Subject: [PATCH 17/21] Use normcase in the sort order of parents. --- pythonFiles/testing_tools/adapter/discovery.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/discovery.py b/pythonFiles/testing_tools/adapter/discovery.py index f60689cac81a..15a9434351eb 100644 --- a/pythonFiles/testing_tools/adapter/discovery.py +++ b/pythonFiles/testing_tools/adapter/discovery.py @@ -5,7 +5,7 @@ import re -from .util import fix_fileid, PATH_SEP, DIRNAME +from .util import fix_fileid, PATH_SEP, DIRNAME, NORMCASE from .info import ParentInfo @@ -55,7 +55,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 v: (NORMCASE(v.root or v.name), + NORMCASE(v.id)), + ) def reset(self): """Clear out any previously discovered tests.""" From eac3c66df2680f4bce8965f31bf04ce58abd518a Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 8 Aug 2019 15:02:17 -0600 Subject: [PATCH 18/21] Fix expected IDs in some of the unit tests. --- .../adapter/pytest/test_discovery.py | 197 +++++++++--------- .../testing_tools/adapter/test_discovery.py | 89 ++++---- 2 files changed, 144 insertions(+), 142 deletions(-) diff --git a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py index 34612f01f1d6..91312c655276 100644 --- a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py +++ b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py @@ -458,12 +458,12 @@ def test_modifyitems(self): ('discovered.reset', None, None), ('discovered.add_test', None, dict( parents=[ - (fix_path('./test_spam.py::SpamTests'), 'SpamTests', 'suite'), - (fix_path('./test_spam.py'), 'test_spam.py', 'file'), + ('./test_spam.py::SpamTests', 'SpamTests', 'suite'), + ('./test_spam.py', 'test_spam.py', 'file'), ('.', testroot, 'folder'), ], test=TestInfo( - id=fix_path('./test_spam.py::SpamTests::test_one'), + id='./test_spam.py::SpamTests::test_one', name='test_one', path=TestPath( root=testroot, @@ -473,17 +473,17 @@ def test_modifyitems(self): ), source='{}:{}'.format(relfile1, 13), markers=None, - parentid=fix_path('./test_spam.py::SpamTests'), + parentid='./test_spam.py::SpamTests', ), )), ('discovered.add_test', None, dict( parents=[ - (fix_path('./test_spam.py::SpamTests'), 'SpamTests', 'suite'), - (fix_path('./test_spam.py'), 'test_spam.py', 'file'), + ('./test_spam.py::SpamTests', 'SpamTests', 'suite'), + ('./test_spam.py', 'test_spam.py', 'file'), ('.', testroot, 'folder'), ], test=TestInfo( - id=fix_path('./test_spam.py::SpamTests::test_other'), + id='./test_spam.py::SpamTests::test_other', name='test_other', path=TestPath( root=testroot, @@ -493,16 +493,16 @@ def test_modifyitems(self): ), source='{}:{}'.format(relfile1, 20), markers=None, - parentid=fix_path('./test_spam.py::SpamTests'), + parentid='./test_spam.py::SpamTests', ), )), ('discovered.add_test', None, dict( parents=[ - (fix_path('./test_spam.py'), 'test_spam.py', 'file'), + ('./test_spam.py', 'test_spam.py', 'file'), ('.', testroot, 'folder'), ], test=TestInfo( - id=fix_path('./test_spam.py::test_all'), + id='./test_spam.py::test_all', name='test_all', path=TestPath( root=testroot, @@ -512,17 +512,17 @@ def test_modifyitems(self): ), source='{}:{}'.format(relfile1, 145), markers=None, - parentid=fix_path('./test_spam.py'), + parentid='./test_spam.py', ), )), ('discovered.add_test', None, dict( parents=[ - (fix_path('./test_spam.py::test_each'), 'test_each', 'function'), - (fix_path('./test_spam.py'), 'test_spam.py', 'file'), + ('./test_spam.py::test_each', 'test_each', 'function'), + ('./test_spam.py', 'test_spam.py', 'file'), ('.', testroot, 'folder'), ], test=TestInfo( - id=fix_path('./test_spam.py::test_each[10-10]'), + id='./test_spam.py::test_each[10-10]', name='test_each[10-10]', path=TestPath( root=testroot, @@ -532,21 +532,21 @@ def test_modifyitems(self): ), source='{}:{}'.format(relfile1, 274), markers=None, - parentid=fix_path('./test_spam.py::test_each'), + parentid='./test_spam.py::test_each', ), )), ('discovered.add_test', None, dict( parents=[ - (fix_path('./x/y/z/test_eggs.py::All::BasicTests'), 'BasicTests', 'suite'), - (fix_path('./x/y/z/test_eggs.py::All'), 'All', 'suite'), - (fix_path('./x/y/z/test_eggs.py'), '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=fix_path('./x/y/z/test_eggs.py::All::BasicTests::test_first'), + id='./x/y/z/test_eggs.py::All::BasicTests::test_first', name='test_first', path=TestPath( root=testroot, @@ -556,22 +556,22 @@ def test_modifyitems(self): ), source='{}:{}'.format(fix_relpath(relfile2), 32), markers=None, - parentid=fix_path('./x/y/z/test_eggs.py::All::BasicTests'), + parentid='./x/y/z/test_eggs.py::All::BasicTests', ), )), ('discovered.add_test', None, dict( parents=[ - (fix_path('./x/y/z/test_eggs.py::All::BasicTests::test_each'), 'test_each', 'function'), - (fix_path('./x/y/z/test_eggs.py::All::BasicTests'), 'BasicTests', 'suite'), - (fix_path('./x/y/z/test_eggs.py::All'), 'All', 'suite'), - (fix_path('./x/y/z/test_eggs.py'), '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=fix_path('./x/y/z/test_eggs.py::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, @@ -581,7 +581,7 @@ def test_modifyitems(self): ), source='{}:{}'.format(fix_relpath(relfile2), 63), markers=['expected-failure', 'skip', 'skip-if'], - parentid=fix_path('./x/y/z/test_eggs.py::All::BasicTests::test_each'), + parentid='./x/y/z/test_eggs.py::All::BasicTests::test_each', ), )), ]) @@ -611,15 +611,15 @@ def test_finish(self): ('discovered.reset', None, None), ('discovered.add_test', None, dict( parents=[ - (fix_path('./x/y/z/test_eggs.py::SpamTests'), 'SpamTests', 'suite'), - (fix_path('./x/y/z/test_eggs.py'), '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=fix_path('./x/y/z/test_eggs.py::SpamTests::test_spam'), + id='./x/y/z/test_eggs.py::SpamTests::test_spam', name='test_spam', path=TestPath( root=testroot, @@ -629,7 +629,7 @@ def test_finish(self): ), source='{}:{}'.format(fix_relpath(relfile), 13), markers=None, - parentid=fix_path('./x/y/z/test_eggs.py::SpamTests'), + parentid='./x/y/z/test_eggs.py::SpamTests', ), )), ]) @@ -681,12 +681,12 @@ def test_doctest(self): ('discovered.reset', None, None), ('discovered.add_test', None, dict( parents=[ - (fix_path('./x/test_doctest.txt'), '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=fix_path('./x/test_doctest.txt::test_doctest.txt'), + id='./x/test_doctest.txt::test_doctest.txt', name='test_doctest.txt', path=TestPath( root=testroot, @@ -695,19 +695,19 @@ def test_doctest(self): ), source='{}:{}'.format(fix_relpath(doctestfile), 1), markers=[], - parentid=fix_path('./x/test_doctest.txt'), + parentid='./x/test_doctest.txt', ), )), ('discovered.add_test', None, dict( parents=[ - (fix_path('./x/y/z/test_eggs.py'), '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=fix_path('./x/y/z/test_eggs.py::test_eggs'), + id='./x/y/z/test_eggs.py::test_eggs', name='test_eggs', path=TestPath( root=testroot, @@ -716,19 +716,19 @@ def test_doctest(self): ), source='{}:{}'.format(fix_relpath(relfile), 1), markers=[], - parentid=fix_path('./x/y/z/test_eggs.py'), + parentid='./x/y/z/test_eggs.py', ), )), ('discovered.add_test', None, dict( parents=[ - (fix_path('./x/y/z/test_eggs.py'), '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=fix_path('./x/y/z/test_eggs.py::test_eggs.TestSpam'), + id='./x/y/z/test_eggs.py::test_eggs.TestSpam', name='test_eggs.TestSpam', path=TestPath( root=testroot, @@ -737,19 +737,19 @@ def test_doctest(self): ), source='{}:{}'.format(fix_relpath(relfile), 13), markers=[], - parentid=fix_path('./x/y/z/test_eggs.py'), + parentid='./x/y/z/test_eggs.py', ), )), ('discovered.add_test', None, dict( parents=[ - (fix_path('./x/y/z/test_eggs.py'), '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=fix_path('./x/y/z/test_eggs.py::test_eggs.TestSpam.TestEggs'), + id='./x/y/z/test_eggs.py::test_eggs.TestSpam.TestEggs', name='test_eggs.TestSpam.TestEggs', path=TestPath( root=testroot, @@ -758,7 +758,7 @@ def test_doctest(self): ), source='{}:{}'.format(fix_relpath(relfile), 28), markers=[], - parentid=fix_path('./x/y/z/test_eggs.py'), + parentid='./x/y/z/test_eggs.py', ), )), ]) @@ -788,16 +788,16 @@ def test_nested_brackets(self): ('discovered.reset', None, None), ('discovered.add_test', None, dict( parents=[ - (fix_path('./x/y/z/test_eggs.py::SpamTests::test_spam'), 'test_spam', 'function'), - (fix_path('./x/y/z/test_eggs.py::SpamTests'), 'SpamTests', 'suite'), - (fix_path('./x/y/z/test_eggs.py'), '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=fix_path('./x/y/z/test_eggs.py::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, @@ -807,7 +807,7 @@ def test_nested_brackets(self): ), source='{}:{}'.format(fix_relpath(relfile), 13), markers=None, - parentid=fix_path('./x/y/z/test_eggs.py::SpamTests::test_spam'), + parentid='./x/y/z/test_eggs.py::SpamTests::test_spam', ), )), ]) @@ -837,17 +837,17 @@ def test_nested_suite(self): ('discovered.reset', None, None), ('discovered.add_test', None, dict( parents=[ - (fix_path('./x/y/z/test_eggs.py::SpamTests::Ham::Eggs'), 'Eggs', 'suite'), - (fix_path('./x/y/z/test_eggs.py::SpamTests::Ham'), 'Ham', 'suite'), - (fix_path('./x/y/z/test_eggs.py::SpamTests'), 'SpamTests', 'suite'), - (fix_path('./x/y/z/test_eggs.py'), '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=fix_path('./x/y/z/test_eggs.py::SpamTests::Ham::Eggs::test_spam'), + id='./x/y/z/test_eggs.py::SpamTests::Ham::Eggs::test_spam', name='test_spam', path=TestPath( root=testroot, @@ -857,7 +857,7 @@ def test_nested_suite(self): ), source='{}:{}'.format(fix_relpath(relfile), 13), markers=None, - parentid=fix_path('./x/y/z/test_eggs.py::SpamTests::Ham::Eggs'), + parentid='./x/y/z/test_eggs.py::SpamTests::Ham::Eggs', ), )), ]) @@ -1125,7 +1125,6 @@ def test_mysterious_parens(self): session = StubPytestSession(stub) testroot = fix_path('/a/b/c') relfile = fix_path('x/y/z/test_eggs.py') - relfileid = fix_relpath(relfile) session.items = [ StubFunctionItem( stub, @@ -1145,15 +1144,15 @@ 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, @@ -1163,7 +1162,7 @@ def test_mysterious_parens(self): ), source='{}:{}'.format(fix_relpath(relfile), 13), markers=None, - parentid=relfileid + '::SpamTests', + parentid='./x/y/z/test_eggs.py::SpamTests', ), )), ]) @@ -1204,15 +1203,15 @@ def test_imported_test(self): ('discovered.reset', None, None), ('discovered.add_test', None, dict( parents=[ - (fix_path('./x/y/z/test_eggs.py::SpamTests'), 'SpamTests', 'suite'), - (fix_path('./x/y/z/test_eggs.py'), '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=fix_path('./x/y/z/test_eggs.py::SpamTests::test_spam'), + id='./x/y/z/test_eggs.py::SpamTests::test_spam', name='test_spam', path=TestPath( root=testroot, @@ -1222,19 +1221,19 @@ def test_imported_test(self): ), source='{}:{}'.format(fix_relpath(srcfile), 13), markers=None, - parentid=fix_path('./x/y/z/test_eggs.py::SpamTests'), + parentid='./x/y/z/test_eggs.py::SpamTests', ), )), ('discovered.add_test', None, dict( parents=[ - (fix_path('./x/y/z/test_eggs.py'), '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=fix_path('./x/y/z/test_eggs.py::test_ham'), + id='./x/y/z/test_eggs.py::test_ham', name='test_ham', path=TestPath( root=testroot, @@ -1244,7 +1243,7 @@ def test_imported_test(self): ), source='{}:{}'.format(fix_relpath(srcfile), 4), markers=None, - parentid=fix_path('./x/y/z/test_eggs.py'), + 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 9f00566f9a85..ce6b615c5f37 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_discovery.py +++ b/pythonFiles/tests/testing_tools/adapter/test_discovery.py @@ -11,8 +11,11 @@ def _fix_nodeid(nodeid): - nodeid = fix_path(nodeid) - return fix_nodeid(nodeid) + + nodeid = nodeid.replace('\\', '/') + if not nodeid.startswith('./'): + nodeid = './' + nodeid + return nodeid class DiscoveredTestsTests(unittest.TestCase): @@ -61,8 +64,8 @@ def test_list(self): ('.', testroot, 'folder'), ], ] - expected = [test._replace(id=fix_relpath(test.id), - parentid=fix_relpath(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): @@ -168,7 +171,7 @@ def test_parents(self): name=testroot, ), ParentInfo( - id=_fix_nodeid('./x'), + id='./x', kind='folder', name='x', root=testroot, @@ -176,49 +179,49 @@ def test_parents(self): parentid='.', ), ParentInfo( - id=_fix_nodeid('./x/y'), + id='./x/y', kind='folder', name='y', root=testroot, relpath=fix_path('./x/y'), - parentid=_fix_nodeid('./x'), + parentid='./x', ), ParentInfo( - id=_fix_nodeid('./x/y/z'), + id='./x/y/z', kind='folder', name='z', root=testroot, relpath=fix_path('./x/y/z'), - parentid=_fix_nodeid('./x/y'), + parentid='./x/y', ), ParentInfo( - id=_fix_nodeid('./x/y/z/test_spam.py'), + id='./x/y/z/test_spam.py', kind='file', name='test_spam.py', root=testroot, relpath=fix_relpath(relfile), - parentid=_fix_nodeid('./x/y/z'), + parentid='./x/y/z', ), ParentInfo( - id=_fix_nodeid('./x/y/z/test_spam.py::All'), + id='./x/y/z/test_spam.py::All', kind='suite', name='All', root=testroot, - parentid=_fix_nodeid('./x/y/z/test_spam.py'), + parentid='./x/y/z/test_spam.py', ), ParentInfo( - id=_fix_nodeid('./x/y/z/test_spam.py::All::BasicTests'), + id='./x/y/z/test_spam.py::All::BasicTests', kind='suite', name='BasicTests', root=testroot, - parentid=_fix_nodeid('./x/y/z/test_spam.py::All'), + parentid='./x/y/z/test_spam.py::All', ), ParentInfo( - id=_fix_nodeid('./x/y/z/test_spam.py::test_each'), + id='./x/y/z/test_spam.py::test_each', kind='function', name='test_each', root=testroot, - parentid=_fix_nodeid('./x/y/z/test_spam.py'), + parentid='./x/y/z/test_spam.py', ), ]) @@ -242,7 +245,7 @@ def test_add_test_simple(self): parentid=relfile, ) expected = test._replace(id=_fix_nodeid(test.id), - parentid=_fix_nodeid(relfile)) + parentid=_fix_nodeid(test.parentid)) discovered = DiscoveredTests() before = list(discovered), discovered.parents @@ -261,7 +264,7 @@ def test_add_test_simple(self): name=testroot, ), ParentInfo( - id=_fix_nodeid('./test_spam.py'), + id='./test_spam.py', kind='file', name=relfile, root=testroot, @@ -332,7 +335,7 @@ def test_multiroot(self): self.assertEqual(tests, [ # the first root TestInfo( - id=_fix_nodeid('./test_spam.py::test_spam'), + id='./test_spam.py::test_spam', name='test_spam', path=TestPath( root=testroot1, @@ -341,11 +344,11 @@ def test_multiroot(self): ), source='{}:{}'.format(relfile1, 10), markers=[], - parentid=_fix_nodeid('./test_spam.py'), + parentid='./test_spam.py', ), # the secondroot TestInfo( - id=_fix_nodeid('./w/test_eggs.py::BasicTests::test_first'), + id='./w/test_eggs.py::BasicTests::test_first', name='test_first', path=TestPath( root=testroot2, @@ -354,7 +357,7 @@ def test_multiroot(self): ), source='{}:{}'.format(relfile2, 61), markers=[], - parentid=_fix_nodeid('./w/test_eggs.py::BasicTests'), + parentid='./w/test_eggs.py::BasicTests', ), ]) self.assertEqual(parents, [ @@ -365,7 +368,7 @@ def test_multiroot(self): name=testroot1, ), ParentInfo( - id=_fix_nodeid('./test_spam.py'), + id='./test_spam.py', kind='file', name='test_spam.py', root=testroot1, @@ -379,7 +382,7 @@ def test_multiroot(self): name=testroot2, ), ParentInfo( - id=_fix_nodeid('./w'), + id='./w', kind='folder', name='w', root=testroot2, @@ -387,19 +390,19 @@ def test_multiroot(self): parentid='.', ), ParentInfo( - id=_fix_nodeid('./w/test_eggs.py'), + id='./w/test_eggs.py', kind='file', name='test_eggs.py', root=testroot2, relpath=fix_relpath(relfile2), - parentid=_fix_nodeid('./w'), + parentid='./w', ), ParentInfo( - id=_fix_nodeid('./w/test_eggs.py::BasicTests'), + id='./w/test_eggs.py::BasicTests', kind='suite', name='BasicTests', root=testroot2, - parentid=_fix_nodeid('./w/test_eggs.py'), + parentid='./w/test_eggs.py', ), ]) @@ -502,7 +505,7 @@ def test_doctest(self): name=testroot, ), ParentInfo( - id=_fix_nodeid('./x'), + id='./x', kind='folder', name='x', root=testroot, @@ -510,36 +513,36 @@ def test_doctest(self): parentid='.', ), ParentInfo( - id=_fix_nodeid('./x/test_doctest.txt'), + id='./x/test_doctest.txt', kind='file', name='test_doctest.txt', root=testroot, relpath=fix_path(doctestfile), - parentid=_fix_nodeid('./x'), + parentid='./x', ), ParentInfo( - id=_fix_nodeid('./x/y'), + id='./x/y', kind='folder', name='y', root=testroot, relpath=fix_path('./x/y'), - parentid=_fix_nodeid('./x'), + parentid='./x', ), ParentInfo( - id=_fix_nodeid('./x/y/z'), + id='./x/y/z', kind='folder', name='z', root=testroot, relpath=fix_path('./x/y/z'), - parentid=_fix_nodeid('./x/y'), + parentid='./x/y', ), ParentInfo( - id=_fix_nodeid('./x/y/z/test_eggs.py'), + id='./x/y/z/test_eggs.py', kind='file', name='test_eggs.py', root=testroot, relpath=fix_relpath(relfile), - parentid=_fix_nodeid('./x/y/z'), + parentid='./x/y/z', ), ]) @@ -603,7 +606,7 @@ def test_nested_suite_simple(self): name=testroot, ), ParentInfo( - id=_fix_nodeid('./test_eggs.py'), + id='./test_eggs.py', kind='file', name='test_eggs.py', root=testroot, @@ -611,17 +614,17 @@ def test_nested_suite_simple(self): parentid='.' ), ParentInfo( - id=_fix_nodeid('./test_eggs.py::TestOuter'), + id='./test_eggs.py::TestOuter', kind='suite', name='TestOuter', root=testroot, - parentid=_fix_nodeid('./test_eggs.py'), + parentid='./test_eggs.py', ), ParentInfo( - id=_fix_nodeid('./test_eggs.py::TestOuter::TestInner'), + id='./test_eggs.py::TestOuter::TestInner', kind='suite', name='TestInner', root=testroot, - parentid=_fix_nodeid('./test_eggs.py::TestOuter'), + parentid='./test_eggs.py::TestOuter', ), ]) From 8f33b77451f3488d77d1f00c87145bd2f18d5fd5 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 8 Aug 2019 15:24:10 -0600 Subject: [PATCH 19/21] Fix node IDs properly in DiscoveredTests.add_test(). --- .../testing_tools/adapter/discovery.py | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/discovery.py b/pythonFiles/testing_tools/adapter/discovery.py index 15a9434351eb..01ca288185c7 100644 --- a/pythonFiles/testing_tools/adapter/discovery.py +++ b/pythonFiles/testing_tools/adapter/discovery.py @@ -5,7 +5,7 @@ import re -from .util import fix_fileid, PATH_SEP, DIRNAME, NORMCASE +from .util import fix_fileid, DIRNAME, NORMCASE from .info import ParentInfo @@ -19,24 +19,23 @@ """, re.VERBOSE) -def fix_nodeid(nodeid, rootdir=None, #*, +def fix_nodeid(nodeid, kind, rootdir=None, #*, _fix_fileid=fix_fileid, - _pathsep=PATH_SEP, ): if not nodeid: raise ValueError('missing nodeid') if nodeid == '.': return nodeid - m = FILE_ID_RE.match(nodeid) - if m: - fileid, remainder = m.groups() - elif len(nodeid) == 1: - fileid = nodeid - remainder = '' - else: - fileid = nodeid[:2] - remainder = nodeid[2:] + 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 '') @@ -72,7 +71,7 @@ def add_test(self, test, parents): # provided test and parents (from the test collector) are # properly generated. However, we play it safe here. test = test._replace( - id=fix_nodeid(test.id, test.path.root), + id=fix_nodeid(test.id, 'test', test.path.root), parentid=parentid, ) self._tests.append(test) @@ -86,11 +85,11 @@ def _ensure_parent(self, path, parents, #*, _parents = iter(parents) nodeid, name, kind = next(_parents) # As in add_test(), the node ID *should* already be correct. - nodeid = fix_nodeid(nodeid, rootdir) + 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. - parentid = fix_nodeid(parentid, rootdir) + parentid = fix_nodeid(parentid, kind, rootdir) if kind in ('folder', 'file'): info = ParentInfo(nodeid, kind, name, rootdir, relpath, parentid) relpath = _dirname(relpath) From 1f68ca357728c7f5a70046e9d3825dbf5c500620 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 8 Aug 2019 15:41:27 -0600 Subject: [PATCH 20/21] Fix the resulting "source" in the functional tests. --- pythonFiles/tests/testing_tools/adapter/test_functional.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pythonFiles/tests/testing_tools/adapter/test_functional.py b/pythonFiles/tests/testing_tools/adapter/test_functional.py index f9623373c1f0..871c9d605446 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_functional.py +++ b/pythonFiles/tests/testing_tools/adapter/test_functional.py @@ -66,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 From 462e58b8ef00a97b6035944c0961e6e17e5b10ef Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 8 Aug 2019 15:43:17 -0600 Subject: [PATCH 21/21] Do not normcase the ID when sorting parents. --- pythonFiles/testing_tools/adapter/discovery.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/discovery.py b/pythonFiles/testing_tools/adapter/discovery.py index 01ca288185c7..3803069f314f 100644 --- a/pythonFiles/testing_tools/adapter/discovery.py +++ b/pythonFiles/testing_tools/adapter/discovery.py @@ -55,8 +55,8 @@ def __getitem__(self, index): @property def parents(self): return sorted(self._parents.values(), - key=lambda v: (NORMCASE(v.root or v.name), - NORMCASE(v.id)), + key=lambda p: (NORMCASE(p.root or p.name), + p.id), ) def reset(self):