classification
Title: unittest: more helpful truncating long strings
Type: enhancement Stage: resolved
Components: Tests Versions: Python 3.4
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: serhiy.storchaka Nosy List: cjw296, ezio.melotti, michael.foord, ned.deily, pitrou, python-dev, serhiy.storchaka, vajrasky
Priority: normal Keywords: patch

Created on 2013-09-10 11:51 by serhiy.storchaka, last changed 2014-06-21 10:35 by cjw296. This issue is now closed.

Files
File name Uploaded Description Edit
assertEqual_shorten.patch serhiy.storchaka, 2013-09-10 16:32 review
Messages (11)
msg197433 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2013-09-10 11:51
Error message in assertEqual() is not vary useful when compared long strings with long common prefix. It looks as:

'For the first time in his life h [truncated]...' != 'For the first time in his life h [truncated]...'

With the proposed patch it will look as:

'For [25 chars]e he seemed to be vividly aware of his own existence.' != 'For [25 chars]e he felt humble. He perceived how misgui[105 chars]ers.'

New algorithm splits strings on common prefix and differing suffixes, shortens every part if it is too long, and concatenates them back. So user always see the start and the end of string, and the place of first difference with some characters before and after.
msg197890 - (view) Author: Michael Foord (michael.foord) * (Python committer) Date: 2013-09-16 10:40
Awesome, thanks for this work. 

The only thing I'd say is that if  _common_shorten is always called with two args - and safe_repr is always called on them - then why not have it take two args and call safe_repr on them?
msg197934 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2013-09-16 19:17
Indeed, I'll rename _common_shorten() to _common_shorten_repr() and call safe_repr() inside. As for two vs multiple args, first I wrote a variant with two args (you can see it in my first unlinked patch), but then I seen that general variant is not harder and even shorter.

I'm open for bikeshedding. Perhaps we should change the placeholder to something better (e.g. to "[... skipped ...]")? Is the number of skipped characters is useful? Note that it is a number of skipped characters in the repr, not in the original strings and can be misguided for strings. Perhaps we should change minimal lengths of head, tail, and interior part? Why part is most important and should be larger? Perhaps add a keyword-only parameter maxlen (or other parameters for customization)?
msg198334 - (view) Author: Roundup Robot (python-dev) Date: 2013-09-23 20:07
New changeset 5bb83faa8818 by Serhiy Storchaka in branch 'default':
Issue #18996: TestCase.assertEqual() now more cleverly shorten differing
http://hg.python.org/cpython/rev/5bb83faa8818
msg201971 - (view) Author: Vajrasky Kok (vajrasky) * Date: 2013-11-02 13:19
Serhiy, you still left debugging code there.

Lib/unittest/test/test_case.py, line 879 and line 880.

    #print()
    #print(str(cm.exception))
msg205781 - (view) Author: Vajrasky Kok (vajrasky) * Date: 2013-12-10 09:31
Hello, Serhiy. Do you want to remove this debug messaging?
msg205797 - (view) Author: Roundup Robot (python-dev) Date: 2013-12-10 11:54
New changeset a0c687dc0039 by Serhiy Storchaka in branch 'default':
Remove commented out debugging code (remnants of issue #18996).
http://hg.python.org/cpython/rev/a0c687dc0039
msg205798 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2013-12-10 11:55
Oh, thank you Vajrasky.
msg221083 - (view) Author: Chris Withers (cjw296) * (Python committer) Date: 2014-06-20 11:57
So, this appears to be the source of some degraded behaviour for me with Python 3.4 versus Python 3.3.

This code, prior to 3.4:

from testfixtures import Comparison as C

class AClass:
    def __init__(self,x,y=None):
        self.x = x
        if y:
            self.y = y
    def __repr__(self):
        return '<'+self.__class__.__name__+'>'

...

self.assertEqual(
    C('testfixtures.tests.test_comparison.AClass',
      y=5, z='missing'),
      AClass(1, 2))

Would give the following output in the failure message:

"""
<C(failed):testfixtures.tests.test_comparison.AClass>
  x:1 not in Comparison
  y:5 != 2
  z:'missing' not in other
</C> != <AClass>"
"""

Now, in 3.4, you get the (rather unhelpful):

"""
<C(failed):testfixtures.tests.test_com[79 chars] </C> != <AClass>
"""

It's particularly disappointing that there's no API (class attribute, etc) to control whether or not this new behaviour is enabled.

What's the process I should tackle for getting this sorted out?
msg221119 - (view) Author: Ned Deily (ned.deily) * (Python committer) Date: 2014-06-20 19:54
Chris, I would start by opening a new issue. Comments on closed issues whose code is already released are likely to be overlooked.
msg221168 - (view) Author: Chris Withers (cjw296) * (Python committer) Date: 2014-06-21 10:35
Okay, opened [issue21820].
History
Date User Action Args
2014-06-21 10:35:26cjw296setmessages: + msg221168
2014-06-20 19:54:59ned.deilysetnosy: + ned.deily
messages: + msg221119
2014-06-20 11:57:14cjw296setnosy: + cjw296
messages: + msg221083
2013-12-10 11:55:13serhiy.storchakasetmessages: + msg205798
2013-12-10 11:54:21python-devsetmessages: + msg205797
2013-12-10 09:31:44vajraskysetmessages: + msg205781
2013-11-02 13:19:22vajraskysetnosy: + vajrasky
messages: + msg201971
2013-09-23 20:08:37serhiy.storchakasetstatus: open -> closed
assignee: serhiy.storchaka
resolution: fixed
stage: patch review -> resolved
2013-09-23 20:07:16python-devsetnosy: + python-dev
messages: + msg198334
2013-09-16 19:17:18serhiy.storchakasetmessages: + msg197934
2013-09-16 10:40:45michael.foordsetmessages: + msg197890
2013-09-10 16:32:00serhiy.storchakasetfiles: + assertEqual_shorten.patch
2013-09-10 16:31:06serhiy.storchakasetfiles: - assertEqual_shorten.patch
2013-09-10 11:51:47serhiy.storchakacreate