~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_plugins.py

  • Committer: Andrew Bennetts
  • Date: 2010-06-25 06:47:40 UTC
  • mto: (5050.3.16 2.2)
  • mto: This revision was merged to the branch mainline in revision 5365.
  • Revision ID: andrew.bennetts@canonical.com-20100625064740-k93ngat248kdcqdm
Remove merge_into_helper for now, as it currently has no callers.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2007 Canonical Ltd
 
1
# Copyright (C) 2005-2010 Canonical Ltd
2
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
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
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""Tests for plugins"""
18
18
 
20
20
# affects the global state of the process.  See bzrlib/plugins.py for more
21
21
# comments.
22
22
 
 
23
from cStringIO import StringIO
23
24
import logging
24
25
import os
25
 
from StringIO import StringIO
26
26
import sys
27
 
import zipfile
28
27
 
29
 
from bzrlib import plugin, tests
30
 
import bzrlib.plugin
31
 
import bzrlib.plugins
32
 
import bzrlib.commands
33
 
import bzrlib.help
34
 
from bzrlib.symbol_versioning import one_three
35
 
from bzrlib.tests import (
36
 
    TestCase,
37
 
    TestCaseInTempDir,
38
 
    TestUtil,
 
28
import bzrlib
 
29
from bzrlib import (
 
30
    errors,
 
31
    osutils,
 
32
    plugin,
 
33
    plugins,
 
34
    tests,
 
35
    trace,
39
36
    )
40
 
from bzrlib.osutils import pathjoin, abspath, normpath
41
 
 
42
 
 
43
 
PLUGIN_TEXT = """\
44
 
import bzrlib.commands
45
 
class cmd_myplug(bzrlib.commands.Command):
46
 
    '''Just a simple test plugin.'''
47
 
    aliases = ['mplg']
48
 
    def run(self):
49
 
        print 'Hello from my plugin'
50
 
