/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_plugins.py

  • Committer: Jelmer Vernooij
  • Date: 2020-09-02 16:35:18 UTC
  • mto: (7490.40.109 work)
  • mto: This revision was merged to the branch mainline in revision 7526.
  • Revision ID: jelmer@jelmer.uk-20200902163518-sy9f4unbboljphgu
Handle duplicate directories entries for git.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2012, 2016 Canonical Ltd
 
2
# Copyright (C) 2017-2018 Breezy developers
2
3
#
3
4
# This program is free software; you can redistribute it and/or modify
4
5
# it under the terms of the GNU General Public License as published by
16
17
 
17
18
"""Tests for plugins"""
18
19
 
19
 
# XXX: There are no plugin tests at the moment because the plugin module
20
 
# affects the global state of the process.  See bzrlib/plugins.py for more
21
 
# comments.
22
 
 
23
 
from cStringIO import StringIO
 
20
import importlib
24
21
import logging
25
22
import os
26
23
import sys
 
24
import types
27
25
 
28
 
import bzrlib
29
 
from bzrlib import (
 
26
import breezy
 
27
from .. import (
30
28
    osutils,
31
29
    plugin,
32
 
    plugins,
33
30
    tests,
34
 
    trace,
 
31
    )
 
32
from ..tests.features import pkg_resources_feature
 
33
from ..sixish import (
 
34
    StringIO,
 
35
    viewkeys,
35
36
    )
36
37
 
37
38
 
38
39
# TODO: Write a test for plugin decoration of commands.
39
40
 
40
 
class TestPluginMixin(object):
 
41
invalidate_caches = getattr(importlib, "invalidate_caches", lambda: None)
 
42
 
 
43
 
 
44
class BaseTestPlugins(tests.TestCaseInTempDir):
 
45
    """TestCase that isolates plugin imports and cleans up on completion."""
 
46
 
 
47
    def setUp(self):
 
48
        super(BaseTestPlugins, self).setUp()
 
49
        self.module_name = "breezy.testingplugins"
 
50
        self.module_prefix = self.module_name + "."
 
51
        self.module = types.ModuleType(self.module_name)
 
52
 
 
53
        self.overrideAttr(plugin, "_MODULE_PREFIX", self.module_prefix)
 
54
        self.overrideAttr(breezy, "testingplugins", self.module)
 
55
 
 
56
        sys.modules[self.module_name] = self.module
 
57
        self.addCleanup(self._unregister_all)
 
58
        self.addCleanup(self._unregister_finder)
 
59
 
 
60
        invalidate_caches()
 
61
 
 
62
    def reset(self):
 
63
        """Remove all global testing state and clean up module."""
 
64
        # GZ 2017-06-02: Ideally don't do this, write new test or generate
 
65
        # bytecode by other mechanism.
 
66
        self.log("resetting plugin testing context")
 
67
        self._unregister_all()
 
68
        self._unregister_finder()
 
69
        sys.modules[self.module_name] = self.module
 
70
        for name in list(self.module.__dict__):
 
71
            if name[:2] != '__':
 
72
                delattr(self.module, name)
 
73
        invalidate_caches()
 
74
        self.plugins = None
 
75
 
 
76
    def update_module_paths(self, paths):
 
77
        paths = plugin.extend_path(paths, self.module_name)
 
78
        self.module.__path__ = paths
 
79
        self.log("using %r", paths)
 
80
        return paths
 
81
 
 
82
    def load_with_paths(self, paths, warn_load_problems=True):
 
83
        self.log("loading plugins!")
 
84
        plugin.load_plugins(
 
85
            self.update_module_paths(paths), state=self,
 
86
            warn_load_problems=warn_load_problems)
41
87
 
42
88
    def create_plugin(self, name, source=None, dir='.', file_name=None):
43
89
        if source is None:
48
94
            file_name = name + '.py'
49
95
        # 'source' must not fail to load
50
96
        path = osutils.pathjoin(dir, file_name)
51
 
        f = open(path, 'w')
52
 
        self.addCleanup(os.unlink, path)
53
 
        try:
 
97
        with open(path, 'w') as f:
54
98
            f.write(source + '\n')
55
 
        finally:
56
 
            f.close()
57
99
 
58
100
    def create_plugin_package(self, name, dir=None, source=None):
59
101
        if dir is None:
64
106
dir_source = '%s'
65
107
''' % (name, dir)
66
108
        os.makedirs(dir)
67
 
        def cleanup():
68
 
            # Workaround lazy import random? madness
69
 
            osutils.rmtree(dir)
70
 
        self.addCleanup(cleanup)
71
109
        self.create_plugin(name, source, dir,
72
110
                           file_name='__init__.py')
73
111
 
74
 
    def _unregister_plugin(self, name):
75
 
        """Remove the plugin from sys.modules and the bzrlib namespace."""
76
 
        py_name = 'bzrlib.plugins.%s' % name
77
 
        if py_name in sys.modules:
78
 
            del sys.modules[py_name]
79
 
        if getattr(bzrlib.plugins, name, None) is not None:
80
 
            delattr(bzrlib.plugins, name)
 
112
    def promote_cache(self, directory):
 
113
        """Move bytecode files out of __pycache__ in given directory."""
 
114
        cache_dir = os.path.join(directory, '__pycache__')
 
115
        if os.path.isdir(cache_dir):
 
116
            for name in os.listdir(cache_dir):
 
117
                magicless_name = '.'.join(name.split('.')[0::name.count('.')])
 
118
                rel = osutils.relpath(self.test_dir, cache_dir)
 
119
                self.log("moving %s in %s to %s", name, rel, magicless_name)
 
120
                os.rename(os.path.join(cache_dir, name),
 
121
                          os.path.join(directory, magicless_name))
 
122
 
 
123
    def _unregister_finder(self):
 
124
        """Removes any test copies of _PluginsAtFinder from sys.meta_path."""
 
125
        idx = len(sys.meta_path)
 
126
        while idx:
 
127
            idx -= 1
 
128
            finder = sys.meta_path[idx]
 
129
            if getattr(finder, "prefix", "") == self.module_prefix:
 
130
                self.log("removed %r from sys.meta_path", finder)
 
131
                sys.meta_path.pop(idx)
 
132
 
 
133
    def _unregister_all(self):
 
134
        """Remove all plugins in the test namespace from sys.modules."""
 
135
        for name in list(sys.modules):
 
136
            if name.startswith(self.module_prefix) or name == self.module_name:
 
137
                self.log("removed %s from sys.modules", name)
 
138
                del sys.modules[name]
 
139
 
 
140
    def assertPluginModules(self, plugin_dict):
 
141
        self.assertEqual(
 
142
            dict((k[len(self.module_prefix):], sys.modules[k])
 
143
                 for k in sys.modules if k.startswith(self.module_prefix)),
 
144
            plugin_dict)
81
145
 
82
146
    def assertPluginUnknown(self, name):
83
 
        self.failIf(getattr(bzrlib.plugins, name, None) is not None)
84
 
        self.failIf('bzrlib.plugins.%s' % name in sys.modules)
 
147
        self.assertTrue(getattr(self.module, name, None) is None)
 
148
        self.assertFalse(self.module_prefix + name in sys.modules)
85
149
 
86
150
    def assertPluginKnown(self, name):
87
 
        self.failUnless(getattr(bzrlib.plugins, name, None) is not None)
88
 
        self.failUnless('bzrlib.plugins.%s' % name in sys.modules)
89
 
 
90
 
 
91
 
class TestLoadingPlugins(tests.TestCaseInTempDir, TestPluginMixin):
 
151
        self.assertTrue(getattr(self.module, name, None) is not None)
 
152
        self.assertTrue(self.module_prefix + name in sys.modules)
 
153
 
 
154
 
 
155
class TestLoadingPlugins(BaseTestPlugins):
92
156
 
93
157
    activeattributes = {}
94
158
 
98
162
        # file name we can use which is also a valid attribute for accessing in
99
163
        # activeattributes. - we cannot give import parameters.
100
164
        tempattribute = "0"
101
 
        self.failIf(tempattribute in self.activeattributes)
 
165
        self.assertFalse(tempattribute in self.activeattributes)
102
166
        # set a place for the plugins to record their loading, and at the same
103
167
        # time validate that the location the plugins should record to is
104
168
        # valid and correct.
105
 
        self.__class__.activeattributes [tempattribute] = []
106
 
        self.failUnless(tempattribute in self.activeattributes)
 
169
        self.__class__.activeattributes[tempattribute] = []
 
170
        self.assertTrue(tempattribute in self.activeattributes)
107
171
        # create two plugin directories
108
172
        os.mkdir('first')
109
173
        os.mkdir('second')
110
174
        # write a plugin that will record when its loaded in the
111
175
        # tempattribute list.
112
 
        template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
 
176
        template = ("from breezy.tests.test_plugins import TestLoadingPlugins\n"
113
177
                    "TestLoadingPlugins.activeattributes[%r].append('%s')\n")
114
178
 
115
 
        outfile = open(os.path.join('first', 'plugin.py'), 'w')
116
 
        try:
 
179
        with open(os.path.join('first', 'plugin.py'), 'w') as outfile:
117
180
            outfile.write(template % (tempattribute, 'first'))
118
181
            outfile.write('\n')
119
 
        finally:
120
 
            outfile.close()
121
182
 
122
 
        outfile = open(os.path.join('second', 'plugin.py'), 'w')
123
 
        try:
 
183
        with open(os.path.join('second', 'plugin.py'), 'w') as outfile:
124
184
            outfile.write(template % (tempattribute, 'second'))
125
185
            outfile.write('\n')
126
 
        finally:
127
 
            outfile.close()
128
186
 
129
187
        try:
130
 
            bzrlib.plugin.load_from_path(['first', 'second'])
 
188
            self.load_with_paths(['first', 'second'])
131
189
            self.assertEqual(['first'], self.activeattributes[tempattribute])
132
190
        finally:
133
 
            # remove the plugin 'plugin'
134
191
            del self.activeattributes[tempattribute]
135
 
            self._unregister_plugin('plugin')
136
 
        self.assertPluginUnknown('plugin')
137
192
 
138
193
    def test_plugins_from_different_dirs_can_demand_load(self):
139
 
        self.failIf('bzrlib.plugins.pluginone' in sys.modules)
140
 
        self.failIf('bzrlib.plugins.plugintwo' in sys.modules)
 
194
        self.assertFalse('breezy.plugins.pluginone' in sys.modules)
 
195
        self.assertFalse('breezy.plugins.plugintwo' in sys.modules)
141
196
        # This test tests that having two plugins in different
142
197
        # directories with different names allows them both to be loaded, when
143
198
        # we do a direct import statement.
144
199
        # Determine a file name we can use which is also a valid attribute
145
200
        # for accessing in activeattributes. - we cannot give import parameters.
146
201
        tempattribute = "different-dirs"
147
 
        self.failIf(tempattribute in self.activeattributes)
 
202
        self.assertFalse(tempattribute in self.activeattributes)
148
203
        # set a place for the plugins to record their loading, and at the same
149
204
        # time validate that the location the plugins should record to is
150
205
        # valid and correct.
151
 
        bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
152
 
            [tempattribute] = []
153
 
        self.failUnless(tempattribute in self.activeattributes)
 
206
        breezy.tests.test_plugins.TestLoadingPlugins.activeattributes[tempattribute] = [
 
207
            ]
 
208
        self.assertTrue(tempattribute in self.activeattributes)
154
209
        # create two plugin directories
155
210
        os.mkdir('first')
156
211
        os.mkdir('second')
157
212
        # write plugins that will record when they are loaded in the
158
213
        # tempattribute list.
159
 
        template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
 
214
        template = ("from breezy.tests.test_plugins import TestLoadingPlugins\n"
160
215
                    "TestLoadingPlugins.activeattributes[%r].append('%s')\n")
161
216
 
162
 
        outfile = open(os.path.join('first', 'pluginone.py'), 'w')
163
 
        try:
 
217
        with open(os.path.join('first', 'pluginone.py'), 'w') as outfile:
164
218
            outfile.write(template % (tempattribute, 'first'))
165
219
            outfile.write('\n')
166
 
        finally:
167
 
            outfile.close()
168
220
 
169
 
        outfile = open(os.path.join('second', 'plugintwo.py'), 'w')
170
 
        try:
 
221
        with open(os.path.join('second', 'plugintwo.py'), 'w') as outfile:
171
222
            outfile.write(template % (tempattribute, 'second'))
172
223
            outfile.write('\n')
173
 
        finally:
174
 
            outfile.close()
175
224
 
176
 
        oldpath = bzrlib.plugins.__path__
177
225
        try:
178
 
            self.failIf('bzrlib.plugins.pluginone' in sys.modules)
179
 
            self.failIf('bzrlib.plugins.plugintwo' in sys.modules)
180
 
            bzrlib.plugins.__path__ = ['first', 'second']
181
 
            exec "import bzrlib.plugins.pluginone"
 
226
            self.assertPluginUnknown('pluginone')
 
227
            self.assertPluginUnknown('plugintwo')
 
228
            self.update_module_paths(['first', 'second'])
 
229
            exec("import %spluginone" % self.module_prefix)
182
230
            self.assertEqual(['first'], self.activeattributes[tempattribute])
183
 
            exec "import bzrlib.plugins.plugintwo"
 
231
            exec("import %splugintwo" % self.module_prefix)
184
232
            self.assertEqual(['first', 'second'],
185
 
                self.activeattributes[tempattribute])
 
233
                             self.activeattributes[tempattribute])
186
234
        finally:
187
 
            # remove the plugin 'plugin'
188
235
            del self.activeattributes[tempattribute]
189
 
            self._unregister_plugin('pluginone')
190
 
            self._unregister_plugin('plugintwo')
191
 
        self.assertPluginUnknown('pluginone')
192
 
        self.assertPluginUnknown('plugintwo')
193
236
 
194
237
    def test_plugins_can_load_from_directory_with_trailing_slash(self):
195
238
        # This test tests that a plugin can load from a directory when the
197
240
        # check the plugin is not loaded already
198
241
        self.assertPluginUnknown('ts_plugin')
199
242
        tempattribute = "trailing-slash"
200
 
        self.failIf(tempattribute in self.activeattributes)
 
243
        self.assertFalse(tempattribute in self.activeattributes)
201
244
        # set a place for the plugin to record its loading, and at the same
202
245
        # time validate that the location the plugin should record to is
203
246
        # valid and correct.
204
 
        bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
205
 
            [tempattribute] = []
206
 
        self.failUnless(tempattribute in self.activeattributes)
 
247
        breezy.tests.test_plugins.TestLoadingPlugins.activeattributes[tempattribute] = [
 
248
            ]
 
249
        self.assertTrue(tempattribute in self.activeattributes)
207
250
        # create a directory for the plugin
208
251
        os.mkdir('plugin_test')
209
252
        # write a plugin that will record when its loaded in the
210
253
        # tempattribute list.
211
 
        template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
 
254
        template = ("from breezy.tests.test_plugins import TestLoadingPlugins\n"
212
255
                    "TestLoadingPlugins.activeattributes[%r].append('%s')\n")
213
256
 
214
 
        outfile = open(os.path.join('plugin_test', 'ts_plugin.py'), 'w')
215
 
        try:
 
257
        with open(os.path.join('plugin_test', 'ts_plugin.py'), 'w') as outfile:
216
258
            outfile.write(template % (tempattribute, 'plugin'))
217
259
            outfile.write('\n')
218
 
        finally:
219
 
            outfile.close()
220
260
 
221
261
        try:
222
 
            bzrlib.plugin.load_from_path(['plugin_test'+os.sep])
 
262
            self.load_with_paths(['plugin_test' + os.sep])
223
263
            self.assertEqual(['plugin'], self.activeattributes[tempattribute])
 
264
            self.assertPluginKnown('ts_plugin')
224
265
        finally:
225
266
            del self.activeattributes[tempattribute]
226
 
            self._unregister_plugin('ts_plugin')
227
 
        self.assertPluginUnknown('ts_plugin')
228
267
 
229
 
    def load_and_capture(self, name):
 
268
    def load_and_capture(self, name, warn_load_problems=True):
230
269
        """Load plugins from '.' capturing the output.
231
270
 
232
271
        :param name: The name of the plugin.
236
275
        stream = StringIO()
237
276
        try:
238
277
            handler = logging.StreamHandler(stream)
239
 
            log = logging.getLogger('bzr')
 
278
            log = logging.getLogger('brz')
240
279
            log.addHandler(handler)
241
280
            try:
242
 
                try:
243
 
                    bzrlib.plugin.load_from_path(['.'])
244
 
                finally:
245
 
                    if 'bzrlib.plugins.%s' % name in sys.modules:
246
 
                        del sys.modules['bzrlib.plugins.%s' % name]
247
 
                    if getattr(bzrlib.plugins, name, None):
248
 
                        delattr(bzrlib.plugins, name)
 
281
                self.load_with_paths(
 
282
                    ['.'], warn_load_problems=warn_load_problems)
249
283
            finally:
250
284
                # Stop capturing output
251
285
                handler.flush()
256
290
            stream.close()
257
291
 
258
292
    def test_plugin_with_bad_api_version_reports(self):
259
 
        # This plugin asks for bzrlib api version 1.0.0, which is not supported
260
 
        # anymore.
 
293
        """Try loading a plugin that requests an unsupported api.
 
294
 
 
295
        Observe that it records the problem but doesn't complain on stderr
 
296
        when warn_load_problems=False
 
297
        """
261
298
        name = 'wants100.py'
262
 
        f = file(name, 'w')
263
 
        try:
264
 
            f.write("import bzrlib.api\n"
265
 
                "bzrlib.api.require_any_api(bzrlib, [(1, 0, 0)])\n")
266
 
        finally:
267
 
            f.close()
268
 
 
269
 
        log = self.load_and_capture(name)
270
 
        self.assertContainsRe(log,
271
 
            r"It requested API version")
 
299
        with open(name, 'w') as f:
 
300
            f.write("import breezy\n"
 
301
                    "from breezy.errors import IncompatibleVersion\n"
 
302
                    "raise IncompatibleVersion(breezy, [(1, 0, 0)], (0, 0, 5))\n")
 
303
        log = self.load_and_capture(name, warn_load_problems=False)
 
304
        self.assertNotContainsRe(log, r"It supports breezy version")
 
305
        self.assertEqual({'wants100'}, viewkeys(self.plugin_warnings))
 
306
        self.assertContainsRe(
 
307
            self.plugin_warnings['wants100'][0],
 
308
            r"It supports breezy version")
272
309
 
273
310
    def test_plugin_with_bad_name_does_not_load(self):
274
311
        # The file name here invalid for a python module.
275
 
        name = 'bzr-bad plugin-name..py'
276
 
        file(name, 'w').close()
 
312
        name = 'brz-bad plugin-name..py'
 
313
        open(name, 'w').close()
277
314
        log = self.load_and_capture(name)
278
315
        self.assertContainsRe(log,
279
 
            r"Unable to load 'bzr-bad plugin-name\.' in '\.' as a plugin "
280
 
            "because the file path isn't a valid module name; try renaming "
281
 
            "it to 'bad_plugin_name_'\.")
282
 
 
283
 
 
284
 
class TestPlugins(tests.TestCaseInTempDir, TestPluginMixin):
 
316
                              r"Unable to load 'brz-bad plugin-name\.' in '\.' as a plugin "
 
317
                              "because the file path isn't a valid module name; try renaming "
 
318
                              "it to 'bad_plugin_name_'\\.")
 
319
 
 
320
    def test_plugin_with_error_suppress(self):
 
321
        # The file name here invalid for a python module.
 
322
        name = 'some_error.py'
 
323
        with open(name, 'w') as f:
 
324
            f.write('raise Exception("bad")\n')
 
325
        log = self.load_and_capture(name, warn_load_problems=False)
 
326
        self.assertEqual('', log)
 
327
 
 
328
    def test_plugin_with_error(self):
 
329
        # The file name here invalid for a python module.
 
330
        name = 'some_error.py'
 
331
        with open(name, 'w') as f:
 
332
            f.write('raise Exception("bad")\n')
 
333
        log = self.load_and_capture(name, warn_load_problems=True)
 
334
        self.assertEqual(
 
335
            'Unable to load plugin \'some_error\' from \'.\': bad\n', log)
 
336
 
 
337
 
 
338
class TestPlugins(BaseTestPlugins):
285
339
 
286
340
    def setup_plugin(self, source=""):
287
 
        # This test tests a new plugin appears in bzrlib.plugin.plugins().
 
341
        # This test tests a new plugin appears in breezy.plugin.plugins().
288
342
        # check the plugin is not loaded already
289
343
        self.assertPluginUnknown('plugin')
290
344
        # write a plugin that _cannot_ fail to load.
291
 
        file('plugin.py', 'w').write(source + '\n')
292
 
        self.addCleanup(self.teardown_plugin)
293
 
        plugin.load_from_path(['.'])
294
 
 
295
 
    def teardown_plugin(self):
296
 
        self._unregister_plugin('plugin')
297
 
        self.assertPluginUnknown('plugin')
 
345
        with open('plugin.py', 'w') as f:
 
346
            f.write(source + '\n')
 
347
        self.load_with_paths(['.'])
 
348
 
 
349
    def test_plugin_loaded(self):
 
350
        self.assertPluginUnknown('plugin')
 
351
        self.assertIs(None, breezy.plugin.get_loaded_plugin('plugin'))
 
352
        self.setup_plugin()
 
353
        p = breezy.plugin.get_loaded_plugin('plugin')
 
354
        self.assertIsInstance(p, breezy.plugin.PlugIn)
 
355
        self.assertIs(p.module, sys.modules[self.module_prefix + 'plugin'])
 
356
 
 
357
    def test_plugin_loaded_disabled(self):
 
358
        self.assertPluginUnknown('plugin')
 
359
        self.overrideEnv('BRZ_DISABLE_PLUGINS', 'plugin')
 
360
        self.setup_plugin()
 
361
        self.assertIs(None, breezy.plugin.get_loaded_plugin('plugin'))
298
362
 
299
363
    def test_plugin_appears_in_plugins(self):
300
364
        self.setup_plugin()
301
365
        self.assertPluginKnown('plugin')
302
 
        p = plugin.plugins()['plugin']
303
 
        self.assertIsInstance(p, bzrlib.plugin.PlugIn)
304
 
        self.assertEqual(p.module, plugins.plugin)
 
366
        p = self.plugins['plugin']
 
367
        self.assertIsInstance(p, breezy.plugin.PlugIn)
 
368
        self.assertIs(p.module, sys.modules[self.module_prefix + 'plugin'])
305
369
 
306
370
    def test_trivial_plugin_get_path(self):
307
371
        self.setup_plugin()
308
 
        p = plugin.plugins()['plugin']
 
372
        p = self.plugins['plugin']
309
373
        plugin_path = self.test_dir + '/plugin.py'
310
374
        self.assertIsSameRealPath(plugin_path, osutils.normpath(p.path()))
311
375
 
312
376
    def test_plugin_get_path_py_not_pyc(self):
313
377
        # first import creates plugin.pyc
314
378
        self.setup_plugin()
315
 
        self.teardown_plugin()
316
 
        plugin.load_from_path(['.']) # import plugin.pyc
 
379
        self.promote_cache(self.test_dir)
 
380
        self.reset()
 
381
        self.load_with_paths(['.'])  # import plugin.pyc
317
382
        p = plugin.plugins()['plugin']
318
383
        plugin_path = self.test_dir + '/plugin.py'
319
384
        self.assertIsSameRealPath(plugin_path, osutils.normpath(p.path()))
321
386
    def test_plugin_get_path_pyc_only(self):
322
387
        # first import creates plugin.pyc (or plugin.pyo depending on __debug__)
323
388
        self.setup_plugin()
324
 
        self.teardown_plugin()
325
389
        os.unlink(self.test_dir + '/plugin.py')
326
 
        plugin.load_from_path(['.']) # import plugin.pyc (or .pyo)
 
390
        self.promote_cache(self.test_dir)
 
391
        self.reset()
 
392
        self.load_with_paths(['.'])  # import plugin.pyc (or .pyo)
327
393
        p = plugin.plugins()['plugin']
328
 
        if __debug__:
329
 
            plugin_path = self.test_dir + '/plugin.pyc'
330
 
        else:
331
 
            plugin_path = self.test_dir + '/plugin.pyo'
 
394
        plugin_path = self.test_dir + '/plugin' + plugin.COMPILED_EXT
332
395
        self.assertIsSameRealPath(plugin_path, osutils.normpath(p.path()))
333
396
 
334
397
    def test_no_test_suite_gives_None_for_test_suite(self):
350
413
 
351
414
    def test_load_plugin_tests_gives_load_plugin_tests_result(self):
352
415
        source = """
353
 
def load_tests(standard_tests, module, loader):
 
416
def load_tests(loader, standard_tests, pattern):
354
417
    return 'foo'"""
355
418
        self.setup_plugin(source)
356
419
        loader = tests.TestUtil.TestLoader()
380
443
 
381
444
    def test_no_version_info___version__(self):
382
445
        self.setup_plugin()
383
 
        plugin = bzrlib.plugin.plugins()['plugin']
 
446
        plugin = breezy.plugin.plugins()['plugin']
384
447
        self.assertEqual("unknown", plugin.__version__)
385
448
 
386
449
    def test_str__version__with_version_info(self):
387
450
        self.setup_plugin("version_info = '1.2.3'")
388
 
        plugin = bzrlib.plugin.plugins()['plugin']
 
451
        plugin = breezy.plugin.plugins()['plugin']
389
452
        self.assertEqual("1.2.3", plugin.__version__)
390
453
 
391
454
    def test_noniterable__version__with_version_info(self):
392
455
        self.setup_plugin("version_info = (1)")
393
 
        plugin = bzrlib.plugin.plugins()['plugin']
 
456
        plugin = breezy.plugin.plugins()['plugin']
394
457
        self.assertEqual("1", plugin.__version__)
395
458
 
396
459
    def test_1__version__with_version_info(self):
397
460
        self.setup_plugin("version_info = (1,)")
398
 
        plugin = bzrlib.plugin.plugins()['plugin']
 
461
        plugin = breezy.plugin.plugins()['plugin']
399
462
        self.assertEqual("1", plugin.__version__)
400
463
 
401
464
    def test_1_2__version__with_version_info(self):
402
465
        self.setup_plugin("version_info = (1, 2)")
403
 
        plugin = bzrlib.plugin.plugins()['plugin']
 
466
        plugin = breezy.plugin.plugins()['plugin']
404
467
        self.assertEqual("1.2", plugin.__version__)
405
468
 
406
469
    def test_1_2_3__version__with_version_info(self):
407
470
        self.setup_plugin("version_info = (1, 2, 3)")
408
 
        plugin = bzrlib.plugin.plugins()['plugin']
 
471
        plugin = breezy.plugin.plugins()['plugin']
409
472
        self.assertEqual("1.2.3", plugin.__version__)
410
473
 
411
474
    def test_candidate__version__with_version_info(self):
412
475
        self.setup_plugin("version_info = (1, 2, 3, 'candidate', 1)")
413
 
        plugin = bzrlib.plugin.plugins()['plugin']
 
476
        plugin = breezy.plugin.plugins()['plugin']
414
477
        self.assertEqual("1.2.3rc1", plugin.__version__)
415
478
 
416
479
    def test_dev__version__with_version_info(self):
417
480
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 0)")
418
 
        plugin = bzrlib.plugin.plugins()['plugin']
 
481
        plugin = breezy.plugin.plugins()['plugin']
419
482
        self.assertEqual("1.2.3dev", plugin.__version__)
420
483
 
421
484
    def test_dev_fallback__version__with_version_info(self):
422
485
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 4)")
423
 
        plugin = bzrlib.plugin.plugins()['plugin']
 
486
        plugin = breezy.plugin.plugins()['plugin']
424
487
        self.assertEqual("1.2.3dev4", plugin.__version__)
425
488
 
426
489
    def test_final__version__with_version_info(self):
427
490
        self.setup_plugin("version_info = (1, 2, 3, 'final', 0)")
428
 
        plugin = bzrlib.plugin.plugins()['plugin']
 
491
        plugin = breezy.plugin.plugins()['plugin']
429
492
        self.assertEqual("1.2.3", plugin.__version__)
430
493
 
431
494
    def test_final_fallback__version__with_version_info(self):
432
495
        self.setup_plugin("version_info = (1, 2, 3, 'final', 2)")
433
 
        plugin = bzrlib.plugin.plugins()['plugin']
434
 
        self.assertEqual("1.2.3.final.2", plugin.__version__)
435
 
 
436
 
 
437
 
class TestPluginHelp(tests.TestCaseInTempDir):
438
 
 
439
 
    def split_help_commands(self):
440
 
        help = {}
441
 
        current = None
442
 
        out, err = self.run_bzr('--no-plugins help commands')
443
 
        for line in out.splitlines():
444
 
            if not line.startswith(' '):
445
 
                current = line.split()[0]
446
 
            help[current] = help.get(current, '') + line
447
 
 
448
 
        return help
449
 
 
450
 
    def test_plugin_help_builtins_unaffected(self):
451
 
        # Check we don't get false positives
452
 
        help_commands = self.split_help_commands()
453
 
        for cmd_name in bzrlib.commands.builtin_command_names():
454
 
            if cmd_name in bzrlib.commands.plugin_command_names():
455
 
                continue
456
 
            try:
457
 
                help = bzrlib.commands.get_cmd_object(cmd_name).get_help_text()
458
 
            except NotImplementedError:
459
 
                # some commands have no help
460
 
                pass
461
 
            else:
462
 
                self.assertNotContainsRe(help, 'plugin "[^"]*"')
463
 
 
464
 
            if cmd_name in help_commands.keys():
465
 
                # some commands are hidden
466
 
                help = help_commands[cmd_name]
467
 
                self.assertNotContainsRe(help, 'plugin "[^"]*"')
468
 
 
469
 
    def test_plugin_help_shows_plugin(self):
470
 
        # Create a test plugin
471
 
        os.mkdir('plugin_test')
472
 
        f = open(osutils.pathjoin('plugin_test', 'myplug.py'), 'w')
473
 
        f.write("""\
474
 
from bzrlib import commands
475
 
class cmd_myplug(commands.Command):
476
 
    __doc__ = '''Just a simple test plugin.'''
477
 
    aliases = ['mplg']
478
 
    def run(self):
479
 
        print 'Hello from my plugin'
480
 
 
481
 
"""
482
 
)
483
 
        f.close()
484
 
 
485
 
        try:
486
 
            # Check its help
487
 
            bzrlib.plugin.load_from_path(['plugin_test'])
488
 
            bzrlib.commands.register_command( bzrlib.plugins.myplug.cmd_myplug)
489
 
            help = self.run_bzr('help myplug')[0]
490
 
            self.assertContainsRe(help, 'plugin "myplug"')
491
 
            help = self.split_help_commands()['myplug']
492
 
            self.assertContainsRe(help, '\[myplug\]')
493
 
        finally:
494
 
            # unregister command
495
 
            if 'myplug' in bzrlib.commands.plugin_cmds:
496
 
                bzrlib.commands.plugin_cmds.remove('myplug')
497
 
            # remove the plugin 'myplug'
498
 
            if getattr(bzrlib.plugins, 'myplug', None):
499
 
                delattr(bzrlib.plugins, 'myplug')
 
496
        plugin = breezy.plugin.plugins()['plugin']
 
497
        self.assertEqual("1.2.3.2", plugin.__version__)
500
498
 
501
499
 
502
500
class TestHelpIndex(tests.TestCase):
515
513
        index = plugin.PluginsHelpIndex()
516
514
        # make a new plugin here for this test, even if we're run with
517
515
        # --no-plugins
518
 
        self.assertFalse(sys.modules.has_key('bzrlib.plugins.demo_module'))
519
 
        demo_module = FakeModule('', 'bzrlib.plugins.demo_module')
520
 
        sys.modules['bzrlib.plugins.demo_module'] = demo_module
 
516
        self.assertFalse('breezy.plugins.demo_module' in sys.modules)
 
517
        demo_module = FakeModule('', 'breezy.plugins.demo_module')
 
518
        sys.modules['breezy.plugins.demo_module'] = demo_module
521
519
        try:
522
520
            topics = index.get_topics('demo_module')
523
521
            self.assertEqual(1, len(topics))
524
522
            self.assertIsInstance(topics[0], plugin.ModuleHelpTopic)
525
523
            self.assertEqual(demo_module, topics[0].module)
526
524
        finally:
527
 
            del sys.modules['bzrlib.plugins.demo_module']
 
525
            del sys.modules['breezy.plugins.demo_module']
528
526
 
529
527
    def test_get_topics_no_topic(self):
530
528
        """Searching for something that is not a plugin returns []."""
541
539
    def test_get_plugin_topic_with_prefix(self):
542
540
        """Searching for plugins/demo_module returns help."""
543
541
        index = plugin.PluginsHelpIndex()
544
 
        self.assertFalse(sys.modules.has_key('bzrlib.plugins.demo_module'))
545
 
        demo_module = FakeModule('', 'bzrlib.plugins.demo_module')
546
 
        sys.modules['bzrlib.plugins.demo_module'] = demo_module
 
542
        self.assertFalse('breezy.plugins.demo_module' in sys.modules)
 
543
        demo_module = FakeModule('', 'breezy.plugins.demo_module')
 
544
        sys.modules['breezy.plugins.demo_module'] = demo_module
547
545
        try:
548
546
            topics = index.get_topics('plugins/demo_module')
549
547
            self.assertEqual(1, len(topics))
550
548
            self.assertIsInstance(topics[0], plugin.ModuleHelpTopic)
551
549
            self.assertEqual(demo_module, topics[0].module)
552
550
        finally:
553
 
            del sys.modules['bzrlib.plugins.demo_module']
 
551
            del sys.modules['breezy.plugins.demo_module']
554
552
 
555
553
 
556
554
class FakeModule(object):
575
573
        mod = FakeModule(None, 'demo')
576
574
        topic = plugin.ModuleHelpTopic(mod)
577
575
        self.assertEqual("Plugin 'demo' has no docstring.\n",
578
 
            topic.get_help_text())
 
576
                         topic.get_help_text())
579
577
 
580
578
    def test_get_help_text_no_carriage_return(self):
581
579
        """ModuleHelpTopic.get_help_text adds a \n if needed."""
582
580
        mod = FakeModule('one line of help', 'demo')
583
581
        topic = plugin.ModuleHelpTopic(mod)
584
582
        self.assertEqual("one line of help\n",
585
 
            topic.get_help_text())
 
583
                         topic.get_help_text())
586
584
 
587
585
    def test_get_help_text_carriage_return(self):
588
586
        """ModuleHelpTopic.get_help_text adds a \n if needed."""
589
587
        mod = FakeModule('two lines of help\nand more\n', 'demo')
590
588
        topic = plugin.ModuleHelpTopic(mod)
591
589
        self.assertEqual("two lines of help\nand more\n",
592
 
            topic.get_help_text())
 
590
                         topic.get_help_text())
593
591
 
594
592
    def test_get_help_text_with_additional_see_also(self):
595
593
        mod = FakeModule('two lines of help\nand more', 'demo')
596
594
        topic = plugin.ModuleHelpTopic(mod)
597
 
        self.assertEqual("two lines of help\nand more\nSee also: bar, foo\n",
 
595
        self.assertEqual(
 
596
            "two lines of help\nand more\n\n:See also: bar, foo\n",
598
597
            topic.get_help_text(['foo', 'bar']))
599
598
 
600
599
    def test_get_help_topic(self):
601
600
        """The help topic for a plugin is its module name."""
602
 
        mod = FakeModule('two lines of help\nand more', 'bzrlib.plugins.demo')
 
601
        mod = FakeModule('two lines of help\nand more', 'breezy.plugins.demo')
603
602
        topic = plugin.ModuleHelpTopic(mod)
604
603
        self.assertEqual('demo', topic.get_help_topic())
605
 
        mod = FakeModule('two lines of help\nand more', 'bzrlib.plugins.foo_bar')
 
604
        mod = FakeModule('two lines of help\nand more',
 
605
                         'breezy.plugins.foo_bar')
606
606
        topic = plugin.ModuleHelpTopic(mod)
607
607
        self.assertEqual('foo_bar', topic.get_help_topic())
608
608
 
609
609
 
610
 
class TestLoadFromPath(tests.TestCaseInTempDir):
611
 
 
612
 
    def setUp(self):
613
 
        super(TestLoadFromPath, self).setUp()
614
 
        # Change bzrlib.plugin to think no plugins have been loaded yet.
615
 
        self.overrideAttr(bzrlib.plugins, '__path__', [])
616
 
        self.overrideAttr(plugin, '_loaded', False)
617
 
 
618
 
        # Monkey-patch load_from_path to stop it from actually loading anything.
619
 
        self.overrideAttr(plugin, 'load_from_path', lambda dirs: None)
620
 
 
621
 
    def test_set_plugins_path_with_args(self):
622
 
        plugin.set_plugins_path(['a', 'b'])
623
 
        self.assertEqual(['a', 'b'], bzrlib.plugins.__path__)
624
 
 
625
 
    def test_set_plugins_path_defaults(self):
626
 
        plugin.set_plugins_path()
627
 
        self.assertEqual(plugin.get_standard_plugins_path(),
628
 
                         bzrlib.plugins.__path__)
629
 
 
630
 
    def test_get_standard_plugins_path(self):
631
 
        path = plugin.get_standard_plugins_path()
632
 
        for directory in path:
633
 
            self.assertNotContainsRe(directory, r'\\/$')
634
 
        try:
635
 
            from distutils.sysconfig import get_python_lib
636
 
        except ImportError:
637
 
            pass
638
 
        else:
639
 
            if sys.platform != 'win32':
640
 
                python_lib = get_python_lib()
641
 
                for directory in path:
642
 
                    if directory.startswith(python_lib):
643
 
                        break
644
 
                else:
645
 
                    self.fail('No path to global plugins')
646
 
 
647
 
    def test_get_standard_plugins_path_env(self):
648
 
        os.environ['BZR_PLUGIN_PATH'] = 'foo/'
649
 
        path = plugin.get_standard_plugins_path()
650
 
        for directory in path:
651
 
            self.assertNotContainsRe(directory, r'\\/$')
652
 
 
653
 
    def test_load_plugins(self):
654
 
        plugin.load_plugins(['.'])
655
 
        self.assertEqual(bzrlib.plugins.__path__, ['.'])
656
 
        # subsequent loads are no-ops
657
 
        plugin.load_plugins(['foo'])
658
 
        self.assertEqual(bzrlib.plugins.__path__, ['.'])
659
 
 
660
 
    def test_load_plugins_default(self):
661
 
        plugin.load_plugins()
662
 
        path = plugin.get_standard_plugins_path()
663
 
        self.assertEqual(path, bzrlib.plugins.__path__)
664
 
 
665
 
 
666
610
class TestEnvPluginPath(tests.TestCase):
667
611
 
668
 
    def setUp(self):
669
 
        super(TestEnvPluginPath, self).setUp()
670
 
        self.overrideAttr(plugin, 'DEFAULT_PLUGIN_PATH', None)
671
 
 
672
 
        self.user = plugin.get_user_plugin_path()
673
 
        self.site = plugin.get_site_plugin_path()
674
 
        self.core = plugin.get_core_plugin_path()
675
 
 
676
 
    def _list2paths(self, *args):
677
 
        paths = []
678
 
        for p in args:
679
 
            plugin._append_new_path(paths, p)
680
 
        return paths
681
 
 
682
 
    def _set_path(self, *args):
683
 
        path = os.pathsep.join(self._list2paths(*args))
684
 
        osutils.set_or_unset_env('BZR_PLUGIN_PATH', path)
 
612
    user = "USER"
 
613
    core = "CORE"
 
614
    site = "SITE"
 
615
    entrypoints = "ENTRYPOINTS"
685
616
 
686
617
    def check_path(self, expected_dirs, setting_dirs):
687
 
        if setting_dirs:
688
 
            self._set_path(*setting_dirs)
689
 
        actual = plugin.get_standard_plugins_path()
690
 
        self.assertEquals(self._list2paths(*expected_dirs), actual)
 
618
        if setting_dirs is None:
 
619
            del os.environ['BRZ_PLUGIN_PATH']
 
620
        else:
 
621
            os.environ['BRZ_PLUGIN_PATH'] = os.pathsep.join(setting_dirs)
 
622
        actual = [(p if t == 'path' else t.upper())
 
623
                  for p, t in plugin._env_plugin_path()]
 
624
        self.assertEqual(expected_dirs, actual)
691
625
 
692
626
    def test_default(self):
693
 
        self.check_path([self.user, self.core, self.site],
694
 
                        None)
 
627
        self.check_path([self.user, self.core, self.site], None)
695
628
 
696
629
    def test_adhoc_policy(self):
697
630
        self.check_path([self.user, self.core, self.site],
705
638
        self.check_path([self.user, self.site, self.core],
706
639
                        ['+user', '+site', '+core'])
707
640
 
 
641
    def test_enable_entrypoints(self):
 
642
        self.check_path([self.user, self.core, self.site, self.entrypoints],
 
643
                        ['+user', '+core', '+site', '+entrypoints'])
 
644
 
708
645
    def test_disable_user(self):
709
646
        self.check_path([self.core, self.site], ['-user'])
710
647
 
746
683
                        ['mycore', '-core'])
747
684
 
748
685
    def test_my_plugin_only(self):
749
 
        self.check_path(['myplugin'], ['myplugin', '-user', '-core', '-site'])
 
686
        self.check_path(
 
687
            ['myplugin'],
 
688
            ['myplugin', '-user', '-core', '-site', '-entrypoints'])
750
689
 
751
690
    def test_my_plugin_first(self):
752
691
        self.check_path(['myplugin', self.core, self.site, self.user],
757
696
                        ['+foo', '-bar'])
758
697
 
759
698
 
760
 
class TestDisablePlugin(tests.TestCaseInTempDir, TestPluginMixin):
761
 
 
762
 
    def setUp(self):
763
 
        super(TestDisablePlugin, self).setUp()
764
 
        self.create_plugin_package('test_foo')
765
 
        # Make sure we don't pollute the plugins namespace
766
 
        self.overrideAttr(plugins, '__path__')
767
 
        # Be paranoid in case a test fail
768
 
        self.addCleanup(self._unregister_plugin, 'test_foo')
 
699
class TestDisablePlugin(BaseTestPlugins):
769
700
 
770
701
    def test_cannot_import(self):
771
 
        osutils.set_or_unset_env('BZR_DISABLE_PLUGINS', 'test_foo')
772
 
        plugin.set_plugins_path(['.'])
 
702
        self.create_plugin_package('works')
 
703
        self.create_plugin_package('fails')
 
704
        self.overrideEnv('BRZ_DISABLE_PLUGINS', 'fails')
 
705
        self.update_module_paths(["."])
 
706
        import breezy.testingplugins.works as works
773
707
        try:
774
 
            import bzrlib.plugins.test_foo
 
708
            import breezy.testingplugins.fails as fails
775
709
        except ImportError:
776
710
            pass
777
 
        self.assertPluginUnknown('test_foo')
778
 
 
779
 
    def test_regular_load(self):
780
 
        self.overrideAttr(plugin, '_loaded', False)
781
 
        plugin.load_plugins(['.'])
782
 
        self.assertPluginKnown('test_foo')
783
 
        self.assertDocstring("This is the doc for test_foo",
784
 
                             bzrlib.plugins.test_foo)
785
 
 
786
 
    def test_not_loaded(self):
787
 
        self.warnings = []
788
 
        def captured_warning(*args, **kwargs):
789
 
            self.warnings.append((args, kwargs))
790
 
        self.overrideAttr(trace, 'warning', captured_warning)
791
 
        # Reset the flag that protect against double loading
792
 
        self.overrideAttr(plugin, '_loaded', False)
793
 
        osutils.set_or_unset_env('BZR_DISABLE_PLUGINS', 'test_foo')
794
 
        plugin.load_plugins(['.'])
795
 
        self.assertPluginUnknown('test_foo')
796
 
        # Make sure we don't warn about the plugin ImportError since this has
797
 
        # been *requested* by the user.
798
 
        self.assertLength(0, self.warnings)
799
 
 
800
 
 
801
 
class TestLoadPluginAt(tests.TestCaseInTempDir, TestPluginMixin):
 
711
        else:
 
712
            self.fail("Loaded blocked plugin: " + repr(fails))
 
713
        self.assertPluginModules({'fails': None, 'works': works})
 
714
 
 
715
    def test_partial_imports(self):
 
716
        self.create_plugin('good')
 
717
        self.create_plugin('bad')
 
718
        self.create_plugin_package('ugly')
 
719
        self.overrideEnv('BRZ_DISABLE_PLUGINS', 'bad:ugly')
 
720
        self.load_with_paths(['.'])
 
721
        self.assertEqual({'good'}, viewkeys(self.plugins))
 
722
        self.assertPluginModules({
 
723
            'good': self.plugins['good'].module,
 
724
            'bad': None,
 
725
            'ugly': None,
 
726
        })
 
727
        # Ensure there are no warnings about plugins not being imported as
 
728
        # the user has explictly requested they be disabled.
 
729
        self.assertNotContainsRe(self.get_log(), r"Unable to load plugin")
 
730
 
 
731
 
 
732
class TestEnvDisablePlugins(tests.TestCase):
 
733
 
 
734
    def _get_names(self, env_value):
 
735
        os.environ['BRZ_DISABLE_PLUGINS'] = env_value
 
736
        return plugin._env_disable_plugins()
 
737
 
 
738
    def test_unset(self):
 
739
        self.assertEqual([], plugin._env_disable_plugins())
 
740
 
 
741
    def test_empty(self):
 
742
        self.assertEqual([], self._get_names(''))
 
743
 
 
744
    def test_single(self):
 
745
        self.assertEqual(['single'], self._get_names('single'))
 
746
 
 
747
    def test_multi(self):
 
748
        expected = ['one', 'two']
 
749
        self.assertEqual(expected, self._get_names(os.pathsep.join(expected)))
 
750
 
 
751
    def test_mixed(self):
 
752
        value = os.pathsep.join(['valid', 'in-valid'])
 
753
        self.assertEqual(['valid'], self._get_names(value))
 
754
        self.assertContainsRe(
 
755
            self.get_log(),
 
756
            r"Invalid name 'in-valid' in BRZ_DISABLE_PLUGINS=" + repr(value))
 
757
 
 
758
 
 
759
class TestEnvPluginsAt(tests.TestCase):
 
760
 
 
761
    def _get_paths(self, env_value):
 
762
        os.environ['BRZ_PLUGINS_AT'] = env_value
 
763
        return plugin._env_plugins_at()
 
764
 
 
765
    def test_empty(self):
 
766
        self.assertEqual([], plugin._env_plugins_at())
 
767
        self.assertEqual([], self._get_paths(''))
 
768
 
 
769
    def test_one_path(self):
 
770
        self.assertEqual([('b', 'man')], self._get_paths('b@man'))
 
771
 
 
772
    def test_multiple(self):
 
773
        self.assertEqual(
 
774
            [('tools', 'bzr-tools'), ('p', 'play.py')],
 
775
            self._get_paths(os.pathsep.join(('tools@bzr-tools', 'p@play.py'))))
 
776
 
 
777
    def test_many_at(self):
 
778
        self.assertEqual(
 
779
            [('church', 'StMichael@Plea@Norwich')],
 
780
            self._get_paths('church@StMichael@Plea@Norwich'))
 
781
 
 
782
    def test_only_py(self):
 
783
        self.assertEqual([('test', './test.py')], self._get_paths('./test.py'))
 
784
 
 
785
    def test_only_package(self):
 
786
        self.assertEqual([('py', '/opt/b/py')], self._get_paths('/opt/b/py'))
 
787
 
 
788
    def test_bad_name(self):
 
789
        self.assertEqual([], self._get_paths('/usr/local/bzr-git'))
 
790
        self.assertContainsRe(
 
791
            self.get_log(),
 
792
            r"Invalid name 'bzr-git' in BRZ_PLUGINS_AT='/usr/local/bzr-git'")
 
793
 
 
794
 
 
795
class TestLoadPluginAt(BaseTestPlugins):
802
796
 
803
797
    def setUp(self):
804
798
        super(TestLoadPluginAt, self).setUp()
805
 
        # Make sure we don't pollute the plugins namespace
806
 
        self.overrideAttr(plugins, '__path__')
807
 
        # Be paranoid in case a test fail
808
 
        self.addCleanup(self._unregister_plugin, 'test_foo')
809
 
        # Reset the flag that protect against double loading
810
 
        self.overrideAttr(plugin, '_loaded', False)
811
799
        # Create the same plugin in two directories
812
800
        self.create_plugin_package('test_foo', dir='non-standard-dir')
813
801
        # The "normal" directory, we use 'standard' instead of 'plugins' to
817
805
    def assertTestFooLoadedFrom(self, path):
818
806
        self.assertPluginKnown('test_foo')
819
807
        self.assertDocstring('This is the doc for test_foo',
820
 
                             bzrlib.plugins.test_foo)
821
 
        self.assertEqual(path, bzrlib.plugins.test_foo.dir_source)
 
808
                             self.module.test_foo)
 
809
        self.assertEqual(path, self.module.test_foo.dir_source)
822
810
 
823
811
    def test_regular_load(self):
824
 
        plugin.load_plugins(['standard'])
 
812
        self.load_with_paths(['standard'])
825
813
        self.assertTestFooLoadedFrom('standard/test_foo')
826
814
 
827
815
    def test_import(self):
828
 
        osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
829
 
        plugin.set_plugins_path(['standard'])
830
 
        try:
831
 
            import bzrlib.plugins.test_foo
832
 
        except ImportError:
833
 
            pass
 
816
        self.overrideEnv('BRZ_PLUGINS_AT', 'test_foo@non-standard-dir')
 
817
        self.update_module_paths(['standard'])
 
818
        import breezy.testingplugins.test_foo
834
819
        self.assertTestFooLoadedFrom('non-standard-dir')
835
820
 
836
821
    def test_loading(self):
837
 
        osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
838
 
        plugin.load_plugins(['standard'])
 
822
        self.overrideEnv('BRZ_PLUGINS_AT', 'test_foo@non-standard-dir')
 
823
        self.load_with_paths(['standard'])
 
824
        self.assertTestFooLoadedFrom('non-standard-dir')
 
825
 
 
826
    def test_loading_other_name(self):
 
827
        self.overrideEnv('BRZ_PLUGINS_AT', 'test_foo@non-standard-dir')
 
828
        os.rename('standard/test_foo', 'standard/test_bar')
 
829
        self.load_with_paths(['standard'])
839
830
        self.assertTestFooLoadedFrom('non-standard-dir')
840
831
 
841
832
    def test_compiled_loaded(self):
842
 
        osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
843
 
        plugin.load_plugins(['standard'])
 
833
        self.overrideEnv('BRZ_PLUGINS_AT', 'test_foo@non-standard-dir')
 
834
        self.load_with_paths(['standard'])
844
835
        self.assertTestFooLoadedFrom('non-standard-dir')
845
 
        self.assertEqual('non-standard-dir/__init__.py',
846
 
                         bzrlib.plugins.test_foo.__file__)
 
836
        self.assertIsSameRealPath('non-standard-dir/__init__.py',
 
837
                                  self.module.test_foo.__file__)
847
838
 
848
839
        # Try importing again now that the source has been compiled
849
 
        self._unregister_plugin('test_foo')
850
 
        plugin._loaded = False
851
 
        plugin.load_plugins(['standard'])
 
840
        os.remove('non-standard-dir/__init__.py')
 
841
        self.promote_cache('non-standard-dir')
 
842
        self.reset()
 
843
        self.load_with_paths(['standard'])
852
844
        self.assertTestFooLoadedFrom('non-standard-dir')
853
 
        if __debug__:
854
 
            suffix = 'pyc'
855
 
        else:
856
 
            suffix = 'pyo'
857
 
        self.assertEqual('non-standard-dir/__init__.%s' % suffix,
858
 
                         bzrlib.plugins.test_foo.__file__)
 
845
        suffix = plugin.COMPILED_EXT
 
846
        self.assertIsSameRealPath('non-standard-dir/__init__' + suffix,
 
847
                                  self.module.test_foo.__file__)
859
848
 
860
849
    def test_submodule_loading(self):
861
850
        # We create an additional directory under the one for test_foo
862
851
        self.create_plugin_package('test_bar', dir='non-standard-dir/test_bar')
863
 
        osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
864
 
        plugin.set_plugins_path(['standard'])
865
 
        import bzrlib.plugins.test_foo
866
 
        self.assertEqual('bzrlib.plugins.test_foo',
867
 
                         bzrlib.plugins.test_foo.__package__)
868
 
        import bzrlib.plugins.test_foo.test_bar
869
 
        self.assertEqual('non-standard-dir/test_bar/__init__.py',
870
 
                         bzrlib.plugins.test_foo.test_bar.__file__)
 
852
        self.overrideEnv('BRZ_PLUGINS_AT', 'test_foo@non-standard-dir')
 
853
        self.update_module_paths(['standard'])
 
854
        import breezy.testingplugins.test_foo  # noqa: F401
 
855
        self.assertEqual(self.module_prefix + 'test_foo',
 
856
                         self.module.test_foo.__package__)
 
857
        import breezy.testingplugins.test_foo.test_bar  # noqa: F401
 
858
        self.assertIsSameRealPath('non-standard-dir/test_bar/__init__.py',
 
859
                                  self.module.test_foo.test_bar.__file__)
 
860
 
 
861
    def test_relative_submodule_loading(self):
 
862
        self.create_plugin_package('test_foo', dir='another-dir', source='''
 
863
from . import test_bar
 
864
''')
 
865
        # We create an additional directory under the one for test_foo
 
866
        self.create_plugin_package('test_bar', dir='another-dir/test_bar')
 
867
        self.overrideEnv('BRZ_PLUGINS_AT', 'test_foo@another-dir')
 
868
        self.update_module_paths(['standard'])
 
869
        import breezy.testingplugins.test_foo  # noqa: F401
 
870
        self.assertEqual(self.module_prefix + 'test_foo',
 
871
                         self.module.test_foo.__package__)
 
872
        self.assertIsSameRealPath('another-dir/test_bar/__init__.py',
 
873
                                  self.module.test_foo.test_bar.__file__)
871
874
 
872
875
    def test_loading_from___init__only(self):
873
876
        # We rename the existing __init__.py file to ensure that we don't load
875
878
        init = 'non-standard-dir/__init__.py'
876
879
        random = 'non-standard-dir/setup.py'
877
880
        os.rename(init, random)
878
 
        self.addCleanup(os.rename, random, init)
879
 
        osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@non-standard-dir')
880
 
        plugin.load_plugins(['standard'])
 
881
        self.overrideEnv('BRZ_PLUGINS_AT', 'test_foo@non-standard-dir')
 
882
        self.load_with_paths(['standard'])
881
883
        self.assertPluginUnknown('test_foo')
882
884
 
883
885
    def test_loading_from_specific_file(self):
890
892
''' % ('test_foo', plugin_path)
891
893
        self.create_plugin('test_foo', source=source,
892
894
                           dir=plugin_dir, file_name=plugin_file_name)
893
 
        osutils.set_or_unset_env('BZR_PLUGINS_AT', 'test_foo@%s' % plugin_path)
894
 
        plugin.load_plugins(['standard'])
 
895
        self.overrideEnv('BRZ_PLUGINS_AT', 'test_foo@%s' % plugin_path)
 
896
        self.load_with_paths(['standard'])
895
897
        self.assertTestFooLoadedFrom(plugin_path)
 
898
 
 
899
 
 
900
class TestDescribePlugins(BaseTestPlugins):
 
901
 
 
902
    def test_describe_plugins(self):
 
903
        class DummyModule(object):
 
904
            __doc__ = 'Hi there'
 
905
 
 
906
        class DummyPlugin(object):
 
907
            __version__ = '0.1.0'
 
908
            module = DummyModule()
 
909
        self.plugin_warnings = {'bad': ['Failed to load (just testing)']}
 
910
        self.plugins = {'good': DummyPlugin()}
 
911
        self.assertEqual("""\
 
912
bad (failed to load)
 
913
  ** Failed to load (just testing)
 
914
 
 
915
good 0.1.0
 
916
  Hi there
 
917
 
 
918
""", ''.join(plugin.describe_plugins(state=self)))
 
919
 
 
920
 
 
921
class DummyPlugin(object):
 
922
    """Plugin."""
 
923
 
 
924
 
 
925
class TestLoadEnvPlugin(BaseTestPlugins):
 
926
 
 
927
    _test_needs_features = [pkg_resources_feature]
 
928
 
 
929
    def setup_plugin(self, source=""):
 
930
        # This test tests a new plugin appears in breezy.plugin.plugins().
 
931
        # check the plugin is not loaded already
 
932
        self.assertPluginUnknown('plugin')
 
933
        # write a plugin that _cannot_ fail to load.
 
934
        import pkg_resources
 
935
        d = pkg_resources.Distribution(__file__)
 
936
        ep = pkg_resources.EntryPoint.parse(
 
937
            'plugin = ' + __name__ + ':DummyPlugin', dist=d)
 
938
        d._ep_map = {'breezy.plugin': {'plugin': ep}}
 
939
        pkg_resources.working_set.add(d, 'plugin', replace=True)
 
940
        eps = list(pkg_resources.iter_entry_points('breezy.plugin'))
 
941
        self.assertEqual(['plugin'], [ep.name for ep in eps])
 
942
        self.load_with_paths(['.'])
 
943
        self.addCleanup(d._ep_map.clear)
 
944
 
 
945
    def test_plugin_loaded(self):
 
946
        self.assertPluginUnknown('plugin')
 
947
        self.overrideEnv('BRZ_PLUGIN_PATH', '+entrypoints')
 
948
        self.setup_plugin()
 
949
        p = self.plugins['plugin']
 
950
        self.assertIsInstance(p, breezy.plugin.PlugIn)
 
951
        self.assertIs(p.module, sys.modules[self.module_prefix + 'plugin'])
 
952
 
 
953
    def test_plugin_loaded_disabled(self):
 
954
        self.assertPluginUnknown('plugin')
 
955
        self.overrideEnv('BRZ_PLUGIN_PATH', '+entrypoints')
 
956
        self.overrideEnv('BRZ_DISABLE_PLUGINS', 'plugin')
 
957
        self.setup_plugin()
 
958
        self.assertNotIn('plugin', self.plugins)