1067 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			1067 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			Python
		
	
	
	
| # -*- coding: utf-8 -*-
 | |
| from __future__ import absolute_import, division, print_function
 | |
| import sys
 | |
| import textwrap
 | |
| 
 | |
| import _pytest.assertion as plugin
 | |
| import py
 | |
| import pytest
 | |
| from _pytest.assertion import util
 | |
| from _pytest.assertion import truncate
 | |
| 
 | |
| PY3 = sys.version_info >= (3, 0)
 | |
| 
 | |
| 
 | |
| @pytest.fixture
 | |
| def mock_config():
 | |
| 
 | |
|     class Config(object):
 | |
|         verbose = False
 | |
| 
 | |
|         def getoption(self, name):
 | |
|             if name == 'verbose':
 | |
|                 return self.verbose
 | |
|             raise KeyError('Not mocked out: %s' % name)
 | |
| 
 | |
|     return Config()
 | |
| 
 | |
| 
 | |
| class TestImportHookInstallation(object):
 | |
| 
 | |
|     @pytest.mark.parametrize('initial_conftest', [True, False])
 | |
|     @pytest.mark.parametrize('mode', ['plain', 'rewrite'])
 | |
|     def test_conftest_assertion_rewrite(self, testdir, initial_conftest, mode):
 | |
|         """Test that conftest files are using assertion rewrite on import.
 | |
|         (#1619)
 | |
|         """
 | |
|         testdir.tmpdir.join('foo/tests').ensure(dir=1)
 | |
|         conftest_path = 'conftest.py' if initial_conftest else 'foo/conftest.py'
 | |
|         contents = {
 | |
|             conftest_path: """
 | |
|                 import pytest
 | |
|                 @pytest.fixture
 | |
|                 def check_first():
 | |
|                     def check(values, value):
 | |
|                         assert values.pop(0) == value
 | |
|                     return check
 | |
|             """,
 | |
|             'foo/tests/test_foo.py': """
 | |
|                 def test(check_first):
 | |
|                     check_first([10, 30], 30)
 | |
|             """
 | |
|         }
 | |
|         testdir.makepyfile(**contents)
 | |
|         result = testdir.runpytest_subprocess('--assert=%s' % mode)
 | |
|         if mode == 'plain':
 | |
|             expected = 'E       AssertionError'
 | |
|         elif mode == 'rewrite':
 | |
|             expected = '*assert 10 == 30*'
 | |
|         else:
 | |
|             assert 0
 | |
|         result.stdout.fnmatch_lines([expected])
 | |
| 
 | |
|     def test_rewrite_assertions_pytester_plugin(self, testdir):
 | |
|         """
 | |
|         Assertions in the pytester plugin must also benefit from assertion
 | |
|         rewriting (#1920).
 | |
|         """
 | |
|         testdir.makepyfile("""
 | |
|             pytest_plugins = ['pytester']
 | |
|             def test_dummy_failure(testdir):  # how meta!
 | |
|                 testdir.makepyfile('def test(): assert 0')
 | |
|                 r = testdir.inline_run()
 | |
|                 r.assertoutcome(passed=1)
 | |
|         """)
 | |
|         result = testdir.runpytest_subprocess()
 | |
|         result.stdout.fnmatch_lines([
 | |
|             '*assert 1 == 0*',
 | |
|         ])
 | |
| 
 | |
|     @pytest.mark.parametrize('mode', ['plain', 'rewrite'])
 | |
|     def test_pytest_plugins_rewrite(self, testdir, mode):
 | |
|         contents = {
 | |
|             'conftest.py': """
 | |
|                 pytest_plugins = ['ham']
 | |
|             """,
 | |
|             'ham.py': """
 | |
|                 import pytest
 | |
|                 @pytest.fixture
 | |
|                 def check_first():
 | |
|                     def check(values, value):
 | |
|                         assert values.pop(0) == value
 | |
|                     return check
 | |
|             """,
 | |
|             'test_foo.py': """
 | |
|                 def test_foo(check_first):
 | |
|                     check_first([10, 30], 30)
 | |
|             """,
 | |
|         }
 | |
|         testdir.makepyfile(**contents)
 | |
|         result = testdir.runpytest_subprocess('--assert=%s' % mode)
 | |
|         if mode == 'plain':
 | |
|             expected = 'E       AssertionError'
 | |
|         elif mode == 'rewrite':
 | |
|             expected = '*assert 10 == 30*'
 | |
|         else:
 | |
|             assert 0
 | |
|         result.stdout.fnmatch_lines([expected])
 | |
| 
 | |
|     @pytest.mark.parametrize('mode', ['str', 'list'])
 | |
|     def test_pytest_plugins_rewrite_module_names(self, testdir, mode):
 | |
|         """Test that pluginmanager correct marks pytest_plugins variables
 | |
|         for assertion rewriting if they are defined as plain strings or
 | |
|         list of strings (#1888).
 | |
|         """
 | |
|         plugins = '"ham"' if mode == 'str' else '["ham"]'
 | |
|         contents = {
 | |
|             'conftest.py': """
 | |
|                 pytest_plugins = {plugins}
 | |
|             """.format(plugins=plugins),
 | |
|             'ham.py': """
 | |
|                 import pytest
 | |
|             """,
 | |
|             'test_foo.py': """
 | |
|                 def test_foo(pytestconfig):
 | |
|                     assert 'ham' in pytestconfig.pluginmanager.rewrite_hook._must_rewrite
 | |
|             """,
 | |
|         }
 | |
