71
# Workaround lazy import random? madness
73
self.addCleanup(cleanup)
74
108
self.create_plugin(name, source, dir,
75
109
file_name='__init__.py')
77
def _unregister_plugin(self, name):
78
"""Remove the plugin from sys.modules and the breezy namespace."""
79
py_name = 'breezy.plugins.%s' % name
80
if py_name in sys.modules:
81
del sys.modules[py_name]
82
if getattr(breezy.plugins, name, None) is not None:
83
delattr(breezy.plugins, name)
85
def _unregister_plugin_submodule(self, plugin_name, submodule_name):
86
"""Remove the submodule from sys.modules and the breezy namespace."""
87
py_name = 'breezy.plugins.%s.%s' % (plugin_name, submodule_name)
88
if py_name in sys.modules:
89
del sys.modules[py_name]
90
plugin = getattr(breezy.plugins, plugin_name, None)
91
if plugin is not None:
92
if getattr(plugin, submodule_name, None) is not None:
93
delattr(plugin, submodule_name)
111
def promote_cache(self, directory):
112
"""Move bytecode files out of __pycache__ in given directory."""
113
cache_dir = os.path.join(directory, '__pycache__')
114
if os.path.isdir(cache_dir):
115
for name in os.listdir(cache_dir):
116
magicless_name = '.'.join(name.split('.')[0::name.count('.')])
117
rel = osutils.relpath(self.test_dir, cache_dir)
118
self.log("moving %s in %s to %s", name, rel, magicless_name)
119
os.rename(os.path.join(cache_dir, name),
120
os.path.join(directory, magicless_name))
122
def _unregister_finder(self):
123
"""Removes any test copies of _PluginsAtFinder from sys.meta_path."""
124
idx = len(sys.meta_path)
127
finder = sys.meta_path[idx]
128
if getattr(finder, "prefix", "") == self.module_prefix:
129
self.log("removed %r from sys.meta_path", finder)
130
sys.meta_path.pop(idx)
132
def _unregister_all(self):
133
"""Remove all plugins in the test namespace from sys.modules."""
134
for name in list(sys.modules):
135
if name.startswith(self.module_prefix) or name == self.module_name:
136
self.log("removed %s from sys.modules", name)
137
del sys.modules[name]
139
def assertPluginModules(self, plugin_dict):
141
dict((k[len(self.module_prefix):], sys.modules[k])
142
for k in sys.modules if k.startswith(self.module_prefix)),
95
145
def assertPluginUnknown(self, name):
96
self.assertFalse(getattr(breezy.plugins, name, None) is not None)
97
self.assertFalse('breezy.plugins.%s' % name in sys.modules)
146
self.assertTrue(getattr(self.module, name, None) is None)
147
self.assertFalse(self.module_prefix + name in sys.modules)
99
149
def assertPluginKnown(self, name):
100
self.assertTrue(getattr(breezy.plugins, name, None) is not None)
101
self.assertTrue('breezy.plugins.%s' % name in sys.modules)
150
self.assertTrue(getattr(self.module, name, None) is not None)
151
self.assertTrue(self.module_prefix + name in sys.modules)
104
154
class TestLoadingPlugins(BaseTestPlugins):
631
630
self.assertEqual('foo_bar', topic.get_help_topic())
634
class TestLoadFromPath(tests.TestCaseInTempDir):
637
super(TestLoadFromPath, self).setUp()
638
# Change breezy.plugin to think no plugins have been loaded yet.
639
self.overrideAttr(breezy.plugins, '__path__', [])
640
self.overrideAttr(plugin, '_loaded', False)
642
# Monkey-patch load_from_path to stop it from actually loading anything.
643
self.overrideAttr(plugin, 'load_from_path', lambda dirs: None)
645
def test_set_plugins_path_with_args(self):
646
plugin.set_plugins_path(['a', 'b'])
647
self.assertEqual(['a', 'b'], breezy.plugins.__path__)
649
def test_set_plugins_path_defaults(self):
650
plugin.set_plugins_path()
651
self.assertEqual(plugin.get_standard_plugins_path(),
652
breezy.plugins.__path__)
654
def test_get_standard_plugins_path(self):
655
path = plugin.get_standard_plugins_path()
656
for directory in path:
657
self.assertNotContainsRe(directory, r'\\/$')
659
from distutils.sysconfig import get_python_lib
663
if sys.platform != 'win32':
664
python_lib = get_python_lib()
665
for directory in path:
666
if directory.startswith(python_lib):
669
self.fail('No path to global plugins')
671
def test_get_standard_plugins_path_env(self):
672
self.overrideEnv('BRZ_PLUGIN_PATH', 'foo/')
673
path = plugin.get_standard_plugins_path()
674
for directory in path:
675
self.assertNotContainsRe(directory, r'\\/$')
677
def test_load_plugins(self):
678
plugin.load_plugins(['.'])
679
self.assertEqual(breezy.plugins.__path__, ['.'])
680
# subsequent loads are no-ops
681
plugin.load_plugins(['foo'])
682
self.assertEqual(breezy.plugins.__path__, ['.'])
684
def test_load_plugins_default(self):
685
plugin.load_plugins()
686
path = plugin.get_standard_plugins_path()
687
self.assertEqual(path, breezy.plugins.__path__)
690
633
class TestEnvPluginPath(tests.TestCase):
693
super(TestEnvPluginPath, self).setUp()
694
self.overrideAttr(plugin, 'DEFAULT_PLUGIN_PATH', None)
696
self.user = plugin.get_user_plugin_path()
697
self.site = plugin.get_site_plugin_path()
698
self.core = plugin.get_core_plugin_path()
700
def _list2paths(self, *args):
703
plugin._append_new_path(paths, p)
706
def _set_path(self, *args):
707
path = os.pathsep.join(self._list2paths(*args))
708
self.overrideEnv('BRZ_PLUGIN_PATH', path)
710
639
def check_path(self, expected_dirs, setting_dirs):
712
self._set_path(*setting_dirs)
713
actual = plugin.get_standard_plugins_path()
714
self.assertEqual(self._list2paths(*expected_dirs), actual)
640
if setting_dirs is None:
641
del os.environ['BRZ_PLUGIN_PATH']
643
os.environ['BRZ_PLUGIN_PATH'] = os.pathsep.join(setting_dirs)
644
actual = [(p if t == 'path' else t.upper())
645
for p, t in plugin._env_plugin_path()]
646
self.assertEqual(expected_dirs, actual)
716
648
def test_default(self):
717
649
self.check_path([self.user, self.core, self.site],
784
716
class TestDisablePlugin(BaseTestPlugins):
787
super(TestDisablePlugin, self).setUp()
788
self.create_plugin_package('test_foo')
789
# Make sure we don't pollute the plugins namespace
790
self.overrideAttr(plugins, '__path__')
791
# Be paranoid in case a test fail
792
self.addCleanup(self._unregister_plugin, 'test_foo')
794
718
def test_cannot_import(self):
795
self.overrideEnv('BRZ_DISABLE_PLUGINS', 'test_foo')
796
plugin.set_plugins_path(['.'])
719
self.create_plugin_package('works')
720
self.create_plugin_package('fails')
721
self.overrideEnv('BRZ_DISABLE_PLUGINS', 'fails')
722
self.update_module_paths(["."])
723
import breezy.testingplugins.works as works
798
import breezy.plugins.test_foo
725
import breezy.testingplugins.fails as fails
799
726
except ImportError:
801
self.assertPluginUnknown('test_foo')
803
def test_regular_load(self):
804
self.overrideAttr(plugin, '_loaded', False)
805
plugin.load_plugins(['.'])
806
self.assertPluginKnown('test_foo')
807
self.assertDocstring("This is the doc for test_foo",
808
breezy.plugins.test_foo)
810
def test_not_loaded(self):
812
def captured_warning(*args, **kwargs):
813
self.warnings.append((args, kwargs))
814
self.overrideAttr(trace, 'warning', captured_warning)
815
# Reset the flag that protect against double loading
816
self.overrideAttr(plugin, '_loaded', False)
817
self.overrideEnv('BRZ_DISABLE_PLUGINS', 'test_foo')
818
plugin.load_plugins(['.'])
819
self.assertPluginUnknown('test_foo')
820
# Make sure we don't warn about the plugin ImportError since this has
821
# been *requested* by the user.
822
self.assertLength(0, self.warnings)
826
class TestLoadPluginAtSyntax(tests.TestCase):
828
def _get_paths(self, paths):
829
return plugin._get_specific_plugin_paths(paths)
831
def test_empty(self):
832
self.assertEqual([], self._get_paths(None))
729
self.fail("Loaded blocked plugin: " + repr(fails))
730
self.assertPluginModules({'fails': None, 'works': works})
732
def test_partial_imports(self):
733
self.create_plugin('good')
734
self.create_plugin('bad')
735
self.create_plugin_package('ugly')
736
self.overrideEnv('BRZ_DISABLE_PLUGINS', 'bad:ugly')
737
self.load_with_paths(['.'])
738
self.assertEqual({'good'}, viewkeys(self.plugins))
739
self.assertPluginModules({
740
'good': self.plugins['good'].module,
744
# Ensure there are no warnings about plugins not being imported as
745
# the user has explictly requested they be disabled.
746
self.assertNotContainsRe(self.get_log(), r"Unable to load plugin")
749
class TestEnvDisablePlugins(tests.TestCase):
751
def _get_names(self, env_value):
752
os.environ['BRZ_DISABLE_PLUGINS'] = env_value
753
return plugin._env_disable_plugins()
755
def test_unset(self):
756
self.assertEqual([], plugin._env_disable_plugins())
758
def test_empty(self):
759
self.assertEqual([], self._get_names(''))
761
def test_single(self):
762
self.assertEqual(['single'], self._get_names('single'))
764
def test_multi(self):
765
expected = ['one', 'two']
766
self.assertEqual(expected, self._get_names(os.pathsep.join(expected)))
768
def test_mixed(self):
769
value = os.pathsep.join(['valid', 'in-valid'])
770
self.assertEqual(['valid'], self._get_names(value))
771
self.assertContainsRe(self.get_log(),
772
r"Invalid name 'in-valid' in BRZ_DISABLE_PLUGINS=" + repr(value))
775
class TestEnvPluginsAt(tests.TestCase):
777
def _get_paths(self, env_value):
778
os.environ['BRZ_PLUGINS_AT'] = env_value
779
return plugin._env_plugins_at()
781
def test_empty(self):
782
self.assertEqual([], plugin._env_plugins_at())
833
783
self.assertEqual([], self._get_paths(''))
835
785
def test_one_path(self):
836
786
self.assertEqual([('b', 'man')], self._get_paths('b@man'))
838
def test_bogus_path(self):
840
self.assertRaises(errors.BzrCommandError, self._get_paths, 'batman')
841
# Too much '@' isn't good either
842
self.assertRaises(errors.BzrCommandError, self._get_paths,
843
'batman@mobile@cave')
844
# An empty description probably indicates a problem
845
self.assertRaises(errors.BzrCommandError, self._get_paths,
846
os.pathsep.join(['batman@cave', '', 'robin@mobile']))
788
def test_multiple(self):
790
[('tools', 'bzr-tools'), ('p', 'play.py')],
791
self._get_paths(os.pathsep.join(('tools@bzr-tools', 'p@play.py'))))
793
def test_many_at(self):
795
[('church', 'StMichael@Plea@Norwich')],
796
self._get_paths('church@StMichael@Plea@Norwich'))
798
def test_only_py(self):
799
self.assertEqual([('test', './test.py')], self._get_paths('./test.py'))
801
def test_only_package(self):
802
self.assertEqual([('py', '/opt/b/py')], self._get_paths('/opt/b/py'))
804
def test_bad_name(self):
805
self.assertEqual([], self._get_paths('/usr/local/bzr-git'))
806
self.assertContainsRe(self.get_log(),
807
r"Invalid name 'bzr-git' in BRZ_PLUGINS_AT='/usr/local/bzr-git'")
849
810
class TestLoadPluginAt(BaseTestPlugins):
852
813
super(TestLoadPluginAt, self).setUp()
853
# Make sure we don't pollute the plugins namespace
854
self.overrideAttr(plugins, '__path__')
855
# Reset the flag that protect against double loading
856
self.overrideAttr(plugin, '_loaded', False)
857
814
# Create the same plugin in two directories
858
815
self.create_plugin_package('test_foo', dir='non-standard-dir')
859
816
# The "normal" directory, we use 'standard' instead of 'plugins' to
860
817
# avoid depending on the precise naming.
861
818
self.create_plugin_package('test_foo', dir='standard/test_foo')
862
# All the tests will load the 'test_foo' plugin from various locations
863
self.addCleanup(self._unregister_plugin, 'test_foo')
864
# Unfortunately there's global cached state for the specific
866
self.addCleanup(plugin.PluginImporter.reset)
868
820
def assertTestFooLoadedFrom(self, path):
869
821
self.assertPluginKnown('test_foo')
870
822
self.assertDocstring('This is the doc for test_foo',
871
breezy.plugins.test_foo)
872
self.assertEqual(path, breezy.plugins.test_foo.dir_source)
823
self.module.test_foo)
824
self.assertEqual(path, self.module.test_foo.dir_source)
874
826
def test_regular_load(self):
875
plugin.load_plugins(['standard'])
827
self.load_with_paths(['standard'])
876
828
self.assertTestFooLoadedFrom('standard/test_foo')
878
830
def test_import(self):
879
831
self.overrideEnv('BRZ_PLUGINS_AT', 'test_foo@non-standard-dir')
880
plugin.set_plugins_path(['standard'])
882
import breezy.plugins.test_foo
832
self.update_module_paths(['standard'])
833
import breezy.testingplugins.test_foo
885
834
self.assertTestFooLoadedFrom('non-standard-dir')
887
836
def test_loading(self):
888
837
self.overrideEnv('BRZ_PLUGINS_AT', 'test_foo@non-standard-dir')
889
plugin.load_plugins(['standard'])
838
self.load_with_paths(['standard'])
839
self.assertTestFooLoadedFrom('non-standard-dir')
841
def test_loading_other_name(self):
842
self.overrideEnv('BRZ_PLUGINS_AT', 'test_foo@non-standard-dir')
843
os.rename('standard/test_foo', 'standard/test_bar')
844
self.load_with_paths(['standard'])
890
845
self.assertTestFooLoadedFrom('non-standard-dir')
892
847
def test_compiled_loaded(self):
893
848
self.overrideEnv('BRZ_PLUGINS_AT', 'test_foo@non-standard-dir')
894
plugin.load_plugins(['standard'])
849
self.load_with_paths(['standard'])
895
850
self.assertTestFooLoadedFrom('non-standard-dir')
896
851
self.assertIsSameRealPath('non-standard-dir/__init__.py',
897
breezy.plugins.test_foo.__file__)
852
self.module.test_foo.__file__)
899
854
# Try importing again now that the source has been compiled
900
self._unregister_plugin('test_foo')
901
plugin._loaded = False
902
plugin.load_plugins(['standard'])
855
os.remove('non-standard-dir/__init__.py')
856
self.promote_cache('non-standard-dir')
858
self.load_with_paths(['standard'])
903
859
self.assertTestFooLoadedFrom('non-standard-dir')
908
self.assertIsSameRealPath('non-standard-dir/__init__.%s' % suffix,
909
breezy.plugins.test_foo.__file__)
860
suffix = plugin.COMPILED_EXT
861
self.assertIsSameRealPath('non-standard-dir/__init__' + suffix,
862
self.module.test_foo.__file__)
911
864
def test_submodule_loading(self):
912
865
# We create an additional directory under the one for test_foo
913
866
self.create_plugin_package('test_bar', dir='non-standard-dir/test_bar')
914
self.addCleanup(self._unregister_plugin_submodule,
915
'test_foo', 'test_bar')
916
867
self.overrideEnv('BRZ_PLUGINS_AT', 'test_foo@non-standard-dir')
917
plugin.set_plugins_path(['standard'])
918
import breezy.plugins.test_foo
919
self.assertEqual('breezy.plugins.test_foo',
920
breezy.plugins.test_foo.__package__)
921
import breezy.plugins.test_foo.test_bar
868
self.update_module_paths(['standard'])
869
import breezy.testingplugins.test_foo
870
self.assertEqual(self.module_prefix + 'test_foo',
871
self.module.test_foo.__package__)
872
import breezy.testingplugins.test_foo.test_bar
922
873
self.assertIsSameRealPath('non-standard-dir/test_bar/__init__.py',
923
breezy.plugins.test_foo.test_bar.__file__)
874
self.module.test_foo.test_bar.__file__)
925
876
def test_relative_submodule_loading(self):
926
877
self.create_plugin_package('test_foo', dir='another-dir', source='''
878
from . import test_bar
929
880
# We create an additional directory under the one for test_foo
930
881
self.create_plugin_package('test_bar', dir='another-dir/test_bar')
931
self.addCleanup(self._unregister_plugin_submodule,
932
'test_foo', 'test_bar')
933
882
self.overrideEnv('BRZ_PLUGINS_AT', 'test_foo@another-dir')
934
plugin.set_plugins_path(['standard'])
935
import breezy.plugins.test_foo
936
self.assertEqual('breezy.plugins.test_foo',
937
breezy.plugins.test_foo.__package__)
883
self.update_module_paths(['standard'])
884
import breezy.testingplugins.test_foo
885
self.assertEqual(self.module_prefix + 'test_foo',
886
self.module.test_foo.__package__)
938
887
self.assertIsSameRealPath('another-dir/test_bar/__init__.py',
939
breezy.plugins.test_foo.test_bar.__file__)
888
self.module.test_foo.test_bar.__file__)
941
890
def test_loading_from___init__only(self):
942
891
# We rename the existing __init__.py file to ensure that we don't load