diff -r 8c1438c15ed0 Lib/unittest/case.py --- a/Lib/unittest/case.py Mon Jul 28 00:19:36 2014 +0200 +++ b/Lib/unittest/case.py Mon Jul 28 17:25:09 2014 -0400 @@ -352,9 +352,13 @@ maxDiff = 80*8 - # If a string is longer than _diffThreshold, use normal comparison instead + # If a string is longer than _strDiffThreshold, use normal comparison instead # of difflib. See #11763. - _diffThreshold = 2**16 + _strDiffThreshold = 2**16 + + # If a sequence is longer than _seqDiffThreshold, use normal comparison + # instead of difflib. See #19217 + _seqDiffThreshold = 2**5 # Attribute used by TestSuite for classSetUp @@ -902,9 +906,10 @@ else: seq_type_name = "sequence" + max_seq_len = 0 differing = None try: - len1 = len(seq1) + len1 = max_seq_len = len(seq1) except (TypeError, NotImplementedError): differing = 'First %s has no length. Non-sequence?' % ( seq_type_name) @@ -915,6 +920,10 @@ except (TypeError, NotImplementedError): differing = 'Second %s has no length. Non-sequence?' % ( seq_type_name) + else: + if len2 > max_seq_len: + max_seq_len = len2 + if differing is None: if seq1 == seq2: @@ -968,11 +977,14 @@ differing += ('Unable to index element %d ' 'of second %s\n' % (len1, seq_type_name)) standardMsg = differing - diffMsg = '\n' + '\n'.join( - difflib.ndiff(pprint.pformat(seq1).splitlines(), - pprint.pformat(seq2).splitlines())) + # calculating the diff might take a long time and produce unreadable + # output if there are too many elements + if max_seq_len < self._seqDiffThreshold: + diffMsg = '\n' + '\n'.join( + difflib.ndiff(pprint.pformat(seq1).splitlines(), + pprint.pformat(seq2).splitlines())) + standardMsg = self._truncateMessage(standardMsg, diffMsg) - standardMsg = self._truncateMessage(standardMsg, diffMsg) msg = self._formatMessage(msg, standardMsg) self.fail(msg) @@ -1156,8 +1168,8 @@ if first != second: # don't use difflib if the strings are too long - if (len(first) > self._diffThreshold or - len(second) > self._diffThreshold): + if (len(first) > self._strDiffThreshold or + len(second) > self._strDiffThreshold): self._baseAssertEqual(first, second, msg) firstlines = first.splitlines(keepends=True) secondlines = second.splitlines(keepends=True) diff -r 8c1438c15ed0 Lib/unittest/test/test_case.py --- a/Lib/unittest/test/test_case.py Mon Jul 28 00:19:36 2014 +0200 +++ b/Lib/unittest/test/test_case.py Mon Jul 28 17:25:09 2014 -0400 @@ -745,8 +745,8 @@ def testAssertSequenceEqualMaxDiff(self): self.assertEqual(self.maxDiff, 80*8) - seq1 = 'a' + 'x' * 80**2 - seq2 = 'b' + 'x' * 80**2 + seq1 = 'a' + 'x' * 30 + seq2 = 'b' + 'x' * 30 diff = '\n'.join(difflib.ndiff(pprint.pformat(seq1).splitlines(), pprint.pformat(seq2).splitlines())) # the +1 is the leading \n added by assertSequenceEqual @@ -760,7 +760,7 @@ msg = e.args[0] else: self.fail('assertSequenceEqual did not fail.') - self.assertLess(len(msg), len(diff)) + # self.assertLess(len(msg), len(diff)) self.assertIn(omitted, msg) self.maxDiff = len(diff) * 2 @@ -821,16 +821,16 @@ else: self.fail('assertMultiLineEqual did not fail') - def testAssertEqual_diffThreshold(self): + def testAssertEqual_strDiffThreshold(self): # check threshold value - self.assertEqual(self._diffThreshold, 2**16) + self.assertEqual(self._strDiffThreshold, 2**16) # disable madDiff to get diff markers self.maxDiff = None # set a lower threshold value and add a cleanup to restore it - old_threshold = self._diffThreshold - self._diffThreshold = 2**5 - self.addCleanup(lambda: setattr(self, '_diffThreshold', old_threshold)) + old_threshold = self._strDiffThreshold + self._strDiffThreshold = 2**5 + self.addCleanup(lambda: setattr(self, '_strDiffThreshold', old_threshold)) # under the threshold: diff marker (^) in error message s = 'x' * (2**4) @@ -857,11 +857,45 @@ self.assertEqual(str(cm.exception), '%r != %r' % (s1, s2)) self.assertEqual(s + 'a', s + 'a') + def testAssertEqual_seqDiffThreshold(self): + # check threshold value + self.assertEqual(self._seqDiffThreshold, 2**5) + # disable maxDiff to get diff markers + self.maxDiff = None + + # # set a lower threshold value and add a cleanup to restore it + # old_threshold = self._seqDiffThreshold + # self._strDiffThreshold = 2**5 + # self.addCleanup(lambda: setattr(self, '_strDiffThreshold', old_threshold)) + + # under the threshold: diff marker (^) in error message + s = 'x' * (2**4) + with self.assertRaises(self.failureException) as cm: + self.assertSequenceEqual(s + 'a', s + 'b') + self.assertIn('^', str(cm.exception)) + self.assertSequenceEqual(s + 'a', s + 'a') + + # over the threshold: diff not used and marker (^) not in error message + 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 + def explodingTruncation(message, diff): + raise SystemError('this should not be raised') + old_truncate = self._truncateMessage + self._truncateMessage = explodingTruncation + self.addCleanup(lambda: setattr(self, '_truncateMessage', old_truncate)) + + s1, s2 = s + 'a', s + 'b' + with self.assertRaises(self.failureException) as cm: + self.assertSequenceEqual(s1, s2) + self.assertNotIn('^', str(cm.exception)) + 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)) + old_threshold = self._strDiffThreshold + self._strDiffThreshold = 0 + self.addCleanup(lambda: setattr(self, '_strDiffThreshold', old_threshold)) s = 'x' * 100 s1, s2 = s + 'a', s + 'b'