48
85
# TODO: Write a test for plugin decoration of commands.
50
class TestLoadingPlugins(TestCaseInTempDir):
87
class TestOneNamedPluginOnly(TestCaseInTempDir):
52
89
activeattributes = {}
54
91
def test_plugins_with_the_same_name_are_not_loaded(self):
55
# This test tests that having two plugins in different directories does
56
# not result in both being loaded when they have the same name. get a
57
# file name we can use which is also a valid attribute for accessing in
58
# activeattributes. - we cannot give import parameters.
60
self.failIf(tempattribute in self.activeattributes)
61
# set a place for the plugins to record their loading, and at the same
62
# time validate that the location the plugins should record to is
64
bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
66
self.failUnless(tempattribute in self.activeattributes)
67
# create two plugin directories
70
# write a plugin that will record when its loaded in the
72
template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
73
"TestLoadingPlugins.activeattributes[%r].append('%s')\n")
75
outfile = open(os.path.join('first', 'plugin.py'), 'w')
77
outfile.write(template % (tempattribute, 'first'))
82
outfile = open(os.path.join('second', 'plugin.py'), 'w')
84
outfile.write(template % (tempattribute, 'second'))
90
bzrlib.plugin.load_from_path(['first', 'second'])
91
self.assertEqual(['first'], self.activeattributes[tempattribute])
93
# remove the plugin 'plugin'
94
del self.activeattributes[tempattribute]
95
if 'bzrlib.plugins.plugin' in sys.modules:
96
del sys.modules['bzrlib.plugins.plugin']
97
if getattr(bzrlib.plugins, 'plugin', None):
98
del bzrlib.plugins.plugin
99
self.failIf(getattr(bzrlib.plugins, 'plugin', None))
101
def test_plugins_from_different_dirs_can_demand_load(self):
102
92
# This test tests that having two plugins in different
103
# directories with different names allows them both to be loaded, when
104
# we do a direct import statement.
105
# Determine a file name we can use which is also a valid attribute
93
# directories does not result in both being loaded.
94
# get a file name we can use which is also a valid attribute
106
95
# for accessing in activeattributes. - we cannot give import parameters.
107
tempattribute = "different-dirs"
108
97
self.failIf(tempattribute in self.activeattributes)
109
98
# set a place for the plugins to record their loading, and at the same
110
99
# time validate that the location the plugins should record to is
111
100
# valid and correct.
112
bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
101
bzrlib.tests.test_plugins.TestOneNamedPluginOnly.activeattributes \
113
102
[tempattribute] = []
114
103
self.failUnless(tempattribute in self.activeattributes)
115
104
# create two plugin directories
116
105
os.mkdir('first')
117
106
os.mkdir('second')
118
# write plugins that will record when they are loaded in the
107
# write a plugin that will record when its loaded in the
119
108
# tempattribute list.
120
template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
121
"TestLoadingPlugins.activeattributes[%r].append('%s')\n")
123
outfile = open(os.path.join('first', 'pluginone.py'), 'w')
125
outfile.write(template % (tempattribute, 'first'))
130
outfile = open(os.path.join('second', 'plugintwo.py'), 'w')
132
outfile.write(template % (tempattribute, 'second'))
137
oldpath = bzrlib.plugins.__path__
139
bzrlib.plugins.__path__ = ['first', 'second']
140
exec "import bzrlib.plugins.pluginone"
109
template = ("from bzrlib.tests.test_plugins import TestOneNamedPluginOnly\n"
110
"TestOneNamedPluginOnly.activeattributes[%r].append('%s')\n")
111
print >> file(os.path.join('first', 'plugin.py'), 'w'), template % (tempattribute, 'first')
112
print >> file(os.path.join('second', 'plugin.py'), 'w'), template % (tempattribute, 'second')
114
bzrlib.plugin.load_from_dirs(['first', 'second'])
141
115
self.assertEqual(['first'], self.activeattributes[tempattribute])
142
exec "import bzrlib.plugins.plugintwo"
143
self.assertEqual(['first', 'second'],
144
self.activeattributes[tempattribute])
146
# remove the plugin 'plugin'
147
del self.activeattributes[tempattribute]
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))
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
165
bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
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")
175
outfile = open(os.path.join('plugin_test', 'ts_plugin.py'), 'w')
177
outfile.write(template % (tempattribute, 'plugin'))
183
bzrlib.plugin.load_from_path(['plugin_test'+os.sep])
184
self.assertEqual(['plugin'], self.activeattributes[tempattribute])
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))
192
def test_plugin_with_bad_name_does_not_load(self):
193
# Create badly-named plugin
194
file('bad plugin-name..py', 'w').close()
198
handler = logging.StreamHandler(stream)
199
log = logging.getLogger('bzr')
200
log.addHandler(handler)
202
bzrlib.plugin.load_from_dir('.')
204
# Stop capturing output
207
log.removeHandler(handler)
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_'\.")
217
class TestPlugins(TestCaseInTempDir):
219
def setup_plugin(self, source=""):
220
# This test tests a new plugin appears in bzrlib.plugin.plugins().
117
# remove the plugin 'plugin'
118
del self.activeattributes[tempattribute]
119
if getattr(bzrlib.plugins, 'plugin', None):
120
del bzrlib.plugins.plugin
121
self.failIf(getattr(bzrlib.plugins, 'plugin', None))
124
class TestAllPlugins(TestCaseInTempDir):
126
def test_plugin_appears_in_all_plugins(self):
127
# This test tests a new plugin appears in bzrlib.plugin.all_plugins().
221
128
# check the plugin is not loaded already
222
129
self.failIf(getattr(bzrlib.plugins, 'plugin', None))
223
130
# write a plugin that _cannot_ fail to load.
224
file('plugin.py', 'w').write(source + '\n')
225
self.addCleanup(self.teardown_plugin)
226
bzrlib.plugin.load_from_path(['.'])
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
131
print >> file('plugin.py', 'w'), ""
133
bzrlib.plugin.load_from_dirs(['.'])
134
self.failUnless('plugin' in bzrlib.plugin.all_plugins())
135
self.failUnless(getattr(bzrlib.plugins, 'plugin', None))
136
self.assertEqual(bzrlib.plugin.all_plugins()['plugin'],
137
bzrlib.plugins.plugin)
139
# remove the plugin 'plugin'
140
if getattr(bzrlib.plugins, 'plugin', None):
141
del bzrlib.plugins.plugin
234
142
self.failIf(getattr(bzrlib.plugins, 'plugin', None))
236
def test_plugin_appears_in_plugins(self):
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)
245
def test_trivial_plugin_get_path(self):
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()))
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()))
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']
269
plugin_path = self.test_dir + '/plugin.pyc'
271
plugin_path = self.test_dir + '/plugin.pyo'
272
self.assertIsSameRealPath(plugin_path, normpath(plugin.path()))
274
def test_no_test_suite_gives_None_for_test_suite(self):
276
plugin = bzrlib.plugin.plugins()['plugin']
277
self.assertEqual(None, plugin.test_suite())
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())
285
def test_no_version_info(self):
287
plugin = bzrlib.plugin.plugins()['plugin']
288
self.assertEqual(None, plugin.version_info())
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())
295
def test_short_version_info_gets_padded(self):
296
# the gtk plugin has version_info = (1,2,3) rather than the 5-tuple.
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())
302
def test_no_version_info___version__(self):
304
plugin = bzrlib.plugin.plugins()['plugin']
305
self.assertEqual("unknown", plugin.__version__)
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__)
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__)
318
145
class TestPluginHelp(TestCaseInTempDir):
320
147
def split_help_commands(self):
323
for line in self.run_bzr('help commands')[0].splitlines():
150
for line in self.capture('help commands').splitlines():
324
151
if not line.startswith(' '):
325
152
current = line.split()[0]
326
153
help[current] = help.get(current, '') + line
402
225
def test_load_package(self):
403
226
self.make_zipped_plugin('./test.zip', 'ziplug/__init__.py')
404
227
self.check_plugin_load('./test.zip', 'ziplug')
407
class TestSetPluginsPath(TestCase):
409
def test_set_plugins_path(self):
410
"""set_plugins_path should set the module __path__ correctly."""
411
old_path = bzrlib.plugins.__path__
413
bzrlib.plugins.__path__ = []
414
expected_path = bzrlib.plugin.set_plugins_path()
415
self.assertEqual(expected_path, bzrlib.plugins.__path__)
417
bzrlib.plugins.__path__ = old_path
419
def test_set_plugins_path_with_trailing_slashes(self):
420
"""set_plugins_path should set the module __path__ based on
422
old_path = bzrlib.plugins.__path__
423
old_env = os.environ.get('BZR_PLUGIN_PATH')
425
bzrlib.plugins.__path__ = []
426
os.environ['BZR_PLUGIN_PATH'] = "first\\//\\" + os.pathsep + \
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__)
433
bzrlib.plugins.__path__ = old_path
435
os.environ['BZR_PLUGIN_PATH'] = old_env
437
del os.environ['BZR_PLUGIN_PATH']
439
class TestHelpIndex(tests.TestCase):
440
"""Tests for the PluginsHelpIndex class."""
442
def test_default_constructable(self):
443
index = plugin.PluginsHelpIndex()
445
def test_get_topics_None(self):
446
"""Searching for None returns an empty list."""
447
index = plugin.PluginsHelpIndex()
448
self.assertEqual([], index.get_topics(None))
450
def test_get_topics_for_plugin(self):
451
"""Searching for plugin name gets its docstring."""
452
index = plugin.PluginsHelpIndex()
453
# make a new plugin here for this test, even if we're run with
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
459
topics = index.get_topics('demo_module')
460
self.assertEqual(1, len(topics))
461
self.assertIsInstance(topics[0], plugin.ModuleHelpTopic)
462
self.assertEqual(demo_module, topics[0].module)
464
del sys.modules['bzrlib.plugins.demo_module']
466
def test_get_topics_no_topic(self):
467
"""Searching for something that is not a plugin returns []."""
468
# test this by using a name that cannot be a plugin - its not
469
# a valid python identifier.
470
index = plugin.PluginsHelpIndex()
471
self.assertEqual([], index.get_topics('nothing by this name'))
473
def test_prefix(self):
474
"""PluginsHelpIndex has a prefix of 'plugins/'."""
475
index = plugin.PluginsHelpIndex()
476
self.assertEqual('plugins/', index.prefix)
478
def test_get_plugin_topic_with_prefix(self):
479
"""Searching for plugins/demo_module returns help."""
480
index = plugin.PluginsHelpIndex()
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
485
topics = index.get_topics('plugins/demo_module')
486
self.assertEqual(1, len(topics))
487
self.assertIsInstance(topics[0], plugin.ModuleHelpTopic)
488
self.assertEqual(demo_module, topics[0].module)
490
del sys.modules['bzrlib.plugins.demo_module']
493
class FakeModule(object):
494
"""A fake module to test with."""
496
def __init__(self, doc, name):
501
class TestModuleHelpTopic(tests.TestCase):
502
"""Tests for the ModuleHelpTopic class."""
504
def test_contruct(self):
505
"""Construction takes the module to document."""
506
mod = FakeModule('foo', 'foo')
507
topic = plugin.ModuleHelpTopic(mod)
508
self.assertEqual(mod, topic.module)
510
def test_get_help_text_None(self):
511
"""A ModuleHelpTopic returns the docstring for get_help_text."""
512
mod = FakeModule(None, 'demo')
513
topic = plugin.ModuleHelpTopic(mod)
514
self.assertEqual("Plugin 'demo' has no docstring.\n",
515
topic.get_help_text())
517
def test_get_help_text_no_carriage_return(self):
518
"""ModuleHelpTopic.get_help_text adds a \n if needed."""
519
mod = FakeModule('one line of help', 'demo')
520
topic = plugin.ModuleHelpTopic(mod)
521
self.assertEqual("one line of help\n",
522
topic.get_help_text())
524
def test_get_help_text_carriage_return(self):
525
"""ModuleHelpTopic.get_help_text adds a \n if needed."""
526
mod = FakeModule('two lines of help\nand more\n', 'demo')
527
topic = plugin.ModuleHelpTopic(mod)
528
self.assertEqual("two lines of help\nand more\n",
529
topic.get_help_text())
531
def test_get_help_text_with_additional_see_also(self):
532
mod = FakeModule('two lines of help\nand more', 'demo')
533
topic = plugin.ModuleHelpTopic(mod)
534
self.assertEqual("two lines of help\nand more\nSee also: bar, foo\n",
535
topic.get_help_text(['foo', 'bar']))
537
def test_get_help_topic(self):
538
"""The help topic for a plugin is its module name."""
539
mod = FakeModule('two lines of help\nand more', 'bzrlib.plugins.demo')
540
topic = plugin.ModuleHelpTopic(mod)
541
self.assertEqual('demo', topic.get_help_topic())
542
mod = FakeModule('two lines of help\nand more', 'bzrlib.plugins.foo_bar')
543
topic = plugin.ModuleHelpTopic(mod)
544
self.assertEqual('foo_bar', topic.get_help_topic())