/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_plugins.py

  • Committer: John Arbash Meinel
  • Date: 2009-12-22 16:28:47 UTC
  • mto: This revision was merged to the branch mainline in revision 4922.
  • Revision ID: john@arbash-meinel.com-20091222162847-tvnsc69to4l4uf5r
Implement a permute_for_extension helper.

Use it for all of the 'simple' extension permutations.
It basically permutes all tests in the current module, by setting TestCase.module.
Which works well for most of our extension tests. Some had more advanced
handling of permutations (extra permutations, custom vars, etc.)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""Tests for plugins"""
 
18
 
 
19
# XXX: There are no plugin tests at the moment because the plugin module
 
20
# affects the global state of the process.  See bzrlib/plugins.py for more
 
21
# comments.
 
22
 
 
23
import logging
 
24
import os
 
25
from StringIO import StringIO
 
26
import sys
 
27
import zipfile
 
28
 
 
29
from bzrlib import (
 
30
    osutils,
 
31
    plugin,
 
32
    tests,
 
33
    )
 
34
import bzrlib.plugin
 
35
import bzrlib.plugins
 
36
import bzrlib.commands
 
37
import bzrlib.help
 
38
from bzrlib.tests import (
 
39
    TestCase,
 
40
    TestCaseInTempDir,
 
41
    TestUtil,
 
42
    )
 
43
from bzrlib.osutils import pathjoin, abspath, normpath
 
44
 
 
45
 
 
46
PLUGIN_TEXT = """\
 
47
import bzrlib.commands
 
48
class cmd_myplug(bzrlib.commands.Command):
 
49
    '''Just a simple test plugin.'''
 
50
    aliases = ['mplg']
 
51
    def run(self):
 
52
        print 'Hello from my plugin'
 
