~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_plugins.py

  • Committer: Martin Pool
  • Date: 2010-03-15 06:54:44 UTC
  • mto: This revision was merged to the branch mainline in revision 5095.
  • Revision ID: mbp@canonical.com-20100315065444-gfs7vp8te4ez5rc9
Fix typo in ReadVFile.readline (thanks mnordhoff)

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
from bzrlib import (
30
30
    osutils,
31
31
    plugin,
32
 
    plugins,
33
32
    tests,
34
 
    trace,
35
33
    )
36
34
 
37
35
 
38
36
# TODO: Write a test for plugin decoration of commands.
39
37
 
40
 
class TestPluginMixin(object):
41
 
 
42
 
    def create_plugin(self, name, source=None, dir='.', file_name=None):
43
 
        if source is None:
44
 
            source = '''\
45
 
"""This is the doc for %s"""
46
 
''' % (name)
47
 
        if file_name is None:
48
 
            file_name = name + '.py'
49
 
        # 'source' must not fail to load
50
 
        path = osutils.pathjoin(dir, file_name)
51
 
        f = open(path, 'w')
52
 
        self.addCleanup(os.unlink, path)
53
 
        try:
54
 
            f.write(source + '\n')
55
 
        finally:
56
 
            f.close()
57
 
 
58
 
    def create_plugin_package(self, name, dir=None, source=None):
59
 
        if dir is None:
60
 
            dir = name
61
 
        if source is None:
62
 
            source = '''\
63
 
"""This is the doc for %s"""
64
 
dir_source = '%s'
65
 
''' % (name, dir)
66
 
        os.makedirs(dir)
67
 
        def cleanup():
68
 
            # Workaround lazy import random? madness
69
 
            osutils.rmtree(dir)
70
 
        self.addCleanup(cleanup)
71
 
        self.create_plugin(name, source, dir,
72
 
                           file_name='__init__.py')
73
 
 
74
 
    def _unregister_plugin(self, name):
75
 
        """Remove the plugin from sys.modules and the bzrlib namespace."""
76
 
        py_name = 'bzrlib.plugins.%s' % name
77
 
        if py_name in sys.modules:
78
 
            del sys.modules[py_name]
79
 
        if getattr(bzrlib.plugins, name, None) is not None:
80
 
            delattr(bzrlib.plugins, name)
81
 
 
82
 
    def assertPluginUnknown(self, name):
83
 
        self.failIf(getattr(bzrlib.plugins, name, None) is not None)
84
 
        self.failIf('bzrlib.plugins.%s' % name in sys.modules)
85
 
 
86
 
    def assertPluginKnown(self, name):
87
 
        self.failUnless(getattr(bzrlib.plugins, name, None) is not None)
88
 
        self.failUnless('bzrlib.plugins.%s' % name in sys.modules)
89
 
 
90
 
 
91
 
class TestLoadingPlugins(tests.TestCaseInTempDir, TestPluginMixin):
 
38
class TestLoadingPlugins(tests.TestCaseInTempDir):
92
39
 
93
40
    activeattributes = {}
94
41
 
102
49
        # set a place for the plugins to record their loading, and at the same
103
50
        # time validate that the location the plugins should record to is
104
51
        # valid and correct.
105
 
        self.__class__.activeattributes [tempattribute] = []
 
52
        bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
 
53
            [tempattribute] = []
106
54
        self.failUnless(tempattribute in self.activeattributes)
107
55
        # create two plugin directories
108
56
        os.mkdir('first')
132
80
        finally:
133
81
            # remove the plugin 'plugin'
134
82
            del self.activeattributes[tempattribute]
135
 
            self._unregister_plugin('plugin')
136
 
        self.assertPluginUnknown('plugin')
 
83
            if 'bzrlib.plugins.plugin' in sys.modules:
 
84
                del sys.modules['bzrlib.plugins.plugin']
 
85
            if getattr(bzrlib.plugins, 'plugin', None):
 
86
                del bzrlib.plugins.plugin
 
87
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
137
88
 
138
89
    def test_plugins_from_different_dirs_can_demand_load(self):
139
 
        self.failIf('bzrlib.plugins.pluginone' in sys.modules)
140
 
        self.failIf('bzrlib.plugins.plugintwo' in sys.modules)
141
90
        # This test tests that having two plugins in different
142
91
        # directories with different names allows them both to be loaded, when
143
92
        # we do a direct import statement.
175
124
 
176
125
        oldpath = bzrlib.plugins.__path__
177
126
        try:
178
 
            self.failIf('bzrlib.plugins.pluginone' in sys.modules)
