~bzr-pqm/bzr/bzr.dev

2425.2.2 by Robert Collins
``bzr help`` now provides cross references to other help topics using the
1
# Copyright (C) 2007 Canonical Ltd
2
#
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.
7
#
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.
12
#
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
"""Unit tests for the bzrlib.help module."""
18
19
from cStringIO import StringIO
20
21
from bzrlib import (
2432.1.13 by Robert Collins
HelpCommandContext now implementes get_topics.
22
    builtins,
2425.2.2 by Robert Collins
``bzr help`` now provides cross references to other help topics using the
23
    commands,
2432.1.5 by Robert Collins
Initial stub for topic searching.
24
    errors,
2425.2.2 by Robert Collins
``bzr help`` now provides cross references to other help topics using the
25
    help,
2432.1.1 by Robert Collins
Add a HelpTopicContext object.
26
    help_topics,
2432.1.24 by Robert Collins
Add plugins as a help index.
27
    plugin,
2425.2.2 by Robert Collins
``bzr help`` now provides cross references to other help topics using the
28
    tests,
29
    )
30
31
32
class TestCommandHelp(tests.TestCase):
33
    """Tests for help on commands."""
34
35
    def test_command_help_includes_see_also(self):
36
        class cmd_WithSeeAlso(commands.Command):
37
            """A sample command."""
38
            _see_also = ['foo', 'bar']
39
        cmd = cmd_WithSeeAlso()
2432.1.12 by Robert Collins
Relocate command help onto Command.
40
        helptext = cmd.get_help_text()
2425.1.3 by Robert Collins
Python 2.4 compatability change for the new help see-also tests.
41
        self.assertEndsWith(
2432.1.12 by Robert Collins
Relocate command help onto Command.
42
            helptext,
2768.1.6 by Ian Clatworthy
Fix existing option and help tests
43
            '  -v, --verbose  Display more information.\n'
44
            '  -q, --quiet    Only display errors and warnings.\n'
45
            '  -h, --help     Show help message.\n'
2425.2.2 by Robert Collins
``bzr help`` now provides cross references to other help topics using the
46
            '\n'
2425.1.3 by Robert Collins
Python 2.4 compatability change for the new help see-also tests.
47
            'See also: bar, foo\n')
2432.1.1 by Robert Collins
Add a HelpTopicContext object.
48
2432.1.12 by Robert Collins
Relocate command help onto Command.
49
    def test_get_help_text(self):
50
        """Commands have a get_help_text method which returns their help."""
51
        class cmd_Demo(commands.Command):
52
            """A sample command."""
53
        cmd = cmd_Demo()
54
        helptext = cmd.get_help_text()
2666.1.4 by Ian Clatworthy
Add help formatting tests
55
        self.assertStartsWith(helptext,
56
            'Purpose: A sample command.\n'
57
            'Usage:   bzr Demo')
2768.1.6 by Ian Clatworthy
Fix existing option and help tests
58
        self.assertEndsWith(helptext,
59
            '  -h, --help     Show help message.\n\n')
2432.1.21 by Robert Collins
Teach Command.get_help_text to show additional help cross references when supplied.
60
61
    def test_command_with_additional_see_also(self):
62
        class cmd_WithSeeAlso(commands.Command):
63
            """A sample command."""
64
            _see_also = ['foo', 'bar']
65
        cmd = cmd_WithSeeAlso()
66
        helptext = cmd.get_help_text(['gam'])
67
        self.assertEndsWith(
68
            helptext,
2768.1.6 by Ian Clatworthy
Fix existing option and help tests
69
            '  -v, --verbose  Display more information.\n'
70
            '  -q, --quiet    Only display errors and warnings.\n'
71
            '  -h, --help     Show help message.\n'
2432.1.21 by Robert Collins
Teach Command.get_help_text to show additional help cross references when supplied.
72
            '\n'
73
            'See also: bar, foo, gam\n')
74
75
    def test_command_only_additional_see_also(self):
76
        class cmd_WithSeeAlso(commands.Command):
77
            """A sample command."""
78
        cmd = cmd_WithSeeAlso()
79
        helptext = cmd.get_help_text(['gam'])
80
        self.assertEndsWith(
81
            helptext,
2768.1.6 by Ian Clatworthy
Fix existing option and help tests
82
            '  -v, --verbose  Display more information.\n'
83
            '  -q, --quiet    Only display errors and warnings.\n'
84
            '  -h, --help     Show help message.\n'
2432.1.21 by Robert Collins
Teach Command.get_help_text to show additional help cross references when supplied.
85
            '\n'
86
            'See also: gam\n')
