~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_plugins.py

Merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 Canonical Ltd
 
1
# Copyright (C) 2005, 2007 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
import logging
23
24
import os
24
25
from StringIO import StringIO
25
26
import sys
30
31
import bzrlib.plugins
31
32
import bzrlib.commands
32
33
import bzrlib.help
 
34
from bzrlib.symbol_versioning import one_three
33
35
from bzrlib.tests import TestCase, TestCaseInTempDir
34
 
from bzrlib.osutils import pathjoin, abspath
 
36
from bzrlib.osutils import pathjoin, abspath, normpath
35
37
 
36
38
 
37
39
PLUGIN_TEXT = """\
69
71
        # tempattribute list.
70
72
        template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
71
73
                    "TestLoadingPlugins.activeattributes[%r].append('%s')\n")
72
 
        print >> file(os.path.join('first', 'plugin.py'), 'w'), template % (tempattribute, 'first')
73
 
        print >> file(os.path.join('second', 'plugin.py'), 'w'), template % (tempattribute, 'second')
 
74
 
 
75
        outfile = open(os.path.join('first', 'plugin.py'), 'w')
 
76
        try:
 
77
            outfile.write(template % (tempattribute, 'first'))
 
78
            outfile.write('\n')
 
79
        finally:
 
80
            outfile.close()
 
81
 
 
82
        outfile = open(os.path.join('second', 'plugin.py'), 'w')
 
83
        try:
 
84
            outfile.write(template % (tempattribute, 'second'))
 
85
            outfile.write('\n')
 
86
        finally:
 
87
            outfile.close()
 
88
 
74
89
        try:
75
90
            bzrlib.plugin.load_from_path(['first', 'second'])
76
91
            self.assertEqual(['first'], self.activeattributes[tempattribute])
77
92
        finally:
78
93
            # remove the plugin 'plugin'
79
94
            del self.activeattributes[tempattribute]
 
95
            if 'bzrlib.plugins.plugin' in sys.modules:
 
96
                del sys.modules['bzrlib.plugins.plugin']
80
97
            if getattr(bzrlib.plugins, 'plugin', None):
81
98
                del bzrlib.plugins.plugin
82
99
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
102
119
        # tempattribute list.
103
120
        template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
104
121
                    "TestLoadingPlugins.activeattributes[%r].append('%s')\n")
105
 
        print >> file(os.path.join('first', 'pluginone.py'), 'w'), template % (tempattribute, 'first')
106
 
        print >> file(os.path.join('second', 'plugintwo.py'), 'w'), template % (tempattribute, 'second')
 
122
 
 
123
        outfile = open(os.path.join('first', 'pluginone.py'), 'w')
 
124
        try:
 
125
            outfile.write(template % (tempattribute, 'first'))
 
126
            outfile.write('\n')
 
127
        finally:
 
128
            outfile.close()
 
129
 
 
130
        outfile = open(os.path.join('second', 'plugintwo.py'), 'w')
 
131
        try:
 
132
            outfile.write(template % (tempattribute, 'second'))
 
133
            outfile.write('\n')
 
134
        finally:
 
135
            outfile.close()
 
136
 
107
137
        oldpath = bzrlib.plugins.__path__
108
138
        try:
109
139
            bzrlib.plugins.__path__ = ['first', 'second']
115
145
        finally:
116
146
            # remove the plugin 'plugin'
117
147
            del self.activeattributes[tempattribute]
118
 
            if getattr(bzrlib.plugins, 'plugin', None):
119
 
                del bzrlib.plugins.plugin
120
 
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
121
 
 
122
 
 
123
 
class TestAllPlugins(TestCaseInTempDir):
124
 
 
125
 
    def test_plugin_appears_in_all_plugins(self):
126
 
        # This test tests a new plugin appears in bzrlib.plugin.all_plugins().
 
148
            if getattr(bzrlib.plugins, 'pluginone', None):
 
149
                del bzrlib.plugins.pluginone
 
150
            if getattr(bzrlib.plugins, 'plugintwo', None):
 
151
                del bzrlib.plugins.plugintwo
 
152
        self.failIf(getattr(bzrlib.plugins, 'pluginone', None))
 
153
        self.failIf(getattr(bzrlib.plugins, 'plugintwo', None))
 
154
 
 
155
    def test_plugins_can_load_from_directory_with_trailing_slash(self):
 
156
        # This test tests that a plugin can load from a directory when the
 
157
        # directory in the path has a trailing slash.
 
158
        # check the plugin is not loaded already
 
159
        self.failIf(getattr(bzrlib.plugins, 'ts_plugin', None))
 
160
        tempattribute = "trailing-slash"
 
161
        self.failIf(tempattribute in self.activeattributes)
 
162
        # set a place for the plugin to record its loading, and at the same
 
163
        # time validate that the location the plugin should record to is
 
164
        # valid and correct.
 
165
        bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
 
166
            [tempattribute] = []
 
167
        self.failUnless(tempattribute in self.activeattributes)
 
168
        # create a directory for the plugin
 
169
        os.mkdir('plugin_test')
 
170
        # write a plugin that will record when its loaded in the 
 
171
        # tempattribute list.
 
172
        template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
 
173
                    "TestLoadingPlugins.activeattributes[%r].append('%s')\n")
 
174
 
 
175
        outfile = open(os.path.join('plugin_test', 'ts_plugin.py'), 'w')
 
176
        try:
 
177
            outfile.write(template % (tempattribute, 'plugin'))
 
178
            outfile.write('\n')
 
179
        finally:
 
180
            outfile.close()
 
181
 
 
182
        try:
 
183
            bzrlib.plugin.load_from_path(['plugin_test'+os.sep])
 
184
            self.assertEqual(['plugin'], self.activeattributes[tempattribute])
 
185
        finally:
 
186
            # remove the plugin 'plugin'
 
187
            del self.activeattributes[tempattribute]
 
188
            if getattr(bzrlib.plugins, 'ts_plugin', None):
 
189
                del bzrlib.plugins.ts_plugin
 
190
        self.failIf(getattr(bzrlib.plugins, 'ts_plugin', None))
 
191
 
 
192
    def test_plugin_with_bad_name_does_not_load(self):
 
193
        # Create badly-named plugin
 
194
        file('bad plugin-name..py', 'w').close()
 
195
 
 
196
        # Capture output
 
197
        stream = StringIO()
 
198
        handler = logging.StreamHandler(stream)
 
199
        log = logging.getLogger('bzr')
 
200
        log.addHandler(handler)
 
201
 
 
202
        bzrlib.plugin.load_from_dir('.')
 
203
 
 
204
        # Stop capturing output
 
205
        handler.flush()
 
206
        handler.close()
 
207
        log.removeHandler(handler)
 
208
 
 
209
        self.assertContainsRe(stream.getvalue(),
 
210
            r"Unable to load 'bad plugin-name\.' in '\.' as a plugin because"
 
211
            " file path isn't a valid module name; try renaming it to"
 
212
            " 'bad_plugin_name_'\.")
 
213
 
 
214
        stream.close()
 
215
 
 
216
 
 
217
class TestPlugins(TestCaseInTempDir):
 
218
 
 
219
    def setup_plugin(self, source=""):
 
220
        # This test tests a new plugin appears in bzrlib.plugin.plugins().
127
221
        # check the plugin is not loaded already
128
222
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
129
223
        # write a plugin that _cannot_ fail to load.
130
 
        print >> file('plugin.py', 'w'), ""
131
 
        try:
132
 
            bzrlib.plugin.load_from_path(['.'])
133
 
            self.failUnless('plugin' in bzrlib.plugin.all_plugins())
134
 
            self.failUnless(getattr(bzrlib.plugins, 'plugin', None))
135
 
            self.assertEqual(bzrlib.plugin.all_plugins()['plugin'],
136
 
                             bzrlib.plugins.plugin)
137
 
        finally:
138
 
            # remove the plugin 'plugin'
139
 
            if 'bzrlib.plugins.plugin' in sys.modules:
140
 
                del sys.modules['bzrlib.plugins.plugin']
141
 
            if getattr(bzrlib.plugins, 'plugin', None):
142
 
                del bzrlib.plugins.plugin
 
224
        file('plugin.py', 'w').write(source + '\n')
 
225
        self.addCleanup(self.teardown_plugin)
 
226
        bzrlib.plugin.load_from_path(['.'])
 
227
    
 
228
    def teardown_plugin(self):
 
229
        # remove the plugin 'plugin'
 
230
        if 'bzrlib.plugins.plugin' in sys.modules:
 
231
            del sys.modules['bzrlib.plugins.plugin']
 
232
        if getattr(bzrlib.plugins, 'plugin', None):
 
233
            del bzrlib.plugins.plugin
143
234
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
144
235
 
 
236
    def test_plugin_appears_in_plugins(self):
 
237
        self.setup_plugin()
 
238
        self.failUnless('plugin' in bzrlib.plugin.plugins())
 
239
        self.failUnless(getattr(bzrlib.plugins, 'plugin', None))
 
240
        plugins = bzrlib.plugin.plugins()
 
241
        plugin = plugins['plugin']
 
242
        self.assertIsInstance(plugin, bzrlib.plugin.PlugIn)
 
243
        self.assertEqual(bzrlib.plugins.plugin, plugin.module)
 
244
 
 
245
    def test_trivial_plugin_get_path(self):
 
246
        self.setup_plugin()
 
247
        plugins = bzrlib.plugin.plugins()
 
248
        plugin = plugins['plugin']
 
249
        plugin_path = self.test_dir + '/plugin.py'
 
250
        self.assertIsSameRealPath(plugin_path, normpath(plugin.path()))
 
251
 
 
252
    def test_plugin_get_path_py_not_pyc(self):
 
253
        self.setup_plugin()         # after first import there will be plugin.pyc
 
254
        self.teardown_plugin()
 
255
        bzrlib.plugin.load_from_path(['.']) # import plugin.pyc
 
256
        plugins = bzrlib.plugin.plugins()
 
257
        plugin = plugins['plugin']
 
258
        plugin_path = self.test_dir + '/plugin.py'
 
259
        self.assertIsSameRealPath(plugin_path, normpath(plugin.path()))
 
260
 
 
261
    def test_plugin_get_path_pyc_only(self):
 
262
        self.setup_plugin()         # after first import there will be plugin.pyc
 
263
        self.teardown_plugin()
 
264
        os.unlink(self.test_dir + '/plugin.py')
 
265
        bzrlib.plugin.load_from_path(['.']) # import plugin.pyc
 
266
        plugins = bzrlib.plugin.plugins()
 
267
        plugin = plugins['plugin']
 
268
        if __debug__:
 
269
            plugin_path = self.test_dir + '/plugin.pyc'
 
270
        else:
 
271
            plugin_path = self.test_dir + '/plugin.pyo'
 
272
        self.assertIsSameRealPath(plugin_path, normpath(plugin.path()))
 
273
 
 
274
    def test_no_test_suite_gives_None_for_test_suite(self):
 
275
        self.setup_plugin()
 
276
        plugin = bzrlib.plugin.plugins()['plugin']
 
277
        self.assertEqual(None, plugin.test_suite())
 
278
 
 
279
    def test_test_suite_gives_test_suite_result(self):
 
280
        source = """def test_suite(): return 'foo'"""
 
281
        self.setup_plugin(source)
 
282
        plugin = bzrlib.plugin.plugins()['plugin']
 
283
        self.assertEqual('foo', plugin.test_suite())
 
284
 
 
285
    def test_no_version_info(self):
 
286
        self.setup_plugin()
 
287
        plugin = bzrlib.plugin.plugins()['plugin']
 
288
        self.assertEqual(None, plugin.version_info())
 
289
 
 
290
    def test_with_version_info(self):
 
291
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 4)")
 
292
        plugin = bzrlib.plugin.plugins()['plugin']
 
293
        self.assertEqual((1, 2, 3, 'dev', 4), plugin.version_info())
 
294
 
 
295
    def test_short_version_info_gets_padded(self):
 
296
        # the gtk plugin has version_info = (1,2,3) rather than the 5-tuple.
 
297
        # so we adapt it
 
298
        self.setup_plugin("version_info = (1, 2, 3)")
 
299
        plugin = bzrlib.plugin.plugins()['plugin']
 
300
        self.assertEqual((1, 2, 3, 'final', 0), plugin.version_info())
 
301
 
 
302
    def test_no_version_info___version__(self):
 
303
        self.setup_plugin()
 
304
        plugin = bzrlib.plugin.plugins()['plugin']
 
305
        self.assertEqual("unknown", plugin.__version__)
 
306
 
 
307
    def test___version__with_version_info(self):
 
308
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 4)")
 
309
        plugin = bzrlib.plugin.plugins()['plugin']
 
310
        self.assertEqual("1.2.3dev4", plugin.__version__)
 
311
 
 
312
    def test_final__version__with_version_info(self):
 
313
        self.setup_plugin("version_info = (1, 2, 3, 'final', 4)")
 
314
        plugin = bzrlib.plugin.plugins()['plugin']
 
315
        self.assertEqual("1.2.3", plugin.__version__)
 
316
 
145
317
 
146
318
class TestPluginHelp(TestCaseInTempDir):
147
319
 
148
320
    def split_help_commands(self):
149
321
        help = {}
150
322
        current = None
151
 
        for line in self.capture('help commands').splitlines():
 
323
        for line in self.run_bzr('help commands')[0].splitlines():
152
324
            if not line.startswith(' '):
153
325
                current = line.split()[0]
154
326
            help[current] = help.get(current, '') + line
167
339
                # some commands have no help
168
340
                pass
169
341
            else:
170
 
                self.assertNotContainsRe(help, 'From plugin "[^"]*"')
 
342
                self.assertNotContainsRe(help, 'plugin "[^"]*"')
171
343
 
172
344
            if cmd_name in help_commands.keys():
173
345
                # some commands are hidden
174
346
                help = help_commands[cmd_name]
175
 
                self.assertNotContainsRe(help, 'From plugin "[^"]*"')
 
347
                self.assertNotContainsRe(help, 'plugin "[^"]*"')
176
348
 
177
349
    def test_plugin_help_shows_plugin(self):
178
350
        # Create a test plugin
185
357
            # Check its help
186
358
            bzrlib.plugin.load_from_path(['plugin_test'])
187
359
            bzrlib.commands.register_command( bzrlib.plugins.myplug.cmd_myplug)
188
 
            help = self.capture('help myplug')
189
 
            self.assertContainsRe(help, 'From plugin "myplug"')
 
360
            help = self.run_bzr('help myplug')[0]
 
361
            self.assertContainsRe(help, 'plugin "myplug"')
190
362
            help = self.split_help_commands()['myplug']
191
363
            self.assertContainsRe(help, '\[myplug\]')
192
364
        finally:
208
380
    def check_plugin_load(self, zip_name, plugin_name):
209
381
        self.assertFalse(plugin_name in dir(bzrlib.plugins),
210
382
                         'Plugin already loaded')
 
383
        old_path = bzrlib.plugins.__path__
211
384
        try:
212
 
            bzrlib.plugin.load_from_zip(zip_name)
 
385
            # this is normally done by load_plugins -> set_plugins_path
 
386
            bzrlib.plugins.__path__ = [zip_name]
 
387
            self.applyDeprecated(one_three,
 
388
                bzrlib.plugin.load_from_zip, zip_name)
213
389
            self.assertTrue(plugin_name in dir(bzrlib.plugins),
214
390
                            'Plugin is not loaded')
215
391
        finally:
216
392
            # unregister plugin
217
393
            if getattr(bzrlib.plugins, plugin_name, None):
218
394
                delattr(bzrlib.plugins, plugin_name)
 
395
                del sys.modules['bzrlib.plugins.' + plugin_name]
 
396
            bzrlib.plugins.__path__ = old_path
219
397
 
220
398
    def test_load_module(self):
221
399
        self.make_zipped_plugin('./test.zip', 'ziplug.py')
238
416
        finally:
239
417
            bzrlib.plugins.__path__ = old_path
240
418
 
 
419
    def test_set_plugins_path_with_trailing_slashes(self):
 
420
        """set_plugins_path should set the module __path__ based on
 
