diff --git a/.travis.yml b/.travis.yml index 5c479feed..00abca0b2 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 015f9dbd3..14881e373 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 000000000..89f136458 --- /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 000000000..b26ad4bad --- /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 000000000..6f7467f50 --- /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 30746d035..3398c92a2 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 060057d91..f83377cf6 100644 --- a/doc/en/warnings.rst +++ b/doc/en/warnings.rst @@ -75,60 +75,6 @@ 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 -------------------------- - -Although not recommended, you can use the ``--disable-warnings`` command-line option to suppress the -warning summary entirely from the test run output. - -Disabling warning capture entirely ----------------------------------- - -This plugin is enabled by default but can be disabled entirely in your ``pytest.ini`` file with: - - .. code-block:: ini - - [pytest] - addopts = -p no:warnings - -Or passing ``-p no:warnings`` in the command-line. This might be useful if your test suites handles warnings -using an external system. - - -.. _`deprecation-warnings`: - -DeprecationWarning and PendingDeprecationWarning ------------------------------------------------- - -.. versionadded:: 3.8 -.. versionchanged:: 3.9 - -By default pytest will display ``DeprecationWarning`` and ``PendingDeprecationWarning``. - -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). -For example: - -.. code-block:: ini - - [pytest] - filterwarnings = - ignore:.*U.*mode is deprecated:DeprecationWarning - - -.. 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 - by calling ``warnings.simplefilter`` (see issue `#2430 `_ - for an example of that). - - .. _`filterwarnings`: ``@pytest.mark.filterwarnings`` @@ -167,24 +113,6 @@ decorator or to all tests in a module by setting the ``pytestmark`` variable: pytestmark = pytest.mark.filterwarnings("error") -.. note:: - - 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. - -.. note:: - - ``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``: - - .. code-block:: ini - - [pytest] - filterwarnings = - once::DeprecationWarning - once::PendingDeprecationWarning - *Credits go to Florian Schulze for the reference implementation in the* `pytest-warnings`_ *plugin.* @@ -193,6 +121,102 @@ decorator or to all tests in a module by setting the ``pytestmark`` variable: .. _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. + +Disabling warning capture entirely +---------------------------------- + +This plugin is enabled by default but can be disabled entirely in your ``pytest.ini`` file with: + + .. code-block:: ini + + [pytest] + addopts = -p no:warnings + +Or passing ``-p no:warnings`` in the command-line. This might be useful if your test suites handles warnings +using an external system. + + +.. _`deprecation-warnings`: + +DeprecationWarning and PendingDeprecationWarning +------------------------------------------------ + +.. versionadded:: 3.8 +.. versionchanged:: 3.9 + +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 warning filters options (ini or marks) to ignore +those warnings. + +For example: + +.. code-block:: ini + + [pytest] + filterwarnings = + 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. + + 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). + + +.. _`ensuring a function triggers a deprecation warning`: + +.. _ensuring_function_triggers: + +Ensuring code 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() + + .. _`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 c83bd853d..d17b936c1 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 6bab5312d..ac0739455 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ if "_PYTEST_SETUP_SKIP_PLUGGY_DEP" not in os.environ: 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 ac00c772a..a39c94c13 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -221,12 +221,14 @@ class _NodeReporter(object): 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 fe6711ac7..ee6795250 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -279,7 +279,7 @@ class LogCaptureFixture(object): 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 cda5e9947..081fce904 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -100,6 +100,26 @@ else: _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 29d3f7f6e..079b01f32 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -1222,3 +1222,19 @@ def test_set_suite_name(testdir, suite_name): 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 9f4158eb7..3c413e7c2 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -196,6 +196,12 @@ class TestNumberedDir(object): 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 @@ class TestNumberedDir(object): 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 2119002a9..86b3b9458 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