39
38
# TODO: Write a test for plugin decoration of commands.
41
class BaseTestPlugins(tests.TestCaseInTempDir):
40
class TestPluginMixin(object):
43
42
def create_plugin(self, name, source=None, dir='.', file_name=None):
80
79
if getattr(bzrlib.plugins, name, None) is not None:
81
80
delattr(bzrlib.plugins, name)
83
def _unregister_plugin_submodule(self, plugin_name, submodule_name):
84
"""Remove the submodule from sys.modules and the bzrlib namespace."""
85
py_name = 'bzrlib.plugins.%s.%s' % (plugin_name, submodule_name)
86
if py_name in sys.modules:
87
del sys.modules[py_name]
88
plugin = getattr(bzrlib.plugins, plugin_name, None)
89
if plugin is not None:
90
if getattr(plugin, submodule_name, None) is not None:
91
delattr(plugin, submodule_name)
93
82
def assertPluginUnknown(self, name):
94
self.assertFalse(getattr(bzrlib.plugins, name, None) is not None)
95
self.assertFalse('bzrlib.plugins.%s' % name in sys.modules)
83
self.failIf(getattr(bzrlib.plugins, name, None) is not None)
84
self.failIf('bzrlib.plugins.%s' % name in sys.modules)
97
86
def assertPluginKnown(self, name):
98
self.assertTrue(getattr(bzrlib.plugins, name, None) is not None)
99
self.assertTrue('bzrlib.plugins.%s' % name in sys.modules)
102
class TestLoadingPlugins(BaseTestPlugins):
87
self.failUnless(getattr(bzrlib.plugins, name, None) is not None)
88
self.failUnless('bzrlib.plugins.%s' % name in sys.modules)
91
class TestLoadingPlugins(tests.TestCaseInTempDir, TestPluginMixin):
104
93
activeattributes = {}
109
98
# file name we can use which is also a valid attribute for accessing in
110
99
# activeattributes. - we cannot give import parameters.
111
100
tempattribute = "0"
112
self.assertFalse(tempattribute in self.activeattributes)
101
self.failIf(tempattribute in self.activeattributes)
113
102
# set a place for the plugins to record their loading, and at the same
114
103
# time validate that the location the plugins should record to is
115
104
# valid and correct.
116
105
self.__class__.activeattributes [tempattribute] = []
117
self.assertTrue(tempattribute in self.activeattributes)
106
self.failUnless(tempattribute in self.activeattributes)
118
107
# create two plugin directories
119
108
os.mkdir('first')
120
109
os.mkdir('second')
147
136
self.assertPluginUnknown('plugin')
149
138
def test_plugins_from_different_dirs_can_demand_load(self):
150
self.assertFalse('bzrlib.plugins.pluginone' in sys.modules)
151
self.assertFalse('bzrlib.plugins.plugintwo' in sys.modules)
139
self.failIf('bzrlib.plugins.pluginone' in sys.modules)
140
self.failIf('bzrlib.plugins.plugintwo' in sys.modules)
152
141
# This test tests that having two plugins in different
153
142
# directories with different names allows them both to be loaded, when
154
143
# we do a direct import statement.
155
144
# Determine a file name we can use which is also a valid attribute
156
145
# for accessing in activeattributes. - we cannot give import parameters.
157
146
tempattribute = "different-dirs"
158
self.assertFalse(tempattribute in self.activeattributes)
147
self.failIf(tempattribute in self.activeattributes)
159
148
# set a place for the plugins to record their loading, and at the same
160
149
# time validate that the location the plugins should record to is
161
150
# valid and correct.
162
151
bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
163
152
[tempattribute] = []
164
self.assertTrue(tempattribute in self.activeattributes)
153
self.failUnless(tempattribute in self.activeattributes)
165
154
# create two plugin directories
166
155
os.mkdir('first')
167
156
os.mkdir('second')
187
176
oldpath = bzrlib.plugins.__path__
189
self.assertFalse('bzrlib.plugins.pluginone' in sys.modules)
190
self.assertFalse('bzrlib.plugins.plugintwo' in sys.modules)
178
self.failIf('bzrlib.plugins.pluginone' in sys.modules)
179
self.failIf('bzrlib.plugins.plugintwo' in sys.modules)
191
180
bzrlib.plugins.__path__ = ['first', 'second']
192
181
exec "import bzrlib.plugins.pluginone"
193
182
self.assertEqual(['first'], self.activeattributes[tempattribute])
208
197
# check the plugin is not loaded already
209
198
self.assertPluginUnknown('ts_plugin')
210
199
tempattribute = "trailing-slash"
211
self.assertFalse(tempattribute in self.activeattributes)
200
self.failIf(tempattribute in self.activeattributes)
212
201
# set a place for the plugin to record its loading, and at the same
213
202
# time validate that the location the plugin should record to is
214
203
# valid and correct.
215
204
bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
216
205
[tempattribute] = []
217
self.assertTrue(tempattribute in self.activeattributes)
206
self.failUnless(tempattribute in self.activeattributes)
218
207
# create a directory for the plugin
219
208
os.mkdir('plugin_test')
220
209
# write a plugin that will record when its loaded in the
269
258
def test_plugin_with_bad_api_version_reports(self):
270
"""Try loading a plugin that requests an unsupported api.
272
Observe that it records the problem but doesn't complain on stderr.
274
See https://bugs.launchpad.net/bzr/+bug/704195
276
self.overrideAttr(plugin, 'plugin_warnings', {})
259
# This plugin asks for bzrlib api version 1.0.0, which is not supported
277
261
name = 'wants100.py'
278
262
f = file(name, 'w')
281
265
"bzrlib.api.require_any_api(bzrlib, [(1, 0, 0)])\n")
284
269
log = self.load_and_capture(name)
285
self.assertNotContainsRe(log,
286
r"It requested API version")
289
plugin.plugin_warnings.keys())
290
self.assertContainsRe(
291
plugin.plugin_warnings['wants100'][0],
270
self.assertContainsRe(log,
292
271
r"It requested API version")
294
273
def test_plugin_with_bad_name_does_not_load(self):
302
281
"it to 'bad_plugin_name_'\.")
305
class TestPlugins(BaseTestPlugins):
284
class TestPlugins(tests.TestCaseInTempDir, TestPluginMixin):
307
286
def setup_plugin(self, source=""):
308
287
# This test tests a new plugin appears in bzrlib.plugin.plugins().
452
431
def test_final_fallback__version__with_version_info(self):
453
432
self.setup_plugin("version_info = (1, 2, 3, 'final', 2)")
454
433
plugin = bzrlib.plugin.plugins()['plugin']
455
self.assertEqual("1.2.3.2", plugin.__version__)
434
self.assertEqual("1.2.3.final.2", plugin.__version__)
458
437
class TestPluginHelp(tests.TestCaseInTempDir):
666
645
self.fail('No path to global plugins')
668
647
def test_get_standard_plugins_path_env(self):
669
self.overrideEnv('BZR_PLUGIN_PATH', 'foo/')
648
os.environ['BZR_PLUGIN_PATH'] = 'foo/'
670
649
path = plugin.get_standard_plugins_path()
671
650
for directory in path:
672
651
self.assertNotContainsRe(directory, r'\\/$')
703
682
def _set_path(self, *args):
704
683
path = os.pathsep.join(self._list2paths(*args))
705
self.overrideEnv('BZR_PLUGIN_PATH', path)
684
osutils.set_or_unset_env('BZR_PLUGIN_PATH', path)
707
686
def check_path(self, expected_dirs, setting_dirs):
789
768
self.addCleanup(self._unregister_plugin, 'test_foo')
791
770
def test_cannot_import(self):
792
self.overrideEnv('BZR_DISABLE_PLUGINS', 'test_foo')
771
osutils.set_or_unset_env('BZR_DISABLE_PLUGINS', 'test_foo')
793
772
plugin.set_plugins_path(['.'])
795
774
import bzrlib.plugins.test_foo
801
780
self.overrideAttr(plugin, '_loaded', False)
802
781
plugin.load_plugins(['.'])
803
782
self.assertPluginKnown('test_foo')
804
self.assertDocstring("This is the doc for test_foo",
805
bzrlib.plugins.test_foo)
783
self.assertEqual("This is the doc for test_foo",
784
bzrlib.plugins.test_foo.__doc__)
807
786
def test_not_loaded(self):
808
787
self.warnings = []
811
790
self.overrideAttr(trace, 'warning', captured_warning)
812
791
# Reset the flag that protect against double loading
813
792
self.overrideAttr(plugin, '_loaded', False)
814
self.overrideEnv('BZR_DISABLE_PLUGINS', 'test_foo')
793
osutils.set_or_unset_env('BZR_DISABLE_PLUGINS', 'test_foo')
815
794
plugin.load_plugins(['.'])
816
795
self.assertPluginUnknown('test_foo')
817
796
# Make sure we don't warn about the plugin ImportError since this has
819
798
self.assertLength(0, self.warnings)
823
class TestLoadPluginAtSyntax(tests.TestCase):
825
def _get_paths(self, paths):
826
return plugin._get_specific_plugin_paths(paths)
828
def test_empty(self):
829
self.assertEquals([], self._get_paths(None))
830
self.assertEquals([], self._get_paths(''))
832
def test_one_path(self):
833
self.assertEquals([('b', 'man')], self._get_paths('b@man'))
835
def test_bogus_path(self):
837
self.assertRaises(errors.BzrCommandError, self._get_paths, 'batman')
838
# Too much '@' isn't good either
839
self.assertRaises(errors.BzrCommandError, self._get_paths,
840
'batman@mobile@cave')
841
# An empty description probably indicates a problem
842
self.assertRaises(errors.BzrCommandError, self._get_paths,
843
os.pathsep.join(['batman@cave', '', 'robin@mobile']))
846
class TestLoadPluginAt(BaseTestPlugins):
801
class TestLoadPluginAt(tests.TestCaseInTempDir, TestPluginMixin):
849
804
super(TestLoadPluginAt, self).setUp()
850
805
# Make sure we don't pollute the plugins namespace
851
806
self.overrideAttr(plugins, '__path__')
807
# Be paranoid in case a test fail
808
self.addCleanup(self._unregister_plugin, 'test_foo')
852
809
# Reset the flag that protect against double loading
853
810
self.overrideAttr(plugin, '_loaded', False)
854
811
# Create the same plugin in two directories
856
813
# The "normal" directory, we use 'standard' instead of 'plugins' to
857
814
# avoid depending on the precise naming.
858
815
self.create_plugin_package('test_foo', dir='standard/test_foo')
859
# All the tests will load the 'test_foo' plugin from various locations
860
self.addCleanup(self._unregister_plugin, 'test_foo')
861
# Unfortunately there's global cached state for the specific
863
self.addCleanup(plugin.PluginImporter.reset)
865
817
def assertTestFooLoadedFrom(self, path):
866
818
self.assertPluginKnown('test_foo')
867
self.assertDocstring('This is the doc for test_foo',
868
bzrlib.plugins.test_foo)
819
self.assertEqual('This is the doc for test_foo',
820
bzrlib.plugins.test_foo.__doc__)
869
821
self.assertEqual(path, bzrlib.plugins.test_foo.dir_source)
871
823
def test_regular_load(self):
873
825
self.assertTestFooLoadedFrom('standard/test_foo')
875
827
def test_import(self):
876
self.overrideEnv('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
828
osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
877
829
plugin.set_plugins_path(['standard'])
879
831
import bzrlib.plugins.test_foo
882
834
self.assertTestFooLoadedFrom('non-standard-dir')
884
836
def test_loading(self):
885
self.overrideEnv('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
837
osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
886
838
plugin.load_plugins(['standard'])
887
839
self.assertTestFooLoadedFrom('non-standard-dir')
889
841
def test_compiled_loaded(self):
890
self.overrideEnv('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
842
osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
891
843
plugin.load_plugins(['standard'])
892
844
self.assertTestFooLoadedFrom('non-standard-dir')
893
self.assertIsSameRealPath('non-standard-dir/__init__.py',
894
bzrlib.plugins.test_foo.__file__)
845
self.assertEqual('non-standard-dir/__init__.py',
846
bzrlib.plugins.test_foo.__file__)
896
848
# Try importing again now that the source has been compiled
897
849
self._unregister_plugin('test_foo')
905
self.assertIsSameRealPath('non-standard-dir/__init__.%s' % suffix,
906
bzrlib.plugins.test_foo.__file__)
857
self.assertEqual('non-standard-dir/__init__.%s' % suffix,
858
bzrlib.plugins.test_foo.__file__)
908
860
def test_submodule_loading(self):
909
861
# We create an additional directory under the one for test_foo
910
862
self.create_plugin_package('test_bar', dir='non-standard-dir/test_bar')
911
self.addCleanup(self._unregister_plugin_submodule,
912
'test_foo', 'test_bar')
913
self.overrideEnv('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
863
osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
914
864
plugin.set_plugins_path(['standard'])
915
865
import bzrlib.plugins.test_foo
916
866
self.assertEqual('bzrlib.plugins.test_foo',
917
867
bzrlib.plugins.test_foo.__package__)
918
868
import bzrlib.plugins.test_foo.test_bar
919
self.assertIsSameRealPath('non-standard-dir/test_bar/__init__.py',
920
bzrlib.plugins.test_foo.test_bar.__file__)
922
def test_relative_submodule_loading(self):
923
self.create_plugin_package('test_foo', dir='another-dir', source='''
926
# We create an additional directory under the one for test_foo
927
self.create_plugin_package('test_bar', dir='another-dir/test_bar')
928
self.addCleanup(self._unregister_plugin_submodule,
929
'test_foo', 'test_bar')
930
self.overrideEnv('BZR_PLUGINS_AT', 'test_foo@another-dir')
931
plugin.set_plugins_path(['standard'])
932
import bzrlib.plugins.test_foo
933
self.assertEqual('bzrlib.plugins.test_foo',
934
bzrlib.plugins.test_foo.__package__)
935
self.assertIsSameRealPath('another-dir/test_bar/__init__.py',
936
bzrlib.plugins.test_foo.test_bar.__file__)
869
self.assertEqual('non-standard-dir/test_bar/__init__.py',
870
bzrlib.plugins.test_foo.test_bar.__file__)
938
872
def test_loading_from___init__only(self):
939
873
# We rename the existing __init__.py file to ensure that we don't load
942
876
random = 'non-standard-dir/setup.py'
943
877
os.rename(init, random)
944
878
self.addCleanup(os.rename, random, init)
945
self.overrideEnv('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
879
osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
946
880
plugin.load_plugins(['standard'])
947
881
self.assertPluginUnknown('test_foo')
956
890
''' % ('test_foo', plugin_path)
957
891
self.create_plugin('test_foo', source=source,
958
892
dir=plugin_dir, file_name=plugin_file_name)
959
self.overrideEnv('BZR_PLUGINS_AT', 'test_foo@%s' % plugin_path)
893
osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@%s' % plugin_path)
960
894
plugin.load_plugins(['standard'])
961
895
self.assertTestFooLoadedFrom(plugin_path)
964
class TestDescribePlugins(BaseTestPlugins):
966
def test_describe_plugins(self):
967
class DummyModule(object):
969
class DummyPlugin(object):
970
__version__ = '0.1.0'
971
module = DummyModule()
973
return { 'good': DummyPlugin() }
974
self.overrideAttr(plugin, 'plugin_warnings',
975
{'bad': ['Failed to load (just testing)']})
976
self.overrideAttr(plugin, 'plugins', dummy_plugins)
977
self.assertEquals("""\
979
** Failed to load (just testing)
984
""", ''.join(plugin.describe_plugins()))