2020-07-28 20:24:24 +08:00
|
|
|
|
import collections
|
2010-09-18 20:03:28 +08:00
|
|
|
|
import sys
|
2014-09-27 09:29:47 +08:00
|
|
|
|
import textwrap
|
2020-03-08 08:23:19 +08:00
|
|
|
|
from typing import Any
|
|
|
|
|
from typing import List
|
2020-10-03 10:57:16 +08:00
|
|
|
|
from typing import MutableSequence
|
2020-03-08 08:23:19 +08:00
|
|
|
|
from typing import Optional
|
2010-09-18 20:03:28 +08:00
|
|
|
|
|
2018-08-03 06:16:14 +08:00
|
|
|
|
import attr
|
2018-10-25 15:01:29 +08:00
|
|
|
|
|
|
|
|
|
import _pytest.assertion as plugin
|
|
|
|
|
import pytest
|
2019-03-22 19:44:32 +08:00
|
|
|
|
from _pytest import outcomes
|
2016-09-22 07:06:45 +08:00
|
|
|
|
from _pytest.assertion import truncate
|
2018-10-25 15:01:29 +08:00
|
|
|
|
from _pytest.assertion import util
|
2023-11-19 22:56:29 +08:00
|
|
|
|
from _pytest.config import Config as _Config
|
2021-08-20 00:27:07 +08:00
|
|
|
|
from _pytest.monkeypatch import MonkeyPatch
|
2020-10-30 09:39:44 +08:00
|
|
|
|
from _pytest.pytester import Pytester
|
2014-09-27 09:29:47 +08:00
|
|
|
|
|
2013-03-28 09:39:01 +08:00
|
|
|
|
|
2023-11-19 22:56:29 +08:00
|
|
|
|
def mock_config(verbose: int = 0, assertion_override: Optional[int] = None):
|
2023-10-24 19:42:21 +08:00
|
|
|
|
class TerminalWriter:
|
|
|
|
|
def _highlight(self, source, lexer):
|
|
|
|
|
return source
|
|
|
|
|
|
2019-06-03 06:32:00 +08:00
|
|
|
|
class Config:
|
2023-10-24 19:42:21 +08:00
|
|
|
|
def get_terminal_writer(self):
|
|
|
|
|
return TerminalWriter()
|
|
|
|
|
|
2023-11-19 22:56:29 +08:00
|
|
|
|
def get_verbosity(self, verbosity_type: Optional[str] = None) -> int:
|
|
|
|
|
if verbosity_type is None:
|
|
|
|
|
return verbose
|
|
|
|
|
if verbosity_type == _Config.VERBOSITY_ASSERTIONS:
|
|
|
|
|
if assertion_override is not None:
|
|
|
|
|
return assertion_override
|
|
|
|
|
return verbose
|
|
|
|
|
|
|
|
|
|
raise KeyError(f"Not mocked out: {verbosity_type}")
|
|
|
|
|
|
2013-03-28 09:39:01 +08:00
|
|
|
|
return Config()
|
|
|
|
|
|
|
|
|
|
|
2023-11-19 22:56:29 +08:00
|
|
|
|
class TestMockConfig:
|
|
|
|
|
SOME_VERBOSITY_LEVEL = 3
|
|
|
|
|
SOME_OTHER_VERBOSITY_LEVEL = 10
|
|
|
|
|
|
|
|
|
|
def test_verbose_exposes_value(self):
|
|
|
|
|
config = mock_config(verbose=TestMockConfig.SOME_VERBOSITY_LEVEL)
|
|
|
|
|
|
|
|
|
|
assert config.get_verbosity() == TestMockConfig.SOME_VERBOSITY_LEVEL
|
|
|
|
|
|
|
|
|
|
def test_get_assertion_override_not_set_verbose_value(self):
|
|
|
|
|
config = mock_config(verbose=TestMockConfig.SOME_VERBOSITY_LEVEL)
|
|
|
|
|
|
|
|
|
|
assert (
|
|
|
|
|
config.get_verbosity(_Config.VERBOSITY_ASSERTIONS)
|
|
|
|
|
== TestMockConfig.SOME_VERBOSITY_LEVEL
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_get_assertion_override_set_custom_value(self):
|
|
|
|
|
config = mock_config(
|
|
|
|
|
verbose=TestMockConfig.SOME_VERBOSITY_LEVEL,
|
|
|
|
|
assertion_override=TestMockConfig.SOME_OTHER_VERBOSITY_LEVEL,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert (
|
|
|
|
|
config.get_verbosity(_Config.VERBOSITY_ASSERTIONS)
|
|
|
|
|
== TestMockConfig.SOME_OTHER_VERBOSITY_LEVEL
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_get_unsupported_type_error(self):
|
|
|
|
|
config = mock_config(verbose=TestMockConfig.SOME_VERBOSITY_LEVEL)
|
|
|
|
|
|
|
|
|
|
with pytest.raises(KeyError):
|
|
|
|
|
config.get_verbosity("--- NOT A VERBOSITY LEVEL ---")
|
|
|
|
|
|
|
|
|
|
|
2019-06-03 06:32:00 +08:00
|
|
|
|
class TestImportHookInstallation:
|
2018-05-23 22:48:46 +08:00
|
|
|
|
@pytest.mark.parametrize("initial_conftest", [True, False])
|
|
|
|
|
@pytest.mark.parametrize("mode", ["plain", "rewrite"])
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_conftest_assertion_rewrite(
|
|
|
|
|
self, pytester: Pytester, initial_conftest, mode
|
|
|
|
|
) -> None:
|
2020-07-18 17:35:13 +08:00
|
|
|
|
"""Test that conftest files are using assertion rewrite on import (#1619)."""
|
2020-10-30 09:39:44 +08:00
|
|
|
|
pytester.mkdir("foo")
|
|
|
|
|
pytester.mkdir("foo/tests")
|
2018-05-23 22:48:46 +08:00
|
|
|
|
conftest_path = "conftest.py" if initial_conftest else "foo/conftest.py"
|
2016-06-22 18:42:11 +08:00
|
|
|
|
contents = {
|
|
|
|
|
conftest_path: """
|
|
|
|
|
import pytest
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def check_first():
|
|
|
|
|
def check(values, value):
|
|
|
|
|
assert values.pop(0) == value
|
|
|
|
|
return check
|
|
|
|
|
""",
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"foo/tests/test_foo.py": """
|
2016-06-22 18:42:11 +08:00
|
|
|
|
def test(check_first):
|
|
|
|
|
check_first([10, 30], 30)
|
2018-05-23 22:48:46 +08:00
|
|
|
|
""",
|
2016-06-22 18:42:11 +08:00
|
|
|
|
}
|
2020-10-30 09:39:44 +08:00
|
|
|
|
pytester.makepyfile(**contents)
|
|
|
|
|
result = pytester.runpytest_subprocess("--assert=%s" % mode)
|
2018-05-23 22:48:46 +08:00
|
|
|
|
if mode == "plain":
|
|
|
|
|
expected = "E AssertionError"
|
|
|
|
|
elif mode == "rewrite":
|
|
|
|
|
expected = "*assert 10 == 30*"
|
2016-06-22 18:42:11 +08:00
|
|
|
|
else:
|
|
|
|
|
assert 0
|
|
|
|
|
result.stdout.fnmatch_lines([expected])
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_rewrite_assertions_pytester_plugin(self, pytester: Pytester) -> None:
|
2017-01-20 07:33:51 +08:00
|
|
|
|
"""
|
|
|
|
|
Assertions in the pytester plugin must also benefit from assertion
|
|
|
|
|
rewriting (#1920).
|
|
|
|
|
"""
|
2020-10-30 09:39:44 +08:00
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2017-01-20 07:33:51 +08:00
|
|
|
|
pytest_plugins = ['pytester']
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_dummy_failure(pytester): # how meta!
|
|
|
|
|
pytester.makepyfile('def test(): assert 0')
|
|
|
|
|
r = pytester.inline_run()
|
2017-01-20 07:33:51 +08:00
|
|
|
|
r.assertoutcome(passed=1)
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest_subprocess()
|
pytester: Hookrecorder: improve assertoutcome
Before:
def assertoutcome(self, passed: int = 0, skipped: int = 0, failed: int = 0) -> None:
realpassed, realskipped, realfailed = self.listoutcomes()
assert passed == len(realpassed)
> assert skipped == len(realskipped)
E assert 1 == 0
E + where 0 = len([])
After:
> reprec = testdir.inline_run(testpath, "-s")
E AssertionError: ([], [], [<TestReport 'nodeid' when='call' outcome='failed'>])
E assert {'failed': 1, 'passed': 0, 'skipped': 0} == {'failed': 0, 'passed': 0, 'skipped': 1}
2019-11-13 09:13:35 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
2020-02-04 09:56:23 +08:00
|
|
|
|
"> r.assertoutcome(passed=1)",
|
|
|
|
|
"E AssertionError: ([[][]], [[][]], [[]<TestReport *>[]])*",
|
|
|
|
|
"E assert {'failed': 1,... 'skipped': 0} == {'failed': 0,... 'skipped': 0}",
|
|
|
|
|
"E Omitting 1 identical items, use -vv to show",
|
|
|
|
|
"E Differing items:",
|
2022-02-15 20:43:20 +08:00
|
|
|
|
"E Use -v to get more diff",
|
2020-02-04 09:56:23 +08:00
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
# XXX: unstable output.
|
|
|
|
|
result.stdout.fnmatch_lines_random(
|
|
|
|
|
[
|
|
|
|
|
"E {'failed': 1} != {'failed': 0}",
|
|
|
|
|
"E {'passed': 0} != {'passed': 1}",
|
pytester: Hookrecorder: improve assertoutcome
Before:
def assertoutcome(self, passed: int = 0, skipped: int = 0, failed: int = 0) -> None:
realpassed, realskipped, realfailed = self.listoutcomes()
assert passed == len(realpassed)
> assert skipped == len(realskipped)
E assert 1 == 0
E + where 0 = len([])
After:
> reprec = testdir.inline_run(testpath, "-s")
E AssertionError: ([], [], [<TestReport 'nodeid' when='call' outcome='failed'>])
E assert {'failed': 1, 'passed': 0, 'skipped': 0} == {'failed': 0, 'passed': 0, 'skipped': 1}
2019-11-13 09:13:35 +08:00
|
|
|
|
]
|
|
|
|
|
)
|
2017-01-20 07:33:51 +08:00
|
|
|
|
|
2018-05-23 22:48:46 +08:00
|
|
|
|
@pytest.mark.parametrize("mode", ["plain", "rewrite"])
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_pytest_plugins_rewrite(self, pytester: Pytester, mode) -> None:
|
2016-06-26 00:26:45 +08:00
|
|
|
|
contents = {
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"conftest.py": """
|
2016-06-26 00:26:45 +08:00
|
|
|
|
pytest_plugins = ['ham']
|
|
|
|
|
""",
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"ham.py": """
|
2016-06-26 00:26:45 +08:00
|
|
|
|
import pytest
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def check_first():
|
|
|
|
|
def check(values, value):
|
|
|
|
|
assert values.pop(0) == value
|
|
|
|
|
return check
|
|
|
|
|
""",
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"test_foo.py": """
|
2016-06-26 00:26:45 +08:00
|
|
|
|
def test_foo(check_first):
|
|
|
|
|
check_first([10, 30], 30)
|
|
|
|
|
""",
|
|
|
|
|
}
|
2020-10-30 09:39:44 +08:00
|
|
|
|
pytester.makepyfile(**contents)
|
|
|
|
|
result = pytester.runpytest_subprocess("--assert=%s" % mode)
|
2018-05-23 22:48:46 +08:00
|
|
|
|
if mode == "plain":
|
|
|
|
|
expected = "E AssertionError"
|
|
|
|
|
elif mode == "rewrite":
|
|
|
|
|
expected = "*assert 10 == 30*"
|
2016-06-26 00:26:45 +08:00
|
|
|
|
else:
|
|
|
|
|
assert 0
|
|
|
|
|
result.stdout.fnmatch_lines([expected])
|
|
|
|
|
|
2018-05-23 22:48:46 +08:00
|
|
|
|
@pytest.mark.parametrize("mode", ["str", "list"])
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_pytest_plugins_rewrite_module_names(
|
|
|
|
|
self, pytester: Pytester, mode
|
|
|
|
|
) -> None:
|
2016-08-31 09:15:53 +08:00
|
|
|
|
"""Test that pluginmanager correct marks pytest_plugins variables
|
|
|
|
|
for assertion rewriting if they are defined as plain strings or
|
|
|
|
|
list of strings (#1888).
|
|
|
|
|
"""
|
2018-05-23 22:48:46 +08:00
|
|
|
|
plugins = '"ham"' if mode == "str" else '["ham"]'
|
2016-08-31 09:15:53 +08:00
|
|
|
|
contents = {
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"conftest.py": """
|
2016-08-31 09:15:53 +08:00
|
|
|
|
pytest_plugins = {plugins}
|
2018-05-23 22:48:46 +08:00
|
|
|
|
""".format(
|
|
|
|
|
plugins=plugins
|
|
|
|
|
),
|
|
|
|
|
"ham.py": """
|
2016-08-31 09:15:53 +08:00
|
|
|
|
import pytest
|
|
|
|
|
""",
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"test_foo.py": """
|
2016-08-31 09:15:53 +08:00
|
|
|
|
def test_foo(pytestconfig):
|
|
|
|
|
assert 'ham' in pytestconfig.pluginmanager.rewrite_hook._must_rewrite
|
|
|
|
|
""",
|
|
|
|
|
}
|
2020-10-30 09:39:44 +08:00
|
|
|
|
pytester.makepyfile(**contents)
|
|
|
|
|
result = pytester.runpytest_subprocess("--assert=rewrite")
|
2016-08-31 09:15:53 +08:00
|
|
|
|
assert result.ret == 0
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_pytest_plugins_rewrite_module_names_correctly(
|
|
|
|
|
self, pytester: Pytester
|
|
|
|
|
) -> None:
|
2017-11-22 08:49:46 +08:00
|
|
|
|
"""Test that we match files correctly when they are marked for rewriting (#2939)."""
|
|
|
|
|
contents = {
|
2019-04-07 08:32:47 +08:00
|
|
|
|
"conftest.py": """\
|
2017-11-22 08:49:46 +08:00
|
|
|
|
pytest_plugins = "ham"
|
|
|
|
|
""",
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"ham.py": "",
|
|
|
|
|
"hamster.py": "",
|
2019-04-07 08:32:47 +08:00
|
|
|
|
"test_foo.py": """\
|
2017-11-22 08:49:46 +08:00
|
|
|
|
def test_foo(pytestconfig):
|
2019-06-23 06:02:32 +08:00
|
|
|
|
assert pytestconfig.pluginmanager.rewrite_hook.find_spec('ham') is not None
|
|
|
|
|
assert pytestconfig.pluginmanager.rewrite_hook.find_spec('hamster') is None
|
2017-11-22 08:49:46 +08:00
|
|
|
|
""",
|
|
|
|
|
}
|
2020-10-30 09:39:44 +08:00
|
|
|
|
pytester.makepyfile(**contents)
|
|
|
|
|
result = pytester.runpytest_subprocess("--assert=rewrite")
|
2017-11-22 08:49:46 +08:00
|
|
|
|
assert result.ret == 0
|
|
|
|
|
|
2018-05-23 22:48:46 +08:00
|
|
|
|
@pytest.mark.parametrize("mode", ["plain", "rewrite"])
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_installed_plugin_rewrite(
|
|
|
|
|
self, pytester: Pytester, mode, monkeypatch
|
|
|
|
|
) -> None:
|
2018-12-09 18:53:41 +08:00
|
|
|
|
monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", raising=False)
|
2016-06-22 18:42:11 +08:00
|
|
|
|
# Make sure the hook is installed early enough so that plugins
|
2017-11-27 03:46:06 +08:00
|
|
|
|
# installed via setuptools are rewritten.
|
2020-10-30 09:39:44 +08:00
|
|
|
|
pytester.mkdir("hampkg")
|
2016-06-26 00:26:45 +08:00
|
|
|
|
contents = {
|
2019-04-07 08:32:47 +08:00
|
|
|
|
"hampkg/__init__.py": """\
|
2016-06-26 00:26:45 +08:00
|
|
|
|
import pytest
|
2016-06-22 18:42:11 +08:00
|
|
|
|
|
2016-06-26 00:26:45 +08:00
|
|
|
|
@pytest.fixture
|
|
|
|
|
def check_first2():
|
|
|
|
|
def check(values, value):
|
|
|
|
|
assert values.pop(0) == value
|
|
|
|
|
return check
|
|
|
|
|
""",
|
2019-04-07 08:32:47 +08:00
|
|
|
|
"spamplugin.py": """\
|
2016-06-22 18:42:11 +08:00
|
|
|
|
import pytest
|
|
|
|
|
from hampkg import check_first2
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def check_first():
|
|
|
|
|
def check(values, value):
|
|
|
|
|
assert values.pop(0) == value
|
|
|
|
|
return check
|
|
|
|
|
""",
|
2019-04-07 08:32:47 +08:00
|
|
|
|
"mainwrapper.py": """\
|
2023-07-01 05:55:42 +08:00
|
|
|
|
import importlib.metadata
|
2019-07-03 07:46:44 +08:00
|
|
|
|
import pytest
|
2016-06-22 18:42:11 +08:00
|
|
|
|
|
2017-02-17 02:41:51 +08:00
|
|
|
|
class DummyEntryPoint(object):
|
2016-06-22 18:42:11 +08:00
|
|
|
|
name = 'spam'
|
|
|
|
|
module_name = 'spam.py'
|
2019-04-07 08:32:47 +08:00
|
|
|
|
group = 'pytest11'
|
2016-06-22 18:42:11 +08:00
|
|
|
|
|
2019-04-07 08:32:47 +08:00
|
|
|
|
def load(self):
|
2016-06-22 18:42:11 +08:00
|
|
|
|
import spamplugin
|
|
|
|
|
return spamplugin
|
|
|
|
|
|
2019-04-07 08:32:47 +08:00
|
|
|
|
class DummyDistInfo(object):
|
|
|
|
|
version = '1.0'
|
|
|
|
|
files = ('spamplugin.py', 'hampkg/__init__.py')
|
|
|
|
|
entry_points = (DummyEntryPoint(),)
|
|
|
|
|
metadata = {'name': 'foo'}
|
2016-06-22 18:42:11 +08:00
|
|
|
|
|
2019-04-07 08:32:47 +08:00
|
|
|
|
def distributions():
|
|
|
|
|
return (DummyDistInfo(),)
|
|
|
|
|
|
2023-07-01 05:55:42 +08:00
|
|
|
|
importlib.metadata.distributions = distributions
|
2016-06-22 18:42:11 +08:00
|
|
|
|
pytest.main()
|
2019-04-07 08:32:47 +08:00
|
|
|
|
""",
|
|
|
|
|
"test_foo.py": """\
|
2016-06-22 18:42:11 +08:00
|
|
|
|
def test(check_first):
|
|
|
|
|
check_first([10, 30], 30)
|
|
|
|
|
|
|
|
|
|
def test2(check_first2):
|
|
|
|
|
check_first([10, 30], 30)
|
|
|
|
|
""",
|
2016-06-26 00:26:45 +08:00
|
|
|
|
}
|
2020-10-30 09:39:44 +08:00
|
|
|
|
pytester.makepyfile(**contents)
|
|
|
|
|
result = pytester.run(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
sys.executable, "mainwrapper.py", "-s", "--assert=%s" % mode
|
|
|
|
|
)
|
|
|
|
|
if mode == "plain":
|
|
|
|
|
expected = "E AssertionError"
|
|
|
|
|
elif mode == "rewrite":
|
|
|
|
|
expected = "*assert 10 == 30*"
|
2016-06-22 18:42:11 +08:00
|
|
|
|
else:
|
|
|
|
|
assert 0
|
|
|
|
|
result.stdout.fnmatch_lines([expected])
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_rewrite_ast(self, pytester: Pytester) -> None:
|
|
|
|
|
pytester.mkdir("pkg")
|
2016-06-26 00:26:45 +08:00
|
|
|
|
contents = {
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"pkg/__init__.py": """
|
2016-06-26 00:26:45 +08:00
|
|
|
|
import pytest
|
|
|
|
|
pytest.register_assert_rewrite('pkg.helper')
|
|
|
|
|
""",
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"pkg/helper.py": """
|
2016-06-26 00:26:45 +08:00
|
|
|
|
def tool():
|
|
|
|
|
a, b = 2, 3
|
|
|
|
|
assert a == b
|
|
|
|
|
""",
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"pkg/plugin.py": """
|
2016-06-26 00:26:45 +08:00
|
|
|
|
import pytest, pkg.helper
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def tool():
|
|
|
|
|
return pkg.helper.tool
|
|
|
|
|
""",
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"pkg/other.py": """
|
2017-11-04 23:17:20 +08:00
|
|
|
|
values = [3, 2]
|
2016-06-26 00:26:45 +08:00
|
|
|
|
def tool():
|
2017-11-04 23:17:20 +08:00
|
|
|
|
assert values.pop() == 3
|
2016-06-26 00:26:45 +08:00
|
|
|
|
""",
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"conftest.py": """
|
2016-06-26 00:26:45 +08:00
|
|
|
|
pytest_plugins = ['pkg.plugin']
|
|
|
|
|
""",
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"test_pkg.py": """
|
2016-06-26 00:26:45 +08:00
|
|
|
|
import pkg.other
|
|
|
|
|
def test_tool(tool):
|
|
|
|
|
tool()
|
|
|
|
|
def test_other():
|
|
|
|
|
pkg.other.tool()
|
|
|
|
|
""",
|
|
|
|
|
}
|
2020-10-30 09:39:44 +08:00
|
|
|
|
pytester.makepyfile(**contents)
|
|
|
|
|
result = pytester.runpytest_subprocess("--assert=rewrite")
|
2018-05-23 22:48:46 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
|
|
|
|
">*assert a == b*",
|
|
|
|
|
"E*assert 2 == 3*",
|
|
|
|
|
">*assert values.pop() == 3*",
|
|
|
|
|
"E*AssertionError",
|
|
|
|
|
]
|
|
|
|
|
)
|
2016-06-26 00:26:45 +08:00
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_register_assert_rewrite_checks_types(self) -> None:
|
2016-08-31 09:15:53 +08:00
|
|
|
|
with pytest.raises(TypeError):
|
2020-05-01 19:40:17 +08:00
|
|
|
|
pytest.register_assert_rewrite(["pytest_tests_internal_non_existing"]) # type: ignore
|
2018-05-23 22:48:46 +08:00
|
|
|
|
pytest.register_assert_rewrite(
|
|
|
|
|
"pytest_tests_internal_non_existing", "pytest_tests_internal_non_existing2"
|
|
|
|
|
)
|
2016-08-31 09:15:53 +08:00
|
|
|
|
|
2016-06-22 18:42:11 +08:00
|
|
|
|
|
2019-06-03 06:32:00 +08:00
|
|
|
|
class TestBinReprIntegration:
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_pytest_assertrepr_compare_called(self, pytester: Pytester) -> None:
|
|
|
|
|
pytester.makeconftest(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2016-07-12 09:03:53 +08:00
|
|
|
|
import pytest
|
2017-11-04 23:17:20 +08:00
|
|
|
|
values = []
|
2012-06-25 23:35:33 +08:00
|
|
|
|
def pytest_assertrepr_compare(op, left, right):
|
2017-11-04 23:17:20 +08:00
|
|
|
|
values.append((op, left, right))
|
2016-07-12 09:03:53 +08:00
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def list(request):
|
2017-11-04 23:17:20 +08:00
|
|
|
|
return values
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2012-06-25 23:35:33 +08:00
|
|
|
|
def test_hello():
|
|
|
|
|
assert 0 == 1
|
2016-07-12 09:03:53 +08:00
|
|
|
|
def test_check(list):
|
|
|
|
|
assert list == [("==", 0, 1)]
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest("-v")
|
2018-05-23 22:48:46 +08:00
|
|
|
|
result.stdout.fnmatch_lines(["*test_hello*FAIL*", "*test_check*PASS*"])
|
2010-10-03 00:47:39 +08:00
|
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
|
2020-03-08 08:23:19 +08:00
|
|
|
|
def callop(op: str, left: Any, right: Any, verbose: int = 0) -> Optional[List[str]]:
|
2019-10-11 10:18:19 +08:00
|
|
|
|
config = mock_config(verbose=verbose)
|
2020-03-08 08:23:19 +08:00
|
|
|
|
return plugin.pytest_assertrepr_compare(config, op, left, right)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def callequal(left: Any, right: Any, verbose: int = 0) -> Optional[List[str]]:
|
|
|
|
|
return callop("==", left, right, verbose)
|
2013-03-28 09:39:01 +08:00
|
|
|
|
|
2010-10-05 00:49:30 +08:00
|
|
|
|
|
2019-06-03 06:32:00 +08:00
|
|
|
|
class TestAssert_reprcompare:
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_different_types(self) -> None:
|
2018-05-23 22:48:46 +08:00
|
|
|
|
assert callequal([0, 1], "foo") is None
|
2010-10-03 00:47:39 +08:00
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_summary(self) -> None:
|
|
|
|
|
lines = callequal([0, 1], [0, 2])
|
|
|
|
|
assert lines is not None
|
|
|
|
|
summary = lines[0]
|
2010-10-03 00:47:39 +08:00
|
|
|
|
assert len(summary) < 65
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_text_diff(self) -> None:
|
2020-02-24 22:19:08 +08:00
|
|
|
|
assert callequal("spam", "eggs") == [
|
|
|
|
|
"'spam' == 'eggs'",
|
|
|
|
|
"- eggs",
|
|
|
|
|
"+ spam",
|
|
|
|
|
]
|
2010-10-03 00:47:39 +08:00
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_text_skipping(self) -> None:
|
2018-05-23 22:48:46 +08:00
|
|
|
|
lines = callequal("a" * 50 + "spam", "a" * 50 + "eggs")
|
2020-05-01 19:40:17 +08:00
|
|
|
|
assert lines is not None
|
2018-05-23 22:48:46 +08:00
|
|
|
|
assert "Skipping" in lines[1]
|
2013-03-28 09:39:01 +08:00
|
|
|
|
for line in lines:
|
2018-05-23 22:48:46 +08:00
|
|
|
|
assert "a" * 50 not in line
|
2013-03-28 09:39:01 +08:00
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_text_skipping_verbose(self) -> None:
|
2019-10-11 10:18:19 +08:00
|
|
|
|
lines = callequal("a" * 50 + "spam", "a" * 50 + "eggs", verbose=1)
|
2020-05-01 19:40:17 +08:00
|
|
|
|
assert lines is not None
|
2020-02-04 21:38:18 +08:00
|
|
|
|
assert "- " + "a" * 50 + "eggs" in lines
|
|
|
|
|
assert "+ " + "a" * 50 + "spam" in lines
|
2013-03-28 09:39:01 +08:00
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_multiline_text_diff(self) -> None:
|
2018-05-23 22:48:46 +08:00
|
|
|
|
left = "foo\nspam\nbar"
|
|
|
|
|
right = "foo\neggs\nbar"
|
2010-10-05 00:49:30 +08:00
|
|
|
|
diff = callequal(left, right)
|
2020-05-01 19:40:17 +08:00
|
|
|
|
assert diff is not None
|
2020-02-04 21:38:18 +08:00
|
|
|
|
assert "- eggs" in diff
|
|
|
|
|
assert "+ spam" in diff
|
2010-09-09 05:21:52 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_bytes_diff_normal(self) -> None:
|
2019-06-26 07:30:18 +08:00
|
|
|
|
"""Check special handling for bytes diff (#5260)"""
|
2019-05-15 18:03:00 +08:00
|
|
|
|
diff = callequal(b"spam", b"eggs")
|
2019-06-26 07:30:18 +08:00
|
|
|
|
|
|
|
|
|
assert diff == [
|
|
|
|
|
"b'spam' == b'eggs'",
|
|
|
|
|
"At index 0 diff: b's' != b'e'",
|
2022-02-15 20:43:20 +08:00
|
|
|
|
"Use -v to get more diff",
|
2019-06-26 07:30:18 +08:00
|
|
|
|
]
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_bytes_diff_verbose(self) -> None:
|
2019-06-26 07:30:18 +08:00
|
|
|
|
"""Check special handling for bytes diff (#5260)"""
|
2019-10-11 10:18:19 +08:00
|
|
|
|
diff = callequal(b"spam", b"eggs", verbose=1)
|
2019-06-26 07:30:18 +08:00
|
|
|
|
assert diff == [
|
|
|
|
|
"b'spam' == b'eggs'",
|
|
|
|
|
"At index 0 diff: b's' != b'e'",
|
|
|
|
|
"Full diff:",
|
2020-02-04 21:38:18 +08:00
|
|
|
|
"- b'eggs'",
|
|
|
|
|
"+ b'spam'",
|
2019-06-26 07:30:18 +08:00
|
|
|
|
]
|
2019-05-15 18:03:00 +08:00
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_list(self) -> None:
|
2010-10-05 00:49:30 +08:00
|
|
|
|
expl = callequal([0, 1], [0, 2])
|
2020-05-01 19:40:17 +08:00
|
|
|
|
assert expl is not None
|
2010-10-03 00:47:39 +08:00
|
|
|
|
assert len(expl) > 1
|
2010-09-18 20:03:28 +08:00
|
|
|
|
|
2014-09-27 09:29:47 +08:00
|
|
|
|
@pytest.mark.parametrize(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
["left", "right", "expected"],
|
|
|
|
|
[
|
2019-11-07 19:23:39 +08:00
|
|
|
|
pytest.param(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
[0, 1],
|
|
|
|
|
[0, 2],
|
|
|
|
|
"""
|
2014-09-27 09:29:47 +08:00
|
|
|
|
Full diff:
|
2023-11-27 22:47:18 +08:00
|
|
|
|
[
|
|
|
|
|
0,
|
|
|
|
|
- 2,
|
2014-09-27 09:29:47 +08:00
|
|
|
|
? ^
|
2023-11-27 22:47:18 +08:00
|
|
|
|
+ 1,
|
2014-09-27 09:29:47 +08:00
|
|
|
|
? ^
|
2023-11-27 22:47:18 +08:00
|
|
|
|
]
|
|
|
|
|
""",
|
2019-11-07 19:23:39 +08:00
|
|
|
|
id="lists",
|
2018-05-23 22:48:46 +08:00
|
|
|
|
),
|
2019-11-07 19:23:39 +08:00
|
|
|
|
pytest.param(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
{0: 1},
|
|
|
|
|
{0: 2},
|
|
|
|
|
"""
|
2014-09-27 09:29:47 +08:00
|
|
|
|
Full diff:
|
2023-11-27 22:47:18 +08:00
|
|
|
|
{
|
|
|
|
|
- 0: 2,
|
|
|
|
|
? ^
|
|
|
|
|
+ 0: 1,
|
|
|
|
|
? ^
|
|
|
|
|
}
|
2018-05-23 22:48:46 +08:00
|
|
|
|
""",
|
2019-11-07 19:23:39 +08:00
|
|
|
|
id="dicts",
|
2018-05-23 22:48:46 +08:00
|
|
|
|
),
|
2019-11-07 19:23:39 +08:00
|
|
|
|
pytest.param(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
{0, 1},
|
|
|
|
|
{0, 2},
|
|
|
|
|
"""
|
2014-09-27 09:29:47 +08:00
|
|
|
|
Full diff:
|
2023-11-27 22:47:18 +08:00
|
|
|
|
{
|
|
|
|
|
0,
|
|
|
|
|
- 2,
|
2014-09-27 09:29:47 +08:00
|
|
|
|
? ^
|
2023-11-27 22:47:18 +08:00
|
|
|
|
+ 1,
|
2014-09-27 09:29:47 +08:00
|
|
|
|
? ^
|
2023-11-27 22:47:18 +08:00
|
|
|
|
}
|
2018-05-23 22:48:46 +08:00
|
|
|
|
""",
|
2019-11-07 19:23:39 +08:00
|
|
|
|
id="sets",
|
2018-05-23 22:48:46 +08:00
|
|
|
|
),
|
|
|
|
|
],
|
2014-09-27 09:29:47 +08:00
|
|
|
|
)
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_iterable_full_diff(self, left, right, expected) -> None:
|
2014-09-27 09:29:47 +08:00
|
|
|
|
"""Test the full diff assertion failure explanation.
|
|
|
|
|
|
|
|
|
|
When verbose is False, then just a -v notice to get the diff is rendered,
|
|
|
|
|
when verbose is True, then ndiff of the pprint is returned.
|
|
|
|
|
"""
|
2019-10-11 10:18:19 +08:00
|
|
|
|
expl = callequal(left, right, verbose=0)
|
2020-05-01 19:40:17 +08:00
|
|
|
|
assert expl is not None
|
2022-02-15 20:43:20 +08:00
|
|
|
|
assert expl[-1] == "Use -v to get more diff"
|
2020-05-01 19:40:17 +08:00
|
|
|
|
verbose_expl = callequal(left, right, verbose=1)
|
|
|
|
|
assert verbose_expl is not None
|
|
|
|
|
assert "\n".join(verbose_expl).endswith(textwrap.dedent(expected).strip())
|
2014-09-27 09:29:47 +08:00
|
|
|
|
|
2022-02-15 20:43:20 +08:00
|
|
|
|
def test_iterable_quiet(self) -> None:
|
|
|
|
|
expl = callequal([1, 2], [10, 2], verbose=-1)
|
|
|
|
|
assert expl == [
|
|
|
|
|
"[1, 2] == [10, 2]",
|
|
|
|
|
"At index 0 diff: 1 != 10",
|
|
|
|
|
"Use -v to get more diff",
|
|
|
|
|
]
|
|
|
|
|
|
2021-08-20 00:27:07 +08:00
|
|
|
|
def test_iterable_full_diff_ci(
|
|
|
|
|
self, monkeypatch: MonkeyPatch, pytester: Pytester
|
|
|
|
|
) -> None:
|
|
|
|
|
pytester.makepyfile(
|
|
|
|
|
r"""
|
|
|
|
|
def test_full_diff():
|
|
|
|
|
left = [0, 1]
|
|
|
|
|
right = [0, 2]
|
|
|
|
|
assert left == right
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
monkeypatch.setenv("CI", "true")
|
|
|
|
|
result = pytester.runpytest()
|
|
|
|
|
result.stdout.fnmatch_lines(["E Full diff:"])
|
|
|
|
|
|
|
|
|
|
monkeypatch.delenv("CI", raising=False)
|
|
|
|
|
result = pytester.runpytest()
|
2022-02-15 20:43:20 +08:00
|
|
|
|
result.stdout.fnmatch_lines(["E Use -v to get more diff"])
|
2021-08-20 00:27:07 +08:00
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_list_different_lengths(self) -> None:
|
2010-10-05 00:49:30 +08:00
|
|
|
|
expl = callequal([0, 1], [0, 1, 2])
|
2020-05-01 19:40:17 +08:00
|
|
|
|
assert expl is not None
|
2010-10-03 00:47:39 +08:00
|
|
|
|
assert len(expl) > 1
|
2010-10-05 00:49:30 +08:00
|
|
|
|
expl = callequal([0, 1, 2], [0, 1])
|
2020-05-01 19:40:17 +08:00
|
|
|
|
assert expl is not None
|
2010-10-03 00:47:39 +08:00
|
|
|
|
assert len(expl) > 1
|
2010-09-18 20:03:28 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_list_wrap_for_multiple_lines(self) -> None:
|
Improve full diff output for lists
Massage text input for difflib when comparing pformat output of
different line lengths.
Also do not strip ndiff output on the left, which currently already
removes indenting for lines with no differences.
Before:
E AssertionError: assert ['version', '...version_info'] == ['version', '...version', ...]
E Right contains 3 more items, first extra item: ' '
E Full diff:
E - ['version', 'version_info', 'sys.version', 'sys.version_info']
E + ['version',
E + 'version_info',
E + 'sys.version',
E + 'sys.version_info',
E + ' ',
E + 'sys.version',
E + 'sys.version_info']
After:
E AssertionError: assert ['version', '...version_info'] == ['version', '...version', ...]
E Right contains 3 more items, first extra item: ' '
E Full diff:
E [
E 'version',
E 'version_info',
E 'sys.version',
E 'sys.version_info',
E + ' ',
E + 'sys.version',
E + 'sys.version_info',
E ]
2019-10-07 08:25:18 +08:00
|
|
|
|
long_d = "d" * 80
|
|
|
|
|
l1 = ["a", "b", "c"]
|
|
|
|
|
l2 = ["a", "b", "c", long_d]
|
|
|
|
|
diff = callequal(l1, l2, verbose=True)
|
|
|
|
|
assert diff == [
|
|
|
|
|
"['a', 'b', 'c'] == ['a', 'b', 'c...dddddddddddd']",
|
|
|
|
|
"Right contains one more item: '" + long_d + "'",
|
|
|
|
|
"Full diff:",
|
|
|
|
|
" [",
|
2023-11-27 22:47:18 +08:00
|
|
|
|
" 'a',",
|
|
|
|
|
" 'b',",
|
|
|
|
|
" 'c',",
|
|
|
|
|
"- '" + long_d + "',",
|
Improve full diff output for lists
Massage text input for difflib when comparing pformat output of
different line lengths.
Also do not strip ndiff output on the left, which currently already
removes indenting for lines with no differences.
Before:
E AssertionError: assert ['version', '...version_info'] == ['version', '...version', ...]
E Right contains 3 more items, first extra item: ' '
E Full diff:
E - ['version', 'version_info', 'sys.version', 'sys.version_info']
E + ['version',
E + 'version_info',
E + 'sys.version',
E + 'sys.version_info',
E + ' ',
E + 'sys.version',
E + 'sys.version_info']
After:
E AssertionError: assert ['version', '...version_info'] == ['version', '...version', ...]
E Right contains 3 more items, first extra item: ' '
E Full diff:
E [
E 'version',
E 'version_info',
E 'sys.version',
E 'sys.version_info',
E + ' ',
E + 'sys.version',
E + 'sys.version_info',
E ]
2019-10-07 08:25:18 +08:00
|
|
|
|
" ]",
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
diff = callequal(l2, l1, verbose=True)
|
|
|
|
|
assert diff == [
|
|
|
|
|
"['a', 'b', 'c...dddddddddddd'] == ['a', 'b', 'c']",
|
|
|
|
|
"Left contains one more item: '" + long_d + "'",
|
|
|
|
|
"Full diff:",
|
|
|
|
|
" [",
|
2023-11-27 22:47:18 +08:00
|
|
|
|
" 'a',",
|
|
|
|
|
" 'b',",
|
|
|
|
|
" 'c',",
|
|
|
|
|
"+ '" + long_d + "',",
|
Improve full diff output for lists
Massage text input for difflib when comparing pformat output of
different line lengths.
Also do not strip ndiff output on the left, which currently already
removes indenting for lines with no differences.
Before:
E AssertionError: assert ['version', '...version_info'] == ['version', '...version', ...]
E Right contains 3 more items, first extra item: ' '
E Full diff:
E - ['version', 'version_info', 'sys.version', 'sys.version_info']
E + ['version',
E + 'version_info',
E + 'sys.version',
E + 'sys.version_info',
E + ' ',
E + 'sys.version',
E + 'sys.version_info']
After:
E AssertionError: assert ['version', '...version_info'] == ['version', '...version', ...]
E Right contains 3 more items, first extra item: ' '
E Full diff:
E [
E 'version',
E 'version_info',
E 'sys.version',
E 'sys.version_info',
E + ' ',
E + 'sys.version',
E + 'sys.version_info',
E ]
2019-10-07 08:25:18 +08:00
|
|
|
|
" ]",
|
|
|
|
|
]
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_list_wrap_for_width_rewrap_same_length(self) -> None:
|
Improve full diff output for lists
Massage text input for difflib when comparing pformat output of
different line lengths.
Also do not strip ndiff output on the left, which currently already
removes indenting for lines with no differences.
Before:
E AssertionError: assert ['version', '...version_info'] == ['version', '...version', ...]
E Right contains 3 more items, first extra item: ' '
E Full diff:
E - ['version', 'version_info', 'sys.version', 'sys.version_info']
E + ['version',
E + 'version_info',
E + 'sys.version',
E + 'sys.version_info',
E + ' ',
E + 'sys.version',
E + 'sys.version_info']
After:
E AssertionError: assert ['version', '...version_info'] == ['version', '...version', ...]
E Right contains 3 more items, first extra item: ' '
E Full diff:
E [
E 'version',
E 'version_info',
E 'sys.version',
E 'sys.version_info',
E + ' ',
E + 'sys.version',
E + 'sys.version_info',
E ]
2019-10-07 08:25:18 +08:00
|
|
|
|
long_a = "a" * 30
|
|
|
|
|
long_b = "b" * 30
|
|
|
|
|
long_c = "c" * 30
|
|
|
|
|
l1 = [long_a, long_b, long_c]
|
|
|
|
|
l2 = [long_b, long_c, long_a]
|
|
|
|
|
diff = callequal(l1, l2, verbose=True)
|
|
|
|
|
assert diff == [
|
|
|
|
|
"['aaaaaaaaaaa...cccccccccccc'] == ['bbbbbbbbbbb...aaaaaaaaaaaa']",
|
|
|
|
|
"At index 0 diff: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' != 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'",
|
|
|
|
|
"Full diff:",
|
|
|
|
|
" [",
|
2023-11-27 22:47:18 +08:00
|
|
|
|
"+ 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',",
|
|
|
|
|
" 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',",
|
|
|
|
|
" 'cccccccccccccccccccccccccccccc',",
|
|
|
|
|
"- 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',",
|
Improve full diff output for lists
Massage text input for difflib when comparing pformat output of
different line lengths.
Also do not strip ndiff output on the left, which currently already
removes indenting for lines with no differences.
Before:
E AssertionError: assert ['version', '...version_info'] == ['version', '...version', ...]
E Right contains 3 more items, first extra item: ' '
E Full diff:
E - ['version', 'version_info', 'sys.version', 'sys.version_info']
E + ['version',
E + 'version_info',
E + 'sys.version',
E + 'sys.version_info',
E + ' ',
E + 'sys.version',
E + 'sys.version_info']
After:
E AssertionError: assert ['version', '...version_info'] == ['version', '...version', ...]
E Right contains 3 more items, first extra item: ' '
E Full diff:
E [
E 'version',
E 'version_info',
E 'sys.version',
E 'sys.version_info',
E + ' ',
E + 'sys.version',
E + 'sys.version_info',
E ]
2019-10-07 08:25:18 +08:00
|
|
|
|
" ]",
|
|
|
|
|
]
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_list_dont_wrap_strings(self) -> None:
|
2019-11-08 11:04:23 +08:00
|
|
|
|
long_a = "a" * 10
|
|
|
|
|
l1 = ["a"] + [long_a for _ in range(0, 7)]
|
|
|
|
|
l2 = ["should not get wrapped"]
|
|
|
|
|
diff = callequal(l1, l2, verbose=True)
|
|
|
|
|
assert diff == [
|
|
|
|
|
"['a', 'aaaaaa...aaaaaaa', ...] == ['should not get wrapped']",
|
|
|
|
|
"At index 0 diff: 'a' != 'should not get wrapped'",
|
|
|
|
|
"Left contains 7 more items, first extra item: 'aaaaaaaaaa'",
|
|
|
|
|
"Full diff:",
|
|
|
|
|
" [",
|
2023-11-27 22:47:18 +08:00
|
|
|
|
"- 'should not get wrapped',",
|
|
|
|
|
"+ 'a',",
|
|
|
|
|
"+ 'aaaaaaaaaa',",
|
|
|
|
|
"+ 'aaaaaaaaaa',",
|
|
|
|
|
"+ 'aaaaaaaaaa',",
|
|
|
|
|
"+ 'aaaaaaaaaa',",
|
|
|
|
|
"+ 'aaaaaaaaaa',",
|
|
|
|
|
"+ 'aaaaaaaaaa',",
|
|
|
|
|
"+ 'aaaaaaaaaa',",
|
2019-11-08 11:04:23 +08:00
|
|
|
|
" ]",
|
|
|
|
|
]
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_dict_wrap(self) -> None:
|
2020-02-04 21:38:18 +08:00
|
|
|
|
d1 = {"common": 1, "env": {"env1": 1, "env2": 2}}
|
|
|
|
|
d2 = {"common": 1, "env": {"env1": 1}}
|
2019-11-06 18:18:20 +08:00
|
|
|
|
|
|
|
|
|
diff = callequal(d1, d2, verbose=True)
|
|
|
|
|
assert diff == [
|
2020-02-04 21:38:18 +08:00
|
|
|
|
"{'common': 1,...1, 'env2': 2}} == {'common': 1,...: {'env1': 1}}",
|
2019-11-06 18:18:20 +08:00
|
|
|
|
"Omitting 1 identical items, use -vv to show",
|
|
|
|
|
"Differing items:",
|
2020-02-04 21:38:18 +08:00
|
|
|
|
"{'env': {'env1': 1, 'env2': 2}} != {'env': {'env1': 1}}",
|
2019-11-06 18:18:20 +08:00
|
|
|
|
"Full diff:",
|
2023-11-27 22:47:18 +08:00
|
|
|
|
" {",
|
|
|
|
|
" 'common': 1,",
|
|
|
|
|
" 'env': {",
|
|
|
|
|
" 'env1': 1,",
|
|
|
|
|
"+ 'env2': 2,",
|
|
|
|
|
" },",
|
|
|
|
|
" }",
|
2019-11-06 18:18:20 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
long_a = "a" * 80
|
2023-11-27 22:47:18 +08:00
|
|
|
|
sub = {"long_a": long_a, "sub1": {"long_a": "substring that gets wrapped " * 3}}
|
2019-11-06 18:18:20 +08:00
|
|
|
|
d1 = {"env": {"sub": sub}}
|
|
|
|
|
d2 = {"env": {"sub": sub}, "new": 1}
|
|
|
|
|
diff = callequal(d1, d2, verbose=True)
|
|
|
|
|
assert diff == [
|
2019-11-08 11:04:23 +08:00
|
|
|
|
"{'env': {'sub... wrapped '}}}} == {'env': {'sub...}}}, 'new': 1}",
|
2019-11-06 18:18:20 +08:00
|
|
|
|
"Omitting 1 identical items, use -vv to show",
|
|
|
|
|
"Right contains 1 more item:",
|
|
|
|
|
"{'new': 1}",
|
|
|
|
|
"Full diff:",
|
|
|
|
|
" {",
|
2023-11-27 22:47:18 +08:00
|
|
|
|
" 'env': {",
|
|
|
|
|
" 'sub': {",
|
|
|
|
|
f" 'long_a': '{long_a}',",
|
|
|
|
|
" 'sub1': {",
|
|
|
|
|
" 'long_a': 'substring that gets wrapped substring that gets wrapped '",
|
|
|
|
|
" 'substring that gets wrapped ',",
|
|
|
|
|
" },",
|
|
|
|
|
" },",
|
|
|
|
|
" },",
|
|
|
|
|
"- 'new': 1,",
|
2019-11-06 18:18:20 +08:00
|
|
|
|
" }",
|
|
|
|
|
]
|
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_dict(self) -> None:
|
2018-05-23 22:48:46 +08:00
|
|
|
|
expl = callequal({"a": 0}, {"a": 1})
|
2020-05-01 19:40:17 +08:00
|
|
|
|
assert expl is not None
|
2010-10-03 00:47:39 +08:00
|
|
|
|
assert len(expl) > 1
|
2010-09-18 20:03:28 +08:00
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_dict_omitting(self) -> None:
|
2018-05-23 22:48:46 +08:00
|
|
|
|
lines = callequal({"a": 0, "b": 1}, {"a": 1, "b": 1})
|
2020-05-01 19:40:17 +08:00
|
|
|
|
assert lines is not None
|
2018-05-23 22:48:46 +08:00
|
|
|
|
assert lines[1].startswith("Omitting 1 identical item")
|
|
|
|
|
assert "Common items" not in lines
|
2013-08-01 20:48:34 +08:00
|
|
|
|
for line in lines[1:]:
|
2018-05-23 22:48:46 +08:00
|
|
|
|
assert "b" not in line
|
2013-08-01 20:48:34 +08:00
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_dict_omitting_with_verbosity_1(self) -> None:
|
2020-07-18 17:35:13 +08:00
|
|
|
|
"""Ensure differing items are visible for verbosity=1 (#1512)."""
|
2018-05-23 22:48:46 +08:00
|
|
|
|
lines = callequal({"a": 0, "b": 1}, {"a": 1, "b": 1}, verbose=1)
|
2020-05-01 19:40:17 +08:00
|
|
|
|
assert lines is not None
|
2018-05-23 22:48:46 +08:00
|
|
|
|
assert lines[1].startswith("Omitting 1 identical item")
|
|
|
|
|
assert lines[2].startswith("Differing items")
|
2016-09-19 19:17:26 +08:00
|
|
|
|
assert lines[3] == "{'a': 0} != {'a': 1}"
|
2018-05-23 22:48:46 +08:00
|
|
|
|
assert "Common items" not in lines
|
2016-09-19 19:17:26 +08:00
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_dict_omitting_with_verbosity_2(self) -> None:
|
2018-05-23 22:48:46 +08:00
|
|
|
|
lines = callequal({"a": 0, "b": 1}, {"a": 1, "b": 1}, verbose=2)
|
2020-05-01 19:40:17 +08:00
|
|
|
|
assert lines is not None
|
2018-05-23 22:48:46 +08:00
|
|
|
|
assert lines[1].startswith("Common items:")
|
|
|
|
|
assert "Omitting" not in lines[1]
|
2013-08-01 20:48:34 +08:00
|
|
|
|
assert lines[2] == "{'b': 1}"
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_dict_different_items(self) -> None:
|
2019-04-04 23:53:39 +08:00
|
|
|
|
lines = callequal({"a": 0}, {"b": 1, "c": 2}, verbose=2)
|
|
|
|
|
assert lines == [
|
|
|
|
|
"{'a': 0} == {'b': 1, 'c': 2}",
|
|
|
|
|
"Left contains 1 more item:",
|
|
|
|
|
"{'a': 0}",
|
|
|
|
|
"Right contains 2 more items:",
|
|
|
|
|
"{'b': 1, 'c': 2}",
|
|
|
|
|
"Full diff:",
|
2023-11-27 22:47:18 +08:00
|
|
|
|
" {",
|
|
|
|
|
"- 'b': 1,",
|
|
|
|
|
"? ^ ^",
|
|
|
|
|
"+ 'a': 0,",
|
|
|
|
|
"? ^ ^",
|
|
|
|
|
"- 'c': 2,",
|
|
|
|
|
" }",
|
2019-04-04 23:53:39 +08:00
|
|
|
|
]
|
|
|
|
|
lines = callequal({"b": 1, "c": 2}, {"a": 0}, verbose=2)
|
|
|
|
|
assert lines == [
|
|
|
|
|
"{'b': 1, 'c': 2} == {'a': 0}",
|
|
|
|
|
"Left contains 2 more items:",
|
|
|
|
|
"{'b': 1, 'c': 2}",
|
|
|
|
|
"Right contains 1 more item:",
|
|
|
|
|
"{'a': 0}",
|
|
|
|
|
"Full diff:",
|
2023-11-27 22:47:18 +08:00
|
|
|
|
" {",
|
|
|
|
|
"- 'a': 0,",
|
|
|
|
|
"? ^ ^",
|
|
|
|
|
"+ 'b': 1,",
|
|
|
|
|
"? ^ ^",
|
|
|
|
|
"+ 'c': 2,",
|
|
|
|
|
" }",
|
2019-04-04 23:53:39 +08:00
|
|
|
|
]
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_sequence_different_items(self) -> None:
|
2019-04-04 23:53:39 +08:00
|
|
|
|
lines = callequal((1, 2), (3, 4, 5), verbose=2)
|
|
|
|
|
assert lines == [
|
|
|
|
|
"(1, 2) == (3, 4, 5)",
|
|
|
|
|
"At index 0 diff: 1 != 3",
|
|
|
|
|
"Right contains one more item: 5",
|
|
|
|
|
"Full diff:",
|
2023-11-27 22:47:18 +08:00
|
|
|
|
" (",
|
|
|
|
|
"- 3,",
|
|
|
|
|
"? ^",
|
|
|
|
|
"+ 1,",
|
|
|
|
|
"? ^",
|
|
|
|
|
"- 4,",
|
|
|
|
|
"? ^",
|
|
|
|
|
"+ 2,",
|
|
|
|
|
"? ^",
|
|
|
|
|
"- 5,",
|
|
|
|
|
" )",
|
2019-04-04 23:53:39 +08:00
|
|
|
|
]
|
|
|
|
|
lines = callequal((1, 2, 3), (4,), verbose=2)
|
|
|
|
|
assert lines == [
|
|
|
|
|
"(1, 2, 3) == (4,)",
|
|
|
|
|
"At index 0 diff: 1 != 4",
|
|
|
|
|
"Left contains 2 more items, first extra item: 2",
|
|
|
|
|
"Full diff:",
|
2023-11-27 22:47:18 +08:00
|
|
|
|
" (",
|
|
|
|
|
"- 4,",
|
|
|
|
|
"? ^",
|
|
|
|
|
"+ 1,",
|
|
|
|
|
"? ^",
|
|
|
|
|
"+ 2,",
|
|
|
|
|
"+ 3,",
|
|
|
|
|
" )",
|
|
|
|
|
]
|
|
|
|
|
lines = callequal((1, 2, 3), (1, 20, 3), verbose=2)
|
|
|
|
|
assert lines == [
|
|
|
|
|
"(1, 2, 3) == (1, 20, 3)",
|
|
|
|
|
"At index 1 diff: 2 != 20",
|
|
|
|
|
"Full diff:",
|
|
|
|
|
" (",
|
|
|
|
|
" 1,",
|
|
|
|
|
"- 20,",
|
|
|
|
|
"? -",
|
|
|
|
|
"+ 2,",
|
|
|
|
|
" 3,",
|
|
|
|
|
" )",
|
2019-04-04 23:53:39 +08:00
|
|
|
|
]
|
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_set(self) -> None:
|
2018-05-18 05:31:16 +08:00
|
|
|
|
expl = callequal({0, 1}, {0, 2})
|
2020-05-01 19:40:17 +08:00
|
|
|
|
assert expl is not None
|
2010-10-05 00:49:30 +08:00
|
|
|
|
assert len(expl) > 1
|
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_frozenzet(self) -> None:
|
2018-05-18 05:31:16 +08:00
|
|
|
|
expl = callequal(frozenset([0, 1]), {0, 2})
|
2020-05-01 19:40:17 +08:00
|
|
|
|
assert expl is not None
|
2013-08-01 20:48:34 +08:00
|
|
|
|
assert len(expl) > 1
|
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_Sequence(self) -> None:
|
2020-04-09 02:33:59 +08:00
|
|
|
|
# Test comparing with a Sequence subclass.
|
2020-10-03 10:57:16 +08:00
|
|
|
|
class TestSequence(MutableSequence[int]):
|
2013-08-01 20:48:34 +08:00
|
|
|
|
def __init__(self, iterable):
|
|
|
|
|
self.elements = list(iterable)
|
|
|
|
|
|
|
|
|
|
def __getitem__(self, item):
|
|
|
|
|
return self.elements[item]
|
|
|
|
|
|
|
|
|
|
def __len__(self):
|
|
|
|
|
return len(self.elements)
|
|
|
|
|
|
|
|
|
|
def __setitem__(self, item, value):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def __delitem__(self, item):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def insert(self, item, index):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
expl = callequal(TestSequence([0, 1]), list([0, 2]))
|
2020-05-01 19:40:17 +08:00
|
|
|
|
assert expl is not None
|
2013-04-29 03:59:10 +08:00
|
|
|
|
assert len(expl) > 1
|
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_list_tuples(self) -> None:
|
2017-07-17 07:25:08 +08:00
|
|
|
|
expl = callequal([], [(1, 2)])
|
2020-05-01 19:40:17 +08:00
|
|
|
|
assert expl is not None
|
2010-10-03 00:47:39 +08:00
|
|
|
|
assert len(expl) > 1
|
2017-07-17 07:25:08 +08:00
|
|
|
|
expl = callequal([(1, 2)], [])
|
2020-05-01 19:40:17 +08:00
|
|
|
|
assert expl is not None
|
2010-10-05 00:49:30 +08:00
|
|
|
|
assert len(expl) > 1
|
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_list_bad_repr(self) -> None:
|
2019-06-03 06:32:00 +08:00
|
|
|
|
class A:
|
2010-10-05 00:49:30 +08:00
|
|
|
|
def __repr__(self):
|
|
|
|
|
raise ValueError(42)
|
2018-05-23 22:48:46 +08:00
|
|
|
|
|
2010-10-05 00:49:30 +08:00
|
|
|
|
expl = callequal([], [A()])
|
2020-05-01 19:40:17 +08:00
|
|
|
|
assert expl is not None
|
2018-05-23 22:48:46 +08:00
|
|
|
|
assert "ValueError" in "".join(expl)
|
2020-02-14 09:17:05 +08:00
|
|
|
|
expl = callequal({}, {"1": A()}, verbose=2)
|
2020-05-01 19:40:17 +08:00
|
|
|
|
assert expl is not None
|
2020-02-14 09:17:05 +08:00
|
|
|
|
assert expl[0].startswith("{} == <[ValueError")
|
|
|
|
|
assert "raised in repr" in expl[0]
|
|
|
|
|
assert expl[1:] == [
|
|
|
|
|
"(pytest_assertion plugin: representation of details failed:"
|
|
|
|
|
" {}:{}: ValueError: 42.".format(
|
|
|
|
|
__file__, A.__repr__.__code__.co_firstlineno + 1
|
|
|
|
|
),
|
|
|
|
|
" Probably an object has a faulty __repr__.)",
|
|
|
|
|
]
|
2010-09-18 20:03:28 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_one_repr_empty(self) -> None:
|
2020-07-18 17:35:13 +08:00
|
|
|
|
"""The faulty empty string repr did trigger an unbound local error in _diff_text."""
|
2018-05-23 22:48:46 +08:00
|
|
|
|
|
2010-10-09 13:35:28 +08:00
|
|
|
|
class A(str):
|
|
|
|
|
def __repr__(self):
|
2018-05-23 22:48:46 +08:00
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
expl = callequal(A(), "")
|
2010-10-09 13:35:28 +08:00
|
|
|
|
assert not expl
|
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_repr_no_exc(self) -> None:
|
|
|
|
|
expl = callequal("foo", "bar")
|
|
|
|
|
assert expl is not None
|
|
|
|
|
assert "raised in repr()" not in " ".join(expl)
|
2011-02-16 07:24:18 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_unicode(self) -> None:
|
2020-02-24 22:19:08 +08:00
|
|
|
|
assert callequal("£€", "£") == [
|
|
|
|
|
"'£€' == '£'",
|
|
|
|
|
"- £",
|
|
|
|
|
"+ £€",
|
|
|
|
|
]
|
2013-11-29 08:29:14 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_nonascii_text(self) -> None:
|
2015-07-25 16:16:05 +08:00
|
|
|
|
"""
|
|
|
|
|
:issue: 877
|
|
|
|
|
non ascii python2 str caused a UnicodeDecodeError
|
|
|
|
|
"""
|
2018-05-23 22:48:46 +08:00
|
|
|
|
|
2015-07-25 16:16:05 +08:00
|
|
|
|
class A(str):
|
|
|
|
|
def __repr__(self):
|
2018-05-23 22:48:46 +08:00
|
|
|
|
return "\xff"
|
|
|
|
|
|
|
|
|
|
expl = callequal(A(), "1")
|
2020-02-04 21:38:18 +08:00
|
|
|
|
assert expl == ["ÿ == '1'", "- 1"]
|
2015-07-25 16:16:05 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_format_nonascii_explanation(self) -> None:
|
2018-05-23 22:48:46 +08:00
|
|
|
|
assert util.format_explanation("λ")
|
2016-02-12 22:54:36 +08:00
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_mojibake(self) -> None:
|
2014-01-29 08:42:58 +08:00
|
|
|
|
# issue 429
|
2018-11-05 09:43:24 +08:00
|
|
|
|
left = b"e"
|
|
|
|
|
right = b"\xc3\xa9"
|
2014-01-29 08:42:58 +08:00
|
|
|
|
expl = callequal(left, right)
|
2020-05-01 19:40:17 +08:00
|
|
|
|
assert expl is not None
|
2014-01-29 08:42:58 +08:00
|
|
|
|
for line in expl:
|
2019-06-03 06:32:00 +08:00
|
|
|
|
assert isinstance(line, str)
|
|
|
|
|
msg = "\n".join(expl)
|
2014-01-29 08:42:58 +08:00
|
|
|
|
assert msg
|
|
|
|
|
|
2022-10-10 08:09:33 +08:00
|
|
|
|
def test_nfc_nfd_same_string(self) -> None:
|
|
|
|
|
# issue 3426
|
|
|
|
|
left = "hyv\xe4"
|
|
|
|
|
right = "hyva\u0308"
|
|
|
|
|
expl = callequal(left, right)
|
|
|
|
|
assert expl == [
|
|
|
|
|
r"'hyv\xe4' == 'hyva\u0308'",
|
|
|
|
|
f"- {str(right)}",
|
|
|
|
|
f"+ {str(left)}",
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
expl = callequal(left, right, verbose=2)
|
|
|
|
|
assert expl == [
|
|
|
|
|
r"'hyv\xe4' == 'hyva\u0308'",
|
|
|
|
|
f"- {str(right)}",
|
|
|
|
|
f"+ {str(left)}",
|
|
|
|
|
]
|
|
|
|
|
|
2013-11-29 08:29:14 +08:00
|
|
|
|
|
2019-06-03 06:32:00 +08:00
|
|
|
|
class TestAssert_reprcompare_dataclass:
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_dataclasses(self, pytester: Pytester) -> None:
|
|
|
|
|
p = pytester.copy_example("dataclasses/test_compare_dataclasses.py")
|
|
|
|
|
result = pytester.runpytest(p)
|
2018-11-13 05:36:16 +08:00
|
|
|
|
result.assert_outcomes(failed=1, passed=0)
|
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
2020-07-09 00:04:56 +08:00
|
|
|
|
"E Omitting 1 identical items, use -vv to show",
|
|
|
|
|
"E Differing attributes:",
|
|
|
|
|
"E ['field_b']",
|
|
|
|
|
"E ",
|
|
|
|
|
"E Drill down into differing attribute field_b:",
|
2022-12-17 21:24:46 +08:00
|
|
|
|
"E field_b: 'b' != 'c'",
|
|
|
|
|
"E - c",
|
|
|
|
|
"E + b",
|
2020-07-09 00:04:56 +08:00
|
|
|
|
],
|
|
|
|
|
consecutive=True,
|
2020-06-02 21:38:41 +08:00
|
|
|
|
)
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_recursive_dataclasses(self, pytester: Pytester) -> None:
|
|
|
|
|
p = pytester.copy_example("dataclasses/test_compare_recursive_dataclasses.py")
|
|
|
|
|
result = pytester.runpytest(p)
|
2020-06-02 21:38:41 +08:00
|
|
|
|
result.assert_outcomes(failed=1, passed=0)
|
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
2020-07-09 00:04:56 +08:00
|
|
|
|
"E Omitting 1 identical items, use -vv to show",
|
|
|
|
|
"E Differing attributes:",
|
|
|
|
|
"E ['g', 'h', 'j']",
|
|
|
|
|
"E ",
|
|
|
|
|
"E Drill down into differing attribute g:",
|
|
|
|
|
"E g: S(a=10, b='ten') != S(a=20, b='xxx')...",
|
|
|
|
|
"E ",
|
2022-12-17 21:24:46 +08:00
|
|
|
|
"E ...Full output truncated (51 lines hidden), use '-vv' to show",
|
2020-07-09 00:04:56 +08:00
|
|
|
|
],
|
|
|
|
|
consecutive=True,
|
2020-06-02 21:38:41 +08:00
|
|
|
|
)
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_recursive_dataclasses_verbose(self, pytester: Pytester) -> None:
|
|
|
|
|
p = pytester.copy_example("dataclasses/test_compare_recursive_dataclasses.py")
|
|
|
|
|
result = pytester.runpytest(p, "-vv")
|
2020-06-02 21:38:41 +08:00
|
|
|
|
result.assert_outcomes(failed=1, passed=0)
|
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
2020-07-09 00:04:56 +08:00
|
|
|
|
"E Matching attributes:",
|
|
|
|
|
"E ['i']",
|
|
|
|
|
"E Differing attributes:",
|
|
|
|
|
"E ['g', 'h', 'j']",
|
|
|
|
|
"E ",
|
|
|
|
|
"E Drill down into differing attribute g:",
|
|
|
|
|
"E g: S(a=10, b='ten') != S(a=20, b='xxx')",
|
|
|
|
|
"E ",
|
|
|
|
|
"E Differing attributes:",
|
|
|
|
|
"E ['a', 'b']",
|
|
|
|
|
"E ",
|
|
|
|
|
"E Drill down into differing attribute a:",
|
|
|
|
|
"E a: 10 != 20",
|
|
|
|
|
"E ",
|
|
|
|
|
"E Drill down into differing attribute b:",
|
|
|
|
|
"E b: 'ten' != 'xxx'",
|
|
|
|
|
"E - xxx",
|
|
|
|
|
"E + ten",
|
|
|
|
|
"E ",
|
|
|
|
|
"E Drill down into differing attribute h:",
|
|
|
|
|
],
|
|
|
|
|
consecutive=True,
|
2018-11-13 05:36:16 +08:00
|
|
|
|
)
|
2018-08-03 06:16:14 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_dataclasses_verbose(self, pytester: Pytester) -> None:
|
|
|
|
|
p = pytester.copy_example("dataclasses/test_compare_dataclasses_verbose.py")
|
|
|
|
|
result = pytester.runpytest(p, "-vv")
|
2018-11-13 05:36:16 +08:00
|
|
|
|
result.assert_outcomes(failed=1, passed=0)
|
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
2018-11-13 23:37:02 +08:00
|
|
|
|
"*Matching attributes:*",
|
2018-11-13 05:36:16 +08:00
|
|
|
|
"*['field_a']*",
|
|
|
|
|
"*Differing attributes:*",
|
2018-11-13 23:37:02 +08:00
|
|
|
|
"*field_b: 'b' != 'c'*",
|
2018-11-13 05:36:16 +08:00
|
|
|
|
]
|
|
|
|
|
)
|
2018-08-03 06:16:14 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_dataclasses_with_attribute_comparison_off(
|
|
|
|
|
self, pytester: Pytester
|
|
|
|
|
) -> None:
|
|
|
|
|
p = pytester.copy_example(
|
2018-11-13 05:36:16 +08:00
|
|
|
|
"dataclasses/test_compare_dataclasses_field_comparison_off.py"
|
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest(p, "-vv")
|
2018-11-13 05:36:16 +08:00
|
|
|
|
result.assert_outcomes(failed=0, passed=1)
|
2018-08-03 06:16:14 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_comparing_two_different_data_classes(self, pytester: Pytester) -> None:
|
|
|
|
|
p = pytester.copy_example(
|
2018-11-13 05:36:16 +08:00
|
|
|
|
"dataclasses/test_compare_two_different_dataclasses.py"
|
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest(p, "-vv")
|
2018-11-13 05:36:16 +08:00
|
|
|
|
result.assert_outcomes(failed=0, passed=1)
|
2018-08-03 06:16:14 +08:00
|
|
|
|
|
2021-12-12 20:38:45 +08:00
|
|
|
|
def test_data_classes_with_custom_eq(self, pytester: Pytester) -> None:
|
|
|
|
|
p = pytester.copy_example(
|
|
|
|
|
"dataclasses/test_compare_dataclasses_with_custom_eq.py"
|
|
|
|
|
)
|
|
|
|
|
# issue 9362
|
|
|
|
|
result = pytester.runpytest(p, "-vv")
|
|
|
|
|
result.assert_outcomes(failed=1, passed=0)
|
|
|
|
|
result.stdout.no_re_match_line(".*Differing attributes.*")
|
|
|
|
|
|
2022-04-09 05:00:31 +08:00
|
|
|
|
def test_data_classes_with_initvar(self, pytester: Pytester) -> None:
|
|
|
|
|
p = pytester.copy_example("dataclasses/test_compare_initvar.py")
|
|
|
|
|
# issue 9820
|
|
|
|
|
result = pytester.runpytest(p, "-vv")
|
|
|
|
|
result.assert_outcomes(failed=1, passed=0)
|
|
|
|
|
result.stdout.no_re_match_line(".*AttributeError.*")
|
|
|
|
|
|
2018-08-03 06:16:14 +08:00
|
|
|
|
|
2019-06-03 06:32:00 +08:00
|
|
|
|
class TestAssert_reprcompare_attrsclass:
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_attrs(self) -> None:
|
2018-08-03 06:16:14 +08:00
|
|
|
|
@attr.s
|
2019-06-03 06:32:00 +08:00
|
|
|
|
class SimpleDataObject:
|
2018-08-03 06:16:14 +08:00
|
|
|
|
field_a = attr.ib()
|
|
|
|
|
field_b = attr.ib()
|
|
|
|
|
|
|
|
|
|
left = SimpleDataObject(1, "b")
|
|
|
|
|
right = SimpleDataObject(1, "c")
|
|
|
|
|
|
|
|
|
|
lines = callequal(left, right)
|
2020-05-01 19:40:17 +08:00
|
|
|
|
assert lines is not None
|
2020-07-09 00:04:56 +08:00
|
|
|
|
assert lines[2].startswith("Omitting 1 identical item")
|
2018-11-13 23:37:02 +08:00
|
|
|
|
assert "Matching attributes" not in lines
|
2020-07-09 00:04:56 +08:00
|
|
|
|
for line in lines[2:]:
|
2018-08-03 06:16:14 +08:00
|
|
|
|
assert "field_a" not in line
|
|
|
|
|
|
2020-06-02 21:38:41 +08:00
|
|
|
|
def test_attrs_recursive(self) -> None:
|
|
|
|
|
@attr.s
|
|
|
|
|
class OtherDataObject:
|
|
|
|
|
field_c = attr.ib()
|
|
|
|
|
field_d = attr.ib()
|
|
|
|
|
|
|
|
|
|
@attr.s
|
|
|
|
|
class SimpleDataObject:
|
|
|
|
|
field_a = attr.ib()
|
|
|
|
|
field_b = attr.ib()
|
|
|
|
|
|
|
|
|
|
left = SimpleDataObject(OtherDataObject(1, "a"), "b")
|
|
|
|
|
right = SimpleDataObject(OtherDataObject(1, "b"), "b")
|
|
|
|
|
|
|
|
|
|
lines = callequal(left, right)
|
2020-06-10 01:48:49 +08:00
|
|
|
|
assert lines is not None
|
2020-06-02 21:38:41 +08:00
|
|
|
|
assert "Matching attributes" not in lines
|
|
|
|
|
for line in lines[1:]:
|
|
|
|
|
assert "field_b:" not in line
|
|
|
|
|
assert "field_c:" not in line
|
|
|
|
|
|
|
|
|
|
def test_attrs_recursive_verbose(self) -> None:
|
|
|
|
|
@attr.s
|
|
|
|
|
class OtherDataObject:
|
|
|
|
|
field_c = attr.ib()
|
|
|
|
|
field_d = attr.ib()
|
|
|
|
|
|
|
|
|
|
@attr.s
|
|
|
|
|
class SimpleDataObject:
|
|
|
|
|
field_a = attr.ib()
|
|
|
|
|
field_b = attr.ib()
|
|
|
|
|
|
|
|
|
|
left = SimpleDataObject(OtherDataObject(1, "a"), "b")
|
|
|
|
|
right = SimpleDataObject(OtherDataObject(1, "b"), "b")
|
|
|
|
|
|
|
|
|
|
lines = callequal(left, right)
|
2020-06-10 01:48:49 +08:00
|
|
|
|
assert lines is not None
|
2020-07-09 00:04:56 +08:00
|
|
|
|
# indentation in output because of nested object structure
|
|
|
|
|
assert " field_d: 'a' != 'b'" in lines
|
2020-06-02 21:38:41 +08:00
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_attrs_verbose(self) -> None:
|
2018-08-03 06:16:14 +08:00
|
|
|
|
@attr.s
|
2019-06-03 06:32:00 +08:00
|
|
|
|
class SimpleDataObject:
|
2018-08-03 06:16:14 +08:00
|
|
|
|
field_a = attr.ib()
|
|
|
|
|
field_b = attr.ib()
|
|
|
|
|
|
|
|
|
|
left = SimpleDataObject(1, "b")
|
|
|
|
|
right = SimpleDataObject(1, "c")
|
|
|
|
|
|
|
|
|
|
lines = callequal(left, right, verbose=2)
|
2020-05-01 19:40:17 +08:00
|
|
|
|
assert lines is not None
|
2020-07-09 00:04:56 +08:00
|
|
|
|
assert lines[2].startswith("Matching attributes:")
|
|
|
|
|
assert "Omitting" not in lines[2]
|
|
|
|
|
assert lines[3] == "['field_a']"
|
2018-08-03 06:16:14 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_attrs_with_attribute_comparison_off(self) -> None:
|
2018-08-03 06:16:14 +08:00
|
|
|
|
@attr.s
|
2019-06-03 06:32:00 +08:00
|
|
|
|
class SimpleDataObject:
|
2018-08-03 06:16:14 +08:00
|
|
|
|
field_a = attr.ib()
|
2020-09-23 19:28:10 +08:00
|
|
|
|
field_b = attr.ib(eq=False)
|
2018-08-03 06:16:14 +08:00
|
|
|
|
|
|
|
|
|
left = SimpleDataObject(1, "b")
|
|
|
|
|
right = SimpleDataObject(1, "b")
|
|
|
|
|
|
|
|
|
|
lines = callequal(left, right, verbose=2)
|
2020-05-01 19:40:17 +08:00
|
|
|
|
assert lines is not None
|
2020-07-09 00:04:56 +08:00
|
|
|
|
assert lines[2].startswith("Matching attributes:")
|
2018-08-03 06:16:14 +08:00
|
|
|
|
assert "Omitting" not in lines[1]
|
2020-07-09 00:04:56 +08:00
|
|
|
|
assert lines[3] == "['field_a']"
|
|
|
|
|
for line in lines[3:]:
|
2018-08-03 06:16:14 +08:00
|
|
|
|
assert "field_b" not in line
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_comparing_two_different_attrs_classes(self) -> None:
|
2018-08-03 06:16:14 +08:00
|
|
|
|
@attr.s
|
2019-06-03 06:32:00 +08:00
|
|
|
|
class SimpleDataObjectOne:
|
2018-08-03 06:16:14 +08:00
|
|
|
|
field_a = attr.ib()
|
|
|
|
|
field_b = attr.ib()
|
|
|
|
|
|
|
|
|
|
@attr.s
|
2019-06-03 06:32:00 +08:00
|
|
|
|
class SimpleDataObjectTwo:
|
2018-08-03 06:16:14 +08:00
|
|
|
|
field_a = attr.ib()
|
|
|
|
|
field_b = attr.ib()
|
|
|
|
|
|
|
|
|
|
left = SimpleDataObjectOne(1, "b")
|
|
|
|
|
right = SimpleDataObjectTwo(1, "c")
|
|
|
|
|
|
|
|
|
|
lines = callequal(left, right)
|
|
|
|
|
assert lines is None
|
|
|
|
|
|
2021-12-12 22:11:33 +08:00
|
|
|
|
def test_attrs_with_auto_detect_and_custom_eq(self) -> None:
|
|
|
|
|
@attr.s(
|
|
|
|
|
auto_detect=True
|
|
|
|
|
) # attr.s doesn’t ignore a custom eq if auto_detect=True
|
|
|
|
|
class SimpleDataObject:
|
|
|
|
|
field_a = attr.ib()
|
|
|
|
|
|
|
|
|
|
def __eq__(self, other): # pragma: no cover
|
|
|
|
|
return super().__eq__(other)
|
|
|
|
|
|
|
|
|
|
left = SimpleDataObject(1)
|
|
|
|
|
right = SimpleDataObject(2)
|
|
|
|
|
# issue 9362
|
|
|
|
|
lines = callequal(left, right, verbose=2)
|
|
|
|
|
assert lines is None
|
|
|
|
|
|
2021-12-12 20:38:45 +08:00
|
|
|
|
def test_attrs_with_custom_eq(self) -> None:
|
2022-02-11 23:20:42 +08:00
|
|
|
|
@attr.define(slots=False)
|
2021-12-12 20:38:45 +08:00
|
|
|
|
class SimpleDataObject:
|
|
|
|
|
field_a = attr.ib()
|
|
|
|
|
|
2021-12-12 21:39:30 +08:00
|
|
|
|
def __eq__(self, other): # pragma: no cover
|
|
|
|
|
return super().__eq__(other)
|
2021-12-12 20:38:45 +08:00
|
|
|
|
|
|
|
|
|
left = SimpleDataObject(1)
|
|
|
|
|
right = SimpleDataObject(2)
|
|
|
|
|
# issue 9362
|
|
|
|
|
lines = callequal(left, right, verbose=2)
|
|
|
|
|
assert lines is None
|
|
|
|
|
|
2018-08-03 06:16:14 +08:00
|
|
|
|
|
2020-07-28 20:24:24 +08:00
|
|
|
|
class TestAssert_reprcompare_namedtuple:
|
|
|
|
|
def test_namedtuple(self) -> None:
|
|
|
|
|
NT = collections.namedtuple("NT", ["a", "b"])
|
|
|
|
|
|
|
|
|
|
left = NT(1, "b")
|
|
|
|
|
right = NT(1, "c")
|
|
|
|
|
|
|
|
|
|
lines = callequal(left, right)
|
|
|
|
|
assert lines == [
|
|
|
|
|
"NT(a=1, b='b') == NT(a=1, b='c')",
|
|
|
|
|
"",
|
|
|
|
|
"Omitting 1 identical items, use -vv to show",
|
|
|
|
|
"Differing attributes:",
|
|
|
|
|
"['b']",
|
|
|
|
|
"",
|
|
|
|
|
"Drill down into differing attribute b:",
|
|
|
|
|
" b: 'b' != 'c'",
|
|
|
|
|
" - c",
|
|
|
|
|
" + b",
|
2022-02-15 20:43:20 +08:00
|
|
|
|
"Use -v to get more diff",
|
2020-07-28 20:24:24 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
def test_comparing_two_different_namedtuple(self) -> None:
|
|
|
|
|
NT1 = collections.namedtuple("NT1", ["a", "b"])
|
|
|
|
|
NT2 = collections.namedtuple("NT2", ["a", "b"])
|
|
|
|
|
|
|
|
|
|
left = NT1(1, "b")
|
|
|
|
|
right = NT2(2, "b")
|
|
|
|
|
|
|
|
|
|
lines = callequal(left, right)
|
|
|
|
|
# Because the types are different, uses the generic sequence matcher.
|
|
|
|
|
assert lines == [
|
|
|
|
|
"NT1(a=1, b='b') == NT2(a=2, b='b')",
|
|
|
|
|
"At index 0 diff: 1 != 2",
|
2022-02-15 20:43:20 +08:00
|
|
|
|
"Use -v to get more diff",
|
2020-07-28 20:24:24 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
2019-06-03 06:32:00 +08:00
|
|
|
|
class TestFormatExplanation:
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_special_chars_full(self, pytester: Pytester) -> None:
|
2014-04-03 00:35:22 +08:00
|
|
|
|
# Issue 453, for the bug this would raise IndexError
|
2020-10-30 09:39:44 +08:00
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2014-04-03 00:35:22 +08:00
|
|
|
|
def test_foo():
|
2014-06-01 05:51:05 +08:00
|
|
|
|
assert '\\n}' == ''
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest()
|
2014-04-03 00:35:22 +08:00
|
|
|
|
assert result.ret == 1
|
2018-05-23 22:48:46 +08:00
|
|
|
|
result.stdout.fnmatch_lines(["*AssertionError*"])
|
2014-04-03 00:35:22 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_fmt_simple(self) -> None:
|
2018-05-23 22:48:46 +08:00
|
|
|
|
expl = "assert foo"
|
|
|
|
|
assert util.format_explanation(expl) == "assert foo"
|
2014-08-24 00:14:25 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_fmt_where(self) -> None:
|
2018-05-23 22:48:46 +08:00
|
|
|
|
expl = "\n".join(["assert 1", "{1 = foo", "} == 2"])
|
|
|
|
|
res = "\n".join(["assert 1 == 2", " + where 1 = foo"])
|
2014-08-24 00:14:25 +08:00
|
|
|
|
assert util.format_explanation(expl) == res
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_fmt_and(self) -> None:
|
2018-05-23 22:48:46 +08:00
|
|
|
|
expl = "\n".join(["assert 1", "{1 = foo", "} == 2", "{2 = bar", "}"])
|
|
|
|
|
res = "\n".join(["assert 1 == 2", " + where 1 = foo", " + and 2 = bar"])
|
2014-08-24 00:14:25 +08:00
|
|
|
|
assert util.format_explanation(expl) == res
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_fmt_where_nested(self) -> None:
|
2018-05-23 22:48:46 +08:00
|
|
|
|
expl = "\n".join(["assert 1", "{1 = foo", "{foo = bar", "}", "} == 2"])
|
|
|
|
|
res = "\n".join(["assert 1 == 2", " + where 1 = foo", " + where foo = bar"])
|
2014-08-24 00:14:25 +08:00
|
|
|
|
assert util.format_explanation(expl) == res
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_fmt_newline(self) -> None:
|
2018-05-23 22:48:46 +08:00
|
|
|
|
expl = "\n".join(['assert "foo" == "bar"', "~- foo", "~+ bar"])
|
|
|
|
|
res = "\n".join(['assert "foo" == "bar"', " - foo", " + bar"])
|
2014-08-24 00:14:25 +08:00
|
|
|
|
assert util.format_explanation(expl) == res
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_fmt_newline_escaped(self) -> None:
|
2018-05-23 22:48:46 +08:00
|
|
|
|
expl = "\n".join(["assert foo == bar", "baz"])
|
|
|
|
|
res = "assert foo == bar\\nbaz"
|
2014-08-24 00:14:25 +08:00
|
|
|
|
assert util.format_explanation(expl) == res
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_fmt_newline_before_where(self) -> None:
|
2018-05-23 22:48:46 +08:00
|
|
|
|
expl = "\n".join(
|
|
|
|
|
[
|
|
|
|
|
"the assertion message here",
|
|
|
|
|
">assert 1",
|
|
|
|
|
"{1 = foo",
|
|
|
|
|
"} == 2",
|
|
|
|
|
"{2 = bar",
|
|
|
|
|
"}",
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
res = "\n".join(
|
|
|
|
|
[
|
|
|
|
|
"the assertion message here",
|
|
|
|
|
"assert 1 == 2",
|
|
|
|
|
" + where 1 = foo",
|
|
|
|
|
" + and 2 = bar",
|
|
|
|
|
]
|
|
|
|
|
)
|
2014-08-24 00:14:25 +08:00
|
|
|
|
assert util.format_explanation(expl) == res
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_fmt_multi_newline_before_where(self) -> None:
|
2018-05-23 22:48:46 +08:00
|
|
|
|
expl = "\n".join(
|
|
|
|
|
[
|
|
|
|
|
"the assertion",
|
|
|
|
|
"~message here",
|
|
|
|
|
">assert 1",
|
|
|
|
|
"{1 = foo",
|
|
|
|
|
"} == 2",
|
|
|
|
|
"{2 = bar",
|
|
|
|
|
"}",
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
res = "\n".join(
|
|
|
|
|
[
|
|
|
|
|
"the assertion",
|
|
|
|
|
" message here",
|
|
|
|
|
"assert 1 == 2",
|
|
|
|
|
" + where 1 = foo",
|
|
|
|
|
" + and 2 = bar",
|
|
|
|
|
]
|
|
|
|
|
)
|
2014-08-24 00:14:25 +08:00
|
|
|
|
assert util.format_explanation(expl) == res
|
|
|
|
|
|
2014-04-03 00:35:22 +08:00
|
|
|
|
|
2019-06-03 06:32:00 +08:00
|
|
|
|
class TestTruncateExplanation:
|
2016-09-22 07:06:45 +08:00
|
|
|
|
# The number of lines in the truncation explanation message. Used
|
|
|
|
|
# to calculate that results have the expected length.
|
2016-10-11 07:17:15 +08:00
|
|
|
|
LINES_IN_TRUNCATION_MSG = 2
|
2016-09-22 07:06:45 +08:00
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_doesnt_truncate_when_input_is_empty_list(self) -> None:
|
2020-10-06 09:13:05 +08:00
|
|
|
|
expl: List[str] = []
|
2016-09-22 07:06:45 +08:00
|
|
|
|
result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100)
|
|
|
|
|
assert result == expl
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_doesnt_truncate_at_when_input_is_5_lines_and_LT_max_chars(self) -> None:
|
2018-05-23 22:48:46 +08:00
|
|
|
|
expl = ["a" * 100 for x in range(5)]
|
2017-07-17 07:25:08 +08:00
|
|
|
|
result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80)
|
2016-09-22 07:06:45 +08:00
|
|
|
|
assert result == expl
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_truncates_at_8_lines_when_given_list_of_empty_strings(self) -> None:
|
2018-05-23 22:48:46 +08:00
|
|
|
|
expl = ["" for x in range(50)]
|
2016-09-22 07:06:45 +08:00
|
|
|
|
result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100)
|
2022-12-17 21:24:46 +08:00
|
|
|
|
assert len(result) != len(expl)
|
2016-09-22 07:06:45 +08:00
|
|
|
|
assert result != expl
|
|
|
|
|
assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG
|
|
|
|
|
assert "Full output truncated" in result[-1]
|
2022-12-17 21:24:46 +08:00
|
|
|
|
assert "42 lines hidden" in result[-1]
|
2018-05-23 22:48:46 +08:00
|
|
|
|
last_line_before_trunc_msg = result[-self.LINES_IN_TRUNCATION_MSG - 1]
|
2016-09-22 07:06:45 +08:00
|
|
|
|
assert last_line_before_trunc_msg.endswith("...")
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_truncates_at_8_lines_when_first_8_lines_are_LT_max_chars(self) -> None:
|
2022-12-17 21:24:46 +08:00
|
|
|
|
total_lines = 100
|
|
|
|
|
expl = ["a" for x in range(total_lines)]
|
2017-07-17 07:25:08 +08:00
|
|
|
|
result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80)
|
2016-09-22 07:06:45 +08:00
|
|
|
|
assert result != expl
|
|
|
|
|
assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG
|
|
|
|
|
assert "Full output truncated" in result[-1]
|
2022-12-17 21:24:46 +08:00
|
|
|
|
assert f"{total_lines - 8} lines hidden" in result[-1]
|
2018-05-23 22:48:46 +08:00
|
|
|
|
last_line_before_trunc_msg = result[-self.LINES_IN_TRUNCATION_MSG - 1]
|
2016-09-22 07:06:45 +08:00
|
|
|
|
assert last_line_before_trunc_msg.endswith("...")
|
|
|
|
|
|
2022-12-17 21:24:46 +08:00
|
|
|
|
def test_truncates_at_8_lines_when_there_is_one_line_to_remove(self) -> None:
|
|
|
|
|
"""The number of line in the result is 9, the same number as if we truncated."""
|
|
|
|
|
expl = ["a" for x in range(9)]
|
|
|
|
|
result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80)
|
|
|
|
|
assert result == expl
|
|
|
|
|
assert "truncated" not in result[-1]
|
|
|
|
|
|
|
|
|
|
def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_chars(
|
|
|
|
|
self,
|
|
|
|
|
) -> None:
|
|
|
|
|
line = "a" * 10
|
|
|
|
|
expl = [line, line]
|
|
|
|
|
result = truncate._truncate_explanation(expl, max_lines=10, max_chars=10)
|
|
|
|
|
assert result == [line, line]
|
|
|
|
|
|
|
|
|
|
def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_lines(
|
|
|
|
|
self,
|
|
|
|
|
) -> None:
|
|
|
|
|
line = "a" * 10
|
|
|
|
|
expl = [line, line]
|
|
|
|
|
result = truncate._truncate_explanation(expl, max_lines=1, max_chars=100)
|
|
|
|
|
assert result == [line, line]
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_truncates_at_8_lines_when_first_8_lines_are_EQ_max_chars(self) -> None:
|
2022-12-17 21:24:46 +08:00
|
|
|
|
expl = [chr(97 + x) * 80 for x in range(16)]
|
2017-07-17 07:25:08 +08:00
|
|
|
|
result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80)
|
2016-09-22 07:06:45 +08:00
|
|
|
|
assert result != expl
|
2022-12-17 21:24:46 +08:00
|
|
|
|
assert len(result) == 16 - 8 + self.LINES_IN_TRUNCATION_MSG
|
2016-09-22 07:06:45 +08:00
|
|
|
|
assert "Full output truncated" in result[-1]
|
2022-12-17 21:24:46 +08:00
|
|
|
|
assert "8 lines hidden" in result[-1]
|
2018-05-23 22:48:46 +08:00
|
|
|
|
last_line_before_trunc_msg = result[-self.LINES_IN_TRUNCATION_MSG - 1]
|
2016-09-22 07:06:45 +08:00
|
|
|
|
assert last_line_before_trunc_msg.endswith("...")
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_truncates_at_4_lines_when_first_4_lines_are_GT_max_chars(self) -> None:
|
2018-05-23 22:48:46 +08:00
|
|
|
|
expl = ["a" * 250 for x in range(10)]
|
2016-09-22 07:06:45 +08:00
|
|
|
|
result = truncate._truncate_explanation(expl, max_lines=8, max_chars=999)
|
|
|
|
|
assert result != expl
|
|
|
|
|
assert len(result) == 4 + self.LINES_IN_TRUNCATION_MSG
|
|
|
|
|
assert "Full output truncated" in result[-1]
|
|
|
|
|
assert "7 lines hidden" in result[-1]
|
2018-05-23 22:48:46 +08:00
|
|
|
|
last_line_before_trunc_msg = result[-self.LINES_IN_TRUNCATION_MSG - 1]
|
2016-09-22 07:06:45 +08:00
|
|
|
|
assert last_line_before_trunc_msg.endswith("...")
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_truncates_at_1_line_when_first_line_is_GT_max_chars(self) -> None:
|
2018-05-23 22:48:46 +08:00
|
|
|
|
expl = ["a" * 250 for x in range(1000)]
|
2016-09-22 07:06:45 +08:00
|
|
|
|
result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100)
|
|
|
|
|
assert result != expl
|
|
|
|
|
assert len(result) == 1 + self.LINES_IN_TRUNCATION_MSG
|
|
|
|
|
assert "Full output truncated" in result[-1]
|
|
|
|
|
assert "1000 lines hidden" in result[-1]
|
2018-05-23 22:48:46 +08:00
|
|
|
|
last_line_before_trunc_msg = result[-self.LINES_IN_TRUNCATION_MSG - 1]
|
2016-09-22 07:06:45 +08:00
|
|
|
|
assert last_line_before_trunc_msg.endswith("...")
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_full_output_truncated(self, monkeypatch, pytester: Pytester) -> None:
|
2020-07-18 17:35:13 +08:00
|
|
|
|
"""Test against full runpytest() output."""
|
2016-09-22 07:06:45 +08:00
|
|
|
|
|
|
|
|
|
line_count = 7
|
|
|
|
|
line_len = 100
|
2022-12-17 21:24:46 +08:00
|
|
|
|
expected_truncated_lines = 1
|
2020-10-30 09:39:44 +08:00
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
r"""
|
2016-09-22 07:06:45 +08:00
|
|
|
|
def test_many_lines():
|
|
|
|
|
a = list([str(i)[0] * %d for i in range(%d)])
|
|
|
|
|
b = a[::2]
|
|
|
|
|
a = '\n'.join(map(str, a))
|
|
|
|
|
b = '\n'.join(map(str, b))
|
|
|
|
|
assert a == b
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
% (line_len, line_count)
|
|
|
|
|
)
|
|
|
|
|
monkeypatch.delenv("CI", raising=False)
|
2016-09-22 07:06:45 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest()
|
2016-09-22 07:06:45 +08:00
|
|
|
|
# without -vv, truncate the message showing a few diff lines only
|
2018-05-23 22:48:46 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
2020-02-04 21:38:18 +08:00
|
|
|
|
"*+ 1*",
|
|
|
|
|
"*+ 3*",
|
|
|
|
|
"*+ 5*",
|
2022-12-17 21:24:46 +08:00
|
|
|
|
"*truncated (%d line hidden)*use*-vv*" % expected_truncated_lines,
|
2018-05-23 22:48:46 +08:00
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest("-vv")
|
2018-05-23 22:48:46 +08:00
|
|
|
|
result.stdout.fnmatch_lines(["* 6*"])
|
|
|
|
|
|
|
|
|
|
monkeypatch.setenv("CI", "1")
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest()
|
2018-05-23 22:48:46 +08:00
|
|
|
|
result.stdout.fnmatch_lines(["* 6*"])
|
2016-09-22 07:06:45 +08:00
|
|
|
|
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_python25_compile_issue257(pytester: Pytester) -> None:
|
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2013-02-13 05:43:33 +08:00
|
|
|
|
def test_rewritten():
|
|
|
|
|
assert 1 == 2
|
|
|
|
|
# some comment
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest()
|
2013-02-13 05:43:33 +08:00
|
|
|
|
assert result.ret == 1
|
2018-05-23 22:48:46 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
"""
|
2013-02-13 05:43:33 +08:00
|
|
|
|
*E*assert 1 == 2*
|
|
|
|
|
*1 failed*
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2013-02-13 05:43:33 +08:00
|
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_rewritten(pytester: Pytester) -> None:
|
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2011-07-04 08:28:48 +08:00
|
|
|
|
def test_rewritten():
|
|
|
|
|
assert "@py_builtins" in globals()
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
assert pytester.runpytest().ret == 0
|
2011-05-27 09:06:11 +08:00
|
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
|
2020-03-08 08:23:19 +08:00
|
|
|
|
def test_reprcompare_notin() -> None:
|
|
|
|
|
assert callop("not in", "foo", "aaafoobbb") == [
|
|
|
|
|
"'foo' not in 'aaafoobbb'",
|
|
|
|
|
"'foo' is contained here:",
|
|
|
|
|
" aaafoobbb",
|
|
|
|
|
"? +++",
|
|
|
|
|
]
|
2010-12-10 09:03:26 +08:00
|
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_reprcompare_whitespaces() -> None:
|
2020-02-10 14:46:46 +08:00
|
|
|
|
assert callequal("\r\n", "\n") == [
|
2018-06-26 21:35:27 +08:00
|
|
|
|
r"'\r\n' == '\n'",
|
|
|
|
|
r"Strings contain only whitespace, escaping them using repr()",
|
2020-02-04 21:38:18 +08:00
|
|
|
|
r"- '\n'",
|
|
|
|
|
r"+ '\r\n'",
|
|
|
|
|
r"? ++",
|
2018-06-26 21:35:27 +08:00
|
|
|
|
]
|
2018-05-04 06:51:41 +08:00
|
|
|
|
|
|
|
|
|
|
2023-10-03 05:37:52 +08:00
|
|
|
|
class TestSetAssertions:
|
|
|
|
|
@pytest.mark.parametrize("op", [">=", ">", "<=", "<", "=="])
|
|
|
|
|
def test_set_extra_item(self, op, pytester: Pytester) -> None:
|
|
|
|
|
pytester.makepyfile(
|
|
|
|
|
f"""
|
|
|
|
|
def test_hello():
|
|
|
|
|
x = set("hello x")
|
|
|
|
|
y = set("hello y")
|
|
|
|
|
assert x {op} y
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2023-10-03 05:37:52 +08:00
|
|
|
|
)
|
2010-09-18 20:03:28 +08:00
|
|
|
|
|
2023-10-03 05:37:52 +08:00
|
|
|
|
result = pytester.runpytest()
|
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
|
|
|
|
"*def test_hello():*",
|
|
|
|
|
f"*assert x {op} y*",
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
if op in [">=", ">", "=="]:
|
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
|
|
|
|
"*E*Extra items in the right set:*",
|
|
|
|
|
"*E*'y'",
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
if op in ["<=", "<", "=="]:
|
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
|
|
|
|
"*E*Extra items in the left set:*",
|
|
|
|
|
"*E*'x'",
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("op", [">", "<", "!="])
|
|
|
|
|
def test_set_proper_superset_equal(self, pytester: Pytester, op) -> None:
|
|
|
|
|
pytester.makepyfile(
|
|
|
|
|
f"""
|
|
|
|
|
def test_hello():
|
|
|
|
|
x = set([1, 2, 3])
|
|
|
|
|
y = x.copy()
|
|
|
|
|
assert x {op} y
|
|
|
|
|
"""
|
|
|
|
|
)
|
2017-07-17 07:25:09 +08:00
|
|
|
|
|
2023-10-03 05:37:52 +08:00
|
|
|
|
result = pytester.runpytest()
|
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
|
|
|
|
"*def test_hello():*",
|
|
|
|
|
f"*assert x {op} y*",
|
|
|
|
|
"*E*Both sets are equal*",
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def test_pytest_assertrepr_compare_integration(self, pytester: Pytester) -> None:
|
|
|
|
|
pytester.makepyfile(
|
|
|
|
|
"""
|
|
|
|
|
def test_hello():
|
|
|
|
|
x = set(range(100))
|
|
|
|
|
y = x.copy()
|
|
|
|
|
y.remove(50)
|
|
|
|
|
assert x == y
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2023-10-03 05:37:52 +08:00
|
|
|
|
)
|
|
|
|
|
result = pytester.runpytest()
|
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
|
|
|
|
"*def test_hello():*",
|
|
|
|
|
"*assert x == y*",
|
|
|
|
|
"*E*Extra items*left*",
|
|
|
|
|
"*E*50*",
|
|
|
|
|
"*= 1 failed in*",
|
|
|
|
|
]
|
|
|
|
|
)
|
2010-10-05 00:49:30 +08:00
|
|
|
|
|
2012-06-27 23:26:55 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_assertrepr_loaded_per_dir(pytester: Pytester) -> None:
|
|
|
|
|
pytester.makepyfile(test_base=["def test_base(): assert 1 == 2"])
|
|
|
|
|
a = pytester.mkdir("a")
|
2023-06-20 19:55:40 +08:00
|
|
|
|
a.joinpath("test_a.py").write_text("def test_a(): assert 1 == 2", encoding="utf-8")
|
2020-10-30 09:39:44 +08:00
|
|
|
|
a.joinpath("conftest.py").write_text(
|
2023-06-20 19:55:40 +08:00
|
|
|
|
'def pytest_assertrepr_compare(): return ["summary a"]', encoding="utf-8"
|
2020-10-30 09:39:44 +08:00
|
|
|
|
)
|
|
|
|
|
b = pytester.mkdir("b")
|
2023-06-20 19:55:40 +08:00
|
|
|
|
b.joinpath("test_b.py").write_text("def test_b(): assert 1 == 2", encoding="utf-8")
|
2020-10-30 09:39:44 +08:00
|
|
|
|
b.joinpath("conftest.py").write_text(
|
2023-06-20 19:55:40 +08:00
|
|
|
|
'def pytest_assertrepr_compare(): return ["summary b"]', encoding="utf-8"
|
2020-10-30 09:39:44 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
result = pytester.runpytest()
|
2018-05-23 22:48:46 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
|
|
|
|
"*def test_base():*",
|
|
|
|
|
"*E*assert 1 == 2*",
|
|
|
|
|
"*def test_a():*",
|
|
|
|
|
"*E*assert summary a*",
|
|
|
|
|
"*def test_b():*",
|
|
|
|
|
"*E*assert summary b*",
|
|
|
|
|
]
|
|
|
|
|
)
|
2011-10-16 18:51:15 +08:00
|
|
|
|
|
2010-10-05 00:49:30 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_assertion_options(pytester: Pytester) -> None:
|
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2009-09-06 22:59:39 +08:00
|
|
|
|
def test_hello():
|
|
|
|
|
x = 3
|
|
|
|
|
assert x == 4
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest()
|
2010-07-27 03:15:15 +08:00
|
|
|
|
assert "3 == 4" in result.stdout.str()
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest_subprocess("--assert=plain")
|
2019-10-06 01:18:51 +08:00
|
|
|
|
result.stdout.no_fnmatch_line("*3 == 4*")
|
2011-05-27 03:34:27 +08:00
|
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_triple_quoted_string_issue113(pytester: Pytester) -> None:
|
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2010-07-29 18:55:39 +08:00
|
|
|
|
def test_hello():
|
|
|
|
|
assert "" == '''
|
2018-05-23 22:48:46 +08:00
|
|
|
|
'''"""
|
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest("--fulltrace")
|
2018-05-23 22:48:46 +08:00
|
|
|
|
result.stdout.fnmatch_lines(["*1 failed*"])
|
2019-10-06 01:18:51 +08:00
|
|
|
|
result.stdout.no_fnmatch_line("*SyntaxError*")
|
2010-07-29 18:55:39 +08:00
|
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_traceback_failure(pytester: Pytester) -> None:
|
|
|
|
|
p1 = pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2009-09-06 22:59:39 +08:00
|
|
|
|
def g():
|
|
|
|
|
return 2
|
|
|
|
|
def f(x):
|
|
|
|
|
assert x == g()
|
|
|
|
|
def test_onefails():
|
|
|
|
|
f(3)
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest(p1, "--tb=long")
|
2018-05-23 22:48:46 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
|
|
|
|
"*test_traceback_failure.py F*",
|
|
|
|
|
"====* FAILURES *====",
|
|
|
|
|
"____*____",
|
|
|
|
|
"",
|
|
|
|
|
" def test_onefails():",
|
|
|
|
|
"> f(3)",
|
|
|
|
|
"",
|
|
|
|
|
"*test_*.py:6: ",
|
|
|
|
|
"_ _ _ *",
|
|
|
|
|
# "",
|
|
|
|
|
" def f(x):",
|
|
|
|
|
"> assert x == g()",
|
|
|
|
|
"E assert 3 == 2",
|
|
|
|
|
"E + where 2 = g()",
|
|
|
|
|
"",
|
|
|
|
|
"*test_traceback_failure.py:4: AssertionError",
|
|
|
|
|
]
|
|
|
|
|
)
|
2009-09-06 22:59:39 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest(p1) # "auto"
|
2018-05-23 22:48:46 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
|
|
|
|
"*test_traceback_failure.py F*",
|
|
|
|
|
"====* FAILURES *====",
|
|
|
|
|
"____*____",
|
|
|
|
|
"",
|
|
|
|
|
" def test_onefails():",
|
|
|
|
|
"> f(3)",
|
|
|
|
|
"",
|
|
|
|
|
"*test_*.py:6: ",
|
|
|
|
|
"",
|
|
|
|
|
" def f(x):",
|
|
|
|
|
"> assert x == g()",
|
|
|
|
|
"E assert 3 == 2",
|
|
|
|
|
"E + where 2 = g()",
|
|
|
|
|
"",
|
|
|
|
|
"*test_traceback_failure.py:4: AssertionError",
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_exception_handling_no_traceback(pytester: Pytester) -> None:
|
2020-07-18 17:35:13 +08:00
|
|
|
|
"""Handle chain exceptions in tasks submitted by the multiprocess module (#1984)."""
|
2020-10-30 09:39:44 +08:00
|
|
|
|
p1 = pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2016-10-28 07:15:05 +08:00
|
|
|
|
from multiprocessing import Pool
|
|
|
|
|
|
|
|
|
|
def process_task(n):
|
|
|
|
|
assert n == 10
|
|
|
|
|
|
|
|
|
|
def multitask_job():
|
|
|
|
|
tasks = [1]
|
|
|
|
|
with Pool(processes=1) as pool:
|
|
|
|
|
pool.map(process_task, tasks)
|
|
|
|
|
|
|
|
|
|
def test_multitask_job():
|
|
|
|
|
multitask_job()
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
pytester.syspathinsert()
|
|
|
|
|
result = pytester.runpytest(p1, "--tb=long")
|
2018-05-23 22:48:46 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
|
|
|
|
"====* FAILURES *====",
|
|
|
|
|
"*multiprocessing.pool.RemoteTraceback:*",
|
|
|
|
|
"Traceback (most recent call last):",
|
|
|
|
|
"*assert n == 10",
|
|
|
|
|
"The above exception was the direct cause of the following exception:",
|
|
|
|
|
"> * multitask_job()",
|
|
|
|
|
]
|
|
|
|
|
)
|
2016-10-28 07:15:05 +08:00
|
|
|
|
|
|
|
|
|
|
2019-05-28 07:31:52 +08:00
|
|
|
|
@pytest.mark.skipif("'__pypy__' in sys.builtin_module_names")
|
2020-06-21 22:26:36 +08:00
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"cmdline_args, warning_output",
|
|
|
|
|
[
|
|
|
|
|
(
|
|
|
|
|
["-OO", "-m", "pytest", "-h"],
|
|
|
|
|
["warning :*PytestConfigWarning:*assert statements are not executed*"],
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
["-OO", "-m", "pytest"],
|
|
|
|
|
[
|
|
|
|
|
"=*= warnings summary =*=",
|
|
|
|
|
"*PytestConfigWarning:*assert statements are not executed*",
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
["-OO", "-m", "pytest", "--assert=plain"],
|
|
|
|
|
[
|
|
|
|
|
"=*= warnings summary =*=",
|
|
|
|
|
"*PytestConfigWarning: ASSERTIONS ARE NOT EXECUTED and FAILING TESTS WILL PASS. "
|
|
|
|
|
"Are you using python -O?",
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_warn_missing(pytester: Pytester, cmdline_args, warning_output) -> None:
|
|
|
|
|
pytester.makepyfile("")
|
2020-06-21 00:15:58 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.run(sys.executable, *cmdline_args)
|
2020-06-21 01:06:41 +08:00
|
|
|
|
result.stdout.fnmatch_lines(warning_output)
|
2013-04-30 18:05:58 +08:00
|
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_recursion_source_decode(pytester: Pytester) -> None:
|
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2013-04-30 18:05:58 +08:00
|
|
|
|
def test_something():
|
|
|
|
|
pass
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
pytester.makeini(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2013-04-30 18:05:58 +08:00
|
|
|
|
[pytest]
|
|
|
|
|
python_files = *.py
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest("--collect-only")
|
2018-05-23 22:48:46 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
"""
|
2013-04-30 18:05:58 +08:00
|
|
|
|
<Module*>
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2013-12-12 13:41:48 +08:00
|
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_AssertionError_message(pytester: Pytester) -> None:
|
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2013-12-12 13:41:48 +08:00
|
|
|
|
def test_hello():
|
|
|
|
|
x,y = 1,2
|
|
|
|
|
assert 0, (x,y)
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest()
|
2018-05-23 22:48:46 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
"""
|
2013-12-12 13:41:48 +08:00
|
|
|
|
*def test_hello*
|
|
|
|
|
*assert 0, (x,y)*
|
|
|
|
|
*AssertionError: (1, 2)*
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2015-06-18 04:31:31 +08:00
|
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_diff_newline_at_end(pytester: Pytester) -> None:
|
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
r"""
|
2016-06-21 22:48:29 +08:00
|
|
|
|
def test_diff():
|
|
|
|
|
assert 'asdf' == 'asdf\n'
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2016-06-21 22:48:29 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest()
|
2018-05-23 22:48:46 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
r"""
|
2016-06-21 22:48:29 +08:00
|
|
|
|
*assert 'asdf' == 'asdf\n'
|
|
|
|
|
* - asdf
|
2020-02-04 21:38:18 +08:00
|
|
|
|
* ? -
|
2016-06-21 22:48:29 +08:00
|
|
|
|
* + asdf
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2016-06-26 00:45:47 +08:00
|
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
|
2018-09-02 08:58:48 +08:00
|
|
|
|
@pytest.mark.filterwarnings("default")
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_assert_tuple_warning(pytester: Pytester) -> None:
|
2018-09-05 00:41:11 +08:00
|
|
|
|
msg = "assertion is always true"
|
2020-10-30 09:39:44 +08:00
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2016-06-26 00:45:47 +08:00
|
|
|
|
def test_tuple():
|
|
|
|
|
assert(False, 'you shall not pass')
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest()
|
2020-10-03 04:16:22 +08:00
|
|
|
|
result.stdout.fnmatch_lines([f"*test_assert_tuple_warning.py:2:*{msg}*"])
|
2018-09-05 00:41:11 +08:00
|
|
|
|
|
|
|
|
|
# tuples with size != 2 should not trigger the warning
|
2020-10-30 09:39:44 +08:00
|
|
|
|
pytester.makepyfile(
|
2018-09-05 00:41:11 +08:00
|
|
|
|
"""
|
|
|
|
|
def test_tuple():
|
|
|
|
|
assert ()
|
|
|
|
|
"""
|
2018-05-23 22:48:46 +08:00
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest()
|
2018-09-05 00:41:11 +08:00
|
|
|
|
assert msg not in result.stdout.str()
|
2016-06-26 08:21:51 +08:00
|
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_assert_indirect_tuple_no_warning(pytester: Pytester) -> None:
|
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2016-06-26 08:21:51 +08:00
|
|
|
|
def test_tuple():
|
|
|
|
|
tpl = ('foo', 'bar')
|
|
|
|
|
assert tpl
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest()
|
2018-05-23 22:48:46 +08:00
|
|
|
|
output = "\n".join(result.stdout.lines)
|
|
|
|
|
assert "WR1" not in output
|
2016-08-26 00:04:14 +08:00
|
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_assert_with_unicode(pytester: Pytester) -> None:
|
|
|
|
|
pytester.makepyfile(
|
2019-06-03 06:40:34 +08:00
|
|
|
|
"""\
|
2016-08-26 00:04:14 +08:00
|
|
|
|
def test_unicode():
|
2019-06-05 08:48:06 +08:00
|
|
|
|
assert '유니코드' == 'Unicode'
|
2019-06-03 06:40:34 +08:00
|
|
|
|
"""
|
2018-05-23 22:48:46 +08:00
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest()
|
2018-05-23 22:48:46 +08:00
|
|
|
|
result.stdout.fnmatch_lines(["*AssertionError*"])
|
2016-09-20 03:16:04 +08:00
|
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_raise_unprintable_assertion_error(pytester: Pytester) -> None:
|
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
r"""
|
2017-03-04 16:26:46 +08:00
|
|
|
|
def test_raise_assertion_error():
|
|
|
|
|
raise AssertionError('\xff')
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest()
|
2018-05-23 22:48:46 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[r"> raise AssertionError('\xff')", "E AssertionError: *"]
|
|
|
|
|
)
|
2017-03-04 16:26:46 +08:00
|
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
|
2022-02-11 23:20:42 +08:00
|
|
|
|
def test_raise_assertion_error_raising_repr(pytester: Pytester) -> None:
|
2020-10-30 09:39:44 +08:00
|
|
|
|
pytester.makepyfile(
|
2019-06-03 06:32:00 +08:00
|
|
|
|
"""
|
2017-03-04 16:26:46 +08:00
|
|
|
|
class RaisingRepr(object):
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
raise Exception()
|
|
|
|
|
def test_raising_repr():
|
|
|
|
|
raise AssertionError(RaisingRepr())
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest()
|
2022-11-10 06:43:10 +08:00
|
|
|
|
result.stdout.fnmatch_lines(["E AssertionError: <exception str() failed>"])
|
2017-03-04 16:26:46 +08:00
|
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_issue_1944(pytester: Pytester) -> None:
|
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2016-09-20 03:16:04 +08:00
|
|
|
|
def f():
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
assert f() == 10
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest()
|
2016-09-20 03:16:04 +08:00
|
|
|
|
result.stdout.fnmatch_lines(["*1 error*"])
|
2018-06-26 21:35:27 +08:00
|
|
|
|
assert (
|
|
|
|
|
"AttributeError: 'Module' object has no attribute '_obj'"
|
|
|
|
|
not in result.stdout.str()
|
|
|
|
|
)
|
2019-03-22 19:44:32 +08:00
|
|
|
|
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_exit_from_assertrepr_compare(monkeypatch) -> None:
|
2019-03-22 19:44:32 +08:00
|
|
|
|
def raise_exit(obj):
|
|
|
|
|
outcomes.exit("Quitting debugger")
|
|
|
|
|
|
|
|
|
|
monkeypatch.setattr(util, "istext", raise_exit)
|
|
|
|
|
|
|
|
|
|
with pytest.raises(outcomes.Exit, match="Quitting debugger"):
|
|
|
|
|
callequal(1, 1)
|
2019-08-18 01:45:44 +08:00
|
|
|
|
|
|
|
|
|
|
2020-10-30 09:39:44 +08:00
|
|
|
|
def test_assertion_location_with_coverage(pytester: Pytester) -> None:
|
2019-08-18 01:45:44 +08:00
|
|
|
|
"""This used to report the wrong location when run with coverage (#5754)."""
|
2020-10-30 09:39:44 +08:00
|
|
|
|
p = pytester.makepyfile(
|
2019-08-18 01:45:44 +08:00
|
|
|
|
"""
|
|
|
|
|
def test():
|
|
|
|
|
assert False, 1
|
|
|
|
|
assert False, 2
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-30 09:39:44 +08:00
|
|
|
|
result = pytester.runpytest(str(p))
|
2019-08-18 01:45:44 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
|
|
|
|
"> assert False, 1",
|
|
|
|
|
"E AssertionError: 1",
|
|
|
|
|
"E assert False",
|
|
|
|
|
"*= 1 failed in*",
|
|
|
|
|
]
|
|
|
|
|
)
|
2022-03-19 19:55:39 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_reprcompare_verbose_long() -> None:
|
|
|
|
|
a = {f"v{i}": i for i in range(11)}
|
|
|
|
|
b = a.copy()
|
|
|
|
|
b["v2"] += 10
|
|
|
|
|
lines = callop("==", a, b, verbose=2)
|
|
|
|
|
assert lines is not None
|
|
|
|
|
assert lines[0] == (
|
|
|
|
|
"{'v0': 0, 'v1': 1, 'v2': 2, 'v3': 3, 'v4': 4, 'v5': 5, "
|
|
|
|
|
"'v6': 6, 'v7': 7, 'v8': 8, 'v9': 9, 'v10': 10}"
|
|
|
|
|
" == "
|
|
|
|
|
"{'v0': 0, 'v1': 1, 'v2': 12, 'v3': 3, 'v4': 4, 'v5': 5, "
|
|
|
|
|
"'v6': 6, 'v7': 7, 'v8': 8, 'v9': 9, 'v10': 10}"
|
|
|
|
|
)
|
2023-10-24 19:42:21 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("enable_colors", [True, False])
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
("test_code", "expected_lines"),
|
|
|
|
|
(
|
|
|
|
|
(
|
|
|
|
|
"""
|
|
|
|
|
def test():
|
|
|
|
|
assert [0, 1] == [0, 2]
|
|
|
|
|
""",
|
|
|
|
|
[
|
2023-11-27 22:47:18 +08:00
|
|
|
|
"{bold}{red}E {light-red}- 2,{hl-reset}{endline}{reset}",
|
|
|
|
|
"{bold}{red}E {light-green}+ 1,{hl-reset}{endline}{reset}",
|
2023-10-24 19:42:21 +08:00
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
"""
|
|
|
|
|
def test():
|
|
|
|
|
assert {f"number-is-{i}": i for i in range(1, 6)} == {
|
|
|
|
|
f"number-is-{i}": i for i in range(5)
|
|
|
|
|
}
|
|
|
|
|
""",
|
|
|
|
|
[
|
|
|
|
|
"{bold}{red}E {light-gray} {hl-reset} {{{endline}{reset}",
|
2023-11-27 22:47:18 +08:00
|
|
|
|
"{bold}{red}E {light-gray} {hl-reset} 'number-is-1': 1,{endline}{reset}",
|
|
|
|
|
"{bold}{red}E {light-green}+ 'number-is-5': 5,{hl-reset}{endline}{reset}",
|
2023-10-24 19:42:21 +08:00
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
def test_comparisons_handle_colors(
|
|
|
|
|
pytester: Pytester, color_mapping, enable_colors, test_code, expected_lines
|
|
|
|
|
) -> None:
|
|
|
|
|
p = pytester.makepyfile(test_code)
|
|
|
|
|
result = pytester.runpytest(
|
|
|
|
|
f"--color={'yes' if enable_colors else 'no'}", "-vv", str(p)
|
|
|
|
|
)
|
|
|
|
|
formatter = (
|
|
|
|
|
color_mapping.format_for_fnmatch
|
|
|
|
|
if enable_colors
|
|
|
|
|
else color_mapping.strip_colors
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
result.stdout.fnmatch_lines(formatter(expected_lines), consecutive=False)
|
2023-11-19 22:56:29 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_fine_grained_assertion_verbosity(pytester: Pytester):
|
|
|
|
|
long_text = "Lorem ipsum dolor sit amet " * 10
|
|
|
|
|
p = pytester.makepyfile(
|
|
|
|
|
f"""
|
|
|
|
|
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 = "{long_text}"
|
|
|
|
|
assert "hello world" in long_text
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
pytester.makeini(
|
|
|
|
|
"""
|
|
|
|
|
[pytest]
|
|
|
|
|
verbosity_assertions = 2
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
result = pytester.runpytest(p)
|
|
|
|
|
|
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
|
|
|
|
f"{p.name} .FFF [100%]",
|
|
|
|
|
"E At index 2 diff: 'grapes' != 'orange'",
|
|
|
|
|
"E Full diff:",
|
2023-11-27 22:47:18 +08:00
|
|
|
|
"E [",
|
|
|
|
|
"E 'banana',",
|
|
|
|
|
"E 'apple',",
|
|
|
|
|
"E - 'orange',",
|
|
|
|
|
"E ? ^ ^^",
|
|
|
|
|
"E + 'grapes',",
|
|
|
|
|
"E ? ^ ^ +",
|
|
|
|
|
"E 'melon',",
|
|
|
|
|
"E 'kiwi',",
|
|
|
|
|
"E ]",
|
2023-11-19 22:56:29 +08:00
|
|
|
|
"E Full diff:",
|
2023-11-27 22:47:18 +08:00
|
|
|
|
"E {",
|
|
|
|
|
"E '0': 0,",
|
|
|
|
|
"E - '10': 10,",
|
|
|
|
|
"E ? - -",
|
|
|
|
|
"E + '1': 1,",
|
|
|
|
|
"E - '20': 20,",
|
|
|
|
|
"E ? - -",
|
|
|
|
|
"E + '2': 2,",
|
|
|
|
|
"E - '30': 30,",
|
|
|
|
|
"E ? - -",
|
|
|
|
|
"E + '3': 3,",
|
|
|
|
|
"E - '40': 40,",
|
|
|
|
|
"E ? - -",
|
|
|
|
|
"E + '4': 4,",
|
|
|
|
|
"E }",
|
2023-11-19 22:56:29 +08:00
|
|
|
|
f"E AssertionError: assert 'hello world' in '{long_text}'",
|
|
|
|
|
]
|
|
|
|
|
)
|