|         testdir.makepyfile(**contents)
 | |
|         result = testdir.runpytest_subprocess('--assert=rewrite')
 | |
|         assert result.ret == 0
 | |
| 
 | |
|     def test_pytest_plugins_rewrite_module_names_correctly(self, testdir):
 | |
|         """Test that we match files correctly when they are marked for rewriting (#2939)."""
 | |
|         contents = {
 | |
|             'conftest.py': """
 | |
|                 pytest_plugins = "ham"
 | |
|             """,
 | |
|             'ham.py': "",
 | |
|             'hamster.py': "",
 | |
|             'test_foo.py': """
 | |
|                 def test_foo(pytestconfig):
 | |
|                     assert pytestconfig.pluginmanager.rewrite_hook.find_module('ham') is not None
 | |
|                     assert pytestconfig.pluginmanager.rewrite_hook.find_module('hamster') is None
 | |
|             """,
 | |
|         }
 | |
|         testdir.makepyfile(**contents)
 | |
|         result = testdir.runpytest_subprocess('--assert=rewrite')
 | |
|         assert result.ret == 0
 | |
| 
 | |
|     @pytest.mark.parametrize('mode', ['plain', 'rewrite'])
 | |
|     @pytest.mark.parametrize('plugin_state', ['development', 'installed'])
 | |
|     def test_installed_plugin_rewrite(self, testdir, mode, plugin_state):
 | |
|         # Make sure the hook is installed early enough so that plugins
 | |
|         # installed via setuptools are rewritten.
 | |
|         testdir.tmpdir.join('hampkg').ensure(dir=1)
 | |
|         contents = {
 | |
|             'hampkg/__init__.py': """
 | |
|                 import pytest
 | |
| 
 | |
|                 @pytest.fixture
 | |
|                 def check_first2():
 | |
|                     def check(values, value):
 | |
|                         assert values.pop(0) == value
 | |
|                     return check
 | |
|             """,
 | |
|             'spamplugin.py': """
 | |
|             import pytest
 | |
|             from hampkg import check_first2
 | |
| 
 | |
|             @pytest.fixture
 | |
|             def check_first():
 | |
|                 def check(values, value):
 | |
|                     assert values.pop(0) == value
 | |
|                 return check
 | |
|             """,
 | |
|             'mainwrapper.py': """
 | |
|             import pytest, pkg_resources
 | |
| 
 | |
|             plugin_state = "{plugin_state}"
 | |
| 
 | |
|             class DummyDistInfo(object):
 | |
|                 project_name = 'spam'
 | |
|                 version = '1.0'
 | |
| 
 | |
|                 def _get_metadata(self, name):
 | |
|                     # 'RECORD' meta-data only available in installed plugins
 | |
|                     if name == 'RECORD' and plugin_state == "installed":
 | |
|                         return ['spamplugin.py,sha256=abc,123',
 | |
|                                 'hampkg/__init__.py,sha256=abc,123']
 | |
|                     # 'SOURCES.txt' meta-data only available for plugins in development mode
 | |
|                     elif name == 'SOURCES.txt' and plugin_state == "development":
 | |
|                         return ['spamplugin.py',
 | |
|                                 'hampkg/__init__.py']
 | |
|                     return []
 | |
| 
 | |
|             class DummyEntryPoint(object):
 | |
|                 name = 'spam'
 | |
|                 module_name = 'spam.py'
 | |
|                 attrs = ()
 | |
|                 extras = None
 | |
|                 dist = DummyDistInfo()
 | |
| 
 | |
|                 def load(self, require=True, *args, **kwargs):
 | |
|                     import spamplugin
 | |
|                     return spamplugin
 | |
| 
 | |
|             def iter_entry_points(name):
 | |
|                 yield DummyEntryPoint()
 | |
| 
 | |
|             pkg_resources.iter_entry_points = iter_entry_points
 | |
|             pytest.main()
 | |
|             """.format(plugin_state=plugin_state),
 | |
|             'test_foo.py': """
 | |
|             def test(check_first):
 | |
|                 check_first([10, 30], 30)
 | |
| 
 | |
|             def test2(check_first2):
 | |
|                 check_first([10, 30], 30)
 | |
|             """,
 | |
|         }
 | |
|         testdir.makepyfile(**contents)
 | |
|         result = testdir.run(sys.executable, 'mainwrapper.py', '-s', '--assert=%s' % mode)
 | |
|         if mode == 'plain':
 | |
|             expected = 'E       AssertionError'
 | |
|         elif mode == 'rewrite':
 | |
|             expected = '*assert 10 == 30*'
 | |
|         else:
 | |
|             assert 0
 | |
|         result.stdout.fnmatch_lines([expected])
 | |
| 
 | |
|     def test_rewrite_ast(self, testdir):
 | |
|         testdir.tmpdir.join('pkg').ensure(dir=1)
 | |