179
 
            self.failIf('bzrlib.plugins.plugintwo' in sys.modules)
180
127
            bzrlib.plugins.__path__ = ['first', 'second']
181
128
            exec "import bzrlib.plugins.pluginone"
182
129
            self.assertEqual(['first'], self.activeattributes[tempattribute])
186
133
        finally:
187
134
            # remove the plugin 'plugin'
188
135
            del self.activeattributes[tempattribute]
189
 
            self._unregister_plugin('pluginone')
190
 
            self._unregister_plugin('plugintwo')
191
 
        self.assertPluginUnknown('pluginone')
192
 
        self.assertPluginUnknown('plugintwo')
 
136
            if getattr(bzrlib.plugins, 'pluginone', None):
 
137
                del bzrlib.plugins.pluginone
 
138
            if getattr(bzrlib.plugins, 'plugintwo', None):
 
139
                del bzrlib.plugins.plugintwo
 
140
        self.failIf(getattr(bzrlib.plugins, 'pluginone', None))
 
141
        self.failIf(getattr(bzrlib.plugins, 'plugintwo', None))
193
142
 
194
143
    def test_plugins_can_load_from_directory_with_trailing_slash(self):
195
144
        # This test tests that a plugin can load from a directory when the
196
145
        # directory in the path has a trailing slash.
197
146
        # check the plugin is not loaded already
198
 
        self.assertPluginUnknown('ts_plugin')
 
147
        self.failIf(getattr(bzrlib.plugins, 'ts_plugin', None))
199
148
        tempattribute = "trailing-slash"
200
149
        self.failIf(tempattribute in self.activeattributes)
201
150
        # set a place for the plugin to record its loading, and at the same
222
171
            bzrlib.plugin.load_from_path(['plugin_test'+os.sep])
223
172
            self.assertEqual(['plugin'], self.activeattributes[tempattribute])
224
173
        finally:
 
174
            # remove the plugin 'plugin'
225
175
            del self.activeattributes[tempattribute]
226
 
            self._unregister_plugin('ts_plugin')
227
 
        self.assertPluginUnknown('ts_plugin')
 
176
            if getattr(bzrlib.plugins, 'ts_plugin', None):
 
177
                del bzrlib.plugins.ts_plugin
 
178
        self.failIf(getattr(bzrlib.plugins, 'ts_plugin', None))
228
179
 
229
180
    def load_and_capture(self, name):