421
        BZR_PLUGIN_PATH."""
 
422
        old_path = bzrlib.plugins.__path__
 
423
        old_env = os.environ.get('BZR_PLUGIN_PATH')
 
424
        try:
 
425
            bzrlib.plugins.__path__ = []
 
426
            os.environ['BZR_PLUGIN_PATH'] = "first\\//\\" + os.pathsep + \
 
427
                "second/\\/\\/"
 
428
            bzrlib.plugin.set_plugins_path()
 
429
            expected_path = ['first', 'second',
 
430
                os.path.dirname(bzrlib.plugins.__file__)]
 
431
            self.assertEqual(expected_path, bzrlib.plugins.__path__)
 
432
        finally:
 
433
            bzrlib.plugins.__path__ = old_path
 
434
            if old_env != None:
 
435
                os.environ['BZR_PLUGIN_PATH'] = old_env
 
436
            else:
 
437
                del os.environ['BZR_PLUGIN_PATH']
241
438
 
242
439
class TestHelpIndex(tests.TestCase):
243
440
    """Tests for the PluginsHelpIndex class."""
250
447
        index = plugin.PluginsHelpIndex()
251
448
        self.assertEqual([], index.get_topics(None))
252
449
 
253
 
    def test_get_topics_launchpad(self):
254
 
        """Searching for 'launchpad' returns the launchpad plugin docstring."""
 
450
    def test_get_topics_for_plugin(self):
 
451
        """Searching for plugin name gets its docstring."""
255
452
        index = plugin.PluginsHelpIndex()
256
 
        self.assertFalse(sys.modules.has_key('bzrlib.plugins.get_topics'))
257
 
        demo_module = FakeModule('', 'bzrlib.plugins.get_topics')
258
 
        sys.modules['bzrlib.plugins.get_topics'] = demo_module
 
453
        # make a new plugin here for this test, even if we're run with
 
454
        # --no-plugins
 
455
        self.assertFalse(sys.modules.has_key('bzrlib.plugins.demo_module'))
 
456
        demo_module = FakeModule('', 'bzrlib.plugins.demo_module')
 
457
        sys.modules['bzrlib.plugins.demo_module'] = demo_module
259
458
        try:
260
 
            topics = index.get_topics('get_topics')
 
459
            topics = index.get_topics('demo_module')
261
460
            self.assertEqual(1, len(topics))
262
461
            self.assertIsInstance(topics[0], plugin.ModuleHelpTopic)
263
462
            self.assertEqual(demo_module, topics[0].module)
264
463
        finally:
265
 
            del sys.modules['bzrlib.plugins.get_topics']
 
464
            del sys.modules['bzrlib.plugins.demo_module']
266
465
 
267
466
    def test_get_topics_no_topic(self):
268
467
        """Searching for something that is not a plugin returns []."""
276
475
        index = plugin.PluginsHelpIndex()
277
476
        self.assertEqual('plugins/', index.prefix)
278
477
 
279
 
    def test_get_topic_with_prefix(self):
280
 
        """Searching for plugins/launchpad returns launchpad module help."""
 
478
    def test_get_plugin_topic_with_prefix(self):
 
479
        """Searching for plugins/demo_module returns help."""
281
480
        index = plugin.PluginsHelpIndex()
282
 
        self.assertFalse(sys.modules.has_key('bzrlib.plugins.get_topics'))
283
 
        demo_module = FakeModule('', 'bzrlib.plugins.get_topics')
284
 
        sys.modules['bzrlib.plugins.get_topics'] = demo_module
 
481
        self.assertFalse(sys.modules.has_key('bzrlib.plugins.demo_module'))
 
482
        demo_module = FakeModule('', 'bzrlib.plugins.demo_module')
 
483
        sys.modules['bzrlib.plugins.demo_module'] = demo_module
285
484
        try:
286
 
            topics = index.get_topics('plugins/get_topics')
 
485
            topics = index.get_topics('plugins/demo_module')
287
486
            self.assertEqual(1, len(topics))
288
487
            self.assertIsInstance(topics[0], plugin.ModuleHelpTopic)
289
488
            self.assertEqual(demo_module, topics[0].module)
290
489
        finally:
291
 
            del sys.modules['bzrlib.plugins.get_topics']
 
490
            del sys.modules['bzrlib.plugins.demo_module']
292
491
 
293
492
 
294
493
class FakeModule(object):