|         contents = {
 | |
|             'pkg/__init__.py': """
 | |
|                 import pytest
 | |
|                 pytest.register_assert_rewrite('pkg.helper')
 | |
|             """,
 | |
|             'pkg/helper.py': """
 | |
|                 def tool():
 | |
|                     a, b = 2, 3
 | |
|                     assert a == b
 | |
|             """,
 | |
|             'pkg/plugin.py': """
 | |
|                 import pytest, pkg.helper
 | |
|                 @pytest.fixture
 | |
|                 def tool():
 | |
|                     return pkg.helper.tool
 | |
|             """,
 | |
|             'pkg/other.py': """
 | |
|                 values = [3, 2]
 | |
|                 def tool():
 | |
|                     assert values.pop() == 3
 | |
|             """,
 | |
|             'conftest.py': """
 | |
|                 pytest_plugins = ['pkg.plugin']
 | |
|             """,
 | |
|             'test_pkg.py': """
 | |
|                 import pkg.other
 | |
|                 def test_tool(tool):
 | |
|                     tool()
 | |
|                 def test_other():
 | |
|                     pkg.other.tool()
 | |
|             """,
 | |
|         }
 | |
|         testdir.makepyfile(**contents)
 | |
|         result = testdir.runpytest_subprocess('--assert=rewrite')
 | |
|         result.stdout.fnmatch_lines(['>*assert a == b*',
 | |
|                                      'E*assert 2 == 3*',
 | |
|                                      '>*assert values.pop() == 3*',
 | |
|                                      'E*AssertionError'])
 | |
| 
 | |
|     def test_register_assert_rewrite_checks_types(self):
 | |
|         with pytest.raises(TypeError):
 | |
|             pytest.register_assert_rewrite(['pytest_tests_internal_non_existing'])
 | |
|         pytest.register_assert_rewrite('pytest_tests_internal_non_existing',
 | |
|                                        'pytest_tests_internal_non_existing2')
 | |
| 
 | |
| 
 | |
| class TestBinReprIntegration(object):
 | |
| 
 | |
|     def test_pytest_assertrepr_compare_called(self, testdir):
 | |
|         testdir.makeconftest("""
 | |
|             import pytest
 | |
|             values = []
 | |
|             def pytest_assertrepr_compare(op, left, right):
 | |
|                 values.append((op, left, right))
 | |
| 
 | |
|             @pytest.fixture
 | |
|             def list(request):
 | |
|                 return values
 | |
|         """)
 | |
|         testdir.makepyfile("""
 | |
|             def test_hello():
 | |
|                 assert 0 == 1
 | |
|             def test_check(list):
 | |
|                 assert list == [("==", 0, 1)]
 | |
|         """)
 | |
|         result = testdir.runpytest("-v")
 | |
|         result.stdout.fnmatch_lines([
 | |
|             "*test_hello*FAIL*",
 | |
|             "*test_check*PASS*",
 | |
|         ])
 | |
| 
 | |
| 
 | |
| def callequal(left, right, verbose=False):
 | |
|     config = mock_config()
 | |
|     config.verbose = verbose
 | |
|     return plugin.pytest_assertrepr_compare(config, '==', left, right)
 | |
| 
 | |
| 
 | |
| class TestAssert_reprcompare(object):
 | |
|     def test_different_types(self):
 | |
|         assert callequal([0, 1], 'foo') is None
 | |
| 
 | |
|     def test_summary(self):
 | |
|         summary = callequal([0, 1], [0, 2])[0]
 | |
|         assert len(summary) < 65
 | |
| 
 | |
|     def test_text_diff(self):
 | |
|         diff = callequal('spam', 'eggs')[1:]
 | |
|         assert '- spam' in diff
 | |
|         assert '+ eggs' in diff
 | |
| 
 | |
|     def test_text_skipping(self):
 | |
|         lines = callequal('a' * 50 + 'spam', 'a' * 50 + 'eggs')
 | |
|         assert 'Skipping' in lines[1]
 | |
|         for line in lines:
 | |
|             assert 'a' * 50 not in line
 | |
| 
 | |
|     def test_text_skipping_verbose(self):
 | |
|         lines = callequal('a' * 50 + 'spam', 'a' * 50 + 'eggs', verbose=True)
 | |
|         assert '- ' + 'a' * 50 + 'spam' in lines
 | |
|         assert '+ ' + 'a' * 50 + 'eggs' in lines
 | |
| 
 | |
|     def test_multiline_text_diff(self):
 | |
|         left = 'foo\nspam\nbar'
 | |
|         right = 'foo\neggs\nbar'
 | |
|         diff = callequal(left, right)
 | |
|         assert '- spam' in diff
 | |
|         assert '+ eggs' in diff
 | |
| 
 | |
|     def test_list(self):
 | |
|         expl = callequal([0, 1], [0, 2])
 | |
|         assert len(expl) > 1
 | |
| 
 | |
|     @pytest.mark.parametrize(
 | |
|         ['left', 'right', 'expected'], [
 | |
|             ([0, 1], [0, 2], """
 | |
|                 Full diff:
 | |
|                 - [0, 1]
 | |
|                 ?     ^
 | |
|                 + [0, 2]
 | |
|                 ?     ^
 | |
|             """),
 | |
|             ({0: 1}, {0: 2}, """
 | |
|                 Full diff:
 | |
|                 - {0: 1}
 | |
|                 ?     ^
 | |
|                 + {0: 2}
 | |
|                 ?     ^
 | |
|             """),
 | |
|             (set([0, 1]), set([0, 2]), """
 | |
|                 Full diff:
 | |
|                 - set([0, 1])
 | |
|                 ?         ^
 | |
|                 + set([0, 2])
 | |
|                 ?         ^
 | |
|             """ if not PY3 else """
 | |
|                 Full diff:
 | |
|                 - {0, 1}
 | |
|                 ?     ^
 | |
|                 + {0, 2}
 | |
|                 ?     ^
 | |
|             """)
 | |
|         ]
 | |
|     )
 | |