"""
 
37
 
51
38
 
52
39
# TODO: Write a test for plugin decoration of commands.
53
40
 
54
 
class TestLoadingPlugins(TestCaseInTempDir):
 
41
class TestPluginMixin(object):
 
42
 
 
43
    def create_plugin(self, name, source=None, dir='.', file_name=None):
 
44
        if source is None:
 
45
            source = '''\
 
46
"""This is the doc for %s"""
 
47
''' % (name)
 
48
        if file_name is None:
 
49
            file_name = name + '.py'
 
50
        # 'source' must not fail to load
 
51
        path = osutils.pathjoin(dir, file_name)
 
52
        f = open(path, 'w')
 
53
        self.addCleanup(os.unlink, path)
 
54
        try:
 
55
            f.write(source + '\n')
 
56
        finally:
 
57
            f.close()
 
58
 
 
59
    def create_plugin_package(self, name, dir=None, source=None):
 
60
        if dir is None:
 
61
            dir = name
 
62
        if source is None:
 
63
            source = '''\
 
64
"""This is the doc for %s"""
 
65
dir_source = '%s'
 
66
''' % (name, dir)
 
67
        os.makedirs(dir)
 
68
        def cleanup():
 
69
            # Workaround lazy import random? madness
 
70
            osutils.rmtree(dir)
 
71
        self.addCleanup(cleanup)
 
72
        self.create_plugin(name, source, dir,
 
73
                           file_name='__init__.py')
 
74
 
 
75
    def _unregister_plugin(self, name):
 
76
        """Remove the plugin from sys.modules and the bzrlib namespace."""
 
77
        py_name = 'bzrlib.plugins.%s' % name
 
78
        if py_name in sys.modules:
 
79
            del sys.modules[py_name]
 
80
        if getattr(bzrlib.plugins, name, None) is not None:
 
81
            delattr(bzrlib.plugins, name)
 
82
 
 
83
    def _unregister_plugin_submodule(self, plugin_name, submodule_name):
 
84
        """Remove the submodule from sys.modules and the bzrlib namespace."""
 
85
        py_name = 'bzrlib.plugins.%s.%s' % (plugin_name, submodule_name)
 
86
        if py_name in sys.modules:
 
87
            del sys.modules[py_name]
 
88
        plugin = getattr(bzrlib.plugins, plugin_name, None)
 
89
        if plugin is not None:
 
90
            if getattr(plugin, submodule_name, None) is not None:
 
91
                delattr(plugin, submodule_name)
 
92
 
 
93
    def assertPluginUnknown(self, name):
 
94
        self.failIf(getattr(bzrlib.plugins, name, None) is not None)
 
95
        self.failIf('bzrlib.plugins.%s' % name in sys.modules)
 
96
 
 
97
    def assertPluginKnown(self, name):
 
98
        self.failUnless(getattr(bzrlib.plugins, name, None) is not None)
 
99
        self.failUnless('bzrlib.plugins.%s' % name in sys.modules)
 
100
 
 
101
 
 
102
class TestLoadingPlugins(tests.TestCaseInTempDir, TestPluginMixin):
55
103
 
56
104
    activeattributes = {}
57
105
 
65
113
        # set a place for the plugins to record their loading, and at the same
66
114
        # time validate that the location the plugins should record to is
67
115
        # valid and correct.
68
 
        bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
69
 
            [tempattribute] = []
 
116
        self.__class__.activeattributes [tempattribute] = []
70
117
        self.failUnless(tempattribute in self.activeattributes)
71
118
        # create two plugin directories
72
119
        os.mkdir('first')
73
120
        os.mkdir('second')
74
 
        # write a plugin that will record when its loaded in the 
 
121
        # write a plugin that will record when its loaded in the
75
122
        # tempattribute list.
76
123
        template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
77
124
                    "TestLoadingPlugins.activeattributes[%r].append('%s')\n")
96
143
        finally:
97
144
            # remove the plugin 'plugin'
98
145
            del self.activeattributes[tempattribute]
99
 
            if 'bzrlib.plugins.plugin' in sys.modules:
100
 
                del sys.modules['bzrlib.plugins.plugin']
101
 
            if getattr(bzrlib.plugins, 'plugin', None):
102
 
                del bzrlib.plugins.plugin
103
 
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
 
146
            self._unregister_plugin('plugin')
 
147
        self.assertPluginUnknown('plugin')
104
148
 
105
149
    def test_plugins_from_different_dirs_can_demand_load(self):
 
150
        self.failIf('bzrlib.plugins.pluginone' in sys.modules)
 
151
        self.failIf('bzrlib.plugins.plugintwo' in sys.modules)
106
152
        # This test tests that having two plugins in different
107
153
        # directories with different names allows them both to be loaded, when
108
154
        # we do a direct import statement.
119
165
        # create two plugin directories
120
166
        os.mkdir('first')
121
167
        os.mkdir('second')
122
 
        # write plugins that will record when they are loaded in the 
 
168
        # write plugins that will record when they are loaded in the
123
169
        # tempattribute list.
124
170
        template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
125
171
                    "TestLoadingPlugins.activeattributes[%r].append('%s')\n")
140
186
 
141
187
        oldpath = bzrlib.plugins.__path__
142
188
        try:
 
189
            self.failIf('bzrlib.plugins.pluginone' in sys.modules)
 
190
            self.failIf('bzrlib.plugins.plugintwo' in sys.modules)
143
191
            bzrlib.plugins.__path__ = ['first', 'second']
144
192
            exec "import bzrlib.plugins.pluginone"
145
193
            self.assertEqual(['first'], self.activeattributes[tempattribute])
149
197
        finally:
150
198
            # remove the plugin 'plugin'
151
199
            del self.activeattributes[tempattribute]
152
 
            if getattr(bzrlib.plugins, 'pluginone', None):
153
 
                del bzrlib.plugins.pluginone
154
 
            if getattr(bzrlib.plugins, 'plugintwo', None):
155
 
                del bzrlib.plugins.plugintwo
156
 
        self.failIf(getattr(bzrlib.plugins, 'pluginone', None))
157
 
        self.failIf(getattr(bzrlib.plugins, 'plugintwo', None))
 
200
            self._unregister_plugin('pluginone')
 
201
            self._unregister_plugin('plugintwo')
 
202
        self.assertPluginUnknown('pluginone')
 
203
        self.assertPluginUnknown('plugintwo')
158
204
 
159
205
    def test_plugins_can_load_from_directory_with_trailing_slash(self):
160
206
        # This test tests that a plugin can load from a directory when the
161
207
        # directory in the path has a trailing slash.
162
208
        # check the plugin is not loaded already
163
 
        self.failIf(getattr(bzrlib.plugins, 'ts_plugin', None))
 
209
        self.assertPluginUnknown('ts_plugin')
164
210
        tempattribute = "trailing-slash"
165
211
        self.failIf(tempattribute in self.activeattributes)
166
212
        # set a place for the plugin to record its loading, and at the same
171
217
        self.failUnless(tempattribute in self.activeattributes)
172
218
        # create a directory for the plugin
173
219
        os.mkdir('plugin_test')
174
 
        # write a plugin that will record when its loaded in the 
 
220
        # write a plugin that will record when its loaded in the
175
221
        # tempattribute list.
176
222
        template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
177
223
                    "TestLoadingPlugins.activeattributes[%r].append('%s')\n")
187
233
            bzrlib.plugin.load_from_path(['plugin_test'+os.sep])
188
234
            self.assertEqual(['plugin'], self.activeattributes[tempattribute])
189
235
        finally:
190
 
            # remove the plugin 'plugin'
191
236
            del self.activeattributes[tempattribute]
192
 
            if getattr(bzrlib.plugins, 'ts_plugin', None):
193
 
                del bzrlib.plugins.ts_plugin
194
 
        self.failIf(getattr(bzrlib.plugins, 'ts_plugin', None))
 
237
            self._unregister_plugin('ts_plugin')
 
238
        self.assertPluginUnknown('ts_plugin')
195
239
 
196
240
    def load_and_capture(self, name):
197
241
        """Load plugins from '.' capturing the output.
198
 
        
 
242
 
199
243
        :param name: The name of the plugin.
200
244
        :return: A string with the log from the plugin loading call.
