Merge remote-tracking branch 'upstream/master' into merge-upstream
This commit is contained in:
commit
69d608aec3
|
@ -1,15 +1,14 @@
|
||||||
Thanks for submitting a PR, your contribution is really appreciated!
|
Thanks for submitting a PR, your contribution is really appreciated!
|
||||||
|
|
||||||
Here's a quick checklist that should be present in PRs:
|
Here's a quick checklist that should be present in PRs (you can delete this text from the final description, this is
|
||||||
|
just a guideline):
|
||||||
|
|
||||||
- [ ] Add a new news fragment into the changelog folder
|
- [ ] Create a new changelog file in the `changelog` folder, with a name like `<ISSUE NUMBER>.<TYPE>.rst`. See [changelog/README.rst](/changelog/README.rst) for details.
|
||||||
* name it `$issue_id.$type` for example (588.bugfix)
|
- [ ] Target the `master` branch for bug fixes, documentation updates and trivial changes.
|
||||||
* if you don't have an issue_id change it to the pr id after creating the pr
|
- [ ] Target the `features` branch for new features and removals/deprecations.
|
||||||
* ensure type is one of `removal`, `feature`, `bugfix`, `vendor`, `doc` or `trivial`
|
- [ ] Include documentation when adding new features.
|
||||||
* Make sure to use full sentences with correct case and punctuation, for example: "Fix issue with non-ascii contents in doctest text files."
|
- [ ] Include new tests or update existing tests when applicable.
|
||||||
- [ ] Target: for `bugfix`, `vendor`, `doc` or `trivial` fixes, target `master`; for removals or features target `features`;
|
|
||||||
- [ ] Make sure to include reasonable tests for your change if necessary
|
|
||||||
|
|
||||||
Unless your change is a trivial or a documentation fix (e.g., a typo or reword of a small section) please:
|
Unless your change is trivial or a small documentation fix (e.g., a typo or reword of a small section) please:
|
||||||
|
|
||||||
- [ ] Add yourself to `AUTHORS`, in alphabetical order;
|
- [ ] Add yourself to `AUTHORS` in alphabetical order;
|
||||||
|
|
|
@ -39,9 +39,6 @@ matrix:
|
||||||
python: '3.5'
|
python: '3.5'
|
||||||
- env: TOXENV=py37
|
- env: TOXENV=py37
|
||||||
python: 'nightly'
|
python: 'nightly'
|
||||||
allow_failures:
|
|
||||||
- env: TOXENV=py37
|
|
||||||
python: 'nightly'
|
|
||||||
|
|
||||||
script: tox --recreate
|
script: tox --recreate
|
||||||
|
|
||||||
|
|
1
AUTHORS
1
AUTHORS
|
@ -29,6 +29,7 @@ Benjamin Peterson
|
||||||
Bernard Pratz
|
Bernard Pratz
|
||||||
Bob Ippolito
|
Bob Ippolito
|
||||||
Brian Dorsey
|
Brian Dorsey
|
||||||
|
Brian Maissy
|
||||||
Brian Okken
|
Brian Okken
|
||||||
Brianna Laugher
|
Brianna Laugher
|
||||||
Bruno Oliveira
|
Bruno Oliveira
|
||||||
|
|
|
@ -79,10 +79,11 @@ def num_mock_patch_args(function):
|
||||||
patchings = getattr(function, "patchings", None)
|
patchings = getattr(function, "patchings", None)
|
||||||
if not patchings:
|
if not patchings:
|
||||||
return 0
|
return 0
|
||||||
mock = sys.modules.get("mock", sys.modules.get("unittest.mock", None))
|
mock_modules = [sys.modules.get("mock"), sys.modules.get("unittest.mock")]
|
||||||
if mock is not None:
|
if any(mock_modules):
|
||||||
|
sentinels = [m.DEFAULT for m in mock_modules if m is not None]
|
||||||
return len([p for p in patchings
|
return len([p for p in patchings
|
||||||
if not p.attribute_name and p.new is mock.DEFAULT])
|
if not p.attribute_name and p.new in sentinels])
|
||||||
return len(patchings)
|
return len(patchings)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
from __future__ import absolute_import, division, print_function
|
from __future__ import absolute_import, division, print_function
|
||||||
import pdb
|
import pdb
|
||||||
import sys
|
import sys
|
||||||
|
from doctest import UnexpectedException
|
||||||
|
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
|
@ -85,6 +86,17 @@ def _enter_pdb(node, excinfo, rep):
|
||||||
# for not completely clear reasons.
|
# for not completely clear reasons.
|
||||||
tw = node.config.pluginmanager.getplugin("terminalreporter")._tw
|
tw = node.config.pluginmanager.getplugin("terminalreporter")._tw
|
||||||
tw.line()
|
tw.line()
|
||||||
|
|
||||||
|
captured_stdout = rep.capstdout
|
||||||
|
if len(captured_stdout) > 0:
|
||||||
|
tw.sep(">", "captured stdout")
|
||||||
|
tw.line(captured_stdout)
|
||||||
|
|
||||||
|
captured_stderr = rep.capstderr
|
||||||
|
if len(captured_stderr) > 0:
|
||||||
|
tw.sep(">", "captured stderr")
|
||||||
|
tw.line(captured_stderr)
|
||||||
|
|
||||||
tw.sep(">", "traceback")
|
tw.sep(">", "traceback")
|
||||||
rep.toterminal(tw)
|
rep.toterminal(tw)
|
||||||
tw.sep(">", "entering PDB")
|
tw.sep(">", "entering PDB")
|
||||||
|
@ -95,10 +107,9 @@ def _enter_pdb(node, excinfo, rep):
|
||||||
|
|
||||||
|
|
||||||
def _postmortem_traceback(excinfo):
|
def _postmortem_traceback(excinfo):
|
||||||
|
if isinstance(excinfo.value, UnexpectedException):
|
||||||
# A doctest.UnexpectedException is not useful for post_mortem.
|
# A doctest.UnexpectedException is not useful for post_mortem.
|
||||||
# Use the underlying exception instead:
|
# Use the underlying exception instead:
|
||||||
from doctest import UnexpectedException
|
|
||||||
if isinstance(excinfo.value, UnexpectedException):
|
|
||||||
return excinfo.value.exc_info[2]
|
return excinfo.value.exc_info[2]
|
||||||
else:
|
else:
|
||||||
return excinfo._excinfo[2]
|
return excinfo._excinfo[2]
|
||||||
|
|
|
@ -166,7 +166,7 @@ def reorder_items(items):
|
||||||
items_by_argkey = {}
|
items_by_argkey = {}
|
||||||
for scopenum in range(0, scopenum_function):
|
for scopenum in range(0, scopenum_function):
|
||||||
argkeys_cache[scopenum] = d = {}
|
argkeys_cache[scopenum] = d = {}
|
||||||
items_by_argkey[scopenum] = item_d = defaultdict(list)
|
items_by_argkey[scopenum] = item_d = defaultdict(deque)
|
||||||
for item in items:
|
for item in items:
|
||||||
keys = OrderedDict.fromkeys(get_parametrized_fixture_keys(item, scopenum))
|
keys = OrderedDict.fromkeys(get_parametrized_fixture_keys(item, scopenum))
|
||||||
if keys:
|
if keys:
|
||||||
|
@ -174,12 +174,19 @@ def reorder_items(items):
|
||||||
for key in keys:
|
for key in keys:
|
||||||
item_d[key].append(item)
|
item_d[key].append(item)
|
||||||
items = OrderedDict.fromkeys(items)
|
items = OrderedDict.fromkeys(items)
|
||||||
return list(reorder_items_atscope(items, set(), argkeys_cache, items_by_argkey, 0))
|
return list(reorder_items_atscope(items, argkeys_cache, items_by_argkey, 0))
|
||||||
|
|
||||||
|
|
||||||
def reorder_items_atscope(items, ignore, argkeys_cache, items_by_argkey, scopenum):
|
def fix_cache_order(item, argkeys_cache, items_by_argkey):
|
||||||
|
for scopenum in range(0, scopenum_function):
|
||||||
|
for key in argkeys_cache[scopenum].get(item, []):
|
||||||
|
items_by_argkey[scopenum][key].appendleft(item)
|
||||||
|
|
||||||
|
|
||||||
|
def reorder_items_atscope(items, argkeys_cache, items_by_argkey, scopenum):
|
||||||
if scopenum >= scopenum_function or len(items) < 3:
|
if scopenum >= scopenum_function or len(items) < 3:
|
||||||
return items
|
return items
|
||||||
|
ignore = set()
|
||||||
items_deque = deque(items)
|
items_deque = deque(items)
|
||||||
items_done = OrderedDict()
|
items_done = OrderedDict()
|
||||||
scoped_items_by_argkey = items_by_argkey[scopenum]
|
scoped_items_by_argkey = items_by_argkey[scopenum]
|
||||||
|
@ -197,13 +204,14 @@ def reorder_items_atscope(items, ignore, argkeys_cache, items_by_argkey, scopenu
|
||||||
else:
|
else:
|
||||||
slicing_argkey, _ = argkeys.popitem()
|
slicing_argkey, _ = argkeys.popitem()
|
||||||
# we don't have to remove relevant items from later in the deque because they'll just be ignored
|
# we don't have to remove relevant items from later in the deque because they'll just be ignored
|
||||||
for i in reversed(scoped_items_by_argkey[slicing_argkey]):
|
matching_items = [i for i in scoped_items_by_argkey[slicing_argkey] if i in items]
|
||||||
if i in items:
|
for i in reversed(matching_items):
|
||||||
|
fix_cache_order(i, argkeys_cache, items_by_argkey)
|
||||||
items_deque.appendleft(i)
|
items_deque.appendleft(i)
|
||||||
break
|
break
|
||||||
if no_argkey_group:
|
if no_argkey_group:
|
||||||
no_argkey_group = reorder_items_atscope(
|
no_argkey_group = reorder_items_atscope(
|
||||||
no_argkey_group, set(), argkeys_cache, items_by_argkey, scopenum + 1)
|
no_argkey_group, argkeys_cache, items_by_argkey, scopenum + 1)
|
||||||
for item in no_argkey_group:
|
for item in no_argkey_group:
|
||||||
items_done[item] = None
|
items_done[item] = None
|
||||||
ignore.add(slicing_argkey)
|
ignore.add(slicing_argkey)
|
||||||
|
|
|
@ -504,7 +504,7 @@ class _LiveLoggingStreamHandler(logging.StreamHandler):
|
||||||
if not self._test_outcome_written:
|
if not self._test_outcome_written:
|
||||||
self._test_outcome_written = True
|
self._test_outcome_written = True
|
||||||
self.stream.write('\n')
|
self.stream.write('\n')
|
||||||
if not self._section_name_shown:
|
if not self._section_name_shown and self._when:
|
||||||
self.stream.section('live log ' + self._when, sep='-', bold=True)
|
self.stream.section('live log ' + self._when, sep='-', bold=True)
|
||||||
self._section_name_shown = True
|
self._section_name_shown = True
|
||||||
logging.StreamHandler.emit(self, record)
|
logging.StreamHandler.emit(self, record)
|
||||||
|
|
|
@ -75,7 +75,7 @@ class ParameterSet(namedtuple('ParameterSet', 'values, marks, id')):
|
||||||
return cls(argval, marks=newmarks, id=None)
|
return cls(argval, marks=newmarks, id=None)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _for_parameterize(cls, argnames, argvalues, function, config):
|
def _for_parametrize(cls, argnames, argvalues, function, config):
|
||||||
if not isinstance(argnames, (tuple, list)):
|
if not isinstance(argnames, (tuple, list)):
|
||||||
argnames = [x.strip() for x in argnames.split(",") if x.strip()]
|
argnames = [x.strip() for x in argnames.split(",") if x.strip()]
|
||||||
force_tuple = len(argnames) == 1
|
force_tuple = len(argnames) == 1
|
||||||
|
|
|
@ -785,7 +785,8 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
|
||||||
from _pytest.fixtures import scope2index
|
from _pytest.fixtures import scope2index
|
||||||
from _pytest.mark import ParameterSet
|
from _pytest.mark import ParameterSet
|
||||||
from py.io import saferepr
|
from py.io import saferepr
|
||||||
argnames, parameters = ParameterSet._for_parameterize(
|
|
||||||
|
argnames, parameters = ParameterSet._for_parametrize(
|
||||||
argnames, argvalues, self.function, self.config)
|
argnames, argvalues, self.function, self.config)
|
||||||
del argvalues
|
del argvalues
|
||||||
|
|
||||||
|
|
|
@ -453,6 +453,10 @@ def raises(expected_exception, *args, **kwargs):
|
||||||
Assert that a code block/function call raises ``expected_exception``
|
Assert that a code block/function call raises ``expected_exception``
|
||||||
and raise a failure exception otherwise.
|
and raise a failure exception otherwise.
|
||||||
|
|
||||||
|
:arg message: if specified, provides a custom failure message if the
|
||||||
|
exception is not raised
|
||||||
|
:arg match: if specified, asserts that the exception matches a text or regex
|
||||||
|
|
||||||
This helper produces a ``ExceptionInfo()`` object (see below).
|
This helper produces a ``ExceptionInfo()`` object (see below).
|
||||||
|
|
||||||
You may use this function as a context manager::
|
You may use this function as a context manager::
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Move import of ``doctest.UnexpectedException`` to top-level to avoid possible errors when using ``--pdb``.
|
|
@ -0,0 +1 @@
|
||||||
|
Added printing of captured stdout/stderr before entering pdb, and improved a test which was giving false negatives about output capturing.
|
|
@ -0,0 +1 @@
|
||||||
|
Fix ordering of tests using parametrized fixtures which can lead to fixtures being created more than necessary.
|
|
@ -0,0 +1 @@
|
||||||
|
Rename ``ParameterSet._for_parameterize()`` to ``_for_parametrize()`` in order to comply with the naming convention.
|
|
@ -0,0 +1 @@
|
||||||
|
Fix bug where logging happening at hooks outside of "test run" hooks would cause an internal error.
|
|
@ -0,0 +1 @@
|
||||||
|
Add Sphinx parameter docs for ``match`` and ``message`` args to ``pytest.raises``.
|
|
@ -0,0 +1 @@
|
||||||
|
Detect arguments injected by ``unittest.mock.patch`` decorator correctly when pypi ``mock.patch`` is installed and imported.
|
|
@ -0,0 +1 @@
|
||||||
|
Skip failing pdb/doctest test on mac.
|
|
@ -0,0 +1,30 @@
|
||||||
|
This directory contains "newsfragments" which are short that contain a small **ReST**-formatted
|
||||||
|
text that will be added to the next ``CHANGELOG``.
|
||||||
|
|
||||||
|
The ``CHANGELOG`` will be read by users, so this description should be aimed to pytest users
|
||||||
|
instead of describing internal changes which are only relevant to the developers.
|
||||||
|
|
||||||
|
Make sure to use full sentences with correct case and punctuation, for example: *Fix issue with non-ascii contents in doctest text files.*
|
||||||
|
|
||||||
|
Each file should be named like ``<ISSUE>.<TYPE>.rst``, where
|
||||||
|
``<ISSUE>`` is an issue number, and ``<TYPE>`` is one of:
|
||||||
|
|
||||||
|
* ``feature``: new user facing features, like new command-line options and new behavior.
|
||||||
|
* ``bugfix``: fixes a reported bug.
|
||||||
|
* ``doc``: documentation improvement, like rewording an entire session or adding missing docs.
|
||||||
|
* ``removal``: feature deprecation or removal.
|
||||||
|
* ``vendor``: changes in packages vendored in pytest.
|
||||||
|
* ``trivial``: fixing a small typo or internal change that might be noteworthy.
|
||||||
|
|
||||||
|
So for example: ``123.feature.rst``, ``456.bugfix.rst``.
|
||||||
|
|
||||||
|
If your PR fixes an issue, use that number here. If there is no issue,
|
||||||
|
then after you submit the PR and get the PR number you can add a
|
||||||
|
changelog using that instead.
|
||||||
|
|
||||||
|
If you are not sure what issue type to use, don't hesitate to ask in your PR.
|
||||||
|
|
||||||
|
Note that the ``towncrier`` tool will automatically
|
||||||
|
reflow your text, so it will work best if you stick to a single paragraph, but multiple sentences and links are OK
|
||||||
|
and encouraged. You can install ``towncrier`` and then run ``towncrier --draft``
|
||||||
|
if you want to get a preview of how your change will look in the final release notes.
|
|
@ -462,19 +462,24 @@ Here is an example definition of a hook wrapper::
|
||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True)
|
@pytest.hookimpl(hookwrapper=True)
|
||||||
def pytest_pyfunc_call(pyfuncitem):
|
def pytest_pyfunc_call(pyfuncitem):
|
||||||
# do whatever you want before the next hook executes
|
do_something_before_next_hook_executes()
|
||||||
|
|
||||||
outcome = yield
|
outcome = yield
|
||||||
# outcome.excinfo may be None or a (cls, val, tb) tuple
|
# outcome.excinfo may be None or a (cls, val, tb) tuple
|
||||||
|
|
||||||
res = outcome.get_result() # will raise if outcome was exception
|
res = outcome.get_result() # will raise if outcome was exception
|
||||||
# postprocess result
|
|
||||||
|
post_process_result(res)
|
||||||
|
|
||||||
|
outcome.force_result(new_res) # to override the return value to the plugin system
|
||||||
|
|
||||||
Note that hook wrappers don't return results themselves, they merely
|
Note that hook wrappers don't return results themselves, they merely
|
||||||
perform tracing or other side effects around the actual hook implementations.
|
perform tracing or other side effects around the actual hook implementations.
|
||||||
If the result of the underlying hook is a mutable object, they may modify
|
If the result of the underlying hook is a mutable object, they may modify
|
||||||
that result but it's probably better to avoid it.
|
that result but it's probably better to avoid it.
|
||||||
|
|
||||||
|
For more information, consult the `pluggy documentation <http://pluggy.readthedocs.io/en/latest/#wrappers>`_.
|
||||||
|
|
||||||
|
|
||||||
Hook function ordering / call example
|
Hook function ordering / call example
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -16,7 +16,7 @@ classifiers = [
|
||||||
'Topic :: Utilities',
|
'Topic :: Utilities',
|
||||||
] + [
|
] + [
|
||||||
('Programming Language :: Python :: %s' % x)
|
('Programming Language :: Python :: %s' % x)
|
||||||
for x in '2 2.7 3 3.4 3.5 3.6'.split()
|
for x in '2 2.7 3 3.4 3.5 3.6 3.7'.split()
|
||||||
]
|
]
|
||||||
|
|
||||||
with open('README.rst') as fd:
|
with open('README.rst') as fd:
|
||||||
|
|
|
@ -2168,6 +2168,47 @@ class TestFixtureMarker(object):
|
||||||
test_mod1.py::test_func1[m2] PASSED
|
test_mod1.py::test_func1[m2] PASSED
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
def test_dynamic_parametrized_ordering(self, testdir):
|
||||||
|
testdir.makeini("""
|
||||||
|
[pytest]
|
||||||
|
console_output_style=classic
|
||||||
|
""")
|
||||||
|
testdir.makeconftest("""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
def pytest_configure(config):
|
||||||
|
class DynamicFixturePlugin(object):
|
||||||
|
@pytest.fixture(scope='session', params=['flavor1', 'flavor2'])
|
||||||
|
def flavor(self, request):
|
||||||
|
return request.param
|
||||||
|
config.pluginmanager.register(DynamicFixturePlugin(), 'flavor-fixture')
|
||||||
|
|
||||||
|
@pytest.fixture(scope='session', params=['vxlan', 'vlan'])
|
||||||
|
def encap(request):
|
||||||
|
return request.param
|
||||||
|
|
||||||
|
@pytest.fixture(scope='session', autouse='True')
|
||||||
|
def reprovision(request, flavor, encap):
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
testdir.makepyfile("""
|
||||||
|
def test(reprovision):
|
||||||
|
pass
|
||||||
|
def test2(reprovision):
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest("-v")
|
||||||
|
result.stdout.fnmatch_lines("""
|
||||||
|
test_dynamic_parametrized_ordering.py::test[flavor1-vxlan] PASSED
|
||||||
|
test_dynamic_parametrized_ordering.py::test2[flavor1-vxlan] PASSED
|
||||||
|
test_dynamic_parametrized_ordering.py::test[flavor2-vxlan] PASSED
|
||||||
|
test_dynamic_parametrized_ordering.py::test2[flavor2-vxlan] PASSED
|
||||||
|
test_dynamic_parametrized_ordering.py::test[flavor2-vlan] PASSED
|
||||||
|
test_dynamic_parametrized_ordering.py::test2[flavor2-vlan] PASSED
|
||||||
|
test_dynamic_parametrized_ordering.py::test[flavor1-vlan] PASSED
|
||||||
|
test_dynamic_parametrized_ordering.py::test2[flavor1-vlan] PASSED
|
||||||
|
""")
|
||||||
|
|
||||||
def test_class_ordering(self, testdir):
|
def test_class_ordering(self, testdir):
|
||||||
testdir.makeini("""
|
testdir.makeini("""
|
||||||
[pytest]
|
[pytest]
|
||||||
|
|
|
@ -147,6 +147,28 @@ class TestMockDecoration(object):
|
||||||
reprec = testdir.inline_run()
|
reprec = testdir.inline_run()
|
||||||
reprec.assertoutcome(passed=1)
|
reprec.assertoutcome(passed=1)
|
||||||
|
|
||||||
|
def test_unittest_mock_and_pypi_mock(self, testdir):
|
||||||
|
pytest.importorskip("unittest.mock")
|
||||||
|
pytest.importorskip("mock", "1.0.1")
|
||||||
|
testdir.makepyfile("""
|
||||||
|
import mock
|
||||||
|
import unittest.mock
|
||||||
|
class TestBoth(object):
|
||||||
|
@unittest.mock.patch("os.path.abspath")
|
||||||
|
def test_hello(self, abspath):
|
||||||
|
import os
|
||||||
|
os.path.abspath("hello")
|
||||||
|
abspath.assert_any_call("hello")
|
||||||
|
|
||||||
|
@mock.patch("os.path.abspath")
|
||||||
|
def test_hello_mock(self, abspath):
|
||||||
|
import os
|
||||||
|
os.path.abspath("hello")
|
||||||
|
abspath.assert_any_call("hello")
|
||||||
|
""")
|
||||||
|
reprec = testdir.inline_run()
|
||||||
|
reprec.assertoutcome(passed=2)
|
||||||
|
|
||||||
def test_mock(self, testdir):
|
def test_mock(self, testdir):
|
||||||
pytest.importorskip("mock", "1.0.1")
|
pytest.importorskip("mock", "1.0.1")
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
|
|
|
@ -141,13 +141,14 @@ class TestPDB(object):
|
||||||
child.sendeof()
|
child.sendeof()
|
||||||
self.flush(child)
|
self.flush(child)
|
||||||
|
|
||||||
def test_pdb_interaction_capture(self, testdir):
|
def test_pdb_print_captured_stdout(self, testdir):
|
||||||
p1 = testdir.makepyfile("""
|
p1 = testdir.makepyfile("""
|
||||||
def test_1():
|
def test_1():
|
||||||
print("getrekt")
|
print("get\\x20rekt")
|
||||||
assert False
|
assert False
|
||||||
""")
|
""")
|
||||||
child = testdir.spawn_pytest("--pdb %s" % p1)
|
child = testdir.spawn_pytest("--pdb %s" % p1)
|
||||||
|
child.expect("captured stdout")
|
||||||
child.expect("get rekt")
|
child.expect("get rekt")
|
||||||
child.expect("(Pdb)")
|
child.expect("(Pdb)")
|
||||||
child.sendeof()
|
child.sendeof()
|
||||||
|
@ -156,6 +157,36 @@ class TestPDB(object):
|
||||||
assert "get rekt" not in rest
|
assert "get rekt" not in rest
|
||||||
self.flush(child)
|
self.flush(child)
|
||||||
|
|
||||||
|
def test_pdb_print_captured_stderr(self, testdir):
|
||||||
|
p1 = testdir.makepyfile("""
|
||||||
|
def test_1():
|
||||||
|
import sys
|
||||||
|
sys.stderr.write("get\\x20rekt")
|
||||||
|
assert False
|
||||||
|
""")
|
||||||
|
child = testdir.spawn_pytest("--pdb %s" % p1)
|
||||||
|
child.expect("captured stderr")
|
||||||
|
child.expect("get rekt")
|
||||||
|
child.expect("(Pdb)")
|
||||||
|
child.sendeof()
|
||||||
|
rest = child.read().decode("utf8")
|
||||||
|
assert "1 failed" in rest
|
||||||
|
assert "get rekt" not in rest
|
||||||
|
self.flush(child)
|
||||||
|
|
||||||
|
def test_pdb_dont_print_empty_captured_stdout_and_stderr(self, testdir):
|
||||||
|
p1 = testdir.makepyfile("""
|
||||||
|
def test_1():
|
||||||
|
assert False
|
||||||
|
""")
|
||||||
|
child = testdir.spawn_pytest("--pdb %s" % p1)
|
||||||
|
child.expect("(Pdb)")
|
||||||
|
output = child.before.decode("utf8")
|
||||||
|
child.sendeof()
|
||||||
|
assert "captured stdout" not in output
|
||||||
|
assert "captured stderr" not in output
|
||||||
|
self.flush(child)
|
||||||
|
|
||||||
def test_pdb_interaction_exception(self, testdir):
|
def test_pdb_interaction_exception(self, testdir):
|
||||||
p1 = testdir.makepyfile("""
|
p1 = testdir.makepyfile("""
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -267,6 +298,10 @@ class TestPDB(object):
|
||||||
child.read()
|
child.read()
|
||||||
self.flush(child)
|
self.flush(child)
|
||||||
|
|
||||||
|
# For some reason the interaction between doctest's and pytest's output
|
||||||
|
# capturing mechanisms are messing up the stdout on mac. (See #985).
|
||||||
|
# Should be solvable, but skipping until we have a chance to investigate.
|
||||||
|
@pytest.mark.xfail("sys.platform == 'darwin'", reason='See issue #985', run=False)
|
||||||
def test_pdb_interaction_doctest(self, testdir):
|
def test_pdb_interaction_doctest(self, testdir):
|
||||||
p1 = testdir.makepyfile("""
|
p1 = testdir.makepyfile("""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
|
@ -206,13 +206,13 @@ class TestWarns(object):
|
||||||
with pytest.warns(RuntimeWarning):
|
with pytest.warns(RuntimeWarning):
|
||||||
warnings.warn("user", UserWarning)
|
warnings.warn("user", UserWarning)
|
||||||
excinfo.match(r"DID NOT WARN. No warnings of type \(.+RuntimeWarning.+,\) was emitted. "
|
excinfo.match(r"DID NOT WARN. No warnings of type \(.+RuntimeWarning.+,\) was emitted. "
|
||||||
r"The list of emitted warnings is: \[UserWarning\('user',\)\].")
|
r"The list of emitted warnings is: \[UserWarning\('user',?\)\].")
|
||||||
|
|
||||||
with pytest.raises(pytest.fail.Exception) as excinfo:
|
with pytest.raises(pytest.fail.Exception) as excinfo:
|
||||||
with pytest.warns(UserWarning):
|
with pytest.warns(UserWarning):
|
||||||
warnings.warn("runtime", RuntimeWarning)
|
warnings.warn("runtime", RuntimeWarning)
|
||||||
excinfo.match(r"DID NOT WARN. No warnings of type \(.+UserWarning.+,\) was emitted. "
|
excinfo.match(r"DID NOT WARN. No warnings of type \(.+UserWarning.+,\) was emitted. "
|
||||||
r"The list of emitted warnings is: \[RuntimeWarning\('runtime',\)\].")
|
r"The list of emitted warnings is: \[RuntimeWarning\('runtime',?\)\].")
|
||||||
|
|
||||||
with pytest.raises(pytest.fail.Exception) as excinfo:
|
with pytest.raises(pytest.fail.Exception) as excinfo:
|
||||||
with pytest.warns(UserWarning):
|
with pytest.warns(UserWarning):
|
||||||
|
|
Loading…
Reference in New Issue