|     def test_iterable_full_diff(self, left, right, expected):
 | |
|         """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.
 | |
|         """
 | |
|         expl = callequal(left, right, verbose=False)
 | |
|         assert expl[-1] == 'Use -v to get the full diff'
 | |
|         expl = '\n'.join(callequal(left, right, verbose=True))
 | |
|         assert expl.endswith(textwrap.dedent(expected).strip())
 | |
| 
 | |
|     def test_list_different_lengths(self):
 | |
|         expl = callequal([0, 1], [0, 1, 2])
 | |
|         assert len(expl) > 1
 | |
|         expl = callequal([0, 1, 2], [0, 1])
 | |
|         assert len(expl) > 1
 | |
| 
 | |
|     def test_dict(self):
 | |
|         expl = callequal({'a': 0}, {'a': 1})
 | |
|         assert len(expl) > 1
 | |
| 
 | |
|     def test_dict_omitting(self):
 | |
|         lines = callequal({'a': 0, 'b': 1}, {'a': 1, 'b': 1})
 | |
|         assert lines[1].startswith('Omitting 1 identical item')
 | |
|         assert 'Common items' not in lines
 | |
|         for line in lines[1:]:
 | |
|             assert 'b' not in line
 | |
| 
 | |
|     def test_dict_omitting_with_verbosity_1(self):
 | |
|         """ Ensure differing items are visible for verbosity=1 (#1512) """
 | |
|         lines = callequal({'a': 0, 'b': 1}, {'a': 1, 'b': 1}, verbose=1)
 | |
|         assert lines[1].startswith('Omitting 1 identical item')
 | |
|         assert lines[2].startswith('Differing items')
 | |
|         assert lines[3] == "{'a': 0} != {'a': 1}"
 | |
|         assert 'Common items' not in lines
 | |
| 
 | |
|     def test_dict_omitting_with_verbosity_2(self):
 | |
|         lines = callequal({'a': 0, 'b': 1}, {'a': 1, 'b': 1}, verbose=2)
 | |
|         assert lines[1].startswith('Common items:')
 | |
|         assert 'Omitting' not in lines[1]
 | |
|         assert lines[2] == "{'b': 1}"
 | |
| 
 | |
|     def test_set(self):
 | |
|         expl = callequal(set([0, 1]), set([0, 2]))
 | |
|         assert len(expl) > 1
 | |
| 
 | |
|     def test_frozenzet(self):
 | |
|         expl = callequal(frozenset([0, 1]), set([0, 2]))
 | |
|         assert len(expl) > 1
 | |
| 
 | |
|     def test_Sequence(self):
 | |
|         col = py.builtin._tryimport(
 | |
|             "collections.abc",
 | |
|             "collections",
 | |
|             "sys")
 | |
|         if not hasattr(col, "MutableSequence"):
 | |
|             pytest.skip("cannot import MutableSequence")
 | |
|         MutableSequence = col.MutableSequence
 | |
| 
 | |
|         class TestSequence(MutableSequence):  # works with a Sequence subclass
 | |
|             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]))
 | |
|         assert len(expl) > 1
 | |
| 
 | |
|     def test_list_tuples(self):
 | |
|         expl = callequal([], [(1, 2)])
 | |
|         assert len(expl) > 1
 | |
|         expl = callequal([(1, 2)], [])
 | |
|         assert len(expl) > 1
 | |
| 
 | |
|     def test_list_bad_repr(self):
 | |
|         class A(object):
 | |
|             def __repr__(self):
 | |
|                 raise ValueError(42)
 | |
|         expl = callequal([], [A()])
 | |
|         assert 'ValueError' in "".join(expl)
 | |
|         expl = callequal({}, {'1': A()})
 | |
|         assert 'faulty' in "".join(expl)
 | |
| 
 | |
|     def test_one_repr_empty(self):
 | |
|         """
 | |
|         the faulty empty string repr did trigger
 | |
|         a unbound local error in _diff_text
 | |
|         """
 | |
|         class A(str):
 | |
|             def __repr__(self):
 | |
|                 return ''
 | |
|         expl = callequal(A(), '')
 | |
|         assert not expl
 | |
| 
 | |
|     def test_repr_no_exc(self):
 | |
|         expl = ' '.join(callequal('foo', 'bar'))
 | |
|         assert 'raised in repr()' not in expl
 | |
| 
 | |
|     def test_unicode(self):
 | |
|         left = py.builtin._totext('£€', 'utf-8')
 | |
|         right = py.builtin._totext('£', 'utf-8')
 | |
|         expl = callequal(left, right)
 | |
|         assert expl[0] == py.builtin._totext("'£€' == '£'", 'utf-8')
 | |
|         assert expl[1] == py.builtin._totext('- £€', 'utf-8')
 | |
|         assert expl[2] == py.builtin._totext('+ £', 'utf-8')
 | |
