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    def test_placeholder_backtrack(self):
563        # Test special case when max_lines insufficient, but what
564        # would be last wrapped line so long the placeholder cannot
565        # be added there without violence. So, textwrap backtracks,
566        # adding placeholder to the penultimate line.
567        text = 'Good grief Python features are advancing quickly!'
568        self.check_wrap(text, 12,
569                        ['Good grief', 'Python*****'],
570                        max_lines=3,
571                        placeholder='*****')
572
573
574class LongWordTestCase (BaseTestCase):
575    def setUp(self):
576        self.wrapper = TextWrapper()
577        self.text = '''\
578Did you say "supercalifragilisticexpialidocious?"
579How *do* you spell that odd word, anyways?
580'''
581
582    def test_break_long(self):
583        # Wrap text with long words and lots of punctuation
584
585        self.check_wrap(self.text, 30,
586                        ['Did you say "supercalifragilis',
587                         'ticexpialidocious?" How *do*',
588                         'you spell that odd word,',
589                         'anyways?'])
590        self.check_wrap(self.text, 50,
591                        ['Did you say "supercalifragilisticexpialidocious?"',
592                         'How *do* you spell that odd word, anyways?'])
593
594        # SF bug 797650.  Prevent an infinite loop by making sure that at
595        # least one character gets split off on every pass.
596        self.check_wrap('-'*10+'hello', 10,
597                        ['----------',
598                         '               h',
599                         '               e',
600                         '               l',
601                         '               l',
602                         '               o'],
603                        subsequent_indent = ' '*15)
604
605        # bug 1146.  Prevent a long word to be wrongly wrapped when the
606        # preceding word is exactly one character shorter than the width
607        self.check_wrap(self.text, 12,
608                        ['Did you say ',
609                         '"supercalifr',
610                         'agilisticexp',
611                         'ialidocious?',
612                         '" How *do*',
613                         'you spell',
614                         'that odd',
615                         'word,',
616                         'anyways?'])
617
618    def test_nobreak_long(self):
619        # Test with break_long_words disabled
620        self.wrapper.break_long_words = 0
621        self.wrapper.width = 30
622        expect = ['Did you say',
623                  '"supercalifragilisticexpialidocious?"',
624                  'How *do* you spell that odd',
625                  'word, anyways?'
626                  ]
627        result = self.wrapper.wrap(self.text)
628        self.check(result, expect)
629
630        # Same thing with kwargs passed to standalone wrap() function.
631        result = wrap(self.text, width=30, break_long_words=0)
632        self.check(result, expect)
633
634    def test_max_lines_long(self):
635        self.check_wrap(self.text, 12,
636                        ['Did you say ',
637                         '"supercalifr',
638                         'agilisticexp',
639                         '[...]'],
640                        max_lines=4)
641
642
643class IndentTestCases(BaseTestCase):
644
645    # called before each test method
646    def setUp(self):
647        self.text = '''\
648This paragraph will be filled, first without any indentation,
649and then with some (including a hanging indent).'''
650
651
652    def test_fill(self):
653        # Test the fill() method
654
655        expect = '''\
656This paragraph will be filled, first
657without any indentation, and then with
658some (including a hanging indent).'''
659
660        result = fill(self.text, 40)
661        self.check(result, expect)
662
663
664    def test_initial_indent(self):
665        # Test initial_indent parameter
666
667        expect = ["     This paragraph will be filled,",
668                  "first without any indentation, and then",
669                  "with some (including a hanging indent)."]
670        result = wrap(self.text, 40, initial_indent="     ")
671        self.check(result, expect)
672
673        expect = "\n".join(expect)
674        result = fill(self.text, 40, initial_indent="     ")
675        self.check(result, expect)
676
677
678    def test_subsequent_indent(self):
679        # Test subsequent_indent parameter
680
681        expect = '''\
682  * This paragraph will be filled, first
683    without any indentation, and then
684    with some (including a hanging
685    indent).'''
686
687        result = fill(self.text, 40,
688                      initial_indent="  * ", subsequent_indent="    ")
689        self.check(result, expect)
690
691
692# Despite the similar names, DedentTestCase is *not* the inverse
693# of IndentTestCase!
694class DedentTestCase(unittest.TestCase):
695
696    def assertUnchanged(self, text):
697        """assert that dedent() has no effect on 'text'"""
698        self.assertEqual(text, dedent(text))
699
700    def test_dedent_nomargin(self):
701        # No lines indented.
702        text = "Hello there.\nHow are you?\nOh good, I'm glad."
703        self.assertUnchanged(text)
704
705        # Similar, with a blank line.
706        text = "Hello there.\n\nBoo!"
707        self.assertUnchanged(text)
708
709        # Some lines indented, but overall margin is still zero.
710        text = "Hello there.\n  This is indented."
711        self.assertUnchanged(text)
712
713        # Again, add a blank line.
714        text = "Hello there.\n\n  Boo!\n"
715        self.assertUnchanged(text)
716
717    def test_dedent_even(self):
718        # All lines indented by two spaces.
719        text = "  Hello there.\n  How are ya?\n  Oh good."
720        expect = "Hello there.\nHow are ya?\nOh good."
721        self.assertEqual(expect, dedent(text))
722
723        # Same, with blank lines.
724        text = "  Hello there.\n\n  How are ya?\n  Oh good.\n"
725        expect = "Hello there.\n\nHow are ya?\nOh good.\n"
726        self.assertEqual(expect, dedent(text))
727
728        # Now indent one of the blank lines.
729        text = "  Hello there.\n  \n  How are ya?\n  Oh good.\n"
730        expect = "Hello there.\n\nHow are ya?\nOh good.\n"
731        self.assertEqual(expect, dedent(text))
732
733    def test_dedent_uneven(self):
734        # Lines indented unevenly.
735        text = '''\
736        def foo():
737            while 1:
738                return foo
739        '''
740        expect = '''\
741def foo():
742    while 1:
743        return foo
744'''
745        self.assertEqual(expect, dedent(text))
746
747        # Uneven indentation with a blank line.
748        text = "  Foo\n    Bar\n\n   Baz\n"
749        expect = "Foo\n  Bar\n\n Baz\n"
750        self.assertEqual(expect, dedent(text))
751
752        # Uneven indentation with a whitespace-only line.
753        text = "  Foo\n    Bar\n \n   Baz\n"
754        expect = "Foo\n  Bar\n\n Baz\n"
755        self.assertEqual(expect, dedent(text))
756
757    def test_dedent_declining(self):
758        # Uneven indentation with declining indent level.
759        text = "     Foo\n    Bar\n"  # 5 spaces, then 4
760        expect = " Foo\nBar\n"
761        self.assertEqual(expect, dedent(text))
762
763        # Declining indent level with blank line.
764        text = "     Foo\n\n    Bar\n"  # 5 spaces, blank, then 4
765        expect = " Foo\n\nBar\n"
766        self.assertEqual(expect, dedent(text))
767
768        # Declining indent level with whitespace only line.
769        text = "     Foo\n    \n    Bar\n"  # 5 spaces, then 4, then 4
770        expect = " Foo\n\nBar\n"
771        self.assertEqual(expect, dedent(text))
772
773    # dedent() should not mangle internal tabs
774    def test_dedent_preserve_internal_tabs(self):
775        text = "  hello\tthere\n  how are\tyou?"
776        expect = "hello\tthere\nhow are\tyou?"
777        self.assertEqual(expect, dedent(text))
778
779        # make sure that it preserves tabs when it's not making any
780        # changes at all
781        self.assertEqual(expect, dedent(expect))
782
783    # dedent() should not mangle tabs in the margin (i.e.
784    # tabs and spaces both count as margin, but are *not*
785    # considered equivalent)
786    def test_dedent_preserve_margin_tabs(self):
787        text = "  hello there\n\thow are you?"
788        self.assertUnchanged(text)
789
790        # same effect even if we have 8 spaces
791        text = "        hello there\n\thow are you?"
792        self.assertUnchanged(text)
793
794        # dedent() only removes whitespace that can be uniformly removed!
795        text = "\thello there\n\thow are you?"
796        expect = "hello there\nhow are you?"
797        self.assertEqual(expect, dedent(text))
798
799        text = "  \thello there\n  \thow are you?"
800        self.assertEqual(expect, dedent(text))
801
802        text = "  \t  hello there\n  \t  how are you?"
803        self.assertEqual(expect, dedent(text))
804
805        text = "  \thello there\n  \t  how are you?"
806        expect = "hello there\n  how are you?"
807        self.assertEqual(expect, dedent(text))
808
809        # test margin is smaller than smallest indent
810        text = "  \thello there\n   \thow are you?\n \tI'm fine, thanks"
811        expect = " \thello there\n  \thow are you?\n\tI'm fine, thanks"
812        self.assertEqual(expect, dedent(text))
813
814
815# Test textwrap.indent
816class IndentTestCase(unittest.TestCase):
817    # The examples used for tests. If any of these change, the expected
818    # results in the various test cases must also be updated.
819    # The roundtrip cases are separate, because textwrap.dedent doesn't
820    # handle Windows line endings
821    ROUNDTRIP_CASES = (
822      # Basic test case
823      "Hi.\nThis is a test.\nTesting.",
824      # Include a blank line
825      "Hi.\nThis is a test.\n\nTesting.",
826      # Include leading and trailing blank lines
827      "\nHi.\nThis is a test.\nTesting.\n",
828    )
829    CASES = ROUNDTRIP_CASES + (
830      # Use Windows line endings
831      "Hi.\r\nThis is a test.\r\nTesting.\r\n",
832      # Pathological case
833      "\nHi.\r\nThis is a test.\n\r\nTesting.\r\n\n",
834    )
835
836    def test_indent_nomargin_default(self):
837        # indent should do nothing if 'prefix' is empty.
838        for text in self.CASES:
839            self.assertEqual(indent(text, ''), text)
840
841    def test_indent_nomargin_explicit_default(self):
842        # The same as test_indent_nomargin, but explicitly requesting
843        # the default behaviour by passing None as the predicate
844        for text in self.CASES:
845            self.assertEqual(indent(text, '', None), text)
846
847    def test_indent_nomargin_all_lines(self):
848        # The same as test_indent_nomargin, but using the optional
849        # predicate argument
850        predicate = lambda line: True
851        for text in self.CASES:
852            self.assertEqual(indent(text, '', predicate), text)
853
854    def test_indent_no_lines(self):
855        # Explicitly skip indenting any lines
856        predicate = lambda line: False
857        for text in self.CASES:
858            self.assertEqual(indent(text, '    ', predicate), text)
859
860    def test_roundtrip_spaces(self):
861        # A whitespace prefix should roundtrip with dedent
862        for text in self.ROUNDTRIP_CASES:
863            self.assertEqual(dedent(indent(text, '    ')), text)
864
865    def test_roundtrip_tabs(self):
866        # A whitespace prefix should roundtrip with dedent
867        for text in self.ROUNDTRIP_CASES:
868            self.assertEqual(dedent(indent(text, '\t\t')), text)
869
870    def test_roundtrip_mixed(self):
871        # A whitespace prefix should roundtrip with dedent
872        for text in self.ROUNDTRIP_CASES:
873            self.assertEqual(dedent(indent(text, ' \t  \t ')), text)
874
875    def test_indent_default(self):
876        # Test default indenting of lines that are not whitespace only
877        prefix = '  '
878        expected = (
879          # Basic test case
880          "  Hi.\n  This is a test.\n  Testing.",
881          # Include a blank line
882          "  Hi.\n  This is a test.\n\n  Testing.",
883          # Include leading and trailing blank lines
884          "\n  Hi.\n  This is a test.\n  Testing.\n",
885          # Use Windows line endings
886          "  Hi.\r\n  This is a test.\r\n  Testing.\r\n",
887          # Pathological case
888          "\n  Hi.\r\n  This is a test.\n\r\n  Testing.\r\n\n",
889        )
890        for text, expect in zip(self.CASES, expected):
891            self.assertEqual(indent(text, prefix), expect)
892
893    def test_indent_explicit_default(self):
894        # Test default indenting of lines that are not whitespace only
895        prefix = '  '
896        expected = (
897          # Basic test case
898          "  Hi.\n  This is a test.\n  Testing.",
899          # Include a blank line
900          "  Hi.\n  This is a test.\n\n  Testing.",
901          # Include leading and trailing blank lines
902          "\n  Hi.\n  This is a test.\n  Testing.\n",
903          # Use Windows line endings
904          "  Hi.\r\n  This is a test.\r\n  Testing.\r\n",
905          # Pathological case
906          "\n  Hi.\r\n  This is a test.\n\r\n  Testing.\r\n\n",
907        )
908        for text, expect in zip(self.CASES, expected):
909            self.assertEqual(indent(text, prefix, None), expect)
910
911    def test_indent_all_lines(self):
912        # Add 'prefix' to all lines, including whitespace-only ones.
913        prefix = '  '
914        expected = (
915          # Basic test case
916          "  Hi.\n  This is a test.\n  Testing.",
917          # Include a blank line
918          "  Hi.\n  This is a test.\n  \n  Testing.",
919          # Include leading and trailing blank lines
920          "  \n  Hi.\n  This is a test.\n  Testing.\n",
921          # Use Windows line endings
922          "  Hi.\r\n  This is a test.\r\n  Testing.\r\n",
923          # Pathological case
924          "  \n  Hi.\r\n  This is a test.\n  \r\n  Testing.\r\n  \n",
925        )
926        predicate = lambda line: True
927        for text, expect in zip(self.CASES, expected):
928            self.assertEqual(indent(text, prefix, predicate), expect)
929
930    def test_indent_empty_lines(self):
931        # Add 'prefix' solely to whitespace-only lines.
932        prefix = '  '
933        expected = (
934          # Basic test case
935          "Hi.\nThis is a test.\nTesting.",
936          # Include a blank line
937          "Hi.\nThis is a test.\n  \nTesting.",
938          # Include leading and trailing blank lines
939          "  \nHi.\nThis is a test.\nTesting.\n",
940          # Use Windows line endings
941          "Hi.\r\nThis is a test.\r\nTesting.\r\n",
942          # Pathological case
943          "  \nHi.\r\nThis is a test.\n  \r\nTesting.\r\n  \n",
944        )
945        predicate = lambda line: not line.strip()
946        for text, expect in zip(self.CASES, expected):
947            self.assertEqual(indent(text, prefix, predicate), expect)
948
949
950class ShortenTestCase(BaseTestCase):
951
952    def check_shorten(self, text, width, expect, **kwargs):
953        result = shorten(text, width, **kwargs)
954        self.check(result, expect)
955
956    def test_simple(self):
957        # Simple case: just words, spaces, and a bit of punctuation
958        text = "Hello there, how are you this fine day? I'm glad to hear it!"
959
960        self.check_shorten(text, 18, "Hello there, [...]")
961        self.check_shorten(text, len(text), text)
962        self.check_shorten(text, len(text) - 1,
963            "Hello there, how are you this fine day? "
964            "I'm glad to [...]")
965
966    def test_placeholder(self):
967        text = "Hello there, how are you this fine day? I'm glad to hear it!"
968
969        self.check_shorten(text, 17, "Hello there,$$", placeholder='$$')
970        self.check_shorten(text, 18, "Hello there, how$$", placeholder='$$')
971        self.check_shorten(text, 18, "Hello there, $$", placeholder=' $$')
972        self.check_shorten(text, len(text), text, placeholder='$$')
973        self.check_shorten(text, len(text) - 1,
974            "Hello there, how are you this fine day? "
975            "I'm glad to hear$$", placeholder='$$')
976
977    def test_empty_string(self):
978        self.check_shorten("", 6, "")
979
980    def test_whitespace(self):
981        # Whitespace collapsing
982        text = """
983            This is a  paragraph that  already has
984            line breaks and \t tabs too."""
985        self.check_shorten(text, 62,
986                             "This is a paragraph that already has line "
987                             "breaks and tabs too.")
988        self.check_shorten(text, 61,
989                             "This is a paragraph that already has line "
990                             "breaks and [...]")
991
992        self.check_shorten("hello      world!  ", 12, "hello world!")
993        self.check_shorten("hello      world!  ", 11, "hello [...]")
994        # The leading space is trimmed from the placeholder
995        # (it would be ugly otherwise).
996        self.check_shorten("hello      world!  ", 10, "[...]")
997
998    def test_width_too_small_for_placeholder(self):
999        shorten("x" * 20, width=8, placeholder="(......)")
1000        with self.assertRaises(ValueError):
1001            shorten("x" * 20, width=8, placeholder="(.......)")
1002
1003    def test_first_word_too_long_but_placeholder_fits(self):
1004        self.check_shorten("Helloo", 5, "[...]")
1005
1006
1007if __name__ == '__main__':
1008    unittest.main()
1009