201
245
        """
221
265
            return stream.getvalue()
222
266
        finally:
223
267
            stream.close()
224
 
    
 
268
 
225
269
    def test_plugin_with_bad_api_version_reports(self):
226
270
        # This plugin asks for bzrlib api version 1.0.0, which is not supported
227
271
        # anymore.
248
292
            "it to 'bad_plugin_name_'\.")
249
293
 
250
294
 
251
 
class TestPlugins(TestCaseInTempDir):
 
295
class TestPlugins(tests.TestCaseInTempDir, TestPluginMixin):
252
296
 
253
297
    def setup_plugin(self, source=""):
254
298
        # This test tests a new plugin appears in bzrlib.plugin.plugins().
255
299
        # check the plugin is not loaded already
256
 
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
 
300
        self.assertPluginUnknown('plugin')
257
301
        # write a plugin that _cannot_ fail to load.
258
302
        file('plugin.py', 'w').write(source + '\n')
259
303
        self.addCleanup(self.teardown_plugin)
260
 
        bzrlib.plugin.load_from_path(['.'])
261
 
    
 
304
        plugin.load_from_path(['.'])
 
305
 
262
306
    def teardown_plugin(self):
263
 
        # remove the plugin 'plugin'
264
 
        if 'bzrlib.plugins.plugin' in sys.modules:
265
 
            del sys.modules['bzrlib.plugins.plugin']
266
 
        if getattr(bzrlib.plugins, 'plugin', None):
267
 
            del bzrlib.plugins.plugin
268
 
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
 
307
        self._unregister_plugin('plugin')
 
308
        self.assertPluginUnknown('plugin')
269
309
 
270
310
    def test_plugin_appears_in_plugins(self):
271
311
        self.setup_plugin()
272
 
        self.failUnless('plugin' in bzrlib.plugin.plugins())
273
 
        self.failUnless(getattr(bzrlib.plugins, 'plugin', None))
274
 
        plugins = bzrlib.plugin.plugins()
275
 
        plugin = plugins['plugin']
276
 
        self.assertIsInstance(plugin, bzrlib.plugin.PlugIn)
277
 
        self.assertEqual(bzrlib.plugins.plugin, plugin.module)
 
312
        self.assertPluginKnown('plugin')
 
313
        p = plugin.plugins()['plugin']
 
314
        self.assertIsInstance(p, bzrlib.plugin.PlugIn)
 
315
        self.assertEqual(p.module, plugins.plugin)
278
316
 
279
317
    def test_trivial_plugin_get_path(self):
280
318
        self.setup_plugin()
281
 
        plugins = bzrlib.plugin.plugins()
282
 
        plugin = plugins['plugin']
 
319
        p = plugin.plugins()['plugin']
283
320
        plugin_path = self.test_dir + '/plugin.py'
284
 
        self.assertIsSameRealPath(plugin_path, normpath(plugin.path()))
 
321
        self.assertIsSameRealPath(plugin_path, osutils.normpath(p.path()))
285
322
 
286
323
    def test_plugin_get_path_py_not_pyc(self):
287
 
        self.setup_plugin()         # after first import there will be plugin.pyc
 
324
        # first import creates plugin.pyc
 
325
        self.setup_plugin()
288
326
        self.teardown_plugin()
289
 
        bzrlib.plugin.load_from_path(['.']) # import plugin.pyc
290
 
        plugins = bzrlib.plugin.plugins()
291
 
        plugin = plugins['plugin']
 
327
        plugin.load_from_path(['.']) # import plugin.pyc
 
328
        p = plugin.plugins()['plugin']
292
329
        plugin_path = self.test_dir + '/plugin.py'
293
 
        self.assertIsSameRealPath(plugin_path, normpath(plugin.path()))
 
330
        self.assertIsSameRealPath(plugin_path, osutils.normpath(p.path()))
294
331
 
295
332
    def test_plugin_get_path_pyc_only(self):
296
 
        self.setup_plugin()         # after first import there will be plugin.pyc
 
333
        # first import creates plugin.pyc (or plugin.pyo depending on __debug__)
 
334
        self.setup_plugin()
297
335
        self.teardown_plugin()
298
336
        os.unlink(self.test_dir + '/plugin.py')
299
 
        bzrlib.plugin.load_from_path(['.']) # import plugin.pyc
300
 
        plugins = bzrlib.plugin.plugins()
301
 
        plugin = plugins['plugin']
 
337
        plugin.load_from_path(['.']) # import plugin.pyc (or .pyo)
 
338
        p = plugin.plugins()['plugin']
302
339
        if __debug__:
303
340
            plugin_path = self.test_dir + '/plugin.pyc'
304
341
        else:
305
342
            plugin_path = self.test_dir + '/plugin.pyo'
306
 
        self.assertIsSameRealPath(plugin_path, normpath(plugin.path()))
 
343
        self.assertIsSameRealPath(plugin_path, osutils.normpath(p.path()))
307
344
 
308
345
    def test_no_test_suite_gives_None_for_test_suite(self):
309
346
        self.setup_plugin()
310
 
        plugin = bzrlib.plugin.plugins()['plugin']
311
 
        self.assertEqual(None, plugin.test_suite())
 
347
        p = plugin.plugins()['plugin']
 
348
        self.assertEqual(None, p.test_suite())
312
349
 
313
350
    def test_test_suite_gives_test_suite_result(self):
314
351
        source = """def test_suite(): return 'foo'"""
315
352
        self.setup_plugin(source)
316
 
        plugin = bzrlib.plugin.plugins()['plugin']
317
 
        self.assertEqual('foo', plugin.test_suite())
 
353
        p = plugin.plugins()['plugin']
 
354
        self.assertEqual('foo', p.test_suite())
318
355
 
319
356
    def test_no_load_plugin_tests_gives_None_for_load_plugin_tests(self):
320
357
        self.setup_plugin()
321
 
        loader = TestUtil.TestLoader()
322
 
        plugin = bzrlib.plugin.plugins()['plugin']
323
 
        self.assertEqual(None, plugin.load_plugin_tests(loader))
 
358
        loader = tests.TestUtil.TestLoader()
 
359
        p = plugin.plugins()['plugin']
 
360
        self.assertEqual(None, p.load_plugin_tests(loader))
324
361
 
325
362
    def test_load_plugin_tests_gives_load_plugin_tests_result(self):
326
363
        source = """
