diff --git a/.travis.yml b/.travis.yml index 5c479feed9b..00abca0b2f0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -93,12 +93,6 @@ after_success: coverage xml --ignore-errors coverage report -m --ignore-errors bash <(curl -s https://codecov.io/bash) -Z -X gcov -X coveragepy -X search -X xcode -X gcovout -X fix -f coverage.xml -F "${TOXENV//-/,},linux" - - # Coveralls does not support merged reports. - if [[ "$TOXENV" = py37 ]]; then - pip install coveralls - coveralls - fi fi notifications: diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 015f9dbd309..14881e373da 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -45,7 +45,7 @@ Deprecations Users should just ``import pytest`` and access those objects using the ``pytest`` module. * ``request.cached_setup``, this was the precursor of the setup/teardown mechanism available to fixtures. You can - consult `funcarg comparision section in the docs `_. + consult `funcarg comparison section in the docs `_. * Using objects named ``"Class"`` as a way to customize the type of nodes that are collected in ``Collector`` subclasses has been deprecated. Users instead should use ``pytest_collect_make_item`` to customize node types during diff --git a/changelog/3533.bugfix.rst b/changelog/3533.bugfix.rst new file mode 100644 index 00000000000..89f13645860 --- /dev/null +++ b/changelog/3533.bugfix.rst @@ -0,0 +1 @@ +Fix unescaped XML raw objects in JUnit report for skipped tests diff --git a/changelog/4177.bugfix.rst b/changelog/4177.bugfix.rst new file mode 100644 index 00000000000..b26ad4bad29 --- /dev/null +++ b/changelog/4177.bugfix.rst @@ -0,0 +1 @@ +Pin ``setuptools>=40.0`` to support ``py_modules`` in ``setup.cfg`` diff --git a/changelog/4179.bugfix.rst b/changelog/4179.bugfix.rst new file mode 100644 index 00000000000..6f7467f50b4 --- /dev/null +++ b/changelog/4179.bugfix.rst @@ -0,0 +1 @@ +Restore the tmpdir behaviour of symlinking the current test run. diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index 30746d03508..3398c92a20e 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -56,7 +56,7 @@ This should be updated to make use of standard fixture mechanisms: session.close() -You can consult `funcarg comparision section in the docs `_ for +You can consult `funcarg comparison section in the docs `_ for more information. This has been documented as deprecated for years, but only now we are actually emitting deprecation warnings. @@ -68,7 +68,7 @@ Using ``Class`` in custom Collectors .. deprecated:: 3.9 Using objects named ``"Class"`` as a way to customize the type of nodes that are collected in ``Collector`` -subclasses has been deprecated. Users instead should use ``pytest_collect_make_item`` to customize node types during +subclasses has been deprecated. Users instead should use ``pytest_pycollect_makeitem`` to customize node types during collection. This issue should affect only advanced plugins who create new collection types, so if you see this warning diff --git a/doc/en/warnings.rst b/doc/en/warnings.rst index 060057d9114..f83377cf68f 100644 --- a/doc/en/warnings.rst +++ b/doc/en/warnings.rst @@ -75,8 +75,54 @@ Both ``-W`` command-line option and ``filterwarnings`` ini option are based on P `-W option`_ and `warnings.simplefilter`_, so please refer to those sections in the Python documentation for other examples and advanced usage. -Disabling warning summary -------------------------- +.. _`filterwarnings`: + +``@pytest.mark.filterwarnings`` +------------------------------- + +.. versionadded:: 3.2 + +You can use the ``@pytest.mark.filterwarnings`` to add warning filters to specific test items, +allowing you to have finer control of which warnings should be captured at test, class or +even module level: + +.. code-block:: python + + import warnings + + + def api_v1(): + warnings.warn(UserWarning("api v1, should use functions from v2")) + return 1 + + + @pytest.mark.filterwarnings("ignore:api v1") + def test_one(): + assert api_v1() == 1 + + +Filters applied using a mark take precedence over filters passed on the command line or configured +by the ``filterwarnings`` ini option. + +You may apply a filter to all tests of a class by using the ``filterwarnings`` mark as a class +decorator or to all tests in a module by setting the ``pytestmark`` variable: + +.. code-block:: python + + # turns all warnings into errors for this module + pytestmark = pytest.mark.filterwarnings("error") + + + +*Credits go to Florian Schulze for the reference implementation in the* `pytest-warnings`_ +*plugin.* + +.. _`-W option`: https://docs.python.org/3/using/cmdline.html?highlight=#cmdoption-W +.. _warnings.simplefilter: https://docs.python.org/3/library/warnings.html#warnings.simplefilter +.. _`pytest-warnings`: https://github.com/fschulze/pytest-warnings + +Disabling warnings summary +-------------------------- Although not recommended, you can use the ``--disable-warnings`` command-line option to suppress the warning summary entirely from the test run output. @@ -103,10 +149,14 @@ DeprecationWarning and PendingDeprecationWarning .. versionadded:: 3.8 .. versionchanged:: 3.9 -By default pytest will display ``DeprecationWarning`` and ``PendingDeprecationWarning``. +By default pytest will display ``DeprecationWarning`` and ``PendingDeprecationWarning`` warnings from +user code and third-party libraries, as recommended by `PEP-0506 `_. +This helps users keep their code modern and avoid breakages when deprecated warnings are effectively removed. Sometimes it is useful to hide some specific deprecation warnings that happen in code that you have no control over -(such as third-party libraries), in which case you might use the standard warning filters options (ini or marks). +(such as third-party libraries), in which case you might use the warning filters options (ini or marks) to ignore +those warnings. + For example: .. code-block:: ini @@ -116,83 +166,57 @@ For example: ignore:.*U.*mode is deprecated:DeprecationWarning +This will ignore all warnings of type ``DeprecationWarning`` where the start of the message matches +the regular expression ``".*U.*mode is deprecated"``. + .. note:: If warnings are configured at the interpreter level, using the `PYTHONWARNINGS `_ environment variable or the ``-W`` command-line option, pytest will not configure any filters by default. -.. note:: - This feature makes pytest more compliant with `PEP-0506 `_ which suggests that those warnings should - be shown by default by test runners, but pytest doesn't follow ``PEP-0506`` completely because resetting all - warning filters like suggested in the PEP will break existing test suites that configure warning filters themselves + Also pytest doesn't follow ``PEP-0506`` suggestion of resetting all warning filters because + it might break test suites that configure warning filters themselves by calling ``warnings.simplefilter`` (see issue `#2430 `_ for an example of that). -.. _`filterwarnings`: - -``@pytest.mark.filterwarnings`` -------------------------------- - -.. versionadded:: 3.2 - -You can use the ``@pytest.mark.filterwarnings`` to add warning filters to specific test items, -allowing you to have finer control of which warnings should be captured at test, class or -even module level: - -.. code-block:: python - - import warnings - - - def api_v1(): - warnings.warn(UserWarning("api v1, should use functions from v2")) - return 1 - - - @pytest.mark.filterwarnings("ignore:api v1") - def test_one(): - assert api_v1() == 1 - - -Filters applied using a mark take precedence over filters passed on the command line or configured -by the ``filterwarnings`` ini option. +.. _`ensuring a function triggers a deprecation warning`: -You may apply a filter to all tests of a class by using the ``filterwarnings`` mark as a class -decorator or to all tests in a module by setting the ``pytestmark`` variable: +.. _ensuring_function_triggers: -.. code-block:: python +Ensuring code triggers a deprecation warning +-------------------------------------------- - # turns all warnings into errors for this module - pytestmark = pytest.mark.filterwarnings("error") +You can also call a global helper for checking +that a certain function call triggers a ``DeprecationWarning`` or +``PendingDeprecationWarning``:: + import pytest -.. note:: + def test_global(): + pytest.deprecated_call(myfunction, 17) - Except for these features, pytest does not change the python warning filter; it only captures - and displays the warnings which are issued with respect to the currently configured filter, - including changes to the filter made by test functions or by the system under test. +By default, ``DeprecationWarning`` and ``PendingDeprecationWarning`` will not be +caught when using ``pytest.warns`` or ``recwarn`` because default Python warnings filters hide +them. If you wish to record them in your own code, use the +command ``warnings.simplefilter('always')``:: -.. note:: + import warnings + import pytest - ``DeprecationWarning`` and ``PendingDeprecationWarning`` are hidden by the standard library - by default so you have to explicitly configure them to be displayed in your ``pytest.ini``: + def test_deprecation(recwarn): + warnings.simplefilter('always') + warnings.warn("deprecated", DeprecationWarning) + assert len(recwarn) == 1 + assert recwarn.pop(DeprecationWarning) - .. code-block:: ini +You can also use it as a contextmanager:: - [pytest] - filterwarnings = - once::DeprecationWarning - once::PendingDeprecationWarning + def test_global(): + with pytest.deprecated_call(): + myobject.deprecated_method() -*Credits go to Florian Schulze for the reference implementation in the* `pytest-warnings`_ -*plugin.* - -.. _`-W option`: https://docs.python.org/3/using/cmdline.html?highlight=#cmdoption-W -.. _warnings.simplefilter: https://docs.python.org/3/library/warnings.html#warnings.simplefilter -.. _`pytest-warnings`: https://github.com/fschulze/pytest-warnings - .. _`asserting warnings`: @@ -299,43 +323,6 @@ warnings, or index into it to get a particular recorded warning. Full API: :class:`WarningsRecorder`. -.. _`ensuring a function triggers a deprecation warning`: - -.. _ensuring_function_triggers: - -Ensuring a function triggers a deprecation warning -------------------------------------------------------- - -You can also call a global helper for checking -that a certain function call triggers a ``DeprecationWarning`` or -``PendingDeprecationWarning``:: - - import pytest - - def test_global(): - pytest.deprecated_call(myfunction, 17) - -By default, ``DeprecationWarning`` and ``PendingDeprecationWarning`` will not be -caught when using ``pytest.warns`` or ``recwarn`` because default Python warnings filters hide -them. If you wish to record them in your own code, use the -command ``warnings.simplefilter('always')``:: - - import warnings - import pytest - - def test_deprecation(recwarn): - warnings.simplefilter('always') - warnings.warn("deprecated", DeprecationWarning) - assert len(recwarn) == 1 - assert recwarn.pop(DeprecationWarning) - -You can also use it as a contextmanager:: - - def test_global(): - with pytest.deprecated_call(): - myobject.deprecated_method() - - .. _internal-warnings: diff --git a/pyproject.toml b/pyproject.toml index c83bd853dec..d17b936c157 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [build-system] requires = [ # sync with setup.py until we discard non-pep-517/518 - "setuptools>=30.3", + "setuptools>=40.0", "setuptools-scm", "wheel", ] diff --git a/setup.py b/setup.py index 6bab5312d49..ac07394558e 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ def main(): setup( use_scm_version={"write_to": "src/_pytest/_version.py"}, - setup_requires=["setuptools-scm", "setuptools>=30.3"], + setup_requires=["setuptools-scm", "setuptools>=40.0"], package_dir={"": "src"}, install_requires=INSTALL_REQUIRES, ) diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index ac00c772a1a..a39c94c1394 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -221,12 +221,14 @@ def append_skipped(self, report): else: filename, lineno, skipreason = report.longrepr if skipreason.startswith("Skipped: "): - skipreason = bin_xml_escape(skipreason[9:]) + skipreason = skipreason[9:] + details = "%s:%s: %s" % (filename, lineno, skipreason) + self.append( Junit.skipped( - "%s:%s: %s" % (filename, lineno, skipreason), + bin_xml_escape(details), type="pytest.skip", - message=skipreason, + message=bin_xml_escape(skipreason), ) ) self.write_captured_output(report) diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index fe6711ac7f4..ee679525043 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -279,7 +279,7 @@ def messages(self): Unlike 'records', which contains the format string and parameters for interpolation, log messages in this list are all interpolated. Unlike 'text', which contains the output from the handler, log messages in this list are unadorned with - levels, timestamps, etc, making exact comparisions more reliable. + levels, timestamps, etc, making exact comparisons more reliable. Note that traceback or stack info (from :func:`logging.exception` or the `exc_info` or `stack_info` arguments to the logging functions) is not included, as this is added by the formatter in the handler. diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index cda5e9947f8..081fce90465 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -100,6 +100,26 @@ def _max(iterable, default): _max = max +def _force_symlink(root, target, link_to): + """helper to create the current symlink + + its full of race conditions that are reasonably ok to ignore + for the contex of best effort linking to the latest testrun + + the presumption being thatin case of much parallelism + the inaccuracy is going to be acceptable + """ + current_symlink = root.joinpath(target) + try: + current_symlink.unlink() + except OSError: + pass + try: + current_symlink.symlink_to(link_to) + except Exception: + pass + + def make_numbered_dir(root, prefix): """create a directory with a increased number as suffix for the given prefix""" for i in range(10): @@ -112,6 +132,7 @@ def make_numbered_dir(root, prefix): except Exception: pass else: + _force_symlink(root, prefix + "current", new_path) return new_path else: raise EnvironmentError( diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index 29d3f7f6ec1..079b01f3201 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -1222,3 +1222,19 @@ def test_func(): assert result.ret == 0 node = dom.find_first_by_tag("testsuite") node.assert_attr(name=expected) + + +def test_escaped_skipreason_issue3533(testdir): + testdir.makepyfile( + """ + import pytest + @pytest.mark.skip(reason='1 <> 2') + def test_skip(): + pass + """ + ) + _, dom = runandparse(testdir) + node = dom.find_first_by_tag("testcase") + snode = node.find_first_by_tag("skipped") + assert "1 <> 2" in snode.text + snode.assert_attr(message="1 <> 2") diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index 9f4158eb76a..3c413e7c271 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -196,6 +196,12 @@ def test_make(self, tmp_path): assert d.name.startswith(self.PREFIX) assert d.name.endswith(str(i)) + symlink = tmp_path.joinpath(self.PREFIX + "current") + if symlink.exists(): + # unix + assert symlink.is_symlink() + assert symlink.resolve() == d.resolve() + def test_cleanup_lock_create(self, tmp_path): d = tmp_path.joinpath("test") d.mkdir() @@ -244,7 +250,7 @@ def _do_cleanup(self, tmp_path): def test_cleanup_keep(self, tmp_path): self._do_cleanup(tmp_path) - a, b = tmp_path.iterdir() + a, b = (x for x in tmp_path.iterdir() if not x.is_symlink()) print(a, b) def test_cleanup_locked(self, tmp_path): diff --git a/tox.ini b/tox.ini index 2119002a9b1..86b3b94581e 100644 --- a/tox.ini +++ b/tox.ini @@ -21,7 +21,7 @@ commands = {env:_PYTEST_TOX_COVERAGE_RUN:} pytest --lsof coverage: coverage combine coverage: coverage report -passenv = USER USERNAME COVERAGE_* +passenv = USER USERNAME COVERAGE_* TRAVIS setenv = # configuration if a user runs tox with a "coverage" factor, for example "tox -e py36-coverage" coverage: _PYTEST_TOX_COVERAGE_RUN=coverage run -m