53
"""
 
54
 
 
55
# TODO: Write a test for plugin decoration of commands.
 
56
 
 
57
class TestLoadingPlugins(TestCaseInTempDir):
 
58
 
 
59
    activeattributes = {}
 
60
 
 
61
    def test_plugins_with_the_same_name_are_not_loaded(self):
 
62
        # This test tests that having two plugins in different directories does
 
63
        # not result in both being loaded when they have the same name.  get a
 
64
        # file name we can use which is also a valid attribute for accessing in
 
65
        # activeattributes. - we cannot give import parameters.
 
66
        tempattribute = "0"
 
67
        self.failIf(tempattribute in self.activeattributes)
 
68
        # set a place for the plugins to record their loading, and at the same
 
69
        # time validate that the location the plugins should record to is
 
70
        # valid and correct.
 
71
        bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
 
72
            [tempattribute] = []
 
73
        self.failUnless(tempattribute in self.activeattributes)
 
74
        # create two plugin directories
 
75
        os.mkdir('first')
 
76
        os.mkdir('second')
 
77
        # write a plugin that will record when its loaded in the
 
78
        # tempattribute list.
 
79
        template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
 
80
                    "TestLoadingPlugins.activeattributes[%r].append('%s')\n")
 
81
 
 
82
        outfile = open(os.path.join('first', 'plugin.py'), 'w')
 
83
        try:
 
84
            outfile.write(template % (tempattribute, 'first'))
 
85
            outfile.write('\n')
 
86
        finally:
 
87
            outfile.close()
 
88
 
 
89
        outfile = open(os.path.join('second', 'plugin.py'), 'w')
 
90
        try:
 
91
            outfile.write(template % (tempattribute, 'second'))
 
92
            outfile.write('\n')
 
93
        finally:
 
94
            outfile.close()
 
95
 
 
96
        try:
 
97
            bzrlib.plugin.load_from_path(['first', 'second'])
 
98
            self.assertEqual(['first'], self.activeattributes[tempattribute])
 
99
        finally:
 
100
            # remove the plugin 'plugin'
 
101
            del self.activeattributes[tempattribute]
 
102
            if 'bzrlib.plugins.plugin' in sys.modules:
 
103
                del sys.modules['bzrlib.plugins.plugin']
 
104
            if getattr(bzrlib.plugins, 'plugin', None):
 
105
                del bzrlib.plugins.plugin
 
106
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
 
107
 
 
108
    def test_plugins_from_different_dirs_can_demand_load(self):
 
109
        # This test tests that having two plugins in different
 
110
        # directories with different names allows them both to be loaded, when
 
111
        # we do a direct import statement.
 
112
        # Determine a file name we can use which is also a valid attribute
 
113
        # for accessing in activeattributes. - we cannot give import parameters.
 
114
        tempattribute = "different-dirs"
 
115
        self.failIf(tempattribute in self.activeattributes)
 
116
        # set a place for the plugins to record their loading, and at the same
 
117
        # time validate that the location the plugins should record to is
 
118
        # valid and correct.
 
119
        bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
 
120
            [tempattribute] = []
 
121
        self.failUnless(tempattribute in self.activeattributes)
 
122
        # create two plugin directories
 
123
        os.mkdir('first')
 
124
        os.mkdir('second')
 
125
        # write plugins that will record when they are loaded in the
 
126
        # tempattribute list.
 
127
        template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
 
128
                    "TestLoadingPlugins.activeattributes[%r].append('%s')\n")
 
129
 
 
130
        outfile = open(os.path.join('first', 'pluginone.py'), 'w')
 
131
        try:
 
132
            outfile.write(template % (tempattribute, 'first'))
 
133
            outfile.write('\n')
 
134
        finally:
 
135
            outfile.close()
 
136
 
 
137
        outfile = open(os.path.join('second', 'plugintwo.py'), 'w')
 
138
        try:
 
139
            outfile.write(template % (tempattribute, 'second'))
 
140
            outfile.write('\n')
 
141
        finally:
 
142
            outfile.close()
 
143
 
 
144
        oldpath = bzrlib.plugins.__path__
 
145
        try:
 
146
            bzrlib.plugins.__path__ = ['first', 'second']
 
147
            exec "import bzrlib.plugins.pluginone"
 
148
            self.assertEqual(['first'], self.activeattributes[tempattribute])
 
149
            exec "import bzrlib.plugins.plugintwo"
 
150
            self.assertEqual(['first', 'second'],
 
151
                self.activeattributes[tempattribute])
 
152
        finally:
 
153
            # remove the plugin 'plugin'
 
154
            del self.activeattributes[tempattribute]
 
155
            if getattr(bzrlib.plugins, 'pluginone', None):
 
156
                del bzrlib.plugins.pluginone
 
157
            if getattr(bzrlib.plugins, 'plugintwo', None):
 
158
                del bzrlib.plugins.plugintwo
 
159
        self.failIf(getattr(bzrlib.plugins, 'pluginone', None))
 
160
        self.failIf(getattr(bzrlib.plugins, 'plugintwo', None))
 
161
 
 
162
    def test_plugins_can_load_from_directory_with_trailing_slash(self):
 
163
        # This test tests that a plugin can load from a directory when the
 
164
        # directory in the path has a trailing slash.
 
165
        # check the plugin is not loaded already
 
166
        self.failIf(getattr(bzrlib.plugins, 'ts_plugin', None))
 
167
        tempattribute = "trailing-slash"
 
168
        self.failIf(tempattribute in self.activeattributes)
 
169
        # set a place for the plugin to record its loading, and at the same
 
170
        # time validate that the location the plugin should record to is
 
171
        # valid and correct.
 
172
        bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
 
173
            [tempattribute] = []
 
174
        self.failUnless(tempattribute in self.activeattributes)
 
175
        # create a directory for the plugin
 
176
        os.mkdir('plugin_test')
 
177
        # write a plugin that will record when its loaded in the
 
178
        # tempattribute list.
 
179
        template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
 
180
                    "TestLoadingPlugins.activeattributes[%r].append('%s')\n")
 
181
 
 
182
        outfile = open(os.path.join('plugin_test', 'ts_plugin.py'), 'w')
 
183
        try:
 
184
            outfile.write(template % (tempattribute, 'plugin'))
 
185
            outfile.write('\n')
 
186
        finally:
 
187
            outfile.close()
 
188
 
 
189
        try:
 
190
            bzrlib.plugin.load_from_path(['plugin_test'+os.sep])
 
191
            self.assertEqual(['plugin'], self.activeattributes[tempattribute])
 
192
        finally:
 
193
            # remove the plugin 'plugin'
 
194
            del self.activeattributes[tempattribute]
 
195
            if getattr(bzrlib.plugins, 'ts_plugin', None):
 
196
                del bzrlib.plugins.ts_plugin
 
197
        self.failIf(getattr(bzrlib.plugins, 'ts_plugin', None))
 
198
 
 
199
    def load_and_capture(self, name):
 
200
        """Load plugins from '.' capturing the output.
 
