~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_plugins.py

  • Committer: Martin Pool
  • Date: 2009-08-20 04:53:23 UTC
  • mto: This revision was merged to the branch mainline in revision 4632.
  • Revision ID: mbp@sourcefrog.net-20090820045323-4hsicfa87pdq3l29
Correction to xdg_cache_dir and add a simple test

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 by Canonical Ltd
 
1
# Copyright (C) 2005, 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
 
 
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
 
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
16
 
18
17
"""Tests for plugins"""
19
18
 
21
20
# affects the global state of the process.  See bzrlib/plugins.py for more
22
21
# comments.
23
22
 
 
23
import logging
24
24
import os
25
25
from StringIO import StringIO
 
26
import sys
 
27
import zipfile
26
28
 
 
29
from bzrlib import plugin, tests
27
30
import bzrlib.plugin
28
31
import bzrlib.plugins
29
32
import bzrlib.commands
30
33
import bzrlib.help
31
 
from bzrlib.tests import TestCaseInTempDir
32
 
from bzrlib.osutils import pathjoin, abspath
33
 
 
34
 
class PluginTest(TestCaseInTempDir):
35
 
    """Create an external plugin and test loading."""
36
 
#    def test_plugin_loading(self):
37
 
#        orig_help = self.run_bzr_captured('bzr help commands')[0]
38
 
#        os.mkdir('plugin_test')
39
 
#        f = open(pathjoin('plugin_test', 'myplug.py'), 'wt')
40
 
#        f.write(PLUGIN_TEXT)
41
 
#        f.close()
42
 
#        newhelp = self.run_bzr_captured('bzr help commands')[0]
43
 
#        assert newhelp.startswith('You have been overridden\n')
44
 
#        # We added a line, but the rest should work
45
 
#        assert newhelp[25:] == help
46
 
#
47
 
#        assert backtick('bzr commit -m test') == "I'm sorry dave, you can't do that\n"
48
 
#
49
 
#        shutil.rmtree('plugin_test')
50
 
#
51
 
 
52
 
#         os.environ['BZRPLUGINPATH'] = abspath('plugin_test')
53
 
#         help = backtick('bzr help commands')
54
 
#         assert help.find('myplug') != -1
55
 
#         assert help.find('Just a simple test plugin.') != -1
56
 
 
57
 
 
58
 
#         assert backtick('bzr myplug') == 'Hello from my plugin\n'
59
 
#         assert backtick('bzr mplg') == 'Hello from my plugin\n'
60
 
 
61
 
#         f = open(pathjoin('plugin_test', 'override.py'), 'wb')
62
 
#         f.write("""import bzrlib, bzrlib.commands
63
 
#     class cmd_commit(bzrlib.commands.cmd_commit):
64
 
#         '''Commit changes into a new revision.'''
65
 
#         def run(self, *args, **kwargs):
66
 
#             print "I'm sorry dave, you can't do that"
67
 
 
68
 
#     class cmd_help(bzrlib.commands.cmd_help):
69
 
#         '''Show help on a command or other topic.'''
70
 
#         def run(self, *args, **kwargs):
71
 
#             print "You have been overridden"
72
 
#             bzrlib.commands.cmd_help.run(self, *args, **kwargs)
73
 
 
74
 
#         """
 
34
from bzrlib.tests import (
 
35
    TestCase,
 
36
    TestCaseInTempDir,
 
37
    TestUtil,
 
38
    )
 
39
from bzrlib.osutils import pathjoin, abspath, normpath
 
40
 
75
41
 