327
364
def load_tests(standard_tests, module, loader):
328
365
    return 'foo'"""
329
366
        self.setup_plugin(source)
330
 
        loader = TestUtil.TestLoader()
331
 
        plugin = bzrlib.plugin.plugins()['plugin']
332
 
        self.assertEqual('foo', plugin.load_plugin_tests(loader))
 
367
        loader = tests.TestUtil.TestLoader()
 
368
        p = plugin.plugins()['plugin']
 
369
        self.assertEqual('foo', p.load_plugin_tests(loader))
 
370
 
 
371
    def check_version_info(self, expected, source='', name='plugin'):
 
372
        self.setup_plugin(source)
 
373
        self.assertEqual(expected, plugin.plugins()[name].version_info())
333
374
 
334
375
    def test_no_version_info(self):
335
 
        self.setup_plugin()
336
 
        plugin = bzrlib.plugin.plugins()['plugin']
337
 
        self.assertEqual(None, plugin.version_info())
 
376
        self.check_version_info(None)
338
377
 
339
378
    def test_with_version_info(self):
340
 
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 4)")
341
 
        plugin = bzrlib.plugin.plugins()['plugin']
342
 
        self.assertEqual((1, 2, 3, 'dev', 4), plugin.version_info())
 
379
        self.check_version_info((1, 2, 3, 'dev', 4),
 
380
                                "version_info = (1, 2, 3, 'dev', 4)")
343
381
 
344
382
    def test_short_version_info_gets_padded(self):
345
383
        # the gtk plugin has version_info = (1,2,3) rather than the 5-tuple.
346
384
        # so we adapt it
347
 
        self.setup_plugin("version_info = (1, 2, 3)")
348
 
        plugin = bzrlib.plugin.plugins()['plugin']
349
 
        self.assertEqual((1, 2, 3, 'final', 0), plugin.version_info())
 
385
        self.check_version_info((1, 2, 3, 'final', 0),
 
386
                                "version_info = (1, 2, 3)")
 
387
 
 
388
    def check_version(self, expected, source=None, name='plugin'):
 
389
        self.setup_plugin(source)
 
390
        self.assertEqual(expected, plugins[name].__version__)
350
391
 
351
392
    def test_no_version_info___version__(self):
352
393
        self.setup_plugin()
391
432
    def test_dev_fallback__version__with_version_info(self):
392
433
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 4)")
393
434
        plugin = bzrlib.plugin.plugins()['plugin']
394
 
        self.assertEqual("1.2.3.dev.4", plugin.__version__)
 
435
        self.assertEqual("1.2.3dev4", plugin.__version__)
395
436
 
396
437
    def test_final__version__with_version_info(self):
397
438
        self.setup_plugin("version_info = (1, 2, 3, 'final', 0)")
398
439
        plugin = bzrlib.plugin.plugins()['plugin']
399
440
        self.assertEqual("1.2.3", plugin.__version__)
400
441
 
401
 
 
402
 
class TestPluginHelp(TestCaseInTempDir):
 
442
    def test_final_fallback__version__with_version_info(self):
 
443
        self.setup_plugin("version_info = (1, 2, 3, 'final', 2)")
 
444
        plugin = bzrlib.plugin.plugins()['plugin']
 
445
        self.assertEqual("1.2.3.final.2", plugin.__version__)
 
446
 
 
447
 
 
448
class TestPluginHelp(tests.TestCaseInTempDir):
403
449
 
404
450
    def split_help_commands(self):
405
451
        help = {}
434
480
    def test_plugin_help_shows_plugin(self):
435
481
        # Create a test plugin
436
482
        os.mkdir('plugin_test')
437
 
        f = open(pathjoin('plugin_test', 'myplug.py'), 'w')
438
 
        f.write(PLUGIN_TEXT)
 
483
        f = open(osutils.pathjoin('plugin_test', 'myplug.py'), 'w')
 
484
        f.write("""\
 
485
from bzrlib import commands
 
486
class cmd_myplug(commands.Command):
 
487
    __doc__ = '''Just a simple test plugin.'''
 
488
    aliases = ['mplg']
 
489
    def run(self):
 
490
        print 'Hello from my plugin'
 