230
181
        """Load plugins from '.' capturing the output.
281
232
            "it to 'bad_plugin_name_'\.")
282
233
 
283
234
 
284
 
class TestPlugins(tests.TestCaseInTempDir, TestPluginMixin):
 
235
class TestPlugins(tests.TestCaseInTempDir):
285
236
 
286
237
    def setup_plugin(self, source=""):
287
238
        # This test tests a new plugin appears in bzrlib.plugin.plugins().
288
239
        # check the plugin is not loaded already
289
 
        self.assertPluginUnknown('plugin')
 
240
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
290
241
        # write a plugin that _cannot_ fail to load.
291
242
        file('plugin.py', 'w').write(source + '\n')
292
243
        self.addCleanup(self.teardown_plugin)
293
 
        plugin.load_from_path(['.'])
 
244
        bzrlib.plugin.load_from_path(['.'])
294
245
 
295
246
    def teardown_plugin(self):
296
 
        self._unregister_plugin('plugin')
297
 
        self.assertPluginUnknown('plugin')
 
247
        # remove the plugin 'plugin'
 
248
        if 'bzrlib.plugins.plugin' in sys.modules:
 
249
            del sys.modules['bzrlib.plugins.plugin']
 
250
        if getattr(bzrlib.plugins, 'plugin', None):
 
251
            del bzrlib.plugins.plugin
 
252
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
298
253
 
299
254
    def test_plugin_appears_in_plugins(self):
300
255
        self.setup_plugin()
301
 
        self.assertPluginKnown('plugin')
302
 
        p = plugin.plugins()['plugin']
303
 
        self.assertIsInstance(p, bzrlib.plugin.PlugIn)
304
 
        self.assertEqual(p.module, plugins.plugin)
 
256
        self.failUnless('plugin' in bzrlib.plugin.plugins())
 
257
        self.failUnless(getattr(bzrlib.plugins, 'plugin', None))
 
258
        plugins = bzrlib.plugin.plugins()
 
259
        plugin = plugins['plugin']
 
260
        self.assertIsInstance(plugin, bzrlib.plugin.PlugIn)
 
261
        self.assertEqual(bzrlib.plugins.plugin, plugin.module)
305
262
 
306
263
    def test_trivial_plugin_get_path(self):
307
264
        self.setup_plugin()
308
 
        p = plugin.plugins()['plugin']
 
265
        plugins = bzrlib.plugin.plugins()
 
266
        plugin = plugins['plugin']
309
267
        plugin_path = self.test_dir + '/plugin.py'
310
 
        self.assertIsSameRealPath(plugin_path, osutils.normpath(p.path()))
 
268
        self.assertIsSameRealPath(plugin_path, osutils.normpath(plugin.path()))
311
269
 
312
270
    def test_plugin_get_path_py_not_pyc(self):
313
271
        # first import creates plugin.pyc
314
272
        self.setup_plugin()
315
273
        self.teardown_plugin()
316
 
        plugin.load_from_path(['.']) # import plugin.pyc
317
 
        p = plugin.plugins()['plugin']
 
274
        bzrlib.plugin.load_from_path(['.']) # import plugin.pyc
 
275
        plugins = bzrlib.plugin.plugins()
 
276
        plugin = plugins['plugin']
318
277
        plugin_path = self.test_dir + '/plugin.py'
319
 
        self.assertIsSameRealPath(plugin_path, osutils.normpath(p.path()))
 
278
        self.assertIsSameRealPath(plugin_path, osutils.normpath(plugin.path()))
320
279
 
321
280
    def test_plugin_get_path_pyc_only(self):
322
281
        # first import creates plugin.pyc (or plugin.pyo depending on __debug__)
323
282
        self.setup_plugin()
324
283
        self.teardown_plugin()
325
284
        os.unlink(self.test_dir + '/plugin.py')
326
 
        plugin.load_from_path(['.']) # import plugin.pyc (or .pyo)
327
 
        p = plugin.plugins()['plugin']
 
285
        bzrlib.plugin.load_from_path(['.']) # import plugin.pyc (or .pyo)
 
286
        plugins = bzrlib.plugin.plugins()
 
287
        plugin = plugins['plugin']
328
288
        if __debug__:
329
289
            plugin_path = self.test_dir + '/plugin.pyc'
330
290
        else:
331
291
            plugin_path = self.test_dir + '/plugin.pyo'
332
 
        self.assertIsSameRealPath(plugin_path, osutils.normpath(p.path()))
 
292
        self.assertIsSameRealPath(plugin_path, osutils.normpath(plugin.path()))
333
293
 
334
294
    def test_no_test_suite_gives_None_for_test_suite(self):
335
295
        self.setup_plugin()
336
 
        p = plugin.plugins()['plugin']
337
 
        self.assertEqual(None, p.test_suite())
 
296
        plugin = bzrlib.plugin.plugins()['plugin']
 
297
        self.assertEqual(None, plugin.test_suite())
338
298
 
339
299
    def test_test_suite_gives_test_suite_result(self):
340
300
        source = """def test_suite(): return 'foo'"""
341
301
        self.setup_plugin(source)
342
 
        p = plugin.plugins()['plugin']
343
 
        self.assertEqual('foo', p.test_suite())
 
302
        plugin = bzrlib.plugin.plugins()['plugin']
 
303
        self.assertEqual('foo', plugin.test_suite())
344
304
 
345
305
    def test_no_load_plugin_tests_gives_None_for_load_plugin_tests(self):
346
306
        self.setup_plugin()
347
307
        loader = tests.TestUtil.TestLoader()
348
 
        p = plugin.plugins()['plugin']
349
 
        self.assertEqual(None, p.load_plugin_tests(loader))
 
308
        plugin = bzrlib.plugin.plugins()['plugin']
 
309
        self.assertEqual(None, plugin.load_plugin_tests(loader))
350
310
 
351
311
    def test_load_plugin_tests_gives_load_plugin_tests_result(self):
352
312
        source = """
