~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_plugins.py

  • Committer: Martin Pool
  • Date: 2008-02-13 03:45:58 UTC
  • mto: (3221.1.9 prepare-1.2rc1)
  • mto: This revision was merged to the branch mainline in revision 3236.
  • Revision ID: mbp@sourcefrog.net-20080213034558-anhm9j1l3mbbmnyo
Add in thumpers tests for selection of the right Launchpad instance

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 by Canonical Ltd
2
 
 
 
1
# Copyright (C) 2005, 2007 Canonical Ltd
 
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
 
 
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
 
 
18
17
"""Tests for plugins"""
19
18
 
20
19
# XXX: There are no plugin tests at the moment because the plugin module
21
20
# affects the global state of the process.  See bzrlib/plugins.py for more
22
21
# comments.
23
22
 
 
23
import logging
24
24
import os
 
25
from StringIO import StringIO
 
26
import sys
 
27
import zipfile
25
28
 
 
29
from bzrlib import plugin, tests
26
30
import bzrlib.plugin
27
31
import bzrlib.plugins
28
 
from bzrlib.tests import TestCaseInTempDir
29
 
 
30
 
class PluginTest(TestCaseInTempDir):
31
 
    """Create an external plugin and test loading."""
32
 
#    def test_plugin_loading(self):
33
 
#        orig_help = self.run_bzr_captured('bzr help commands')[0]
34
 
#        os.mkdir('plugin_test')
35
 
#        f = open(os.path.join('plugin_test', 'myplug.py'), 'wt')
36
 
#        f.write(PLUGIN_TEXT)
37
 
#        f.close()
38
 
#        newhelp = self.run_bzr_captured('bzr help commands')[0]
39
 
#        assert newhelp.startswith('You have been overridden\n')
40
 
#        # We added a line, but the rest should work
41
 
#        assert newhelp[25:] == help
42
 
#
43
 
#        assert backtick('bzr commit -m test') == "I'm sorry dave, you can't do that\n"
44
 
#
45
 
#        shutil.rmtree('plugin_test')
46
 
#
47
 
 
48
 
#         os.environ['BZRPLUGINPATH'] = os.path.abspath('plugin_test')
49
 
#         help = backtick('bzr help commands')
50
 
#         assert help.find('myplug') != -1
51
 
#         assert help.find('Just a simple test plugin.') != -1
52
 
 
53
 
 
54
 
#         assert backtick('bzr myplug') == 'Hello from my plugin\n'
55
 
#         assert backtick('bzr mplg') == 'Hello from my plugin\n'
56
 
 
57
 
#         f = open(os.path.join('plugin_test', 'override.py'), 'wb')
58
 
#         f.write("""import bzrlib, bzrlib.commands
59
 
#     class cmd_commit(bzrlib.commands.cmd_commit):
60
 
#         '''Commit changes into a new revision.'''
61
 
#         def run(self, *args, **kwargs):
62
 
#             print "I'm sorry dave, you can't do that"
63
 
 
64
 
#     class cmd_help(bzrlib.commands.cmd_help):
65
 
#         '''Show help on a command or other topic.'''
66
 
#         def run(self, *args, **kwargs):
67
 
#             print "You have been overridden"
68
 
#             bzrlib.commands.cmd_help.run(self, *args, **kwargs)
69
 
 
70
 
#         """
 
32
import bzrlib.commands
 
33
import bzrlib.help
 
34
from bzrlib.symbol_versioning import zero_ninetyone
 
35
from bzrlib.tests import TestCase, TestCaseInTempDir
 
36
from bzrlib.osutils import pathjoin, abspath, normpath
 
37
 
71
38
 