491
 
 
492
"""
 
493
)
439
494
        f.close()
440
495
 
441
496
        try:
455
510
                delattr(bzrlib.plugins, 'myplug')
456
511
 
457
512
 
458
 
class TestPluginFromZip(TestCaseInTempDir):
459
 
 
460
 
    def make_zipped_plugin(self, zip_name, filename):
461
 
        z = zipfile.ZipFile(zip_name, 'w')
462
 
        z.writestr(filename, PLUGIN_TEXT)
463
 
        z.close()
464
 
 
465
 
    def check_plugin_load(self, zip_name, plugin_name):
466
 
        self.assertFalse(plugin_name in dir(bzrlib.plugins),
467
 
                         'Plugin already loaded')
468
 
        old_path = bzrlib.plugins.__path__
469
 
        try:
470
 
            # this is normally done by load_plugins -> set_plugins_path
471
 
            bzrlib.plugins.__path__ = [zip_name]
472
 
            self.applyDeprecated(one_three,
473
 
                bzrlib.plugin.load_from_zip, zip_name)
474
 
            self.assertTrue(plugin_name in dir(bzrlib.plugins),
475
 
                            'Plugin is not loaded')
476
 
        finally:
477
 
            # unregister plugin
478
 
            if getattr(bzrlib.plugins, plugin_name, None):
479
 
                delattr(bzrlib.plugins, plugin_name)
480
 
                del sys.modules['bzrlib.plugins.' + plugin_name]
481
 
            bzrlib.plugins.__path__ = old_path
482
 
 
483
 
    def test_load_module(self):
484
 
        self.make_zipped_plugin('./test.zip', 'ziplug.py')
485
 
        self.check_plugin_load('./test.zip', 'ziplug')
486
 
 
487
 
    def test_load_package(self):
488
 
        self.make_zipped_plugin('./test.zip', 'ziplug/__init__.py')
489
 
        self.check_plugin_load('./test.zip', 'ziplug')
490
 
 
491
 
 
492
 
class TestSetPluginsPath(TestCase):
493
 
    
494
 
    def test_set_plugins_path(self):
495
 
        """set_plugins_path should set the module __path__ correctly."""
496
 
        old_path = bzrlib.plugins.__path__
497
 
        try:
498
 
            bzrlib.plugins.__path__ = []
499
 
            expected_path = bzrlib.plugin.set_plugins_path()
500
 
            self.assertEqual(expected_path, bzrlib.plugins.__path__)
501
 
        finally:
502
 
            bzrlib.plugins.__path__ = old_path
503
 
 
504
 
    def test_set_plugins_path_with_trailing_slashes(self):
505
 
        """set_plugins_path should set the module __path__ based on
