~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_plugins.py

  • Committer: Gary van der Merwe
  • Date: 2010-08-02 19:56:52 UTC
  • mfrom: (5050.3.18 2.2)
  • mto: (5050.3.19 2.2)
  • mto: This revision was merged to the branch mainline in revision 5371.
  • Revision ID: garyvdm@gmail.com-20100802195652-o1ppjemhwrr98i61
MergeĀ lp:bzr/2.2.

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
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.tests import (
35
 
    TestCase,
36
 
    TestCaseInTempDir,
37
 
    TestUtil,
 
28
import bzrlib
 
29
from bzrlib import (
 
30
    errors,
 
31
    osutils,
 
32
    plugin,
 
33
    plugins,
 
34
    tests,
 
35
    trace,
38
36
    )
39
 
from bzrlib.osutils import pathjoin, abspath, normpath
40
 
 
41
 
 
42
 
PLUGIN_TEXT = """\
43
 
import bzrlib.commands
44
 
class cmd_myplug(bzrlib.commands.Command):
45
 
    '''Just a simple test plugin.'''
46
 
    aliases = ['mplg']
47
 
    def run(self):
48
 
        print 'Hello from my plugin'
49
 
"""
 
37
 
50
38
 
51
39
# TODO: Write a test for plugin decoration of commands.
52
40
 
53
 
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):
54
103
 
55
104
    activeattributes = {}
56
105
 
64
113
        # set a place for the plugins to record their loading, and at the same
65
114
        # time validate that the location the plugins should record to is
66
115
        # valid and correct.
67
 
        bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
68
 
            [tempattribute] = []
 
116
        self.__class__.activeattributes [tempattribute] = []
69
117
        self.failUnless(tempattribute in self.activeattributes)
70
118
        # create two plugin directories
71
119
        os.mkdir('first')
95
143
        finally:
96
144
            # remove the plugin 'plugin'
97
145
            del self.activeattributes[tempattribute]
98
 
            if 'bzrlib.plugins.plugin' in sys.modules:
99
 
                del sys.modules['bzrlib.plugins.plugin']
100
 
            if getattr(bzrlib.plugins, 'plugin', None):
101
 
                del bzrlib.plugins.plugin
102
 
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
 
146
            self._unregister_plugin('plugin')
 
147
        self.assertPluginUnknown('plugin')
103
148
 
104
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)
105
152
        # This test tests that having two plugins in different
106
153
        # directories with different names allows them both to be loaded, when
107
154
        # we do a direct import statement.
139
186
 
140
187
        oldpath = bzrlib.plugins.__path__
141
188
        try:
 
189
            self.failIf('bzrlib.plugins.pluginone' in sys.modules)
 
190
            self.failIf('bzrlib.plugins.plugintwo' in sys.modules)
142
191
            bzrlib.plugins.__path__ = ['first', 'second']
143
192
            exec "import bzrlib.plugins.pluginone"
144
193
            self.assertEqual(['first'], self.activeattributes[tempattribute])
148
197
        finally:
149
198
            # remove the plugin 'plugin'
150
199
            del self.activeattributes[tempattribute]
151
 
            if getattr(bzrlib.plugins, 'pluginone', None):
152
 
                del bzrlib.plugins.pluginone
153
 
            if getattr(bzrlib.plugins, 'plugintwo', None):
154
 
                del bzrlib.plugins.plugintwo
155
 
        self.failIf(getattr(bzrlib.plugins, 'pluginone', None))
156
 
        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')
157
204
 
158
205
    def test_plugins_can_load_from_directory_with_trailing_slash(self):
159
206
        # This test tests that a plugin can load from a directory when the
160
207
        # directory in the path has a trailing slash.
161
208
        # check the plugin is not loaded already
162
 
        self.failIf(getattr(bzrlib.plugins, 'ts_plugin', None))
 
209
        self.assertPluginUnknown('ts_plugin')
163
210
        tempattribute = "trailing-slash"
164
211
        self.failIf(tempattribute in self.activeattributes)
165
212
        # set a place for the plugin to record its loading, and at the same
186
233
            bzrlib.plugin.load_from_path(['plugin_test'+os.sep])
187
234
            self.assertEqual(['plugin'], self.activeattributes[tempattribute])
188
235
        finally:
189
 
            # remove the plugin 'plugin'
190
236
            del self.activeattributes[tempattribute]
191
 
            if getattr(bzrlib.plugins, 'ts_plugin', None):
192
 
                del bzrlib.plugins.ts_plugin
193
 
        self.failIf(getattr(bzrlib.plugins, 'ts_plugin', None))
 
237
            self._unregister_plugin('ts_plugin')
 
238
        self.assertPluginUnknown('ts_plugin')
194
239
 
195
240
    def load_and_capture(self, name):
196
241
        """Load plugins from '.' capturing the output.
247
292
            "it to 'bad_plugin_name_'\.")
248
293
 
249
294
 
250
 
class TestPlugins(TestCaseInTempDir):
 
295
class TestPlugins(tests.TestCaseInTempDir, TestPluginMixin):
251
296
 
252
297
    def setup_plugin(self, source=""):
253
298
        # This test tests a new plugin appears in bzrlib.plugin.plugins().
254
299
        # check the plugin is not loaded already
255
 
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
 
300
        self.assertPluginUnknown('plugin')
256
301
        # write a plugin that _cannot_ fail to load.
257
302
        file('plugin.py', 'w').write(source + '\n')
258
303
        self.addCleanup(self.teardown_plugin)
259
 
        bzrlib.plugin.load_from_path(['.'])
 
304
        plugin.load_from_path(['.'])
260
305
 
261
306
    def teardown_plugin(self):
262
 
        # remove the plugin 'plugin'
263
 
        if 'bzrlib.plugins.plugin' in sys.modules:
264
 
            del sys.modules['bzrlib.plugins.plugin']
265
 
        if getattr(bzrlib.plugins, 'plugin', None):
266
 
            del bzrlib.plugins.plugin
267
 
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
 
307
        self._unregister_plugin('plugin')
 
308
        self.assertPluginUnknown('plugin')
268
309
 
269
310
    def test_plugin_appears_in_plugins(self):
270
311
        self.setup_plugin()
271
 
        self.failUnless('plugin' in bzrlib.plugin.plugins())
272
 
        self.failUnless(getattr(bzrlib.plugins, 'plugin', None))
273
 
        plugins = bzrlib.plugin.plugins()
274
 
        plugin = plugins['plugin']
275
 
        self.assertIsInstance(plugin, bzrlib.plugin.PlugIn)
276
 
        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)
277
316
 
278
317
    def test_trivial_plugin_get_path(self):
279
318
        self.setup_plugin()
280
 
        plugins = bzrlib.plugin.plugins()
281
 
        plugin = plugins['plugin']
 
319
        p = plugin.plugins()['plugin']
282
320
        plugin_path = self.test_dir + '/plugin.py'
283
 
        self.assertIsSameRealPath(plugin_path, normpath(plugin.path()))
 
321
        self.assertIsSameRealPath(plugin_path, osutils.normpath(p.path()))
284
322
 
285
323
    def test_plugin_get_path_py_not_pyc(self):
286
 
        self.setup_plugin()         # after first import there will be plugin.pyc
 
324
        # first import creates plugin.pyc
 
325
        self.setup_plugin()
287
326
        self.teardown_plugin()
288
 
        bzrlib.plugin.load_from_path(['.']) # import plugin.pyc
289
 
        plugins = bzrlib.plugin.plugins()
290
 
        plugin = plugins['plugin']
 
327
        plugin.load_from_path(['.']) # import plugin.pyc
 
328
        p = plugin.plugins()['plugin']
291
329
        plugin_path = self.test_dir + '/plugin.py'
292
 
        self.assertIsSameRealPath(plugin_path, normpath(plugin.path()))
 
330
        self.assertIsSameRealPath(plugin_path, osutils.normpath(p.path()))
293
331
 
294
332
    def test_plugin_get_path_pyc_only(self):
295
 
        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()
296
335
        self.teardown_plugin()
297
336
        os.unlink(self.test_dir + '/plugin.py')
298
 
        bzrlib.plugin.load_from_path(['.']) # import plugin.pyc
299
 
        plugins = bzrlib.plugin.plugins()
300
 
        plugin = plugins['plugin']
 
337
        plugin.load_from_path(['.']) # import plugin.pyc (or .pyo)
 
338
        p = plugin.plugins()['plugin']
301
339
        if __debug__:
302
340
            plugin_path = self.test_dir + '/plugin.pyc'
303
341
        else:
304
342
            plugin_path = self.test_dir + '/plugin.pyo'
305
 
        self.assertIsSameRealPath(plugin_path, normpath(plugin.path()))
 
343
        self.assertIsSameRealPath(plugin_path, osutils.normpath(p.path()))
306
344
 
307
345
    def test_no_test_suite_gives_None_for_test_suite(self):
308
346
        self.setup_plugin()
309
 
        plugin = bzrlib.plugin.plugins()['plugin']
310
 
        self.assertEqual(None, plugin.test_suite())
 
347
        p = plugin.plugins()['plugin']
 
348
        self.assertEqual(None, p.test_suite())
311
349
 
312
350
    def test_test_suite_gives_test_suite_result(self):
313
351
        source = """def test_suite(): return 'foo'"""
314
352
        self.setup_plugin(source)
315
 
        plugin = bzrlib.plugin.plugins()['plugin']
316
 
        self.assertEqual('foo', plugin.test_suite())
 
353
        p = plugin.plugins()['plugin']
 
354
        self.assertEqual('foo', p.test_suite())
317
355
 
318
356
    def test_no_load_plugin_tests_gives_None_for_load_plugin_tests(self):
319
357
        self.setup_plugin()
320
 
        loader = TestUtil.TestLoader()
321
 
        plugin = bzrlib.plugin.plugins()['plugin']
322
 
        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))
323
361
 
324
362
    def test_load_plugin_tests_gives_load_plugin_tests_result(self):
325
363
        source = """
326
364
def load_tests(standard_tests, module, loader):
327
365
    return 'foo'"""
328
366
        self.setup_plugin(source)
329
 
        loader = TestUtil.TestLoader()
330
 
        plugin = bzrlib.plugin.plugins()['plugin']
331
 
        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())
332
374
 
333
375
    def test_no_version_info(self):
334
 
        self.setup_plugin()
335
 
        plugin = bzrlib.plugin.plugins()['plugin']
336
 
        self.assertEqual(None, plugin.version_info())
 
376
        self.check_version_info(None)
337
377
 
338
378
    def test_with_version_info(self):
339
 
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 4)")
340
 
        plugin = bzrlib.plugin.plugins()['plugin']
341
 
        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)")
342
381
 
343
382
    def test_short_version_info_gets_padded(self):
344
383
        # the gtk plugin has version_info = (1,2,3) rather than the 5-tuple.
345
384
        # so we adapt it
346
 
        self.setup_plugin("version_info = (1, 2, 3)")
347
 
        plugin = bzrlib.plugin.plugins()['plugin']
348
 
        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__)
349
391
 
350
392
    def test_no_version_info___version__(self):
351
393
        self.setup_plugin()
390
432
    def test_dev_fallback__version__with_version_info(self):
391
433
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 4)")
392
434
        plugin = bzrlib.plugin.plugins()['plugin']
393
 
        self.assertEqual("1.2.3.dev.4", plugin.__version__)
 
435
        self.assertEqual("1.2.3dev4", plugin.__version__)
394
436
 
395
437
    def test_final__version__with_version_info(self):
396
438
        self.setup_plugin("version_info = (1, 2, 3, 'final', 0)")
397
439
        plugin = bzrlib.plugin.plugins()['plugin']
398
440
        self.assertEqual("1.2.3", plugin.__version__)
399
441
 
400
 
 
401
 
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):
402
449
 
403
450
    def split_help_commands(self):
404
451
        help = {}
433
480
    def test_plugin_help_shows_plugin(self):
434
481
        # Create a test plugin
435
482
        os.mkdir('plugin_test')
436
 
        f = open(pathjoin('plugin_test', 'myplug.py'), 'w')
437
 
        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
)
438
494
        f.close()
439
495
 
440
496
        try:
454
510
                delattr(bzrlib.plugins, 'myplug')
455
511
 
456
512
 
457
 
class TestSetPluginsPath(TestCase):
458
 
 
459
 
    def test_set_plugins_path(self):
460
 
        """set_plugins_path should set the module __path__ correctly."""
461
 
        old_path = bzrlib.plugins.__path__
462
 
        try:
463
 
            bzrlib.plugins.__path__ = []
464
 
            expected_path = bzrlib.plugin.set_plugins_path()
465
 
            self.assertEqual(expected_path, bzrlib.plugins.__path__)
466
 
        finally:
467
 
            bzrlib.plugins.__path__ = old_path
468
 
 
469
 
    def test_set_plugins_path_with_trailing_slashes(self):
470
 
        """set_plugins_path should set the module __path__ based on
471
 
        BZR_PLUGIN_PATH after removing all trailing slashes."""
472
 
        old_path = bzrlib.plugins.__path__
473
 
        old_env = os.environ.get('BZR_PLUGIN_PATH')
474
 
        try:
475
 
            bzrlib.plugins.__path__ = []
476
 
            os.environ['BZR_PLUGIN_PATH'] = "first\\//\\" + os.pathsep + \
477
 
                "second/\\/\\/"
478
 
            bzrlib.plugin.set_plugins_path()
479
 
            # We expect our nominated paths to have all path-seps removed,
480
 
            # and this is testing only that.
481
 
            expected_path = ['first', 'second']
482
 
            self.assertEqual(expected_path,
483
 
                bzrlib.plugins.__path__[:len(expected_path)])
484
 
        finally:
485
 
            bzrlib.plugins.__path__ = old_path
486
 
            if old_env is not None:
487
 
                os.environ['BZR_PLUGIN_PATH'] = old_env
488
 
            else:
489
 
                del os.environ['BZR_PLUGIN_PATH']
490
 
 
491
 
 
492
513
class TestHelpIndex(tests.TestCase):
493
514
    """Tests for the PluginsHelpIndex class."""
494
515
 
597
618
        self.assertEqual('foo_bar', topic.get_help_topic())
598
619
 
599
620
 
600
 
def clear_plugins(test_case):
601
 
    # Save the attributes that we're about to monkey-patch.
602
 
    old_plugins_path = bzrlib.plugins.__path__
603
 
    old_loaded = plugin._loaded
604
 
    old_load_from_path = plugin.load_from_path
605
 
    # Change bzrlib.plugin to think no plugins have been loaded yet.
606
 
    bzrlib.plugins.__path__ = []
607
 
    plugin._loaded = False
608
 
    # Monkey-patch load_from_path to stop it from actually loading anything.
609
 
    def load_from_path(dirs):
610
 
        pass
611
 
    plugin.load_from_path = load_from_path
612
 
    def restore_plugins():
613
 
        bzrlib.plugins.__path__ = old_plugins_path
614
 
        plugin._loaded = old_loaded
615
 
        plugin.load_from_path = old_load_from_path
616
 
    test_case.addCleanup(restore_plugins)
617
 
 
618
 
 
619
 
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)
620
631
 
621
632
    def test_set_plugins_path_with_args(self):
622
 
        clear_plugins(self)
623
633
        plugin.set_plugins_path(['a', 'b'])
624
634
        self.assertEqual(['a', 'b'], bzrlib.plugins.__path__)
625
635
 
626
636
    def test_set_plugins_path_defaults(self):
627
 
        clear_plugins(self)
628
637
        plugin.set_plugins_path()
629
638
        self.assertEqual(plugin.get_standard_plugins_path(),
630
639
                         bzrlib.plugins.__path__)
631
640
 
632
641
    def test_get_standard_plugins_path(self):
633
642
        path = plugin.get_standard_plugins_path()
634
 
        self.assertEqual(plugin.get_default_plugin_path(), path[0])
635
643
        for directory in path:
636
644
            self.assertNotContainsRe(directory, r'\\/$')
637
645
        try:
649
657
 
650
658
    def test_get_standard_plugins_path_env(self):
651
659
        os.environ['BZR_PLUGIN_PATH'] = 'foo/'
652
 
        self.assertEqual('foo', plugin.get_standard_plugins_path()[0])
653
 
 
654
 
 
655
 
class TestLoadPlugins(tests.TestCaseInTempDir):
 
660
        path = plugin.get_standard_plugins_path()
 
661
        for directory in path:
 
662
            self.assertNotContainsRe(directory, r'\\/$')
656
663
 
657
664
    def test_load_plugins(self):
658
 
        clear_plugins(self)
659
665
        plugin.load_plugins(['.'])
660
666
        self.assertEqual(bzrlib.plugins.__path__, ['.'])
661
667
        # subsequent loads are no-ops
663
669
        self.assertEqual(bzrlib.plugins.__path__, ['.'])
664
670
 
665
671
    def test_load_plugins_default(self):
666
 
        clear_plugins(self)
667
672
        plugin.load_plugins()
668
673
        path = plugin.get_standard_plugins_path()
669
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)