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): | ||||||
|     # A doctest.UnexpectedException is not useful for post_mortem. |  | ||||||
|     # Use the underlying exception instead: |  | ||||||
|     from doctest import UnexpectedException |  | ||||||
|     if isinstance(excinfo.value, UnexpectedException): |     if isinstance(excinfo.value, UnexpectedException): | ||||||
|  |         # A doctest.UnexpectedException is not useful for post_mortem. | ||||||
|  |         # Use the underlying exception instead: | ||||||
|         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): | ||||||
|                         items_deque.appendleft(i) |                     fix_cache_order(i, argkeys_cache, items_by_argkey) | ||||||
|  |                     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,19 +141,50 @@ 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("getrekt") |         child.expect("captured stdout") | ||||||
|  |         child.expect("get rekt") | ||||||
|         child.expect("(Pdb)") |         child.expect("(Pdb)") | ||||||
|         child.sendeof() |         child.sendeof() | ||||||
|         rest = child.read().decode("utf8") |         rest = child.read().decode("utf8") | ||||||
|         assert "1 failed" in rest |         assert "1 failed" in rest | ||||||
|         assert "getrekt" not in rest |         assert "get rekt" not in rest | ||||||
|  |         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) |         self.flush(child) | ||||||
| 
 | 
 | ||||||
|     def test_pdb_interaction_exception(self, testdir): |     def test_pdb_interaction_exception(self, testdir): | ||||||
|  | @ -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