76
42
PLUGIN_TEXT = """\
77
43
import bzrlib.commands
84
50
 
85
51
# TODO: Write a test for plugin decoration of commands.
86
52
 
87
 
class TestOneNamedPluginOnly(TestCaseInTempDir):
 
53
class TestLoadingPlugins(TestCaseInTempDir):
88
54
 
89
55
    activeattributes = {}
90
56
 
91
57
    def test_plugins_with_the_same_name_are_not_loaded(self):
 
58
        # This test tests that having two plugins in different directories does
 
59
        # not result in both being loaded when they have the same name.  get a
 
60
        # file name we can use which is also a valid attribute for accessing in
 
61
        # activeattributes. - we cannot give import parameters.
 
62
        tempattribute = "0"
 
63
        self.failIf(tempattribute in self.activeattributes)
 
64
        # set a place for the plugins to record their loading, and at the same
 
65
        # time validate that the location the plugins should record to is
 
66
        # valid and correct.
 
67
        bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
 
68
            [tempattribute] = []
 
69
        self.failUnless(tempattribute in self.activeattributes)
 
70
        # create two plugin directories
 
71
        os.mkdir('first')
 
72
        os.mkdir('second')
 
73
        # write a plugin that will record when its loaded in the
 
74
        # tempattribute list.
 
75
        template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
 
76
                    "TestLoadingPlugins.activeattributes[%r].append('%s')\n")
 
77
 
 
78
        outfile = open(os.path.join('first', 'plugin.py'), 'w')
 
79
        try:
 
80
            outfile.write(template % (tempattribute, 'first'))
 
81
            outfile.write('\n')
 
82
        finally:
 
83
            outfile.close()
 
84
 
 
85
        outfile = open(os.path.join('second', 'plugin.py'), 'w')
 
86
        try:
 
87
            outfile.write(template % (tempattribute, 'second'))
 
88
            outfile.write('\n')
 
89
        finally:
 
90
            outfile.close()
 
91
 
 
92
        try:
 
93
            bzrlib.plugin.load_from_path(['first', 'second'])
 
94
            self.assertEqual(['first'], self.activeattributes[tempattribute])
 
95
        finally:
 
96
            # remove the plugin 'plugin'
 
97
            del self.activeattributes[tempattribute]
 
98
            if 'bzrlib.plugins.plugin' in sys.modules:
 
99
                del sys.modules['bzrlib.plugins.plugin']
 
100
            if getattr(bzrlib.plugins, 'plugin', None):
 
101
                del bzrlib.plugins.plugin
 
102
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
 
103
 
 
104
    def test_plugins_from_different_dirs_can_demand_load(self):
92
105
        # This test tests that having two plugins in different
93
 
        # directories does not result in both being loaded.
94
 
        # get a file name we can use which is also a valid attribute
 
106
        # directories with different names allows them both to be loaded, when
 
107
        # we do a direct import statement.
 
108
        # Determine a file name we can use which is also a valid attribute
95
109
        # for accessing in activeattributes. - we cannot give import parameters.
96
 
        tempattribute = "0"
 
110
        tempattribute = "different-dirs"
97
111
        self.failIf(tempattribute in self.activeattributes)
98
112
        # set a place for the plugins to record their loading, and at the same
99
113
        # time validate that the location the plugins should record to is
100
114
        # valid and correct.
101
 
        bzrlib.tests.test_plugins.TestOneNamedPluginOnly.activeattributes \
 
115
        bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
102
116
            [tempattribute] = []
103
117
        self.failUnless(tempattribute in self.activeattributes)
104
118
        # create two plugin directories
105
119
        os.mkdir('first')
106
120
        os.mkdir('second')
107
 
        # write a plugin that will record when its loaded in the 
 
121
        # write plugins that will record when they are loaded in the
108
122
        # tempattribute list.
109
 
        template = ("from bzrlib.tests.test_plugins import TestOneNamedPluginOnly\n"
110
 
                    "TestOneNamedPluginOnly.activeattributes[%r].append('%s')\n")
111
 
        print >> file(os.path.join('first', 'plugin.py'), 'w'), template % (tempattribute, 'first')
112
 
        print >> file(os.path.join('second', 'plugin.py'), 'w'), template % (tempattribute, 'second')
113
 
        try:
114
 
            bzrlib.plugin.load_from_dirs(['first', 'second'])
 
123
        template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
 
124
                    "TestLoadingPlugins.activeattributes[%r].append('%s')\n")
 
125
 
 
126
        outfile = open(os.path.join('first', 'pluginone.py'), 'w')
 
127
        try:
 
128
            outfile.write(template % (tempattribute, 'first'))
 
129
            outfile.write('\n')
 
130
        finally:
 
131
            outfile.close()
 
132
 
 
133
        outfile = open(os.path.join('second', 'plugintwo.py'), 'w')
 
134
        try:
 
135
            outfile.write(template % (tempattribute, 'second'))
 
136
            outfile.write('\n')
 
137
        finally:
 
138
            outfile.close()
 
139
 
 
140
        oldpath = bzrlib.plugins.__path__
 
141
        try:
 
142
            bzrlib.plugins.__path__ = ['first', 'second']
 
143
            exec "import bzrlib.plugins.pluginone"
115
144
            self.assertEqual(['first'], self.activeattributes[tempattribute])
116
 
        finally:
117
 
            # remove the plugin 'plugin'
118
 
            del self.activeattributes[tempattribute]
119
 
            if getattr(bzrlib.plugins, 'plugin', None):
120
 
                del bzrlib.plugins.plugin
121
 
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
122
 
 
123
 
 
124
 
class TestAllPlugins(TestCaseInTempDir):
125
 
 
126
 
    def test_plugin_appears_in_all_plugins(self):
127
 
        # This test tests a new plugin appears in bzrlib.plugin.all_plugins().
 
145
            exec "import bzrlib.plugins.plugintwo"
 
146
            self.assertEqual(['first', 'second'],
 
147
                self.activeattributes[tempattribute])
 
148
        finally:
 
149
            # remove the plugin 'plugin'
 
150
            del self.activeattributes[tempattribute]
 
151
            if getattr(bzrlib.plugins, 'pluginone', None):
 
152
                del bzrlib.plugins.pluginone
 
153
            if getattr(bzrlib.plugins, 'plugintwo', None):
 
154
                del bzrlib.plugins.plugintwo
 
155
        self.failIf(getattr(bzrlib.plugins, 'pluginone', None))
 
156
        self.failIf(getattr(bzrlib.plugins, 'plugintwo', None))
 
157
 
 
158
    def test_plugins_can_load_from_directory_with_trailing_slash(self):
 
159
        # This test tests that a plugin can load from a directory when the
 
160
        # directory in the path has a trailing slash.
 
161
        # check the plugin is not loaded already
 
162
        self.failIf(getattr(bzrlib.plugins, 'ts_plugin', None))
 
163
        tempattribute = "trailing-slash"
 
164
        self.failIf(tempattribute in self.activeattributes)
 
165
        # set a place for the plugin to record its loading, and at the same
 
166
        # time validate that the location the plugin should record to is
 
167
        # valid and correct.
 
168
        bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
 
169
            [tempattribute] = []
 
170
        self.failUnless(tempattribute in self.activeattributes)
 
171
        # create a directory for the plugin
 
172
        os.mkdir('plugin_test')
 
173
        # write a plugin that will record when its loaded in the
 
174
        # tempattribute list.
 
175
        template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
 
176
                    "TestLoadingPlugins.activeattributes[%r].append('%s')\n")
 
177
 
 
178
        outfile = open(os.path.join('plugin_test', 'ts_plugin.py'), 'w')
 
179
        try:
 
180
            outfile.write(template % (tempattribute, 'plugin'))
 
181
            outfile.write('\n')
 
182
        finally:
 
183
            outfile.close()
 
184
 
 
185
        try:
 
186
            bzrlib.plugin.load_from_path(['plugin_test'+os.sep])
 
187
            self.assertEqual(['plugin'], self.activeattributes[tempattribute])
 
188
        finally:
 
189
            # remove the plugin 'plugin'
 
190
            del self.activeattributes[tempattribute]
 
191
            if getattr(bzrlib.plugins, 'ts_plugin', None):
 
192
                del bzrlib.plugins.ts_plugin
 
193
        self.failIf(getattr(bzrlib.plugins, 'ts_plugin', None))
 
194
 
 
195
    def load_and_capture(self, name):
 
196
        """Load plugins from '.' capturing the output.
 