201
 
 
202
        :param name: The name of the plugin.
 
203
        :return: A string with the log from the plugin loading call.
 
204
        """
 
205
        # Capture output
 
206
        stream = StringIO()
 
207
        try:
 
208
            handler = logging.StreamHandler(stream)
 
209
            log = logging.getLogger('bzr')
 
210
            log.addHandler(handler)
 
211
            try:
 
212
                try:
 
213
                    bzrlib.plugin.load_from_path(['.'])
 
214
                finally:
 
215
                    if 'bzrlib.plugins.%s' % name in sys.modules:
 
216
                        del sys.modules['bzrlib.plugins.%s' % name]
 
217
                    if getattr(bzrlib.plugins, name, None):
 
218
                        delattr(bzrlib.plugins, name)
 
219
            finally:
 
220
                # Stop capturing output
 
221
                handler.flush()
 
222
                handler.close()
 
223
                log.removeHandler(handler)
 
224
            return stream.getvalue()
 
225
        finally:
 
226
            stream.close()
 
227
 
 
228
    def test_plugin_with_bad_api_version_reports(self):
 
229
        # This plugin asks for bzrlib api version 1.0.0, which is not supported
 
230
        # anymore.
 
231
        name = 'wants100.py'
 
232
        f = file(name, 'w')
 
233
        try:
 
234
            f.write("import bzrlib.api\n"
 
235
                "bzrlib.api.require_any_api(bzrlib, [(1, 0, 0)])\n")
 
236
        finally:
 
237
            f.close()
 
238
 
 
239
        log = self.load_and_capture(name)
 
240
        self.assertContainsRe(log,
 
241
            r"It requested API version")
 
242
 
 
243
    def test_plugin_with_bad_name_does_not_load(self):
 
244
        # The file name here invalid for a python module.
 
245
        name = 'bzr-bad plugin-name..py'
 
246
        file(name, 'w').close()
 
247
        log = self.load_and_capture(name)
 
248
        self.assertContainsRe(log,
 
249
            r"Unable to load 'bzr-bad plugin-name\.' in '\.' as a plugin "
 
250
            "because the file path isn't a valid module name; try renaming "
 
251
            "it to 'bad_plugin_name_'\.")
 
252
 
 
253
 
 
254
class TestPlugins(TestCaseInTempDir):
 
255
 
 
256
    def setup_plugin(self, source=""):
 
257
        # This test tests a new plugin appears in bzrlib.plugin.plugins().
 
258
        # check the plugin is not loaded already
 
259
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
 
260
        # write a plugin that _cannot_ fail to load.
 
261
        file('plugin.py', 'w').write(source + '\n')
 
262
        self.addCleanup(self.teardown_plugin)
 
263
        bzrlib.plugin.load_from_path(['.'])
 
264
 
 
265
    def teardown_plugin(self):
 
266
        # remove the plugin 'plugin'
 
267
        if 'bzrlib.plugins.plugin' in sys.modules:
 
268
            del sys.modules['bzrlib.plugins.plugin']
 
269
        if getattr(bzrlib.plugins, 'plugin', None):
 
270
            del bzrlib.plugins.plugin
 
271
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
 
272
 
 
273
    def test_plugin_appears_in_plugins(self):
 
274
        self.setup_plugin()
 
275
        self.failUnless('plugin' in bzrlib.plugin.plugins())
 
276
        self.failUnless(getattr(bzrlib.plugins, 'plugin', None))
 
277
        plugins = bzrlib.plugin.plugins()
 
278
        plugin = plugins['plugin']
 
279
        self.assertIsInstance(plugin, bzrlib.plugin.PlugIn)
 
280
        self.assertEqual(bzrlib.plugins.plugin, plugin.module)
 
281
 
 
282
    def test_trivial_plugin_get_path(self):
 
283
        self.setup_plugin()
 
284
        plugins = bzrlib.plugin.plugins()
 
285
        plugin = plugins['plugin']
 
286
        plugin_path = self.test_dir + '/plugin.py'
 
287
        self.assertIsSameRealPath(plugin_path, normpath(plugin.path()))
 
288
 
 
289
    def test_plugin_get_path_py_not_pyc(self):
 
290
        self.setup_plugin()         # after first import there will be plugin.pyc
 
291
        self.teardown_plugin()
 
292
        bzrlib.plugin.load_from_path(['.']) # import plugin.pyc
 
293
        plugins = bzrlib.plugin.plugins()
 
294
        plugin = plugins['plugin']
 
295
        plugin_path = self.test_dir + '/plugin.py'
 
296
        self.assertIsSameRealPath(plugin_path, normpath(plugin.path()))
 
297
 
 
298
    def test_plugin_get_path_pyc_only(self):
 
299
        self.setup_plugin()         # after first import there will be plugin.pyc
 
300
        self.teardown_plugin()
 
301
        os.unlink(self.test_dir + '/plugin.py')
 
302
        bzrlib.plugin.load_from_path(['.']) # import plugin.pyc
 
303
        plugins = bzrlib.plugin.plugins()
 
304
        plugin = plugins['plugin']
 
305
        if __debug__:
 
306
            plugin_path = self.test_dir + '/plugin.pyc'
 
307
        else:
 
308
            plugin_path = self.test_dir + '/plugin.pyo'
 
309
        self.assertIsSameRealPath(plugin_path, normpath(plugin.path()))
 
310
 
 
311
    def test_no_test_suite_gives_None_for_test_suite(self):
 
312
        self.setup_plugin()
 
313
        plugin = bzrlib.plugin.plugins()['plugin']
 
314
        self.assertEqual(None, plugin.test_suite())
 
315
 
 
316
    def test_test_suite_gives_test_suite_result(self):
 
317
        source = """def test_suite(): return 'foo'"""
 
318
        self.setup_plugin(source)
 
319
        plugin = bzrlib.plugin.plugins()['plugin']
 
320
        self.assertEqual('foo', plugin.test_suite())
 
321
 
 
322
    def test_no_load_plugin_tests_gives_None_for_load_plugin_tests(self):
 
323
        self.setup_plugin()
 
324
        loader = TestUtil.TestLoader()
 
325
        plugin = bzrlib.plugin.plugins()['plugin']
 
326
        self.assertEqual(None, plugin.load_plugin_tests(loader))
 
327
 
 
328
    def test_load_plugin_tests_gives_load_plugin_tests_result(self):
 
329
        source = """
 