| 
 | |
|     def test_nonascii_text(self):
 | |
|         """
 | |
|         :issue: 877
 | |
|         non ascii python2 str caused a UnicodeDecodeError
 | |
|         """
 | |
|         class A(str):
 | |
|             def __repr__(self):
 | |
|                 return '\xff'
 | |
|         expl = callequal(A(), '1')
 | |
|         assert expl
 | |
| 
 | |
|     def test_format_nonascii_explanation(self):
 | |
|         assert util.format_explanation('λ')
 | |
| 
 | |
|     def test_mojibake(self):
 | |
|         # issue 429
 | |
|         left = 'e'
 | |
|         right = '\xc3\xa9'
 | |
|         if not isinstance(left, py.builtin.bytes):
 | |
|             left = py.builtin.bytes(left, 'utf-8')
 | |
|             right = py.builtin.bytes(right, 'utf-8')
 | |
|         expl = callequal(left, right)
 | |
|         for line in expl:
 | |
|             assert isinstance(line, py.builtin.text)
 | |
|         msg = py.builtin._totext('\n').join(expl)
 | |
|         assert msg
 | |
| 
 | |
| 
 | |
| class TestFormatExplanation(object):
 | |
| 
 | |
|     def test_special_chars_full(self, testdir):
 | |
|         # Issue 453, for the bug this would raise IndexError
 | |
|         testdir.makepyfile("""
 | |
|             def test_foo():
 | |
|                 assert '\\n}' == ''
 | |
|         """)
 | |
|         result = testdir.runpytest()
 | |
|         assert result.ret == 1
 | |
|         result.stdout.fnmatch_lines([
 | |
|             "*AssertionError*",
 | |
|         ])
 | |
| 
 | |
|     def test_fmt_simple(self):
 | |
|         expl = 'assert foo'
 | |
|         assert util.format_explanation(expl) == 'assert foo'
 | |
| 
 | |
|     def test_fmt_where(self):
 | |
|         expl = '\n'.join(['assert 1',
 | |
|                           '{1 = foo',
 | |
|                           '} == 2'])
 | |
|         res = '\n'.join(['assert 1 == 2',
 | |
|                          ' +  where 1 = foo'])
 | |
|         assert util.format_explanation(expl) == res
 | |
| 
 | |
|     def test_fmt_and(self):
 | |
|         expl = '\n'.join(['assert 1',
 | |
|                           '{1 = foo',
 | |
|                           '} == 2',
 | |
|                           '{2 = bar',
 | |
|                           '}'])
 | |
|         res = '\n'.join(['assert 1 == 2',
 | |
|                          ' +  where 1 = foo',
 | |
|                          ' +  and   2 = bar'])
 | |
|         assert util.format_explanation(expl) == res
 | |
| 
 | |
|     def test_fmt_where_nested(self):
 | |
|         expl = '\n'.join(['assert 1',
 | |
|                           '{1 = foo',
 | |
|                           '{foo = bar',
 | |
|                           '}',
 | |
|                           '} == 2'])
 | |
|         res = '\n'.join(['assert 1 == 2',
 | |
|                          ' +  where 1 = foo',
 | |
|                          ' +    where foo = bar'])
 | |
|         assert util.format_explanation(expl) == res
 | |
| 
 | |
|     def test_fmt_newline(self):
 | |
|         expl = '\n'.join(['assert "foo" == "bar"',
 | |
|                           '~- foo',
 | |
|                           '~+ bar'])
 | |
|         res = '\n'.join(['assert "foo" == "bar"',
 | |
|                          '  - foo',
 | |
|                          '  + bar'])
 | |
|         assert util.format_explanation(expl) == res
 | |
| 
 | |
|     def test_fmt_newline_escaped(self):
 | |
|         expl = '\n'.join(['assert foo == bar',
 | |
|                           'baz'])
 | |
|         res = 'assert foo == bar\\nbaz'
 | |
|         assert util.format_explanation(expl) == res
 | |
| 
 | |
|     def test_fmt_newline_before_where(self):
 | |
|         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'])
 | |
|         assert util.format_explanation(expl) == res
 | |
| 
 | |
|     def test_fmt_multi_newline_before_where(self):
 | |
|         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'])
 | |
|         assert util.format_explanation(expl) == res
 | |
| 
 | |
| 
 | |
| class TestTruncateExplanation(object):
 | |
| 
 | |
|     """ Confirm assertion output is truncated as expected """
 | |
| 
 | |
|     # The number of lines in the truncation explanation message. Used
 | |
|     # to calculate that results have the expected length.
 | |
|     LINES_IN_TRUNCATION_MSG = 2
 | |
| 
 | |
|     def test_doesnt_truncate_when_input_is_empty_list(self):
 | |
|         expl = []
 | |
|         result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100)
 | |
|         assert result == expl
 | |
| 
 | |
|     def test_doesnt_truncate_at_when_input_is_5_lines_and_LT_max_chars(self):
 | |
|         expl = ['a' * 100 for x in range(5)]
 | |
|         result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80)
 | |
|         assert result == expl
 | |
| 
 | |
|     def test_truncates_at_8_lines_when_given_list_of_empty_strings(self):
 | |
|         expl = ['' for x in range(50)]
 | |
|         result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100)
 | |
|         assert result != expl
 | |