354
314
    return 'foo'"""
355
315
        self.setup_plugin(source)
356
316
        loader = tests.TestUtil.TestLoader()
357
 
        p = plugin.plugins()['plugin']
358
 
        self.assertEqual('foo', p.load_plugin_tests(loader))
359
 
 
360
 
    def check_version_info(self, expected, source='', name='plugin'):
361
 
        self.setup_plugin(source)
362
 
        self.assertEqual(expected, plugin.plugins()[name].version_info())
 
317
        plugin = bzrlib.plugin.plugins()['plugin']
 
318
        self.assertEqual('foo', plugin.load_plugin_tests(loader))
363
319
 
364
320
    def test_no_version_info(self):
365
 
        self.check_version_info(None)
 
321
        self.setup_plugin()
 
322
        plugin = bzrlib.plugin.plugins()['plugin']
 
323
        self.assertEqual(None, plugin.version_info())
366
324
 
367
325
    def test_with_version_info(self):
368
 
        self.check_version_info((1, 2, 3, 'dev', 4),
369
 
                                "version_info = (1, 2, 3, 'dev', 4)")
 
326
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 4)")
 
327
        plugin = bzrlib.plugin.plugins()['plugin']
 
328
        self.assertEqual((1, 2, 3, 'dev', 4), plugin.version_info())
370
329
 
371
330
    def test_short_version_info_gets_padded(self):
372
331
        # the gtk plugin has version_info = (1,2,3) rather than the 5-tuple.
373
332
        # so we adapt it
374
 
        self.check_version_info((1, 2, 3, 'final', 0),
375
 
                                "version_info = (1, 2, 3)")
376
 
 
377
 
    def check_version(self, expected, source=None, name='plugin'):
378
 
        self.setup_plugin(source)
379
 
        self.assertEqual(expected, plugins[name].__version__)
 
333
        self.setup_plugin("version_info = (1, 2, 3)")
 
334
        plugin = bzrlib.plugin.plugins()['plugin']
 
335
        self.assertEqual((1, 2, 3, 'final', 0), plugin.version_info())
380
336
 
381
337
    def test_no_version_info___version__(self):
382
338
        self.setup_plugin()
473
429
        f.write("""\
474
430
from bzrlib import commands
475
431
class cmd_myplug(commands.Command):
476
 
    __doc__ = '''Just a simple test plugin.'''
 
432
    '''Just a simple test plugin.'''
477
433
    aliases = ['mplg']
478
434
    def run(self):
479
435
        print 'Hello from my plugin'
722
678
                         '+user', '+site', '+site',
723
679
                         '+core'])
724
680
 
725
 
    def test_disable_overrides_enable(self):
 
681
    def test_disable_overrides_disable(self):
726
682
        self.check_path([self.core, self.site], ['-user', '+user'])
727
683
 
728
684
    def test_disable_core(self):
755
711
    def test_bogus_references(self):
756
712
        self.check_path(['+foo', '-bar', self.core, self.site],
757
713
                        ['+foo', '-bar'])
758
 
 
759
 
 
760
 
class TestDisablePlugin(tests.TestCaseInTempDir, TestPluginMixin):
761
 
 
762
 
    def setUp(self):
763
 
        super(TestDisablePlugin, self).setUp()
764
 
        self.create_plugin_package('test_foo')
765
 
        # Make sure we don't pollute the plugins namespace
766
 
        self.overrideAttr(plugins, '__path__')
767
 
        # Be paranoid in case a test fail
768
 
        self.addCleanup(self._unregister_plugin, 'test_foo')
769
 
 
770
 
    def test_cannot_import(self):
771
 
        osutils.set_or_unset_env('BZR_DISABLE_PLUGINS', 'test_foo')
772
 
        plugin.set_plugins_path(['.'])
773
 
        try:
774
 
            import bzrlib.plugins.test_foo
775
 
        except ImportError:
776
 
            pass
777
 
        self.assertPluginUnknown('test_foo')
778
 
 
779
 
    def test_regular_load(self):
780
 
        self.overrideAttr(plugin, '_loaded', False)
781
 
        plugin.load_plugins(['.'])
782
 
        self.assertPluginKnown('test_foo')
783
 
        self.assertDocstring("This is the doc for test_foo",
784
 
                             bzrlib.plugins.test_foo)
785
 
 
786
 
    def test_not_loaded(self):
787
 
        self.warnings = []
788
 
        def captured_warning(*args, **kwargs):
789
 
            self.warnings.append((args, kwargs))
790
 
        self.overrideAttr(trace, 'warning', captured_warning)
791
 
        # Reset the flag that protect against double loading
792
 
        self.overrideAttr(plugin, '_loaded', False)
793
 
        osutils.set_or_unset_env('BZR_DISABLE_PLUGINS', 'test_foo')
794
 
        plugin.load_plugins(['.'])
795
 
        self.assertPluginUnknown('test_foo')
796
 
        # Make sure we don't warn about the plugin ImportError since this has
797
 
        # been *requested* by the user.
798
 
        self.assertLength(0, self.warnings)
799
 
 
800
 
 
801
 
class TestLoadPluginAt(tests.TestCaseInTempDir, TestPluginMixin):
802
 
 
803
 
    def setUp(self):
804
 
        super(TestLoadPluginAt, self).setUp()
805
 
        # Make sure we don't pollute the plugins namespace
