/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
7015.1.3 by Martin
Tweak copyright headers
1
# Copyright (C) 2005-2012, 2016 Canonical Ltd
2
# Copyright (C) 2017-2018 Breezy developers
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
3
#
750 by Martin Pool
- stubbed-out tests for python plugins
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
8
#
750 by Martin Pool
- stubbed-out tests for python plugins
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
13
#
750 by Martin Pool
- stubbed-out tests for python plugins
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
750 by Martin Pool
- stubbed-out tests for python plugins
17
18
"""Tests for plugins"""
19
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
20
import importlib
2967.4.5 by Daniel Watkins
Added test for badly-named plugins.
21
import logging
1185.16.83 by mbp at sourcefrog
- notes on testability of plugins
22
import os
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
23
import sys
7015.1.1 by Martin
Switch to compatible means of creating a module
24
import types
750 by Martin Pool
- stubbed-out tests for python plugins
25
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
26
import breezy
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
27
from .. import (
5268.5.1 by Vincent Ladeuil
Reproduce bug #591215.
28
    errors,
4628.2.2 by Vincent Ladeuil
Add [+-]{user|core|site} handling in BZR_PLUGIN_PATH.
29
    osutils,
30
    plugin,
31
    tests,
5086.1.8 by Vincent Ladeuil
Fix warnings during autoload, add doc and a NEWS entry.
32
    trace,
4628.2.2 by Vincent Ladeuil
Add [+-]{user|core|site} handling in BZR_PLUGIN_PATH.
33
    )
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
34
from ..sixish import (
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
35
    PY3,
36
    StringIO,
37
    viewkeys,
6621.22.2 by Martin
Use BytesIO or StringIO from bzrlib.sixish
38
    )
1141 by Martin Pool
- rename FunctionalTest to TestCaseInTempDir
39
1185.16.83 by mbp at sourcefrog
- notes on testability of plugins
40
1492 by Robert Collins
Support decoration of commands.
41
# TODO: Write a test for plugin decoration of commands.
1515 by Robert Collins
* Plugins with the same name in different directories in the bzr plugin
42
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
43
invalidate_caches = getattr(importlib, "invalidate_caches", lambda: None)
44
45
5616.7.10 by Martin Pool
Clean up describe_plugins to sort loaded and unloaded plugins together.
46
class BaseTestPlugins(tests.TestCaseInTempDir):
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
47
    """TestCase that isolates plugin imports and cleans up on completion."""
48
49
    def setUp(self):
50
        super(BaseTestPlugins, self).setUp()
51
        self.module_name = "breezy.testingplugins"
52
        self.module_prefix = self.module_name + "."
7015.1.1 by Martin
Switch to compatible means of creating a module
53
        self.module = types.ModuleType(self.module_name)
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
54
55
        self.overrideAttr(plugin, "_MODULE_PREFIX", self.module_prefix)
56
        self.overrideAttr(breezy, "testingplugins", self.module)
57
58
        sys.modules[self.module_name] = self.module
59
        self.addCleanup(self._unregister_all)
60
        self.addCleanup(self._unregister_finder)
61
62
        invalidate_caches()
63
64
    def reset(self):
65
        """Remove all global testing state and clean up module."""
66
        # GZ 2017-06-02: Ideally don't do this, write new test or generate
67
        # bytecode by other mechanism.
68
        self.log("resetting plugin testing context")
69
        self._unregister_all()
70
        self._unregister_finder()
71
        sys.modules[self.module_name] = self.module
72
        for name in list(self.module.__dict__):
73
            if name[:2] != '__':
74
                delattr(self.module, name)
75
        invalidate_caches()
76
        self.plugins = None
77
78
    def update_module_paths(self, paths):
79
        paths = plugin.extend_path(paths, self.module_name)
80
        self.module.__path__ = paths
81
        self.log("using %r", paths)
82
        return paths
83
84
    def load_with_paths(self, paths):
85
        self.log("loading plugins!")
86
        plugin.load_plugins(self.update_module_paths(paths), state=self)
5086.1.4 by Vincent Ladeuil
Slight plugin tests rewriting.
87
5086.5.8 by Vincent Ladeuil
Make sure we can load from a non-standard directory name.
88
    def create_plugin(self, name, source=None, dir='.', file_name=None):
89
        if source is None:
90
            source = '''\
91
"""This is the doc for %s"""
92
''' % (name)
5086.1.6 by Vincent Ladeuil
Crude fix for bug #411413.
93
        if file_name is None:
94
            file_name = name + '.py'
95
        # 'source' must not fail to load
5086.1.7 by Vincent Ladeuil
Cleaner fix for bug #411413.
96
        path = osutils.pathjoin(dir, file_name)
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
97
        with open(path, 'w') as f:
5086.1.6 by Vincent Ladeuil
Crude fix for bug #411413.
98
            f.write(source + '\n')
99
5086.5.8 by Vincent Ladeuil
Make sure we can load from a non-standard directory name.
100
    def create_plugin_package(self, name, dir=None, source=None):
101
        if dir is None:
102
            dir = name
103
        if source is None:
104
            source = '''\
105
"""This is the doc for %s"""
106
dir_source = '%s'
107
''' % (name, dir)
108
        os.makedirs(dir)
109
        self.create_plugin(name, source, dir,
5086.1.6 by Vincent Ladeuil
Crude fix for bug #411413.
110
                           file_name='__init__.py')
111
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
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)
5268.6.1 by Vincent Ladeuil
Drive-by fix of the submodule leak.
145
5086.1.4 by Vincent Ladeuil
Slight plugin tests rewriting.
146
    def assertPluginUnknown(self, name):
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
147
        self.assertTrue(getattr(self.module, name, None) is None)
148
        self.assertFalse(self.module_prefix + name in sys.modules)
5086.1.4 by Vincent Ladeuil
Slight plugin tests rewriting.
149
150
    def assertPluginKnown(self, name):
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
151
        self.assertTrue(getattr(self.module, name, None) is not None)
152
        self.assertTrue(self.module_prefix + name in sys.modules)
5086.1.4 by Vincent Ladeuil
Slight plugin tests rewriting.
153
154
5616.7.10 by Martin Pool
Clean up describe_plugins to sort loaded and unloaded plugins together.
155
class TestLoadingPlugins(BaseTestPlugins):
1515 by Robert Collins
* Plugins with the same name in different directories in the bzr plugin
156
157
    activeattributes = {}
158
159
    def test_plugins_with_the_same_name_are_not_loaded(self):
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
160
        # This test tests that having two plugins in different directories does
161
        # not result in both being loaded when they have the same name.  get a
162
        # file name we can use which is also a valid attribute for accessing in
163
        # activeattributes. - we cannot give import parameters.
1515 by Robert Collins
* Plugins with the same name in different directories in the bzr plugin
164
        tempattribute = "0"
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
165
        self.assertFalse(tempattribute in self.activeattributes)
1515 by Robert Collins
* Plugins with the same name in different directories in the bzr plugin
166
        # set a place for the plugins to record their loading, and at the same
167
        # time validate that the location the plugins should record to is
168
        # valid and correct.
5086.1.4 by Vincent Ladeuil
Slight plugin tests rewriting.
169
        self.__class__.activeattributes [tempattribute] = []
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
170
        self.assertTrue(tempattribute in self.activeattributes)
1515 by Robert Collins
* Plugins with the same name in different directories in the bzr plugin
171
        # create two plugin directories