72
39
PLUGIN_TEXT = """\
73
40
import bzrlib.commands
80
47
 
81
48
# TODO: Write a test for plugin decoration of commands.
82
49
 
83
 
class TestOneNamedPluginOnly(TestCaseInTempDir):
 
50
class TestLoadingPlugins(TestCaseInTempDir):
84
51
 
85
52
    activeattributes = {}
86
53
 
87
54
    def test_plugins_with_the_same_name_are_not_loaded(self):
 
55
        # This test tests that having two plugins in different directories does
 
56
        # not result in both being loaded when they have the same name.  get a
 
57
        # file name we can use which is also a valid attribute for accessing in
 
58
        # activeattributes. - we cannot give import parameters.
 
59
        tempattribute = "0"
 
60
        self.failIf(tempattribute in self.activeattributes)
 
61
        # set a place for the plugins to record their loading, and at the same
 
62
        # time validate that the location the plugins should record to is
 
63
        # valid and correct.
 
64
        bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
 
65
            [tempattribute] = []
 
66
        self.failUnless(tempattribute in self.activeattributes)
 
67
        # create two plugin directories
 
68
        os.mkdir('first')
 
69
        os.mkdir('second')
 
70
        # write a plugin that will record when its loaded in the 
 
71
        # tempattribute list.
 
72
        template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
 
73
                    "TestLoadingPlugins.activeattributes[%r].append('%s')\n")
 
74
 
 
75
        outfile = open(os.path.join('first', 'plugin.py'), 'w')
 
76
        try:
 
77
            outfile.write(template % (tempattribute, 'first'))
 
78
            outfile.write('\n')
 
79
        finally:
 
80
            outfile.close()
 
81
 
 
82
        outfile = open(os.path.join('second', 'plugin.py'), 'w')
 
83
        try:
 
84
            outfile.write(template % (tempattribute, 'second'))
 
85
            outfile.write('\n')
 
86
        finally:
 
87
            outfile.close()
 
88
 
 
89
        try:
 
90
            bzrlib.plugin.load_from_path(['first', 'second'])
 
91
            self.assertEqual(['first'], self.activeattributes[tempattribute])
 
92
        finally:
 
93
            # remove the plugin 'plugin'
 
94
            del self.activeattributes[tempattribute]
 
95
            if 'bzrlib.plugins.plugin' in sys.modules:
 
96
                del sys.modules['bzrlib.plugins.plugin']
 
97
            if getattr(bzrlib.plugins, 'plugin', None):
 
98
                del bzrlib.plugins.plugin
 
99
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
 
100
 
 
101
    def test_plugins_from_different_dirs_can_demand_load(self):
88
102
        # This test tests that having two plugins in different
89
 
        # directories does not result in both being loaded.
90
 
        # get a file name we can use which is also a valid attribute
 
103
        # directories with different names allows them both to be loaded, when
 
104
        # we do a direct import statement.
 
105
        # Determine a file name we can use which is also a valid attribute
91
106
        # for accessing in activeattributes. - we cannot give import parameters.
92
 
        tempattribute = "0"
 
107
        tempattribute = "different-dirs"
93
108
        self.failIf(tempattribute in self.activeattributes)
94
109
        # set a place for the plugins to record their loading, and at the same
95
110
        # time validate that the location the plugins should record to is
96
111
        # valid and correct.
97
 
        bzrlib.tests.test_plugins.TestOneNamedPluginOnly.activeattributes \
 
112
        bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
98
113
            [tempattribute] = []
99
114
        self.failUnless(tempattribute in self.activeattributes)
100
115
        # create two plugin directories
101
116
        os.mkdir('first')
102
117
        os.mkdir('second')
 
118
        # write plugins that will record when they are loaded in the 
 
119
        # tempattribute list.
 
120
        template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
 
121
                    "TestLoadingPlugins.activeattributes[%r].append('%s')\n")
 
122
 
 
123
        outfile = open(os.path.join('first', 'pluginone.py'), 'w')
 
124
        try:
 
125
            outfile.write(template % (tempattribute, 'first'))
 
126
            outfile.write('\n')
 
127
        finally:
 
128
            outfile.close()
 
129
 
 
130
        outfile = open(os.path.join('second', 'plugintwo.py'), 'w')
 
131
        try:
 
132
            outfile.write(template % (tempattribute, 'second'))
 
133
            outfile.write('\n')
 
134
        finally:
 
135
            outfile.close()
 
136
 
 
137
        oldpath = bzrlib.plugins.__path__
 
138
        try:
 
139
            bzrlib.plugins.__path__ = ['first', 'second']
 
140
            exec "import bzrlib.plugins.pluginone"
 
141
            self.assertEqual(['first'], self.activeattributes[tempattribute])
 
142
            exec "import bzrlib.plugins.plugintwo"
 
143
            self.assertEqual(['first', 'second'],
 
144
                self.activeattributes[tempattribute])
 
145
        finally:
 
146
            # remove the plugin 'plugin'
 
147
            del self.activeattributes[tempattribute]
 
148
            if getattr(bzrlib.plugins, 'pluginone', None):
 
149
                del bzrlib.plugins.pluginone
 
150
            if getattr(bzrlib.plugins, 'plugintwo', None):
 
151
                del bzrlib.plugins.plugintwo
 
152
        self.failIf(getattr(bzrlib.plugins, 'pluginone', None))
 
153
        self.failIf(getattr(bzrlib.plugins, 'plugintwo', None))
 
154
 
 
155
    def test_plugins_can_load_from_directory_with_trailing_slash(self):
 
156
        # This test tests that a plugin can load from a directory when the
 
157
        # directory in the path has a trailing slash.
 
158
        # check the plugin is not loaded already
 
159
        self.failIf(getattr(bzrlib.plugins, 'ts_plugin', None))
 
160
        tempattribute = "trailing-slash"
 
161
        self.failIf(tempattribute in self.activeattributes)
 
162
        # set a place for the plugin to record its loading, and at the same
 
163
        # time validate that the location the plugin should record to is
 
164
        # valid and correct.
 
165
        bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
 
166
            [tempattribute] = []
 
167
        self.failUnless(tempattribute in self.activeattributes)
 
168
        # create a directory for the plugin
 
169
        os.mkdir('plugin_test')
103
170
        # write a plugin that will record when its loaded in the 
104
171
        # tempattribute list.
105
 
        template = ("from bzrlib.tests.test_plugins import TestOneNamedPluginOnly\n"
106
 
                    "TestOneNamedPluginOnly.activeattributes[%r].append('%s')\n")
107
 
        print >> file(os.path.join('first', 'plugin.py'), 'w'), template % (tempattribute, 'first')
108
 
        print >> file(os.path.join('second', 'plugin.py'), 'w'), template % (tempattribute, 'second')
109
 
        try:
110
 
            bzrlib.plugin.load_from_dirs(['first', 'second'])
111
 
            self.assertEqual(['first'], self.activeattributes[tempattribute])
 
172
        template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
 
173
                    "TestLoadingPlugins.activeattributes[%r].append('%s')\n")
 
174
 
 
175
        outfile = open(os.path.join('plugin_test', 'ts_plugin.py'), 'w')
 
176
        try:
 
177
            outfile.write(template % (tempattribute, 'plugin'))
 
178
            outfile.write('\n')
 
179
        finally:
 
180
            outfile.close()
 
181
 
 
182
        try:
 
183
            bzrlib.plugin.load_from_path(['plugin_test'+os.sep])
 
184
            self.assertEqual(['plugin'], self.activeattributes[tempattribute])
112
185
        finally:
113
186
            # remove the plugin 'plugin'
114
187
            del self.activeattributes[tempattribute]
115
 
            if getattr(bzrlib.plugins, 'plugin', None):
116
 
                del bzrlib.plugins.plugin
117
 
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
 
188
            if getattr(bzrlib.plugins, 'ts_plugin', None):
 
189
                del bzrlib.plugins.ts_plugin
 
190
        self.failIf(getattr(bzrlib.plugins, 'ts_plugin', None))
 
191
 
 
192
    def test_plugin_with_bad_name_does_not_load(self):
 
193
        # Create badly-named plugin
 
194
        file('bad plugin-name..py', 'w').close()
 
195
 
 
196
        # Capture output
 
197
        stream = StringIO()
 
198
        handler = logging.StreamHandler(stream)
 
199
        log = logging.getLogger('bzr')
 
200
        log.addHandler(handler)
 
201
 
 
202
        bzrlib.plugin.load_from_dir('.')
 
203
 
 
204
        # Stop capturing output
 
205
        handler.flush()
 
206
        handler.close()
 
207
        log.removeHandler(handler)
 
208
 
 
209
        self.assertContainsRe(stream.getvalue(),
 
210
            r"Unable to load 'bad plugin-name\.' in '\.' as a plugin because"
 
211
            " file path isn't a valid module name; try renaming it to"
 
212
            " 'bad_plugin_name_'\.")
 
213
 
 
214
        stream.close()
118
215
 
119
216
 
120
217
class TestAllPlugins(TestCaseInTempDir):
124
221
        # check the plugin is not loaded already
125
222
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
126
223
        # write a plugin that _cannot_ fail to load.
127
 
        print >> file('plugin.py', 'w'), ""
 
224
        file('plugin.py', 'w').write("\n")
128
225
        try:
129
 
            bzrlib.plugin.load_from_dirs(['.'])
130
 
            self.failUnless('plugin' in bzrlib.plugin.all_plugins())
 
226
            bzrlib.plugin.load_from_path(['.'])
 
227
            all_plugins = self.applyDeprecated(zero_ninetyone,
 
228
                bzrlib.plugin.all_plugins)
 
229
            self.failUnless('plugin' in all_plugins)
131
230
            self.failUnless(getattr(bzrlib.plugins, 'plugin', None))
132
 
            self.assertEqual(bzrlib.plugin.all_plugins()['plugin'],
133
 
                             bzrlib.plugins.plugin)
 
231
            self.assertEqual(all_plugins['plugin'], bzrlib.plugins.plugin)
134
232
        finally:
135
233
            # remove the plugin 'plugin'
 
234
            if 'bzrlib.plugins.plugin' in sys.modules:
 
235
                del sys.modules['bzrlib.plugins.plugin']
136
236
            if getattr(bzrlib.plugins, 'plugin', None):
137
237
                del bzrlib.plugins.plugin
138
238
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
 
239
 
 
240
 
 
241
class TestPlugins(TestCaseInTempDir):
 
242
 
 
243
    def setup_plugin(self, source=""):
 
244
        # This test tests a new plugin appears in bzrlib.plugin.plugins().
 
245
        # check the plugin is not loaded already
 
246
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
 
247
        # write a plugin that _cannot_ fail to load.
 
248
        file('plugin.py', 'w').write(source + '\n')
 
249
        self.addCleanup(self.teardown_plugin)
 
250
        bzrlib.plugin.load_from_path(['.'])
 
251
    
 
252
    def teardown_plugin(self):
 
253
        # remove the plugin 'plugin'
 
254
        if 'bzrlib.plugins.plugin' in sys.modules:
 
255
            del sys.modules['bzrlib.plugins.plugin']
 
256
        if getattr(bzrlib.plugins, 'plugin', None):
 
257
            del bzrlib.plugins.plugin
 
258
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
 
259
 
 
260
    def test_plugin_appears_in_plugins(self):
 
261
        self.setup_plugin()
 
262
        self.failUnless('plugin' in bzrlib.plugin.plugins())
 
263
        self.failUnless(getattr(bzrlib.plugins, 'plugin', None))
 
264
        plugins = bzrlib.plugin.plugins()
 
265
        plugin = plugins['plugin']
 
266
        self.assertIsInstance(plugin, bzrlib.plugin.PlugIn)
 
267
        self.assertEqual(bzrlib.plugins.plugin, plugin.module)
 
268
 
 
269
    def test_trivial_plugin_get_path(self):
 
270
        self.setup_plugin()
 
271
        plugins = bzrlib.plugin.plugins()
 
272
        plugin = plugins['plugin']
 
273
        plugin_path = self.test_dir + '/plugin.py'
 
274
        self.assertIsSameRealPath(plugin_path, normpath(plugin.path()))
 
275
 
 
276
    def test_plugin_get_path_py_not_pyc(self):
 
277
        self.setup_plugin()         # after first import there will be plugin.pyc
 
278
        self.teardown_plugin()
 
279
        bzrlib.plugin.load_from_path(['.']) # import plugin.pyc
 
280
        plugins = bzrlib.plugin.plugins()
 
281
        plugin = plugins['plugin']
 
282
        plugin_path = self.test_dir + '/plugin.py'
 
283
        self.assertIsSameRealPath(plugin_path, normpath(plugin.path()))
 
284
 
 
285
    def test_plugin_get_path_pyc_only(self):
 
286
        self.setup_plugin()         # after first import there will be plugin.pyc
 
287
        self.teardown_plugin()
 
288
        os.unlink(self.test_dir + '/plugin.py')
 
289
        bzrlib.plugin.load_from_path(['.']) # import plugin.pyc
 
290
        plugins = bzrlib.plugin.plugins()
 
291
        plugin = plugins['plugin']
 
292
        if __debug__:
 
293
            plugin_path = self.test_dir + '/plugin.pyc'
 
294
        else:
 
295
            plugin_path = self.test_dir + '/plugin.pyo'
 
296
        self.assertIsSameRealPath(plugin_path, normpath(plugin.path()))
 
297
 
 
298
    def test_no_test_suite_gives_None_for_test_suite(self):
 
299
        self.setup_plugin()
 
300
        plugin = bzrlib.plugin.plugins()['plugin']
 
301
        self.assertEqual(None, plugin.test_suite())
 
302
 
 
303
    def test_test_suite_gives_test_suite_result(self):
 
304
        source = """def test_suite(): return 'foo'"""
 
305
        self.setup_plugin(source)
 
306
        plugin = bzrlib.plugin.plugins()['plugin']
 
307
        self.assertEqual('foo', plugin.test_suite())
 
308
 
 
309
    def test_no_version_info(self):
 
310
        self.setup_plugin()
 
311
        plugin = bzrlib.plugin.plugins()['plugin']
 
312
        self.assertEqual(None, plugin.version_info())
 
313
 
 
314
    def test_with_version_info(self):
 
315
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 4)")
 
316
        plugin = bzrlib.plugin.plugins()['plugin']
 
317
        self.assertEqual((1, 2, 3, 'dev', 4), plugin.version_info())
 
318
 
 
319
    def test_short_version_info_gets_padded(self):
 
320
        # the gtk plugin has version_info = (1,2,3) rather than the 5-tuple.
 
321
        # so we adapt it
 
322
        self.setup_plugin("version_info = (1, 2, 3)")
 
323
        plugin = bzrlib.plugin.plugins()['plugin']
 
324
        self.assertEqual((1, 2, 3, 'final', 0), plugin.version_info())
 
325
 
 
326
    def test_no_version_info___version__(self):
 
327
        self.setup_plugin()
 
328
        plugin = bzrlib.plugin.plugins()['plugin']
 
329
        self.assertEqual("unknown", plugin.__version__)
 
330
 
 
331
    def test___version__with_version_info(self):
 
332
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 4)")
 
333
        plugin = bzrlib.plugin.plugins()['plugin']
 
334
        self.assertEqual("1.2.3dev4", plugin.__version__)
 
335
 
 
336
    def test_final__version__with_version_info(self):
 
337
        self.setup_plugin("version_info = (1, 2, 3, 'final', 4)")
 
338
        plugin = bzrlib.plugin.plugins()['plugin']
 
339
        self.assertEqual("1.2.3", plugin.__version__)
 
340
 
 
341
 
 
342
class TestPluginHelp(TestCaseInTempDir):
 
343
 
 
344
    def split_help_commands(self):
 
345
        help = {}
 
346
        current = None
 
347
        for line in self.run_bzr('help commands')[0].splitlines():
 
348
            if not line.startswith(' '):
 
349
                current = line.split()[0]
 
350
            help[current] = help.get(current, '') + line
 
351
 
 
352
        return help
 
353
 
 
354
    def test_plugin_help_builtins_unaffected(self):
 
355
        # Check we don't get false positives
 
356
        help_commands = self.split_help_commands()
 
357
        for cmd_name in bzrlib.commands.builtin_command_names():
 
358
            if cmd_name in bzrlib.commands.plugin_command_names():
 
359
                continue
 
360
            try:
 
361
                help = bzrlib.commands.get_cmd_object(cmd_name).get_help_text()
 
362
            except NotImplementedError:
 
363
                # some commands have no help
 
364
                pass
 
365
            else:
 
366
                self.assertNotContainsRe(help, 'plugin "[^"]*"')
 
367
 
 
368
            if cmd_name in help_commands.keys():
 
369
                # some commands are hidden
 
370
                help = help_commands[cmd_name]
 
371
                self.assertNotContainsRe(help, 'plugin "[^"]*"')
 
372
 
 
373
    def test_plugin_help_shows_plugin(self):
 
374
        # Create a test plugin
 
375
        os.mkdir('plugin_test')
 
376
        f = open(pathjoin('plugin_test', 'myplug.py'), 'w')
 
377
        f.write(PLUGIN_TEXT)
 
378
        f.close()
 
379
 
 
380
        try:
 
381
            # Check its help
 
382
            bzrlib.plugin.load_from_path(['plugin_test'])
 
383
            bzrlib.commands.register_command( bzrlib.plugins.myplug.cmd_myplug)
 
384
            help = self.run_bzr('help myplug')[0]
 
385
            self.assertContainsRe(help, 'plugin "myplug"')
 
386
            help = self.split_help_commands()['myplug']
 
387
            self.assertContainsRe(help, '\[myplug\]')
 
388
        finally:
 
389
            # unregister command
 
390
            if bzrlib.commands.plugin_cmds.get('myplug', None):
 
391
                del bzrlib.commands.plugin_cmds['myplug']
 
392
            # remove the plugin 'myplug'
 
393
            if getattr(bzrlib.plugins, 'myplug', None):
 
394
                delattr(bzrlib.plugins, 'myplug')
 
395
 
 
396
 
 
397
class TestPluginFromZip(TestCaseInTempDir):
 
398
 
 
399
    def make_zipped_plugin(self, zip_name, filename):
 
400
        z = zipfile.ZipFile(zip_name, 'w')
 
401
        z.writestr(filename, PLUGIN_TEXT)
 
402
        z.close()
 
403
 
 
404
    def check_plugin_load(self, zip_name, plugin_name):
 
405
        self.assertFalse(plugin_name in dir(bzrlib.plugins),
 
406
                         'Plugin already loaded')
 
407
        old_path = bzrlib.plugins.__path__
 
408
        try:
 
409
            # this is normally done by load_plugins -> set_plugins_path
 
410
            bzrlib.plugins.__path__ = [zip_name]
 
411
            bzrlib.plugin.load_from_zip(zip_name)
 
412
            self.assertTrue(plugin_name in dir(bzrlib.plugins),
 
413
                            'Plugin is not loaded')
 
414
        finally:
 
415
            # unregister plugin
 
416
            if getattr(bzrlib.plugins, plugin_name, None):
 
417
                delattr(bzrlib.plugins, plugin_name)
 
418
                del sys.modules['bzrlib.plugins.' + plugin_name]
 
419
            bzrlib.plugins.__path__ = old_path
 
420
 
 
421
    def test_load_module(self):
 
422
        self.make_zipped_plugin('./test.zip', 'ziplug.py')
 
423
        self.check_plugin_load('./test.zip', 'ziplug')
 
424
 
 
425
    def test_load_package(self):
 
426
        self.make_zipped_plugin('./test.zip', 'ziplug/__init__.py')
 
427
        self.check_plugin_load('./test.zip', 'ziplug')
 
428
 
 
429
 
 
430
class TestSetPluginsPath(TestCase):
 
431
    
 
432
    def test_set_plugins_path(self):
 
433
        """set_plugins_path should set the module __path__ correctly."""
 
434
        old_path = bzrlib.plugins.__path__
 
435
        try:
 
436
            bzrlib.plugins.__path__ = []
 
437
            expected_path = bzrlib.plugin.set_plugins_path()
 
438
            self.assertEqual(expected_path, bzrlib.plugins.__path__)
 
439
        finally:
 
440
            bzrlib.plugins.__path__ = old_path
 
441
 
 
442
    def test_set_plugins_path_with_trailing_slashes(self):
 
443
        """set_plugins_path should set the module __path__ based on
 