2432.1.28 by Robert Collins
Add a get_help_topic method to commands.Command.
87
88
    def test_get_help_topic(self):
89
        """The help topic for a Command is its name()."""
90
        class cmd_foo_bar(commands.Command):
91
            """A sample command."""
92
        cmd = cmd_foo_bar()
93
        self.assertEqual(cmd.name(), cmd.get_help_topic())
2666.1.4 by Ian Clatworthy
Add help formatting tests
94
95
    def test_formatted_help_text(self):
96
        """Help text should be plain text by default."""
97
        class cmd_Demo(commands.Command):
98
            """A sample command.
99
 
100
            :Examples:
101
                Example 1::
102
 
103
                    cmd arg1
104
 
105
                Example 2::
106
 
107
                    cmd arg2
108
            """
109
        cmd = cmd_Demo()
110
        helptext = cmd.get_help_text()
111
        self.assertEquals(
112
            helptext,
113
            'Purpose: A sample command.\n'
114
            'Usage:   bzr Demo\n'
115
            '\n'
116
            'Options:\n'
2768.1.6 by Ian Clatworthy
Fix existing option and help tests
117
            '  -v, --verbose  Display more information.\n'
118
            '  -q, --quiet    Only display errors and warnings.\n'
119
            '  -h, --help     Show help message.\n'
2666.1.4 by Ian Clatworthy
Add help formatting tests
120
            '\n'
121
            'Examples:\n'
122
            '    Example 1:\n'
123
            '\n'
124
            '        cmd arg1\n'
125
            '\n'
126
            '    Example 2:\n'
127
            '\n'
128
            '        cmd arg2\n'
129
            '\n')
130
        helptext = cmd.get_help_text(plain=False)
131
        self.assertEquals(helptext,
132
            ':Purpose: A sample command.\n'
133
            ':Usage:   bzr Demo\n'
134
            '\n'
135
            ':Options:\n'
2768.1.6 by Ian Clatworthy
Fix existing option and help tests
136
            '  -v, --verbose  Display more information.\n'
137
            '  -q, --quiet    Only display errors and warnings.\n'
138
            '  -h, --help     Show help message.\n'
2666.1.4 by Ian Clatworthy
Add help formatting tests
139
            '\n'
140
            ':Examples:\n'
141
            '    Example 1::\n'
142
            '\n'
143
            '        cmd arg1\n'
144
            '\n'
145
            '    Example 2::\n'
146
            '\n'
147
            '        cmd arg2\n'
148
            '\n')
149
150
    def test_help_text_custom_usage(self):
151
        """Help text may contain a custom usage section."""
152
        class cmd_Demo(commands.Command):
153
            """A sample command.
154
 
155
            :Usage:
156
                cmd Demo [opts] args
157
 
158
                cmd Demo -h
159
 
160
            Blah blah blah.
161
            """
162
        cmd = cmd_Demo()
163
        helptext = cmd.get_help_text()
164
        self.assertEquals(helptext,
165
            'Purpose: A sample command.\n'
166
            'Usage:\n'
167
            '    cmd Demo [opts] args\n'
168
            '\n'
169
            '    cmd Demo -h\n'
170
            '\n'
171
            '\n'
172
            'Options:\n'
2768.1.6 by Ian Clatworthy
Fix existing option and help tests
173
            '  -v, --verbose  Display more information.\n'
174
            '  -q, --quiet    Only display errors and warnings.\n'
175
            '  -h, --help     Show help message.\n'
2666.1.4 by Ian Clatworthy
Add help formatting tests
176
            '\n'
177
            'Description:\n'
178
            '  Blah blah blah.\n\n')
179
2432.1.1 by Robert Collins
Add a HelpTopicContext object.
180
2432.1.8 by Robert Collins
HelpTopicContext now returns RegisteredTopic objects for get_topics calls.
181
class TestRegisteredTopic(tests.TestCase):
182
    """Tests for the RegisteredTopic class."""
183
184
    def test_contruct(self):
185
        """Construction takes the help topic name for the registered item."""
186
        # validate our test 
187
        self.assertTrue('basic' in help_topics.topic_registry)
188
        topic = help_topics.RegisteredTopic('basic')
189
        self.assertEqual('basic', topic.topic)