172
        os.mkdir('first')
173
        os.mkdir('second')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
174
        # write a plugin that will record when its loaded in the
1515 by Robert Collins
* Plugins with the same name in different directories in the bzr plugin
175
        # tempattribute list.
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
176
        template = ("from breezy.tests.test_plugins import TestLoadingPlugins\n"
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
177
                    "TestLoadingPlugins.activeattributes[%r].append('%s')\n")
2652.2.7 by Blake Winton
fix lines which were wider than 79 chars. Also handle files a little more safely.
178
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
179
        with open(os.path.join('first', 'plugin.py'), 'w') as outfile:
2911.6.1 by Blake Winton
Change 'print >> f,'s to 'f.write('s.
180
            outfile.write(template % (tempattribute, 'first'))
181
            outfile.write('\n')
2652.2.7 by Blake Winton
fix lines which were wider than 79 chars. Also handle files a little more safely.
182
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
183
        with open(os.path.join('second', 'plugin.py'), 'w') as outfile:
2911.6.1 by Blake Winton
Change 'print >> f,'s to 'f.write('s.
184
            outfile.write(template % (tempattribute, 'second'))
185
            outfile.write('\n')
2652.2.7 by Blake Winton
fix lines which were wider than 79 chars. Also handle files a little more safely.
186
1515 by Robert Collins
* Plugins with the same name in different directories in the bzr plugin
187
        try:
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
188
            self.load_with_paths(['first', 'second'])
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
189
            self.assertEqual(['first'], self.activeattributes[tempattribute])
190
        finally:
191
            del self.activeattributes[tempattribute]
192
193
    def test_plugins_from_different_dirs_can_demand_load(self):
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
194
        self.assertFalse('breezy.plugins.pluginone' in sys.modules)
195
        self.assertFalse('breezy.plugins.plugintwo' in sys.modules)
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
196
        # This test tests that having two plugins in different
197
        # directories with different names allows them both to be loaded, when
198
        # we do a direct import statement.
199
        # Determine a file name we can use which is also a valid attribute
200
        # for accessing in activeattributes. - we cannot give import parameters.
201
        tempattribute = "different-dirs"
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
202
        self.assertFalse(tempattribute in self.activeattributes)
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
203
        # set a place for the plugins to record their loading, and at the same
204
        # time validate that the location the plugins should record to is
205
        # valid and correct.
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
206
        breezy.tests.test_plugins.TestLoadingPlugins.activeattributes \
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
207
            [tempattribute] = []
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
208
        self.assertTrue(tempattribute in self.activeattributes)
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
209
        # create two plugin directories
210
        os.mkdir('first')
211
        os.mkdir('second')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
212
        # write plugins that will record when they are loaded in the
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
213
        # tempattribute list.
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
214
        template = ("from breezy.tests.test_plugins import TestLoadingPlugins\n"
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
215
                    "TestLoadingPlugins.activeattributes[%r].append('%s')\n")
2652.2.7 by Blake Winton
fix lines which were wider than 79 chars. Also handle files a little more safely.
216
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
217
        with open(os.path.join('first', 'pluginone.py'), 'w') as outfile:
2911.6.1 by Blake Winton
Change 'print >> f,'s to 'f.write('s.
218
            outfile.write(template % (tempattribute, 'first'))
219
            outfile.write('\n')
2652.2.7 by Blake Winton
fix lines which were wider than 79 chars. Also handle files a little more safely.
220
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
221
        with open(os.path.join('second', 'plugintwo.py'), 'w') as outfile:
2911.6.1 by Blake Winton
Change 'print >> f,'s to 'f.write('s.
222
            outfile.write(template % (tempattribute, 'second'))
223
            outfile.write('\n')
2652.2.7 by Blake Winton
fix lines which were wider than 79 chars. Also handle files a little more safely.
224
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
225
        try:
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
226
            self.assertPluginUnknown('pluginone')
227
            self.assertPluginUnknown('plugintwo')
228
            self.update_module_paths(['first', 'second'])
229
            exec("import %spluginone" % self.module_prefix)
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
230
            self.assertEqual(['first'], self.activeattributes[tempattribute])
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
231
            exec("import %splugintwo" % self.module_prefix)
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
232
            self.assertEqual(['first', 'second'],
233
                self.activeattributes[tempattribute])
1515 by Robert Collins
* Plugins with the same name in different directories in the bzr plugin
234
        finally:
235
            del self.activeattributes[tempattribute]
1516 by Robert Collins
* bzrlib.plugin.all_plugins has been changed from an attribute to a
236
2652.2.1 by Blake Winton
Add a test for BZR_PLUGIN_PATH, and code and another test to allow BZR_PLUGIN_PATH to contain trailing slashes.
237
    def test_plugins_can_load_from_directory_with_trailing_slash(self):
238
        # This test tests that a plugin can load from a directory when the
239
        # directory in the path has a trailing slash.
2652.2.7 by Blake Winton
fix lines which were wider than 79 chars. Also handle files a little more safely.
240
        # check the plugin is not loaded already
5086.1.4 by Vincent Ladeuil
Slight plugin tests rewriting.
241
        self.assertPluginUnknown('ts_plugin')
2652.2.7 by Blake Winton
fix lines which were wider than 79 chars. Also handle files a little more safely.
242
        tempattribute = "trailing-slash"
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
243
        self.assertFalse(tempattribute in self.activeattributes)
2652.2.3 by Blake Winton
Understand the code and comments of the test, instead of just cargo-culting them.
244
        # set a place for the plugin to record its loading, and at the same
245
        # time validate that the location the plugin should record to is
2652.2.1 by Blake Winton
Add a test for BZR_PLUGIN_PATH, and code and another test to allow BZR_PLUGIN_PATH to contain trailing slashes.
246
        # valid and correct.
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
247
        breezy.tests.test_plugins.TestLoadingPlugins.activeattributes \
2652.2.1 by Blake Winton
Add a test for BZR_PLUGIN_PATH, and code and another test to allow BZR_PLUGIN_PATH to contain trailing slashes.
248
            [tempattribute] = []
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
249
        self.assertTrue(tempattribute in self.activeattributes)
2652.2.3 by Blake Winton
Understand the code and comments of the test, instead of just cargo-culting them.
250
        # create a directory for the plugin
251
        os.mkdir('plugin_test')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
252
        # write a plugin that will record when its loaded in the
2652.2.1 by Blake Winton
Add a test for BZR_PLUGIN_PATH, and code and another test to allow BZR_PLUGIN_PATH to contain trailing slashes.
253
        # tempattribute list.
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
254
        template = ("from breezy.tests.test_plugins import TestLoadingPlugins\n"
2652.2.1 by Blake Winton
Add a test for BZR_PLUGIN_PATH, and code and another test to allow BZR_PLUGIN_PATH to contain trailing slashes.
255
                    "TestLoadingPlugins.activeattributes[%r].append('%s')\n")
2652.2.7 by Blake Winton
fix lines which were wider than 79 chars. Also handle files a little more safely.
256
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
257
        with open(os.path.join('plugin_test', 'ts_plugin.py'), 'w') as outfile:
2911.6.1 by Blake Winton
Change 'print >> f,'s to 'f.write('s.
258
            outfile.write(template % (tempattribute, 'plugin'))
2911.6.4 by Blake Winton
Fix test failures
259
            outfile.write('\n')