444
        BZR_PLUGIN_PATH."""
 
445
        old_path = bzrlib.plugins.__path__
 
446
        old_env = os.environ.get('BZR_PLUGIN_PATH')
 
447
        try:
 
448
            bzrlib.plugins.__path__ = []
 
449
            os.environ['BZR_PLUGIN_PATH'] = "first\\//\\" + os.pathsep + \
 
450
                "second/\\/\\/"
 
451
            bzrlib.plugin.set_plugins_path()
 
452
            expected_path = ['first', 'second',
 
453
                os.path.dirname(bzrlib.plugins.__file__)]
 
454
            self.assertEqual(expected_path, bzrlib.plugins.__path__)
 
455
        finally:
 
456
            bzrlib.plugins.__path__ = old_path
 
457
            if old_env != None:
 
458
                os.environ['BZR_PLUGIN_PATH'] = old_env
 
459
            else:
 
460
                del os.environ['BZR_PLUGIN_PATH']
 
461
 
 
462
class TestHelpIndex(tests.TestCase):
 
463
    """Tests for the PluginsHelpIndex class."""
 
464
 
 
465
    def test_default_constructable(self):
 
466
        index = plugin.PluginsHelpIndex()
 
467
 
 
468
    def test_get_topics_None(self):
 
469
        """Searching for None returns an empty list."""
 
470
        index = plugin.PluginsHelpIndex()
 
471
        self.assertEqual([], index.get_topics(None))
 
472
 
 
473
    def test_get_topics_for_plugin(self):
 
474
        """Searching for plugin name gets its docstring."""
 
475
        index = plugin.PluginsHelpIndex()
 
476
        # make a new plugin here for this test, even if we're run with
 
477
        # --no-plugins
 
478
        self.assertFalse(sys.modules.has_key('bzrlib.plugins.demo_module'))
 
479
        demo_module = FakeModule('', 'bzrlib.plugins.demo_module')
 
480
        sys.modules['bzrlib.plugins.demo_module'] = demo_module
 
481
        try:
 
482
            topics = index.get_topics('demo_module')
 
483
            self.assertEqual(1, len(topics))
 
484
            self.assertIsInstance(topics[0], plugin.ModuleHelpTopic)
 
485
            self.assertEqual(demo_module, topics[0].module)
 
486
        finally:
 
487
            del sys.modules['bzrlib.plugins.demo_module']
 
488
 
 
489
    def test_get_topics_no_topic(self):
 
490
        """Searching for something that is not a plugin returns []."""
 
491
        # test this by using a name that cannot be a plugin - its not
 
492
        # a valid python identifier.
 
493
        index = plugin.PluginsHelpIndex()
 
494
        self.assertEqual([], index.get_topics('nothing by this name'))
 
495
 
 
496
    def test_prefix(self):
 
497
        """PluginsHelpIndex has a prefix of 'plugins/'."""
 
498
        index = plugin.PluginsHelpIndex()
 
499
        self.assertEqual('plugins/', index.prefix)
 
500
 
 
501
    def test_get_plugin_topic_with_prefix(self):
 
502
        """Searching for plugins/demo_module returns help."""
 
503
        index = plugin.PluginsHelpIndex()
 
504
        self.assertFalse(sys.modules.has_key('bzrlib.plugins.demo_module'))
 
505
        demo_module = FakeModule('', 'bzrlib.plugins.demo_module')
 
506
        sys.modules['bzrlib.plugins.demo_module'] = demo_module
 
507
        try:
 
508
            topics = index.get_topics('plugins/demo_module')
 
509
            self.assertEqual(1, len(topics))
 
510
            self.assertIsInstance(topics[0], plugin.ModuleHelpTopic)
 
511
            self.assertEqual(demo_module, topics[0].module)
 
512
        finally:
 
513
            del sys.modules['bzrlib.plugins.demo_module']
 
514
 
 
515
 
 
516
class FakeModule(object):
 
517
    """A fake module to test with."""
 
518
 
 
519
    def __init__(self, doc, name):
 
520
        self.__doc__ = doc
 
521
        self.__name__ = name
 
522
 
 
523
 
 
524
class TestModuleHelpTopic(tests.TestCase):
 
525
    """Tests for the ModuleHelpTopic class."""
 
526
 
 
527
    def test_contruct(self):
 
528
        """Construction takes the module to document."""
 
529
        mod = FakeModule('foo', 'foo')
 
530
        topic = plugin.ModuleHelpTopic(mod)
 
531
        self.assertEqual(mod, topic.module)
 
532
 
 
533
    def test_get_help_text_None(self):
 
534
        """A ModuleHelpTopic returns the docstring for get_help_text."""
 
535
        mod = FakeModule(None, 'demo')
 
536
        topic = plugin.ModuleHelpTopic(mod)
 
537
        self.assertEqual("Plugin 'demo' has no docstring.\n",
 
538
            topic.get_help_text())
 
539
 
 
540
    def test_get_help_text_no_carriage_return(self):
 
541
        """ModuleHelpTopic.get_help_text adds a \n if needed."""
 
542
        mod = FakeModule('one line of help', 'demo')
 
543
        topic = plugin.ModuleHelpTopic(mod)
 
544
        self.assertEqual("one line of help\n",
 
545
            topic.get_help_text())
 
546
 
 
547
    def test_get_help_text_carriage_return(self):
 
548
        """ModuleHelpTopic.get_help_text adds a \n if needed."""
 
549
        mod = FakeModule('two lines of help\nand more\n', 'demo')
 
550
        topic = plugin.ModuleHelpTopic(mod)
 
551
        self.assertEqual("two lines of help\nand more\n",
 
552
            topic.get_help_text())
 
553
 
 
554
    def test_get_help_text_with_additional_see_also(self):
 
555
        mod = FakeModule('two lines of help\nand more', 'demo')
 
556
        topic = plugin.ModuleHelpTopic(mod)
 
557
        self.assertEqual("two lines of help\nand more\nSee also: bar, foo\n",
 
558
            topic.get_help_text(['foo', 'bar']))
 
559
 
 
560
    def test_get_help_topic(self):
 
561
        """The help topic for a plugin is its module name."""
 
562
        mod = FakeModule('two lines of help\nand more', 'bzrlib.plugins.demo')
 
563
        topic = plugin.ModuleHelpTopic(mod)
 
564
        self.assertEqual('demo', topic.get_help_topic())
 
565
        mod = FakeModule('two lines of help\nand more', 'bzrlib.plugins.foo_bar')
 
566
        topic = plugin.ModuleHelpTopic(mod)
 
567
        self.assertEqual('foo_bar', topic.get_help_topic())