197
 
 
198
        :param name: The name of the plugin.
 
199
        :return: A string with the log from the plugin loading call.
 
200
        """
 
201
        # Capture output
 
202
        stream = StringIO()
 
203
        try:
 
204
            handler = logging.StreamHandler(stream)
 
205
            log = logging.getLogger('bzr')
 
206
            log.addHandler(handler)
 
207
            try:
 
208
                try:
 
209
                    bzrlib.plugin.load_from_path(['.'])
 
210
                finally:
 
211
                    if 'bzrlib.plugins.%s' % name in sys.modules:
 
212
                        del sys.modules['bzrlib.plugins.%s' % name]
 
213
                    if getattr(bzrlib.plugins, name, None):
 
214
                        delattr(bzrlib.plugins, name)
 
215
            finally:
 
216
                # Stop capturing output
 
217
                handler.flush()
 
218
                handler.close()
 
219
                log.removeHandler(handler)
 
220
            return stream.getvalue()
 
221
        finally:
 
222
            stream.close()
 
223
 
 
224
    def test_plugin_with_bad_api_version_reports(self):
 
225
        # This plugin asks for bzrlib api version 1.0.0, which is not supported
 
226
        # anymore.
 
227
        name = 'wants100.py'
 
228
        f = file(name, 'w')
 
229
        try:
 
230
            f.write("import bzrlib.api\n"
 
231
                "bzrlib.api.require_any_api(bzrlib, [(1, 0, 0)])\n")
 
232
        finally:
 
233
            f.close()
 
234
 
 
235
        log = self.load_and_capture(name)
 
236
        self.assertContainsRe(log,
 
237
            r"It requested API version")
 
238
 
 
239
    def test_plugin_with_bad_name_does_not_load(self):
 
240
        # The file name here invalid for a python module.
 
241
        name = 'bzr-bad plugin-name..py'
 
242
        file(name, 'w').close()
 
243
        log = self.load_and_capture(name)
 
244
        self.assertContainsRe(log,
 
245
            r"Unable to load 'bzr-bad plugin-name\.' in '\.' as a plugin "
 
246
            "because the file path isn't a valid module name; try renaming "
 
247
            "it to 'bad_plugin_name_'\.")
 
248
 
 
249
 
 
250
class TestPlugins(TestCaseInTempDir):
 
251
 
 
252
    def setup_plugin(self, source=""):
 
253
        # This test tests a new plugin appears in bzrlib.plugin.plugins().
128
254
        # check the plugin is not loaded already
129
255
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
130
256
        # write a plugin that _cannot_ fail to load.
131
 
        print >> file('plugin.py', 'w'), ""
132
 
        try:
133
 
            bzrlib.plugin.load_from_dirs(['.'])
134
 
            self.failUnless('plugin' in bzrlib.plugin.all_plugins())
135
 
            self.failUnless(getattr(bzrlib.plugins, 'plugin', None))
136
 
            self.assertEqual(bzrlib.plugin.all_plugins()['plugin'],
137
 
                             bzrlib.plugins.plugin)
138
 
        finally:
139
 
            # remove the plugin 'plugin'
140
 
            if getattr(bzrlib.plugins, 'plugin', None):
141
 
                del bzrlib.plugins.plugin
 
257
        file('plugin.py', 'w').write(source + '\n')
 
258
        self.addCleanup(self.teardown_plugin)
 
259
        bzrlib.plugin.load_from_path(['.'])
 
260
 
 
261
    def teardown_plugin(self):
 
262
        # remove the plugin 'plugin'
 
263
        if 'bzrlib.plugins.plugin' in sys.modules:
 
264
            del sys.modules['bzrlib.plugins.plugin']
 
265
        if getattr(bzrlib.plugins, 'plugin', None):
 
266
            del bzrlib.plugins.plugin
142
267
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
143
268
 
 
269
    def test_plugin_appears_in_plugins(self):
 
270
        self.setup_plugin()
 
271
        self.failUnless('plugin' in bzrlib.plugin.plugins())
 
272
        self.failUnless(getattr(bzrlib.plugins, 'plugin', None))
 
273
        plugins = bzrlib.plugin.plugins()
 
274
        plugin = plugins['plugin']
 
275
        self.assertIsInstance(plugin, bzrlib.plugin.PlugIn)
 
276
        self.assertEqual(bzrlib.plugins.plugin, plugin.module)
 
277
 
 
278
    def test_trivial_plugin_get_path(self):
 
279
        self.setup_plugin()
 
280
        plugins = bzrlib.plugin.plugins()
 
281
        plugin = plugins['plugin']
 
282
        plugin_path = self.test_dir + '/plugin.py'
 
283
        self.assertIsSameRealPath(plugin_path, normpath(plugin.path()))
 
284
 
 
285
    def test_plugin_get_path_py_not_pyc(self):
 
286
        self.setup_plugin()         # after first import there will be plugin.pyc
 
287
        self.teardown_plugin()
 
288
        bzrlib.plugin.load_from_path(['.']) # import plugin.pyc
 
289
        plugins = bzrlib.plugin.plugins()
 
290
        plugin = plugins['plugin']
 
291
        plugin_path = self.test_dir + '/plugin.py'
 
292
        self.assertIsSameRealPath(plugin_path, normpath(plugin.path()))
 
293
 
 
294
    def test_plugin_get_path_pyc_only(self):
 
295
        self.setup_plugin()         # after first import there will be plugin.pyc
 
296
        self.teardown_plugin()
 
297
        os.unlink(self.test_dir + '/plugin.py')
 
298
        bzrlib.plugin.load_from_path(['.']) # import plugin.pyc
 
299
        plugins = bzrlib.plugin.plugins()
 
300
        plugin = plugins['plugin']
 
301
        if __debug__:
 
302
            plugin_path = self.test_dir + '/plugin.pyc'
 
303
        else:
 
304
            plugin_path = self.test_dir + '/plugin.pyo'
 
305
        self.assertIsSameRealPath(plugin_path, normpath(plugin.path()))
 
306
 
 
307
    def test_no_test_suite_gives_None_for_test_suite(self):
 
308
        self.setup_plugin()
 
309
        plugin = bzrlib.plugin.plugins()['plugin']
 
310
        self.assertEqual(None, plugin.test_suite())
 
311
 
 
312
    def test_test_suite_gives_test_suite_result(self):
 
313
        source = """def test_suite(): return 'foo'"""
 
314
        self.setup_plugin(source)
 
315
        plugin = bzrlib.plugin.plugins()['plugin']
 
316
        self.assertEqual('foo', plugin.test_suite())
 
317
 
 
318
    def test_no_load_plugin_tests_gives_None_for_load_plugin_tests(self):
 
319
        self.setup_plugin()
 
320
        loader = TestUtil.TestLoader()
 
321
        plugin = bzrlib.plugin.plugins()['plugin']
 
322
        self.assertEqual(None, plugin.load_plugin_tests(loader))
 
323
 
 
324
    def test_load_plugin_tests_gives_load_plugin_tests_result(self):
 
325
        source = """
 