2652.2.7 by Blake Winton
fix lines which were wider than 79 chars. Also handle files a little more safely.
260
2652.2.1 by Blake Winton
Add a test for BZR_PLUGIN_PATH, and code and another test to allow BZR_PLUGIN_PATH to contain trailing slashes.
261
        try:
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
262
            self.load_with_paths(['plugin_test'+os.sep])
2652.2.3 by Blake Winton
Understand the code and comments of the test, instead of just cargo-culting them.
263
            self.assertEqual(['plugin'], self.activeattributes[tempattribute])
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
264
            self.assertPluginKnown('ts_plugin')
2652.2.1 by Blake Winton
Add a test for BZR_PLUGIN_PATH, and code and another test to allow BZR_PLUGIN_PATH to contain trailing slashes.
265
        finally:
266
            del self.activeattributes[tempattribute]
267
3766.3.2 by Robert Collins
Fix reporting of incompatible api plugin load errors, fixing bug 279451.
268
    def load_and_capture(self, name):
269
        """Load plugins from '.' capturing the output.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
270
3766.3.2 by Robert Collins
Fix reporting of incompatible api plugin load errors, fixing bug 279451.
271
        :param name: The name of the plugin.
272
        :return: A string with the log from the plugin loading call.
273
        """
2967.4.5 by Daniel Watkins
Added test for badly-named plugins.
274
        # Capture output
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
275
        stream = StringIO()
3766.3.2 by Robert Collins
Fix reporting of incompatible api plugin load errors, fixing bug 279451.
276
        try:
277
            handler = logging.StreamHandler(stream)
6622.1.33 by Jelmer Vernooij
Fix more tests (all?)
278
            log = logging.getLogger('brz')
3766.3.2 by Robert Collins
Fix reporting of incompatible api plugin load errors, fixing bug 279451.
279
            log.addHandler(handler)
280
            try:
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
281
                self.load_with_paths(['.'])
3766.3.2 by Robert Collins
Fix reporting of incompatible api plugin load errors, fixing bug 279451.
282
            finally:
283
                # Stop capturing output
284
                handler.flush()
285
                handler.close()
286
                log.removeHandler(handler)
287
            return stream.getvalue()
288
        finally:
289
            stream.close()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
290
3766.3.2 by Robert Collins
Fix reporting of incompatible api plugin load errors, fixing bug 279451.
291
    def test_plugin_with_bad_api_version_reports(self):
5616.7.3 by Martin Pool
Put plugin warnings into both the apport and plain crash report
292
        """Try loading a plugin that requests an unsupported api.
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
293
5616.7.12 by Martin Pool
Comment correction
294
        Observe that it records the problem but doesn't complain on stderr.
5616.7.7 by Martin Pool
Paper over test global state dependency
295
296
        See https://bugs.launchpad.net/bzr/+bug/704195
5616.7.3 by Martin Pool
Put plugin warnings into both the apport and plain crash report
297
        """
3766.3.2 by Robert Collins
Fix reporting of incompatible api plugin load errors, fixing bug 279451.
298
        name = 'wants100.py'
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
299
        with open(name, 'w') as f:
6672.1.2 by Jelmer Vernooij
Remove breezy.api.
300
            f.write("import breezy\n"
301
                "from breezy.errors import IncompatibleVersion\n"
302
                "raise IncompatibleVersion(breezy, [(1, 0, 0)], (0, 0, 5))\n")
3766.3.2 by Robert Collins
Fix reporting of incompatible api plugin load errors, fixing bug 279451.
303
        log = self.load_and_capture(name)
5616.7.1 by Martin Pool
Record but don't show warnings about updated plugins
304
        self.assertNotContainsRe(log,
6672.1.2 by Jelmer Vernooij
Remove breezy.api.
305
            r"It supports breezy version")
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
306
        self.assertEqual({'wants100'}, viewkeys(self.plugin_warnings))
5616.7.1 by Martin Pool
Record but don't show warnings about updated plugins
307
        self.assertContainsRe(
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
308
            self.plugin_warnings['wants100'][0],
6672.1.2 by Jelmer Vernooij
Remove breezy.api.
309
            r"It supports breezy version")
3766.3.2 by Robert Collins
Fix reporting of incompatible api plugin load errors, fixing bug 279451.
310
311
    def test_plugin_with_bad_name_does_not_load(self):
312
        # The file name here invalid for a python module.
6622.1.33 by Jelmer Vernooij
Fix more tests (all?)
313
        name = 'brz-bad plugin-name..py'
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
314
        open(name, 'w').close()
3766.3.2 by Robert Collins
Fix reporting of incompatible api plugin load errors, fixing bug 279451.
315
        log = self.load_and_capture(name)
316
        self.assertContainsRe(log,
6622.1.33 by Jelmer Vernooij
Fix more tests (all?)
317
            r"Unable to load 'brz-bad plugin-name\.' in '\.' as a plugin "
3290.1.1 by James Westby
Strip "bzr_" from the start of the suggested plugin name.
318
            "because the file path isn't a valid module name; try renaming "
6798.1.1 by Jelmer Vernooij
Properly escape backslashes.
319
            "it to 'bad_plugin_name_'\\.")
2967.4.5 by Daniel Watkins
Added test for badly-named plugins.
320
1516 by Robert Collins
* bzrlib.plugin.all_plugins has been changed from an attribute to a
321
5616.7.10 by Martin Pool
Clean up describe_plugins to sort loaded and unloaded plugins together.
322
class TestPlugins(BaseTestPlugins):
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
323
324
    def setup_plugin(self, source=""):
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
325
        # This test tests a new plugin appears in breezy.plugin.plugins().
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
326
        # check the plugin is not loaded already
5086.1.4 by Vincent Ladeuil
Slight plugin tests rewriting.
327
        self.assertPluginUnknown('plugin')
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
328
        # write a plugin that _cannot_ fail to load.
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
329
        with open('plugin.py', 'w') as f: f.write(source + '\n')
330
        self.load_with_paths(['.'])
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
331
6759.4.3 by Jelmer Vernooij
Avoid accessing global state.
332
    def test_plugin_loaded(self):
333
        self.assertPluginUnknown('plugin')
334
        self.assertIs(None, breezy.plugin.get_loaded_plugin('plugin'))
335
        self.setup_plugin()
336
        p = breezy.plugin.get_loaded_plugin('plugin')
337
        self.assertIsInstance(p, breezy.plugin.PlugIn)
338
        self.assertIs(p.module, sys.modules[self.module_prefix + 'plugin'])
339
6780.1.1 by Jelmer Vernooij
Check for plugin existing in sys.modules but being None.
340
    def test_plugin_loaded_disabled(self):
341
        self.assertPluginUnknown('plugin')
342
        self.overrideEnv('BRZ_DISABLE_PLUGINS', 'plugin')
343
        self.setup_plugin()
344
        self.assertIs(None, breezy.plugin.get_loaded_plugin('plugin'))
345
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
346
    def test_plugin_appears_in_plugins(self):
347
        self.setup_plugin()
5086.1.4 by Vincent Ladeuil
Slight plugin tests rewriting.
348
        self.assertPluginKnown('plugin')
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
349
        p = self.plugins['plugin']
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
350
        self.assertIsInstance(p, breezy.plugin.PlugIn)
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
351
        self.assertIs(p.module, sys.modules[self.module_prefix + 'plugin'])
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
352
353
    def test_trivial_plugin_get_path(self):
