Merge pull request #4285 from kchmck/fix-4046
Fix problems with running tests in package `__init__` files (#4046)
This commit is contained in:
		
						commit
						21725e9304
					
				| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					Fix problems with running tests in package ``__init__.py`` files.
 | 
				
			||||||
| 
						 | 
					@ -281,15 +281,6 @@ def pytest_ignore_collect(path, config):
 | 
				
			||||||
    if _in_venv(path) and not allow_in_venv:
 | 
					    if _in_venv(path) and not allow_in_venv:
 | 
				
			||||||
        return True
 | 
					        return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Skip duplicate paths.
 | 
					 | 
				
			||||||
    keepduplicates = config.getoption("keepduplicates")
 | 
					 | 
				
			||||||
    duplicate_paths = config.pluginmanager._duplicatepaths
 | 
					 | 
				
			||||||
    if not keepduplicates:
 | 
					 | 
				
			||||||
        if path in duplicate_paths:
 | 
					 | 
				
			||||||
            return True
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            duplicate_paths.add(path)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return False
 | 
					    return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -551,7 +542,15 @@ class Session(nodes.FSCollector):
 | 
				
			||||||
                col = root._collectfile(argpath)
 | 
					                col = root._collectfile(argpath)
 | 
				
			||||||
                if col:
 | 
					                if col:
 | 
				
			||||||
                    self._node_cache[argpath] = col
 | 
					                    self._node_cache[argpath] = col
 | 
				
			||||||
            for y in self.matchnodes(col, names):
 | 
					            m = self.matchnodes(col, names)
 | 
				
			||||||
 | 
					            # If __init__.py was the only file requested, then the matched node will be
 | 
				
			||||||
 | 
					            # the corresponding Package, and the first yielded item will be the __init__
 | 
				
			||||||
 | 
					            # Module itself, so just use that. If this special case isn't taken, then all
 | 
				
			||||||
 | 
					            # the files in the package will be yielded.
 | 
				
			||||||
 | 
					            if argpath.basename == "__init__.py":
 | 
				
			||||||
 | 
					                yield next(m[0].collect())
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					            for y in m:
 | 
				
			||||||
                yield y
 | 
					                yield y
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _collectfile(self, path):
 | 
					    def _collectfile(self, path):
 | 
				
			||||||
| 
						 | 
					@ -559,6 +558,16 @@ class Session(nodes.FSCollector):
 | 
				
			||||||
        if not self.isinitpath(path):
 | 
					        if not self.isinitpath(path):
 | 
				
			||||||
            if ihook.pytest_ignore_collect(path=path, config=self.config):
 | 
					            if ihook.pytest_ignore_collect(path=path, config=self.config):
 | 
				
			||||||
                return ()
 | 
					                return ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Skip duplicate paths.
 | 
				
			||||||
 | 
					        keepduplicates = self.config.getoption("keepduplicates")
 | 
				
			||||||
 | 
					        if not keepduplicates:
 | 
				
			||||||
 | 
					            duplicate_paths = self.config.pluginmanager._duplicatepaths
 | 
				
			||||||
 | 
					            if path in duplicate_paths:
 | 
				
			||||||
 | 
					                return ()
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                duplicate_paths.add(path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return ihook.pytest_collect_file(path=path, parent=self)
 | 
					        return ihook.pytest_collect_file(path=path, parent=self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _recurse(self, path):
 | 
					    def _recurse(self, path):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -553,15 +553,6 @@ class Package(Module):
 | 
				
			||||||
        return path in self.session._initialpaths
 | 
					        return path in self.session._initialpaths
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def collect(self):
 | 
					    def collect(self):
 | 
				
			||||||
        # XXX: HACK!
 | 
					 | 
				
			||||||
        # Before starting to collect any files from this package we need
 | 
					 | 
				
			||||||
        # to cleanup the duplicate paths added by the session's collect().
 | 
					 | 
				
			||||||
        # Proper fix is to not track these as duplicates in the first place.
 | 
					 | 
				
			||||||
        for path in list(self.session.config.pluginmanager._duplicatepaths):
 | 
					 | 
				
			||||||
            # if path.parts()[:len(self.fspath.dirpath().parts())] == self.fspath.dirpath().parts():
 | 
					 | 
				
			||||||
            if path.dirname.startswith(self.name):
 | 
					 | 
				
			||||||
                self.session.config.pluginmanager._duplicatepaths.remove(path)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this_path = self.fspath.dirpath()
 | 
					        this_path = self.fspath.dirpath()
 | 
				
			||||||
        init_module = this_path.join("__init__.py")
 | 
					        init_module = this_path.join("__init__.py")
 | 
				
			||||||
        if init_module.check(file=1) and path_matches_patterns(
 | 
					        if init_module.check(file=1) and path_matches_patterns(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -957,6 +957,21 @@ def test_collect_init_tests(testdir):
 | 
				
			||||||
            "*<Function 'test_foo'>",
 | 
					            "*<Function 'test_foo'>",
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					    result = testdir.runpytest("./tests", "--collect-only")
 | 
				
			||||||
 | 
					    result.stdout.fnmatch_lines(
 | 
				
			||||||
 | 
					        [
 | 
				
			||||||
 | 
					            "*<Module '__init__.py'>",
 | 
				
			||||||
 | 
					            "*<Function 'test_init'>",
 | 
				
			||||||
 | 
					            "*<Module 'test_foo.py'>",
 | 
				
			||||||
 | 
					            "*<Function 'test_foo'>",
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    result = testdir.runpytest("./tests/test_foo.py", "--collect-only")
 | 
				
			||||||
 | 
					    result.stdout.fnmatch_lines(["*<Module 'test_foo.py'>", "*<Function 'test_foo'>"])
 | 
				
			||||||
 | 
					    assert "test_init" not in result.stdout.str()
 | 
				
			||||||
 | 
					    result = testdir.runpytest("./tests/__init__.py", "--collect-only")
 | 
				
			||||||
 | 
					    result.stdout.fnmatch_lines(["*<Module '__init__.py'>", "*<Function 'test_init'>"])
 | 
				
			||||||
 | 
					    assert "test_foo" not in result.stdout.str()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_collect_invalid_signature_message(testdir):
 | 
					def test_collect_invalid_signature_message(testdir):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -219,7 +219,7 @@ class TestNewSession(SessionTests):
 | 
				
			||||||
        started = reprec.getcalls("pytest_collectstart")
 | 
					        started = reprec.getcalls("pytest_collectstart")
 | 
				
			||||||
        finished = reprec.getreports("pytest_collectreport")
 | 
					        finished = reprec.getreports("pytest_collectreport")
 | 
				
			||||||
        assert len(started) == len(finished)
 | 
					        assert len(started) == len(finished)
 | 
				
			||||||
        assert len(started) == 7  # XXX extra TopCollector
 | 
					        assert len(started) == 8
 | 
				
			||||||
        colfail = [x for x in finished if x.failed]
 | 
					        colfail = [x for x in finished if x.failed]
 | 
				
			||||||
        assert len(colfail) == 1
 | 
					        assert len(colfail) == 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue