From d0bd01beca0308cfb263ec7f44c9acf0933a1fc0 Mon Sep 17 00:00:00 2001 From: turturica Date: Thu, 9 Aug 2018 18:06:38 -0700 Subject: [PATCH 01/11] Collect any tests from a package's __init__.py --- src/_pytest/python.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 2657bff638a..ad8a5f2525d 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -596,6 +596,7 @@ def isinitpath(self, path): def collect(self): this_path = self.fspath.dirpath() pkg_prefix = None + yield Module(this_path.join("__init__.py"), self) for path in this_path.visit(rec=self._recurse, bf=True, sort=True): # we will visit our own __init__.py file, in which case we skip it if path.basename == "__init__.py" and path.dirpath() == this_path: From 4d3c1ab4f09ad352b9b0a274f0d802826771e4ed Mon Sep 17 00:00:00 2001 From: turturica Date: Wed, 22 Aug 2018 21:42:59 -0700 Subject: [PATCH 02/11] Fixes #3854 --- src/_pytest/python.py | 15 ++++++++++----- testing/python/collect.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 51bc28fe5a2..891e071c6b2 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -590,18 +590,23 @@ def collect(self): self.session.config.pluginmanager._duplicatepaths.remove(path) this_path = self.fspath.dirpath() - pkg_prefix = None - yield Module(this_path.join("__init__.py"), self) + pkg_prefixes = set() for path in this_path.visit(rec=self._recurse, bf=True, sort=True): # we will visit our own __init__.py file, in which case we skip it + skip = False if path.basename == "__init__.py" and path.dirpath() == this_path: continue - if pkg_prefix and pkg_prefix in path.parts(): + + if path.isdir() and path.join('__init__.py').check(file=1): + pkg_prefixes.add(path) + + for pkg_prefix in pkg_prefixes: + if pkg_prefix in path.parts() and pkg_prefix.join('__init__.py') == path: + skip = True + if skip: continue for x in self._collectfile(path): yield x - if isinstance(x, Package): - pkg_prefix = path.dirpath() def _get_xunit_setup_teardown(holder, attr_name, param_obj=None): diff --git a/testing/python/collect.py b/testing/python/collect.py index c040cc09e68..7356597cb7a 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -1623,3 +1623,40 @@ def test_package_with_modules(testdir): root.chdir() result = testdir.runpytest("-v", "-s") result.assert_outcomes(passed=2) + + +def test_package_ordering(testdir): + """ + . + └── root + ├── TestRoot.py + ├── __init__.py + ├── sub1 + │ ├── TestSub1.py + │ └── __init__.py + └── sub2 + └── test + ├── TestSub2.py + └── test_in_sub2.py + + """ + testdir.makeini( + """ + [pytest] + python_files=Test*.py + """ + ) + root = testdir.mkpydir("root") + sub1 = root.mkdir("sub1") + sub1.ensure("__init__.py") + sub2 = root.mkdir("sub2") + sub2_test = sub2.mkdir("sub2") + + root.join("TestRoot.py").write("def test_1(): pass") + sub1.join("TestSub1.py").write("def test_2(): pass") + sub2_test.join("TestSub2.py").write("def test_3(): pass") + sub2_test.join("test_in_sub2.py").write("def test_4(): pass") + + # Execute from . + result = testdir.runpytest("-v", "-s") + result.assert_outcomes(passed=3) From 5f8b50c0946b17d5ee5054066cd2ee6dd75e187b Mon Sep 17 00:00:00 2001 From: turturica Date: Thu, 23 Aug 2018 22:48:44 -0700 Subject: [PATCH 03/11] Address #3796 and add a test for it. --- src/_pytest/python.py | 10 +++--- testing/python/fixture.py | 68 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 891e071c6b2..1ecd9310ed4 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -597,14 +597,16 @@ def collect(self): if path.basename == "__init__.py" and path.dirpath() == this_path: continue - if path.isdir() and path.join('__init__.py').check(file=1): - pkg_prefixes.add(path) - for pkg_prefix in pkg_prefixes: - if pkg_prefix in path.parts() and pkg_prefix.join('__init__.py') == path: + if pkg_prefix in path.parts() and pkg_prefix.join('__init__.py') != path: skip = True + if skip: continue + + if path.isdir() and path.join('__init__.py').check(file=1): + pkg_prefixes.add(path) + for x in self._collectfile(path): yield x diff --git a/testing/python/fixture.py b/testing/python/fixture.py index bbfcf775be4..4170f8f3732 100644 --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -3979,3 +3979,71 @@ def test_func(self, f2, f1, m2): items, _ = testdir.inline_genitems() request = FixtureRequest(items[0]) assert request.fixturenames == "s1 p1 m1 m2 c1 f2 f1".split() + + def test_multiple_packages(self, testdir): + """Complex test involving multiple package fixtures. Make sure teardowns + are executed in order. + . + └── root + ├── __init__.py + ├── sub1 + │ ├── __init__.py + │ ├── conftest.py + │ └── test_1.py + └── sub2 + ├── __init__.py + ├── conftest.py + └── test_2.py + """ + root = testdir.mkdir("root") + root.join("__init__.py").write("values = []") + sub1 = root.mkdir("sub1") + sub1.ensure("__init__.py") + sub1.join("conftest.py").write( + dedent( + """\ + import pytest + from .. import values + @pytest.fixture(scope="package") + def fix(): + values.append("pre-sub1") + yield values + values.pop() + """ + ) + ) + sub1.join("test_1.py").write( + dedent( + """\ + from .. import values + def test_1(fix): + assert values == ["pre-sub1"] + """ + ) + ) + sub2 = root.mkdir("sub2") + sub2.ensure("__init__.py") + sub2.join("conftest.py").write( + dedent( + """\ + import pytest + from .. import values + @pytest.fixture(scope="package") + def fix(): + values.append("pre-sub2") + yield values + values.pop() + """ + ) + ) + sub2.join("test_2.py").write( + dedent( + """\ + from .. import values + def test_2(fix): + assert values == ["pre-sub2"] + """ + ) + ) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=2) From 72e6482994974741ddc2058807cf4d3f7b771ced Mon Sep 17 00:00:00 2001 From: turturica Date: Thu, 23 Aug 2018 22:58:36 -0700 Subject: [PATCH 04/11] Make linting happy. --- src/_pytest/python.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 1ecd9310ed4..d694ba67640 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -598,13 +598,16 @@ def collect(self): continue for pkg_prefix in pkg_prefixes: - if pkg_prefix in path.parts() and pkg_prefix.join('__init__.py') != path: + if ( + pkg_prefix in path.parts() + and pkg_prefix.join("__init__.py") != path + ): skip = True if skip: continue - if path.isdir() and path.join('__init__.py').check(file=1): + if path.isdir() and path.join("__init__.py").check(file=1): pkg_prefixes.add(path) for x in self._collectfile(path): From 459b040d2179ba6af0a8c51c1134a9979331959c Mon Sep 17 00:00:00 2001 From: turturica Date: Fri, 24 Aug 2018 11:54:04 -0700 Subject: [PATCH 05/11] Fix dedent after merge. --- testing/python/fixture.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/testing/python/fixture.py b/testing/python/fixture.py index 8306ae315b2..c36395b54f8 100644 --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -3998,7 +3998,7 @@ def test_multiple_packages(self, testdir): sub1 = root.mkdir("sub1") sub1.ensure("__init__.py") sub1.join("conftest.py").write( - dedent( + textwrap.dedent( """\ import pytest from .. import values @@ -4011,7 +4011,7 @@ def fix(): ) ) sub1.join("test_1.py").write( - dedent( + textwrap.dedent( """\ from .. import values def test_1(fix): @@ -4022,7 +4022,7 @@ def test_1(fix): sub2 = root.mkdir("sub2") sub2.ensure("__init__.py") sub2.join("conftest.py").write( - dedent( + textwrap.dedent( """\ import pytest from .. import values @@ -4035,7 +4035,7 @@ def fix(): ) ) sub2.join("test_2.py").write( - dedent( + textwrap.dedent( """\ from .. import values def test_2(fix): From e3df1031ca2662763a5d9ad7d9282e62accb8821 Mon Sep 17 00:00:00 2001 From: turturica Date: Fri, 24 Aug 2018 12:26:18 -0700 Subject: [PATCH 06/11] Add encoding: utf8 for python 2.7 --- testing/python/fixture.py | 1 + 1 file changed, 1 insertion(+) diff --git a/testing/python/fixture.py b/testing/python/fixture.py index c36395b54f8..47503b3407a 100644 --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import textwrap import pytest From dce8df45d5b86c7b20802ee790a8c6da2ecd55cb Mon Sep 17 00:00:00 2001 From: turturica Date: Fri, 24 Aug 2018 15:51:42 -0700 Subject: [PATCH 07/11] Added changelog items. --- changelog/3796.bugfix.rst | 2 ++ changelog/3854.bugfix.rst | 1 + testing/python/fixture.py | 4 ++-- 3 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 changelog/3796.bugfix.rst create mode 100644 changelog/3854.bugfix.rst diff --git a/changelog/3796.bugfix.rst b/changelog/3796.bugfix.rst new file mode 100644 index 00000000000..bc590815f55 --- /dev/null +++ b/changelog/3796.bugfix.rst @@ -0,0 +1,2 @@ +Fixed an issue where teardown of fixtures of consecutive sub-packages were executed once, at the end of the outer +package. \ No newline at end of file diff --git a/changelog/3854.bugfix.rst b/changelog/3854.bugfix.rst new file mode 100644 index 00000000000..72af08aba99 --- /dev/null +++ b/changelog/3854.bugfix.rst @@ -0,0 +1 @@ +Fixes double collection of tests within packages when the filename starts with a capital letter. \ No newline at end of file diff --git a/testing/python/fixture.py b/testing/python/fixture.py index 47503b3407a..f8f5eb54e34 100644 --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -4007,7 +4007,7 @@ def test_multiple_packages(self, testdir): def fix(): values.append("pre-sub1") yield values - values.pop() + assert values.pop() == "pre-sub1" """ ) ) @@ -4031,7 +4031,7 @@ def test_1(fix): def fix(): values.append("pre-sub2") yield values - values.pop() + assert values.pop() == "pre-sub2" """ ) ) From f0226e9329d364084be6bc22f0c11a8053ae9d9c Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 24 Aug 2018 20:10:37 -0300 Subject: [PATCH 08/11] Fix test_package_ordering on Windows --- testing/python/collect.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/testing/python/collect.py b/testing/python/collect.py index 52c06a42e1d..5fe85a012bb 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -1630,21 +1630,21 @@ def test_package_ordering(testdir): """ . └── root - ├── TestRoot.py + ├── check_root.py ├── __init__.py ├── sub1 - │ ├── TestSub1.py + │ ├── check_sub1.py │ └── __init__.py └── sub2 └── test - ├── TestSub2.py + ├── check_sub2.py └── test_in_sub2.py """ testdir.makeini( """ [pytest] - python_files=Test*.py + python_files=check_*.py """ ) root = testdir.mkpydir("root") @@ -1653,9 +1653,9 @@ def test_package_ordering(testdir): sub2 = root.mkdir("sub2") sub2_test = sub2.mkdir("sub2") - root.join("TestRoot.py").write("def test_1(): pass") - sub1.join("TestSub1.py").write("def test_2(): pass") - sub2_test.join("TestSub2.py").write("def test_3(): pass") + root.join("check_root.py").write("def test_1(): pass") + sub1.join("check_sub1.py").write("def test_2(): pass") + sub2_test.join("check_sub2.py").write("def test_3(): pass") sub2_test.join("test_in_sub2.py").write("def test_4(): pass") # Execute from . From 8cf0e46bbf51bcf1d95019ce7c4d501e8cc4b7d7 Mon Sep 17 00:00:00 2001 From: turturica Date: Fri, 24 Aug 2018 16:23:50 -0700 Subject: [PATCH 09/11] test_package_ordering: Collect *.py, but keep a mix of case for filenames. The test doesn't make sense for Windows, because of its case-insensitivity. --- changelog/3796.bugfix.rst | 2 +- changelog/3854.bugfix.rst | 2 +- testing/python/collect.py | 16 +++++++--------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/changelog/3796.bugfix.rst b/changelog/3796.bugfix.rst index bc590815f55..7a115301a95 100644 --- a/changelog/3796.bugfix.rst +++ b/changelog/3796.bugfix.rst @@ -1,2 +1,2 @@ Fixed an issue where teardown of fixtures of consecutive sub-packages were executed once, at the end of the outer -package. \ No newline at end of file +package. diff --git a/changelog/3854.bugfix.rst b/changelog/3854.bugfix.rst index 72af08aba99..6184f03b66e 100644 --- a/changelog/3854.bugfix.rst +++ b/changelog/3854.bugfix.rst @@ -1 +1 @@ -Fixes double collection of tests within packages when the filename starts with a capital letter. \ No newline at end of file +Fixes double collection of tests within packages when the filename starts with a capital letter. diff --git a/testing/python/collect.py b/testing/python/collect.py index 5fe85a012bb..8f4283e4072 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -1630,21 +1630,20 @@ def test_package_ordering(testdir): """ . └── root - ├── check_root.py + ├── Test_root.py ├── __init__.py ├── sub1 - │ ├── check_sub1.py + │ ├── Test_sub1.py │ └── __init__.py └── sub2 └── test - ├── check_sub2.py - └── test_in_sub2.py + └── test_sub2.py """ testdir.makeini( """ [pytest] - python_files=check_*.py + python_files=*.py """ ) root = testdir.mkpydir("root") @@ -1653,10 +1652,9 @@ def test_package_ordering(testdir): sub2 = root.mkdir("sub2") sub2_test = sub2.mkdir("sub2") - root.join("check_root.py").write("def test_1(): pass") - sub1.join("check_sub1.py").write("def test_2(): pass") - sub2_test.join("check_sub2.py").write("def test_3(): pass") - sub2_test.join("test_in_sub2.py").write("def test_4(): pass") + root.join("Test_root.py").write("def test_1(): pass") + sub1.join("Test_sub1.py").write("def test_2(): pass") + sub2_test.join("test_sub2.py").write("def test_3(): pass") # Execute from . result = testdir.runpytest("-v", "-s") From 1e4ecda8845dcf32bdfd395063b797a91d54e80d Mon Sep 17 00:00:00 2001 From: turturica Date: Fri, 24 Aug 2018 18:01:38 -0700 Subject: [PATCH 10/11] Fix the package fixture ordering in Windows. --- src/_pytest/fixtures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index c12691caa59..66efe0f813b 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -93,7 +93,7 @@ def get_scope_package(node, fixturedef): cls = pytest.Package current = node - fixture_package_name = os.path.join(fixturedef.baseid, "__init__.py") + fixture_package_name = "%s/%s" % (fixturedef.baseid, "__init__.py") while current and ( type(current) is not cls or fixture_package_name != current.nodeid ): From c336449729659ede3f3f2535172f911a8c268b86 Mon Sep 17 00:00:00 2001 From: turturica Date: Fri, 24 Aug 2018 18:05:35 -0700 Subject: [PATCH 11/11] Make linting happy. Argh. --- src/_pytest/fixtures.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 66efe0f813b..bfbf7bb546f 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -2,7 +2,6 @@ import functools import inspect -import os import sys import warnings from collections import OrderedDict, deque, defaultdict