24
from StringIO import StringIO
28
from bzrlib import plugin, tests
29
26
import bzrlib.plugin
30
27
import bzrlib.plugins
31
import bzrlib.commands
33
from bzrlib.tests import TestCase, TestCaseInTempDir
28
from bzrlib.tests import TestCaseInTempDir
34
29
from bzrlib.osutils import pathjoin, abspath
31
class PluginTest(TestCaseInTempDir):
32
"""Create an external plugin and test loading."""
33
# def test_plugin_loading(self):
34
# orig_help = self.run_bzr_captured('bzr help commands')[0]
35
# os.mkdir('plugin_test')
36
# f = open(pathjoin('plugin_test', 'myplug.py'), 'wt')
37
# f.write(PLUGIN_TEXT)
39
# newhelp = self.run_bzr_captured('bzr help commands')[0]
40
# assert newhelp.startswith('You have been overridden\n')
41
# # We added a line, but the rest should work
42
# assert newhelp[25:] == help
44
# assert backtick('bzr commit -m test') == "I'm sorry dave, you can't do that\n"
46
# shutil.rmtree('plugin_test')
49
# os.environ['BZRPLUGINPATH'] = abspath('plugin_test')
50
# help = backtick('bzr help commands')
51
# assert help.find('myplug') != -1
52
# assert help.find('Just a simple test plugin.') != -1
55
# assert backtick('bzr myplug') == 'Hello from my plugin\n'
56
# assert backtick('bzr mplg') == 'Hello from my plugin\n'
58
# f = open(pathjoin('plugin_test', 'override.py'), 'wb')
59
# f.write("""import bzrlib, bzrlib.commands
60
# class cmd_commit(bzrlib.commands.cmd_commit):
61
# '''Commit changes into a new revision.'''
62
# def run(self, *args, **kwargs):
63
# print "I'm sorry dave, you can't do that"
65
# class cmd_help(bzrlib.commands.cmd_help):
66
# '''Show help on a command or other topic.'''
67
# def run(self, *args, **kwargs):
68
# print "You have been overridden"
69
# bzrlib.commands.cmd_help.run(self, *args, **kwargs)
38
74
import bzrlib.commands
67
103
os.mkdir('second')
68
104
# write a plugin that will record when its loaded in the
69
105
# tempattribute list.
70
template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
71
"TestLoadingPlugins.activeattributes[%r].append('%s')\n")
106
template = ("from bzrlib.tests.test_plugins import TestOneNamedPluginOnly\n"
107
"TestOneNamedPluginOnly.activeattributes[%r].append('%s')\n")
72
108
print >> file(os.path.join('first', 'plugin.py'), 'w'), template % (tempattribute, 'first')
73
109
print >> file(os.path.join('second', 'plugin.py'), 'w'), template % (tempattribute, 'second')
75
bzrlib.plugin.load_from_path(['first', 'second'])
76
self.assertEqual(['first'], self.activeattributes[tempattribute])
78
# remove the plugin 'plugin'
79
del self.activeattributes[tempattribute]
80
if getattr(bzrlib.plugins, 'plugin', None):
81
del bzrlib.plugins.plugin
82
self.failIf(getattr(bzrlib.plugins, 'plugin', None))
84
def test_plugins_from_different_dirs_can_demand_load(self):
85
# This test tests that having two plugins in different
86
# directories with different names allows them both to be loaded, when
87
# we do a direct import statement.
88
# Determine a file name we can use which is also a valid attribute
89
# for accessing in activeattributes. - we cannot give import parameters.
90
tempattribute = "different-dirs"
91
self.failIf(tempattribute in self.activeattributes)
92
# set a place for the plugins to record their loading, and at the same
93
# time validate that the location the plugins should record to is
95
bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
97
self.failUnless(tempattribute in self.activeattributes)
98
# create two plugin directories
101
# write plugins that will record when they are loaded in the
102
# tempattribute list.
103
template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
104
"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')
107
oldpath = bzrlib.plugins.__path__
109
bzrlib.plugins.__path__ = ['first', 'second']
110
exec "import bzrlib.plugins.pluginone"
111
self.assertEqual(['first'], self.activeattributes[tempattribute])
112
exec "import bzrlib.plugins.plugintwo"
113
self.assertEqual(['first', 'second'],
114
self.activeattributes[tempattribute])
111
bzrlib.plugin.load_from_dirs(['first', 'second'])
112
self.assertEqual(['first'], self.activeattributes[tempattribute])
116
114
# remove the plugin 'plugin'
117
115
del self.activeattributes[tempattribute]
129
127
# write a plugin that _cannot_ fail to load.
130
128
print >> file('plugin.py', 'w'), ""
132
bzrlib.plugin.load_from_path(['.'])
130
bzrlib.plugin.load_from_dirs(['.'])
133
131
self.failUnless('plugin' in bzrlib.plugin.all_plugins())
134
132
self.failUnless(getattr(bzrlib.plugins, 'plugin', None))
135
133
self.assertEqual(bzrlib.plugin.all_plugins()['plugin'],
136
134
bzrlib.plugins.plugin)
138
136
# remove the plugin 'plugin'
139
if 'bzrlib.plugins.plugin' in sys.modules:
140
del sys.modules['bzrlib.plugins.plugin']
141
137
if getattr(bzrlib.plugins, 'plugin', None):
142
138
del bzrlib.plugins.plugin
143
139
self.failIf(getattr(bzrlib.plugins, 'plugin', None))
146
class TestPluginHelp(TestCaseInTempDir):
148
def split_help_commands(self):
151
for line in self.run_bzr('help commands')[0].splitlines():
152
if not line.startswith(' '):
153
current = line.split()[0]
154
help[current] = help.get(current, '') + line
158
def test_plugin_help_builtins_unaffected(self):
159
# Check we don't get false positives
160
help_commands = self.split_help_commands()
161
for cmd_name in bzrlib.commands.builtin_command_names():
162
if cmd_name in bzrlib.commands.plugin_command_names():
165
help = bzrlib.commands.get_cmd_object(cmd_name).get_help_text()
166
except NotImplementedError:
167
# some commands have no help
170
self.assertNotContainsRe(help, 'From plugin "[^"]*"')
172
if cmd_name in help_commands.keys():
173
# some commands are hidden
174
help = help_commands[cmd_name]
175
self.assertNotContainsRe(help, 'From plugin "[^"]*"')
177
def test_plugin_help_shows_plugin(self):
178
# Create a test plugin
179
os.mkdir('plugin_test')
180
f = open(pathjoin('plugin_test', 'myplug.py'), 'w')
186
bzrlib.plugin.load_from_path(['plugin_test'])
187
bzrlib.commands.register_command( bzrlib.plugins.myplug.cmd_myplug)
188
help = self.run_bzr('help myplug')[0]
189
self.assertContainsRe(help, 'From plugin "myplug"')
190
help = self.split_help_commands()['myplug']
191
self.assertContainsRe(help, '\[myplug\]')
194
if bzrlib.commands.plugin_cmds.get('myplug', None):
195
del bzrlib.commands.plugin_cmds['myplug']
196
# remove the plugin 'myplug'
197
if getattr(bzrlib.plugins, 'myplug', None):
198
delattr(bzrlib.plugins, 'myplug')
201
class TestPluginFromZip(TestCaseInTempDir):
203
def make_zipped_plugin(self, zip_name, filename):
204
z = zipfile.ZipFile(zip_name, 'w')
205
z.writestr(filename, PLUGIN_TEXT)
208
def check_plugin_load(self, zip_name, plugin_name):
209
self.assertFalse(plugin_name in dir(bzrlib.plugins),
210
'Plugin already loaded')
211
old_path = bzrlib.plugins.__path__
213
# this is normally done by load_plugins -> set_plugins_path
214
bzrlib.plugins.__path__ = [zip_name]
215
bzrlib.plugin.load_from_zip(zip_name)
216
self.assertTrue(plugin_name in dir(bzrlib.plugins),
217
'Plugin is not loaded')
220
if getattr(bzrlib.plugins, plugin_name, None):
221
delattr(bzrlib.plugins, plugin_name)
222
del sys.modules['bzrlib.plugins.' + plugin_name]
223
bzrlib.plugins.__path__ = old_path
225
def test_load_module(self):
226
self.make_zipped_plugin('./test.zip', 'ziplug.py')
227
self.check_plugin_load('./test.zip', 'ziplug')
229
def test_load_package(self):
230
self.make_zipped_plugin('./test.zip', 'ziplug/__init__.py')
231
self.check_plugin_load('./test.zip', 'ziplug')
234
class TestSetPluginsPath(TestCase):
236
def test_set_plugins_path(self):
237
"""set_plugins_path should set the module __path__ correctly."""
238
old_path = bzrlib.plugins.__path__
240
bzrlib.plugins.__path__ = []
241
expected_path = bzrlib.plugin.set_plugins_path()
242
self.assertEqual(expected_path, bzrlib.plugins.__path__)
244
bzrlib.plugins.__path__ = old_path
247
class TestHelpIndex(tests.TestCase):
248
"""Tests for the PluginsHelpIndex class."""
250
def test_default_constructable(self):
251
index = plugin.PluginsHelpIndex()
253
def test_get_topics_None(self):
254
"""Searching for None returns an empty list."""
255
index = plugin.PluginsHelpIndex()
256
self.assertEqual([], index.get_topics(None))
258
def test_get_topics_for_plugin(self):
259
"""Searching for plugin name gets its docstring."""
260
index = plugin.PluginsHelpIndex()
261
# make a new plugin here for this test, even if we're run with
263
self.assertFalse(sys.modules.has_key('bzrlib.plugins.demo_module'))
264
demo_module = FakeModule('', 'bzrlib.plugins.demo_module')
265
sys.modules['bzrlib.plugins.demo_module'] = demo_module
267
topics = index.get_topics('demo_module')
268
self.assertEqual(1, len(topics))
269
self.assertIsInstance(topics[0], plugin.ModuleHelpTopic)
270
self.assertEqual(demo_module, topics[0].module)
272
del sys.modules['bzrlib.plugins.demo_module']
274
def test_get_topics_no_topic(self):
275
"""Searching for something that is not a plugin returns []."""
276
# test this by using a name that cannot be a plugin - its not
277
# a valid python identifier.
278
index = plugin.PluginsHelpIndex()
279
self.assertEqual([], index.get_topics('nothing by this name'))
281
def test_prefix(self):
282
"""PluginsHelpIndex has a prefix of 'plugins/'."""
283
index = plugin.PluginsHelpIndex()
284
self.assertEqual('plugins/', index.prefix)
286
def test_get_plugin_topic_with_prefix(self):
287
"""Searching for plugins/demo_module returns help."""
288
index = plugin.PluginsHelpIndex()
289
self.assertFalse(sys.modules.has_key('bzrlib.plugins.demo_module'))
290
demo_module = FakeModule('', 'bzrlib.plugins.demo_module')
291
sys.modules['bzrlib.plugins.demo_module'] = demo_module
293
topics = index.get_topics('plugins/demo_module')
294
self.assertEqual(1, len(topics))
295
self.assertIsInstance(topics[0], plugin.ModuleHelpTopic)
296
self.assertEqual(demo_module, topics[0].module)
298
del sys.modules['bzrlib.plugins.demo_module']
301
class FakeModule(object):
302
"""A fake module to test with."""
304
def __init__(self, doc, name):
309
class TestModuleHelpTopic(tests.TestCase):
310
"""Tests for the ModuleHelpTopic class."""
312
def test_contruct(self):
313
"""Construction takes the module to document."""
314
mod = FakeModule('foo', 'foo')
315
topic = plugin.ModuleHelpTopic(mod)
316
self.assertEqual(mod, topic.module)
318
def test_get_help_text_None(self):
319
"""A ModuleHelpTopic returns the docstring for get_help_text."""
320
mod = FakeModule(None, 'demo')
321
topic = plugin.ModuleHelpTopic(mod)
322
self.assertEqual("Plugin 'demo' has no docstring.\n",
323
topic.get_help_text())
325
def test_get_help_text_no_carriage_return(self):
326
"""ModuleHelpTopic.get_help_text adds a \n if needed."""
327
mod = FakeModule('one line of help', 'demo')
328
topic = plugin.ModuleHelpTopic(mod)
329
self.assertEqual("one line of help\n",
330
topic.get_help_text())
332
def test_get_help_text_carriage_return(self):
333
"""ModuleHelpTopic.get_help_text adds a \n if needed."""
334
mod = FakeModule('two lines of help\nand more\n', 'demo')
335
topic = plugin.ModuleHelpTopic(mod)
336
self.assertEqual("two lines of help\nand more\n",
337
topic.get_help_text())
339
def test_get_help_text_with_additional_see_also(self):
340
mod = FakeModule('two lines of help\nand more', 'demo')
341
topic = plugin.ModuleHelpTopic(mod)
342
self.assertEqual("two lines of help\nand more\nSee also: bar, foo\n",
343
topic.get_help_text(['foo', 'bar']))
345
def test_get_help_topic(self):
346
"""The help topic for a plugin is its module name."""
347
mod = FakeModule('two lines of help\nand more', 'bzrlib.plugins.demo')
348
topic = plugin.ModuleHelpTopic(mod)
349
self.assertEqual('demo', topic.get_help_topic())
350
mod = FakeModule('two lines of help\nand more', 'bzrlib.plugins.foo_bar')
351
topic = plugin.ModuleHelpTopic(mod)
352
self.assertEqual('foo_bar', topic.get_help_topic())