diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c537d6b79..4c3d2b249 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -18,6 +18,73 @@ with advance notice in the **Deprecations** section of releases. .. towncrier release notes start +pytest 4.0.0 (2018-11-13) +========================= + +Removals +-------- + +- `#3737 `_: **RemovedInPytest4Warnings are now errors by default.** + + Following our plan to remove deprecated features with as little disruption as + possible, all warnings of type ``RemovedInPytest4Warnings`` now generate errors + instead of warning messages. + + **The affected features will be effectively removed in pytest 4.1**, so please consult the + `Deprecations and Removals `__ + section in the docs for directions on how to update existing code. + + In the pytest ``4.0.X`` series, it is possible to change the errors back into warnings as a stop + gap measure by adding this to your ``pytest.ini`` file: + + .. code-block:: ini + + [pytest] + filterwarnings = + ignore::pytest.RemovedInPytest4Warning + + But this will stop working when pytest ``4.1`` is released. + + **If you have concerns** about the removal of a specific feature, please add a + comment to `#4348 `__. + + +- `#4358 `_: Remove the ``::()`` notation to denote a test class instance in node ids. + + Previously, node ids that contain test instances would use ``::()`` to denote the instance like this:: + + test_foo.py::Test::()::test_bar + + The extra ``::()`` was puzzling to most users and has been removed, so that the test id becomes now:: + + test_foo.py::Test::test_bar + + This change could not accompany a deprecation period as is usual when user-facing functionality changes because + it was not really possible to detect when the functionality was being used explicitly. + + The extra ``::()`` might have been removed in some places internally already, + which then led to confusion in places where it was expected, e.g. with + ``--deselect`` (`#4127 `_). + + Test class instances are also not listed with ``--collect-only`` anymore. + + + +Features +-------- + +- `#4270 `_: The ``cache_dir`` option uses ``$TOX_ENV_DIR`` as prefix (if set in the environment). + + This uses a different cache per tox environment by default. + + + +Bug Fixes +--------- + +- `#3554 `_: Fix ``CallInfo.__repr__`` for when the call is not finished yet. + + pytest 3.10.1 (2018-11-11) ========================== diff --git a/changelog/3554.bugfix.rst b/changelog/3554.bugfix.rst deleted file mode 100644 index b4c43cb8f..000000000 --- a/changelog/3554.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix ``CallInfo.__repr__`` for when the call is not finished yet. diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst index e0df8eb1d..504d63484 100644 --- a/doc/en/announce/index.rst +++ b/doc/en/announce/index.rst @@ -6,6 +6,7 @@ Release announcements :maxdepth: 2 + release-4.0.0 release-3.10.1 release-3.10.0 release-3.9.3 diff --git a/doc/en/announce/release-4.0.0.rst b/doc/en/announce/release-4.0.0.rst new file mode 100644 index 000000000..e5ad69b5f --- /dev/null +++ b/doc/en/announce/release-4.0.0.rst @@ -0,0 +1,30 @@ +pytest-4.0.0 +======================================= + +The pytest team is proud to announce the 4.0.0 release! + +pytest is a mature Python testing tool with more than a 2000 tests +against itself, passing on many different interpreters and platforms. + +This release contains a number of bugs fixes and improvements, so users are encouraged +to take a look at the CHANGELOG: + + https://docs.pytest.org/en/latest/changelog.html + +For complete documentation, please visit: + + https://docs.pytest.org/en/latest/ + +As usual, you can upgrade from pypi via: + + pip install -U pytest + +Thanks to all who contributed to this release, among them: + +* Bruno Oliveira +* Daniel Hahler +* Ronny Pfannschmidt + + +Happy testing, +The Pytest Development Team diff --git a/doc/en/example/assertion/failure_demo.py b/doc/en/example/assertion/failure_demo.py index 115fd3e22..a9615c215 100644 --- a/doc/en/example/assertion/failure_demo.py +++ b/doc/en/example/assertion/failure_demo.py @@ -1,6 +1,7 @@ import six import _pytest._code +import pytest from pytest import raises @@ -16,15 +17,11 @@ def otherfunc_multi(a, b): assert a == b +@pytest.mark.parametrize("param1, param2", [(3, 6)]) def test_generative(param1, param2): assert param1 * 2 < param2 -def pytest_generate_tests(metafunc): - if "param1" in metafunc.fixturenames: - metafunc.addcall(funcargs=dict(param1=3, param2=6)) - - class TestFailing(object): def test_simple(self): def f(): diff --git a/doc/en/example/parametrize.rst b/doc/en/example/parametrize.rst index 868dfcf57..dcd59cfb3 100644 --- a/doc/en/example/parametrize.rst +++ b/doc/en/example/parametrize.rst @@ -213,7 +213,6 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia collected 4 items - diff --git a/doc/en/example/pythoncollection.rst b/doc/en/example/pythoncollection.rst index 500873817..9a439cbae 100644 --- a/doc/en/example/pythoncollection.rst +++ b/doc/en/example/pythoncollection.rst @@ -132,7 +132,6 @@ The test collection would look like this:: collected 2 items - @@ -187,7 +186,6 @@ You can always peek at the collection tree without running tests like this:: - diff --git a/doc/en/example/reportingdemo.rst b/doc/en/example/reportingdemo.rst index c599fe6b3..35ec7b6cc 100644 --- a/doc/en/example/reportingdemo.rst +++ b/doc/en/example/reportingdemo.rst @@ -18,15 +18,16 @@ get on the terminal - we are working on that):: failure_demo.py FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF [100%] ================================= FAILURES ================================= - ____________________________ test_generative[0] ____________________________ + ___________________________ test_generative[3-6] ___________________________ param1 = 3, param2 = 6 + @pytest.mark.parametrize("param1, param2", [(3, 6)]) def test_generative(param1, param2): > assert param1 * 2 < param2 E assert (3 * 2) < 6 - failure_demo.py:20: AssertionError + failure_demo.py:22: AssertionError _________________________ TestFailing.test_simple __________________________ self = @@ -43,7 +44,7 @@ get on the terminal - we are working on that):: E + where 42 = .f at 0xdeadbeef>() E + and 43 = .g at 0xdeadbeef>() - failure_demo.py:36: AssertionError + failure_demo.py:33: AssertionError ____________________ TestFailing.test_simple_multiline _____________________ self = @@ -51,7 +52,7 @@ get on the terminal - we are working on that):: def test_simple_multiline(self): > otherfunc_multi(42, 6 * 9) - failure_demo.py:39: + failure_demo.py:36: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ a = 42, b = 54 @@ -60,7 +61,7 @@ get on the terminal - we are working on that):: > assert a == b E assert 42 == 54 - failure_demo.py:16: AssertionError + failure_demo.py:17: AssertionError ___________________________ TestFailing.test_not ___________________________ self = @@ -73,7 +74,7 @@ get on the terminal - we are working on that):: E assert not 42 E + where 42 = .f at 0xdeadbeef>() - failure_demo.py:45: AssertionError + failure_demo.py:42: AssertionError _________________ TestSpecialisedExplanations.test_eq_text _________________ self = @@ -84,7 +85,7 @@ get on the terminal - we are working on that):: E - spam E + eggs - failure_demo.py:50: AssertionError + failure_demo.py:47: AssertionError _____________ TestSpecialisedExplanations.test_eq_similar_text _____________ self = @@ -97,7 +98,7 @@ get on the terminal - we are working on that):: E + foo 2 bar E ? ^ - failure_demo.py:53: AssertionError + failure_demo.py:50: AssertionError ____________ TestSpecialisedExplanations.test_eq_multiline_text ____________ self = @@ -110,7 +111,7 @@ get on the terminal - we are working on that):: E + eggs E bar - failure_demo.py:56: AssertionError + failure_demo.py:53: AssertionError ______________ TestSpecialisedExplanations.test_eq_long_text _______________ self = @@ -127,7 +128,7 @@ get on the terminal - we are working on that):: E + 1111111111b222222222 E ? ^ - failure_demo.py:61: AssertionError + failure_demo.py:58: AssertionError _________ TestSpecialisedExplanations.test_eq_long_text_multiline __________ self = @@ -147,7 +148,7 @@ get on the terminal - we are working on that):: E E ...Full output truncated (7 lines hidden), use '-vv' to show - failure_demo.py:66: AssertionError + failure_demo.py:63: AssertionError _________________ TestSpecialisedExplanations.test_eq_list _________________ self = @@ -158,7 +159,7 @@ get on the terminal - we are working on that):: E At index 2 diff: 2 != 3 E Use -v to get the full diff - failure_demo.py:69: AssertionError + failure_demo.py:66: AssertionError ______________ TestSpecialisedExplanations.test_eq_list_long _______________ self = @@ -171,7 +172,7 @@ get on the terminal - we are working on that):: E At index 100 diff: 1 != 2 E Use -v to get the full diff - failure_demo.py:74: AssertionError + failure_demo.py:71: AssertionError _________________ TestSpecialisedExplanations.test_eq_dict _________________ self = @@ -189,7 +190,7 @@ get on the terminal - we are working on that):: E E ...Full output truncated (2 lines hidden), use '-vv' to show - failure_demo.py:77: AssertionError + failure_demo.py:74: AssertionError _________________ TestSpecialisedExplanations.test_eq_set __________________ self = @@ -207,7 +208,7 @@ get on the terminal - we are working on that):: E E ...Full output truncated (2 lines hidden), use '-vv' to show - failure_demo.py:80: AssertionError + failure_demo.py:77: AssertionError _____________ TestSpecialisedExplanations.test_eq_longer_list ______________ self = @@ -218,7 +219,7 @@ get on the terminal - we are working on that):: E Right contains more items, first extra item: 3 E Use -v to get the full diff - failure_demo.py:83: AssertionError + failure_demo.py:80: AssertionError _________________ TestSpecialisedExplanations.test_in_list _________________ self = @@ -227,7 +228,7 @@ get on the terminal - we are working on that):: > assert 1 in [0, 2, 3, 4, 5] E assert 1 in [0, 2, 3, 4, 5] - failure_demo.py:86: AssertionError + failure_demo.py:83: AssertionError __________ TestSpecialisedExplanations.test_not_in_text_multiline __________ self = @@ -246,7 +247,7 @@ get on the terminal - we are working on that):: E E ...Full output truncated (2 lines hidden), use '-vv' to show - failure_demo.py:90: AssertionError + failure_demo.py:87: AssertionError ___________ TestSpecialisedExplanations.test_not_in_text_single ____________ self = @@ -259,7 +260,7 @@ get on the terminal - we are working on that):: E single foo line E ? +++ - failure_demo.py:94: AssertionError + failure_demo.py:91: AssertionError _________ TestSpecialisedExplanations.test_not_in_text_single_long _________ self = @@ -272,7 +273,7 @@ get on the terminal - we are working on that):: E head head foo tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail E ? +++ - failure_demo.py:98: AssertionError + failure_demo.py:95: AssertionError ______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______ self = @@ -285,7 +286,7 @@ get on the terminal - we are working on that):: E head head fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffftail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail E ? ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - failure_demo.py:102: AssertionError + failure_demo.py:99: AssertionError ______________________________ test_attribute ______________________________ def test_attribute(): @@ -297,7 +298,7 @@ get on the terminal - we are working on that):: E assert 1 == 2 E + where 1 = .Foo object at 0xdeadbeef>.b - failure_demo.py:110: AssertionError + failure_demo.py:107: AssertionError _________________________ test_attribute_instance __________________________ def test_attribute_instance(): @@ -309,7 +310,7 @@ get on the terminal - we are working on that):: E + where 1 = .Foo object at 0xdeadbeef>.b E + where .Foo object at 0xdeadbeef> = .Foo'>() - failure_demo.py:117: AssertionError + failure_demo.py:114: AssertionError __________________________ test_attribute_failure __________________________ def test_attribute_failure(): @@ -322,7 +323,7 @@ get on the terminal - we are working on that):: i = Foo() > assert i.b == 2 - failure_demo.py:128: + failure_demo.py:125: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = .Foo object at 0xdeadbeef> @@ -331,7 +332,7 @@ get on the terminal - we are working on that):: > raise Exception("Failed to get attrib") E Exception: Failed to get attrib - failure_demo.py:123: Exception + failure_demo.py:120: Exception _________________________ test_attribute_multiple __________________________ def test_attribute_multiple(): @@ -348,7 +349,7 @@ get on the terminal - we are working on that):: E + and 2 = .Bar object at 0xdeadbeef>.b E + where .Bar object at 0xdeadbeef> = .Bar'>() - failure_demo.py:138: AssertionError + failure_demo.py:135: AssertionError __________________________ TestRaises.test_raises __________________________ self = @@ -357,7 +358,7 @@ get on the terminal - we are working on that):: s = "qwe" # NOQA > raises(TypeError, "int(s)") - failure_demo.py:148: + failure_demo.py:145: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ > int(s) @@ -372,7 +373,7 @@ get on the terminal - we are working on that):: > raises(IOError, "int('3')") E Failed: DID NOT RAISE - failure_demo.py:151: Failed + failure_demo.py:148: Failed __________________________ TestRaises.test_raise ___________________________ self = @@ -381,7 +382,7 @@ get on the terminal - we are working on that):: > raise ValueError("demo error") E ValueError: demo error - failure_demo.py:154: ValueError + failure_demo.py:151: ValueError ________________________ TestRaises.test_tupleerror ________________________ self = @@ -390,7 +391,7 @@ get on the terminal - we are working on that):: > a, b = [1] # NOQA E ValueError: not enough values to unpack (expected 2, got 1) - failure_demo.py:157: ValueError + failure_demo.py:154: ValueError ______ TestRaises.test_reinterpret_fails_with_print_for_the_fun_of_it ______ self = @@ -401,7 +402,7 @@ get on the terminal - we are working on that):: > a, b = items.pop() E TypeError: 'int' object is not iterable - failure_demo.py:162: TypeError + failure_demo.py:159: TypeError --------------------------- Captured stdout call --------------------------- items is [1, 2, 3] ________________________ TestRaises.test_some_error ________________________ @@ -412,7 +413,7 @@ get on the terminal - we are working on that):: > if namenotexi: # NOQA E NameError: name 'namenotexi' is not defined - failure_demo.py:165: NameError + failure_demo.py:162: NameError ____________________ test_dynamic_compile_shows_nicely _____________________ def test_dynamic_compile_shows_nicely(): @@ -427,14 +428,14 @@ get on the terminal - we are working on that):: sys.modules[name] = module > module.foo() - failure_demo.py:183: + failure_demo.py:180: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ def foo(): > assert 1 == 0 E AssertionError - <2-codegen 'abc-123' $REGENDOC_TMPDIR/assertion/failure_demo.py:180>:2: AssertionError + <2-codegen 'abc-123' $REGENDOC_TMPDIR/assertion/failure_demo.py:177>:2: AssertionError ____________________ TestMoreErrors.test_complex_error _____________________ self = @@ -448,9 +449,9 @@ get on the terminal - we are working on that):: > somefunc(f(), g()) - failure_demo.py:194: + failure_demo.py:191: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - failure_demo.py:12: in somefunc + failure_demo.py:13: in somefunc otherfunc(x, y) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ @@ -460,7 +461,7 @@ get on the terminal - we are working on that):: > assert a == b E assert 44 == 43 - failure_demo.py:8: AssertionError + failure_demo.py:9: AssertionError ___________________ TestMoreErrors.test_z1_unpack_error ____________________ self = @@ -470,7 +471,7 @@ get on the terminal - we are working on that):: > a, b = items E ValueError: not enough values to unpack (expected 2, got 0) - failure_demo.py:198: ValueError + failure_demo.py:195: ValueError ____________________ TestMoreErrors.test_z2_type_error _____________________ self = @@ -480,7 +481,7 @@ get on the terminal - we are working on that):: > a, b = items E TypeError: 'int' object is not iterable - failure_demo.py:202: TypeError + failure_demo.py:199: TypeError ______________________ TestMoreErrors.test_startswith ______________________ self = @@ -493,7 +494,7 @@ get on the terminal - we are working on that):: E + where False = ('456') E + where = '123'.startswith - failure_demo.py:207: AssertionError + failure_demo.py:204: AssertionError __________________ TestMoreErrors.test_startswith_nested ___________________ self = @@ -512,7 +513,7 @@ get on the terminal - we are working on that):: E + where '123' = .f at 0xdeadbeef>() E + and '456' = .g at 0xdeadbeef>() - failure_demo.py:216: AssertionError + failure_demo.py:213: AssertionError _____________________ TestMoreErrors.test_global_func ______________________ self = @@ -523,7 +524,7 @@ get on the terminal - we are working on that):: E + where False = isinstance(43, float) E + where 43 = globf(42) - failure_demo.py:219: AssertionError + failure_demo.py:216: AssertionError _______________________ TestMoreErrors.test_instance _______________________ self = @@ -534,7 +535,7 @@ get on the terminal - we are working on that):: E assert 42 != 42 E + where 42 = .x - failure_demo.py:223: AssertionError + failure_demo.py:220: AssertionError _______________________ TestMoreErrors.test_compare ________________________ self = @@ -544,7 +545,7 @@ get on the terminal - we are working on that):: E assert 11 < 5 E + where 11 = globf(10) - failure_demo.py:226: AssertionError + failure_demo.py:223: AssertionError _____________________ TestMoreErrors.test_try_finally ______________________ self = @@ -555,7 +556,7 @@ get on the terminal - we are working on that):: > assert x == 0 E assert 1 == 0 - failure_demo.py:231: AssertionError + failure_demo.py:228: AssertionError ___________________ TestCustomAssertMsg.test_single_line ___________________ self = @@ -570,7 +571,7 @@ get on the terminal - we are working on that):: E assert 1 == 2 E + where 1 = .A'>.a - failure_demo.py:242: AssertionError + failure_demo.py:239: AssertionError ____________________ TestCustomAssertMsg.test_multiline ____________________ self = @@ -589,7 +590,7 @@ get on the terminal - we are working on that):: E assert 1 == 2 E + where 1 = .A'>.a - failure_demo.py:249: AssertionError + failure_demo.py:246: AssertionError ___________________ TestCustomAssertMsg.test_custom_repr ___________________ self = @@ -611,12 +612,5 @@ get on the terminal - we are working on that):: E assert 1 == 2 E + where 1 = This is JSON\n{\n 'foo': 'bar'\n}.a - failure_demo.py:262: AssertionError - ============================= warnings summary ============================= - failure_demo.py:25 - $REGENDOC_TMPDIR/assertion/failure_demo.py:25: RemovedInPytest4Warning: Metafunc.addcall is deprecated and scheduled to be removed in pytest 4.0. - Please use Metafunc.parametrize instead. - metafunc.addcall(funcargs=dict(param1=3, param2=6)) - - -- Docs: https://docs.pytest.org/en/latest/warnings.html - ================== 42 failed, 1 warnings in 0.12 seconds =================== + failure_demo.py:259: AssertionError + ======================== 42 failed in 0.12 seconds ========================= diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index b16070287..2edaf9a74 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -496,7 +496,7 @@ If we run this:: test_step.py:11: AssertionError ========================= short test summary info ========================== - XFAIL test_step.py::TestUserHandling::()::test_deletion + XFAIL test_step.py::TestUserHandling::test_deletion reason: previous test failed (test_modification) ============== 1 failed, 2 passed, 1 xfailed in 0.12 seconds =============== diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index a78d857f6..d762d867d 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -9,6 +9,7 @@ from __future__ import division from __future__ import print_function import json +import os from collections import OrderedDict import attr @@ -275,7 +276,10 @@ def pytest_addoption(parser): dest="cacheclear", help="remove all cache contents at start of test run.", ) - parser.addini("cache_dir", default=".pytest_cache", help="cache directory path.") + cache_dir_default = ".pytest_cache" + if "TOX_ENV_DIR" in os.environ: + cache_dir_default = os.path.join(os.environ["TOX_ENV_DIR"], cache_dir_default) + parser.addini("cache_dir", default=cache_dir_default, help="cache directory path.") group.addoption( "--lfnf", "--last-failed-no-failures", diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 0fc895546..60b455bb3 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -851,11 +851,13 @@ class Config(object): args, self.option, namespace=self.option ) if not args: - cwd = os.getcwd() - if cwd == self.rootdir: - args = self.getini("testpaths") + if self.invocation_dir == self.rootdir: + args = [ + str(self.invocation_dir.join(x, abs=True)) + for x in self.getini("testpaths") + ] if not args: - args = [cwd] + args = [str(self.invocation_dir)] self.args = args except PrintHelp: pass diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 75f515614..df4f1c8fb 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -9,6 +9,7 @@ import os import pkgutil import sys +import attr import py import six @@ -368,6 +369,16 @@ class Failed(Exception): """ signals a stop as failed test run. """ +@attr.s +class _bestrelpath_cache(dict): + path = attr.ib() + + def __missing__(self, path): + r = self.path.bestrelpath(path) + self[path] = r + return r + + class Session(nodes.FSCollector): Interrupted = Interrupted Failed = Failed @@ -386,11 +397,16 @@ class Session(nodes.FSCollector): self._initialpaths = frozenset() # Keep track of any collected nodes in here, so we don't duplicate fixtures self._node_cache = {} + self._bestrelpathcache = _bestrelpath_cache(config.rootdir) # Dirnames of pkgs with dunder-init files. self._pkg_roots = {} self.config.pluginmanager.register(self, name="session") + def _node_location_to_relpath(self, node_path): + # bestrelpath is a quite slow function + return self._bestrelpathcache[node_path] + @hookimpl(tryfirst=True) def pytest_collectstart(self): if self.shouldfail: diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 032d4e1d2..86e541152 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -27,7 +27,7 @@ def _splitnode(nodeid): '' 'testing/code' 'testing/code/test_excinfo.py' - 'testing/code/test_excinfo.py::TestFormattedExcinfo::()' + 'testing/code/test_excinfo.py::TestFormattedExcinfo' Return values are lists e.g. [] @@ -39,7 +39,7 @@ def _splitnode(nodeid): # If there is no root node at all, return an empty list so the caller's logic can remain sane return [] parts = nodeid.split(SEP) - # Replace single last element 'test_foo.py::Bar::()' with multiple elements 'test_foo.py', 'Bar', '()' + # Replace single last element 'test_foo.py::Bar' with multiple elements 'test_foo.py', 'Bar' parts[-1:] = parts[-1].split("::") return parts @@ -47,7 +47,7 @@ def _splitnode(nodeid): def ischildnode(baseid, nodeid): """Return True if the nodeid is a child node of the baseid. - E.g. 'foo/bar::Baz::()' is a child of 'foo', 'foo/bar' and 'foo/bar::Baz', but not of 'foo/blorp' + E.g. 'foo/bar::Baz' is a child of 'foo', 'foo/bar' and 'foo/bar::Baz', but not of 'foo/blorp' """ base_parts = _splitnode(baseid) node_parts = _splitnode(nodeid) @@ -107,10 +107,12 @@ class Node(object): self._name2pseudofixturedef = {} if nodeid is not None: + assert "::()" not in nodeid self._nodeid = nodeid else: - assert parent is not None - self._nodeid = self.parent.nodeid + "::" + self.name + self._nodeid = self.parent.nodeid + if self.name != "()": + self._nodeid += "::" + self.name @property def ihook(self): @@ -523,13 +525,7 @@ class Item(Node): return self._location except AttributeError: location = self.reportinfo() - # bestrelpath is a quite slow function - cache = self.config.__dict__.setdefault("_bestrelpathcache", {}) - try: - fspath = cache[location[0]] - except KeyError: - fspath = self.session.fspath.bestrelpath(location[0]) - cache[location[0]] = fspath + fspath = self.session._node_location_to_relpath(location[0]) location = (fspath, location[1], str(location[2])) self._location = location return location diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 8d5a6b141..24ddf4e9e 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -502,6 +502,7 @@ class Testdir(object): self.tmpdir = tmpdir_factory.mktemp(name, numbered=True) self.test_tmproot = tmpdir_factory.mktemp("tmp-" + name, numbered=True) os.environ["PYTEST_DEBUG_TEMPROOT"] = str(self.test_tmproot) + os.environ.pop("TOX_ENV_DIR", None) # Ensure that it is not used for caching. self.plugins = [] self._cwd_snapshot = CwdSnapshot() self._sys_path_snapshot = SysPathsSnapshot() diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index c2de759b3..86298a7aa 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -60,8 +60,7 @@ def pytest_terminal_summary(terminalreporter): tr.write_line("") tr.write_line("(0.00 durations hidden. Use -vv to show these durations.)") break - nodeid = rep.nodeid.replace("::()::", "::") - tr.write_line("%02.2fs %-8s %s" % (rep.duration, rep.when, nodeid)) + tr.write_line("%02.2fs %-8s %s" % (rep.duration, rep.when, rep.nodeid)) def pytest_sessionstart(session): diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index dde2750be..f19325088 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -605,9 +605,7 @@ class TerminalReporter(object): self._tw.line("%s: %d" % (name, count)) else: for item in items: - nodeid = item.nodeid - nodeid = nodeid.replace("::()::", "::") - self._tw.line(nodeid) + self._tw.line(item.nodeid) return stack = [] indent = "" @@ -619,8 +617,8 @@ class TerminalReporter(object): stack.pop() for col in needed_collectors[len(stack) :]: stack.append(col) - # if col.name == "()": - # continue + if col.name == "()": # Skip Instances. + continue indent = (len(stack) - 1) * " " self._tw.line("%s%s" % (indent, col)) @@ -687,7 +685,7 @@ class TerminalReporter(object): # collect_fspath comes from testid which has a "/"-normalized path if fspath: - res = mkrel(nodeid).replace("::()", "") # parens-normalization + res = mkrel(nodeid) if self.verbosity >= 2 and nodeid.split("::")[0] != fspath.replace( "\\", nodes.SEP ): diff --git a/src/_pytest/warnings.py b/src/_pytest/warnings.py index 52afbe784..9de9d01d5 100644 --- a/src/_pytest/warnings.py +++ b/src/_pytest/warnings.py @@ -9,6 +9,8 @@ from contextlib import contextmanager import pytest from _pytest import compat +SHOW_PYTEST_WARNINGS_ARG = "-Walways::pytest.RemovedInPytest4Warning" + def _setoption(wmod, arg): """ @@ -77,6 +79,8 @@ def catch_warnings_for_item(config, ihook, when, item): warnings.filterwarnings("always", category=DeprecationWarning) warnings.filterwarnings("always", category=PendingDeprecationWarning) + warnings.filterwarnings("error", category=pytest.RemovedInPytest4Warning) + # filters should have this precedence: mark, cmdline options, ini # filters should be applied in the inverse order of precedence for arg in inifilters: diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 315bd570a..37ec6d84d 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -14,6 +14,7 @@ import six import pytest from _pytest.main import EXIT_NOTESTSCOLLECTED from _pytest.main import EXIT_USAGEERROR +from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG def prepend_pythonpath(*dirs): @@ -307,7 +308,7 @@ class TestGeneralUsage(object): """ ) p = testdir.makepyfile("""def test_func(x): pass""") - res = testdir.runpytest(p) + res = testdir.runpytest(p, SHOW_PYTEST_WARNINGS_ARG) assert res.ret == 0 res.stdout.fnmatch_lines(["*1 skipped*"]) @@ -321,7 +322,9 @@ class TestGeneralUsage(object): pass """ ) - res = testdir.runpytest(p.basename + "::" + "test_func[1]") + res = testdir.runpytest( + p.basename + "::" + "test_func[1]", SHOW_PYTEST_WARNINGS_ARG + ) assert res.ret == 0 res.stdout.fnmatch_lines(["*1 passed*"]) diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 37ade6d6a..bc120b263 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -5,11 +5,11 @@ from __future__ import print_function import os import pytest +from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG pytestmark = pytest.mark.pytester_example_path("deprecated") -@pytest.mark.filterwarnings("default") def test_yield_tests_deprecation(testdir): testdir.makepyfile( """ @@ -23,7 +23,7 @@ def test_yield_tests_deprecation(testdir): yield func1, 1, 1 """ ) - result = testdir.runpytest() + result = testdir.runpytest(SHOW_PYTEST_WARNINGS_ARG) result.stdout.fnmatch_lines( [ "*test_yield_tests_deprecation.py:3:*yield tests are deprecated*", @@ -41,7 +41,7 @@ def test_compat_properties_deprecation(testdir): print(request.node.Module) """ ) - result = testdir.runpytest() + result = testdir.runpytest(SHOW_PYTEST_WARNINGS_ARG) result.stdout.fnmatch_lines( [ "*test_compat_properties_deprecation.py:2:*usage of Function.Module is deprecated, " @@ -63,7 +63,7 @@ def test_cached_setup_deprecation(testdir): assert fix == 1 """ ) - result = testdir.runpytest() + result = testdir.runpytest(SHOW_PYTEST_WARNINGS_ARG) result.stdout.fnmatch_lines( [ "*test_cached_setup_deprecation.py:4:*cached_setup is deprecated*", @@ -93,7 +93,7 @@ def test_custom_class_deprecation(testdir): pass """ ) - result = testdir.runpytest() + result = testdir.runpytest(SHOW_PYTEST_WARNINGS_ARG) result.stdout.fnmatch_lines( [ '*test_custom_class_deprecation.py:1:*"Class" objects in collectors of type "MyModule*', @@ -102,7 +102,6 @@ def test_custom_class_deprecation(testdir): ) -@pytest.mark.filterwarnings("default") def test_funcarg_prefix_deprecation(testdir): testdir.makepyfile( """ @@ -113,7 +112,7 @@ def test_funcarg_prefix_deprecation(testdir): assert value == 10 """ ) - result = testdir.runpytest("-ra") + result = testdir.runpytest("-ra", SHOW_PYTEST_WARNINGS_ARG) result.stdout.fnmatch_lines( [ ( @@ -198,7 +197,6 @@ def test_resultlog_is_deprecated(testdir): ) -@pytest.mark.filterwarnings("always:Metafunc.addcall is deprecated") def test_metafunc_addcall_deprecated(testdir): testdir.makepyfile( """ @@ -209,7 +207,7 @@ def test_metafunc_addcall_deprecated(testdir): pass """ ) - res = testdir.runpytest("-s") + res = testdir.runpytest("-s", SHOW_PYTEST_WARNINGS_ARG) assert res.ret == 0 res.stdout.fnmatch_lines( ["*Metafunc.addcall is deprecated*", "*2 passed, 2 warnings*"] @@ -263,7 +261,7 @@ def test_pytest_plugins_in_non_top_level_conftest_deprecated(testdir): pass """ ) - res = testdir.runpytest() + res = testdir.runpytest(SHOW_PYTEST_WARNINGS_ARG) assert res.ret == 0 msg = str(PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST).splitlines()[0] res.stdout.fnmatch_lines( @@ -292,6 +290,7 @@ def test_pytest_plugins_in_non_top_level_conftest_deprecated_pyargs( testdir.syspathinsert(testdir.tmpdir.join("src")) args = ("--pyargs", "pkg") if use_pyargs else () + args += (SHOW_PYTEST_WARNINGS_ARG,) res = testdir.runpytest(*args) assert res.ret == 0 msg = str(PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST).splitlines()[0] diff --git a/testing/python/collect.py b/testing/python/collect.py index 61039a506..2e5d62dd5 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -7,6 +7,7 @@ import _pytest._code import pytest from _pytest.main import EXIT_NOTESTSCOLLECTED from _pytest.nodes import Collector +from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG class TestModule(object): @@ -370,7 +371,7 @@ class TestGenerator(object): yield assert_order_of_execution """ ) - reprec = testdir.inline_run(o) + reprec = testdir.inline_run(o, SHOW_PYTEST_WARNINGS_ARG) passed, skipped, failed = reprec.countoutcomes() assert passed == 7 assert not skipped and not failed @@ -404,7 +405,7 @@ class TestGenerator(object): yield assert_order_of_execution """ ) - reprec = testdir.inline_run(o) + reprec = testdir.inline_run(o, SHOW_PYTEST_WARNINGS_ARG) passed, skipped, failed = reprec.countoutcomes() assert passed == 4 assert not skipped and not failed @@ -448,7 +449,7 @@ class TestGenerator(object): assert setuplist[1] != setuplist[2], setuplist """ ) - reprec = testdir.inline_run(o, "-v") + reprec = testdir.inline_run(o, "-v", SHOW_PYTEST_WARNINGS_ARG) passed, skipped, failed = reprec.countoutcomes() assert passed == 4 assert not skipped and not failed @@ -1380,7 +1381,7 @@ def test_collector_attributes(testdir): pass """ ) - result = testdir.runpytest() + result = testdir.runpytest(SHOW_PYTEST_WARNINGS_ARG) result.stdout.fnmatch_lines(["*1 passed*"]) @@ -1407,10 +1408,8 @@ def test_customize_through_attributes(testdir): pass """ ) - result = testdir.runpytest("--collect-only") - result.stdout.fnmatch_lines( - ["*MyClass*", "*MyInstance*", "*MyFunction*test_hello*"] - ) + result = testdir.runpytest("--collect-only", SHOW_PYTEST_WARNINGS_ARG) + result.stdout.fnmatch_lines(["*MyClass*", "*MyFunction*test_hello*"]) def test_unorderable_types(testdir): diff --git a/testing/python/fixture.py b/testing/python/fixture.py index 86cd51114..b483fff45 100644 --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -8,6 +8,7 @@ from _pytest.fixtures import FixtureLookupError from _pytest.fixtures import FixtureRequest from _pytest.pathlib import Path from _pytest.pytester import get_public_names +from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG def test_getfuncargnames(): @@ -975,7 +976,8 @@ class TestRequestCachedSetup(object): class TestClass(object): def test_func1a(self, something): assert something == "hello" - """ + """, + SHOW_PYTEST_WARNINGS_ARG, ) reprec.assertoutcome(passed=2) @@ -997,7 +999,8 @@ class TestRequestCachedSetup(object): assert something == "hello" def test_func2b(self, something): assert something == "hello" - """ + """, + SHOW_PYTEST_WARNINGS_ARG, ) reprec.assertoutcome(passed=4) @@ -1057,7 +1060,7 @@ class TestRequestCachedSetup(object): assert arg1 != arg2 """ ) - result = testdir.runpytest("-v") + result = testdir.runpytest("-v", SHOW_PYTEST_WARNINGS_ARG) result.stdout.fnmatch_lines(["*1 passed*"]) def test_request_cached_setup_getfixturevalue(self, testdir): @@ -1076,7 +1079,7 @@ class TestRequestCachedSetup(object): assert arg1 == 11 """ ) - result = testdir.runpytest("-v") + result = testdir.runpytest("-v", SHOW_PYTEST_WARNINGS_ARG) result.stdout.fnmatch_lines(["*1 passed*"]) def test_request_cached_setup_functional(self, testdir): @@ -1107,7 +1110,7 @@ class TestRequestCachedSetup(object): assert test_0.values == [2] """ ) - result = testdir.runpytest("-v") + result = testdir.runpytest("-v", SHOW_PYTEST_WARNINGS_ARG) result.stdout.fnmatch_lines(["*3 passed*"]) def test_issue117_sessionscopeteardown(self, testdir): @@ -1126,7 +1129,7 @@ class TestRequestCachedSetup(object): pass """ ) - result = testdir.runpytest() + result = testdir.runpytest(SHOW_PYTEST_WARNINGS_ARG) assert result.ret != 0 result.stdout.fnmatch_lines(["*3/x*", "*ZeroDivisionError*"]) @@ -1868,7 +1871,7 @@ class TestAutouseManagement(object): yield f, -3 """ ) - reprec = testdir.inline_run() + reprec = testdir.inline_run(SHOW_PYTEST_WARNINGS_ARG) reprec.assertoutcome(passed=2) def test_funcarg_and_setup(self, testdir): @@ -2348,7 +2351,7 @@ class TestFixtureMarker(object): """ % method ) - result = testdir.runpytest() + result = testdir.runpytest(SHOW_PYTEST_WARNINGS_ARG) assert result.ret != 0 result.stdout.fnmatch_lines( ["*ScopeMismatch*You tried*function*session*request*"] diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 77fefdcac..1a9cbf408 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -10,6 +10,7 @@ from hypothesis import strategies import pytest from _pytest import fixtures from _pytest import python +from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG PY3 = sys.version_info >= (3, 0) @@ -444,7 +445,7 @@ class TestMetafunc(object): pass """ ) - result = testdir.runpytest("--collect-only") + result = testdir.runpytest("--collect-only", SHOW_PYTEST_WARNINGS_ARG) result.stdout.fnmatch_lines( [ "", @@ -866,7 +867,7 @@ class TestMetafuncFunctional(object): assert metafunc.cls == TestClass """ ) - result = testdir.runpytest(p, "-v") + result = testdir.runpytest(p, "-v", SHOW_PYTEST_WARNINGS_ARG) result.assert_outcomes(passed=2) def test_addcall_with_two_funcargs_generators(self, testdir): @@ -887,7 +888,7 @@ class TestMetafuncFunctional(object): assert arg1 == arg2 """ ) - result = testdir.runpytest("-v", p) + result = testdir.runpytest("-v", p, SHOW_PYTEST_WARNINGS_ARG) result.stdout.fnmatch_lines( ["*test_myfunc*0*PASS*", "*test_myfunc*1*FAIL*", "*1 failed, 1 passed*"] ) @@ -910,7 +911,7 @@ class TestMetafuncFunctional(object): assert arg1 in (10, 20) """ ) - result = testdir.runpytest("-v", p) + result = testdir.runpytest("-v", p, SHOW_PYTEST_WARNINGS_ARG) result.stdout.fnmatch_lines( [ "*test_func1*0*PASS*", @@ -960,7 +961,7 @@ class TestMetafuncFunctional(object): assert arg1 == arg2 """ ) - result = testdir.runpytest("-v", p) + result = testdir.runpytest("-v", p, SHOW_PYTEST_WARNINGS_ARG) result.stdout.fnmatch_lines( [ "*test_myfunc*hello*PASS*", @@ -980,7 +981,7 @@ class TestMetafuncFunctional(object): assert hello == "world" """ ) - result = testdir.runpytest("-v", p) + result = testdir.runpytest("-v", p, SHOW_PYTEST_WARNINGS_ARG) result.stdout.fnmatch_lines(["*test_myfunc*hello*PASS*", "*1 passed*"]) def test_two_functions_not_same_instance(self, testdir): @@ -996,7 +997,7 @@ class TestMetafuncFunctional(object): self.x = 1 """ ) - result = testdir.runpytest("-v", p) + result = testdir.runpytest("-v", p, SHOW_PYTEST_WARNINGS_ARG) result.stdout.fnmatch_lines( ["*test_func*0*PASS*", "*test_func*1*PASS*", "*2 pass*"] ) @@ -1014,7 +1015,7 @@ class TestMetafuncFunctional(object): self.val = 1 """ ) - result = testdir.runpytest(p) + result = testdir.runpytest(p, SHOW_PYTEST_WARNINGS_ARG) result.assert_outcomes(passed=1) def test_parametrize_functional2(self, testdir): @@ -1536,7 +1537,7 @@ class TestMarkersWithParametrization(object): assert n + 1 == expected """ testdir.makepyfile(s) - rec = testdir.inline_run("-m", "foo") + rec = testdir.inline_run("-m", "foo", SHOW_PYTEST_WARNINGS_ARG) passed, skipped, fail = rec.listoutcomes() assert len(passed) == 1 assert len(skipped) == 0 @@ -1576,7 +1577,7 @@ class TestMarkersWithParametrization(object): assert n + 1 == expected """ testdir.makepyfile(s) - reprec = testdir.inline_run() + reprec = testdir.inline_run(SHOW_PYTEST_WARNINGS_ARG) # xfail is skip?? reprec.assertoutcome(passed=2, skipped=1) @@ -1593,7 +1594,7 @@ class TestMarkersWithParametrization(object): assert n % 2 == 0 """ testdir.makepyfile(s) - reprec = testdir.inline_run() + reprec = testdir.inline_run(SHOW_PYTEST_WARNINGS_ARG) reprec.assertoutcome(passed=2, skipped=1) def test_xfail_with_arg(self, testdir): @@ -1609,7 +1610,7 @@ class TestMarkersWithParametrization(object): assert n + 1 == expected """ testdir.makepyfile(s) - reprec = testdir.inline_run() + reprec = testdir.inline_run(SHOW_PYTEST_WARNINGS_ARG) reprec.assertoutcome(passed=2, skipped=1) def test_xfail_with_kwarg(self, testdir): @@ -1625,7 +1626,7 @@ class TestMarkersWithParametrization(object): assert n + 1 == expected """ testdir.makepyfile(s) - reprec = testdir.inline_run() + reprec = testdir.inline_run(SHOW_PYTEST_WARNINGS_ARG) reprec.assertoutcome(passed=2, skipped=1) def test_xfail_with_arg_and_kwarg(self, testdir): @@ -1641,7 +1642,7 @@ class TestMarkersWithParametrization(object): assert n + 1 == expected """ testdir.makepyfile(s) - reprec = testdir.inline_run() + reprec = testdir.inline_run(SHOW_PYTEST_WARNINGS_ARG) reprec.assertoutcome(passed=2, skipped=1) @pytest.mark.parametrize("strict", [True, False]) @@ -1660,7 +1661,7 @@ class TestMarkersWithParametrization(object): strict=strict ) testdir.makepyfile(s) - reprec = testdir.inline_run() + reprec = testdir.inline_run(SHOW_PYTEST_WARNINGS_ARG) passed, failed = (2, 1) if strict else (3, 0) reprec.assertoutcome(passed=passed, failed=failed) @@ -1684,7 +1685,7 @@ class TestMarkersWithParametrization(object): assert n + 1 == expected """ testdir.makepyfile(s) - reprec = testdir.inline_run() + reprec = testdir.inline_run(SHOW_PYTEST_WARNINGS_ARG) reprec.assertoutcome(passed=2, skipped=2) @pytest.mark.issue290 diff --git a/testing/test_cacheprovider.py b/testing/test_cacheprovider.py index cd888dce1..2b8ca2e18 100644 --- a/testing/test_cacheprovider.py +++ b/testing/test_cacheprovider.py @@ -148,15 +148,17 @@ class TestNewAPI(object): assert testdir.tmpdir.join("custom_cache_dir").isdir() -def test_cache_reportheader(testdir): - testdir.makepyfile( - """ - def test_hello(): - pass - """ - ) +@pytest.mark.parametrize("env", ((), ("TOX_ENV_DIR", "/tox_env_dir"))) +def test_cache_reportheader(env, testdir, monkeypatch): + testdir.makepyfile("""def test_foo(): pass""") + if env: + monkeypatch.setenv(*env) + expected = os.path.join(env[1], ".pytest_cache") + else: + monkeypatch.delenv("TOX_ENV_DIR", raising=False) + expected = ".pytest_cache" result = testdir.runpytest("-v") - result.stdout.fnmatch_lines(["cachedir: .pytest_cache"]) + result.stdout.fnmatch_lines(["cachedir: %s" % expected]) def test_cache_reportheader_external_abspath(testdir, tmpdir_factory): diff --git a/testing/test_collection.py b/testing/test_collection.py index 62bc0caf8..9385f5461 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -512,13 +512,8 @@ class TestSession(object): pass """ ) - normid = p.basename + "::TestClass::()::test_method" - for id in [ - p.basename, - p.basename + "::TestClass", - p.basename + "::TestClass::()", - normid, - ]: + normid = p.basename + "::TestClass::test_method" + for id in [p.basename, p.basename + "::TestClass", normid]: items, hookrec = testdir.inline_genitems(id) assert len(items) == 1 assert items[0].name == "test_method" @@ -627,7 +622,7 @@ class TestSession(object): items, hookrec = testdir.inline_genitems(arg) assert len(items) == 1 item, = items - assert item.nodeid.endswith("TestClass::()::test_method") + assert item.nodeid.endswith("TestClass::test_method") # ensure we are reporting the collection of the single test item (#2464) assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"] @@ -1051,8 +1046,44 @@ def test_collect_handles_raising_on_dunder_class(testdir): """ ) result = testdir.runpytest() - assert result.ret == 0 result.stdout.fnmatch_lines(["*1 passed in*"]) + assert result.ret == 0 + + +def test_collect_with_chdir_during_import(testdir): + subdir = testdir.tmpdir.mkdir("sub") + testdir.tmpdir.join("conftest.py").write( + textwrap.dedent( + """ + import os + os.chdir(%r) + """ + % (str(subdir),) + ) + ) + testdir.makepyfile( + """ + def test_1(): + import os + assert os.getcwd() == %r + """ + % (str(subdir),) + ) + with testdir.tmpdir.as_cwd(): + result = testdir.runpytest() + result.stdout.fnmatch_lines(["*1 passed in*"]) + assert result.ret == 0 + + # Handles relative testpaths. + testdir.makeini( + """ + [pytest] + testpaths = . + """ + ) + with testdir.tmpdir.as_cwd(): + result = testdir.runpytest("--collect-only") + result.stdout.fnmatch_lines(["collected 1 item"]) @pytest.mark.skipif( diff --git a/testing/test_config.py b/testing/test_config.py index 1be266387..605d28aa0 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -12,6 +12,7 @@ from _pytest.config.findpaths import determine_setup from _pytest.config.findpaths import get_common_ancestor from _pytest.config.findpaths import getcfg from _pytest.main import EXIT_NOTESTSCOLLECTED +from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG class TestParseIni(object): @@ -808,7 +809,7 @@ class TestLegacyWarning(object): assert conftest.values == [1] """ ) - result = testdir.runpytest() + result = testdir.runpytest(SHOW_PYTEST_WARNINGS_ARG) result.stdout.fnmatch_lines( ["*hello", "*config.warn has been deprecated*", "*1 passed*"] ) @@ -832,10 +833,12 @@ class TestLegacyWarning(object): code_kw=code_kw, message_kw=message_kw ) ) - result = testdir.runpytest("--disable-pytest-warnings") + result = testdir.runpytest( + "--disable-pytest-warnings", SHOW_PYTEST_WARNINGS_ARG + ) assert "hello" not in result.stdout.str() - result = testdir.runpytest() + result = testdir.runpytest(SHOW_PYTEST_WARNINGS_ARG) result.stdout.fnmatch_lines( """ ===*warnings summary*=== diff --git a/testing/test_mark.py b/testing/test_mark.py index 7109da9b6..1f50045c5 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -5,6 +5,8 @@ from __future__ import print_function import os import sys +from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG + try: import mock except ImportError: @@ -862,7 +864,7 @@ class TestFunctional(object): assert marker.kwargs == {} """ ) - reprec = testdir.inline_run("-m", "mark1") + reprec = testdir.inline_run("-m", "mark1", SHOW_PYTEST_WARNINGS_ARG) reprec.assertoutcome(passed=1) def assert_markers(self, items, **expected): @@ -904,7 +906,7 @@ class TestFunctional(object): assert True """ ) - reprec = testdir.inline_run() + reprec = testdir.inline_run(SHOW_PYTEST_WARNINGS_ARG) reprec.assertoutcome(skipped=1) @@ -1248,5 +1250,5 @@ def test_markers_from_parametrize(testdir): """ ) - result = testdir.runpytest() + result = testdir.runpytest(SHOW_PYTEST_WARNINGS_ARG) result.assert_outcomes(passed=4) diff --git a/testing/test_nodes.py b/testing/test_nodes.py index 0a7f7ec6f..b13ce1fe6 100644 --- a/testing/test_nodes.py +++ b/testing/test_nodes.py @@ -10,11 +10,11 @@ from _pytest import nodes ("", "", True), ("", "foo", True), ("", "foo/bar", True), - ("", "foo/bar::TestBaz::()", True), + ("", "foo/bar::TestBaz", True), ("foo", "food", False), - ("foo/bar::TestBaz::()", "foo/bar", False), - ("foo/bar::TestBaz::()", "foo/bar::TestBop::()", False), - ("foo/bar", "foo/bar::TestBop::()", True), + ("foo/bar::TestBaz", "foo/bar", False), + ("foo/bar::TestBaz", "foo/bar::TestBop", False), + ("foo/bar", "foo/bar::TestBop", True), ), ) def test_ischildnode(baseid, nodeid, expected): diff --git a/testing/test_nose.py b/testing/test_nose.py index b2c724b8d..0af591b36 100644 --- a/testing/test_nose.py +++ b/testing/test_nose.py @@ -3,6 +3,7 @@ from __future__ import division from __future__ import print_function import pytest +from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG def setup_module(mod): @@ -224,7 +225,7 @@ def test_nose_test_generator_fixtures(testdir): eq_(self.called, expect) """ ) - result = testdir.runpytest(p, "-p", "nose") + result = testdir.runpytest(p, "-p", "nose", SHOW_PYTEST_WARNINGS_ARG) result.stdout.fnmatch_lines(["*10 passed*"]) diff --git a/testing/test_pdb.py b/testing/test_pdb.py index 600ecbb62..dd349454b 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -8,6 +8,7 @@ import sys import _pytest._code import pytest +from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG try: breakpoint @@ -818,7 +819,9 @@ class TestTraceOption: yield is_equal, 1, 1 """ ) - child = testdir.spawn_pytest("--trace " + str(p1)) + child = testdir.spawn_pytest( + "{} --trace {}".format(SHOW_PYTEST_WARNINGS_ARG, str(p1)) + ) child.expect("is_equal") child.expect("Pdb") child.sendeof() diff --git a/testing/test_runner_xunit.py b/testing/test_runner_xunit.py index 4538b18db..31937c919 100644 --- a/testing/test_runner_xunit.py +++ b/testing/test_runner_xunit.py @@ -7,6 +7,7 @@ from __future__ import division from __future__ import print_function import pytest +from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG def test_module_and_function_setup(testdir): @@ -189,7 +190,8 @@ def test_method_generator_setup(testdir): assert self.classsetup assert self.methsetup == self.test_generate assert value == 5 - """ + """, + SHOW_PYTEST_WARNINGS_ARG, ) reprec.assertoutcome(passed=1, failed=1) @@ -219,7 +221,8 @@ def test_func_generator_setup(testdir): assert x == [1] yield check assert x == [1] - """ + """, + SHOW_PYTEST_WARNINGS_ARG, ) rep = reprec.matchreport("test_one", names="pytest_runtest_logreport") assert rep.passed diff --git a/testing/test_session.py b/testing/test_session.py index 746618308..e2c3701da 100644 --- a/testing/test_session.py +++ b/testing/test_session.py @@ -274,16 +274,26 @@ def test_deselect(testdir): testdir.makepyfile( test_a=""" import pytest + def test_a1(): pass + @pytest.mark.parametrize('b', range(3)) def test_a2(b): pass + + class TestClass: + def test_c1(self): pass + + def test_c2(self): pass """ ) result = testdir.runpytest( - "-v", "--deselect=test_a.py::test_a2[1]", "--deselect=test_a.py::test_a2[2]" + "-v", + "--deselect=test_a.py::test_a2[1]", + "--deselect=test_a.py::test_a2[2]", + "--deselect=test_a.py::TestClass::test_c1", ) assert result.ret == 0 - result.stdout.fnmatch_lines(["*2 passed, 2 deselected*"]) + result.stdout.fnmatch_lines(["*3 passed, 3 deselected*"]) for line in result.stdout.lines: assert not line.startswith(("test_a.py::test_a2[1]", "test_a.py::test_a2[2]")) diff --git a/testing/test_stepwise.py b/testing/test_stepwise.py index ad9b77296..b85839925 100644 --- a/testing/test_stepwise.py +++ b/testing/test_stepwise.py @@ -41,6 +41,14 @@ def test_success(): """ ) + # customize cache directory so we don't use the tox's cache directory, which makes tests in this module flaky + testdir.makeini( + """ + [pytest] + cache_dir = .cache + """ + ) + return testdir diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 2f7d02bba..9c2f93ed1 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -20,7 +20,7 @@ from _pytest.terminal import build_summary_stats_line from _pytest.terminal import getreportopt from _pytest.terminal import repr_pythonversion from _pytest.terminal import TerminalReporter - +from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG DistInfo = collections.namedtuple("DistInfo", ["project_name", "version"]) @@ -602,7 +602,7 @@ class TestTerminalFunctional(object): yield check, 0 """ ) - result = testdir.runpytest(p1, "-v") + result = testdir.runpytest(p1, "-v", SHOW_PYTEST_WARNINGS_ARG) result.stdout.fnmatch_lines( [ "*test_verbose_reporting.py::test_fail *FAIL*", @@ -616,7 +616,7 @@ class TestTerminalFunctional(object): if not pytestconfig.pluginmanager.get_plugin("xdist"): pytest.skip("xdist plugin not installed") - result = testdir.runpytest(p1, "-v", "-n 1") + result = testdir.runpytest(p1, "-v", "-n 1", SHOW_PYTEST_WARNINGS_ARG) result.stdout.fnmatch_lines(["*FAIL*test_verbose_reporting.py::test_fail*"]) assert result.ret == 1 diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index 39e5bc443..38b0672b7 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -9,6 +9,7 @@ import six import pytest from _pytest import pathlib from _pytest.pathlib import Path +from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG def test_tmpdir_fixture(testdir): @@ -67,7 +68,7 @@ def test_basetemp(testdir): pytest.ensuretemp("hello") """ ) - result = testdir.runpytest(p, "--basetemp=%s" % mytemp) + result = testdir.runpytest(p, "--basetemp=%s" % mytemp, SHOW_PYTEST_WARNINGS_ARG) assert result.ret == 0 assert mytemp.join("hello").check() diff --git a/testing/test_warnings.py b/testing/test_warnings.py index 569414300..d79e956e3 100644 --- a/testing/test_warnings.py +++ b/testing/test_warnings.py @@ -592,3 +592,34 @@ def test_infinite_loop_warning_against_unicode_usage_py2(testdir): ) result = testdir.runpytest_subprocess() result.stdout.fnmatch_lines(["*1 passed, * warnings in*"]) + + +@pytest.mark.parametrize("change_default", [None, "ini", "cmdline"]) +def test_removed_in_pytest4_warning_as_error(testdir, change_default): + testdir.makepyfile( + """ + import warnings, pytest + def test(): + warnings.warn(pytest.RemovedInPytest4Warning("some warning")) + """ + ) + if change_default == "ini": + testdir.makeini( + """ + [pytest] + filterwarnings = + ignore::pytest.RemovedInPytest4Warning + """ + ) + + args = ( + ("-Wignore::pytest.RemovedInPytest4Warning",) + if change_default == "cmdline" + else () + ) + result = testdir.runpytest(*args) + if change_default is None: + result.stdout.fnmatch_lines(["* 1 failed in *"]) + else: + assert change_default in ("ini", "cmdline") + result.stdout.fnmatch_lines(["* 1 passed in *"]) diff --git a/tox.ini b/tox.ini index bc13826e2..8fe0124f3 100644 --- a/tox.ini +++ b/tox.ini @@ -207,6 +207,7 @@ filterwarnings = error ignore:yield tests are deprecated, and scheduled to be removed in pytest 4.0:pytest.RemovedInPytest4Warning ignore:Metafunc.addcall is deprecated and scheduled to be removed in pytest 4.0:pytest.RemovedInPytest4Warning + ignore::pytest.RemovedInPytest4Warning ignore:Module already imported so cannot be rewritten:pytest.PytestWarning # produced by path.local ignore:bad escape.*:DeprecationWarning:re