354
        self.setup_plugin()
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
355
        p = self.plugins['plugin']
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
356
        plugin_path = self.test_dir + '/plugin.py'
5086.1.4 by Vincent Ladeuil
Slight plugin tests rewriting.
357
        self.assertIsSameRealPath(plugin_path, osutils.normpath(p.path()))
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
358
3193.2.1 by Alexander Belchenko
show path to plugin module as *.py instead of *.pyc if python source available
359
    def test_plugin_get_path_py_not_pyc(self):
5086.1.3 by Vincent Ladeuil
Fix imports in test_plugins.
360
        # first import creates plugin.pyc
361
        self.setup_plugin()
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
362
        self.promote_cache(self.test_dir)
363
        self.reset()
364
        self.load_with_paths(['.']) # import plugin.pyc
5086.1.4 by Vincent Ladeuil
Slight plugin tests rewriting.
365
        p = plugin.plugins()['plugin']
3193.2.1 by Alexander Belchenko
show path to plugin module as *.py instead of *.pyc if python source available
366
        plugin_path = self.test_dir + '/plugin.py'
5086.1.4 by Vincent Ladeuil
Slight plugin tests rewriting.
367
        self.assertIsSameRealPath(plugin_path, osutils.normpath(p.path()))
3193.2.1 by Alexander Belchenko
show path to plugin module as *.py instead of *.pyc if python source available
368
369
    def test_plugin_get_path_pyc_only(self):
5086.1.3 by Vincent Ladeuil
Fix imports in test_plugins.
370
        # first import creates plugin.pyc (or plugin.pyo depending on __debug__)
371
        self.setup_plugin()
3193.2.1 by Alexander Belchenko
show path to plugin module as *.py instead of *.pyc if python source available
372
        os.unlink(self.test_dir + '/plugin.py')
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
373
        self.promote_cache(self.test_dir)
374
        self.reset()
375
        self.load_with_paths(['.']) # import plugin.pyc (or .pyo)
5086.1.4 by Vincent Ladeuil
Slight plugin tests rewriting.
376
        p = plugin.plugins()['plugin']
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
377
        plugin_path = self.test_dir + '/plugin' + plugin.COMPILED_EXT
5086.1.4 by Vincent Ladeuil
Slight plugin tests rewriting.
378
        self.assertIsSameRealPath(plugin_path, osutils.normpath(p.path()))
3193.2.1 by Alexander Belchenko
show path to plugin module as *.py instead of *.pyc if python source available
379
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
380
    def test_no_test_suite_gives_None_for_test_suite(self):
381
        self.setup_plugin()
5086.1.4 by Vincent Ladeuil
Slight plugin tests rewriting.
382
        p = plugin.plugins()['plugin']
383
        self.assertEqual(None, p.test_suite())
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
384
385
    def test_test_suite_gives_test_suite_result(self):
386
        source = """def test_suite(): return 'foo'"""
387
        self.setup_plugin(source)
5086.1.4 by Vincent Ladeuil
Slight plugin tests rewriting.
388
        p = plugin.plugins()['plugin']
389
        self.assertEqual('foo', p.test_suite())
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
390
3302.8.21 by Vincent Ladeuil
Fixed as per Robert's review.
391
    def test_no_load_plugin_tests_gives_None_for_load_plugin_tests(self):
3302.8.10 by Vincent Ladeuil
Prepare bzrlib.plugin to use the new test loader.
392
        self.setup_plugin()
5086.1.3 by Vincent Ladeuil
Fix imports in test_plugins.
393
        loader = tests.TestUtil.TestLoader()
5086.1.4 by Vincent Ladeuil
Slight plugin tests rewriting.
394
        p = plugin.plugins()['plugin']
395
        self.assertEqual(None, p.load_plugin_tests(loader))
3302.8.10 by Vincent Ladeuil
Prepare bzrlib.plugin to use the new test loader.
396
3302.8.21 by Vincent Ladeuil
Fixed as per Robert's review.
397
    def test_load_plugin_tests_gives_load_plugin_tests_result(self):
3302.8.10 by Vincent Ladeuil
Prepare bzrlib.plugin to use the new test loader.
398
        source = """
6625.1.5 by Martin
Drop custom load_tests implementation and use unittest signature
399
def load_tests(loader, standard_tests, pattern):
3302.8.10 by Vincent Ladeuil
Prepare bzrlib.plugin to use the new test loader.
400
    return 'foo'"""
401
        self.setup_plugin(source)
5086.1.3 by Vincent Ladeuil
Fix imports in test_plugins.
402
        loader = tests.TestUtil.TestLoader()
5086.1.4 by Vincent Ladeuil
Slight plugin tests rewriting.
403
        p = plugin.plugins()['plugin']
404
        self.assertEqual('foo', p.load_plugin_tests(loader))
405
406
    def check_version_info(self, expected, source='', name='plugin'):
407
        self.setup_plugin(source)
408
        self.assertEqual(expected, plugin.plugins()[name].version_info())
3302.8.10 by Vincent Ladeuil
Prepare bzrlib.plugin to use the new test loader.
409
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
410
    def test_no_version_info(self):
5086.1.4 by Vincent Ladeuil
Slight plugin tests rewriting.
411
        self.check_version_info(None)
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
412
413
    def test_with_version_info(self):
5086.1.4 by Vincent Ladeuil
Slight plugin tests rewriting.
414
        self.check_version_info((1, 2, 3, 'dev', 4),
415
                                "version_info = (1, 2, 3, 'dev', 4)")
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
416
417
    def test_short_version_info_gets_padded(self):
418
        # the gtk plugin has version_info = (1,2,3) rather than the 5-tuple.
419
        # so we adapt it
5086.1.4 by Vincent Ladeuil
Slight plugin tests rewriting.
420
        self.check_version_info((1, 2, 3, 'final', 0),
421
                                "version_info = (1, 2, 3)")
422
423
    def check_version(self, expected, source=None, name='plugin'):
424
        self.setup_plugin(source)
425
        self.assertEqual(expected, plugins[name].__version__)
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
426
427
    def test_no_version_info___version__(self):
428
        self.setup_plugin()
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
429
        plugin = breezy.plugin.plugins()['plugin']
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
430
        self.assertEqual("unknown", plugin.__version__)
431
3777.6.7 by Marius Kruger
* Can now also handle non-iteratable and string plugin versions.
432
    def test_str__version__with_version_info(self):
433
        self.setup_plugin("version_info = '1.2.3'")
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
434
        plugin = breezy.plugin.plugins()['plugin']
3777.6.7 by Marius Kruger
* Can now also handle non-iteratable and string plugin versions.
435
        self.assertEqual("1.2.3", plugin.__version__)
436
437
    def test_noniterable__version__with_version_info(self):
438
        self.setup_plugin("version_info = (1)")
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
439
        plugin = breezy.plugin.plugins()['plugin']
3777.6.7 by Marius Kruger
* Can now also handle non-iteratable and string plugin versions.
440
        self.assertEqual("1", plugin.__version__)
441
442
    def test_1__version__with_version_info(self):
443
        self.setup_plugin("version_info = (1,)")
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
444
        plugin = breezy.plugin.plugins()['plugin']
3777.6.7 by Marius Kruger
* Can now also handle non-iteratable and string plugin versions.
445
        self.assertEqual("1", plugin.__version__)
446
447
    def test_1_2__version__with_version_info(self):
