1#
2# Test suite for the textwrap module.
3#
4# Original tests written by Greg Ward <gward@python.net>.
5# Converted to PyUnit by Peter Hansen <peter@engcorp.com>.
6# Currently maintained by Greg Ward.
7#
8# $Id$
9#
10
11import unittest
12
13from textwrap import TextWrapper, wrap, fill, dedent, indent, shorten
14
15
16class BaseTestCase(unittest.TestCase):
17    '''Parent class with utility methods for textwrap tests.'''
18
19    def show(self, textin):
20        if isinstance(textin, list):
21            result = []
22            for i in range(len(textin)):
23                result.append("  %d: %r" % (i, textin[i]))
24            result = "\n".join(result) if result else "  no lines"
25        elif isinstance(textin, str):
26            result = "  %s\n" % repr(textin)
27        return result
28
29
30    def check(self, result, expect):
31        self.assertEqual(result, expect,
32            'expected:\n%s\nbut got:\n%s' % (
33                self.show(expect), self.show(result)))
34
35    def check_wrap(self, text, width, expect, **kwargs):
36        result = wrap(text, width, **kwargs)
37        self.check(result, expect)
38
39    def check_split(self, text, expect):
40        result = self.wrapper._split(text)
41        self.assertEqual(result, expect,
42                         "\nexpected %r\n"
43                         "but got  %r" % (expect, result))
44
45
46class WrapTestCase(BaseTestCase):
47
48    def setUp(self):
49        self.wrapper = TextWrapper(width=45)
50
51    def test_simple(self):
52        # Simple case: just words, spaces, and a bit of punctuation
53
54        text = "Hello there, how are you this fine day?  I'm glad to hear it!"
55
56        self.check_wrap(text, 12,
57                        ["Hello there,",
58                         "how are you",
59                         "this fine",
60                         "day?  I'm",
61                         "glad to hear",
62                         "it!"])
63        self.check_wrap(text, 42,
64                        ["Hello there, how are you this fine day?",
65                         "I'm glad to hear it!"])
66        self.check_wrap(text, 80, [text])
67
68    def test_empty_string(self):
69        # Check that wrapping the empty string returns an empty list.
70        self.check_wrap("", 6, [])
71        self.check_wrap("", 6, [], drop_whitespace=False)
72
73    def test_empty_string_with_initial_indent(self):
74        # Check that the empty string is not indented.
75        self.check_wrap("", 6, [], initial_indent="++")
76        self.check_wrap("", 6, [], initial_indent="++", drop_whitespace=False)
77
78    def test_whitespace(self):
79        # Whitespace munging and end-of-sentence detection
80
81        text = """\
82This is a paragraph that already has
83line breaks.  But some of its lines are much longer than the others,
84so it needs to be wrapped.
85Some lines are \ttabbed too.
86What a mess!
87"""
88
89        expect = ["This is a paragraph that already has line",
90                  "breaks.  But some of its lines are much",
91                  "longer than the others, so it needs to be",
92                  "wrapped.  Some lines are  tabbed too.  What a",
93                  "mess!"]
94
95        wrapper = TextWrapper(45, fix_sentence_endings=True)
96        result = wrapper.wrap(text)
97        self.check(result, expect)
98
99        result = wrapper.fill(text)
100        self.check(result, '\n'.join(expect))
101
102        text = "\tTest\tdefault\t\ttabsize."
103        expect = ["        Test    default         tabsize."]
104        self.check_wrap(text, 80, expect)
105
106        text = "\tTest\tcustom\t\ttabsize."
107        expect = ["    Test    custom      tabsize."]
108        self.check_wrap(text, 80, expect, tabsize=4)
109
110    def test_fix_sentence_endings(self):
111        wrapper = TextWrapper(60, fix_sentence_endings=True)
112
113        # SF #847346: ensure that fix_sentence_endings=True does the
114        # right thing even on input short enough that it doesn't need to
115        # be wrapped.
116        text = "A short line. Note the single space."
117        expect = ["A short line.  Note the single space."]
118        self.check(wrapper.wrap(text), expect)
119
120        # Test some of the hairy end cases that _fix_sentence_endings()
121        # is supposed to handle (the easy stuff is tested in
122        # test_whitespace() above).
123        text = "Well, Doctor? What do you think?"
124        expect = ["Well, Doctor?  What do you think?"]
125        self.check(wrapper.wrap(text), expect)
126
127        text = "Well, Doctor?\nWhat do you think?"
128        self.check(wrapper.wrap(text), expect)
129
130        text = 'I say, chaps! Anyone for "tennis?"\nHmmph!'
131        expect = ['I say, chaps!  Anyone for "tennis?"  Hmmph!']
132        self.check(wrapper.wrap(text), expect)
133
134        wrapper.width = 20
135        expect = ['I say, chaps!', 'Anyone for "tennis?"', 'Hmmph!']
136        self.check(wrapper.wrap(text), expect)
137
138        text = 'And she said, "Go to hell!"\nCan you believe that?'
139        expect = ['And she said, "Go to',
140                  'hell!"  Can you',
141                  'believe that?']
142        self.check(wrapper.wrap(text), expect)
143
144        wrapper.width = 60
145        expect = ['And she said, "Go to hell!"  Can you believe that?']
146        self.check(wrapper.wrap(text), expect)
147
148        text = 'File stdio.h is nice.'
149        expect = ['File stdio.h is nice.']
150        self.check(wrapper.wrap(text), expect)
151
152    def test_wrap_short(self):
153        # Wrapping to make short lines longer
154
155        text = "This is a\nshort paragraph."
156
157        self.check_wrap(text, 20, ["This is a short",
158                                   "paragraph."])
159        self.check_wrap(text, 40, ["This is a short paragraph."])
160
161
162    def test_wrap_short_1line(self):
163        # Test endcases
164
165        text = "This is a short line."
166
167        self.check_wrap(text, 30, ["This is a short line."])
168        self.check_wrap(text, 30, ["(1) This is a short line."],
169                        initial_indent="(1) ")
170
171
172    def test_hyphenated(self):
173        # Test breaking hyphenated words
174
175        text = ("this-is-a-useful-feature-for-"
176                "reformatting-posts-from-tim-peters'ly")
177
178        self.check_wrap(text, 40,
179                        ["this-is-a-useful-feature-for-",
180                         "reformatting-posts-from-tim-peters'ly"])
181        self.check_wrap(text, 41,
182                        ["this-is-a-useful-feature-for-",
183                         "reformatting-posts-from-tim-peters'ly"])
184        self.check_wrap(text, 42,
185                        ["this-is-a-useful-feature-for-reformatting-",
186                         "posts-from-tim-peters'ly"])
187        # The test tests current behavior but is not testing parts of the API.
188        expect = ("this-|is-|a-|useful-|feature-|for-|"
189                  "reformatting-|posts-|from-|tim-|peters'ly").split('|')
190        self.check_wrap(text, 1, expect, break_long_words=False)
191        self.check_split(text, expect)
192
193        self.check_split('e-mail', ['e-mail'])
194        self.check_split('Jelly-O', ['Jelly-O'])
195        # The test tests current behavior but is not testing parts of the API.
196        self.check_split('half-a-crown', 'half-|a-|crown'.split('|'))
197
198    def test_hyphenated_numbers(self):
199        # Test that hyphenated numbers (eg. dates) are not broken like words.
200        text = ("Python 1.0.0 was released on 1994-01-26.  Python 1.0.1 was\n"
201                "released on 1994-02-15.")
202
203        self.check_wrap(text, 30, ['Python 1.0.0 was released on',
204                                   '1994-01-26.  Python 1.0.1 was',
205                                   'released on 1994-02-15.'])
206        self.check_wrap(text, 40, ['Python 1.0.0 was released on 1994-01-26.',
207                                   'Python 1.0.1 was released on 1994-02-15.'])
208        self.check_wrap(text, 1, text.split(), break_long_words=False)
209
210        text = "I do all my shopping at 7-11."
211        self.check_wrap(text, 25, ["I do all my shopping at",
212                                   "7-11."])
213        self.check_wrap(text, 27, ["I do all my shopping at",
214                                   "7-11."])
215        self.check_wrap(text, 29, ["I do all my shopping at 7-11."])
216        self.check_wrap(text, 1, text.split(), break_long_words=False)
217
218    def test_em_dash(self):
219        # Test text with em-dashes
220        text = "Em-dashes should be written -- thus."
221        self.check_wrap(text, 25,
222                        ["Em-dashes should be",
223                         "written -- thus."])
224
225        # Probe the boundaries of the properly written em-dash,
226        # ie. " -- ".
227        self.check_wrap(text, 29,
228                        ["Em-dashes should be written",
229                         "-- thus."])
230        expect = ["Em-dashes should be written --",
231                  "thus."]
232        self.check_wrap(text, 30, expect)
233        self.check_wrap(text, 35, expect)
234        self.check_wrap(text, 36,
235                        ["Em-dashes should be written -- thus."])
236
237        # The improperly written em-dash is handled too, because
238        # it's adjacent to non-whitespace on both sides.
239        text = "You can also do--this or even---this."
240        expect = ["You can also do",
241                  "--this or even",
242                  "---this."]
243        self.check_wrap(text, 15, expect)
244        self.check_wrap(text, 16, expect)
245        expect = ["You can also do--",
246                  "this or even---",
247                  "this."]
248        self.check_wrap(text, 17, expect)
249        self.check_wrap(text, 19, expect)
250        expect = ["You can also do--this or even",
251                  "---this."]
252        self.check_wrap(text, 29, expect)
253        self.check_wrap(text, 31, expect)
254        expect = ["You can also do--this or even---",
255                  "this."]
256        self.check_wrap(text, 32, expect)
257        self.check_wrap(text, 35, expect)
258
259        # All of the above behaviour could be deduced by probing the
260        # _split() method.
261        text = "Here's an -- em-dash and--here's another---and another!"
262        expect = ["Here's", " ", "an", " ", "--", " ", "em-", "dash", " ",
263                  "and", "--", "here's", " ", "another", "---",
264                  "and", " ", "another!"]
265        self.check_split(text, expect)
266
267        text = "and then--bam!--he was gone"
268        expect = ["and", " ", "then", "--", "bam!", "--",
269                  "he", " ", "was", " ", "gone"]
270        self.check_split(text, expect)
271
272
273    def test_unix_options (self):
274        # Test that Unix-style command-line options are wrapped correctly.
275        # Both Optik (OptionParser) and Docutils rely on this behaviour!
276
277        text = "You should use the -n option, or --dry-run in its long form."
278        self.check_wrap(text, 20,
279                        ["You should use the",
280                         "-n option, or --dry-",
281                         "run in its long",
282                         "form."])
283        self.check_wrap(text, 21,
284                        ["You should use the -n",
285                         "option, or --dry-run",
286                         "in its long form."])
287        expect = ["You should use the -n option, or",
288                  "--dry-run in its long form."]
289        self.check_wrap(text, 32, expect)
290        self.check_wrap(text, 34, expect)
291        self.check_wrap(text, 35, expect)
292        self.check_wrap(text, 38, expect)
293        expect = ["You should use the -n option, or --dry-",
294                  "run in its long form."]
295        self.check_wrap(text, 39, expect)
296        self.check_wrap(text, 41, expect)
297        expect = ["You should use the -n option, or --dry-run",
298                  "in its long form."]
299        self.check_wrap(text, 42, expect)
300
301        # Again, all of the above can be deduced from _split().
302        text = "the -n option, or --dry-run or --dryrun"
303        expect = ["the", " ", "-n", " ", "option,", " ", "or", " ",
304                  "--dry-", "run", " ", "or", " ", "--dryrun"]
305        self.check_split(text, expect)
306
307    def test_funky_hyphens (self):
308        # Screwy edge cases cooked up by David Goodger.  All reported
309        # in SF bug #596434.
310        self.check_split("what the--hey!", ["what", " ", "the", "--", "hey!"])
311        self.check_split("what the--", ["what", " ", "the--"])
312        self.check_split("what the--.", ["what", " ", "the--."])
313        self.check_split("--text--.", ["--text--."])
314
315        # When I first read bug #596434, this is what I thought David
316        # was talking about.  I was wrong; these have always worked
317        # fine.  The real problem is tested in test_funky_parens()
318        # below...
319        self.check_split("--option", ["--option"])
320        self.check_split("--option-opt", ["--option-", "opt"])
321        self.check_split("foo --option-opt bar",
322                         ["foo", " ", "--option-", "opt", " ", "bar"])
323
324    def test_punct_hyphens(self):
325        # Oh bother, SF #965425 found another problem with hyphens --
326        # hyphenated words in single quotes weren't handled correctly.
327        # In fact, the bug is that *any* punctuation around a hyphenated
328        # word was handled incorrectly, except for a leading "--", which
329        # was special-cased for Optik and Docutils.  So test a variety
330        # of styles of punctuation around a hyphenated word.
331        # (Actually this is based on an Optik bug report, #813077).
332        self.check_split("the 'wibble-wobble' widget",
333                         ['the', ' ', "'wibble-", "wobble'", ' ', 'widget'])
334        self.check_split('the "wibble-wobble" widget',
335                         ['the', ' ', '"wibble-', 'wobble"', ' ', 'widget'])
336        self.check_split("the (wibble-wobble) widget",
337                         ['the', ' ', "(wibble-", "wobble)", ' ', 'widget'])
338        self.check_split("the ['wibble-wobble'] widget",
339                         ['the', ' ', "['wibble-", "wobble']", ' ', 'widget'])
340
341        # The test tests current behavior but is not testing parts of the API.
342        self.check_split("what-d'you-call-it.",
343                         "what-d'you-|call-|it.".split('|'))
344
345    def test_funky_parens (self):
346        # Second part of SF bug #596434: long option strings inside
347        # parentheses.
348        self.check_split("foo (--option) bar",
349                         ["foo", " ", "(--option)", " ", "bar"])
350
351        # Related stuff -- make sure parens work in simpler contexts.
352        self.check_split("foo (bar) baz",
353                         ["foo", " ", "(bar)", " ", "baz"])
354        self.check_split("blah (ding dong), wubba",
355                         ["blah", " ", "(ding", " ", "dong),",
356                          " ", "wubba"])
357
358    def test_drop_whitespace_false(self):
359        # Check that drop_whitespace=False preserves whitespace.
360        # SF patch #1581073
361        text = " This is a    sentence with     much whitespace."
362        self.check_wrap(text, 10,
363                        [" This is a", "    ", "sentence ",
364                         "with     ", "much white", "space."],
365                        drop_whitespace=False)
366
367    def test_drop_whitespace_false_whitespace_only(self):
368        # Check that drop_whitespace=False preserves a whitespace-only string.
369        self.check_wrap("   ", 6, ["   "], drop_whitespace=False)
370
371    def test_drop_whitespace_false_whitespace_only_with_indent(self):
372        # Check that a whitespace-only string gets indented (when
373        # drop_whitespace is False).
374        self.check_wrap("   ", 6, ["     "], drop_whitespace=False,
375                        initial_indent="  ")
376
377    def test_drop_whitespace_whitespace_only(self):
378        # Check drop_whitespace on a whitespace-only string.
379        self.check_wrap("  ", 6, [])
380
381    def test_drop_whitespace_leading_whitespace(self):
382        # Check that drop_whitespace does not drop leading whitespace (if
383        # followed by non-whitespace).
384        # SF bug #622849 reported inconsistent handling of leading
385        # whitespace; let's test that a bit, shall we?
386        text = " This is a sentence with leading whitespace."
387        self.check_wrap(text, 50,
388                        [" This is a sentence with leading whitespace."])
389        self.check_wrap(text, 30,
390                        [" This is a sentence with", "leading whitespace."])
391
392    def test_drop_whitespace_whitespace_line(self):
393        # Check that drop_whitespace skips the whole line if a non-leading
394        # line consists only of whitespace.
395        text = "abcd    efgh"
396        # Include the result for drop_whitespace=False for comparison.
397        self.check_wrap(text, 6, ["abcd", "    ", "efgh"],
398                        drop_whitespace=False)
399        self.check_wrap(text, 6, ["abcd", "efgh"])
400
401    def test_drop_whitespace_whitespace_only_with_indent(self):
402        # Check that initial_indent is not applied to a whitespace-only
403        # string.  This checks a special case of the fact that dropping
404        # whitespace occurs before indenting.
405        self.check_wrap("  ", 6, [], initial_indent="++")
406
407    def test_drop_whitespace_whitespace_indent(self):
408        # Check that drop_whitespace does not drop whitespace indents.
409        # This checks a special case of the fact that dropping whitespace
410        # occurs before indenting.
411        self.check_wrap("abcd efgh", 6, ["  abcd", "  efgh"],
412                        initial_indent="  ", subsequent_indent="  ")
413
414    def test_split(self):
415        # Ensure that the standard _split() method works as advertised
416        # in the comments
417
418        text = "Hello there -- you goof-ball, use the -b option!"
419
420        result = self.wrapper._split(text)
421        self.check(result,
422             ["Hello", " ", "there", " ", "--", " ", "you", " ", "goof-",
423              "ball,", " ", "use", " ", "the", " ", "-b", " ",  "option!"])
424
425    def test_break_on_hyphens(self):
426        # Ensure that the break_on_hyphens attributes work
427        text = "yaba daba-doo"
428        self.check_wrap(text, 10, ["yaba daba-", "doo"],
429                        break_on_hyphens=True)
430        self.check_wrap(text, 10, ["yaba", "daba-doo"],
431                        break_on_hyphens=False)
432
433    def test_bad_width(self):
434        # Ensure that width <= 0 is caught.
435        text = "Whatever, it doesn't matter."
436        self.assertRaises(ValueError, wrap, text, 0)
437        self.assertRaises(ValueError, wrap, text, -1)
438
439    def test_no_split_at_umlaut(self):
440        text = "Die Empf\xe4nger-Auswahl"
441        self.check_wrap(text, 13, ["Die", "Empf\xe4nger-", "Auswahl"])
442
443    def test_umlaut_followed_by_dash(self):
444        text = "aa \xe4\xe4-\xe4\xe4"
445        self.check_wrap(text, 7, ["aa \xe4\xe4-", "\xe4\xe4"])
446
447    def test_non_breaking_space(self):
448        text = 'This is a sentence with non-breaking\N{NO-BREAK SPACE}space.'
449
450        self.check_wrap(text, 20,
451                        ['This is a sentence',
452                         'with non-',
453                         'breaking\N{NO-BREAK SPACE}space.'],
454                        break_on_hyphens=True)
455
456        self.check_wrap(text, 20,
457                        ['This is a sentence',
458                         'with',
459                         'non-breaking\N{NO-BREAK SPACE}space.'],
460                        break_on_hyphens=False)
461
462    def test_narrow_non_breaking_space(self):
463        text = ('This is a sentence with non-breaking'
464                '\N{NARROW NO-BREAK SPACE}space.')
465
466        self.check_wrap(text, 20,
467                        ['This is a sentence',
468                         'with non-',
469                         'breaking\N{NARROW NO-BREAK SPACE}space.'],
470                        break_on_hyphens=True)
471
472        self.check_wrap(text, 20,
473                        ['This is a sentence',
474                         'with',
475                         'non-breaking\N{NARROW NO-BREAK SPACE}space.'],
476                        break_on_hyphens=False)
477
478
479class MaxLinesTestCase(BaseTestCase):
480    text = "Hello there, how are you this fine day?  I'm glad to hear it!"
481
482    def test_simple(self):
483        self.check_wrap(self.text, 12,
484                        ["Hello [...]"],
485                        max_lines=0)
486        self.check_wrap(self.text, 12,
487                        ["Hello [...]"],
488                        max_lines=1)
489        self.check_wrap(self.text, 12,
490                        ["Hello there,",
491                         "how [...]"],
492                        max_lines=2)
493        self.check_wrap(self.text, 13,
494                        ["Hello there,",
495                         "how are [...]"],
496                        max_lines=2)
497        self.check_wrap(self.text, 80, [self.text], max_lines=1)
498        self.check_wrap(self.text, 12,
499                        ["Hello there,",
500                         "how are you",
501                         "this fine",
502                         "day?  I'm",
503                         "glad to hear",
504                         "it!"],
505                        max_lines=6)
506
507    def test_spaces(self):
508        # strip spaces before placeholder
509        self.check_wrap(self.text, 12,
510                        ["Hello there,",
511                         "how are you",
512                         "this fine",
513                         "day? [...]"],
514                        max_lines=4)
515        # placeholder at the start of line
516        self.check_wrap(self.text, 6,
517                        ["Hello",
518                         "[...]"],
519                        max_lines=2)
520        # final spaces
521        self.check_wrap(self.text + ' ' * 10, 12,
522                        ["Hello there,",
523                         "how are you",
524                         "this fine",
525                         "day?  I'm",
526                         "glad to hear",
527                         "it!"],
528                        max_lines=6)
529
530    def test_placeholder(self):
531        self.check_wrap(self.text, 12,
532                        ["Hello..."],
533                        max_lines=1,
534                        placeholder='...')
535        self.check_wrap(self.text, 12,
536                        ["Hello there,",
537                         "how are..."],
538                        max_lines=2,
539                        placeholder='...')
540        # long placeholder and indentation
541        with self.assertRaises(ValueError):
542            wrap(self.text, 16, initial_indent='    ',
543                 max_lines=1, placeholder=' [truncated]...')
544        with self.assertRaises(ValueError):
545            wrap(self.text, 16, subsequent_indent='    ',
546                 max_lines=2, placeholder=' [truncated]...')
547        self.check_wrap(self.text, 16,
548                        ["    Hello there,",
549                         "  [truncated]..."],
550                        max_lines=2,
551                        initial_indent='    ',
552                        subsequent_indent='  ',
553                        placeholder=' [truncated]...')
554        self.check_wrap(self.text, 16,
555                        ["  [truncated]..."],
556                        max_lines=1,
557                        initial_indent='  ',
558                        subsequent_indent='    ',
559                        placeholder=' [truncated]...')
560        self.check_wrap(self.text, 80, [self.text], placeholder='.' * 1000)
561
562
563class LongWordTestCase (BaseTestCase):
564    def setUp(self):
565        self.wrapper = TextWrapper()
566        self.text = '''\
567Did you say "supercalifragilisticexpialidocious?"
568How *do* you spell that odd word, anyways?
569'''
570
571    def test_break_long(self):
572        # Wrap text with long words and lots of punctuation
573
574        self.check_wrap(self.text, 30,
575                        ['Did you say "supercalifragilis',
576                         'ticexpialidocious?" How *do*',
577                         'you spell that odd word,',
578                         'anyways?'])
579        self.check_wrap(self.text, 50,
580                        ['Did you say "supercalifragilisticexpialidocious?"',
581                         'How *do* you spell that odd word, anyways?'])
582
583        # SF bug 797650.  Prevent an infinite loop by making sure that at
584        # least one character gets split off on every pass.
585        self.check_wrap('-'*10+'hello', 10,
586                        ['----------',
587                         '               h',
588                         '               e',
589                         '               l',
590                         '               l',
591                         '               o'],
592                        subsequent_indent = ' '*15)
593
594        # bug 1146.  Prevent a long word to be wrongly wrapped when the
595        # preceding word is exactly one character shorter than the width
596        self.check_wrap(self.text, 12,
597                        ['Did you say ',
598                         '"supercalifr',
599                         'agilisticexp',
600                         'ialidocious?',
601                         '" How *do*',
602                         'you spell',
603                         'that odd',
604                         'word,',
605                         'anyways?'])
606
607    def test_nobreak_long(self):
608        # Test with break_long_words disabled
609        self.wrapper.break_long_words = 0
610        self.wrapper.width = 30
611        expect = ['Did you say',
612                  '"supercalifragilisticexpialidocious?"',
613                  'How *do* you spell that odd',
614                  'word, anyways?'
615                  ]
616        result = self.wrapper.wrap(self.text)
617        self.check(result, expect)
618
619        # Same thing with kwargs passed to standalone wrap() function.
620        result = wrap(self.text, width=30, break_long_words=0)
621        self.check(result, expect)
622
623    def test_max_lines_long(self):
624        self.check_wrap(self.text, 12,
625                        ['Did you say ',
626                         '"supercalifr',
627                         'agilisticexp',
628                         '[...]'],
629                        max_lines=4)
630
631
632class IndentTestCases(BaseTestCase):
633
634    # called before each test method
635    def setUp(self):
636        self.text = '''\
637This paragraph will be filled, first without any indentation,
638and then with some (including a hanging indent).'''
639
640
641    def test_fill(self):
642        # Test the fill() method
643
644        expect = '''\
645This paragraph will be filled, first
646without any indentation, and then with
647some (including a hanging indent).'''
648
649        result = fill(self.text, 40)
650        self.check(result, expect)
651
652
653    def test_initial_indent(self):
654        # Test initial_indent parameter
655
656        expect = ["     This paragraph will be filled,",
657                  "first without any indentation, and then",
658                  "with some (including a hanging indent)."]
659        result = wrap(self.text, 40, initial_indent="     ")
660        self.check(result, expect)
661
662        expect = "\n".join(expect)
663        result = fill(self.text, 40, initial_indent="     ")
664        self.check(result, expect)
665
666
667    def test_subsequent_indent(self):
668        # Test subsequent_indent parameter
669
670        expect = '''\
671  * This paragraph will be filled, first
672    without any indentation, and then
673    with some (including a hanging
674    indent).'''
675
676        result = fill(self.text, 40,
677                      initial_indent="  * ", subsequent_indent="    ")
678        self.check(result, expect)
679
680
681# Despite the similar names, DedentTestCase is *not* the inverse
682# of IndentTestCase!
683class DedentTestCase(unittest.TestCase):
684
685    def assertUnchanged(self, text):
686        """assert that dedent() has no effect on 'text'"""
687        self.assertEqual(text, dedent(text))
688
689    def test_dedent_nomargin(self):
690        # No lines indented.
691        text = "Hello there.\nHow are you?\nOh good, I'm glad."
692        self.assertUnchanged(text)
693
694        # Similar, with a blank line.
695        text = "Hello there.\n\nBoo!"
696        self.assertUnchanged(text)
697
698        # Some lines indented, but overall margin is still zero.
699        text = "Hello there.\n  This is indented."
700        self.assertUnchanged(text)
701
702        # Again, add a blank line.
703        text = "Hello there.\n\n  Boo!\n"
704        self.assertUnchanged(text)
705
706    def test_dedent_even(self):
707        # All lines indented by two spaces.
708        text = "  Hello there.\n  How are ya?\n  Oh good."
709        expect = "Hello there.\nHow are ya?\nOh good."
710        self.assertEqual(expect, dedent(text))
711
712        # Same, with blank lines.
713        text = "  Hello there.\n\n  How are ya?\n  Oh good.\n"
714        expect = "Hello there.\n\nHow are ya?\nOh good.\n"
715        self.assertEqual(expect, dedent(text))
716
717        # Now indent one of the blank lines.
718        text = "  Hello there.\n  \n  How are ya?\n  Oh good.\n"
719        expect = "Hello there.\n\nHow are ya?\nOh good.\n"
720        self.assertEqual(expect, dedent(text))
721
722    def test_dedent_uneven(self):
723        # Lines indented unevenly.
724        text = '''\
725        def foo():
726            while 1:
727                return foo
728        '''
729        expect = '''\
730def foo():
731    while 1:
732        return foo
733'''
734        self.assertEqual(expect, dedent(text))
735
736        # Uneven indentation with a blank line.
737        text = "  Foo\n    Bar\n\n   Baz\n"
738        expect = "Foo\n  Bar\n\n Baz\n"
739        self.assertEqual(expect, dedent(text))
740
741        # Uneven indentation with a whitespace-only line.
742        text = "  Foo\n    Bar\n \n   Baz\n"
743        expect = "Foo\n  Bar\n\n Baz\n"
744        self.assertEqual(expect, dedent(text))
745
746    # dedent() should not mangle internal tabs
747    def test_dedent_preserve_internal_tabs(self):
748        text = "  hello\tthere\n  how are\tyou?"
749        expect = "hello\tthere\nhow are\tyou?"
750        self.assertEqual(expect, dedent(text))
751
752        # make sure that it preserves tabs when it's not making any
753        # changes at all
754        self.assertEqual(expect, dedent(expect))
755
756    # dedent() should not mangle tabs in the margin (i.e.
757    # tabs and spaces both count as margin, but are *not*
758    # considered equivalent)
759    def test_dedent_preserve_margin_tabs(self):
760        text = "  hello there\n\thow are you?"
761        self.assertUnchanged(text)
762
763        # same effect even if we have 8 spaces
764        text = "        hello there\n\thow are you?"
765        self.assertUnchanged(text)
766
767        # dedent() only removes whitespace that can be uniformly removed!
768        text = "\thello there\n\thow are you?"
769        expect = "hello there\nhow are you?"
770        self.assertEqual(expect, dedent(text))
771
772        text = "  \thello there\n  \thow are you?"
773        self.assertEqual(expect, dedent(text))
774
775        text = "  \t  hello there\n  \t  how are you?"
776        self.assertEqual(expect, dedent(text))
777
778        text = "  \thello there\n  \t  how are you?"
779        expect = "hello there\n  how are you?"
780        self.assertEqual(expect, dedent(text))
781
782        # test margin is smaller than smallest indent
783        text = "  \thello there\n   \thow are you?\n \tI'm fine, thanks"
784        expect = " \thello there\n  \thow are you?\n\tI'm fine, thanks"
785        self.assertEqual(expect, dedent(text))
786
787
788# Test textwrap.indent
789class IndentTestCase(unittest.TestCase):
790    # The examples used for tests. If any of these change, the expected
791    # results in the various test cases must also be updated.
792    # The roundtrip cases are separate, because textwrap.dedent doesn't
793    # handle Windows line endings
794    ROUNDTRIP_CASES = (
795      # Basic test case
796      "Hi.\nThis is a test.\nTesting.",
797      # Include a blank line
798      "Hi.\nThis is a test.\n\nTesting.",
799      # Include leading and trailing blank lines
800      "\nHi.\nThis is a test.\nTesting.\n",
801    )
802    CASES = ROUNDTRIP_CASES + (
803      # Use Windows line endings
804      "Hi.\r\nThis is a test.\r\nTesting.\r\n",
805      # Pathological case
806      "\nHi.\r\nThis is a test.\n\r\nTesting.\r\n\n",
807    )
808
809    def test_indent_nomargin_default(self):
810        # indent should do nothing if 'prefix' is empty.
811        for text in self.CASES:
812            self.assertEqual(indent(text, ''), text)
813
814    def test_indent_nomargin_explicit_default(self):
815        # The same as test_indent_nomargin, but explicitly requesting
816        # the default behaviour by passing None as the predicate
817        for text in self.CASES:
818            self.assertEqual(indent(text, '', None), text)
819
820    def test_indent_nomargin_all_lines(self):
821        # The same as test_indent_nomargin, but using the optional
822        # predicate argument
823        predicate = lambda line: True
824        for text in self.CASES:
825            self.assertEqual(indent(text, '', predicate), text)
826
827    def test_indent_no_lines(self):
828        # Explicitly skip indenting any lines
829        predicate = lambda line: False
830        for text in self.CASES:
831            self.assertEqual(indent(text, '    ', predicate), text)
832
833    def test_roundtrip_spaces(self):
834        # A whitespace prefix should roundtrip with dedent
835        for text in self.ROUNDTRIP_CASES:
836            self.assertEqual(dedent(indent(text, '    ')), text)
837
838    def test_roundtrip_tabs(self):
839        # A whitespace prefix should roundtrip with dedent
840        for text in self.ROUNDTRIP_CASES:
841            self.assertEqual(dedent(indent(text, '\t\t')), text)
842
843    def test_roundtrip_mixed(self):
844        # A whitespace prefix should roundtrip with dedent
845        for text in self.ROUNDTRIP_CASES:
846            self.assertEqual(dedent(indent(text, ' \t  \t ')), text)
847
848    def test_indent_default(self):
849        # Test default indenting of lines that are not whitespace only
850        prefix = '  '
851        expected = (
852          # Basic test case
853          "  Hi.\n  This is a test.\n  Testing.",
854          # Include a blank line
855          "  Hi.\n  This is a test.\n\n  Testing.",
856          # Include leading and trailing blank lines
857          "\n  Hi.\n  This is a test.\n  Testing.\n",
858          # Use Windows line endings
859          "  Hi.\r\n  This is a test.\r\n  Testing.\r\n",
860          # Pathological case
861          "\n  Hi.\r\n  This is a test.\n\r\n  Testing.\r\n\n",
862        )
863        for text, expect in zip(self.CASES, expected):
864            self.assertEqual(indent(text, prefix), expect)
865
866    def test_indent_explicit_default(self):
867        # Test default indenting of lines that are not whitespace only
868        prefix = '  '
869        expected = (
870          # Basic test case
871          "  Hi.\n  This is a test.\n  Testing.",
872          # Include a blank line
873          "  Hi.\n  This is a test.\n\n  Testing.",
874          # Include leading and trailing blank lines
875          "\n  Hi.\n  This is a test.\n  Testing.\n",
876          # Use Windows line endings
877          "  Hi.\r\n  This is a test.\r\n  Testing.\r\n",
878          # Pathological case
879          "\n  Hi.\r\n  This is a test.\n\r\n  Testing.\r\n\n",
880        )
881        for text, expect in zip(self.CASES, expected):
882            self.assertEqual(indent(text, prefix, None), expect)
883
884    def test_indent_all_lines(self):
885        # Add 'prefix' to all lines, including whitespace-only ones.
886        prefix = '  '
887        expected = (
888          # Basic test case
889          "  Hi.\n  This is a test.\n  Testing.",
890          # Include a blank line
891          "  Hi.\n  This is a test.\n  \n  Testing.",
892          # Include leading and trailing blank lines
893          "  \n  Hi.\n  This is a test.\n  Testing.\n",
894          # Use Windows line endings
895          "  Hi.\r\n  This is a test.\r\n  Testing.\r\n",
896          # Pathological case
897          "  \n  Hi.\r\n  This is a test.\n  \r\n  Testing.\r\n  \n",
898        )
899        predicate = lambda line: True
900        for text, expect in zip(self.CASES, expected):
901            self.assertEqual(indent(text, prefix, predicate), expect)
902
903    def test_indent_empty_lines(self):
904        # Add 'prefix' solely to whitespace-only lines.
905        prefix = '  '
906        expected = (
907          # Basic test case
908          "Hi.\nThis is a test.\nTesting.",
909          # Include a blank line
910          "Hi.\nThis is a test.\n  \nTesting.",
911          # Include leading and trailing blank lines
912          "  \nHi.\nThis is a test.\nTesting.\n",
913          # Use Windows line endings
914          "Hi.\r\nThis is a test.\r\nTesting.\r\n",
915          # Pathological case
916          "  \nHi.\r\nThis is a test.\n  \r\nTesting.\r\n  \n",
917        )
918        predicate = lambda line: not line.strip()
919        for text, expect in zip(self.CASES, expected):
920            self.assertEqual(indent(text, prefix, predicate), expect)
921
922
923class ShortenTestCase(BaseTestCase):
924
925    def check_shorten(self, text, width, expect, **kwargs):
926        result = shorten(text, width, **kwargs)
927        self.check(result, expect)
928
929    def test_simple(self):
930        # Simple case: just words, spaces, and a bit of punctuation
931        text = "Hello there, how are you this fine day? I'm glad to hear it!"
932
933        self.check_shorten(text, 18, "Hello there, [...]")
934        self.check_shorten(text, len(text), text)
935        self.check_shorten(text, len(text) - 1,
936            "Hello there, how are you this fine day? "
937            "I'm glad to [...]")
938
939    def test_placeholder(self):
940        text = "Hello there, how are you this fine day? I'm glad to hear it!"
941
942        self.check_shorten(text, 17, "Hello there,$$", placeholder='$$')
943        self.check_shorten(text, 18, "Hello there, how$$", placeholder='$$')
944        self.check_shorten(text, 18, "Hello there, $$", placeholder=' $$')
945        self.check_shorten(text, len(text), text, placeholder='$$')
946        self.check_shorten(text, len(text) - 1,
947            "Hello there, how are you this fine day? "
948            "I'm glad to hear$$", placeholder='$$')
949
950    def test_empty_string(self):
951        self.check_shorten("", 6, "")
952
953    def test_whitespace(self):
954        # Whitespace collapsing
955        text = """
956            This is a  paragraph that  already has
957            line breaks and \t tabs too."""
958        self.check_shorten(text, 62,
959                             "This is a paragraph that already has line "
960                             "breaks and tabs too.")
961        self.check_shorten(text, 61,
962                             "This is a paragraph that already has line "
963                             "breaks and [...]")
964
965        self.check_shorten("hello      world!  ", 12, "hello world!")
966        self.check_shorten("hello      world!  ", 11, "hello [...]")
967        # The leading space is trimmed from the placeholder
968        # (it would be ugly otherwise).
969        self.check_shorten("hello      world!  ", 10, "[...]")
970
971    def test_width_too_small_for_placeholder(self):
972        shorten("x" * 20, width=8, placeholder="(......)")
973        with self.assertRaises(ValueError):
974            shorten("x" * 20, width=8, placeholder="(.......)")
975
976    def test_first_word_too_long_but_placeholder_fits(self):
977        self.check_shorten("Helloo", 5, "[...]")
978
979
980if __name__ == '__main__':
981    unittest.main()
982