506
 
        BZR_PLUGIN_PATH after removing all trailing slashes."""
507
 
        old_path = bzrlib.plugins.__path__
508
 
        old_env = os.environ.get('BZR_PLUGIN_PATH')
509
 
        try:
510
 
            bzrlib.plugins.__path__ = []
511
 
            os.environ['BZR_PLUGIN_PATH'] = "first\\//\\" + os.pathsep + \
512
 
                "second/\\/\\/"
513
 
            bzrlib.plugin.set_plugins_path()
514
 
            # We expect our nominated paths to have all path-seps removed,
515
 
            # and this is testing only that.
516
 
            expected_path = ['first', 'second']
517
 
            self.assertEqual(expected_path,
518
 
                bzrlib.plugins.__path__[:len(expected_path)])
519
 
        finally:
520
 
            bzrlib.plugins.__path__ = old_path
521
 
            if old_env is not None:
522
 
                os.environ['BZR_PLUGIN_PATH'] = old_env
523
 
            else:
524
 
                del os.environ['BZR_PLUGIN_PATH']
525
 
 
526
 
 
527
513
class TestHelpIndex(tests.TestCase):
528
514
    """Tests for the PluginsHelpIndex class."""
529
515
 
632
618
        self.assertEqual('foo_bar', topic.get_help_topic())
633
619
 
634
620
 
635
 
def clear_plugins(test_case):
636
 
    # Save the attributes that we're about to monkey-patch.
637
 
    old_plugins_path = bzrlib.plugins.__path__
638
 
    old_loaded = plugin._loaded
639
 
    old_load_from_path = plugin.load_from_path
640
 
    # Change bzrlib.plugin to think no plugins have been loaded yet.
641
 
    bzrlib.plugins.__path__ = []
642
 
    plugin._loaded = False
643
 
    # Monkey-patch load_from_path to stop it from actually loading anything.
644
 
    def load_from_path(dirs):
645
 
        pass
646
 
    plugin.load_from_path = load_from_path
647
 
    def restore_plugins():
648
 
        bzrlib.plugins.__path__ = old_plugins_path
649
 
        plugin._loaded = old_loaded
650
 
        plugin.load_from_path = old_load_from_path
651
 
    test_case.addCleanup(restore_plugins)
652
 
 
653
 
 
654
 
class TestPluginPaths(tests.TestCase):
 
621
class TestLoadFromPath(tests.TestCaseInTempDir):
 
622
 
 
623
    def setUp(self):
 
624
        super(TestLoadFromPath, self).setUp()
 
625
        # Change bzrlib.plugin to think no plugins have been loaded yet.
 
626
        self.overrideAttr(bzrlib.plugins, '__path__', [])
 
627
        self.overrideAttr(plugin, '_loaded', False)
 
628
 
 
629
        # Monkey-patch load_from_path to stop it from actually loading anything.
 
630
        self.overrideAttr(plugin, 'load_from_path', lambda dirs: None)
655
631
 
656
632
    def test_set_plugins_path_with_args(self):
657
 
        clear_plugins(self)
658
633
        plugin.set_plugins_path(['a', 'b'])
659
634
        self.assertEqual(['a', 'b'], bzrlib.plugins.__path__)
660
635
 
661
636
    def test_set_plugins_path_defaults(self):
662
 
        clear_plugins(self)
663
637
        plugin.set_plugins_path()
664
638
        self.assertEqual(plugin.get_standard_plugins_path(),
665
639
                         bzrlib.plugins.__path__)
666
640
 
667
641
    def test_get_standard_plugins_path(self):
668
642
        path = plugin.get_standard_plugins_path()
669
 
        self.assertEqual(plugin.get_default_plugin_path(), path[0])
670
643
        for directory in path:
671
 
            self.assertNotContainsRe(r'\\/$', directory)
 
644
            self.assertNotContainsRe(directory, r'\\/$')
672
645
        try:
673
646
            from distutils.sysconfig import get_python_lib
674
647
        except ImportError:
684
657
 
685
658
    def test_get_standard_plugins_path_env(self):
686
659
        os.environ['BZR_PLUGIN_PATH'] = 'foo/'
687
 
        self.assertEqual('foo', plugin.get_standard_plugins_path()[0])
688
 
 
689
 
 
690
 
class TestLoadPlugins(tests.TestCaseInTempDir):
 
660
        path = plugin.get_standard_plugins_path()
 
661
        for directory in path:
 
662
            self.assertNotContainsRe(directory, r'\\/$')
691
663
 
692
664
    def test_load_plugins(self):
693
 
        clear_plugins(self)
694
665
        plugin.load_plugins(['.'])
695
666
        self.assertEqual(bzrlib.plugins.__path__, ['.'])
696
667
        # subsequent loads are no-ops
698
669
        self.assertEqual(bzrlib.plugins.__path__, ['.'])
699
670
 
700
671
    def test_load_plugins_default(self):
701
 
        clear_plugins(self)
702
672
        plugin.load_plugins()
703
673
        path = plugin.get_standard_plugins_path()
704
674
        self.assertEqual(path, bzrlib.plugins.__path__)
 
675
 
 
676
 
 
677
class TestEnvPluginPath(tests.TestCase):
 
678
 
 
679
    def setUp(self):
 
680
        super(TestEnvPluginPath, self).setUp()
 
681
        self.overrideAttr(plugin, 'DEFAULT_PLUGIN_PATH', None)
 
682
 
 
683
        self.user = plugin.get_user_plugin_path()
 
684
        self.site = plugin.get_site_plugin_path()
 
685
        self.core = plugin.get_core_plugin_path()
 
686
 
 
687
    def _list2paths(self, *args):
 
688
        paths = []
 
689
        for p in args:
 
690
            plugin._append_new_path(paths, p)
 
691
        return paths
 
692
 
 
693
    def _set_path(self, *args):
 
694
        path = os.pathsep.join(self._list2paths(*args))
 
695
        osutils.set_or_unset_env('BZR_PLUGIN_PATH', path)
 
696
 
 
697
    def check_path(self, expected_dirs, setting_dirs):
 
698
        if setting_dirs:
 
699
            self._set_path(*setting_dirs)
 
700
        actual = plugin.get_standard_plugins_path()
 
701
        self.assertEquals(self._list2paths(*expected_dirs), actual)
 
702
 
 
703
    def test_default(self):
 
704
        self.check_path([self.user, self.core, self.site],
 
705
                        None)
 
706
 
 
707
    def test_adhoc_policy(self):
 
708
        self.check_path([self.user, self.core, self.site],
 
709
                        ['+user', '+core', '+site'])
 
710
 
 
711
    def test_fallback_policy(self):
 
712
        self.check_path([self.core, self.site, self.user],
 
713
                        ['+core', '+site', '+user'])
 
714
 
 
715
    def test_override_policy(self):
 
716
        self.check_path([self.user, self.site, self.core],
 
717
                        ['+user', '+site', '+core'])
 
718
 
 
719
    def test_disable_user(self):
 
720
        self.check_path([self.core, self.site], ['-user'])
 
721
 
 
722
    def test_disable_user_twice(self):
 
723
        # Ensures multiple removals don't left cruft
 
724
        self.check_path([self.core, self.site], ['-user', '-user'])
 
725
 
 
726
    def test_duplicates_are_removed(self):
 
727
        self.check_path([self.user, self.core, self.site],
 
728
                        ['+user', '+user'])
 
729
        # And only the first reference is kept (since the later references will
 
730
        # only produce '<plugin> already loaded' mutters)
 
731
        self.check_path([self.user, self.core, self.site],
 
732
                        ['+user', '+user', '+core',
 
733
                         '+user', '+site', '+site',
 
734
                         '+core'])
 
735
 
 
736
    def test_disable_overrides_enable(self):
 
737
        self.check_path([self.core, self.site], ['-user', '+user'])
 
738
 
 
739
    def test_disable_core(self):
 
740
        self.check_path([self.site], ['-core'])
 
741
        self.check_path([self.user, self.site], ['+user', '-core'])
 
742
 
 
743
    def test_disable_site(self):
 
744
        self.check_path([self.core], ['-site'])
 
745
        self.check_path([self.user, self.core], ['-site', '+user'])
 
746
 
 
747
    def test_override_site(self):
 
748
        self.check_path(['mysite', self.user, self.core],
 
749
                        ['mysite', '-site', '+user'])
 
750
        self.check_path(['mysite', self.core],
 
751
                        ['mysite', '-site'])
 
752
 
 
753
    def test_override_core(self):
 
754
        self.check_path(['mycore', self.user, self.site],
 
755
                        ['mycore', '-core', '+user', '+site'])
 
756
        self.check_path(['mycore', self.site],
 
757
                        ['mycore', '-core'])
 
758
 
 
759
    def test_my_plugin_only(self):
 
760
        self.check_path(['myplugin'], ['myplugin', '-user', '-core', '-site'])
 
761
 
 
762
    def test_my_plugin_first(self):
 
763
        self.check_path(['myplugin', self.core, self.site, self.user],
 
764
                        ['myplugin', '+core', '+site', '+user'])
 
765
 
 
766
    def test_bogus_references(self):
 
767
        self.check_path(['+foo', '-bar', self.core, self.site],
 
768
                        ['+foo', '-bar'])
 
769
 
 
770
 
 
771
class TestDisablePlugin(tests.TestCaseInTempDir, TestPluginMixin):
 
772
 
 
773
    def setUp(self):
 
774
        super(TestDisablePlugin, self).setUp()
 
775
        self.create_plugin_package('test_foo')
 
776
        # Make sure we don't pollute the plugins namespace
 
777
        self.overrideAttr(plugins, '__path__')
 
778
        # Be paranoid in case a test fail
 
779
        self.addCleanup(self._unregister_plugin, 'test_foo')
 
780
 
 
781
    def test_cannot_import(self):
 
782
        osutils.set_or_unset_env('BZR_DISABLE_PLUGINS', 'test_foo')
 
783
        plugin.set_plugins_path(['.'])
 
784
        try:
 
785
            import bzrlib.plugins.test_foo
 
786
        except ImportError:
 
787
            pass
 
788
        self.assertPluginUnknown('test_foo')
 
789
 
 
790
    def test_regular_load(self):
 
791
        self.overrideAttr(plugin, '_loaded', False)
 
792
        plugin.load_plugins(['.'])
 
793
        self.assertPluginKnown('test_foo')
 
794
        self.assertDocstring("This is the doc for test_foo",
 
795
                             bzrlib.plugins.test_foo)
 
796
 
 
797
    def test_not_loaded(self):
 
798
        self.warnings = []
 
799
        def captured_warning(*args, **kwargs):
 
800
            self.warnings.append((args, kwargs))
 
801
        self.overrideAttr(trace, 'warning', captured_warning)
 
802
        # Reset the flag that protect against double loading
 
803
        self.overrideAttr(plugin, '_loaded', False)
 
804
        osutils.set_or_unset_env('BZR_DISABLE_PLUGINS', 'test_foo')
 
805
        plugin.load_plugins(['.'])
 
806
        self.assertPluginUnknown('test_foo')
 
807
        # Make sure we don't warn about the plugin ImportError since this has
 
808
        # been *requested* by the user.
 
809
        self.assertLength(0, self.warnings)
 
810
 
 
811
 
 
812
class TestLoadPluginAtSyntax(tests.TestCase):
 
813
 
 
814
    def _get_paths(self, paths):
 
815
        return plugin._get_specific_plugin_paths(paths)
 
816
 
 
817
    def test_empty(self):
 
818
        self.assertEquals([], self._get_paths(None))
 
819
        self.assertEquals([], self._get_paths(''))
 
820
 
 
821
    def test_one_path(self):
 
822
        self.assertEquals([('b', 'man')], self._get_paths('b@man'))
 
823
 
 
824
    def test_bogus_path(self):
 
825
        # We need a '@'
 
826
        self.assertRaises(errors.BzrCommandError, self._get_paths, 'batman')
 
827
        # Too much '@' isn't good either
 
828
        self.assertRaises(errors.BzrCommandError, self._get_paths,
 
829
                          'batman@mobile@cave')
 
830
        # An empty description probably indicates a problem
 
831
        self.assertRaises(errors.BzrCommandError, self._get_paths,
 
832
                          os.pathsep.join(['batman@cave', '', 'robin@mobile']))
 
833
 
 
834
 
 
835
class TestLoadPluginAt(tests.TestCaseInTempDir, TestPluginMixin):
 
836
 
 
837
    def setUp(self):
 
838
        super(TestLoadPluginAt, self).setUp()
 
839
        # Make sure we don't pollute the plugins namespace
 
840
        self.overrideAttr(plugins, '__path__')
 
841
        # Reset the flag that protect against double loading
 
842
        self.overrideAttr(plugin, '_loaded', False)
 
843
        # Create the same plugin in two directories
 
844
        self.create_plugin_package('test_foo', dir='non-standard-dir')
 
845
        # The "normal" directory, we use 'standard' instead of 'plugins' to
 
846
        # avoid depending on the precise naming.
 
847
        self.create_plugin_package('test_foo', dir='standard/test_foo')
 
848
        # All the tests will load the 'test_foo' plugin from various locations
 
849
        self.addCleanup(self._unregister_plugin, 'test_foo')
 
850
 
 
851
    def assertTestFooLoadedFrom(self, path):
 
852
        self.assertPluginKnown('test_foo')
 
853
        self.assertDocstring('This is the doc for test_foo',
 
854
                             bzrlib.plugins.test_foo)
 
855
        self.assertEqual(path, bzrlib.plugins.test_foo.dir_source)
 
856
 
 
857
    def test_regular_load(self):
 
858
        plugin.load_plugins(['standard'])
 
859
        self.assertTestFooLoadedFrom('standard/test_foo')
 
860
 
 
861
    def test_import(self):
 
862
        osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
 
863
        plugin.set_plugins_path(['standard'])
 
864
        try:
 
865
            import bzrlib.plugins.test_foo
 
866
        except ImportError:
 
867
            pass
 
868
        self.assertTestFooLoadedFrom('non-standard-dir')
 
869
 
 
870
    def test_loading(self):
 
871
        osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
 
872
        plugin.load_plugins(['standard'])
 
873
        self.assertTestFooLoadedFrom('non-standard-dir')
 
874
 
 
875
    def test_compiled_loaded(self):
 
876
        osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
 
877
        plugin.load_plugins(['standard'])
 
878
        self.assertTestFooLoadedFrom('non-standard-dir')
 
879
        self.assertIsSameRealPath('non-standard-dir/__init__.py',
 
880
                                  bzrlib.plugins.test_foo.__file__)
 
881
 
 
882
        # Try importing again now that the source has been compiled
 
883
        self._unregister_plugin('test_foo')
 
884
        plugin._loaded = False
 
885
        plugin.load_plugins(['standard'])
 
886
        self.assertTestFooLoadedFrom('non-standard-dir')
 
887
        if __debug__:
 
888
            suffix = 'pyc'
 
889
        else:
 
890
            suffix = 'pyo'
 
891
        self.assertIsSameRealPath('non-standard-dir/__init__.%s' % suffix,
 
892
                                  bzrlib.plugins.test_foo.__file__)
 
893
 
 
894
    def test_submodule_loading(self):
 
895
        # We create an additional directory under the one for test_foo
 
896
        self.create_plugin_package('test_bar', dir='non-standard-dir/test_bar')
 
897
        self.addCleanup(self._unregister_plugin_submodule,
 
898
                        'test_foo', 'test_bar')
 
899
        osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
 
900
        plugin.set_plugins_path(['standard'])
 
901
        import bzrlib.plugins.test_foo
 
902
        self.assertEqual('bzrlib.plugins.test_foo',
 
903
                         bzrlib.plugins.test_foo.__package__)
 
904
        import bzrlib.plugins.test_foo.test_bar
 
905
        self.assertIsSameRealPath('non-standard-dir/test_bar/__init__.py',
 
906
                                  bzrlib.plugins.test_foo.test_bar.__file__)
 
907
 
 
908
    def test_relative_submodule_loading(self):
 
909
        self.create_plugin_package('test_foo', dir='another-dir', source='''
 
910
import test_bar
 
911
''')
 
912
        # We create an additional directory under the one for test_foo
 
913
        self.create_plugin_package('test_bar', dir='another-dir/test_bar')
 
914
        self.addCleanup(self._unregister_plugin_submodule,
 
915
                        'test_foo', 'test_bar')
 
916
        osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@another-dir')
 
917
        plugin.set_plugins_path(['standard'])
 
918
        import bzrlib.plugins.test_foo
 
919
        self.assertEqual('bzrlib.plugins.test_foo',
 
920
                         bzrlib.plugins.test_foo.__package__)
 
921
        self.assertIsSameRealPath('another-dir/test_bar/__init__.py',
 
922
                                  bzrlib.plugins.test_foo.test_bar.__file__)
 
923
 
 
924
    def test_loading_from___init__only(self):
 
925
        # We rename the existing __init__.py file to ensure that we don't load
 
926
        # a random file
 
927
        init = 'non-standard-dir/__init__.py'
 
928
        random = 'non-standard-dir/setup.py'
 
929
        os.rename(init, random)
 
930
        self.addCleanup(os.rename, random, init)
 
931
        osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
 
932
        plugin.load_plugins(['standard'])
 
933
        self.assertPluginUnknown('test_foo')
 
934
 
 
935
    def test_loading_from_specific_file(self):
 
936
        plugin_dir = 'non-standard-dir'
 
937
        plugin_file_name = 'iamtestfoo.py'
 
938
        plugin_path = osutils.pathjoin(plugin_dir, plugin_file_name)
 
939
        source = '''\
 
940
"""This is the doc for %s"""
 
941
dir_source = '%s'
 
942
''' % ('test_foo', plugin_path)
 
943
        self.create_plugin('test_foo', source=source,
 
944
                           dir=plugin_dir, file_name=plugin_file_name)
 
945
        osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@%s' % plugin_path)
 
946
        plugin.load_plugins(['standard'])
 
947
        self.assertTestFooLoadedFrom(plugin_path)