330
def load_tests(standard_tests, module, loader):
 
331
    return 'foo'"""
 
332
        self.setup_plugin(source)
 
333
        loader = TestUtil.TestLoader()
 
334
        plugin = bzrlib.plugin.plugins()['plugin']
 
335
        self.assertEqual('foo', plugin.load_plugin_tests(loader))
 
336
 
 
337
    def test_no_version_info(self):
 
338
        self.setup_plugin()
 
339
        plugin = bzrlib.plugin.plugins()['plugin']
 
340
        self.assertEqual(None, plugin.version_info())
 
341
 
 
342
    def test_with_version_info(self):
 
343
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 4)")
 
344
        plugin = bzrlib.plugin.plugins()['plugin']
 
345
        self.assertEqual((1, 2, 3, 'dev', 4), plugin.version_info())
 
346
 
 
347
    def test_short_version_info_gets_padded(self):
 
348
        # the gtk plugin has version_info = (1,2,3) rather than the 5-tuple.
 
349
        # so we adapt it
 
350
        self.setup_plugin("version_info = (1, 2, 3)")
 
351
        plugin = bzrlib.plugin.plugins()['plugin']
 
352
        self.assertEqual((1, 2, 3, 'final', 0), plugin.version_info())
 
353
 
 
354
    def test_no_version_info___version__(self):
 
355
        self.setup_plugin()
 
356
        plugin = bzrlib.plugin.plugins()['plugin']
 
357
        self.assertEqual("unknown", plugin.__version__)
 
358
 
 
359
    def test_str__version__with_version_info(self):
 
360
        self.setup_plugin("version_info = '1.2.3'")
 
361
        plugin = bzrlib.plugin.plugins()['plugin']
 
362
        self.assertEqual("1.2.3", plugin.__version__)
 
363
 
 
364
    def test_noniterable__version__with_version_info(self):
 
365
        self.setup_plugin("version_info = (1)")
 
366
        plugin = bzrlib.plugin.plugins()['plugin']
 
367
        self.assertEqual("1", plugin.__version__)
 
368
 
 
369
    def test_1__version__with_version_info(self):
 
370
        self.setup_plugin("version_info = (1,)")
 
371
        plugin = bzrlib.plugin.plugins()['plugin']
 
372
        self.assertEqual("1", plugin.__version__)
 
373
 
 
374
    def test_1_2__version__with_version_info(self):
 
375
        self.setup_plugin("version_info = (1, 2)")
 
376
        plugin = bzrlib.plugin.plugins()['plugin']
 
377
        self.assertEqual("1.2", plugin.__version__)
 
378
 
 
379
    def test_1_2_3__version__with_version_info(self):
 
380
        self.setup_plugin("version_info = (1, 2, 3)")
 
381
        plugin = bzrlib.plugin.plugins()['plugin']
 
382
        self.assertEqual("1.2.3", plugin.__version__)
 
383
 
 
384
    def test_candidate__version__with_version_info(self):
 
385
        self.setup_plugin("version_info = (1, 2, 3, 'candidate', 1)")
 
386
        plugin = bzrlib.plugin.plugins()['plugin']
 
387
        self.assertEqual("1.2.3rc1", plugin.__version__)
 
388
 
 
389
    def test_dev__version__with_version_info(self):
 
390
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 0)")
 
391
        plugin = bzrlib.plugin.plugins()['plugin']
 
392
        self.assertEqual("1.2.3dev", plugin.__version__)
 
393
 
 
394
    def test_dev_fallback__version__with_version_info(self):
 
395
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 4)")
 
396
        plugin = bzrlib.plugin.plugins()['plugin']
 
397
        self.assertEqual("1.2.3dev4", plugin.__version__)
 
398
 
 
399
    def test_final__version__with_version_info(self):
 
400
        self.setup_plugin("version_info = (1, 2, 3, 'final', 0)")
 
401
        plugin = bzrlib.plugin.plugins()['plugin']
 
402
        self.assertEqual("1.2.3", plugin.__version__)
 
403
 
 
404
    def test_final_fallback__version__with_version_info(self):
 
405
        self.setup_plugin("version_info = (1, 2, 3, 'final', 2)")
 
406
        plugin = bzrlib.plugin.plugins()['plugin']
 
407
        self.assertEqual("1.2.3.final.2", plugin.__version__)
 
408
 
 
409
 
 
410
class TestPluginHelp(TestCaseInTempDir):
 
411
 
 
412
    def split_help_commands(self):
 
413
        help = {}
 
414
        current = None
 
415
        out, err = self.run_bzr('--no-plugins help commands')
 
416
        for line in out.splitlines():
 
417
            if not line.startswith(' '):
 
418
                current = line.split()[0]
 
419
            help[current] = help.get(current, '') + line
 
420
 
 
421
        return help
 
422
 
 
423
    def test_plugin_help_builtins_unaffected(self):
 
424
        # Check we don't get false positives
 
425
        help_commands = self.split_help_commands()
 
426
        for cmd_name in bzrlib.commands.builtin_command_names():
 
427
            if cmd_name in bzrlib.commands.plugin_command_names():
 
428
                continue
 
429
            try:
 
430
                help = bzrlib.commands.get_cmd_object(cmd_name).get_help_text()
 
431
            except NotImplementedError:
 
432
                # some commands have no help
 
433
                pass
 
434
            else:
 
435
                self.assertNotContainsRe(help, 'plugin "[^"]*"')
 
436
 
 
437
            if cmd_name in help_commands.keys():
 
438
                # some commands are hidden
 
439
                help = help_commands[cmd_name]
 
440
                self.assertNotContainsRe(help, 'plugin "[^"]*"')
 
441
 
 
442
    def test_plugin_help_shows_plugin(self):
 
443
        # Create a test plugin
 
444
        os.mkdir('plugin_test')
 
445
        f = open(pathjoin('plugin_test', 'myplug.py'), 'w')
 
446
        f.write(PLUGIN_TEXT)
 
447
        f.close()
 
448
 
 
449
        try:
 
450
            # Check its help
 
451
            bzrlib.plugin.load_from_path(['plugin_test'])
 
452
            bzrlib.commands.register_command( bzrlib.plugins.myplug.cmd_myplug)
 
453
            help = self.run_bzr('help myplug')[0]
 
454
            self.assertContainsRe(help, 'plugin "myplug"')
 
455
            help = self.split_help_commands()['myplug']
 
456
            self.assertContainsRe(help, '\[myplug\]')
 
457
        finally:
 
458
            # unregister command
 
459
            if 'myplug' in bzrlib.commands.plugin_cmds:
 
460
                bzrlib.commands.plugin_cmds.remove('myplug')
 
461
            # remove the plugin 'myplug'
 
462
            if getattr(bzrlib.plugins, 'myplug', None):
 
463
                delattr(bzrlib.plugins, 'myplug')
 
464
 
 
465
 
 
466
class TestHelpIndex(tests.TestCase):
 
467
    """Tests for the PluginsHelpIndex class."""
 
468
 
 
469
    def test_default_constructable(self):
 
470
        index = plugin.PluginsHelpIndex()
 
471
 
 
472
    def test_get_topics_None(self):
 
473
        """Searching for None returns an empty list."""
 
474
        index = plugin.PluginsHelpIndex()
 
475
        self.assertEqual([], index.get_topics(None))
 
476
 
 
477
    def test_get_topics_for_plugin(self):
 
478
        """Searching for plugin name gets its docstring."""
 
479
        index = plugin.PluginsHelpIndex()
 
480
        # make a new plugin here for this test, even if we're run with
 
481
        # --no-plugins
 
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('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
    def test_get_topics_no_topic(self):
 
494
        """Searching for something that is not a plugin returns []."""
 
495
        # test this by using a name that cannot be a plugin - its not
 
496
        # a valid python identifier.
 
497
        index = plugin.PluginsHelpIndex()
 
498
        self.assertEqual([], index.get_topics('nothing by this name'))
 
499
 
 
500
    def test_prefix(self):
 
501
        """PluginsHelpIndex has a prefix of 'plugins/'."""
 
502
        index = plugin.PluginsHelpIndex()
 
503
        self.assertEqual('plugins/', index.prefix)
 
504
 
 
505
    def test_get_plugin_topic_with_prefix(self):
 
506
        """Searching for plugins/demo_module returns help."""
 
507
        index = plugin.PluginsHelpIndex()
 
508
        self.assertFalse(sys.modules.has_key('bzrlib.plugins.demo_module'))
 
509
        demo_module = FakeModule('', 'bzrlib.plugins.demo_module')
 
510
        sys.modules['bzrlib.plugins.demo_module'] = demo_module
 
511
        try:
 
512
            topics = index.get_topics('plugins/demo_module')
 
513
            self.assertEqual(1, len(topics))
 
514
            self.assertIsInstance(topics[0], plugin.ModuleHelpTopic)
 
515
            self.assertEqual(demo_module, topics[0].module)
 
516
        finally:
 
517
            del sys.modules['bzrlib.plugins.demo_module']
 
518
 
 
519
 
 
520
class FakeModule(object):
 
521
    """A fake module to test with."""
 
522
 
 
523
    def __init__(self, doc, name):
 
524
        self.__doc__ = doc
 
525
        self.__name__ = name
 
526
 
 
527
 
 
528
class TestModuleHelpTopic(tests.TestCase):
 
529
    """Tests for the ModuleHelpTopic class."""
 
530
 
 
531
    def test_contruct(self):
 
532
        """Construction takes the module to document."""
 
533
        mod = FakeModule('foo', 'foo')
 
534
        topic = plugin.ModuleHelpTopic(mod)
 
535
        self.assertEqual(mod, topic.module)
 
536
 
 
537
    def test_get_help_text_None(self):
 
538
        """A ModuleHelpTopic returns the docstring for get_help_text."""
 
539
        mod = FakeModule(None, 'demo')
 
540
        topic = plugin.ModuleHelpTopic(mod)
 
541
        self.assertEqual("Plugin 'demo' has no docstring.\n",
 
542
            topic.get_help_text())
 
543
 
 
544
    def test_get_help_text_no_carriage_return(self):
 
545
        """ModuleHelpTopic.get_help_text adds a \n if needed."""
 
546
        mod = FakeModule('one line of help', 'demo')
 
547
        topic = plugin.ModuleHelpTopic(mod)
 
548
        self.assertEqual("one line of help\n",
 
549
            topic.get_help_text())
 
550
 
 
551
    def test_get_help_text_carriage_return(self):
 
552
        """ModuleHelpTopic.get_help_text adds a \n if needed."""
 
553
        mod = FakeModule('two lines of help\nand more\n', 'demo')
 
554
        topic = plugin.ModuleHelpTopic(mod)
 
555
        self.assertEqual("two lines of help\nand more\n",
 
556
            topic.get_help_text())
 
557
 
 
558
    def test_get_help_text_with_additional_see_also(self):
 
559
        mod = FakeModule('two lines of help\nand more', 'demo')
 
560
        topic = plugin.ModuleHelpTopic(mod)
 
561
        self.assertEqual("two lines of help\nand more\nSee also: bar, foo\n",
 
562
            topic.get_help_text(['foo', 'bar']))
 
563
 
 
564
    def test_get_help_topic(self):
 
565
        """The help topic for a plugin is its module name."""
 
566
        mod = FakeModule('two lines of help\nand more', 'bzrlib.plugins.demo')
 
567
        topic = plugin.ModuleHelpTopic(mod)
 
568
        self.assertEqual('demo', topic.get_help_topic())
 
569
        mod = FakeModule('two lines of help\nand more', 'bzrlib.plugins.foo_bar')
 
570
        topic = plugin.ModuleHelpTopic(mod)
 
571
        self.assertEqual('foo_bar', topic.get_help_topic())
 
572
 
 
573
 
 
574
class TestLoadFromPath(tests.TestCaseInTempDir):
 
575
 
 
576
    def setUp(self):
 
577
        super(TestLoadFromPath, self).setUp()
 
578
        # Save the attributes that we're about to monkey-patch.
 
579
        old_plugins_path = bzrlib.plugins.__path__
 
580
        old_loaded = plugin._loaded
 
581
        old_load_from_path = plugin.load_from_path
 
582
 
 
583
        def restore():
 
584
            bzrlib.plugins.__path__ = old_plugins_path
 
585
            plugin._loaded = old_loaded
 
586
            plugin.load_from_path = old_load_from_path
 
587
 
 
588
        self.addCleanup(restore)
 
589
 
 
590
        # Change bzrlib.plugin to think no plugins have been loaded yet.
 
591
        bzrlib.plugins.__path__ = []
 
592
        plugin._loaded = False
 
593
 
 
594
        # Monkey-patch load_from_path to stop it from actually loading anything.
 
595
        def load_from_path(dirs):
 
596
            pass
 
597
        plugin.load_from_path = load_from_path
 
598
 
 
599
    def test_set_plugins_path_with_args(self):
 
600
        plugin.set_plugins_path(['a', 'b'])
 
601
        self.assertEqual(['a', 'b'], bzrlib.plugins.__path__)
 
602
 
 
603
    def test_set_plugins_path_defaults(self):
 
604
        plugin.set_plugins_path()
 
605
        self.assertEqual(plugin.get_standard_plugins_path(),
 
606
                         bzrlib.plugins.__path__)
 
607
 
 
608
    def test_get_standard_plugins_path(self):
 
609
        path = plugin.get_standard_plugins_path()
 
610
        for directory in path:
 
611
            self.assertNotContainsRe(directory, r'\\/$')
 
612
        try:
 
613
            from distutils.sysconfig import get_python_lib
 
614
        except ImportError:
 
615
            pass
 
616
        else:
 
617
            if sys.platform != 'win32':
 
618
                python_lib = get_python_lib()
 
619
                for directory in path:
 
620
                    if directory.startswith(python_lib):
 
621
                        break
 
622
                else:
 
623
                    self.fail('No path to global plugins')
 
624
 
 
625
    def test_get_standard_plugins_path_env(self):
 
626
        os.environ['BZR_PLUGIN_PATH'] = 'foo/'
 
627
        path = plugin.get_standard_plugins_path()
 
628
        for directory in path:
 
629
            self.assertNotContainsRe(directory, r'\\/$')
 
630
 
 
631
    def test_load_plugins(self):
 
632
        plugin.load_plugins(['.'])
 
633
        self.assertEqual(bzrlib.plugins.__path__, ['.'])
 
634
        # subsequent loads are no-ops
 
635
        plugin.load_plugins(['foo'])
 
636
        self.assertEqual(bzrlib.plugins.__path__, ['.'])
 
637
 
 
638
    def test_load_plugins_default(self):
 
639
        plugin.load_plugins()
 
640
        path = plugin.get_standard_plugins_path()
 
641
        self.assertEqual(path, bzrlib.plugins.__path__)
 
642
 
 
643
 
 
644
class TestEnvPluginPath(tests.TestCaseInTempDir):
 
645
 
 
646
    def setUp(self):
 
647
        super(TestEnvPluginPath, self).setUp()
 
648
        old_default = plugin.DEFAULT_PLUGIN_PATH
 
649
 
 
650
        def restore():
 
651
            plugin.DEFAULT_PLUGIN_PATH = old_default
 
652
 
 
653
        self.addCleanup(restore)
 
654
 
 
655
        plugin.DEFAULT_PLUGIN_PATH = None
 
656
 
 
657
        self.user = plugin.get_user_plugin_path()
 
658
        self.site = plugin.get_site_plugin_path()
 
659
        self.core = plugin.get_core_plugin_path()
 
660
 
 
661
    def _list2paths(self, *args):
 
662
        paths = []
 
663
        for p in args:
 
664
            plugin._append_new_path(paths, p)
 
665
        return paths
 
666
 
 
667
    def _set_path(self, *args):
 
668
        path = os.pathsep.join(self._list2paths(*args))
 
669
        osutils.set_or_unset_env('BZR_PLUGIN_PATH', path)
 
670
 
 
671
    def check_path(self, expected_dirs, setting_dirs):
 
672
        if setting_dirs:
 
673
            self._set_path(*setting_dirs)
 
674
        actual = plugin.get_standard_plugins_path()
 
675
        self.assertEquals(self._list2paths(*expected_dirs), actual)
 
676
 
 
677
    def test_default(self):
 
678
        self.check_path([self.user, self.core, self.site],
 
679
                        None)
 
680
 
 
681
    def test_adhoc_policy(self):
 
682
        self.check_path([self.user, self.core, self.site],
 
683
                        ['+user', '+core', '+site'])
 
684
 
 
685
    def test_fallback_policy(self):
 
686
        self.check_path([self.core, self.site, self.user],
 
687
                        ['+core', '+site', '+user'])
 
688
 
 
689
    def test_override_policy(self):
 
690
        self.check_path([self.user, self.site, self.core],
 
691
                        ['+user', '+site', '+core'])
 
692
 
 
693
    def test_disable_user(self):
 
694
        self.check_path([self.core, self.site], ['-user'])
 
695
 
 
696
    def test_disable_user_twice(self):
 
697
        # Ensures multiple removals don't left cruft
 
698
        self.check_path([self.core, self.site], ['-user', '-user'])
 
699
 
 
700
    def test_duplicates_are_removed(self):
 
701
        self.check_path([self.user, self.core, self.site],
 
702
                        ['+user', '+user'])
 
703
        # And only the first reference is kept (since the later references will
 
704
        # onnly produce <plugin> already loaded mutters)
 
705
        self.check_path([self.user, self.core, self.site],
 
706
                        ['+user', '+user', '+core',
 
707
                         '+user', '+site', '+site',
 
708
                         '+core'])
 
709
 
 
710
    def test_disable_overrides_disable(self):
 
711
        self.check_path([self.core, self.site], ['-user', '+user'])
 
712
 
 
713
    def test_disable_core(self):
 
714
        self.check_path([self.site], ['-core'])
 
715
        self.check_path([self.user, self.site], ['+user', '-core'])
 
716
 
 
717
    def test_disable_site(self):
 
718
        self.check_path([self.core], ['-site'])
 
719
        self.check_path([self.user, self.core], ['-site', '+user'])
 
720
 
 
721
    def test_override_site(self):
 
722
        self.check_path(['mysite', self.user, self.core],
 
723
                        ['mysite', '-site', '+user'])
 
724
        self.check_path(['mysite', self.core],
 
725
                        ['mysite', '-site'])
 
726
 
 
727
    def test_override_core(self):
 
728
        self.check_path(['mycore', self.user, self.site],
 
729
                        ['mycore', '-core', '+user', '+site'])
 
730
        self.check_path(['mycore', self.site],
 
731
                        ['mycore', '-core'])
 
732
 
 
733
    def test_my_plugin_only(self):
 
734
        self.check_path(['myplugin'], ['myplugin', '-user', '-core', '-site'])
 
735
 
 
736
    def test_my_plugin_first(self):
 
737
        self.check_path(['myplugin', self.core, self.site, self.user],
 
738
                        ['myplugin', '+core', '+site', '+user'])
 
739
 
 
740
    def test_bogus_references(self):
 
741
        self.check_path(['+foo', '-bar', self.core, self.site],
 
742
                        ['+foo', '-bar'])