summaryrefslogtreecommitdiffstats
path: root/Lib/unittest
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2013-09-23 20:07:00 (GMT)
committerSerhiy Storchaka <storchaka@gmail.com>2013-09-23 20:07:00 (GMT)
commit77622f55c2705014005623fd58020f6f06379e12 (patch)
tree1a303a515d7bf14d78e0c1955c28845d2f6a7dc8 /Lib/unittest
parent463bd4b5c6046f2501b36978ea2732e5bcd4ea19 (diff)
downloadcpython-77622f55c2705014005623fd58020f6f06379e12.zip
cpython-77622f55c2705014005623fd58020f6f06379e12.tar.gz
cpython-77622f55c2705014005623fd58020f6f06379e12.tar.bz2
Issue #18996: TestCase.assertEqual() now more cleverly shorten differing
strings in error report.
Diffstat (limited to 'Lib/unittest')
-rw-r--r--Lib/unittest/case.py20
-rw-r--r--Lib/unittest/test/test_case.py37
-rw-r--r--Lib/unittest/util.py37
3 files changed, 78 insertions, 16 deletions
diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py
index 2909610..7ed932f 100644
--- a/Lib/unittest/case.py
+++ b/Lib/unittest/case.py
@@ -12,7 +12,7 @@ import contextlib
from . import result
from .util import (strclass, safe_repr, _count_diff_all_purpose,
- _count_diff_hashable)
+ _count_diff_hashable, _common_shorten_repr)
__unittest = True
@@ -770,7 +770,7 @@ class TestCase(object):
def _baseAssertEqual(self, first, second, msg=None):
"""The default assertEqual implementation, not type specific."""
if not first == second:
- standardMsg = '%s != %s' % (safe_repr(first), safe_repr(second))
+ standardMsg = '%s != %s' % _common_shorten_repr(first, second)
msg = self._formatMessage(msg, standardMsg)
raise self.failureException(msg)
@@ -905,14 +905,9 @@ class TestCase(object):
if seq1 == seq2:
return
- seq1_repr = safe_repr(seq1)
- seq2_repr = safe_repr(seq2)
- if len(seq1_repr) > 30:
- seq1_repr = seq1_repr[:30] + '...'
- if len(seq2_repr) > 30:
- seq2_repr = seq2_repr[:30] + '...'
- elements = (seq_type_name.capitalize(), seq1_repr, seq2_repr)
- differing = '%ss differ: %s != %s\n' % elements
+ differing = '%ss differ: %s != %s\n' % (
+ (seq_type_name.capitalize(),) +
+ _common_shorten_repr(seq1, seq2))
for i in range(min(len1, len2)):
try:
@@ -1070,7 +1065,7 @@ class TestCase(object):
self.assertIsInstance(d2, dict, 'Second argument is not a dictionary')
if d1 != d2:
- standardMsg = '%s != %s' % (safe_repr(d1, True), safe_repr(d2, True))
+ standardMsg = '%s != %s' % _common_shorten_repr(d1, d2)
diff = ('\n' + '\n'.join(difflib.ndiff(
pprint.pformat(d1).splitlines(),
pprint.pformat(d2).splitlines())))
@@ -1154,8 +1149,7 @@ class TestCase(object):
if len(firstlines) == 1 and first.strip('\r\n') == first:
firstlines = [first + '\n']
secondlines = [second + '\n']
- standardMsg = '%s != %s' % (safe_repr(first, True),
- safe_repr(second, True))
+ standardMsg = '%s != %s' % _common_shorten_repr(first, second)
diff = '\n' + ''.join(difflib.ndiff(firstlines, secondlines))
standardMsg = self._truncateMessage(standardMsg, diff)
self.fail(self._formatMessage(msg, standardMsg))
diff --git a/Lib/unittest/test/test_case.py b/Lib/unittest/test/test_case.py
index f08668f..9aa9fd1 100644
--- a/Lib/unittest/test/test_case.py
+++ b/Lib/unittest/test/test_case.py
@@ -829,18 +829,18 @@ class Test_TestCase(unittest.TestCase, TestEquality, TestHashing):
# set a lower threshold value and add a cleanup to restore it
old_threshold = self._diffThreshold
- self._diffThreshold = 2**8
+ self._diffThreshold = 2**5
self.addCleanup(lambda: setattr(self, '_diffThreshold', old_threshold))
# under the threshold: diff marker (^) in error message
- s = 'x' * (2**7)
+ s = 'x' * (2**4)
with self.assertRaises(self.failureException) as cm:
self.assertEqual(s + 'a', s + 'b')
self.assertIn('^', str(cm.exception))
self.assertEqual(s + 'a', s + 'a')
# over the threshold: diff not used and marker (^) not in error message
- s = 'x' * (2**9)
+ s = 'x' * (2**6)
# if the path that uses difflib is taken, _truncateMessage will be
# called -- replace it with explodingTruncation to verify that this
# doesn't happen
@@ -857,6 +857,37 @@ class Test_TestCase(unittest.TestCase, TestEquality, TestHashing):
self.assertEqual(str(cm.exception), '%r != %r' % (s1, s2))
self.assertEqual(s + 'a', s + 'a')
+ def testAssertEqual_shorten(self):
+ # set a lower threshold value and add a cleanup to restore it
+ old_threshold = self._diffThreshold
+ self._diffThreshold = 0
+ self.addCleanup(lambda: setattr(self, '_diffThreshold', old_threshold))
+
+ s = 'x' * 100
+ s1, s2 = s + 'a', s + 'b'
+ with self.assertRaises(self.failureException) as cm:
+ self.assertEqual(s1, s2)
+ c = 'xxxx[35 chars]' + 'x' * 61
+ self.assertEqual(str(cm.exception), "'%sa' != '%sb'" % (c, c))
+ self.assertEqual(s + 'a', s + 'a')
+
+ p = 'y' * 50
+ s1, s2 = s + 'a' + p, s + 'b' + p
+ with self.assertRaises(self.failureException) as cm:
+ self.assertEqual(s1, s2)
+ c = 'xxxx[85 chars]xxxxxxxxxxx'
+ #print()
+ #print(str(cm.exception))
+ self.assertEqual(str(cm.exception), "'%sa%s' != '%sb%s'" % (c, p, c, p))
+
+ p = 'y' * 100
+ s1, s2 = s + 'a' + p, s + 'b' + p
+ with self.assertRaises(self.failureException) as cm:
+ self.assertEqual(s1, s2)
+ c = 'xxxx[91 chars]xxxxx'
+ d = 'y' * 40 + '[56 chars]yyyy'
+ self.assertEqual(str(cm.exception), "'%sa%s' != '%sb%s'" % (c, d, c, d))
+
def testAssertCountEqual(self):
a = object()
self.assertCountEqual([1, 2, 3], [3, 2, 1])
diff --git a/Lib/unittest/util.py b/Lib/unittest/util.py
index ccdf0b8..aee498f 100644
--- a/Lib/unittest/util.py
+++ b/Lib/unittest/util.py
@@ -1,10 +1,47 @@
"""Various utility functions."""
from collections import namedtuple, OrderedDict
+from os.path import commonprefix
__unittest = True
_MAX_LENGTH = 80
+_PLACEHOLDER_LEN = 12
+_MIN_BEGIN_LEN = 5
+_MIN_END_LEN = 5
+_MIN_COMMON_LEN = 5
+_MIN_DIFF_LEN = _MAX_LENGTH - \
+ (_MIN_BEGIN_LEN + _PLACEHOLDER_LEN + _MIN_COMMON_LEN +
+ _PLACEHOLDER_LEN + _MIN_END_LEN)
+assert _MIN_DIFF_LEN >= 0
+
+def _shorten(s, prefixlen, suffixlen):
+ skip = len(s) - prefixlen - suffixlen
+ if skip > _PLACEHOLDER_LEN:
+ s = '%s[%d chars]%s' % (s[:prefixlen], skip, s[len(s) - suffixlen:])
+ return s
+
+def _common_shorten_repr(*args):
+ args = tuple(map(safe_repr, args))
+ maxlen = max(map(len, args))
+ if maxlen <= _MAX_LENGTH:
+ return args
+
+ prefix = commonprefix(args)
+ prefixlen = len(prefix)
+
+ common_len = _MAX_LENGTH - \
+ (maxlen - prefixlen + _MIN_BEGIN_LEN + _PLACEHOLDER_LEN)
+ if common_len > _MIN_COMMON_LEN:
+ assert _MIN_BEGIN_LEN + _PLACEHOLDER_LEN + _MIN_COMMON_LEN + \
+ (maxlen - prefixlen) < _MAX_LENGTH
+ prefix = _shorten(prefix, _MIN_BEGIN_LEN, common_len)
+ return tuple(prefix + s[prefixlen:] for s in args)
+
+ prefix = _shorten(prefix, _MIN_BEGIN_LEN, _MIN_COMMON_LEN)
+ return tuple(prefix + _shorten(s[prefixlen:], _MIN_DIFF_LEN, _MIN_END_LEN)
+ for s in args)
+
def safe_repr(obj, short=False):
try:
result = repr(obj)