190
2432.1.10 by Robert Collins
Add get_help_text() to RegisteredTopic to get the help as a string.
191
    def test_get_help_text(self):
192
        """A RegisteredTopic returns the get_detail results for get_help_text."""
193
        topic = help_topics.RegisteredTopic('commands')
194
        self.assertEqual(help_topics.topic_registry.get_detail('commands'),
195
            topic.get_help_text())
196
2432.1.22 by Robert Collins
Teach RegisteredTopic to support the additional_see_also list of related help terms.
197
    def test_get_help_text_with_additional_see_also(self):
198
        topic = help_topics.RegisteredTopic('commands')
199
        self.assertEndsWith(
200
            topic.get_help_text(['foo', 'bar']),
201
            '\n'
202
            'See also: bar, foo\n')
203
3089.3.5 by Ian Clatworthy
add test for loading help from a file
204
    def test_get_help_text_loaded_from_file(self):
205
        # Pick a known topic stored in an external file
206
        topic = help_topics.RegisteredTopic('hooks')
207
        self.assertStartsWith(topic.get_help_text(),
208
            'Hooks\n'
209
            '=====\n'
210
            '\n')
211
2432.1.27 by Robert Collins
Add a get_help_topic method to RegisteredTopic.
212
    def test_get_help_topic(self):
213
        """The help topic for a RegisteredTopic is its topic from construction."""
214
        topic = help_topics.RegisteredTopic('foobar')
215
        self.assertEqual('foobar', topic.get_help_topic())
216
        topic = help_topics.RegisteredTopic('baz')
217
        self.assertEqual('baz', topic.get_help_topic())
218
2432.1.8 by Robert Collins
HelpTopicContext now returns RegisteredTopic objects for get_topics calls.
219
2432.1.15 by Robert Collins
Rename Context (in bzrlib.help) to Index, for a clearer name.
220
class TestTopicIndex(tests.TestCase):
221
    """Tests for the HelpTopicIndex class."""
2432.1.1 by Robert Collins
Add a HelpTopicContext object.
222
2432.1.3 by Robert Collins
Create a HelpContexts object to do help lookups.
223
    def test_default_constructable(self):
2432.1.15 by Robert Collins
Rename Context (in bzrlib.help) to Index, for a clearer name.
224
        index = help_topics.HelpTopicIndex()
2432.1.1 by Robert Collins
Add a HelpTopicContext object.
225
2432.1.8 by Robert Collins
HelpTopicContext now returns RegisteredTopic objects for get_topics calls.
226
    def test_get_topics_None(self):
227
        """Searching for None returns the basic help topic."""
2432.1.15 by Robert Collins
Rename Context (in bzrlib.help) to Index, for a clearer name.
228
        index = help_topics.HelpTopicIndex()
229
        topics = index.get_topics(None)
2432.1.8 by Robert Collins
HelpTopicContext now returns RegisteredTopic objects for get_topics calls.
230
        self.assertEqual(1, len(topics))
231
        self.assertIsInstance(topics[0], help_topics.RegisteredTopic)
232
        self.assertEqual('basic', topics[0].topic)
233
234
    def test_get_topics_topics(self):
235
        """Searching for a string returns the matching string."""
2432.1.15 by Robert Collins
Rename Context (in bzrlib.help) to Index, for a clearer name.
236
        index = help_topics.HelpTopicIndex()
237
        topics = index.get_topics('topics')
2432.1.8 by Robert Collins
HelpTopicContext now returns RegisteredTopic objects for get_topics calls.
238
        self.assertEqual(1, len(topics))
239
        self.assertIsInstance(topics[0], help_topics.RegisteredTopic)
240
        self.assertEqual('topics', topics[0].topic)
241
242
    def test_get_topics_no_topic(self):
243
        """Searching for something not registered returns []."""
2432.1.15 by Robert Collins
Rename Context (in bzrlib.help) to Index, for a clearer name.
244
        index = help_topics.HelpTopicIndex()
245
        self.assertEqual([], index.get_topics('nothing by this name'))
246
2432.1.17 by Robert Collins
Add prefixes to HelpIndexes.
247
    def test_prefix(self):
248
        """TopicIndex has a prefix of ''."""
249
        index = help_topics.HelpTopicIndex()
250
        self.assertEqual('', index.prefix)
251
2432.1.15 by Robert Collins
Rename Context (in bzrlib.help) to Index, for a clearer name.
252
253
class TestCommandIndex(tests.TestCase):
254
    """Tests for the HelpCommandIndex class."""