326
def load_tests(standard_tests, module, loader):
 
327
    return 'foo'"""
 
328
        self.setup_plugin(source)
 
329
        loader = TestUtil.TestLoader()
 
330
        plugin = bzrlib.plugin.plugins()['plugin']
 
331
        self.assertEqual('foo', plugin.load_plugin_tests(loader))
 
332
 
 
333
    def test_no_version_info(self):
 
334
        self.setup_plugin()
 
335
        plugin = bzrlib.plugin.plugins()['plugin']
 
336
        self.assertEqual(None, plugin.version_info())
 
337
 
 
338
    def test_with_version_info(self):
 
339
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 4)")
 
340
        plugin = bzrlib.plugin.plugins()['plugin']
 
341
        self.assertEqual((1, 2, 3, 'dev', 4), plugin.version_info())
 
342
 
 
343
    def test_short_version_info_gets_padded(self):
 
344
        # the gtk plugin has version_info = (1,2,3) rather than the 5-tuple.
 
345
        # so we adapt it
 
346
        self.setup_plugin("version_info = (1, 2, 3)")
 
347
        plugin = bzrlib.plugin.plugins()['plugin']
 
348
        self.assertEqual((1, 2, 3, 'final', 0), plugin.version_info())
 
349
 
 
350
    def test_no_version_info___version__(self):
 
351
        self.setup_plugin()
 
352
        plugin = bzrlib.plugin.plugins()['plugin']
 
353
        self.assertEqual("unknown", plugin.__version__)
 
354
 
 
355
    def test_str__version__with_version_info(self):
 
356
        self.setup_plugin("version_info = '1.2.3'")
 
357
        plugin = bzrlib.plugin.plugins()['plugin']
 
358
        self.assertEqual("1.2.3", plugin.__version__)
 
359
 
 
360
    def test_noniterable__version__with_version_info(self):
 
361
        self.setup_plugin("version_info = (1)")
 
362
        plugin = bzrlib.plugin.plugins()['plugin']
 
363
        self.assertEqual("1", plugin.__version__)
 
364
 
 
365
    def test_1__version__with_version_info(self):
 
366
        self.setup_plugin("version_info = (1,)")
 
367
        plugin = bzrlib.plugin.plugins()['plugin']
 
368
        self.assertEqual("1", plugin.__version__)
 
369
 
 
370
    def test_1_2__version__with_version_info(self):
 
371
        self.setup_plugin("version_info = (1, 2)")
 
372
        plugin = bzrlib.plugin.plugins()['plugin']
 
373
        self.assertEqual("1.2", plugin.__version__)
 
374
 
 
375
    def test_1_2_3__version__with_version_info(self):
 
376
        self.setup_plugin("version_info = (1, 2, 3)")
 
377
        plugin = bzrlib.plugin.plugins()['plugin']
 
378
        self.assertEqual("1.2.3", plugin.__version__)
 
379
 
 
380
    def test_candidate__version__with_version_info(self):
 
381
        self.setup_plugin("version_info = (1, 2, 3, 'candidate', 1)")
 
382
        plugin = bzrlib.plugin.plugins()['plugin']
 
383
        self.assertEqual("1.2.3rc1", plugin.__version__)
 
384
 
 
385
    def test_dev__version__with_version_info(self):
 
386
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 0)")
 
387
        plugin = bzrlib.plugin.plugins()['plugin']
 
388
        self.assertEqual("1.2.3dev", plugin.__version__)
 
389
 
 
390
    def test_dev_fallback__version__with_version_info(self):
 
391
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 4)")
 
392
        plugin = bzrlib.plugin.plugins()['plugin']
 
393
        self.assertEqual("1.2.3.dev.4", plugin.__version__)
 
394
 
 
395
    def test_final__version__with_version_info(self):
 
396
        self.setup_plugin("version_info = (1, 2, 3, 'final', 0)")
 
397
        plugin = bzrlib.plugin.plugins()['plugin']
 
398
        self.assertEqual("1.2.3", plugin.__version__)
 
399
 
144
400
 
145
401
class TestPluginHelp(TestCaseInTempDir):
146
402
 
147
403
    def split_help_commands(self):
148
404
        help = {}
149
405
        current = None
150
 
        for line in self.capture('help commands').splitlines():
151
 
            if line.startswith('bzr '):
152
 
                current = line.split()[1]
 
406
        out, err = self.run_bzr('--no-plugins help commands')
 
407
        for line in out.splitlines():
 
408
            if not line.startswith(' '):
 
409
                current = line.split()[0]
153
410
            help[current] = help.get(current, '') + line
154
411
 
155
412
        return help
160
417
        for cmd_name in bzrlib.commands.builtin_command_names():
161
418
            if cmd_name in bzrlib.commands.plugin_command_names():
162
419
                continue
163
 
            help = StringIO()
164
420
            try:
165
 
                bzrlib.help.help_on_command(cmd_name, help)
 
421
                help = bzrlib.commands.get_cmd_object(cmd_name).get_help_text()
166
422
            except NotImplementedError:
167
423
                # some commands have no help
168
424
                pass
169
425
            else:
170
 
                help.seek(0)
171
 
                self.assertNotContainsRe(help.read(), 'From plugin "[^"]*"')
 
426
                self.assertNotContainsRe(help, 'plugin "[^"]*"')
172
427
 
173
 
            if help in help_commands.keys():
 
428
            if cmd_name in help_commands.keys():
174
429
                # some commands are hidden
175
430
                help = help_commands[cmd_name]
176
 
                self.assertNotContainsRe(help, 'From plugin "[^"]*"')
 
431
                self.assertNotContainsRe(help, 'plugin "[^"]*"')
177
432
 
178
433
    def test_plugin_help_shows_plugin(self):
179
434
        # Create a test plugin
184
439
 
185
440
        try:
186
441
            # Check its help
187
 
            bzrlib.plugin.load_from_dirs(['plugin_test'])
 
442
            bzrlib.plugin.load_from_path(['plugin_test'])
188
443
            bzrlib.commands.register_command( bzrlib.plugins.myplug.cmd_myplug)
189
 
            help = self.capture('help myplug')
190
 
            self.assertContainsRe(help, 'From plugin "myplug"')
 
444
            help = self.run_bzr('help myplug')[0]
 
445
            self.assertContainsRe(help, 'plugin "myplug"')
191
446
            help = self.split_help_commands()['myplug']
192
 
            self.assertContainsRe(help, 'From plugin "myplug"')
193
 
        finally:
194
 
            # remove the plugin 'plugin'
195
 
            if getattr(bzrlib.plugins, 'plugin', None):
196
 
                del bzrlib.plugins.plugin
 
447
            self.assertContainsRe(help, '\[myplug\]')
 
448
        finally:
 
449
            # unregister command
 
450
            if 'myplug' in bzrlib.commands.plugin_cmds:
 
451
                bzrlib.commands.plugin_cmds.remove('myplug')
 
452
            # remove the plugin 'myplug'
 
453
            if getattr(bzrlib.plugins, 'myplug', None):
 
454
                delattr(bzrlib.plugins, 'myplug')
 
455
 
 
456
 
 
457
class TestSetPluginsPath(TestCase):
 
458
 
 
459
    def test_set_plugins_path(self):
 
460
        """set_plugins_path should set the module __path__ correctly."""
 
461
        old_path = bzrlib.plugins.__path__
 
462
        try:
 
463
            bzrlib.plugins.__path__ = []
 
464
            expected_path = bzrlib.plugin.set_plugins_path()
 
465
            self.assertEqual(expected_path, bzrlib.plugins.__path__)
 
466
        finally:
 
467
            bzrlib.plugins.__path__ = old_path
 
468
 
 
469
    def test_set_plugins_path_with_trailing_slashes(self):
 
470
        """set_plugins_path should set the module __path__ based on
 
