/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: Breezy landing bot
  • Author(s): Jelmer Vernooij
  • Date: 2020-08-23 01:15:41 UTC
  • mfrom: (7520.1.4 merge-3.1)
  • Revision ID: breezy.the.bot@gmail.com-20200823011541-nv0oh7nzaganx2qy
Merge lp:brz/3.1.

Merged from https://code.launchpad.net/~jelmer/brz/merge-3.1/+merge/389690

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