~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_plugins.py

  • Committer: Vincent Ladeuil
  • Date: 2008-01-29 15:16:31 UTC
  • mto: (3206.1.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 3207.
  • Revision ID: v.ladeuil+lp@free.fr-20080129151631-vqjd13tb405mobx6
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.

* bzrlib/tests/test_transform.py:
(TestTransformMerge): Revert previous patch and cleanly call
preview.finalize now that we can.

* bzrlib/tests/test_merge.py:
(TestMerge.test_make_preview_transform): Catch TransformPreview
leak.

* bzrlib/builtins.py:
(cmd_merge._do_preview): Finalize the TransformPreview or the
limbodir will stay in /tmp.

* bzrlib/transform.py:
(TreeTransformBase.__init__): Create the _deletiondir since it's
reffered to by finalize.
(TreeTransformBase.finalize): Delete the dir only if _deletiondir
is set.
(TreeTransform.__init__): Use a temp var for deletiondir and set
the attribute after the base class __init__ has been called.
(TransformPreview.__init__): Read locks the tree since finalize
wants to unlock it (as suggested by Aaron).

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
 
from bzrlib.osutils import pathjoin, abspath
30
 
 
31
 
class PluginTest(TestCaseInTempDir):
32
 
    """Create an external plugin and test loading."""
33
 
#    def test_plugin_loading(self):
34
 
#        orig_help = self.run_bzr_captured('bzr help commands')[0]
35
 
#        os.mkdir('plugin_test')
36
 
#        f = open(pathjoin('plugin_test', 'myplug.py'), 'wt')
37
 
#        f.write(PLUGIN_TEXT)
38
 
#        f.close()
39
 
#        newhelp = self.run_bzr_captured('bzr help commands')[0]
40
 
#        assert newhelp.startswith('You have been overridden\n')
41
 
#        # We added a line, but the rest should work
42
 
#        assert newhelp[25:] == help
43
 
#
44
 
#        assert backtick('bzr commit -m test') == "I'm sorry dave, you can't do that\n"
45
 
#
46
 
#        shutil.rmtree('plugin_test')
47
 
#
48
 
 
49
 
#         os.environ['BZRPLUGINPATH'] = abspath('plugin_test')
50
 
#         help = backtick('bzr help commands')
51
 
#         assert help.find('myplug') != -1
52
 
#         assert help.find('Just a simple test plugin.') != -1
53
 
 
54
 
 
55
 
#         assert backtick('bzr myplug') == 'Hello from my plugin\n'
56
 
#         assert backtick('bzr mplg') == 'Hello from my plugin\n'
57
 
 
58
 
#         f = open(pathjoin('plugin_test', 'override.py'), 'wb')
59
 
#         f.write("""import bzrlib, bzrlib.commands
60
 
#     class cmd_commit(bzrlib.commands.cmd_commit):
61
 
#         '''Commit changes into a new revision.'''
62
 
#         def run(self, *args, **kwargs):
63
 
#             print "I'm sorry dave, you can't do that"
64
 
 
65
 
#     class cmd_help(bzrlib.commands.cmd_help):
66
 
#         '''Show help on a command or other topic.'''
67
 
#         def run(self, *args, **kwargs):
68
 
#             print "You have been overridden"
69
 
#             bzrlib.commands.cmd_help.run(self, *args, **kwargs)
70
 
 
71
 
#         """
 
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
 
72
38
 
73
39
PLUGIN_TEXT = """\
74
40
import bzrlib.commands
81
47
 
82
48
# TODO: Write a test for plugin decoration of commands.
83
49
 
84
 
class TestOneNamedPluginOnly(TestCaseInTempDir):
 
50
class TestLoadingPlugins(TestCaseInTempDir):
85
51
 
86
52
    activeattributes = {}
87
53
 
88
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):
89
102
        # This test tests that having two plugins in different
90
 
        # directories does not result in both being loaded.
91
 
        # 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
92
106
        # for accessing in activeattributes. - we cannot give import parameters.
93
 
        tempattribute = "0"
 
107
        tempattribute = "different-dirs"
94
108
        self.failIf(tempattribute in self.activeattributes)
95
109
        # set a place for the plugins to record their loading, and at the same
96
110
        # time validate that the location the plugins should record to is
97
111
        # valid and correct.
98
 
        bzrlib.tests.test_plugins.TestOneNamedPluginOnly.activeattributes \
 
112
        bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
99
113
            [tempattribute] = []
100
114
        self.failUnless(tempattribute in self.activeattributes)
101
115
        # create two plugin directories
102
116
        os.mkdir('first')
103
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')
104
170
        # write a plugin that will record when its loaded in the 
105
171
        # tempattribute list.
106
 
        template = ("from bzrlib.tests.test_plugins import TestOneNamedPluginOnly\n"
107
 
                    "TestOneNamedPluginOnly.activeattributes[%r].append('%s')\n")
108
 
        print >> file(os.path.join('first', 'plugin.py'), 'w'), template % (tempattribute, 'first')
109
 
        print >> file(os.path.join('second', 'plugin.py'), 'w'), template % (tempattribute, 'second')
110
 
        try:
111
 
            bzrlib.plugin.load_from_dirs(['first', 'second'])
112
 
            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])
113
185
        finally:
114
186
            # remove the plugin 'plugin'
115
187
            del self.activeattributes[tempattribute]
116
 
            if getattr(bzrlib.plugins, 'plugin', None):
117
 
                del bzrlib.plugins.plugin
118
 
        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()
119
215
 
120
216
 
121
217
class TestAllPlugins(TestCaseInTempDir):
125
221
        # check the plugin is not loaded already
126
222
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
127
223
        # write a plugin that _cannot_ fail to load.
128
 
        print >> file('plugin.py', 'w'), ""
 
224
        file('plugin.py', 'w').write("\n")
129
225
        try:
130
 
            bzrlib.plugin.load_from_dirs(['.'])
131
 
            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)
132
230
            self.failUnless(getattr(bzrlib.plugins, 'plugin', None))
133
 
            self.assertEqual(bzrlib.plugin.all_plugins()['plugin'],
134
 
                             bzrlib.plugins.plugin)
 
231
            self.assertEqual(all_plugins['plugin'], bzrlib.plugins.plugin)
135
232
        finally:
136
233
            # remove the plugin 'plugin'
 
234
            if 'bzrlib.plugins.plugin' in sys.modules:
 
235
                del sys.modules['bzrlib.plugins.plugin']
137
236
            if getattr(bzrlib.plugins, 'plugin', None):
138
237
                del bzrlib.plugins.plugin
139
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_no_test_suite_gives_None_for_test_suite(self):
 
277
        self.setup_plugin()
 
278
        plugin = bzrlib.plugin.plugins()['plugin']
 
279
        self.assertEqual(None, plugin.test_suite())
 
280
 
 
281
    def test_test_suite_gives_test_suite_result(self):
 
282
        source = """def test_suite(): return 'foo'"""
 
283
        self.setup_plugin(source)
 
284
        plugin = bzrlib.plugin.plugins()['plugin']
 
285
        self.assertEqual('foo', plugin.test_suite())
 
286
 
 
287
    def test_no_version_info(self):
 
288
        self.setup_plugin()
 
289
        plugin = bzrlib.plugin.plugins()['plugin']
 
290
        self.assertEqual(None, plugin.version_info())
 
291
 
 
292
    def test_with_version_info(self):
 
293
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 4)")
 
294
        plugin = bzrlib.plugin.plugins()['plugin']
 
295
        self.assertEqual((1, 2, 3, 'dev', 4), plugin.version_info())
 
296
 
 
297
    def test_short_version_info_gets_padded(self):
 
298
        # the gtk plugin has version_info = (1,2,3) rather than the 5-tuple.
 
299
        # so we adapt it
 
300
        self.setup_plugin("version_info = (1, 2, 3)")
 
301
        plugin = bzrlib.plugin.plugins()['plugin']
 
302
        self.assertEqual((1, 2, 3, 'final', 0), plugin.version_info())
 
303
 
 
304
    def test_no_version_info___version__(self):
 
305
        self.setup_plugin()
 
306
        plugin = bzrlib.plugin.plugins()['plugin']
 
307
        self.assertEqual("unknown", plugin.__version__)
 
308
 
 
309
    def test___version__with_version_info(self):
 
310
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 4)")
 
311
        plugin = bzrlib.plugin.plugins()['plugin']
 
312
        self.assertEqual("1.2.3dev4", plugin.__version__)
 
313
 
 
314
    def test_final__version__with_version_info(self):
 
315
        self.setup_plugin("version_info = (1, 2, 3, 'final', 4)")
 
316
        plugin = bzrlib.plugin.plugins()['plugin']
 
317
        self.assertEqual("1.2.3", plugin.__version__)
 
318
 
 
319
 
 
320
class TestPluginHelp(TestCaseInTempDir):
 
321
 
 
322
    def split_help_commands(self):
 
323
        help = {}
 
324
        current = None
 
325
        for line in self.run_bzr('help commands')[0].splitlines():
 
326
            if not line.startswith(' '):
 
327
                current = line.split()[0]
 
328
            help[current] = help.get(current, '') + line
 
329
 
 
330
        return help
 
331
 
 
332
    def test_plugin_help_builtins_unaffected(self):
 
333
        # Check we don't get false positives
 
334
        help_commands = self.split_help_commands()
 
335
        for cmd_name in bzrlib.commands.builtin_command_names():
 
336
            if cmd_name in bzrlib.commands.plugin_command_names():
 
337
                continue
 
338
            try:
 
339
                help = bzrlib.commands.get_cmd_object(cmd_name).get_help_text()
 
340
            except NotImplementedError:
 
341
                # some commands have no help
 
342
                pass
 
343
            else:
 
344
                self.assertNotContainsRe(help, 'plugin "[^"]*"')
 
345
 
 
346
            if cmd_name in help_commands.keys():
 
347
                # some commands are hidden
 
348
                help = help_commands[cmd_name]
 
349
                self.assertNotContainsRe(help, 'plugin "[^"]*"')
 
350
 
 
351
    def test_plugin_help_shows_plugin(self):
 
352
        # Create a test plugin
 
353
        os.mkdir('plugin_test')
 
354
        f = open(pathjoin('plugin_test', 'myplug.py'), 'w')
 
355
        f.write(PLUGIN_TEXT)
 
356
        f.close()
 
357
 
 
358
        try:
 
359
            # Check its help
 
360
            bzrlib.plugin.load_from_path(['plugin_test'])
 
361
            bzrlib.commands.register_command( bzrlib.plugins.myplug.cmd_myplug)
 
362
            help = self.run_bzr('help myplug')[0]
 
363
            self.assertContainsRe(help, 'plugin "myplug"')
 
364
            help = self.split_help_commands()['myplug']
 
365
            self.assertContainsRe(help, '\[myplug\]')
 
366
        finally:
 
367
            # unregister command
 
368
            if bzrlib.commands.plugin_cmds.get('myplug', None):
 
369
                del bzrlib.commands.plugin_cmds['myplug']
 
370
            # remove the plugin 'myplug'
 
371
            if getattr(bzrlib.plugins, 'myplug', None):
 
372
                delattr(bzrlib.plugins, 'myplug')
 
373
 
 
374
 
 
375
class TestPluginFromZip(TestCaseInTempDir):
 
376
 
 
377
    def make_zipped_plugin(self, zip_name, filename):
 
378
        z = zipfile.ZipFile(zip_name, 'w')
 
379
        z.writestr(filename, PLUGIN_TEXT)
 
380
        z.close()
 
381
 
 
382
    def check_plugin_load(self, zip_name, plugin_name):
 
383
        self.assertFalse(plugin_name in dir(bzrlib.plugins),
 
384
                         'Plugin already loaded')
 
385
        old_path = bzrlib.plugins.__path__
 
386
        try:
 
387
            # this is normally done by load_plugins -> set_plugins_path
 
388
            bzrlib.plugins.__path__ = [zip_name]
 
389
            bzrlib.plugin.load_from_zip(zip_name)
 
390
            self.assertTrue(plugin_name in dir(bzrlib.plugins),
 
391
                            'Plugin is not loaded')
 
392
        finally:
 
393
            # unregister plugin
 
394
            if getattr(bzrlib.plugins, plugin_name, None):
 
395
                delattr(bzrlib.plugins, plugin_name)
 
396
                del sys.modules['bzrlib.plugins.' + plugin_name]
 
397
            bzrlib.plugins.__path__ = old_path
 
398
 
 
399
    def test_load_module(self):
 
400
        self.make_zipped_plugin('./test.zip', 'ziplug.py')
 
401
        self.check_plugin_load('./test.zip', 'ziplug')
 
402
 
 
403
    def test_load_package(self):
 
404
        self.make_zipped_plugin('./test.zip', 'ziplug/__init__.py')
 
405
        self.check_plugin_load('./test.zip', 'ziplug')
 
406
 
 
407
 
 
408
class TestSetPluginsPath(TestCase):
 
409
    
 
410
    def test_set_plugins_path(self):
 
411
        """set_plugins_path should set the module __path__ correctly."""
 
412
        old_path = bzrlib.plugins.__path__
 
413
        try:
 
414
            bzrlib.plugins.__path__ = []
 
415
            expected_path = bzrlib.plugin.set_plugins_path()
 
416
            self.assertEqual(expected_path, bzrlib.plugins.__path__)
 
417
        finally:
 
418
            bzrlib.plugins.__path__ = old_path
 
419
 
 
420
    def test_set_plugins_path_with_trailing_slashes(self):
 
421
        """set_plugins_path should set the module __path__ based on
 
422
        BZR_PLUGIN_PATH."""
 
423
        old_path = bzrlib.plugins.__path__
 
424
        old_env = os.environ.get('BZR_PLUGIN_PATH')
 
425
        try:
 
426
            bzrlib.plugins.__path__ = []
 
427
            os.environ['BZR_PLUGIN_PATH'] = "first\\//\\" + os.pathsep + \
 
428
                "second/\\/\\/"
 
429
            bzrlib.plugin.set_plugins_path()
 
430
            expected_path = ['first', 'second',
 
431
                os.path.dirname(bzrlib.plugins.__file__)]
 
432
            self.assertEqual(expected_path, bzrlib.plugins.__path__)
 
433
        finally:
 
434
            bzrlib.plugins.__path__ = old_path
 
435
            if old_env != None:
 
436
                os.environ['BZR_PLUGIN_PATH'] = old_env
 
437
            else:
 
438
                del os.environ['BZR_PLUGIN_PATH']
 
439
 
 
440
class TestHelpIndex(tests.TestCase):
 
441
    """Tests for the PluginsHelpIndex class."""
 
442
 
 
443
    def test_default_constructable(self):
 
444
        index = plugin.PluginsHelpIndex()
 
445
 
 
446
    def test_get_topics_None(self):
 
447
        """Searching for None returns an empty list."""
 
448
        index = plugin.PluginsHelpIndex()
 
449
        self.assertEqual([], index.get_topics(None))
 
450
 
 
451
    def test_get_topics_for_plugin(self):
 
452
        """Searching for plugin name gets its docstring."""
 
453
        index = plugin.PluginsHelpIndex()
 
454
        # make a new plugin here for this test, even if we're run with
 
455
        # --no-plugins
 
456
        self.assertFalse(sys.modules.has_key('bzrlib.plugins.demo_module'))
 
457
        demo_module = FakeModule('', 'bzrlib.plugins.demo_module')
 
458
        sys.modules['bzrlib.plugins.demo_module'] = demo_module
 
459
        try:
 
460
            topics = index.get_topics('demo_module')
 
461
            self.assertEqual(1, len(topics))
 
462
            self.assertIsInstance(topics[0], plugin.ModuleHelpTopic)
 
463
            self.assertEqual(demo_module, topics[0].module)
 
464
        finally:
 
465
            del sys.modules['bzrlib.plugins.demo_module']
 
466
 
 
467
    def test_get_topics_no_topic(self):
 
468
        """Searching for something that is not a plugin returns []."""
 
469
        # test this by using a name that cannot be a plugin - its not
 
470
        # a valid python identifier.
 
471
        index = plugin.PluginsHelpIndex()
 
472
        self.assertEqual([], index.get_topics('nothing by this name'))
 
473
 
 
474
    def test_prefix(self):
 
475
        """PluginsHelpIndex has a prefix of 'plugins/'."""
 
476
        index = plugin.PluginsHelpIndex()
 
477
        self.assertEqual('plugins/', index.prefix)
 
478
 
 
479
    def test_get_plugin_topic_with_prefix(self):
 
480
        """Searching for plugins/demo_module returns help."""
 
481
        index = plugin.PluginsHelpIndex()
 
482
        self.assertFalse(sys.modules.has_key('bzrlib.plugins.demo_module'))
 
483
        demo_module = FakeModule('', 'bzrlib.plugins.demo_module')
 
484
        sys.modules['bzrlib.plugins.demo_module'] = demo_module
 
485
        try:
 
486
            topics = index.get_topics('plugins/demo_module')
 
487
            self.assertEqual(1, len(topics))
 
488
            self.assertIsInstance(topics[0], plugin.ModuleHelpTopic)
 
489
            self.assertEqual(demo_module, topics[0].module)
 
490
        finally:
 
491
            del sys.modules['bzrlib.plugins.demo_module']
 
492
 
 
493
 
 
494
class FakeModule(object):
 
495
    """A fake module to test with."""
 
496
 
 
497
    def __init__(self, doc, name):
 
498
        self.__doc__ = doc
 
499
        self.__name__ = name
 
500
 
 
501
 
 
502
class TestModuleHelpTopic(tests.TestCase):
 
503
    """Tests for the ModuleHelpTopic class."""
 
504
 
 
505
    def test_contruct(self):
 
506
        """Construction takes the module to document."""
 
507
        mod = FakeModule('foo', 'foo')
 
508
        topic = plugin.ModuleHelpTopic(mod)
 
509
        self.assertEqual(mod, topic.module)
 
510
 
 
511
    def test_get_help_text_None(self):
 
512
        """A ModuleHelpTopic returns the docstring for get_help_text."""
 
513
        mod = FakeModule(None, 'demo')
 
514
        topic = plugin.ModuleHelpTopic(mod)
 
515
        self.assertEqual("Plugin 'demo' has no docstring.\n",
 
516
            topic.get_help_text())
 
517
 
 
518
    def test_get_help_text_no_carriage_return(self):
 
519
        """ModuleHelpTopic.get_help_text adds a \n if needed."""
 
520
        mod = FakeModule('one line of help', 'demo')
 
521
        topic = plugin.ModuleHelpTopic(mod)
 
522
        self.assertEqual("one line of help\n",
 
523
            topic.get_help_text())
 
524
 
 
525
    def test_get_help_text_carriage_return(self):
 
526
        """ModuleHelpTopic.get_help_text adds a \n if needed."""
 
527
        mod = FakeModule('two lines of help\nand more\n', 'demo')
 
528
        topic = plugin.ModuleHelpTopic(mod)
 
529
        self.assertEqual("two lines of help\nand more\n",
 
530
            topic.get_help_text())
 
531
 
 
532
    def test_get_help_text_with_additional_see_also(self):
 
533
        mod = FakeModule('two lines of help\nand more', 'demo')
 
534
        topic = plugin.ModuleHelpTopic(mod)
 
535
        self.assertEqual("two lines of help\nand more\nSee also: bar, foo\n",
 
536
            topic.get_help_text(['foo', 'bar']))
 
537
 
 
538
    def test_get_help_topic(self):
 
539
        """The help topic for a plugin is its module name."""
 
540
        mod = FakeModule('two lines of help\nand more', 'bzrlib.plugins.demo')
 
541
        topic = plugin.ModuleHelpTopic(mod)
 
542
        self.assertEqual('demo', topic.get_help_topic())
 
543
        mod = FakeModule('two lines of help\nand more', 'bzrlib.plugins.foo_bar')
 
544
        topic = plugin.ModuleHelpTopic(mod)
 
545
        self.assertEqual('foo_bar', topic.get_help_topic())