3777.6.5 by Marius Kruger
add 2 more tests for plugin version numbers
448
        self.setup_plugin("version_info = (1, 2)")
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
449
        plugin = breezy.plugin.plugins()['plugin']
3777.6.5 by Marius Kruger
add 2 more tests for plugin version numbers
450
        self.assertEqual("1.2", plugin.__version__)
451
3777.6.7 by Marius Kruger
* Can now also handle non-iteratable and string plugin versions.
452
    def test_1_2_3__version__with_version_info(self):
3777.6.5 by Marius Kruger
add 2 more tests for plugin version numbers
453
        self.setup_plugin("version_info = (1, 2, 3)")
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
454
        plugin = breezy.plugin.plugins()['plugin']
3777.6.5 by Marius Kruger
add 2 more tests for plugin version numbers
455
        self.assertEqual("1.2.3", plugin.__version__)
456
457
    def test_candidate__version__with_version_info(self):
3777.6.4 by Marius Kruger
fix tests
458
        self.setup_plugin("version_info = (1, 2, 3, 'candidate', 1)")
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
459
        plugin = breezy.plugin.plugins()['plugin']
3777.6.4 by Marius Kruger
fix tests
460
        self.assertEqual("1.2.3rc1", plugin.__version__)
461
462
    def test_dev__version__with_version_info(self):
463
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 0)")
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
464
        plugin = breezy.plugin.plugins()['plugin']
3777.6.4 by Marius Kruger
fix tests
465
        self.assertEqual("1.2.3dev", plugin.__version__)
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
466
3777.6.7 by Marius Kruger
* Can now also handle non-iteratable and string plugin versions.
467
    def test_dev_fallback__version__with_version_info(self):
468
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 4)")
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
469
        plugin = breezy.plugin.plugins()['plugin']
4634.50.6 by John Arbash Meinel
Handle a plugin fallback versioning issue.
470
        self.assertEqual("1.2.3dev4", plugin.__version__)
3777.6.7 by Marius Kruger
* Can now also handle non-iteratable and string plugin versions.
471
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
472
    def test_final__version__with_version_info(self):
3777.6.4 by Marius Kruger
fix tests
473
        self.setup_plugin("version_info = (1, 2, 3, 'final', 0)")
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
474
        plugin = breezy.plugin.plugins()['plugin']
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
475
        self.assertEqual("1.2.3", plugin.__version__)
476
4634.50.6 by John Arbash Meinel
Handle a plugin fallback versioning issue.
477
    def test_final_fallback__version__with_version_info(self):
478
        self.setup_plugin("version_info = (1, 2, 3, 'final', 2)")
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
479
        plugin = breezy.plugin.plugins()['plugin']
5851.2.2 by Martin Pool
Format plugin version as 1.2.3.2 not 1.2.3.final.2
480
        self.assertEqual("1.2.3.2", plugin.__version__)
4634.50.6 by John Arbash Meinel
Handle a plugin fallback versioning issue.
481
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
482
2432.1.25 by Robert Collins
Return plugin module docstrings for 'bzr help plugin'.
483
class TestHelpIndex(tests.TestCase):
484
    """Tests for the PluginsHelpIndex class."""
485
486
    def test_default_constructable(self):
487
        index = plugin.PluginsHelpIndex()
488
489
    def test_get_topics_None(self):
490
        """Searching for None returns an empty list."""
491
        index = plugin.PluginsHelpIndex()
492
        self.assertEqual([], index.get_topics(None))
493
2475.1.1 by Martin Pool
Rename test_plugin tests and the example module used there.
494
    def test_get_topics_for_plugin(self):
495
        """Searching for plugin name gets its docstring."""
2432.1.25 by Robert Collins
Return plugin module docstrings for 'bzr help plugin'.
496
        index = plugin.PluginsHelpIndex()
2475.1.1 by Martin Pool
Rename test_plugin tests and the example module used there.
497
        # make a new plugin here for this test, even if we're run with
498
        # --no-plugins
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
499
        self.assertFalse('breezy.plugins.demo_module' in sys.modules)
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
500
        demo_module = FakeModule('', 'breezy.plugins.demo_module')
501
        sys.modules['breezy.plugins.demo_module'] = demo_module
2457.1.1 by Robert Collins
(robertc) Fix bzr --no-plugins selftest which was broken by the help indices patch. (Robert Collins, Martin Pool)
502
        try:
2475.1.1 by Martin Pool
Rename test_plugin tests and the example module used there.
503
            topics = index.get_topics('demo_module')
2457.1.1 by Robert Collins
(robertc) Fix bzr --no-plugins selftest which was broken by the help indices patch. (Robert Collins, Martin Pool)
504
            self.assertEqual(1, len(topics))
505
            self.assertIsInstance(topics[0], plugin.ModuleHelpTopic)
506
            self.assertEqual(demo_module, topics[0].module)
507
        finally:
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
508
            del sys.modules['breezy.plugins.demo_module']
2432.1.25 by Robert Collins
Return plugin module docstrings for 'bzr help plugin'.
509
510
    def test_get_topics_no_topic(self):
511
        """Searching for something that is not a plugin returns []."""
512
        # test this by using a name that cannot be a plugin - its not
513
        # a valid python identifier.
514
        index = plugin.PluginsHelpIndex()
515
        self.assertEqual([], index.get_topics('nothing by this name'))
516
517
    def test_prefix(self):
518
        """PluginsHelpIndex has a prefix of 'plugins/'."""
519
        index = plugin.PluginsHelpIndex()
520
        self.assertEqual('plugins/', index.prefix)
521
2475.1.1 by Martin Pool
Rename test_plugin tests and the example module used there.
522
    def test_get_plugin_topic_with_prefix(self):
523
        """Searching for plugins/demo_module returns help."""
2432.1.25 by Robert Collins
Return plugin module docstrings for 'bzr help plugin'.
524
        index = plugin.PluginsHelpIndex()
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
525
        self.assertFalse('breezy.plugins.demo_module' in sys.modules)
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
526
        demo_module = FakeModule('', 'breezy.plugins.demo_module')
527
        sys.modules['breezy.plugins.demo_module'] = demo_module
2457.1.1 by Robert Collins
(robertc) Fix bzr --no-plugins selftest which was broken by the help indices patch. (Robert Collins, Martin Pool)
528
        try:
2475.1.1 by Martin Pool
Rename test_plugin tests and the example module used there.
529
            topics = index.get_topics('plugins/demo_module')
2457.1.1 by Robert Collins
(robertc) Fix bzr --no-plugins selftest which was broken by the help indices patch. (Robert Collins, Martin Pool)
530
            self.assertEqual(1, len(topics))
531
            self.assertIsInstance(topics[0], plugin.ModuleHelpTopic)
532
            self.assertEqual(demo_module, topics[0].module)
533
        finally:
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
534
            del sys.modules['breezy.plugins.demo_module']
2432.1.25 by Robert Collins
Return plugin module docstrings for 'bzr help plugin'.
535
536
537
class FakeModule(object):
538
    """A fake module to test with."""
539
540
    def __init__(self, doc, name):
541
        self.__doc__ = doc
542
        self.__name__ = name
543
544
545
class TestModuleHelpTopic(tests.TestCase):
546
    """Tests for the ModuleHelpTopic class."""
547
548
    def test_contruct(self):
549
        """Construction takes the module to document."""
550
        mod = FakeModule('foo', 'foo')