2432.1.2 by Robert Collins
Add a HelpCommandContext class for help from commands.
255
2432.1.3 by Robert Collins
Create a HelpContexts object to do help lookups.
256
    def test_default_constructable(self):
2432.1.15 by Robert Collins
Rename Context (in bzrlib.help) to Index, for a clearer name.
257
        index = commands.HelpCommandIndex()
2432.1.3 by Robert Collins
Create a HelpContexts object to do help lookups.
258
2432.1.13 by Robert Collins
HelpCommandContext now implementes get_topics.
259
    def test_get_topics_None(self):
260
        """Searching for None returns an empty list."""
2432.1.15 by Robert Collins
Rename Context (in bzrlib.help) to Index, for a clearer name.
261
        index = commands.HelpCommandIndex()
262
        self.assertEqual([], index.get_topics(None))
2432.1.13 by Robert Collins
HelpCommandContext now implementes get_topics.
263
264
    def test_get_topics_rocks(self):
265
        """Searching for 'rocks' returns the cmd_rocks command instance."""
2432.1.15 by Robert Collins
Rename Context (in bzrlib.help) to Index, for a clearer name.
266
        index = commands.HelpCommandIndex()
267
        topics = index.get_topics('rocks')
2432.1.13 by Robert Collins
HelpCommandContext now implementes get_topics.
268
        self.assertEqual(1, len(topics))
269
        self.assertIsInstance(topics[0], builtins.cmd_rocks)
270
271
    def test_get_topics_no_topic(self):
272
        """Searching for something that is not a command returns []."""
2432.1.15 by Robert Collins
Rename Context (in bzrlib.help) to Index, for a clearer name.
273
        index = commands.HelpCommandIndex()
274
        self.assertEqual([], index.get_topics('nothing by this name'))
275
2432.1.17 by Robert Collins
Add prefixes to HelpIndexes.
276
    def test_prefix(self):
277
        """CommandIndex has a prefix of 'commands/'."""
278
        index = commands.HelpCommandIndex()
279
        self.assertEqual('commands/', index.prefix)
280
2432.1.18 by Robert Collins
Add support for doing bzr help commands/COMMANDNAME.
281
    def test_get_topic_with_prefix(self):
282
        """Searching for commands/rocks returns the rocks command object."""
283
        index = commands.HelpCommandIndex()
284
        topics = index.get_topics('commands/rocks')
285
        self.assertEqual(1, len(topics))
286
        self.assertIsInstance(topics[0], builtins.cmd_rocks)
287
2432.1.15 by Robert Collins
Rename Context (in bzrlib.help) to Index, for a clearer name.
288
2432.1.16 by Robert Collins
Correct spelling of Indexs to Indices.
289
class TestHelpIndices(tests.TestCase):
290
    """Tests for the HelpIndices class."""
2432.1.3 by Robert Collins
Create a HelpContexts object to do help lookups.
291
292
    def test_default_search_path(self):
2432.1.15 by Robert Collins
Rename Context (in bzrlib.help) to Index, for a clearer name.
293
        """The default search path should include internal indexs."""
2432.1.16 by Robert Collins
Correct spelling of Indexs to Indices.
294
        indices = help.HelpIndices()
2432.1.24 by Robert Collins
Add plugins as a help index.
295
        self.assertEqual(3, len(indices.search_path))
2432.1.3 by Robert Collins
Create a HelpContexts object to do help lookups.
296
        # help topics should be searched in first.
2432.1.16 by Robert Collins
Correct spelling of Indexs to Indices.
297
        self.assertIsInstance(indices.search_path[0],
2432.1.15 by Robert Collins
Rename Context (in bzrlib.help) to Index, for a clearer name.
298
            help_topics.HelpTopicIndex)
2432.1.3 by Robert Collins
Create a HelpContexts object to do help lookups.
299
        # with commands being search second.
2432.1.16 by Robert Collins
Correct spelling of Indexs to Indices.
300
        self.assertIsInstance(indices.search_path[1],
2432.1.15 by Robert Collins
Rename Context (in bzrlib.help) to Index, for a clearer name.
301
            commands.HelpCommandIndex)
2432.1.24 by Robert Collins
Add plugins as a help index.
302
        # and plugins are a third index.
303
        self.assertIsInstance(indices.search_path[2],
304
            plugin.PluginsHelpIndex)
2432.1.5 by Robert Collins
Initial stub for topic searching.
305
306
    def test_search_for_unknown_topic_raises(self):