806
 
        self.overrideAttr(plugins, '__path__')
807
 
        # Be paranoid in case a test fail
808
 
        self.addCleanup(self._unregister_plugin, 'test_foo')
809
 
        # Reset the flag that protect against double loading
810
 
        self.overrideAttr(plugin, '_loaded', False)
811
 
        # Create the same plugin in two directories
812
 
        self.create_plugin_package('test_foo', dir='non-standard-dir')
813
 
        # The "normal" directory, we use 'standard' instead of 'plugins' to
814
 
        # avoid depending on the precise naming.
815
 
        self.create_plugin_package('test_foo', dir='standard/test_foo')
816
 
 
817
 
    def assertTestFooLoadedFrom(self, path):
818
 
        self.assertPluginKnown('test_foo')
819
 
        self.assertDocstring('This is the doc for test_foo',
820
 
                             bzrlib.plugins.test_foo)
821
 
        self.assertEqual(path, bzrlib.plugins.test_foo.dir_source)
822
 
 
823
 
    def test_regular_load(self):
824
 
        plugin.load_plugins(['standard'])
825
 
        self.assertTestFooLoadedFrom('standard/test_foo')
826
 
 
827
 
    def test_import(self):
828
 
        osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
829
 
        plugin.set_plugins_path(['standard'])
830
 
        try:
831
 
            import bzrlib.plugins.test_foo
832
 
        except ImportError:
833
 
            pass
834
 
        self.assertTestFooLoadedFrom('non-standard-dir')
835
 
 
836
 
    def test_loading(self):
837
 
        osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
838
 
        plugin.load_plugins(['standard'])
839
 
        self.assertTestFooLoadedFrom('non-standard-dir')
840
 
 
841
 
    def test_compiled_loaded(self):
842
 
        osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
843
 
        plugin.load_plugins(['standard'])
844
 
        self.assertTestFooLoadedFrom('non-standard-dir')
845
 
        self.assertEqual('non-standard-dir/__init__.py',
846
 
                         bzrlib.plugins.test_foo.__file__)
847
 
 
848
 
        # Try importing again now that the source has been compiled
849
 
        self._unregister_plugin('test_foo')
850
 
        plugin._loaded = False
851
 
        plugin.load_plugins(['standard'])
852
 
        self.assertTestFooLoadedFrom('non-standard-dir')
853
 
        if __debug__:
854
 
            suffix = 'pyc'
855
 
        else:
856
 
            suffix = 'pyo'
857
 
        self.assertEqual('non-standard-dir/__init__.%s' % suffix,
858
 
                         bzrlib.plugins.test_foo.__file__)
859
 
 
860
 
    def test_submodule_loading(self):
861
 
        # We create an additional directory under the one for test_foo
862
 
        self.create_plugin_package('test_bar', dir='non-standard-dir/test_bar')
863
 
        osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
864
 
        plugin.set_plugins_path(['standard'])
865
 
        import bzrlib.plugins.test_foo
866
 
        self.assertEqual('bzrlib.plugins.test_foo',
867
 
                         bzrlib.plugins.test_foo.__package__)
868
 
        import bzrlib.plugins.test_foo.test_bar
869
 
        self.assertEqual('non-standard-dir/test_bar/__init__.py',
870
 
                         bzrlib.plugins.test_foo.test_bar.__file__)
871
 
 
872
 
    def test_loading_from___init__only(self):
873
 
        # We rename the existing __init__.py file to ensure that we don't load
874
 
        # a random file
875
 
        init = 'non-standard-dir/__init__.py'
876
 
        random = 'non-standard-dir/setup.py'
877
 
        os.rename(init, random)
878
 
        self.addCleanup(os.rename, random, init)
879
 
        osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
880
 
        plugin.load_plugins(['standard'])
881
 
        self.assertPluginUnknown('test_foo')
882
 
 
883
 
    def test_loading_from_specific_file(self):
884
 
        plugin_dir = 'non-standard-dir'
885
 
        plugin_file_name = 'iamtestfoo.py'
886
 
        plugin_path = osutils.pathjoin(plugin_dir, plugin_file_name)
887
 
        source = '''\
888
 
"""This is the doc for %s"""
889
 
dir_source = '%s'
890
 
''' % ('test_foo', plugin_path)
891
 
        self.create_plugin('test_foo', source=source,
892
 
                           dir=plugin_dir, file_name=plugin_file_name)
893
 
        osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@%s' % plugin_path)
894
 
        plugin.load_plugins(['standard'])
895
 
        self.assertTestFooLoadedFrom(plugin_path)