551
        topic = plugin.ModuleHelpTopic(mod)
552
        self.assertEqual(mod, topic.module)
553
554
    def test_get_help_text_None(self):
555
        """A ModuleHelpTopic returns the docstring for get_help_text."""
556
        mod = FakeModule(None, 'demo')
557
        topic = plugin.ModuleHelpTopic(mod)
558
        self.assertEqual("Plugin 'demo' has no docstring.\n",
559
            topic.get_help_text())
560
561
    def test_get_help_text_no_carriage_return(self):
562
        """ModuleHelpTopic.get_help_text adds a \n if needed."""
563
        mod = FakeModule('one line of help', 'demo')
564
        topic = plugin.ModuleHelpTopic(mod)
565
        self.assertEqual("one line of help\n",
566
            topic.get_help_text())
567
568
    def test_get_help_text_carriage_return(self):
569
        """ModuleHelpTopic.get_help_text adds a \n if needed."""
570
        mod = FakeModule('two lines of help\nand more\n', 'demo')
571
        topic = plugin.ModuleHelpTopic(mod)
572
        self.assertEqual("two lines of help\nand more\n",
573
            topic.get_help_text())
574
575
    def test_get_help_text_with_additional_see_also(self):
576
        mod = FakeModule('two lines of help\nand more', 'demo')
577
        topic = plugin.ModuleHelpTopic(mod)
6059.3.6 by Vincent Ladeuil
Fix tests failing on pqm.
578
        self.assertEqual("two lines of help\nand more\n\n:See also: bar, foo\n",
579
                         topic.get_help_text(['foo', 'bar']))
2432.1.29 by Robert Collins
Add get_help_topic to ModuleHelpTopic.
580
581
    def test_get_help_topic(self):
582
        """The help topic for a plugin is its module name."""
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
583
        mod = FakeModule('two lines of help\nand more', 'breezy.plugins.demo')
2432.1.29 by Robert Collins
Add get_help_topic to ModuleHelpTopic.
584
        topic = plugin.ModuleHelpTopic(mod)
585
        self.assertEqual('demo', topic.get_help_topic())
6059.3.6 by Vincent Ladeuil
Fix tests failing on pqm.
586
        mod = FakeModule('two lines of help\nand more',
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
587
                         'breezy.plugins.foo_bar')
2432.1.29 by Robert Collins
Add get_help_topic to ModuleHelpTopic.
588
        topic = plugin.ModuleHelpTopic(mod)
589
        self.assertEqual('foo_bar', topic.get_help_topic())
3835.2.7 by Aaron Bentley
Add tests for plugins
590
591
5086.1.2 by Vincent Ladeuil
Cosmetic changes.
592
class TestEnvPluginPath(tests.TestCase):
4628.2.1 by Vincent Ladeuil
Start introducing accessors for plugin paths.
593
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
594
    user = "USER"
595
    core = "CORE"
596
    site = "SITE"
4628.2.2 by Vincent Ladeuil
Add [+-]{user|core|site} handling in BZR_PLUGIN_PATH.
597
598
    def check_path(self, expected_dirs, setting_dirs):
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
599
        if setting_dirs is None:
600
            del os.environ['BRZ_PLUGIN_PATH']
601
        else:
602
            os.environ['BRZ_PLUGIN_PATH'] = os.pathsep.join(setting_dirs)
603
        actual = [(p if t == 'path' else t.upper())
604
            for p, t in plugin._env_plugin_path()]
605
        self.assertEqual(expected_dirs, actual)
4628.2.2 by Vincent Ladeuil
Add [+-]{user|core|site} handling in BZR_PLUGIN_PATH.
606
4628.2.1 by Vincent Ladeuil
Start introducing accessors for plugin paths.
607
    def test_default(self):
4628.2.2 by Vincent Ladeuil
Add [+-]{user|core|site} handling in BZR_PLUGIN_PATH.
608
        self.check_path([self.user, self.core, self.site],
609
                        None)
610
611
    def test_adhoc_policy(self):
612
        self.check_path([self.user, self.core, self.site],
613
                        ['+user', '+core', '+site'])
614
615
    def test_fallback_policy(self):
616
        self.check_path([self.core, self.site, self.user],
617
                        ['+core', '+site', '+user'])
618
619
    def test_override_policy(self):
620
        self.check_path([self.user, self.site, self.core],
621
                        ['+user', '+site', '+core'])
622
623
    def test_disable_user(self):
624
        self.check_path([self.core, self.site], ['-user'])
625
626
    def test_disable_user_twice(self):
627
        # Ensures multiple removals don't left cruft
628
        self.check_path([self.core, self.site], ['-user', '-user'])
629
4628.2.5 by Vincent Ladeuil
Fixes prompted by review.
630
    def test_duplicates_are_removed(self):
631
        self.check_path([self.user, self.core, self.site],
632
                        ['+user', '+user'])
633
        # And only the first reference is kept (since the later references will
5086.1.2 by Vincent Ladeuil
Cosmetic changes.
634
        # only produce '<plugin> already loaded' mutters)
4628.2.5 by Vincent Ladeuil
Fixes prompted by review.
635
        self.check_path([self.user, self.core, self.site],
636
                        ['+user', '+user', '+core',
637
                         '+user', '+site', '+site',
638
                         '+core'])
639
5086.1.5 by Vincent Ladeuil
Fix typo in test name.
640
    def test_disable_overrides_enable(self):
4628.2.2 by Vincent Ladeuil
Add [+-]{user|core|site} handling in BZR_PLUGIN_PATH.
641
        self.check_path([self.core, self.site], ['-user', '+user'])
642
643
    def test_disable_core(self):
4628.2.3 by Vincent Ladeuil
Update doc and add NEWS entry.
644
        self.check_path([self.site], ['-core'])
645
        self.check_path([self.user, self.site], ['+user', '-core'])
4628.2.2 by Vincent Ladeuil
Add [+-]{user|core|site} handling in BZR_PLUGIN_PATH.
646
647
    def test_disable_site(self):
4628.2.3 by Vincent Ladeuil
Update doc and add NEWS entry.
648
        self.check_path([self.core], ['-site'])
649
        self.check_path([self.user, self.core], ['-site', '+user'])
4628.2.2 by Vincent Ladeuil
Add [+-]{user|core|site} handling in BZR_PLUGIN_PATH.
650
651
    def test_override_site(self):
4628.2.3 by Vincent Ladeuil
Update doc and add NEWS entry.
652
        self.check_path(['mysite', self.user, self.core],
653
                        ['mysite', '-site', '+user'])
654
        self.check_path(['mysite', self.core],
4628.2.2 by Vincent Ladeuil
Add [+-]{user|core|site} handling in BZR_PLUGIN_PATH.
655
                        ['mysite', '-site'])
656
657
    def test_override_core(self):
4628.2.3 by Vincent Ladeuil
Update doc and add NEWS entry.
658
        self.check_path(['mycore', self.user, self.site],
659
                        ['mycore', '-core', '+user', '+site'])
660
        self.check_path(['mycore', self.site],
4628.2.2 by Vincent Ladeuil
Add [+-]{user|core|site} handling in BZR_PLUGIN_PATH.
661
                        ['mycore', '-core'])
662
663
    def test_my_plugin_only(self):
664
        self.check_path(['myplugin'], ['myplugin', '-user', '-core', '-site'])
665
666
    def test_my_plugin_first(self):