307
        """Searching for an unknown topic should raise NoHelpTopic."""
2432.1.16 by Robert Collins
Correct spelling of Indexs to Indices.
308
        indices = help.HelpIndices()
309
        indices.search_path = []
310
        error = self.assertRaises(errors.NoHelpTopic, indices.search, 'foo')
2432.1.5 by Robert Collins
Initial stub for topic searching.
311
        self.assertEqual('foo', error.topic)
2432.1.6 by Robert Collins
HelpContexts.search now invokes get_topics on each context.
312
313
    def test_search_calls_get_topic(self):
314
        """Searching should call get_topics in all indexes in order."""
315
        calls = []
2432.1.15 by Robert Collins
Rename Context (in bzrlib.help) to Index, for a clearer name.
316
        class RecordingIndex(object):
2432.1.6 by Robert Collins
HelpContexts.search now invokes get_topics on each context.
317
            def __init__(self, name):
2432.1.19 by Robert Collins
Ensure each HelpIndex has a unique prefix.
318
                self.prefix = name
2432.1.6 by Robert Collins
HelpContexts.search now invokes get_topics on each context.
319
            def get_topics(self, topic):
2432.1.19 by Robert Collins
Ensure each HelpIndex has a unique prefix.
320
                calls.append(('get_topics', self.prefix, topic))
2432.1.6 by Robert Collins
HelpContexts.search now invokes get_topics on each context.
321
                return ['something']
2432.1.16 by Robert Collins
Correct spelling of Indexs to Indices.
322
        index = help.HelpIndices()
2432.1.15 by Robert Collins
Rename Context (in bzrlib.help) to Index, for a clearer name.
323
        index.search_path = [RecordingIndex('1'), RecordingIndex('2')]
2432.1.6 by Robert Collins
HelpContexts.search now invokes get_topics on each context.
324
        # try with None
2432.1.15 by Robert Collins
Rename Context (in bzrlib.help) to Index, for a clearer name.
325
        index.search(None)
2432.1.6 by Robert Collins
HelpContexts.search now invokes get_topics on each context.
326
        self.assertEqual([
327
            ('get_topics', '1', None),
328
            ('get_topics', '2', None),
329
            ],
330
            calls)
331
        # and with a string
332
        del calls[:]
2432.1.15 by Robert Collins
Rename Context (in bzrlib.help) to Index, for a clearer name.
333
        index.search('bar')
2432.1.6 by Robert Collins
HelpContexts.search now invokes get_topics on each context.
334
        self.assertEqual([
335
            ('get_topics', '1', 'bar'),
336
            ('get_topics', '2', 'bar'),
337
            ],
338
            calls)
2432.1.7 by Robert Collins
HelpContexts.search now returns the found topics.
339
2432.1.20 by Robert Collins
Modify the result of HelpIndices.search to include the index each result was found in.
340
    def test_search_returns_index_and_results(self):
341
        """Searching should return help topics with their index"""
2432.1.15 by Robert Collins
Rename Context (in bzrlib.help) to Index, for a clearer name.
342
        class CannedIndex(object):
2432.1.19 by Robert Collins
Ensure each HelpIndex has a unique prefix.
343
            def __init__(self, prefix, search_result):
344
                self.prefix = prefix
2432.1.7 by Robert Collins
HelpContexts.search now returns the found topics.
345
                self.result = search_result
346
            def get_topics(self, topic):
347
                return self.result
2432.1.16 by Robert Collins
Correct spelling of Indexs to Indices.
348
        index = help.HelpIndices()
2432.1.20 by Robert Collins
Modify the result of HelpIndices.search to include the index each result was found in.
349
        index_one = CannedIndex('1', ['a'])
350
        index_two = CannedIndex('2', ['b', 'c'])
351
        index.search_path = [index_one, index_two]
352
        self.assertEqual([(index_one, 'a'), (index_two, 'b'), (index_two, 'c')],
353
            index.search(None))
2432.1.19 by Robert Collins
Ensure each HelpIndex has a unique prefix.
354
355
    def test_search_checks_for_duplicate_prefixes(self):
356
        """Its an error when there are multiple indices with the same prefix."""
357
        indices = help.HelpIndices()
358
        indices.search_path = [help_topics.HelpTopicIndex(),
359
            help_topics.HelpTopicIndex()]
360
        self.assertRaises(errors.DuplicateHelpPrefix, indices.search, None)