24
from StringIO import StringIO
28
from bzrlib import plugin, tests
26
29
import bzrlib.plugin
27
30
import bzrlib.plugins
28
from bzrlib.tests import TestCaseInTempDir
31
import bzrlib.commands
33
from bzrlib.tests import TestCase, TestCaseInTempDir
29
34
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)
74
38
import bzrlib.commands
103
67
os.mkdir('second')
104
68
# write a plugin that will record when its loaded in the
105
69
# tempattribute list.
106
template = ("from bzrlib.tests.test_plugins import TestOneNamedPluginOnly\n"
107
"TestOneNamedPluginOnly.activeattributes[%r].append('%s')\n")
70
template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
71
"TestLoadingPlugins.activeattributes[%r].append('%s')\n")
108
72
print >> file(os.path.join('first', 'plugin.py'), 'w'), template % (tempattribute, 'first')
109
73
print >> file(os.path.join('second', 'plugin.py'), 'w'), template % (tempattribute, 'second')
111
bzrlib.plugin.load_from_dirs(['first', 'second'])
112
self.assertEqual(['first'], self.activeattributes[tempattribute])
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])
114
116
# remove the plugin 'plugin'
115
117
del self.activeattributes[tempattribute]
127
129
# write a plugin that _cannot_ fail to load.
128
130
print >> file('plugin.py', 'w'), ""
130
bzrlib.plugin.load_from_dirs(['.'])
132
bzrlib.plugin.load_from_path(['.'])
131
133
self.failUnless('plugin' in bzrlib.plugin.all_plugins())
132
134
self.failUnless(getattr(bzrlib.plugins, 'plugin', None))
133
135
self.assertEqual(bzrlib.plugin.all_plugins()['plugin'],
134
136
bzrlib.plugins.plugin)
136
138
# remove the plugin 'plugin'
139
if 'bzrlib.plugins.plugin' in sys.modules:
140
del sys.modules['bzrlib.plugins.plugin']
137
141
if getattr(bzrlib.plugins, 'plugin', None):
138
142
del bzrlib.plugins.plugin
139
143
self.failIf(getattr(bzrlib.plugins, 'plugin', None))
146
class TestPluginHelp(TestCaseInTempDir):
148
def split_help_commands(self):
151
for line in self.capture('help commands').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.capture('help myplug')
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')
212
bzrlib.plugin.load_from_zip(zip_name)
213
self.assertTrue(plugin_name in dir(bzrlib.plugins),
214
'Plugin is not loaded')
217
if getattr(bzrlib.plugins, plugin_name, None):
218
delattr(bzrlib.plugins, plugin_name)
220
def test_load_module(self):
221
self.make_zipped_plugin('./test.zip', 'ziplug.py')
222
self.check_plugin_load('./test.zip', 'ziplug')
224
def test_load_package(self):
225
self.make_zipped_plugin('./test.zip', 'ziplug/__init__.py')
226
self.check_plugin_load('./test.zip', 'ziplug')
229
class TestSetPluginsPath(TestCase):
231
def test_set_plugins_path(self):
232
"""set_plugins_path should set the module __path__ correctly."""
233
old_path = bzrlib.plugins.__path__
235
bzrlib.plugins.__path__ = []
236
expected_path = bzrlib.plugin.set_plugins_path()
237
self.assertEqual(expected_path, bzrlib.plugins.__path__)
239
bzrlib.plugins.__path__ = old_path
242
class TestHelpIndex(tests.TestCase):
243
"""Tests for the PluginsHelpIndex class."""
245
def test_default_constructable(self):
246
index = plugin.PluginsHelpIndex()
248
def test_get_topics_None(self):
249
"""Searching for None returns an empty list."""
250
index = plugin.PluginsHelpIndex()
251
self.assertEqual([], index.get_topics(None))
253
def test_get_topics_for_plugin(self):
254
"""Searching for plugin name gets its docstring."""
255
index = plugin.PluginsHelpIndex()
256
# make a new plugin here for this test, even if we're run with
258
self.assertFalse(sys.modules.has_key('bzrlib.plugins.demo_module'))
259
demo_module = FakeModule('', 'bzrlib.plugins.demo_module')
260
sys.modules['bzrlib.plugins.demo_module'] = demo_module
262
topics = index.get_topics('demo_module')
263
self.assertEqual(1, len(topics))
264
self.assertIsInstance(topics[0], plugin.ModuleHelpTopic)
265
self.assertEqual(demo_module, topics[0].module)
267
del sys.modules['bzrlib.plugins.demo_module']
269
def test_get_topics_no_topic(self):
270
"""Searching for something that is not a plugin returns []."""
271
# test this by using a name that cannot be a plugin - its not
272
# a valid python identifier.
273
index = plugin.PluginsHelpIndex()
274
self.assertEqual([], index.get_topics('nothing by this name'))
276
def test_prefix(self):
277
"""PluginsHelpIndex has a prefix of 'plugins/'."""
278
index = plugin.PluginsHelpIndex()
279
self.assertEqual('plugins/', index.prefix)
281
def test_get_plugin_topic_with_prefix(self):
282
"""Searching for plugins/demo_module returns help."""
283
index = plugin.PluginsHelpIndex()
284
self.assertFalse(sys.modules.has_key('bzrlib.plugins.demo_module'))
285
demo_module = FakeModule('', 'bzrlib.plugins.demo_module')
286
sys.modules['bzrlib.plugins.demo_module'] = demo_module
288
topics = index.get_topics('plugins/demo_module')
289
self.assertEqual(1, len(topics))
290
self.assertIsInstance(topics[0], plugin.ModuleHelpTopic)
291
self.assertEqual(demo_module, topics[0].module)
293
del sys.modules['bzrlib.plugins.demo_module']
296
class FakeModule(object):
297
"""A fake module to test with."""
299
def __init__(self, doc, name):
304
class TestModuleHelpTopic(tests.TestCase):
305
"""Tests for the ModuleHelpTopic class."""
307
def test_contruct(self):
308
"""Construction takes the module to document."""
309
mod = FakeModule('foo', 'foo')
310
topic = plugin.ModuleHelpTopic(mod)
311
self.assertEqual(mod, topic.module)
313
def test_get_help_text_None(self):
314
"""A ModuleHelpTopic returns the docstring for get_help_text."""
315
mod = FakeModule(None, 'demo')
316
topic = plugin.ModuleHelpTopic(mod)
317
self.assertEqual("Plugin 'demo' has no docstring.\n",
318
topic.get_help_text())
320
def test_get_help_text_no_carriage_return(self):
321
"""ModuleHelpTopic.get_help_text adds a \n if needed."""
322
mod = FakeModule('one line of help', 'demo')
323
topic = plugin.ModuleHelpTopic(mod)
324
self.assertEqual("one line of help\n",
325
topic.get_help_text())
327
def test_get_help_text_carriage_return(self):
328
"""ModuleHelpTopic.get_help_text adds a \n if needed."""
329
mod = FakeModule('two lines of help\nand more\n', 'demo')
330
topic = plugin.ModuleHelpTopic(mod)
331
self.assertEqual("two lines of help\nand more\n",
332
topic.get_help_text())
334
def test_get_help_text_with_additional_see_also(self):
335
mod = FakeModule('two lines of help\nand more', 'demo')
336
topic = plugin.ModuleHelpTopic(mod)
337
self.assertEqual("two lines of help\nand more\nSee also: bar, foo\n",
338
topic.get_help_text(['foo', 'bar']))
340
def test_get_help_topic(self):
341
"""The help topic for a plugin is its module name."""
342
mod = FakeModule('two lines of help\nand more', 'bzrlib.plugins.demo')
343
topic = plugin.ModuleHelpTopic(mod)
344
self.assertEqual('demo', topic.get_help_topic())
345
mod = FakeModule('two lines of help\nand more', 'bzrlib.plugins.foo_bar')
346
topic = plugin.ModuleHelpTopic(mod)
347
self.assertEqual('foo_bar', topic.get_help_topic())