667
        self.check_path(['myplugin', self.core, self.site, self.user],
668
                        ['myplugin', '+core', '+site', '+user'])
4628.2.1 by Vincent Ladeuil
Start introducing accessors for plugin paths.
669
4628.2.5 by Vincent Ladeuil
Fixes prompted by review.
670
    def test_bogus_references(self):
671
        self.check_path(['+foo', '-bar', self.core, self.site],
672
                        ['+foo', '-bar'])
5086.1.4 by Vincent Ladeuil
Slight plugin tests rewriting.
673
5086.1.6 by Vincent Ladeuil
Crude fix for bug #411413.
674
5616.7.10 by Martin Pool
Clean up describe_plugins to sort loaded and unloaded plugins together.
675
class TestDisablePlugin(BaseTestPlugins):
5086.1.6 by Vincent Ladeuil
Crude fix for bug #411413.
676
5086.1.8 by Vincent Ladeuil
Fix warnings during autoload, add doc and a NEWS entry.
677
    def test_cannot_import(self):
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
678
        self.create_plugin_package('works')
679
        self.create_plugin_package('fails')
680
        self.overrideEnv('BRZ_DISABLE_PLUGINS', 'fails')
681
        self.update_module_paths(["."])
682
        import breezy.testingplugins.works as works
5086.1.6 by Vincent Ladeuil
Crude fix for bug #411413.
683
        try:
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
684
            import breezy.testingplugins.fails as fails
5086.1.6 by Vincent Ladeuil
Crude fix for bug #411413.
685
        except ImportError:
686
            pass
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
687
        else:
688
            self.fail("Loaded blocked plugin: " + repr(fails))
689
        self.assertPluginModules({'fails': None, 'works': works})
690
691
    def test_partial_imports(self):
692
        self.create_plugin('good')
693
        self.create_plugin('bad')
694
        self.create_plugin_package('ugly')
695
        self.overrideEnv('BRZ_DISABLE_PLUGINS', 'bad:ugly')
696
        self.load_with_paths(['.'])
697
        self.assertEqual({'good'}, viewkeys(self.plugins))
698
        self.assertPluginModules({
699
            'good': self.plugins['good'].module,
700
            'bad': None,
701
            'ugly': None,
702
        })
703
        # Ensure there are no warnings about plugins not being imported as
704
        # the user has explictly requested they be disabled.
705
        self.assertNotContainsRe(self.get_log(), r"Unable to load plugin")
706
707
708
class TestEnvDisablePlugins(tests.TestCase):
709
710
    def _get_names(self, env_value):
711
        os.environ['BRZ_DISABLE_PLUGINS'] = env_value
712
        return plugin._env_disable_plugins()
713
714
    def test_unset(self):
715
        self.assertEqual([], plugin._env_disable_plugins())
716
717
    def test_empty(self):
718
        self.assertEqual([], self._get_names(''))
719
720
    def test_single(self):
721
        self.assertEqual(['single'], self._get_names('single'))
722
723
    def test_multi(self):
724
        expected = ['one', 'two']
725
        self.assertEqual(expected, self._get_names(os.pathsep.join(expected)))
726
727
    def test_mixed(self):
728
        value = os.pathsep.join(['valid', 'in-valid'])
729
        self.assertEqual(['valid'], self._get_names(value))
730
        self.assertContainsRe(self.get_log(),
731
            r"Invalid name 'in-valid' in BRZ_DISABLE_PLUGINS=" + repr(value))
732
733
734
class TestEnvPluginsAt(tests.TestCase):
735
736
    def _get_paths(self, env_value):
737
        os.environ['BRZ_PLUGINS_AT'] = env_value
738
        return plugin._env_plugins_at()
739
740
    def test_empty(self):
741
        self.assertEqual([], plugin._env_plugins_at())
6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
742
        self.assertEqual([], self._get_paths(''))
5268.5.1 by Vincent Ladeuil
Reproduce bug #591215.
743
744
    def test_one_path(self):
6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
745
        self.assertEqual([('b', 'man')], self._get_paths('b@man'))
5268.5.1 by Vincent Ladeuil
Reproduce bug #591215.
746
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
747
    def test_multiple(self):
748
        self.assertEqual(
749
            [('tools', 'bzr-tools'), ('p', 'play.py')],
750
            self._get_paths(os.pathsep.join(('tools@bzr-tools', 'p@play.py'))))
751
752
    def test_many_at(self):
753
        self.assertEqual(
754
            [('church', 'StMichael@Plea@Norwich')],
755
            self._get_paths('church@StMichael@Plea@Norwich'))
756
757
    def test_only_py(self):
758
        self.assertEqual([('test', './test.py')], self._get_paths('./test.py'))
759
760
    def test_only_package(self):
761
        self.assertEqual([('py', '/opt/b/py')], self._get_paths('/opt/b/py'))
762
763
    def test_bad_name(self):
764
        self.assertEqual([], self._get_paths('/usr/local/bzr-git'))
765
        self.assertContainsRe(self.get_log(),
766
            r"Invalid name 'bzr-git' in BRZ_PLUGINS_AT='/usr/local/bzr-git'")
5268.5.1 by Vincent Ladeuil
Reproduce bug #591215.
767
768
5616.7.10 by Martin Pool
Clean up describe_plugins to sort loaded and unloaded plugins together.
769
class TestLoadPluginAt(BaseTestPlugins):
5086.5.3 by Vincent Ladeuil
First shot at loading plugins from a specific directory.
770
771
    def setUp(self):
772
        super(TestLoadPluginAt, self).setUp()
773
        # Create the same plugin in two directories
5086.5.8 by Vincent Ladeuil
Make sure we can load from a non-standard directory name.
774
        self.create_plugin_package('test_foo', dir='non-standard-dir')
5086.5.13 by Vincent Ladeuil
Reproduce bug #552922.
775
        # The "normal" directory, we use 'standard' instead of 'plugins' to
776
        # avoid depending on the precise naming.
777
        self.create_plugin_package('test_foo', dir='standard/test_foo')
5086.5.8 by Vincent Ladeuil
Make sure we can load from a non-standard directory name.
778
5086.5.14 by Vincent Ladeuil
Fix bug #552922 by controlling which files can be used to load a plugin.
779
    def assertTestFooLoadedFrom(self, path):
5086.5.8 by Vincent Ladeuil
Make sure we can load from a non-standard directory name.
780
        self.assertPluginKnown('test_foo')
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
781
        self.assertDocstring('This is the doc for test_foo',
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
782
                             self.module.test_foo)
783
        self.assertEqual(path, self.module.test_foo.dir_source)
5086.5.3 by Vincent Ladeuil
First shot at loading plugins from a specific directory.
784
785
    def test_regular_load(self):
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
786
        self.load_with_paths(['standard'])
5086.5.13 by Vincent Ladeuil
Reproduce bug #552922.
787
        self.assertTestFooLoadedFrom('standard/test_foo')
5086.5.3 by Vincent Ladeuil
First shot at loading plugins from a specific directory.
788
789
    def test_import(self):
6622.1.28 by Jelmer Vernooij
More renames; commands in output, environment variables.
790
        self.overrideEnv('BRZ_PLUGINS_AT', 'test_foo@non-standard-dir')
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
791
        self.update_module_paths(['standard'])
792
        import breezy.testingplugins.test_foo
5086.5.8 by Vincent Ladeuil
Make sure we can load from a non-standard directory name.
793
        self.assertTestFooLoadedFrom('non-standard-dir')
