1
# Copyright (C) 2007-2011 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""Unit tests for the bzrlib.help module."""
32
from bzrlib.tests.test_i18n import ZzzTranslations
36
class TestCommandHelp(tests.TestCase):
37
"""Tests for help on commands."""
39
def assertCmdHelp(self, expected, cmd):
40
self.assertEqualDiff(textwrap.dedent(expected), cmd.get_help_text())
42
def test_command_help_includes_see_also(self):
43
class cmd_WithSeeAlso(commands.Command):
44
__doc__ = """A sample command."""
45
_see_also = ['foo', 'bar']
46
self.assertCmdHelp('''\
47
Purpose: A sample command.
48
Usage: bzr WithSeeAlso
51
--usage Show usage message and options.
52
-v, --verbose Display more information.
53
-q, --quiet Only display errors and warnings.
54
-h, --help Show help message.
60
def test_get_help_text(self):
61
"""Commands have a get_help_text method which returns their help."""
62
class cmd_Demo(commands.Command):
63
__doc__ = """A sample command."""
64
self.assertCmdHelp('''\
65
Purpose: A sample command.
69
--usage Show usage message and options.
70
-v, --verbose Display more information.
71
-q, --quiet Only display errors and warnings.
72
-h, --help Show help message.
77
helptext = cmd.get_help_text()
78
self.assertStartsWith(helptext,
79
'Purpose: A sample command.\n'
81
self.assertEndsWith(helptext,
82
' -h, --help Show help message.\n\n')
84
def test_command_with_additional_see_also(self):
85
class cmd_WithSeeAlso(commands.Command):
86
__doc__ = """A sample command."""
87
_see_also = ['foo', 'bar']
88
cmd = cmd_WithSeeAlso()
89
helptext = cmd.get_help_text(['gam'])
92
' -v, --verbose Display more information.\n'
93
' -q, --quiet Only display errors and warnings.\n'
94
' -h, --help Show help message.\n'
96
'See also: bar, foo, gam\n')
98
def test_command_only_additional_see_also(self):
99
class cmd_WithSeeAlso(commands.Command):
100
__doc__ = """A sample command."""
101
cmd = cmd_WithSeeAlso()
102
helptext = cmd.get_help_text(['gam'])
105
' -v, --verbose Display more information.\n'
106
' -q, --quiet Only display errors and warnings.\n'
107
' -h, --help Show help message.\n'
111
def test_get_help_topic(self):
112
"""The help topic for a Command is its name()."""
113
class cmd_foo_bar(commands.Command):
114
__doc__ = """A sample command."""
116
self.assertEqual(cmd.name(), cmd.get_help_topic())
118
def test_formatted_help_text(self):
119
"""Help text should be plain text by default."""
120
class cmd_Demo(commands.Command):
121
__doc__ = """A sample command.
132
A code block follows.
139
helptext = cmd.get_help_text()
142
'Purpose: A sample command.\n'
146
' --usage Show usage message and options.\n'
147
' -v, --verbose Display more information.\n'
148
' -q, --quiet Only display errors and warnings.\n'
149
' -h, --help Show help message.\n'
160
' A code block follows.\n'
162
' bzr Demo something\n'
164
helptext = cmd.get_help_text(plain=False)
165
self.assertEquals(helptext,
166
':Purpose: A sample command.\n'
170
' --usage Show usage message and options.\n'
171
' -v, --verbose Display more information.\n'
172
' -q, --quiet Only display errors and warnings.\n'
173
' -h, --help Show help message.\n'
184
' A code block follows.\n'
188
' bzr Demo something\n'
191
def test_concise_help_text(self):
192
"""Concise help text excludes the descriptive sections."""
193
class cmd_Demo(commands.Command):
194
__doc__ = """A sample command.
204
helptext = cmd.get_help_text()
205
self.assertEqualDiff(
207
'Purpose: A sample command.\n'
211
' --usage Show usage message and options.\n'
212
' -v, --verbose Display more information.\n'
213
' -q, --quiet Only display errors and warnings.\n'
214
' -h, --help Show help message.\n'
224
helptext = cmd.get_help_text(verbose=False)
225
self.assertEquals(helptext,
226
'Purpose: A sample command.\n'
230
' --usage Show usage message and options.\n'
231
' -v, --verbose Display more information.\n'
232
' -q, --quiet Only display errors and warnings.\n'
233
' -h, --help Show help message.\n'
235
'See bzr help Demo for more details and examples.\n'
238
def test_help_custom_section_ordering(self):
239
"""Custom descriptive sections should remain in the order given."""
240
class cmd_Demo(commands.Command):
241
__doc__ = """A sample command.
246
Interesting stuff about formats.
254
Clever things to keep in mind.
257
helptext = cmd.get_help_text()
258
self.assertEqualDiff(
260
'Purpose: A sample command.\n'
264
' --usage Show usage message and options.\n'
265
' -v, --verbose Display more information.\n'
266
' -q, --quiet Only display errors and warnings.\n'
267
' -h, --help Show help message.\n'
273
' Interesting stuff about formats.\n'
281
' Clever things to keep in mind.\n'
284
def test_help_text_custom_usage(self):
285
"""Help text may contain a custom usage section."""
286
class cmd_Demo(commands.Command):
287
__doc__ = """A sample command.
297
helptext = cmd.get_help_text()
298
self.assertEquals(helptext,
299
'Purpose: A sample command.\n'
301
' cmd Demo [opts] args\n'
307
' --usage Show usage message and options.\n'
308
' -v, --verbose Display more information.\n'
309
' -q, --quiet Only display errors and warnings.\n'
310
' -h, --help Show help message.\n'
313
' Blah blah blah.\n\n')
316
class ZzzTranslationsForDoc(ZzzTranslations):
318
_section_pat = re.compile(':\w+:\\n\\s+')
319
_indent_pat = re.compile('\\s+')
322
m = self._section_pat.match(s)
324
m = self._indent_pat.match(s)
326
return u'%szz{{%s}}' % (m.group(0), s[m.end():])
327
return u'zz{{%s}}' % s
330
class TestCommandHelpI18n(tests.TestCase):
331
"""Tests for help on translated commands."""
334
super(TestCommandHelpI18n, self).setUp()
335
self.overrideAttr(i18n, '_translations', ZzzTranslationsForDoc())
337
def assertCmdHelp(self, expected, cmd):
338
self.assertEqualDiff(textwrap.dedent(expected), cmd.get_help_text())
340
def test_command_help_includes_see_also(self):
341
class cmd_WithSeeAlso(commands.Command):
342
__doc__ = """A sample command."""
343
_see_also = ['foo', 'bar']
344
self.assertCmdHelp('''\
345
zz{{:Purpose: zz{{A sample command.}}
346
}}zz{{:Usage: bzr WithSeeAlso
349
--usage Show usage message and options.
350
-v, --verbose Display more information.
351
-q, --quiet Only display errors and warnings.
352
-h, --help Show help message.
354
zz{{:See also: bar, foo}}
358
def test_get_help_text(self):
359
"""Commands have a get_help_text method which returns their help."""
360
class cmd_Demo(commands.Command):
361
__doc__ = """A sample command."""
362
self.assertCmdHelp('''\
363
zz{{:Purpose: zz{{A sample command.}}
364
}}zz{{:Usage: bzr Demo
367
--usage Show usage message and options.
368
-v, --verbose Display more information.
369
-q, --quiet Only display errors and warnings.
370
-h, --help Show help message.
375
def test_command_with_additional_see_also(self):
376
class cmd_WithSeeAlso(commands.Command):
377
__doc__ = """A sample command."""
378
_see_also = ['foo', 'bar']
379
cmd = cmd_WithSeeAlso()
380
helptext = cmd.get_help_text(['gam'])
383
' -v, --verbose Display more information.\n'
384
' -q, --quiet Only display errors and warnings.\n'
385
' -h, --help Show help message.\n'
387
'zz{{:See also: bar, foo, gam}}\n')
389
def test_command_only_additional_see_also(self):
390
class cmd_WithSeeAlso(commands.Command):
391
__doc__ = """A sample command."""
392
cmd = cmd_WithSeeAlso()
393
helptext = cmd.get_help_text(['gam'])
397
' --usage Show usage message and options.\n'
398
' -v, --verbose Display more information.\n'
399
' -q, --quiet Only display errors and warnings.\n'
400
' -h, --help Show help message.\n'
402
'zz{{:See also: gam}}\n')
405
def test_help_custom_section_ordering(self):
406
"""Custom descriptive sections should remain in the order given."""
407
# The help formatter expect the class name to start with 'cmd_'
408
class cmd_Demo(commands.Command):
409
__doc__ = """A sample command.
414
Interesting stuff about formats.
422
Clever things to keep in mind.
424
self.assertCmdHelp('''\
425
zz{{:Purpose: zz{{A sample command.}}
426
}}zz{{:Usage: bzr Demo
429
--usage Show usage message and options.
430
-v, --verbose Display more information.
431
-q, --quiet Only display errors and warnings.
432
-h, --help Show help message.
435
zz{{zz{{Blah blah blah.}}
438
zz{{Interesting stuff about formats.}}
446
zz{{Clever things to keep in mind.}}
451
def test_help_text_custom_usage(self):
452
"""Help text may contain a custom usage section."""
453
class cmd_Demo(commands.Command):
454
__doc__ = """A sample command.
463
self.assertCmdHelp('''\
464
zz{{:Purpose: zz{{A sample command.}}
466
zz{{cmd Demo [opts] args}}
472
--usage Show usage message and options.
473
-v, --verbose Display more information.
474
-q, --quiet Only display errors and warnings.
475
-h, --help Show help message.
478
zz{{zz{{Blah blah blah.}}
485
class TestHelp(tests.TestCase):
488
tests.TestCase.setUp(self)
489
commands.install_bzr_command_hooks()
492
class TestRegisteredTopic(TestHelp):
493
"""Tests for the RegisteredTopic class."""
495
def test_contruct(self):
496
"""Construction takes the help topic name for the registered item."""
498
self.assertTrue('basic' in help_topics.topic_registry)
499
topic = help_topics.RegisteredTopic('basic')
500
self.assertEqual('basic', topic.topic)
502
def test_get_help_text(self):
503
"""RegisteredTopic returns the get_detail results for get_help_text."""
504
topic = help_topics.RegisteredTopic('commands')
505
self.assertEqual(help_topics.topic_registry.get_detail('commands'),
506
topic.get_help_text())
508
def test_get_help_text_with_additional_see_also(self):
509
topic = help_topics.RegisteredTopic('commands')
511
topic.get_help_text(['foo', 'bar']),
513
'See also: bar, foo\n')
515
def test_get_help_text_loaded_from_file(self):
516
# Pick a known topic stored in an external file
517
topic = help_topics.RegisteredTopic('authentication')
518
self.assertStartsWith(topic.get_help_text(),
519
'Authentication Settings\n'
520
'=======================\n'
523
def test_get_help_topic(self):
524
"""The help topic for RegisteredTopic is its topic from construction."""
525
topic = help_topics.RegisteredTopic('foobar')
526
self.assertEqual('foobar', topic.get_help_topic())
527
topic = help_topics.RegisteredTopic('baz')
528
self.assertEqual('baz', topic.get_help_topic())
531
class TestTopicIndex(TestHelp):
532
"""Tests for the HelpTopicIndex class."""
534
def test_default_constructable(self):
535
index = help_topics.HelpTopicIndex()
537
def test_get_topics_None(self):
538
"""Searching for None returns the basic help topic."""
539
index = help_topics.HelpTopicIndex()
540
topics = index.get_topics(None)
541
self.assertEqual(1, len(topics))
542
self.assertIsInstance(topics[0], help_topics.RegisteredTopic)
543
self.assertEqual('basic', topics[0].topic)
545
def test_get_topics_topics(self):
546
"""Searching for a string returns the matching string."""
547
index = help_topics.HelpTopicIndex()
548
topics = index.get_topics('topics')
549
self.assertEqual(1, len(topics))
550
self.assertIsInstance(topics[0], help_topics.RegisteredTopic)
551
self.assertEqual('topics', topics[0].topic)
553
def test_get_topics_no_topic(self):
554
"""Searching for something not registered returns []."""
555
index = help_topics.HelpTopicIndex()
556
self.assertEqual([], index.get_topics('nothing by this name'))
558
def test_prefix(self):
559
"""TopicIndex has a prefix of ''."""
560
index = help_topics.HelpTopicIndex()
561
self.assertEqual('', index.prefix)
564
class TestCommandIndex(TestHelp):
565
"""Tests for the HelpCommandIndex class."""
567
def test_default_constructable(self):
568
index = commands.HelpCommandIndex()
570
def test_get_topics_None(self):
571
"""Searching for None returns an empty list."""
572
index = commands.HelpCommandIndex()
573
self.assertEqual([], index.get_topics(None))
575
def test_get_topics_rocks(self):
576
"""Searching for 'rocks' returns the cmd_rocks command instance."""
577
index = commands.HelpCommandIndex()
578
topics = index.get_topics('rocks')
579
self.assertEqual(1, len(topics))
580
self.assertIsInstance(topics[0], builtins.cmd_rocks)
582
def test_get_topics_no_topic(self):
583
"""Searching for something that is not a command returns []."""
584
index = commands.HelpCommandIndex()
585
self.assertEqual([], index.get_topics('nothing by this name'))
587
def test_prefix(self):
588
"""CommandIndex has a prefix of 'commands/'."""
589
index = commands.HelpCommandIndex()
590
self.assertEqual('commands/', index.prefix)
592
def test_get_topic_with_prefix(self):
593
"""Searching for commands/rocks returns the rocks command object."""
594
index = commands.HelpCommandIndex()
595
topics = index.get_topics('commands/rocks')
596
self.assertEqual(1, len(topics))
597
self.assertIsInstance(topics[0], builtins.cmd_rocks)
600
class TestHelpIndices(tests.TestCase):
601
"""Tests for the HelpIndices class."""
603
def test_default_search_path(self):
604
"""The default search path should include internal indexs."""
605
indices = help.HelpIndices()
606
self.assertEqual(3, len(indices.search_path))
607
# help topics should be searched in first.
608
self.assertIsInstance(indices.search_path[0],
609
help_topics.HelpTopicIndex)
610
# with commands being search second.
611
self.assertIsInstance(indices.search_path[1],
612
commands.HelpCommandIndex)
613
# and plugins are a third index.
614
self.assertIsInstance(indices.search_path[2],
615
plugin.PluginsHelpIndex)
617
def test_search_for_unknown_topic_raises(self):
618
"""Searching for an unknown topic should raise NoHelpTopic."""
619
indices = help.HelpIndices()
620
indices.search_path = []
621
error = self.assertRaises(errors.NoHelpTopic, indices.search, 'foo')
622
self.assertEqual('foo', error.topic)
624
def test_search_calls_get_topic(self):
625
"""Searching should call get_topics in all indexes in order."""
627
class RecordingIndex(object):
628
def __init__(self, name):
630
def get_topics(self, topic):
631
calls.append(('get_topics', self.prefix, topic))
633
index = help.HelpIndices()
634
index.search_path = [RecordingIndex('1'), RecordingIndex('2')]
638
('get_topics', '1', None),
639
('get_topics', '2', None),
646
('get_topics', '1', 'bar'),
647
('get_topics', '2', 'bar'),
651
def test_search_returns_index_and_results(self):
652
"""Searching should return help topics with their index"""
653
class CannedIndex(object):
654
def __init__(self, prefix, search_result):
656
self.result = search_result
657
def get_topics(self, topic):
659
index = help.HelpIndices()
660
index_one = CannedIndex('1', ['a'])
661
index_two = CannedIndex('2', ['b', 'c'])
662
index.search_path = [index_one, index_two]
663
self.assertEqual([(index_one, 'a'), (index_two, 'b'), (index_two, 'c')],
666
def test_search_checks_for_duplicate_prefixes(self):
667
"""Its an error when there are multiple indices with the same prefix."""
668
indices = help.HelpIndices()
669
indices.search_path = [help_topics.HelpTopicIndex(),
670
help_topics.HelpTopicIndex()]
671
self.assertRaises(errors.DuplicateHelpPrefix, indices.search, None)