Compare commits

...

25 Commits
4.6.6 ... 4.6.8

Author SHA1 Message Date
Anthony Sottile
2d398d8706 Preparing release version 4.6.8 2019-12-19 14:42:09 -08:00
Anthony Sottile
9ab4032f74 Merge pull request #6345 from nightlark/patch-1
Pin the colorama version only for Python 3.4
2019-12-19 14:38:59 -08:00
Ryan Mast
53b08730e4 Pin the colorama version only for Python 3.4 2019-12-14 10:10:16 -03:00
Bruno Oliveira
1deb60f02f Ensure colorama version is no newer than 0.4.1 (#6340)
Ensure colorama version is no newer than 0.4.1
2019-12-13 08:42:59 -03:00
Bruno Oliveira
fb8395d93f Create CHANGELOG for #6340 2019-12-13 07:46:12 -03:00
Ryan Mast
b08c599bad Ensure colorama versions is no newer than 0.4.1 2019-12-12 21:09:21 -08:00
Bruno Oliveira
51fd451dc9 [4.6] Bugfix 5430 pass logs to junit report (#6338)
[4.6] Bugfix 5430 pass logs to junit report
2019-12-12 19:03:58 -03:00
Bruno Oliveira
1d021540a3 Drop 3.4 testing on Azure
Azure no longer supports testing on Python 3.4
2019-12-12 16:47:47 -03:00
Bruno Oliveira
8bfe434f75 Drop validation against multiple xmlfamilies
This was not backported to 4.6 before, so had to adapt the test slightly
to the old method of validation.
2019-12-12 16:44:07 -03:00
Bruno Oliveira
f9ebe3c607 Bugfix 5430 pass logs to junit report (#6274)
Bugfix 5430 pass logs to junit report
2019-12-12 16:39:19 -03:00
Bruno Oliveira
bd54116d03 [4.6] Add hostname and timestamp to JUnit XML testsuite tag (#5… (#6332)
[4.6] Add hostname and timestamp to JUnit XML testsuite tag (#5692)
2019-12-10 18:11:28 -03:00
Bruno Oliveira
8b9482e39c Add hostname and timestamp to JUnit XML testsuite tag (#5692)
Add hostname and timestamp to JUnit XML testsuite tag

Conflicts:
  	testing/test_junitxml.py
2019-12-10 17:27:22 -03:00
Bruno Oliveira
943f4ac236 Release version 4.6.7 (#6318)
Release version 4.6.7
2019-12-05 22:44:59 -03:00
Bruno Oliveira
6f43eee106 Preparing release version 4.6.7 2019-12-05 15:29:25 -05:00
Bruno Oliveira
e1f3c0f9c3 [4.6] Include <testsuites> root tag in generated XML (#5550) (#6295)
[4.6] Include <testsuites> root tag in generated XML (#5550)
2019-11-30 12:13:57 -03:00
Bruno Oliveira
192f6992d2 Include <testsuites> root tag in generated XML (#5550)
Include <testsuites> root tag in generated XML
2019-11-30 11:30:43 -03:00
Daniel Hahler
6465244269 [4.6] Upgrade black (#6209) 2019-11-16 19:25:01 +01:00
Daniel Hahler
097acaf11b re-run black 2019-11-16 19:17:01 +01:00
Daniel Hahler
3d8649b206 Remove (now) unnecessary fmt: off 2019-11-16 19:15:56 +01:00
Daniel Hahler
a8c16d9b75 pre-commit: upgrade black
This brings https://github.com/psf/black/pull/826, which helps with
https://github.com/psf/black/issues/601.
2019-11-16 19:15:54 +01:00
Bruno Oliveira
3edf417969 [4.6] Review rm_rf handling of FileNotFoundErrors (#6050)
[4.6] Review rm_rf handling of FileNotFoundErrors
2019-10-23 20:19:05 -03:00
Bruno Oliveira
0084fd9783 Review rm_rf handling of FileNotFoundErrors (#6044)
Review rm_rf handling of FileNotFoundErrors
Conflicts:
 	 src/_pytest/pathlib.py
  	testing/test_tmpdir.py
2019-10-23 19:36:35 -03:00
Daniel Hahler
e89efa8325 Merge pull request #5957 from blueyed/4.6-comment-off
[4.6] ci: add codecov.yml to turn comments off
2019-10-15 02:14:31 +02:00
Daniel Hahler
3edcc71c41 ci: add codecov.yml to turn comments off
The only benefit for me is to get notified about finished builds, but
that might happen to early anyway.  Apart from that they are rather big
and distract from actual comments.

(cherry picked from commit d50198a3ff)
2019-10-14 22:32:27 +02:00
Bruno Oliveira
866daf57fe Release 4.6.6 (#5947)
Release 4.6.6
2019-10-13 11:55:23 -03:00
26 changed files with 247 additions and 56 deletions

View File

@@ -1,7 +1,7 @@
exclude: doc/en/example/py2py3/test_py2.py
repos:
- repo: https://github.com/python/black
rev: 19.3b0
- repo: https://github.com/psf/black
rev: 19.10b0
hooks:
- id: black
args: [--safe, --quiet]

View File

@@ -58,6 +58,7 @@ Christian Theunert
Christian Tismer
Christopher Gilling
Christopher Dignam
Claudio Madotto
CrazyMerlyn
Cyrus Maden
Damian Skrzypczak

View File

@@ -18,6 +18,43 @@ with advance notice in the **Deprecations** section of releases.
.. towncrier release notes start
pytest 4.6.8 (2019-12-19)
=========================
Features
--------
- `#5471 <https://github.com/pytest-dev/pytest/issues/5471>`_: JUnit XML now includes a timestamp and hostname in the testsuite tag.
Bug Fixes
---------
- `#5430 <https://github.com/pytest-dev/pytest/issues/5430>`_: junitxml: Logs for failed test are now passed to junit report in case the test fails during call phase.
Trivial/Internal Changes
------------------------
- `#6345 <https://github.com/pytest-dev/pytest/issues/6345>`_: Pin ``colorama`` to ``0.4.1`` only for Python 3.4 so newer Python versions can still receive colorama updates.
pytest 4.6.7 (2019-12-05)
=========================
Bug Fixes
---------
- `#5477 <https://github.com/pytest-dev/pytest/issues/5477>`_: The XML file produced by ``--junitxml`` now correctly contain a ``<testsuites>`` root element.
- `#6044 <https://github.com/pytest-dev/pytest/issues/6044>`_: Properly ignore ``FileNotFoundError`` (``OSError.errno == NOENT`` in Python 2) exceptions when trying to remove old temporary directories,
for instance when multiple processes try to remove the same directory (common with ``pytest-xdist``
for example).
pytest 4.6.6 (2019-10-11)
=========================

View File

@@ -48,12 +48,6 @@ jobs:
# pypy3:
# python.version: 'pypy3'
# tox.env: 'pypy3'
py34-xdist:
python.version: '3.4'
tox.env: 'py34-xdist'
# Coverage for:
# - _pytest.compat._bytes_to_ascii
PYTEST_COVERAGE: '1'
py35-xdist:
python.version: '3.5'
tox.env: 'py35-xdist'

7
codecov.yml Normal file
View File

@@ -0,0 +1,7 @@
coverage:
status:
project: true
patch: true
changes: true
comment: off

View File

@@ -6,6 +6,8 @@ Release announcements
:maxdepth: 2
release-4.6.8
release-4.6.7
release-4.6.6
release-4.6.5
release-4.6.4

View File

@@ -0,0 +1,19 @@
pytest-4.6.7
=======================================
pytest 4.6.7 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
Thanks to all who contributed to this release, among them:
* Bruno Oliveira
* Daniel Hahler
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,20 @@
pytest-4.6.8
=======================================
pytest 4.6.8 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
Thanks to all who contributed to this release, among them:
* Anthony Sottile
* Bruno Oliveira
* Ryan Mast
Happy testing,
The pytest Development Team

View File

@@ -434,11 +434,10 @@ Running it results in some skips if we don't have all the python interpreters in
.. code-block:: pytest
. $ pytest -rs -q multipython.py
...ssssssssssssssssssssssss [100%]
......sss......ssssssssssss [100%]
========================= short test summary info ==========================
SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:31: 'python3.4' not found
SKIPPED [12] $REGENDOC_TMPDIR/CWD/multipython.py:31: 'python3.5' not found
3 passed, 24 skipped in 0.12 seconds
SKIPPED [15] $REGENDOC_TMPDIR/CWD/multipython.py:31: 'python3.5' not found
12 passed, 15 skipped in 0.12 seconds
Indirect parametrization of optional implementations/imports
--------------------------------------------------------------------

View File

@@ -436,7 +436,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
items = [1, 2, 3]
print("items is %r" % items)
> a, b = items.pop()
E TypeError: cannot unpack non-iterable int object
E TypeError: 'int' object is not iterable
failure_demo.py:182: TypeError
--------------------------- Captured stdout call ---------------------------
@@ -515,7 +515,7 @@ Here is a nice run of several failures and how ``pytest`` presents things:
def test_z2_type_error(self):
items = 3
> a, b = items
E TypeError: cannot unpack non-iterable int object
E TypeError: 'int' object is not iterable
failure_demo.py:222: TypeError
______________________ TestMoreErrors.test_startswith ______________________

View File

@@ -28,7 +28,7 @@ Install ``pytest``
.. code-block:: bash
$ pytest --version
This is pytest version 4.x.y, imported from $PYTHON_PREFIX/lib/python3.7/site-packages/pytest.py
This is pytest version 4.x.y, imported from $PYTHON_PREFIX/lib/python3.6/site-packages/pytest.py
.. _`simpletest`:

View File

@@ -13,7 +13,8 @@ INSTALL_REQUIRES = [
"atomicwrites>=1.0",
'funcsigs>=1.0;python_version<"3.0"',
'pathlib2>=2.2.0;python_version<"3.6"',
'colorama;sys_platform=="win32"',
'colorama<=0.4.1;sys_platform=="win32" and python_version=="3.4"',
'colorama;sys_platform=="win32" and python_version!="3.4"',
"pluggy>=0.12,<1.0",
'importlib-metadata>=0.12;python_version<"3.8"',
"wcwidth",

View File

@@ -116,13 +116,13 @@ def directory_arg(path, optname):
# Plugins that cannot be disabled via "-p no:X" currently.
essential_plugins = ( # fmt: off
essential_plugins = (
"mark",
"main",
"runner",
"fixtures",
"helpconfig", # Provides -p.
) # fmt: on
)
default_plugins = essential_plugins + (
"python",

View File

@@ -15,9 +15,11 @@ from __future__ import print_function
import functools
import os
import platform
import re
import sys
import time
from datetime import datetime
import py
import six
@@ -595,6 +597,8 @@ class LogXML(object):
if report.when == "call":
reporter.append_failure(report)
self.open_reports.append(report)
if not self.log_passing_tests:
reporter.write_captured_output(report)
else:
reporter.append_error(report)
elif report.skipped:
@@ -667,18 +671,19 @@ class LogXML(object):
)
logfile.write('<?xml version="1.0" encoding="utf-8"?>')
logfile.write(
Junit.testsuite(
self._get_global_properties_node(),
[x.to_xml() for x in self.node_reporters_ordered],
name=self.suite_name,
errors=self.stats["error"],
failures=self.stats["failure"],
skipped=self.stats["skipped"],
tests=numtests,
time="%.3f" % suite_time_delta,
).unicode(indent=0)
suite_node = Junit.testsuite(
self._get_global_properties_node(),
[x.to_xml() for x in self.node_reporters_ordered],
name=self.suite_name,
errors=self.stats["error"],
failures=self.stats["failure"],
skipped=self.stats["skipped"],
tests=numtests,
time="%.3f" % suite_time_delta,
timestamp=datetime.fromtimestamp(self.suite_start_time).isoformat(),
hostname=platform.node(),
)
logfile.write(Junit.testsuites([suite_node]).unicode(indent=0))
logfile.close()
def pytest_terminal_summary(self, terminalreporter):

View File

@@ -48,24 +48,38 @@ def ensure_reset_dir(path):
def on_rm_rf_error(func, path, exc, **kwargs):
"""Handles known read-only errors during rmtree."""
"""Handles known read-only errors during rmtree.
The returned value is used only by our own tests.
"""
start_path = kwargs["start_path"]
excvalue = exc[1]
exctype, excvalue = exc[:2]
# another process removed the file in the middle of the "rm_rf" (xdist for example)
# more context: https://github.com/pytest-dev/pytest/issues/5974#issuecomment-543799018
if isinstance(excvalue, OSError) and excvalue.errno == errno.ENOENT:
return False
if not isinstance(excvalue, OSError) or excvalue.errno not in (
errno.EACCES,
errno.EPERM,
):
warnings.warn(
PytestWarning("(rm_rf) error removing {}: {}".format(path, excvalue))
PytestWarning(
"(rm_rf) error removing {}\n{}: {}".format(path, exctype, excvalue)
)
)
return
return False
if func not in (os.rmdir, os.remove, os.unlink):
warnings.warn(
PytestWarning("(rm_rf) error removing {}: {}".format(path, excvalue))
PytestWarning(
"(rm_rf) unknown function {} when removing {}:\n{}: {}".format(
path, func, exctype, excvalue
)
)
)
return
return False
# Chmod + retry.
import stat
@@ -86,6 +100,7 @@ def on_rm_rf_error(func, path, exc, **kwargs):
chmod_rw(str(path))
func(path)
return True
def rm_rf(path):

View File

@@ -694,7 +694,7 @@ def raises(expected_exception, *args, **kwargs):
return RaisesContext(expected_exception, message, match_expr)
elif isinstance(args[0], str):
warnings.warn(deprecated.RAISES_EXEC, stacklevel=2)
code, = args
(code,) = args
assert isinstance(code, str)
frame = sys._getframe(1)
loc = frame.f_locals.copy()

View File

@@ -95,7 +95,7 @@ def warns(expected_warning, *args, **kwargs):
return WarningsChecker(expected_warning, match_expr=match_expr)
elif isinstance(args[0], str):
warnings.warn(WARNS_EXEC, stacklevel=2)
code, = args
(code,) = args
assert isinstance(code, str)
frame = sys._getframe(1)
loc = frame.f_locals.copy()

View File

@@ -152,7 +152,7 @@ def test_pytest_plugins_in_non_top_level_conftest_unsupported_pyargs(
def test_pytest_plugins_in_non_top_level_conftest_unsupported_no_top_level_conftest(
testdir
testdir,
):
from _pytest.deprecated import PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST
@@ -181,7 +181,7 @@ def test_pytest_plugins_in_non_top_level_conftest_unsupported_no_top_level_conft
def test_pytest_plugins_in_non_top_level_conftest_unsupported_no_false_positives(
testdir
testdir,
):
from _pytest.deprecated import PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST

View File

@@ -464,7 +464,7 @@ class TestRequestBasic(object):
assert repr(req).find(req.function.__name__) != -1
def test_request_attributes_method(self, testdir):
item, = testdir.getitems(
(item,) = testdir.getitems(
"""
import pytest
class TestB(object):
@@ -492,7 +492,7 @@ class TestRequestBasic(object):
pass
"""
)
item1, = testdir.genitems([modcol])
(item1,) = testdir.genitems([modcol])
assert item1.name == "test_method"
arg2fixturedefs = fixtures.FixtureRequest(item1)._arg2fixturedefs
assert len(arg2fixturedefs) == 1
@@ -756,7 +756,7 @@ class TestRequestBasic(object):
def test_request_getmodulepath(self, testdir):
modcol = testdir.getmodulecol("def test_somefunc(): pass")
item, = testdir.genitems([modcol])
(item,) = testdir.genitems([modcol])
req = fixtures.FixtureRequest(item)
assert req.fspath == modcol.fspath

View File

@@ -494,7 +494,7 @@ class TestSession(object):
p = testdir.makepyfile("def test_func(): pass")
id = "::".join([p.basename, "test_func"])
items, hookrec = testdir.inline_genitems(id)
item, = items
(item,) = items
assert item.name == "test_func"
newid = item.nodeid
assert newid == id
@@ -613,9 +613,9 @@ class TestSession(object):
testdir.makepyfile("def test_func(): pass")
items, hookrec = testdir.inline_genitems()
assert len(items) == 1
item, = items
(item,) = items
items2, hookrec = testdir.inline_genitems(item.nodeid)
item2, = items2
(item2,) = items2
assert item2.name == item.name
assert item2.fspath == item.fspath
@@ -630,7 +630,7 @@ class TestSession(object):
arg = p.basename + "::TestClass::test_method"
items, hookrec = testdir.inline_genitems(arg)
assert len(items) == 1
item, = items
(item,) = items
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"]

View File

@@ -4,7 +4,9 @@ from __future__ import division
from __future__ import print_function
import os
import platform
import sys
from datetime import datetime
from xml.dom import minidom
import py
@@ -47,6 +49,16 @@ class DomNode(object):
def _by_tag(self, tag):
return self.__node.getElementsByTagName(tag)
@property
def children(self):
return [type(self)(x) for x in self.__node.childNodes]
@property
def get_unique_child(self):
children = self.children
assert len(children) == 1
return children[0]
def find_nth_by_tag(self, tag, n):
items = self._by_tag(tag)
try:
@@ -81,7 +93,7 @@ class DomNode(object):
return self.__node.tagName
@property
def next_siebling(self):
def next_sibling(self):
return type(self)(self.__node.nextSibling)
@@ -135,6 +147,30 @@ class TestPython(object):
node = dom.find_first_by_tag("testsuite")
node.assert_attr(name="pytest", errors=1, failures=2, skipped=1, tests=5)
def test_hostname_in_xml(self, testdir):
testdir.makepyfile(
"""
def test_pass():
pass
"""
)
result, dom = runandparse(testdir)
node = dom.find_first_by_tag("testsuite")
node.assert_attr(hostname=platform.node())
def test_timestamp_in_xml(self, testdir):
testdir.makepyfile(
"""
def test_pass():
pass
"""
)
start_time = datetime.now()
result, dom = runandparse(testdir)
node = dom.find_first_by_tag("testsuite")
timestamp = datetime.strptime(node["timestamp"], "%Y-%m-%dT%H:%M:%S.%f")
assert start_time <= timestamp < datetime.now()
def test_timing_function(self, testdir):
testdir.makepyfile(
"""
@@ -390,11 +426,11 @@ class TestPython(object):
fnode = tnode.find_first_by_tag("failure")
fnode.assert_attr(message="ValueError: 42")
assert "ValueError" in fnode.toxml()
systemout = fnode.next_siebling
systemout = fnode.next_sibling
assert systemout.tag == "system-out"
assert "hello-stdout" in systemout.toxml()
assert "info msg" not in systemout.toxml()
systemerr = systemout.next_siebling
systemerr = systemout.next_sibling
assert systemerr.tag == "system-err"
assert "hello-stderr" in systemerr.toxml()
assert "info msg" not in systemerr.toxml()
@@ -1101,6 +1137,20 @@ def test_random_report_log_xdist(testdir, monkeypatch):
assert failed == ["test_x[22]"]
def test_root_testsuites_tag(testdir):
testdir.makepyfile(
"""
def test_x():
pass
"""
)
_, dom = runandparse(testdir)
root = dom.get_unique_child
assert root.tag == "testsuites"
suite_node = root.get_unique_child
assert suite_node.tag == "testsuite"
def test_runs_twice(testdir):
f = testdir.makepyfile(
"""
@@ -1359,3 +1409,39 @@ def test_logging_passing_tests_disabled_does_not_log_test_output(testdir):
node = dom.find_first_by_tag("testcase")
assert len(node.find_by_tag("system-err")) == 0
assert len(node.find_by_tag("system-out")) == 0
@pytest.mark.parametrize("junit_logging", ["no", "system-out", "system-err"])
def test_logging_passing_tests_disabled_logs_output_for_failing_test_issue5430(
testdir, junit_logging
):
testdir.makeini(
"""
[pytest]
junit_log_passing_tests=False
"""
)
testdir.makepyfile(
"""
import pytest
import logging
import sys
def test_func():
logging.warning('hello')
assert 0
"""
)
result, dom = runandparse(testdir, "-o", "junit_logging=%s" % junit_logging)
assert result.ret == 1
node = dom.find_first_by_tag("testcase")
if junit_logging == "system-out":
assert len(node.find_by_tag("system-err")) == 0
assert len(node.find_by_tag("system-out")) == 1
elif junit_logging == "system-err":
assert len(node.find_by_tag("system-err")) == 1
assert len(node.find_by_tag("system-out")) == 0
else:
assert junit_logging == "no"
assert len(node.find_by_tag("system-err")) == 0
assert len(node.find_by_tag("system-out")) == 0

View File

@@ -1008,7 +1008,7 @@ def test_markers_from_parametrize(testdir):
def test_pytest_param_id_requires_string():
with pytest.raises(TypeError) as excinfo:
pytest.param(id=True)
msg, = excinfo.value.args
(msg,) = excinfo.value.args
if six.PY2:
assert msg == "Expected id to be a string, got <type 'bool'>: True"
else:
@@ -1025,7 +1025,7 @@ def test_pytest_param_warning_on_unknown_kwargs():
# typo, should be marks=
pytest.param(1, 2, mark=pytest.mark.xfail())
assert warninfo[0].filename == __file__
msg, = warninfo[0].message.args
(msg,) = warninfo[0].message.args
assert msg == (
"pytest.param() got unexpected keyword arguments: ['mark'].\n"
"This will be an error in future versions."

View File

@@ -225,7 +225,7 @@ class TestWarns(object):
assert len(warninfo) == 3
for w in warninfo:
assert w.filename == __file__
msg, = w.message.args
(msg,) = w.message.args
assert msg.startswith("warns(..., 'code(as_a_string)') is deprecated")
def test_function(self):

View File

@@ -136,7 +136,7 @@ class TestEvaluator(object):
)
def test_skipif_class(self, testdir):
item, = testdir.getitems(
(item,) = testdir.getitems(
"""
import pytest
class TestClass(object):

View File

@@ -273,7 +273,7 @@ class TestNumberedDir(object):
registry = []
register_cleanup_lock_removal(lock, register=registry.append)
cleanup_func, = registry
(cleanup_func,) = registry
assert lock.is_file()
@@ -398,9 +398,14 @@ class TestRmRf:
on_rm_rf_error(os.unlink, str(fn), exc_info, start_path=tmp_path)
assert fn.is_file()
# we ignore FileNotFoundError
file_not_found = OSError()
file_not_found.errno = errno.ENOENT
exc_info = (None, file_not_found, None)
assert not on_rm_rf_error(None, str(fn), exc_info, start_path=tmp_path)
permission_error = OSError()
permission_error.errno = errno.EACCES
# unknown function
with pytest.warns(pytest.PytestWarning):
exc_info = (None, permission_error, None)

View File

@@ -388,7 +388,7 @@ def test_testcase_custom_exception_info(testdir, type):
def test_testcase_totally_incompatible_exception_info(testdir):
item, = testdir.getitems(
(item,) = testdir.getitems(
"""
from unittest import TestCase
class MyTestCase(TestCase):