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