diff --git a/AUTHORS b/AUTHORS index 4320d74ff..5398cd1df 100644 --- a/AUTHORS +++ b/AUTHORS @@ -95,6 +95,7 @@ Punyashloka Biswal Quentin Pradet Ralf Schmitt Raphael Pierzina +Roberto Polli Romain Dorgueil Roman Bolshakov Ronny Pfannschmidt diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 31e0a457b..bef5abef8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -120,8 +120,11 @@ time or change existing behaviors in order to make them less surprising/more use fixtures and reports them; + ``--setup-show``: performs normal test execution and additionally shows setup and teardown of fixtures; + + ``--keep-duplicates``: default behavior is now to ignore duplicate paths, you can + retain the old behavior and running multiple times the tests + using the ``--keep-duplicates`` cli argument `#1609`_; - Thanks `@d6e`_, `@kvas-it`_, `@sallner`_ and `@omarkohl`_ for the PRs. + Thanks `@d6e`_, `@kvas-it`_, `@sallner`_, `@ioggstream`_ and `@omarkohl`_ for the PRs. * New CLI flag ``--override-ini``/``-o``: overrides values from the ini file. For example: ``"-o xfail_strict=True"``'. @@ -524,6 +527,7 @@ time or change existing behaviors in order to make them less surprising/more use .. _`traceback style docs`: https://pytest.org/latest/usage.html#modifying-python-traceback-printing +.. _#1609: https://github.com/pytest-dev/pytest/issues/1609 .. _#1422: https://github.com/pytest-dev/pytest/issues/1422 .. _#1379: https://github.com/pytest-dev/pytest/issues/1379 .. _#1366: https://github.com/pytest-dev/pytest/issues/1366 @@ -549,6 +553,7 @@ time or change existing behaviors in order to make them less surprising/more use .. _@rabbbit: https://github.com/rabbbit .. _@hackebrot: https://github.com/hackebrot .. _@pquentin: https://github.com/pquentin +.. _@ioggstream: https://github.com/ioggstream 2.8.7 ===== diff --git a/_pytest/config.py b/_pytest/config.py index 46c53ce68..f179e5661 100644 --- a/_pytest/config.py +++ b/_pytest/config.py @@ -155,6 +155,7 @@ class PytestPluginManager(PluginManager): self._conftestpath2mod = {} self._confcutdir = None self._noconftest = False + self._duplicatepaths = set() self.add_hookspecs(_pytest.hookspec) self.register(self) diff --git a/_pytest/main.py b/_pytest/main.py index 6119fcb03..ca77fdee4 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -63,6 +63,9 @@ def pytest_addoption(parser): group.addoption('--noconftest', action="store_true", dest="noconftest", default=False, help="Don't load any conftest.py files.") + group.addoption('--keepduplicates', '--keep-duplicates', action="store_true", + dest="keepduplicates", default=False, + help="Skip duplicate tests.") group = parser.getgroup("debugconfig", "test session debugging and configuration") @@ -154,7 +157,25 @@ def pytest_ignore_collect(path, config): excludeopt = config.getoption("ignore") if excludeopt: ignore_paths.extend([py.path.local(x) for x in excludeopt]) - return path in ignore_paths + + if path in ignore_paths: + return True + + # Skip duplicate paths. + # TODO: is this called when specifying direct filenames + # from command lines, eg. + # py.test test_a.py test_b.py + keepduplicates = config.getoption("keepduplicates") + duplicate_paths = config.pluginmanager._duplicatepaths + if not keepduplicates: + if path in duplicate_paths: + # TODO should we log this? + return True + else: + duplicate_paths.add(path) + + return False + class FSHookProxy: def __init__(self, fspath, pm, remove_mods): diff --git a/doc/en/example/pythoncollection.rst b/doc/en/example/pythoncollection.rst index e475eee25..b839b5a45 100644 --- a/doc/en/example/pythoncollection.rst +++ b/doc/en/example/pythoncollection.rst @@ -40,6 +40,40 @@ you will see that ``pytest`` only collects test-modules, which do not match the ======= 5 passed in 0.02 seconds ======= +Keeping duplicate paths specified from command line +---------------------------------------------------- + +Default behavior of ``pytest`` is to ignore duplicate paths specified from the command line. +Example:: + + py.test path_a path_a + + ... + collected 1 item + ... + +Just collect tests once. + +To collect duplicate tests, use the ``--keep-duplicates`` option on the cli. +Example:: + + py.test --keep-duplicates path_a path_a + + ... + collected 2 items + ... + +As the collector just works on directories, if you specify twice a single test file, ``pytest`` will +still collect it twice, no matter if the ``--keep-duplicates`` is not specified. +Example:: + + py.test test_a.py test_a.py + + ... + collected 2 items + ... + + Changing directory recursion ----------------------------------------------------- diff --git a/testing/python/collect.py b/testing/python/collect.py index c4b9259bd..fb2264f0a 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -1213,3 +1213,40 @@ def test_syntax_error_with_non_ascii_chars(testdir): '*SyntaxError*', '*1 error in*', ]) + + +def test_skip_duplicates_by_default(testdir): + """Test for issue https://github.com/pytest-dev/pytest/issues/1609 (#1609) + + Ignore duplicate directories. + """ + a = testdir.mkdir("a") + fh = a.join("test_a.py") + fh.write(_pytest._code.Source(""" + import pytest + def test_real(): + pass + """)) + result = testdir.runpytest(a.strpath, a.strpath) + result.stdout.fnmatch_lines([ + '*collected 1 item*', + ]) + + + +def test_keep_duplicates(testdir): + """Test for issue https://github.com/pytest-dev/pytest/issues/1609 (#1609) + + Use --keep-duplicates to collect tests from duplicate directories. + """ + a = testdir.mkdir("a") + fh = a.join("test_a.py") + fh.write(_pytest._code.Source(""" + import pytest + def test_real(): + pass + """)) + result = testdir.runpytest("--keep-duplicates", a.strpath, a.strpath) + result.stdout.fnmatch_lines([ + '*collected 2 item*', + ]) diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index e55761896..98864ea02 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -973,7 +973,7 @@ class TestMetafuncFunctional: """)) sub1.join("test_in_sub1.py").write("def test_1(): pass") sub2.join("test_in_sub2.py").write("def test_2(): pass") - result = testdir.runpytest("-v", "-s", sub1, sub2, sub1) + result = testdir.runpytest("--keep-duplicates", "-v", "-s", sub1, sub2, sub1) result.assert_outcomes(passed=3) def test_generate_same_function_names_issue403(self, testdir):