diff -r 751328f413a8 Lib/unittest/case.py --- a/Lib/unittest/case.py Sat Aug 02 22:32:33 2014 -0700 +++ b/Lib/unittest/case.py Mon Aug 04 23:27:14 2014 +0300 @@ -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 = 32 # 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,13 @@ 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())) - - standardMsg = self._truncateMessage(standardMsg, diffMsg) + # 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) msg = self._formatMessage(msg, standardMsg) self.fail(msg) @@ -1078,8 +1089,11 @@ def assertDictEqual(self, d1, d2, msg=None): self.assertIsInstance(d1, dict, 'First argument is not a dictionary') self.assertIsInstance(d2, dict, 'Second argument is not a dictionary') - if d1 != d2: + ## don't use difflib if the strings are too long + if (len(d1) > self._seqDiffThreshold or + len(d2) > self._seqDiffThreshold): + self._baseAssertEqual(d1, d2, msg) standardMsg = '%s != %s' % _common_shorten_repr(d1, d2) diff = ('\n' + '\n'.join(difflib.ndiff( pprint.pformat(d1).splitlines(), @@ -1156,8 +1170,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 751328f413a8 Lib/unittest/test/test_case.py --- a/Lib/unittest/test/test_case.py Sat Aug 02 22:32:33 2014 -0700 +++ b/Lib/unittest/test/test_case.py Mon Aug 04 23:27:14 2014 +0300 @@ -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 @@ -754,13 +754,12 @@ self.maxDiff = len(diff)//2 try: - self.assertSequenceEqual(seq1, seq2) except self.failureException as e: msg = e.args[0] else: self.fail('assertSequenceEqual did not fail.') - self.assertLess(len(msg), len(diff)) + self.assertNotIn(diff, msg) self.assertIn(omitted, msg) self.maxDiff = len(diff) * 2 @@ -821,47 +820,82 @@ else: self.fail('assertMultiLineEqual did not fail') - def testAssertEqual_diffThreshold(self): - # check threshold value - self.assertEqual(self._diffThreshold, 2**16) - # disable madDiff to get diff markers + def assertEqual_thresholdHelper(self, threshold_type, + value1_below_threshold, value2_below_threshold, + value1_above_threshold, value2_above_threshold): 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)) - - # under the threshold: diff marker (^) in error message - s = 'x' * (2**4) with self.assertRaises(self.failureException) as cm: - self.assertEqual(s + 'a', s + 'b') + self.assertEqual(value1_below_threshold, value2_below_threshold) + 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**6) - # if the path that uses difflib is taken, _truncateMessage will be - # called -- replace it with explodingTruncation to verify that this - # doesn't happen + + self.assertEqual(value1_below_threshold, value1_below_threshold) + 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.assertEqual(s1, s2) + self.assertEqual(value1_above_threshold, value2_above_threshold) + self.assertNotIn('^', str(cm.exception)) - self.assertEqual(str(cm.exception), '%r != %r' % (s1, s2)) - self.assertEqual(s + 'a', s + 'a') + #self.assertIn('%r != %r' % (value1_above_threshold, value2_above_threshold), str(cm.exception)) + self.assertEqual(value1_above_threshold, value1_above_threshold) + + + def testAssertEqual_strDiffThreshold(self): + self.assertEqual(self._strDiffThreshold, 2**16) + + old_threshold = self._strDiffThreshold + self._strDiffThreshold = 32 + self.addCleanup(lambda: setattr(self, '_strDiffThreshold', old_threshold)) + + s = 'x' * 16 + s1_below_threshold = s + 'a' + s2_below_threshold = s + 'b' + s = 'x' * 64 + s1_above_threshold = s + 'a' + s2_above_threshold = s + 'b' + + self.assertEqual_thresholdHelper('_strDiffThreshold', s1_below_threshold, s2_below_threshold, s1_above_threshold, s2_above_threshold) + + def testAssertEqual_seqDiffThreshold(self): + self.assertEqual(self._seqDiffThreshold, 32) + + s = (1,) * 16 + s1_below_threshold = s + (2,) + s2_below_threshold = s + (3,) + s = (1,) * 64 + s1_above_threshold = s + (2,) + s2_above_threshold = s + (3,) + + self.assertEqual_thresholdHelper('_seqDiffThreshold', s1_below_threshold, s2_below_threshold, s1_above_threshold, s2_above_threshold) + + + def testAssertEqual_dictDiffThreshold(self): + self.assertEqual(self._seqDiffThreshold, 32) + + d1_below_threshold = {i: 5 for i in range(16)} + d2_below_threshold = {i: 5 for i in range(16)} + d1_below_threshold[16] = 10000 + d2_below_threshold[16] = 10001 + + d1_above_threshold = {i: 5 for i in range(64)} + d2_above_threshold = {i: 5 for i in range(64)} + d1_above_threshold[64] = 10000 + d2_above_threshold[64] = 10001 + + self.assertEqual_thresholdHelper('_seqDiffThreshold', d1_below_threshold, d2_below_threshold, d1_above_threshold, d2_above_threshold) 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'