|         assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG
 | |
|         assert "Full output truncated" in result[-1]
 | |
|         assert "43 lines hidden" in result[-1]
 | |
|         last_line_before_trunc_msg = result[- self.LINES_IN_TRUNCATION_MSG - 1]
 | |
|         assert last_line_before_trunc_msg.endswith("...")
 | |
| 
 | |
|     def test_truncates_at_8_lines_when_first_8_lines_are_LT_max_chars(self):
 | |
|         expl = ['a' for x in range(100)]
 | |
|         result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80)
 | |
|         assert result != expl
 | |
|         assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG
 | |
|         assert "Full output truncated" in result[-1]
 | |
|         assert "93 lines hidden" in result[-1]
 | |
|         last_line_before_trunc_msg = result[- self.LINES_IN_TRUNCATION_MSG - 1]
 | |
|         assert last_line_before_trunc_msg.endswith("...")
 | |
| 
 | |
|     def test_truncates_at_8_lines_when_first_8_lines_are_EQ_max_chars(self):
 | |
|         expl = ['a' * 80 for x in range(16)]
 | |
|         result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80)
 | |
|         assert result != expl
 | |
|         assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG
 | |
|         assert "Full output truncated" in result[-1]
 | |
|         assert "9 lines hidden" in result[-1]
 | |
|         last_line_before_trunc_msg = result[- self.LINES_IN_TRUNCATION_MSG - 1]
 | |
|         assert last_line_before_trunc_msg.endswith("...")
 | |
| 
 | |
|     def test_truncates_at_4_lines_when_first_4_lines_are_GT_max_chars(self):
 | |
|         expl = ['a' * 250 for x in range(10)]
 | |
|         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]
 | |
|         last_line_before_trunc_msg = result[- self.LINES_IN_TRUNCATION_MSG - 1]
 | |
|         assert last_line_before_trunc_msg.endswith("...")
 | |
| 
 | |
|     def test_truncates_at_1_line_when_first_line_is_GT_max_chars(self):
 | |
|         expl = ['a' * 250 for x in range(1000)]
 | |
|         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]
 | |
|         last_line_before_trunc_msg = result[- self.LINES_IN_TRUNCATION_MSG - 1]
 | |
|         assert last_line_before_trunc_msg.endswith("...")
 | |
| 
 | |
|     def test_full_output_truncated(self, monkeypatch, testdir):
 | |
|         """ Test against full runpytest() output. """
 | |
| 
 | |
|         line_count = 7
 | |
|         line_len = 100
 | |
|         expected_truncated_lines = 2
 | |
|         testdir.makepyfile(r"""
 | |
|             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
 | |
|         """ % (line_len, line_count))
 | |
|         monkeypatch.delenv('CI', raising=False)
 | |
| 
 | |
|         result = testdir.runpytest()
 | |
|         # without -vv, truncate the message showing a few diff lines only
 | |
|         result.stdout.fnmatch_lines([
 | |
|             "*- 1*",
 | |
|             "*- 3*",
 | |
|             "*- 5*",
 | |
|             "*truncated (%d lines hidden)*use*-vv*" % expected_truncated_lines,
 | |
|         ])
 | |
| 
 | |
|         result = testdir.runpytest('-vv')
 | |
|         result.stdout.fnmatch_lines([
 | |
|             "* 6*",
 | |
|         ])
 | |
| 
 | |
|         monkeypatch.setenv('CI', '1')
 | |
|         result = testdir.runpytest()
 | |
|         result.stdout.fnmatch_lines([
 | |
|             "* 6*",
 | |
|         ])
 | |
| 
 | |
| 
 | |
| def test_python25_compile_issue257(testdir):
 | |
|     testdir.makepyfile("""
 | |
|         def test_rewritten():
 | |
|             assert 1 == 2
 | |
|         # some comment
 | |
|     """)
 | |
|     result = testdir.runpytest()
 | |
|     assert result.ret == 1
 | |
|     result.stdout.fnmatch_lines("""
 | |
|             *E*assert 1 == 2*
 | |
|             *1 failed*
 | |
|     """)
 | |
| 
 | |
| 
 | |
| def test_rewritten(testdir):
 | |
|     testdir.makepyfile("""
 | |
|         def test_rewritten():
 | |
|             assert "@py_builtins" in globals()
 | |
|     """)
 | |
|     assert testdir.runpytest().ret == 0
 | |
| 
 | |
| 
 | |
| def test_reprcompare_notin(mock_config):
 | |
|     detail = plugin.pytest_assertrepr_compare(
 | |
|         mock_config, 'not in', 'foo', 'aaafoobbb')[1:]
 | |
|     assert detail == ["'foo' is contained here:", '  aaafoobbb', '?    +++']
 | |
| 
 | |
| 
 | |
| def test_pytest_assertrepr_compare_integration(testdir):
 | |
|     testdir.makepyfile("""
 | |
|         def test_hello():
 | |
|             x = set(range(100))
 | |
|             y = x.copy()
 | |
|             y.remove(50)
 | |
|             assert x == y
 | |
|     """)
 | |
|     result = testdir.runpytest()
 | |
|     result.stdout.fnmatch_lines([
 | |
|         "*def test_hello():*",
 | |
|         "*assert x == y*",
 | |
|         "*E*Extra items*left*",
 | |
|         "*E*50*",
 | |
|     ])
 | |