794
795
    def test_loading(self):
6622.1.28 by Jelmer Vernooij
More renames; commands in output, environment variables.
796
        self.overrideEnv('BRZ_PLUGINS_AT', 'test_foo@non-standard-dir')
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
797
        self.load_with_paths(['standard'])
798
        self.assertTestFooLoadedFrom('non-standard-dir')
799
800
    def test_loading_other_name(self):
801
        self.overrideEnv('BRZ_PLUGINS_AT', 'test_foo@non-standard-dir')
802
        os.rename('standard/test_foo', 'standard/test_bar')
803
        self.load_with_paths(['standard'])
5086.5.9 by Vincent Ladeuil
More tests.
804
        self.assertTestFooLoadedFrom('non-standard-dir')
805
806
    def test_compiled_loaded(self):
6622.1.28 by Jelmer Vernooij
More renames; commands in output, environment variables.
807
        self.overrideEnv('BRZ_PLUGINS_AT', 'test_foo@non-standard-dir')
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
808
        self.load_with_paths(['standard'])
5086.5.9 by Vincent Ladeuil
More tests.
809
        self.assertTestFooLoadedFrom('non-standard-dir')
5235.1.1 by Martin
Make BZR_PLUGINS_AT tests that check filenames use a path-based assertion method rather than just string comparison
810
        self.assertIsSameRealPath('non-standard-dir/__init__.py',
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
811
                                  self.module.test_foo.__file__)
5086.5.9 by Vincent Ladeuil
More tests.
812
813
        # Try importing again now that the source has been compiled
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
814
        os.remove('non-standard-dir/__init__.py')
815
        self.promote_cache('non-standard-dir')
816
        self.reset()
817
        self.load_with_paths(['standard'])
5086.5.9 by Vincent Ladeuil
More tests.
818
        self.assertTestFooLoadedFrom('non-standard-dir')
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
819
        suffix = plugin.COMPILED_EXT
820
        self.assertIsSameRealPath('non-standard-dir/__init__' + suffix,
821
                                  self.module.test_foo.__file__)
5086.5.9 by Vincent Ladeuil
More tests.
822
823
    def test_submodule_loading(self):
824
        # We create an additional directory under the one for test_foo
825
        self.create_plugin_package('test_bar', dir='non-standard-dir/test_bar')
6622.1.28 by Jelmer Vernooij
More renames; commands in output, environment variables.
826
        self.overrideEnv('BRZ_PLUGINS_AT', 'test_foo@non-standard-dir')
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
827
        self.update_module_paths(['standard'])
828
        import breezy.testingplugins.test_foo
829
        self.assertEqual(self.module_prefix + 'test_foo',
830
                         self.module.test_foo.__package__)
831
        import breezy.testingplugins.test_foo.test_bar
5235.1.1 by Martin
Make BZR_PLUGINS_AT tests that check filenames use a path-based assertion method rather than just string comparison
832
        self.assertIsSameRealPath('non-standard-dir/test_bar/__init__.py',
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
833
                                  self.module.test_foo.test_bar.__file__)
5086.5.13 by Vincent Ladeuil
Reproduce bug #552922.
834
5268.6.2 by Vincent Ladeuil
Reproduce bug #588959.
835
    def test_relative_submodule_loading(self):
836
        self.create_plugin_package('test_foo', dir='another-dir', source='''
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
837
from . import test_bar
5268.6.2 by Vincent Ladeuil
Reproduce bug #588959.
838
''')
839
        # We create an additional directory under the one for test_foo
840
        self.create_plugin_package('test_bar', dir='another-dir/test_bar')
6622.1.28 by Jelmer Vernooij
More renames; commands in output, environment variables.
841
        self.overrideEnv('BRZ_PLUGINS_AT', 'test_foo@another-dir')
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
842
        self.update_module_paths(['standard'])
843
        import breezy.testingplugins.test_foo
844
        self.assertEqual(self.module_prefix + 'test_foo',
845
                         self.module.test_foo.__package__)
5268.6.2 by Vincent Ladeuil
Reproduce bug #588959.
846
        self.assertIsSameRealPath('another-dir/test_bar/__init__.py',
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
847
                                  self.module.test_foo.test_bar.__file__)
5268.6.2 by Vincent Ladeuil
Reproduce bug #588959.
848
5086.5.15 by Vincent Ladeuil
Fixed as per Ian's review.
849
    def test_loading_from___init__only(self):
5086.5.13 by Vincent Ladeuil
Reproduce bug #552922.
850
        # We rename the existing __init__.py file to ensure that we don't load
851
        # a random file
852
        init = 'non-standard-dir/__init__.py'
853
        random = 'non-standard-dir/setup.py'
854
        os.rename(init, random)
6622.1.28 by Jelmer Vernooij
More renames; commands in output, environment variables.
855
        self.overrideEnv('BRZ_PLUGINS_AT', 'test_foo@non-standard-dir')
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
856
        self.load_with_paths(['standard'])
5086.5.13 by Vincent Ladeuil
Reproduce bug #552922.
857
        self.assertPluginUnknown('test_foo')
5086.5.14 by Vincent Ladeuil
Fix bug #552922 by controlling which files can be used to load a plugin.
858
859
    def test_loading_from_specific_file(self):
860
        plugin_dir = 'non-standard-dir'
861
        plugin_file_name = 'iamtestfoo.py'
862
        plugin_path = osutils.pathjoin(plugin_dir, plugin_file_name)
863
        source = '''\
864
"""This is the doc for %s"""
865
dir_source = '%s'
866
''' % ('test_foo', plugin_path)
867
        self.create_plugin('test_foo', source=source,
868
                           dir=plugin_dir, file_name=plugin_file_name)
6622.1.28 by Jelmer Vernooij
More renames; commands in output, environment variables.
869
        self.overrideEnv('BRZ_PLUGINS_AT', 'test_foo@%s' % plugin_path)
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
870
        self.load_with_paths(['standard'])
5086.5.14 by Vincent Ladeuil
Fix bug #552922 by controlling which files can be used to load a plugin.
871
        self.assertTestFooLoadedFrom(plugin_path)
5616.7.10 by Martin Pool
Clean up describe_plugins to sort loaded and unloaded plugins together.
872
873
874
class TestDescribePlugins(BaseTestPlugins):
875
876
    def test_describe_plugins(self):
5616.7.11 by Martin Pool
Additional tests and fixes for refactored describe_plugins.
877
        class DummyModule(object):
878
            __doc__ = 'Hi there'
879
        class DummyPlugin(object):
880
            __version__ = '0.1.0'
881
            module = DummyModule()
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
882
        self.plugin_warnings = {'bad': ['Failed to load (just testing)']}
883
        self.plugins = {'good': DummyPlugin()}
6614.1.3 by Vincent Ladeuil
Fix assertEquals being deprecated by using assertEqual.
884
        self.assertEqual("""\
5616.7.10 by Martin Pool
Clean up describe_plugins to sort loaded and unloaded plugins together.
885
bad (failed to load)
886
  ** Failed to load (just testing)
887
5616.7.11 by Martin Pool
Additional tests and fixes for refactored describe_plugins.
888
good 0.1.0
889
  Hi there
890
6651.4.1 by Martin
Rewrite of the plugin module for Python 3 compat and general sanity
891
""", ''.join(plugin.describe_plugins(state=self)))