471
        BZR_PLUGIN_PATH after removing all trailing slashes."""
 
472
        old_path = bzrlib.plugins.__path__
 
473
        old_env = os.environ.get('BZR_PLUGIN_PATH')
 
474
        try:
 
475
            bzrlib.plugins.__path__ = []
 
476
            os.environ['BZR_PLUGIN_PATH'] = "first\\//\\" + os.pathsep + \
 
477
                "second/\\/\\/"
 
478
            bzrlib.plugin.set_plugins_path()
 
479
            # We expect our nominated paths to have all path-seps removed,
 
480
            # and this is testing only that.
 
481
            expected_path = ['first', 'second']
 
482
            self.assertEqual(expected_path,
 
483
                bzrlib.plugins.__path__[:len(expected_path)])
 
484
        finally:
 
485
            bzrlib.plugins.__path__ = old_path
 
486
            if old_env is not None:
 
487
                os.environ['BZR_PLUGIN_PATH'] = old_env
 
488
            else:
 
489
                del os.environ['BZR_PLUGIN_PATH']
 
490
 
 
491
 
 
492
class TestHelpIndex(tests.TestCase):
 
493
    """Tests for the PluginsHelpIndex class."""
 
494
 
 
495
    def test_default_constructable(self):
 
496
        index = plugin.PluginsHelpIndex()
 
497
 
 
498
    def test_get_topics_None(self):
 
499
        """Searching for None returns an empty list."""
 
500
        index = plugin.PluginsHelpIndex()
 
501
        self.assertEqual([], index.get_topics(None))
 
502
 
 
503
    def test_get_topics_for_plugin(self):
 
504
        """Searching for plugin name gets its docstring."""
 
505
        index = plugin.PluginsHelpIndex()
 
506
        # make a new plugin here for this test, even if we're run with
 
507
        # --no-plugins
 
508
        self.assertFalse(sys.modules.has_key('bzrlib.plugins.demo_module'))
 
509
        demo_module = FakeModule('', 'bzrlib.plugins.demo_module')
 
510
        sys.modules['bzrlib.plugins.demo_module'] = demo_module
 
511
        try:
 
512
            topics = index.get_topics('demo_module')
 
513
            self.assertEqual(1, len(topics))
 
514
            self.assertIsInstance(topics[0], plugin.ModuleHelpTopic)
 
515
            self.assertEqual(demo_module, topics[0].module)
 
516
        finally:
 
517
            del sys.modules['bzrlib.plugins.demo_module']
 
518
 
 
519
    def test_get_topics_no_topic(self):
 
520
        """Searching for something that is not a plugin returns []."""
 
521
        # test this by using a name that cannot be a plugin - its not
 
522
        # a valid python identifier.
 
523
        index = plugin.PluginsHelpIndex()
 
524
        self.assertEqual([], index.get_topics('nothing by this name'))
 
525
 
 
526
    def test_prefix(self):
 
527
        """PluginsHelpIndex has a prefix of 'plugins/'."""
 
528
        index = plugin.PluginsHelpIndex()
 
529
        self.assertEqual('plugins/', index.prefix)
 
530
 
 
531
    def test_get_plugin_topic_with_prefix(self):
 
532
        """Searching for plugins/demo_module returns help."""
 
533
        index = plugin.PluginsHelpIndex()
 
534
        self.assertFalse(sys.modules.has_key('bzrlib.plugins.demo_module'))
 
535
        demo_module = FakeModule('', 'bzrlib.plugins.demo_module')
 
536
        sys.modules['bzrlib.plugins.demo_module'] = demo_module
 
537
        try:
 
538
            topics = index.get_topics('plugins/demo_module')
 
539
            self.assertEqual(1, len(topics))
 
540
            self.assertIsInstance(topics[0], plugin.ModuleHelpTopic)
 
541
            self.assertEqual(demo_module, topics[0].module)
 
542
        finally:
 
543
            del sys.modules['bzrlib.plugins.demo_module']
 
544
 
 
545
 
 
546
class FakeModule(object):
 
547
    """A fake module to test with."""
 
548
 
 
549
    def __init__(self, doc, name):
 
550
        self.__doc__ = doc
 
551
        self.__name__ = name
 
552
 
 
553
 
 
554
class TestModuleHelpTopic(tests.TestCase):
 
555
    """Tests for the ModuleHelpTopic class."""
 
556
 
 
557
    def test_contruct(self):
 
558
        """Construction takes the module to document."""
 
559
        mod = FakeModule('foo', 'foo')
 
560
        topic = plugin.ModuleHelpTopic(mod)
 
561
        self.assertEqual(mod, topic.module)
 
562
 
 
563
    def test_get_help_text_None(self):
 
564
        """A ModuleHelpTopic returns the docstring for get_help_text."""
 
565
        mod = FakeModule(None, 'demo')
 
566
        topic = plugin.ModuleHelpTopic(mod)
 
567
        self.assertEqual("Plugin 'demo' has no docstring.\n",
 
568
            topic.get_help_text())
 
569
 
 
570
    def test_get_help_text_no_carriage_return(self):
 
571
        """ModuleHelpTopic.get_help_text adds a \n if needed."""
 
572
        mod = FakeModule('one line of help', 'demo')
 
573
        topic = plugin.ModuleHelpTopic(mod)
 
574
        self.assertEqual("one line of help\n",
 
575
            topic.get_help_text())
 
576
 
 
577
    def test_get_help_text_carriage_return(self):
 
578
        """ModuleHelpTopic.get_help_text adds a \n if needed."""
 
579
        mod = FakeModule('two lines of help\nand more\n', 'demo')
 
580
        topic = plugin.ModuleHelpTopic(mod)
 
581
        self.assertEqual("two lines of help\nand more\n",
 
582
            topic.get_help_text())
 
583
 
 
584
    def test_get_help_text_with_additional_see_also(self):
 
585
        mod = FakeModule('two lines of help\nand more', 'demo')
 
586
        topic = plugin.ModuleHelpTopic(mod)
 
587
        self.assertEqual("two lines of help\nand more\nSee also: bar, foo\n",
 
588
            topic.get_help_text(['foo', 'bar']))
 
589
 
 
590
    def test_get_help_topic(self):
 
591
        """The help topic for a plugin is its module name."""
 
592
        mod = FakeModule('two lines of help\nand more', 'bzrlib.plugins.demo')
 
593
        topic = plugin.ModuleHelpTopic(mod)
 
594
        self.assertEqual('demo', topic.get_help_topic())
 
595
        mod = FakeModule('two lines of help\nand more', 'bzrlib.plugins.foo_bar')
 
596
        topic = plugin.ModuleHelpTopic(mod)
 
597
        self.assertEqual('foo_bar', topic.get_help_topic())
 
598
 
 
599
 
 
600
def clear_plugins(test_case):
 
601
    # Save the attributes that we're about to monkey-patch.
 
602
    old_plugins_path = bzrlib.plugins.__path__
 
603
    old_loaded = plugin._loaded
 
604
    old_load_from_path = plugin.load_from_path
 
605
    # Change bzrlib.plugin to think no plugins have been loaded yet.
 
606
    bzrlib.plugins.__path__ = []
 
607
    plugin._loaded = False
 
608
    # Monkey-patch load_from_path to stop it from actually loading anything.
 
609
    def load_from_path(dirs):
 
610
        pass
 
611
    plugin.load_from_path = load_from_path
 
612
    def restore_plugins():
 
613
        bzrlib.plugins.__path__ = old_plugins_path
 
614
        plugin._loaded = old_loaded
 
615
        plugin.load_from_path = old_load_from_path
 
616
    test_case.addCleanup(restore_plugins)
 
617
 
 
618
 
 
619
class TestPluginPaths(tests.TestCase):
 
620
 
 
621
    def test_set_plugins_path_with_args(self):
 
622
        clear_plugins(self)
 
623
        plugin.set_plugins_path(['a', 'b'])
 
624
        self.assertEqual(['a', 'b'], bzrlib.plugins.__path__)
 
625
 
 
626
    def test_set_plugins_path_defaults(self):
 
627
        clear_plugins(self)
 
628
        plugin.set_plugins_path()
 
629
        self.assertEqual(plugin.get_standard_plugins_path(),
 
630
                         bzrlib.plugins.__path__)
 
631
 
 
632
    def test_get_standard_plugins_path(self):
 
633
        path = plugin.get_standard_plugins_path()
 
634
        self.assertEqual(plugin.get_default_plugin_path(), path[0])
 
635
        for directory in path:
 
636
            self.assertNotContainsRe(directory, r'\\/$')
 
637
        try:
 
638
            from distutils.sysconfig import get_python_lib
 
639
        except ImportError:
 
640
            pass
 
641
        else:
 
642
            if sys.platform != 'win32':
 
643
                python_lib = get_python_lib()
 
644
                for directory in path:
 
645
                    if directory.startswith(python_lib):
 
646
                        break
 
647
                else:
 
648
                    self.fail('No path to global plugins')
 
649
 
 
650
    def test_get_standard_plugins_path_env(self):
 
651
        os.environ['BZR_PLUGIN_PATH'] = 'foo/'
 
652
        self.assertEqual('foo', plugin.get_standard_plugins_path()[0])
 
653
 
 
654
 
 
655
class TestLoadPlugins(tests.TestCaseInTempDir):
 
656
 
 
657
    def test_load_plugins(self):
 
658
        clear_plugins(self)
 
659
        plugin.load_plugins(['.'])
 
660
        self.assertEqual(bzrlib.plugins.__path__, ['.'])
 
661
        # subsequent loads are no-ops
 
662
        plugin.load_plugins(['foo'])
 
663
        self.assertEqual(bzrlib.plugins.__path__, ['.'])
 
664
 
 
665
    def test_load_plugins_default(self):
 
666
        clear_plugins(self)
 
667
        plugin.load_plugins()
 
668
        path = plugin.get_standard_plugins_path()
 
669
        self.assertEqual(path, bzrlib.plugins.__path__)