| 
 | |
| 
 | |
| def test_sequence_comparison_uses_repr(testdir):
 | |
|     testdir.makepyfile("""
 | |
|         def test_hello():
 | |
|             x = set("hello x")
 | |
|             y = set("hello y")
 | |
|             assert x == y
 | |
|     """)
 | |
|     result = testdir.runpytest()
 | |
|     result.stdout.fnmatch_lines([
 | |
|         "*def test_hello():*",
 | |
|         "*assert x == y*",
 | |
|         "*E*Extra items*left*",
 | |
|         "*E*'x'*",
 | |
|         "*E*Extra items*right*",
 | |
|         "*E*'y'*",
 | |
|     ])
 | |
| 
 | |
| 
 | |
| def test_assertrepr_loaded_per_dir(testdir):
 | |
|     testdir.makepyfile(test_base=['def test_base(): assert 1 == 2'])
 | |
|     a = testdir.mkdir('a')
 | |
|     a_test = a.join('test_a.py')
 | |
|     a_test.write('def test_a(): assert 1 == 2')
 | |
|     a_conftest = a.join('conftest.py')
 | |
|     a_conftest.write('def pytest_assertrepr_compare(): return ["summary a"]')
 | |
|     b = testdir.mkdir('b')
 | |
|     b_test = b.join('test_b.py')
 | |
|     b_test.write('def test_b(): assert 1 == 2')
 | |
|     b_conftest = b.join('conftest.py')
 | |
|     b_conftest.write('def pytest_assertrepr_compare(): return ["summary b"]')
 | |
|     result = testdir.runpytest()
 | |
|     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*'])
 | |
| 
 | |
| 
 | |
| def test_assertion_options(testdir):
 | |
|     testdir.makepyfile("""
 | |
|         def test_hello():
 | |
|             x = 3
 | |
|             assert x == 4
 | |
|     """)
 | |
|     result = testdir.runpytest()
 | |
|     assert "3 == 4" in result.stdout.str()
 | |
|     result = testdir.runpytest_subprocess("--assert=plain")
 | |
|     assert "3 == 4" not in result.stdout.str()
 | |
| 
 | |
| 
 | |
| def test_triple_quoted_string_issue113(testdir):
 | |
|     testdir.makepyfile("""
 | |
|         def test_hello():
 | |
|             assert "" == '''
 | |
|     '''""")
 | |
|     result = testdir.runpytest("--fulltrace")
 | |
|     result.stdout.fnmatch_lines([
 | |
|         "*1 failed*",
 | |
|     ])
 | |
|     assert 'SyntaxError' not in result.stdout.str()
 | |
| 
 | |
| 
 | |
| def test_traceback_failure(testdir):
 | |
|     p1 = testdir.makepyfile("""
 | |
|         def g():
 | |
|             return 2
 | |
|         def f(x):
 | |
|             assert x == g()
 | |
|         def test_onefails():
 | |
|             f(3)
 | |
|     """)
 | |
|     result = testdir.runpytest(p1, "--tb=long")
 | |
|     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"
 | |
|     ])
 | |
| 
 | |
|     result = testdir.runpytest(p1)  # "auto"
 | |
|     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"
 | |
|     ])
 | |
| 
 | |
| 
 | |
| @pytest.mark.skipif(sys.version_info[:2] <= (3, 3), reason='Python 3.4+ shows chained exceptions on multiprocess')
 | |
| def test_exception_handling_no_traceback(testdir):
 | |
|     """
 | |
|     Handle chain exceptions in tasks submitted by the multiprocess module (#1984).
 | |
|     """
 | |
|     p1 = testdir.makepyfile("""
 | |
|         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()
 | |
|     """)
 | |
|     result = testdir.runpytest(p1, "--tb=long")
 | |
|     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()",
 | |
|     ])
 | |
| 
 | |
| 
 | |
| @pytest.mark.skipif("'__pypy__' in sys.builtin_module_names or sys.platform.startswith('java')")
 | |
| def test_warn_missing(testdir):
 | |
|     testdir.makepyfile("")
 | |
|     result = testdir.run(sys.executable, "-OO", "-m", "pytest", "-h")
 | |
|     result.stderr.fnmatch_lines([
 | |
|         "*WARNING*assert statements are not executed*",
 | |
|     ])
 | |
|     result = testdir.run(sys.executable, "-OO", "-m", "pytest")
 | |
|     result.stderr.fnmatch_lines([
 | |
|         "*WARNING*assert statements are not executed*",
 | |
|     ])
 | |
| 
 | |
| 
 | |
| def test_recursion_source_decode(testdir):
 | |
|     testdir.makepyfile("""
 | |
|         def test_something():
 | |
|             pass
 | |
|     """)
 | |
|     testdir.makeini("""
 | |
|         [pytest]
 | |
|         python_files = *.py
 | |
|     """)
 | |
|     result = testdir.runpytest("--collect-only")
 | |
|     result.stdout.fnmatch_lines("""
 | |
|         <Module*>
 | |
|     """)
 | |
| 
 | |
| 
 | |
| def test_AssertionError_message(testdir):
 | |
|     testdir.makepyfile("""
 | |
|         def test_hello():
 | |
|             x,y = 1,2
 | |
|             assert 0, (x,y)
 | |
|     """)
 | |
