Increase truncation threshold with -v, disable with -vv
Fix #6682 Fix #8403
This commit is contained in:
parent
9e8a6b6eeb
commit
be8d63e33b
|
@ -0,0 +1,5 @@
|
||||||
|
By default, pytest will truncate long strings in assert errors so they don't clutter the output too much,
|
||||||
|
currently at ``240`` characters by default.
|
||||||
|
|
||||||
|
However, in some cases the longer output helps, or is even crucial, to diagnose a failure. Using ``-v`` will
|
||||||
|
now increase the truncation threshold to ``2400`` characters, and ``-vv`` or higher will disable truncation entirely.
|
|
@ -161,6 +161,243 @@ will be shown (because KeyboardInterrupt is caught by pytest). By using this
|
||||||
option you make sure a trace is shown.
|
option you make sure a trace is shown.
|
||||||
|
|
||||||
|
|
||||||
|
Verbosity
|
||||||
|
---------
|
||||||
|
|
||||||
|
The ``-v`` flag controls the verbosity of pytest output in various aspects: test session progress, assertion
|
||||||
|
details when tests fail, fixtures details with ``--fixtures``, etc.
|
||||||
|
|
||||||
|
.. regendoc:wipe
|
||||||
|
|
||||||
|
Consider this simple file:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# content of test_verbosity_example.py
|
||||||
|
def test_ok():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_words_fail():
|
||||||
|
fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"]
|
||||||
|
fruits2 = ["banana", "apple", "orange", "melon", "kiwi"]
|
||||||
|
assert fruits1 == fruits2
|
||||||
|
|
||||||
|
|
||||||
|
def test_numbers_fail():
|
||||||
|
number_to_text1 = {str(x): x for x in range(5)}
|
||||||
|
number_to_text2 = {str(x * 10): x * 10 for x in range(5)}
|
||||||
|
assert number_to_text1 == number_to_text2
|
||||||
|
|
||||||
|
|
||||||
|
def test_long_text_fail():
|
||||||
|
long_text = "Lorem ipsum dolor sit amet " * 10
|
||||||
|
assert "hello world" in long_text
|
||||||
|
|
||||||
|
Executing pytest normally gives us this output (we are skipping the header to focus on the rest):
|
||||||
|
|
||||||
|
.. code-block:: pytest
|
||||||
|
|
||||||
|
$ pytest --no-header
|
||||||
|
=========================== test session starts ===========================
|
||||||
|
collected 4 items
|
||||||
|
|
||||||
|
test_verbosity_example.py .FFF [100%]
|
||||||
|
|
||||||
|
================================ FAILURES =================================
|
||||||
|
_____________________________ test_words_fail _____________________________
|
||||||
|
|
||||||
|
def test_words_fail():
|
||||||
|
fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"]
|
||||||
|
fruits2 = ["banana", "apple", "orange", "melon", "kiwi"]
|
||||||
|
> assert fruits1 == fruits2
|
||||||
|
E AssertionError: assert ['banana', 'a...elon', 'kiwi'] == ['banana', 'a...elon', 'kiwi']
|
||||||
|
E At index 2 diff: 'grapes' != 'orange'
|
||||||
|
E Use -v to get the full diff
|
||||||
|
|
||||||
|
test_verbosity_example.py:8: AssertionError
|
||||||
|
____________________________ test_numbers_fail ____________________________
|
||||||
|
|
||||||
|
def test_numbers_fail():
|
||||||
|
number_to_text1 = {str(x): x for x in range(5)}
|
||||||
|
number_to_text2 = {str(x * 10): x * 10 for x in range(5)}
|
||||||
|
> assert number_to_text1 == number_to_text2
|
||||||
|
E AssertionError: assert {'0': 0, '1':..., '3': 3, ...} == {'0': 0, '10'...'30': 30, ...}
|
||||||
|
E Omitting 1 identical items, use -vv to show
|
||||||
|
E Left contains 4 more items:
|
||||||
|
E {'1': 1, '2': 2, '3': 3, '4': 4}
|
||||||
|
E Right contains 4 more items:
|
||||||
|
E {'10': 10, '20': 20, '30': 30, '40': 40}
|
||||||
|
E Use -v to get the full diff
|
||||||
|
|
||||||
|
test_verbosity_example.py:14: AssertionError
|
||||||
|
___________________________ test_long_text_fail ___________________________
|
||||||
|
|
||||||
|
def test_long_text_fail():
|
||||||
|
long_text = "Lorem ipsum dolor sit amet " * 10
|
||||||
|
> assert "hello world" in long_text
|
||||||
|
E AssertionError: assert 'hello world' in 'Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ips... sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet '
|
||||||
|
|
||||||
|
test_verbosity_example.py:19: AssertionError
|
||||||
|
========================= short test summary info =========================
|
||||||
|
FAILED test_verbosity_example.py::test_words_fail - AssertionError: asser...
|
||||||
|
FAILED test_verbosity_example.py::test_numbers_fail - AssertionError: ass...
|
||||||
|
FAILED test_verbosity_example.py::test_long_text_fail - AssertionError: a...
|
||||||
|
======================= 3 failed, 1 passed in 0.08s =======================
|
||||||
|
|
||||||
|
Notice that:
|
||||||
|
|
||||||
|
* Each test inside the file is shown by a single character in the output: ``.`` for passing, ``F`` for failure.
|
||||||
|
* ``test_words_fail`` failed, and we are shown a short summary indicating the index 2 of the two lists differ.
|
||||||
|
* ``test_numbers_fail`` failed, and we are shown a summary of left/right differences on dictionary items. Identical items are omitted.
|
||||||
|
* ``test_long_text_fail`` failed, and the right hand side of the ``in`` statement is truncated using ``...```
|
||||||
|
because it is longer than an internal threshold (240 characters currently).
|
||||||
|
|
||||||
|
Now we can increase pytest's verbosity:
|
||||||
|
|
||||||
|
.. code-block:: pytest
|
||||||
|
|
||||||
|
$ pytest --no-header -v
|
||||||
|
=========================== test session starts ===========================
|
||||||
|
collecting ... collected 4 items
|
||||||
|
|
||||||
|
test_verbosity_example.py::test_ok PASSED [ 25%]
|
||||||
|
test_verbosity_example.py::test_words_fail FAILED [ 50%]
|
||||||
|
test_verbosity_example.py::test_numbers_fail FAILED [ 75%]
|
||||||
|
test_verbosity_example.py::test_long_text_fail FAILED [100%]
|
||||||
|
|
||||||
|
================================ FAILURES =================================
|
||||||
|
_____________________________ test_words_fail _____________________________
|
||||||
|
|
||||||
|
def test_words_fail():
|
||||||
|
fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"]
|
||||||
|
fruits2 = ["banana", "apple", "orange", "melon", "kiwi"]
|
||||||
|
> assert fruits1 == fruits2
|
||||||
|
E AssertionError: assert ['banana', 'a...elon', 'kiwi'] == ['banana', 'a...elon', 'kiwi']
|
||||||
|
E At index 2 diff: 'grapes' != 'orange'
|
||||||
|
E Full diff:
|
||||||
|
E - ['banana', 'apple', 'orange', 'melon', 'kiwi']
|
||||||
|
E ? ^ ^^
|
||||||
|
E + ['banana', 'apple', 'grapes', 'melon', 'kiwi']
|
||||||
|
E ? ^ ^ +
|
||||||
|
|
||||||
|
test_verbosity_example.py:8: AssertionError
|
||||||
|
____________________________ test_numbers_fail ____________________________
|
||||||
|
|
||||||
|
def test_numbers_fail():
|
||||||
|
number_to_text1 = {str(x): x for x in range(5)}
|
||||||
|
number_to_text2 = {str(x * 10): x * 10 for x in range(5)}
|
||||||
|
> assert number_to_text1 == number_to_text2
|
||||||
|
E AssertionError: assert {'0': 0, '1':..., '3': 3, ...} == {'0': 0, '10'...'30': 30, ...}
|
||||||
|
E Omitting 1 identical items, use -vv to show
|
||||||
|
E Left contains 4 more items:
|
||||||
|
E {'1': 1, '2': 2, '3': 3, '4': 4}
|
||||||
|
E Right contains 4 more items:
|
||||||
|
E {'10': 10, '20': 20, '30': 30, '40': 40}
|
||||||
|
E Full diff:
|
||||||
|
E - {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40}...
|
||||||
|
E
|
||||||
|
E ...Full output truncated (3 lines hidden), use '-vv' to show
|
||||||
|
|
||||||
|
test_verbosity_example.py:14: AssertionError
|
||||||
|
___________________________ test_long_text_fail ___________________________
|
||||||
|
|
||||||
|
def test_long_text_fail():
|
||||||
|
long_text = "Lorem ipsum dolor sit amet " * 10
|
||||||
|
> assert "hello world" in long_text
|
||||||
|
E AssertionError: assert 'hello world' in 'Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet '
|
||||||
|
|
||||||
|
test_verbosity_example.py:19: AssertionError
|
||||||
|
========================= short test summary info =========================
|
||||||
|
FAILED test_verbosity_example.py::test_words_fail - AssertionError: asser...
|
||||||
|
FAILED test_verbosity_example.py::test_numbers_fail - AssertionError: ass...
|
||||||
|
FAILED test_verbosity_example.py::test_long_text_fail - AssertionError: a...
|
||||||
|
======================= 3 failed, 1 passed in 0.07s =======================
|
||||||
|
|
||||||
|
Notice now that:
|
||||||
|
|
||||||
|
* Each test inside the file gets its own line in the output.
|
||||||
|
* ``test_words_fail`` now shows the two failing lists in full, in addition to which index differs.
|
||||||
|
* ``test_numbers_fail`` now shows a text diff of the two dictionaries, truncated.
|
||||||
|
* ``test_long_text_fail`` no longer truncates the right hand side of the ``in`` statement, because the internal
|
||||||
|
threshold for truncation is larger now (2400 characters currently).
|
||||||
|
|
||||||
|
Now if we increase verbosity even more:
|
||||||
|
|
||||||
|
.. code-block:: pytest
|
||||||
|
|
||||||
|
$ pytest --no-header -vv
|
||||||
|
=========================== test session starts ===========================
|
||||||
|
collecting ... collected 4 items
|
||||||
|
|
||||||
|
test_verbosity_example.py::test_ok PASSED [ 25%]
|
||||||
|
test_verbosity_example.py::test_words_fail FAILED [ 50%]
|
||||||
|
test_verbosity_example.py::test_numbers_fail FAILED [ 75%]
|
||||||
|
test_verbosity_example.py::test_long_text_fail FAILED [100%]
|
||||||
|
|
||||||
|
================================ FAILURES =================================
|
||||||
|
_____________________________ test_words_fail _____________________________
|
||||||
|
|
||||||
|
def test_words_fail():
|
||||||
|
fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"]
|
||||||
|
fruits2 = ["banana", "apple", "orange", "melon", "kiwi"]
|
||||||
|
> assert fruits1 == fruits2
|
||||||
|
E AssertionError: assert ['banana', 'apple', 'grapes', 'melon', 'kiwi'] == ['banana', 'apple', 'orange', 'melon', 'kiwi']
|
||||||
|
E At index 2 diff: 'grapes' != 'orange'
|
||||||
|
E Full diff:
|
||||||
|
E - ['banana', 'apple', 'orange', 'melon', 'kiwi']
|
||||||
|
E ? ^ ^^
|
||||||
|
E + ['banana', 'apple', 'grapes', 'melon', 'kiwi']
|
||||||
|
E ? ^ ^ +
|
||||||
|
|
||||||
|
test_verbosity_example.py:8: AssertionError
|
||||||
|
____________________________ test_numbers_fail ____________________________
|
||||||
|
|
||||||
|
def test_numbers_fail():
|
||||||
|
number_to_text1 = {str(x): x for x in range(5)}
|
||||||
|
number_to_text2 = {str(x * 10): x * 10 for x in range(5)}
|
||||||
|
> assert number_to_text1 == number_to_text2
|
||||||
|
E AssertionError: assert {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4} == {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40}
|
||||||
|
E Common items:
|
||||||
|
E {'0': 0}
|
||||||
|
E Left contains 4 more items:
|
||||||
|
E {'1': 1, '2': 2, '3': 3, '4': 4}
|
||||||
|
E Right contains 4 more items:
|
||||||
|
E {'10': 10, '20': 20, '30': 30, '40': 40}
|
||||||
|
E Full diff:
|
||||||
|
E - {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40}
|
||||||
|
E ? - - - - - - - -
|
||||||
|
E + {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4}
|
||||||
|
|
||||||
|
test_verbosity_example.py:14: AssertionError
|
||||||
|
___________________________ test_long_text_fail ___________________________
|
||||||
|
|
||||||
|
def test_long_text_fail():
|
||||||
|
long_text = "Lorem ipsum dolor sit amet " * 10
|
||||||
|
> assert "hello world" in long_text
|
||||||
|
E AssertionError: assert 'hello world' in 'Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet '
|
||||||
|
|
||||||
|
test_verbosity_example.py:19: AssertionError
|
||||||
|
========================= short test summary info =========================
|
||||||
|
FAILED test_verbosity_example.py::test_words_fail - AssertionError: asser...
|
||||||
|
FAILED test_verbosity_example.py::test_numbers_fail - AssertionError: ass...
|
||||||
|
FAILED test_verbosity_example.py::test_long_text_fail - AssertionError: a...
|
||||||
|
======================= 3 failed, 1 passed in 0.07s =======================
|
||||||
|
|
||||||
|
Notice now that:
|
||||||
|
|
||||||
|
* Each test inside the file gets its own line in the output.
|
||||||
|
* ``test_words_fail`` gives the same output as before in this case.
|
||||||
|
* ``test_numbers_fail`` now shows a full text diff of the two dictionaries.
|
||||||
|
* ``test_long_text_fail`` also doesn't truncate on the right hand side as before, but now pytest won't truncate any
|
||||||
|
text at all, regardless of its size.
|
||||||
|
|
||||||
|
Those were examples of how verbosity affects normal test session output, but verbosity also is used in other
|
||||||
|
situations, for example you are shown even fixtures that start with ``_`` if you use ``pytest --fixtures -v``.
|
||||||
|
|
||||||
|
Using higher verbosity levels (``-vvv``, ``-vvvv``, ...) is supported, but has no effect in pytest itself at the moment,
|
||||||
|
however some plugins might make use of higher verbosity.
|
||||||
|
|
||||||
.. _`pytest.detailed_failed_tests_usage`:
|
.. _`pytest.detailed_failed_tests_usage`:
|
||||||
|
|
||||||
Producing a detailed summary report
|
Producing a detailed summary report
|
||||||
|
@ -171,6 +408,8 @@ making it easy in large test suites to get a clear picture of all failures, skip
|
||||||
|
|
||||||
It defaults to ``fE`` to list failures and errors.
|
It defaults to ``fE`` to list failures and errors.
|
||||||
|
|
||||||
|
.. regendoc:wipe
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
|
@ -36,12 +36,23 @@ def _ellipsize(s: str, maxsize: int) -> str:
|
||||||
|
|
||||||
|
|
||||||
class SafeRepr(reprlib.Repr):
|
class SafeRepr(reprlib.Repr):
|
||||||
"""repr.Repr that limits the resulting size of repr() and includes
|
"""
|
||||||
information on exceptions raised during the call."""
|
repr.Repr that limits the resulting size of repr() and includes
|
||||||
|
information on exceptions raised during the call.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, maxsize: int) -> None:
|
def __init__(self, maxsize: Optional[int]) -> None:
|
||||||
|
"""
|
||||||
|
:param maxsize:
|
||||||
|
If not None, will truncate the resulting repr to that specific size, using ellipsis
|
||||||
|
somewhere in the middle to hide the extra text.
|
||||||
|
If None, will not impose any size limits on the returning repr.
|
||||||
|
"""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.maxstring = maxsize
|
# ``maxstring`` is used by the superclass, and needs to be an int; using a
|
||||||
|
# very large number in case maxsize is None, meaning we want to disable
|
||||||
|
# truncation.
|
||||||
|
self.maxstring = maxsize if maxsize is not None else 1_000_000_000
|
||||||
self.maxsize = maxsize
|
self.maxsize = maxsize
|
||||||
|
|
||||||
def repr(self, x: object) -> str:
|
def repr(self, x: object) -> str:
|
||||||
|
@ -51,7 +62,9 @@ class SafeRepr(reprlib.Repr):
|
||||||
raise
|
raise
|
||||||
except BaseException as exc:
|
except BaseException as exc:
|
||||||
s = _format_repr_exception(exc, x)
|
s = _format_repr_exception(exc, x)
|
||||||
return _ellipsize(s, self.maxsize)
|
if self.maxsize is not None:
|
||||||
|
s = _ellipsize(s, self.maxsize)
|
||||||
|
return s
|
||||||
|
|
||||||
def repr_instance(self, x: object, level: int) -> str:
|
def repr_instance(self, x: object, level: int) -> str:
|
||||||
try:
|
try:
|
||||||
|
@ -60,7 +73,9 @@ class SafeRepr(reprlib.Repr):
|
||||||
raise
|
raise
|
||||||
except BaseException as exc:
|
except BaseException as exc:
|
||||||
s = _format_repr_exception(exc, x)
|
s = _format_repr_exception(exc, x)
|
||||||
return _ellipsize(s, self.maxsize)
|
if self.maxsize is not None:
|
||||||
|
s = _ellipsize(s, self.maxsize)
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
def safeformat(obj: object) -> str:
|
def safeformat(obj: object) -> str:
|
||||||
|
@ -75,7 +90,11 @@ def safeformat(obj: object) -> str:
|
||||||
return _format_repr_exception(exc, obj)
|
return _format_repr_exception(exc, obj)
|
||||||
|
|
||||||
|
|
||||||
def saferepr(obj: object, maxsize: int = 240) -> str:
|
# Maximum size of overall repr of objects to display during assertion errors.
|
||||||
|
DEFAULT_REPR_MAX_SIZE = 240
|
||||||
|
|
||||||
|
|
||||||
|
def saferepr(obj: object, maxsize: Optional[int] = DEFAULT_REPR_MAX_SIZE) -> str:
|
||||||
"""Return a size-limited safe repr-string for the given object.
|
"""Return a size-limited safe repr-string for the given object.
|
||||||
|
|
||||||
Failing __repr__ functions of user instances will be represented
|
Failing __repr__ functions of user instances will be represented
|
||||||
|
@ -83,7 +102,7 @@ def saferepr(obj: object, maxsize: int = 240) -> str:
|
||||||
care to never raise exceptions itself.
|
care to never raise exceptions itself.
|
||||||
|
|
||||||
This function is a wrapper around the Repr/reprlib functionality of the
|
This function is a wrapper around the Repr/reprlib functionality of the
|
||||||
standard 2.6 lib.
|
stdlib.
|
||||||
"""
|
"""
|
||||||
return SafeRepr(maxsize).repr(obj)
|
return SafeRepr(maxsize).repr(obj)
|
||||||
|
|
||||||
|
|
|
@ -153,6 +153,7 @@ def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]:
|
||||||
|
|
||||||
saved_assert_hooks = util._reprcompare, util._assertion_pass
|
saved_assert_hooks = util._reprcompare, util._assertion_pass
|
||||||
util._reprcompare = callbinrepr
|
util._reprcompare = callbinrepr
|
||||||
|
util._config = item.config
|
||||||
|
|
||||||
if ihook.pytest_assertion_pass.get_hookimpls():
|
if ihook.pytest_assertion_pass.get_hookimpls():
|
||||||
|
|
||||||
|
@ -164,6 +165,7 @@ def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]:
|
||||||
yield
|
yield
|
||||||
|
|
||||||
util._reprcompare, util._assertion_pass = saved_assert_hooks
|
util._reprcompare, util._assertion_pass = saved_assert_hooks
|
||||||
|
util._config = None
|
||||||
|
|
||||||
|
|
||||||
def pytest_sessionfinish(session: "Session") -> None:
|
def pytest_sessionfinish(session: "Session") -> None:
|
||||||
|
|
|
@ -27,6 +27,7 @@ from typing import Tuple
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
|
from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE
|
||||||
from _pytest._io.saferepr import saferepr
|
from _pytest._io.saferepr import saferepr
|
||||||
from _pytest._version import version
|
from _pytest._version import version
|
||||||
from _pytest.assertion import util
|
from _pytest.assertion import util
|
||||||
|
@ -427,7 +428,18 @@ def _saferepr(obj: object) -> str:
|
||||||
sequences, especially '\n{' and '\n}' are likely to be present in
|
sequences, especially '\n{' and '\n}' are likely to be present in
|
||||||
JSON reprs.
|
JSON reprs.
|
||||||
"""
|
"""
|
||||||
return saferepr(obj).replace("\n", "\\n")
|
maxsize = _get_maxsize_for_saferepr(util._config)
|
||||||
|
return saferepr(obj, maxsize=maxsize).replace("\n", "\\n")
|
||||||
|
|
||||||
|
|
||||||
|
def _get_maxsize_for_saferepr(config: Optional[Config]) -> Optional[int]:
|
||||||
|
"""Get `maxsize` configuration for saferepr based on the given config object."""
|
||||||
|
verbosity = config.getoption("verbose") if config is not None else 0
|
||||||
|
if verbosity >= 2:
|
||||||
|
return None
|
||||||
|
if verbosity >= 1:
|
||||||
|
return DEFAULT_REPR_MAX_SIZE * 10
|
||||||
|
return DEFAULT_REPR_MAX_SIZE
|
||||||
|
|
||||||
|
|
||||||
def _format_assertmsg(obj: object) -> str:
|
def _format_assertmsg(obj: object) -> str:
|
||||||
|
|
|
@ -15,6 +15,8 @@ from _pytest import outcomes
|
||||||
from _pytest._io.saferepr import _pformat_dispatch
|
from _pytest._io.saferepr import _pformat_dispatch
|
||||||
from _pytest._io.saferepr import safeformat
|
from _pytest._io.saferepr import safeformat
|
||||||
from _pytest._io.saferepr import saferepr
|
from _pytest._io.saferepr import saferepr
|
||||||
|
from _pytest.config import Config
|
||||||
|
|
||||||
|
|
||||||
# The _reprcompare attribute on the util module is used by the new assertion
|
# The _reprcompare attribute on the util module is used by the new assertion
|
||||||
# interpretation code and assertion rewriter to detect this plugin was
|
# interpretation code and assertion rewriter to detect this plugin was
|
||||||
|
@ -26,6 +28,9 @@ _reprcompare: Optional[Callable[[str, object, object], Optional[str]]] = None
|
||||||
# when pytest_runtest_setup is called.
|
# when pytest_runtest_setup is called.
|
||||||
_assertion_pass: Optional[Callable[[int, str, str], None]] = None
|
_assertion_pass: Optional[Callable[[int, str, str], None]] = None
|
||||||
|
|
||||||
|
# Config object which is assigned during pytest_runtest_protocol.
|
||||||
|
_config: Optional[Config] = None
|
||||||
|
|
||||||
|
|
||||||
def format_explanation(explanation: str) -> str:
|
def format_explanation(explanation: str) -> str:
|
||||||
r"""Format an explanation.
|
r"""Format an explanation.
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest._io.saferepr import _pformat_dispatch
|
from _pytest._io.saferepr import _pformat_dispatch
|
||||||
|
from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE
|
||||||
from _pytest._io.saferepr import saferepr
|
from _pytest._io.saferepr import saferepr
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,6 +16,13 @@ def test_maxsize():
|
||||||
assert s == expected
|
assert s == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_no_maxsize():
|
||||||
|
text = "x" * DEFAULT_REPR_MAX_SIZE * 10
|
||||||
|
s = saferepr(text, maxsize=None)
|
||||||
|
expected = repr(text)
|
||||||
|
assert s == expected
|
||||||
|
|
||||||
|
|
||||||
def test_maxsize_error_on_instance():
|
def test_maxsize_error_on_instance():
|
||||||
class A:
|
class A:
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
|
|
@ -11,6 +11,7 @@ import textwrap
|
||||||
import zipfile
|
import zipfile
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import cast
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import Mapping
|
from typing import Mapping
|
||||||
|
@ -19,13 +20,16 @@ from typing import Set
|
||||||
|
|
||||||
import _pytest._code
|
import _pytest._code
|
||||||
import pytest
|
import pytest
|
||||||
|
from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE
|
||||||
from _pytest.assertion import util
|
from _pytest.assertion import util
|
||||||
from _pytest.assertion.rewrite import _get_assertion_exprs
|
from _pytest.assertion.rewrite import _get_assertion_exprs
|
||||||
|
from _pytest.assertion.rewrite import _get_maxsize_for_saferepr
|
||||||
from _pytest.assertion.rewrite import AssertionRewritingHook
|
from _pytest.assertion.rewrite import AssertionRewritingHook
|
||||||
from _pytest.assertion.rewrite import get_cache_dir
|
from _pytest.assertion.rewrite import get_cache_dir
|
||||||
from _pytest.assertion.rewrite import PYC_TAIL
|
from _pytest.assertion.rewrite import PYC_TAIL
|
||||||
from _pytest.assertion.rewrite import PYTEST_TAG
|
from _pytest.assertion.rewrite import PYTEST_TAG
|
||||||
from _pytest.assertion.rewrite import rewrite_asserts
|
from _pytest.assertion.rewrite import rewrite_asserts
|
||||||
|
from _pytest.config import Config
|
||||||
from _pytest.config import ExitCode
|
from _pytest.config import ExitCode
|
||||||
from _pytest.pathlib import make_numbered_dir
|
from _pytest.pathlib import make_numbered_dir
|
||||||
from _pytest.pytester import Pytester
|
from _pytest.pytester import Pytester
|
||||||
|
@ -1706,3 +1710,52 @@ class TestPyCacheDir:
|
||||||
cache_tag=sys.implementation.cache_tag
|
cache_tag=sys.implementation.cache_tag
|
||||||
)
|
)
|
||||||
assert bar_init_pyc.is_file()
|
assert bar_init_pyc.is_file()
|
||||||
|
|
||||||
|
|
||||||
|
class TestReprSizeVerbosity:
|
||||||
|
"""
|
||||||
|
Check that verbosity also controls the string length threshold to shorten it using
|
||||||
|
ellipsis.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"verbose, expected_size",
|
||||||
|
[
|
||||||
|
(0, DEFAULT_REPR_MAX_SIZE),
|
||||||
|
(1, DEFAULT_REPR_MAX_SIZE * 10),
|
||||||
|
(2, None),
|
||||||
|
(3, None),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_get_maxsize_for_saferepr(self, verbose: int, expected_size) -> None:
|
||||||
|
class FakeConfig:
|
||||||
|
def getoption(self, name: str) -> int:
|
||||||
|
assert name == "verbose"
|
||||||
|
return verbose
|
||||||
|
|
||||||
|
config = FakeConfig()
|
||||||
|
assert _get_maxsize_for_saferepr(cast(Config, config)) == expected_size
|
||||||
|
|
||||||
|
def create_test_file(self, pytester: Pytester, size: int) -> None:
|
||||||
|
pytester.makepyfile(
|
||||||
|
f"""
|
||||||
|
def test_very_long_string():
|
||||||
|
text = "x" * {size}
|
||||||
|
assert "hello world" in text
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_default_verbosity(self, pytester: Pytester) -> None:
|
||||||
|
self.create_test_file(pytester, DEFAULT_REPR_MAX_SIZE)
|
||||||
|
result = pytester.runpytest()
|
||||||
|
result.stdout.fnmatch_lines(["*xxx...xxx*"])
|
||||||
|
|
||||||
|
def test_increased_verbosity(self, pytester: Pytester) -> None:
|
||||||
|
self.create_test_file(pytester, DEFAULT_REPR_MAX_SIZE)
|
||||||
|
result = pytester.runpytest("-v")
|
||||||
|
result.stdout.no_fnmatch_line("*xxx...xxx*")
|
||||||
|
|
||||||
|
def test_max_increased_verbosity(self, pytester: Pytester) -> None:
|
||||||
|
self.create_test_file(pytester, DEFAULT_REPR_MAX_SIZE * 10)
|
||||||
|
result = pytester.runpytest("-vv")
|
||||||
|
result.stdout.no_fnmatch_line("*xxx...xxx*")
|
||||||
|
|
Loading…
Reference in New Issue