|     result = testdir.runpytest()
 | |
|     result.stdout.fnmatch_lines("""
 | |
|         *def test_hello*
 | |
|         *assert 0, (x,y)*
 | |
|         *AssertionError: (1, 2)*
 | |
|     """)
 | |
| 
 | |
| 
 | |
| @pytest.mark.skipif(PY3, reason='This bug does not exist on PY3')
 | |
| def test_set_with_unsortable_elements():
 | |
|     # issue #718
 | |
|     class UnsortableKey(object):
 | |
|         def __init__(self, name):
 | |
|             self.name = name
 | |
| 
 | |
|         def __lt__(self, other):
 | |
|             raise RuntimeError()
 | |
| 
 | |
|         def __repr__(self):
 | |
|             return 'repr({0})'.format(self.name)
 | |
| 
 | |
|         def __eq__(self, other):
 | |
|             return self.name == other.name
 | |
| 
 | |
|         def __hash__(self):
 | |
|             return hash(self.name)
 | |
| 
 | |
|     left_set = set(UnsortableKey(str(i)) for i in range(1, 3))
 | |
|     right_set = set(UnsortableKey(str(i)) for i in range(2, 4))
 | |
|     expl = callequal(left_set, right_set, verbose=True)
 | |
|     # skip first line because it contains the "construction" of the set, which does not have a guaranteed order
 | |
|     expl = expl[1:]
 | |
|     dedent = textwrap.dedent("""
 | |
|         Extra items in the left set:
 | |
|         repr(1)
 | |
|         Extra items in the right set:
 | |
|         repr(3)
 | |
|         Full diff (fallback to calling repr on each item):
 | |
|         - repr(1)
 | |
|         repr(2)
 | |
|         + repr(3)
 | |
|     """).strip()
 | |
|     assert '\n'.join(expl) == dedent
 | |
| 
 | |
| 
 | |
| def test_diff_newline_at_end(monkeypatch, testdir):
 | |
|     testdir.makepyfile(r"""
 | |
|         def test_diff():
 | |
|             assert 'asdf' == 'asdf\n'
 | |
|     """)
 | |
| 
 | |
|     result = testdir.runpytest()
 | |
|     result.stdout.fnmatch_lines(r"""
 | |
|         *assert 'asdf' == 'asdf\n'
 | |
|         *  - asdf
 | |
|         *  + asdf
 | |
|         *  ?     +
 | |
|     """)
 | |
| 
 | |
| 
 | |
| def test_assert_tuple_warning(testdir):
 | |
|     testdir.makepyfile("""
 | |
|         def test_tuple():
 | |
|             assert(False, 'you shall not pass')
 | |
|     """)
 | |
|     result = testdir.runpytest('-rw')
 | |
|     result.stdout.fnmatch_lines([
 | |
|         '*test_assert_tuple_warning.py:2',
 | |
|         '*assertion is always true*',
 | |
|     ])
 | |
| 
 | |
| 
 | |
| def test_assert_indirect_tuple_no_warning(testdir):
 | |
|     testdir.makepyfile("""
 | |
|         def test_tuple():
 | |
|             tpl = ('foo', 'bar')
 | |
|             assert tpl
 | |
|     """)
 | |
|     result = testdir.runpytest('-rw')
 | |
|     output = '\n'.join(result.stdout.lines)
 | |
|     assert 'WR1' not in output
 | |
| 
 | |
| 
 | |
| def test_assert_with_unicode(monkeypatch, testdir):
 | |
|     testdir.makepyfile(u"""
 | |
|         # -*- coding: utf-8 -*-
 | |
|         def test_unicode():
 | |
|             assert u'유니코드' == u'Unicode'
 | |
|     """)
 | |
|     result = testdir.runpytest()
 | |
|     result.stdout.fnmatch_lines(['*AssertionError*'])
 | |
| 
 | |
| 
 | |
| def test_raise_unprintable_assertion_error(testdir):
 | |
|     testdir.makepyfile(r"""
 | |
|         def test_raise_assertion_error():
 | |
|             raise AssertionError('\xff')
 | |
|     """)
 | |
|     result = testdir.runpytest()
 | |
|     result.stdout.fnmatch_lines([r">       raise AssertionError('\xff')", 'E       AssertionError: *'])
 | |
| 
 | |
| 
 | |
| def test_raise_assertion_error_raisin_repr(testdir):
 | |
|     testdir.makepyfile(u"""
 | |
|         class RaisingRepr(object):
 | |
|             def __repr__(self):
 | |
|                 raise Exception()
 | |
|         def test_raising_repr():
 | |
|             raise AssertionError(RaisingRepr())
 | |
|     """)
 | |
|     result = testdir.runpytest()
 | |
|     result.stdout.fnmatch_lines(['E       AssertionError: <unprintable AssertionError object>'])
 | |
| 
 | |
| 
 | |
| def test_issue_1944(testdir):
 | |
|     testdir.makepyfile("""
 | |
|         def f():
 | |
|             return
 | |
| 
 | |
|         assert f() == 10
 | |
|     """)
 | |
|     result = testdir.runpytest()
 | |
|     result.stdout.fnmatch_lines(["*1 error*"])
 | |
|     assert "AttributeError: 'Module' object has no attribute '_obj'" not in result.stdout.str()
 |