/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2475.1.1 by Martin Pool
Rename test_plugin tests and the example module used there.
1
# Copyright (C) 2005, 2007 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
750 by Martin Pool
- stubbed-out tests for python plugins
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
750 by Martin Pool
- stubbed-out tests for python plugins
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
12
#
750 by Martin Pool
- stubbed-out tests for python plugins
13
# You should have received a copy of the GNU General Public License
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
17
"""Tests for plugins"""
18
1185.16.83 by mbp at sourcefrog
- notes on testability of plugins
19
# XXX: There are no plugin tests at the moment because the plugin module
20
# affects the global state of the process.  See bzrlib/plugins.py for more
21
# comments.
22
2967.4.5 by Daniel Watkins
Added test for badly-named plugins.
23
import logging
1185.16.83 by mbp at sourcefrog
- notes on testability of plugins
24
import os
1733.2.5 by Michael Ellerman
Show which plugin (if any) provides a command.
25
from StringIO import StringIO
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
26
import sys
2215.4.1 by Alexander Belchenko
Bugfix #68124: Allow plugins import from zip archives.
27
import zipfile
750 by Martin Pool
- stubbed-out tests for python plugins
28
2432.1.25 by Robert Collins
Return plugin module docstrings for 'bzr help plugin'.
29
from bzrlib import plugin, tests
1515 by Robert Collins
* Plugins with the same name in different directories in the bzr plugin
30
import bzrlib.plugin
31
import bzrlib.plugins
1733.2.5 by Michael Ellerman
Show which plugin (if any) provides a command.
32
import bzrlib.commands
33
import bzrlib.help
3193.7.11 by Alexander Belchenko
merge bzr.dev; update patch for 1.3
34
from bzrlib.symbol_versioning import one_three
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
35
from bzrlib.tests import TestCase, TestCaseInTempDir
2804.4.1 by Alexander Belchenko
some win32-specific fixes for selftest
36
from bzrlib.osutils import pathjoin, abspath, normpath
1141 by Martin Pool
- rename FunctionalTest to TestCaseInTempDir
37
1185.16.83 by mbp at sourcefrog
- notes on testability of plugins
38
1185.16.84 by mbp at sourcefrog
- fix indents
39
PLUGIN_TEXT = """\
40
import bzrlib.commands
41
class cmd_myplug(bzrlib.commands.Command):
42
    '''Just a simple test plugin.'''
43
    aliases = ['mplg']
44
    def run(self):
45
        print 'Hello from my plugin'
46
"""
1492 by Robert Collins
Support decoration of commands.
47
48
# 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
49
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
50
class TestLoadingPlugins(TestCaseInTempDir):
1515 by Robert Collins
* Plugins with the same name in different directories in the bzr plugin
51
52
    activeattributes = {}
53
54
    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
55
        # This test tests that having two plugins in different directories does
56
        # not result in both being loaded when they have the same name.  get a
57
        # file name we can use which is also a valid attribute for accessing in
58
        # activeattributes. - we cannot give import parameters.
1515 by Robert Collins
* Plugins with the same name in different directories in the bzr plugin
59
        tempattribute = "0"
60
        self.failIf(tempattribute in self.activeattributes)
61
        # set a place for the plugins to record their loading, and at the same
62
        # time validate that the location the plugins should record to is
63
        # valid and correct.
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
64
        bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
1515 by Robert Collins
* Plugins with the same name in different directories in the bzr plugin
65
            [tempattribute] = []
66
        self.failUnless(tempattribute in self.activeattributes)
67
        # create two plugin directories
68
        os.mkdir('first')
69
        os.mkdir('second')
70
        # write a plugin that will record when its loaded in the 
71
        # tempattribute list.
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
72
        template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
73
                    "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.
74
75
        outfile = open(os.path.join('first', 'plugin.py'), 'w')
76
        try:
2911.6.1 by Blake Winton
Change 'print >> f,'s to 'f.write('s.
77
            outfile.write(template % (tempattribute, 'first'))
78
            outfile.write('\n')
2652.2.7 by Blake Winton
fix lines which were wider than 79 chars. Also handle files a little more safely.
79
        finally:
80
            outfile.close()
81
82
        outfile = open(os.path.join('second', 'plugin.py'), 'w')
83
        try:
2911.6.1 by Blake Winton
Change 'print >> f,'s to 'f.write('s.
84
            outfile.write(template % (tempattribute, 'second'))
85
            outfile.write('\n')
2652.2.7 by Blake Winton
fix lines which were wider than 79 chars. Also handle files a little more safely.
86
        finally:
87
            outfile.close()
88
1515 by Robert Collins
* Plugins with the same name in different directories in the bzr plugin
89
        try:
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
90
            bzrlib.plugin.load_from_path(['first', 'second'])
91
            self.assertEqual(['first'], self.activeattributes[tempattribute])
92
        finally:
93
            # remove the plugin 'plugin'
94
            del self.activeattributes[tempattribute]
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
95
            if 'bzrlib.plugins.plugin' in sys.modules:
96
                del sys.modules['bzrlib.plugins.plugin']
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
97
            if getattr(bzrlib.plugins, 'plugin', None):
98
                del bzrlib.plugins.plugin
99
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
100
101
    def test_plugins_from_different_dirs_can_demand_load(self):
102
        # This test tests that having two plugins in different
103
        # directories with different names allows them both to be loaded, when
104
        # we do a direct import statement.
105
        # Determine a file name we can use which is also a valid attribute
106
        # for accessing in activeattributes. - we cannot give import parameters.
107
        tempattribute = "different-dirs"
108
        self.failIf(tempattribute in self.activeattributes)
109
        # set a place for the plugins to record their loading, and at the same
110
        # time validate that the location the plugins should record to is
111
        # valid and correct.
112
        bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
113
            [tempattribute] = []
114
        self.failUnless(tempattribute in self.activeattributes)
115
        # create two plugin directories
116
        os.mkdir('first')
117
        os.mkdir('second')
118
        # write plugins that will record when they are loaded in the 
119
        # tempattribute list.
120
        template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
121
                    "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.
122
123
        outfile = open(os.path.join('first', 'pluginone.py'), 'w')
124
        try:
2911.6.1 by Blake Winton
Change 'print >> f,'s to 'f.write('s.
125
            outfile.write(template % (tempattribute, 'first'))
126
            outfile.write('\n')
2652.2.7 by Blake Winton
fix lines which were wider than 79 chars. Also handle files a little more safely.
127
        finally:
128
            outfile.close()
129
130
        outfile = open(os.path.join('second', 'plugintwo.py'), 'w')
131
        try:
2911.6.1 by Blake Winton
Change 'print >> f,'s to 'f.write('s.
132
            outfile.write(template % (tempattribute, 'second'))
133
            outfile.write('\n')
2652.2.7 by Blake Winton
fix lines which were wider than 79 chars. Also handle files a little more safely.
134
        finally:
135
            outfile.close()
136
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
137
        oldpath = bzrlib.plugins.__path__
138
        try:
139
            bzrlib.plugins.__path__ = ['first', 'second']
140
            exec "import bzrlib.plugins.pluginone"
141
            self.assertEqual(['first'], self.activeattributes[tempattribute])
142
            exec "import bzrlib.plugins.plugintwo"
143
            self.assertEqual(['first', 'second'],
144
                self.activeattributes[tempattribute])
1515 by Robert Collins
* Plugins with the same name in different directories in the bzr plugin
145
        finally:
146
            # remove the plugin 'plugin'
147
            del self.activeattributes[tempattribute]
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
148
            if getattr(bzrlib.plugins, 'pluginone', None):
149
                del bzrlib.plugins.pluginone
150
            if getattr(bzrlib.plugins, 'plugintwo', None):
151
                del bzrlib.plugins.plugintwo
152
        self.failIf(getattr(bzrlib.plugins, 'pluginone', None))
153
        self.failIf(getattr(bzrlib.plugins, 'plugintwo', None))
1516 by Robert Collins
* bzrlib.plugin.all_plugins has been changed from an attribute to a
154
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.
155
    def test_plugins_can_load_from_directory_with_trailing_slash(self):
156
        # This test tests that a plugin can load from a directory when the
157
        # 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.
158
        # check the plugin is not loaded already
159
        self.failIf(getattr(bzrlib.plugins, 'ts_plugin', None))
160
        tempattribute = "trailing-slash"
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.
161
        self.failIf(tempattribute in self.activeattributes)
2652.2.3 by Blake Winton
Understand the code and comments of the test, instead of just cargo-culting them.
162
        # set a place for the plugin to record its loading, and at the same
163
        # 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.
164
        # valid and correct.
165
        bzrlib.tests.test_plugins.TestLoadingPlugins.activeattributes \
166
            [tempattribute] = []
167
        self.failUnless(tempattribute in self.activeattributes)
2652.2.3 by Blake Winton
Understand the code and comments of the test, instead of just cargo-culting them.
168
        # create a directory for the plugin
169
        os.mkdir('plugin_test')
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.
170
        # write a plugin that will record when its loaded in the 
171
        # tempattribute list.
172
        template = ("from bzrlib.tests.test_plugins import TestLoadingPlugins\n"
173
                    "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.
174
175
        outfile = open(os.path.join('plugin_test', 'ts_plugin.py'), 'w')
176
        try:
2911.6.1 by Blake Winton
Change 'print >> f,'s to 'f.write('s.
177
            outfile.write(template % (tempattribute, 'plugin'))
2911.6.4 by Blake Winton
Fix test failures
178
            outfile.write('\n')
2652.2.7 by Blake Winton
fix lines which were wider than 79 chars. Also handle files a little more safely.
179
        finally:
180
            outfile.close()
181
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.
182
        try:
2652.2.3 by Blake Winton
Understand the code and comments of the test, instead of just cargo-culting them.
183
            bzrlib.plugin.load_from_path(['plugin_test'+os.sep])
184
            self.assertEqual(['plugin'], self.activeattributes[tempattribute])
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.
185
        finally:
186
            # remove the plugin 'plugin'
187
            del self.activeattributes[tempattribute]
2652.2.7 by Blake Winton
fix lines which were wider than 79 chars. Also handle files a little more safely.
188
            if getattr(bzrlib.plugins, 'ts_plugin', None):
189
                del bzrlib.plugins.ts_plugin
190
        self.failIf(getattr(bzrlib.plugins, 'ts_plugin', None))
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.
191
2967.4.5 by Daniel Watkins
Added test for badly-named plugins.
192
    def test_plugin_with_bad_name_does_not_load(self):
193
        # Create badly-named plugin
194
        file('bad plugin-name..py', 'w').close()
195
196
        # Capture output
197
        stream = StringIO()
198
        handler = logging.StreamHandler(stream)
199
        log = logging.getLogger('bzr')
200
        log.addHandler(handler)
201
202
        bzrlib.plugin.load_from_dir('.')
203
204
        # Stop capturing output
205
        handler.flush()
206
        handler.close()
207
        log.removeHandler(handler)
208
209
        self.assertContainsRe(stream.getvalue(),
210
            r"Unable to load 'bad plugin-name\.' in '\.' as a plugin because"
2967.4.10 by Daniel Watkins
Fixed message.
211
            " file path isn't a valid module name; try renaming it to"
2967.4.5 by Daniel Watkins
Added test for badly-named plugins.
212
            " 'bad_plugin_name_'\.")
213
214
        stream.close()
215
1516 by Robert Collins
* bzrlib.plugin.all_plugins has been changed from an attribute to a
216
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
217
class TestPlugins(TestCaseInTempDir):
218
219
    def setup_plugin(self, source=""):
220
        # This test tests a new plugin appears in bzrlib.plugin.plugins().
221
        # check the plugin is not loaded already
222
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
223
        # write a plugin that _cannot_ fail to load.
2911.6.1 by Blake Winton
Change 'print >> f,'s to 'f.write('s.
224
        file('plugin.py', 'w').write(source + '\n')
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
225
        self.addCleanup(self.teardown_plugin)
226
        bzrlib.plugin.load_from_path(['.'])
227
    
228
    def teardown_plugin(self):
229
        # remove the plugin 'plugin'
230
        if 'bzrlib.plugins.plugin' in sys.modules:
231
            del sys.modules['bzrlib.plugins.plugin']
232
        if getattr(bzrlib.plugins, 'plugin', None):
233
            del bzrlib.plugins.plugin
234
        self.failIf(getattr(bzrlib.plugins, 'plugin', None))
235
236
    def test_plugin_appears_in_plugins(self):
237
        self.setup_plugin()
238
        self.failUnless('plugin' in bzrlib.plugin.plugins())
239
        self.failUnless(getattr(bzrlib.plugins, 'plugin', None))
240
        plugins = bzrlib.plugin.plugins()
241
        plugin = plugins['plugin']
242
        self.assertIsInstance(plugin, bzrlib.plugin.PlugIn)
243
        self.assertEqual(bzrlib.plugins.plugin, plugin.module)
244
245
    def test_trivial_plugin_get_path(self):
246
        self.setup_plugin()
247
        plugins = bzrlib.plugin.plugins()
248
        plugin = plugins['plugin']
249
        plugin_path = self.test_dir + '/plugin.py'
2823.1.10 by Vincent Ladeuil
merge bzr.dev
250
        self.assertIsSameRealPath(plugin_path, normpath(plugin.path()))
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
251
3193.2.1 by Alexander Belchenko
show path to plugin module as *.py instead of *.pyc if python source available
252
    def test_plugin_get_path_py_not_pyc(self):
253
        self.setup_plugin()         # after first import there will be plugin.pyc
254
        self.teardown_plugin()
255
        bzrlib.plugin.load_from_path(['.']) # import plugin.pyc
256
        plugins = bzrlib.plugin.plugins()
257
        plugin = plugins['plugin']
258
        plugin_path = self.test_dir + '/plugin.py'
259
        self.assertIsSameRealPath(plugin_path, normpath(plugin.path()))
260
261
    def test_plugin_get_path_pyc_only(self):
262
        self.setup_plugin()         # after first import there will be plugin.pyc
263
        self.teardown_plugin()
264
        os.unlink(self.test_dir + '/plugin.py')
265
        bzrlib.plugin.load_from_path(['.']) # import plugin.pyc
266
        plugins = bzrlib.plugin.plugins()
267
        plugin = plugins['plugin']
268
        if __debug__:
269
            plugin_path = self.test_dir + '/plugin.pyc'
270
        else:
271
            plugin_path = self.test_dir + '/plugin.pyo'
272
        self.assertIsSameRealPath(plugin_path, normpath(plugin.path()))
273
2762.2.1 by Robert Collins
* ``bzr plugins`` now lists the version number for each plugin in square
274
    def test_no_test_suite_gives_None_for_test_suite(self):
275
        self.setup_plugin()
276
        plugin = bzrlib.plugin.plugins()['plugin']
277
        self.assertEqual(None, plugin.test_suite())
278
279
    def test_test_suite_gives_test_suite_result(self):
280
        source = """def test_suite(): return 'foo'"""
281
        self.setup_plugin(source)
282
        plugin = bzrlib.plugin.plugins()['plugin']
283
        self.assertEqual('foo', plugin.test_suite())
284
285
    def test_no_version_info(self):
286
        self.setup_plugin()
287
        plugin = bzrlib.plugin.plugins()['plugin']
288
        self.assertEqual(None, plugin.version_info())
289
290
    def test_with_version_info(self):
291
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 4)")
292
        plugin = bzrlib.plugin.plugins()['plugin']
293
        self.assertEqual((1, 2, 3, 'dev', 4), plugin.version_info())
294
295
    def test_short_version_info_gets_padded(self):
296
        # the gtk plugin has version_info = (1,2,3) rather than the 5-tuple.
297
        # so we adapt it
298
        self.setup_plugin("version_info = (1, 2, 3)")
299
        plugin = bzrlib.plugin.plugins()['plugin']
300
        self.assertEqual((1, 2, 3, 'final', 0), plugin.version_info())
301
302
    def test_no_version_info___version__(self):
303
        self.setup_plugin()
304
        plugin = bzrlib.plugin.plugins()['plugin']
305
        self.assertEqual("unknown", plugin.__version__)
306
307
    def test___version__with_version_info(self):
308
        self.setup_plugin("version_info = (1, 2, 3, 'dev', 4)")
309
        plugin = bzrlib.plugin.plugins()['plugin']
310
        self.assertEqual("1.2.3dev4", plugin.__version__)
311
312
    def test_final__version__with_version_info(self):
313
        self.setup_plugin("version_info = (1, 2, 3, 'final', 4)")
314
        plugin = bzrlib.plugin.plugins()['plugin']
315
        self.assertEqual("1.2.3", plugin.__version__)
316
317
1733.2.5 by Michael Ellerman
Show which plugin (if any) provides a command.
318
class TestPluginHelp(TestCaseInTempDir):
319
320
    def split_help_commands(self):
321
        help = {}
322
        current = None
2530.3.4 by Martin Pool
Deprecate run_bzr_captured in favour of just run_bzr
323
        for line in self.run_bzr('help commands')[0].splitlines():
2034.1.2 by Aaron Bentley
Fix testcase
324
            if not line.startswith(' '):
325
                current = line.split()[0]
1733.2.5 by Michael Ellerman
Show which plugin (if any) provides a command.
326
            help[current] = help.get(current, '') + line
327
328
        return help
329
330
    def test_plugin_help_builtins_unaffected(self):
331
        # Check we don't get false positives
332
        help_commands = self.split_help_commands()
333
        for cmd_name in bzrlib.commands.builtin_command_names():
334
            if cmd_name in bzrlib.commands.plugin_command_names():
335
                continue
336
            try:
2432.1.12 by Robert Collins
Relocate command help onto Command.
337
                help = bzrlib.commands.get_cmd_object(cmd_name).get_help_text()
1733.2.5 by Michael Ellerman
Show which plugin (if any) provides a command.
338
            except NotImplementedError:
339
                # some commands have no help
340
                pass
341
            else:
2666.1.1 by Ian Clatworthy
Bazaar User Reference generated from online help
342
                self.assertNotContainsRe(help, 'plugin "[^"]*"')
1733.2.5 by Michael Ellerman
Show which plugin (if any) provides a command.
343
2432.1.12 by Robert Collins
Relocate command help onto Command.
344
            if cmd_name in help_commands.keys():
1733.2.5 by Michael Ellerman
Show which plugin (if any) provides a command.
345
                # some commands are hidden
346
                help = help_commands[cmd_name]
2666.1.1 by Ian Clatworthy
Bazaar User Reference generated from online help
347
                self.assertNotContainsRe(help, 'plugin "[^"]*"')
1733.2.5 by Michael Ellerman
Show which plugin (if any) provides a command.
348
349
    def test_plugin_help_shows_plugin(self):
350
        # Create a test plugin
351
        os.mkdir('plugin_test')
352
        f = open(pathjoin('plugin_test', 'myplug.py'), 'w')
353
        f.write(PLUGIN_TEXT)
354
        f.close()
355
356
        try:
357
            # Check its help
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
358
            bzrlib.plugin.load_from_path(['plugin_test'])
1733.2.5 by Michael Ellerman
Show which plugin (if any) provides a command.
359
            bzrlib.commands.register_command( bzrlib.plugins.myplug.cmd_myplug)
2530.3.4 by Martin Pool
Deprecate run_bzr_captured in favour of just run_bzr
360
            help = self.run_bzr('help myplug')[0]
2666.1.1 by Ian Clatworthy
Bazaar User Reference generated from online help
361
            self.assertContainsRe(help, 'plugin "myplug"')
1733.2.5 by Michael Ellerman
Show which plugin (if any) provides a command.
362
            help = self.split_help_commands()['myplug']
2034.1.4 by Aaron Bentley
Change angle brackets to square brackets
363
            self.assertContainsRe(help, '\[myplug\]')
1733.2.5 by Michael Ellerman
Show which plugin (if any) provides a command.
364
        finally:
2204.3.2 by Alexander Belchenko
cherrypicking: test_plugin_help_shows_plugin: fix cleanup after test
365
            # unregister command
366
            if bzrlib.commands.plugin_cmds.get('myplug', None):
367
                del bzrlib.commands.plugin_cmds['myplug']
368
            # remove the plugin 'myplug'
369
            if getattr(bzrlib.plugins, 'myplug', None):
370
                delattr(bzrlib.plugins, 'myplug')
2215.4.1 by Alexander Belchenko
Bugfix #68124: Allow plugins import from zip archives.
371
372
373
class TestPluginFromZip(TestCaseInTempDir):
374
375
    def make_zipped_plugin(self, zip_name, filename):
376
        z = zipfile.ZipFile(zip_name, 'w')
377
        z.writestr(filename, PLUGIN_TEXT)
378
        z.close()
379
380
    def check_plugin_load(self, zip_name, plugin_name):
381
        self.assertFalse(plugin_name in dir(bzrlib.plugins),
382
                         'Plugin already loaded')
2610.2.1 by Martin Pool
(Lukas Lalinsky) don't create a duplicate zipimporter, avoiding loading plugins twice
383
        old_path = bzrlib.plugins.__path__
2215.4.1 by Alexander Belchenko
Bugfix #68124: Allow plugins import from zip archives.
384
        try:
2610.2.1 by Martin Pool
(Lukas Lalinsky) don't create a duplicate zipimporter, avoiding loading plugins twice
385
            # this is normally done by load_plugins -> set_plugins_path
386
            bzrlib.plugins.__path__ = [zip_name]
3193.7.11 by Alexander Belchenko
merge bzr.dev; update patch for 1.3
387
            self.applyDeprecated(one_three,
3193.7.5 by Alexander Belchenko
deprecate bzrlib.plugins.load_from_zip feature; remove 0.91-deprecated function all_plugins()
388
                bzrlib.plugin.load_from_zip, zip_name)
2215.4.1 by Alexander Belchenko
Bugfix #68124: Allow plugins import from zip archives.
389
            self.assertTrue(plugin_name in dir(bzrlib.plugins),
390
                            'Plugin is not loaded')
391
        finally:
392
            # unregister plugin
393
            if getattr(bzrlib.plugins, plugin_name, None):
394
                delattr(bzrlib.plugins, plugin_name)
2610.2.1 by Martin Pool
(Lukas Lalinsky) don't create a duplicate zipimporter, avoiding loading plugins twice
395
                del sys.modules['bzrlib.plugins.' + plugin_name]
396
            bzrlib.plugins.__path__ = old_path
2215.4.1 by Alexander Belchenko
Bugfix #68124: Allow plugins import from zip archives.
397
398
    def test_load_module(self):
399
        self.make_zipped_plugin('./test.zip', 'ziplug.py')
400
        self.check_plugin_load('./test.zip', 'ziplug')
401
402
    def test_load_package(self):
403
        self.make_zipped_plugin('./test.zip', 'ziplug/__init__.py')
404
        self.check_plugin_load('./test.zip', 'ziplug')
2256.2.2 by Robert Collins
Allow 'import bzrlib.plugins.NAME' to work when the plugin NAME has not
405
406
407
class TestSetPluginsPath(TestCase):
408
    
409
    def test_set_plugins_path(self):
410
        """set_plugins_path should set the module __path__ correctly."""
411
        old_path = bzrlib.plugins.__path__
412
        try:
413
            bzrlib.plugins.__path__ = []
414
            expected_path = bzrlib.plugin.set_plugins_path()
415
            self.assertEqual(expected_path, bzrlib.plugins.__path__)
416
        finally:
417
            bzrlib.plugins.__path__ = old_path
2432.1.25 by Robert Collins
Return plugin module docstrings for 'bzr help plugin'.
418
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.
419
    def test_set_plugins_path_with_trailing_slashes(self):
2652.2.7 by Blake Winton
fix lines which were wider than 79 chars. Also handle files a little more safely.
420
        """set_plugins_path should set the module __path__ based on
421
        BZR_PLUGIN_PATH."""
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.
422
        old_path = bzrlib.plugins.__path__
423
        old_env = os.environ.get('BZR_PLUGIN_PATH')
424
        try:
425
            bzrlib.plugins.__path__ = []
2652.2.7 by Blake Winton
fix lines which were wider than 79 chars. Also handle files a little more safely.
426
            os.environ['BZR_PLUGIN_PATH'] = "first\\//\\" + os.pathsep + \
427
                "second/\\/\\/"
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.
428
            bzrlib.plugin.set_plugins_path()
2652.2.7 by Blake Winton
fix lines which were wider than 79 chars. Also handle files a little more safely.
429
            expected_path = ['first', 'second',
430
                os.path.dirname(bzrlib.plugins.__file__)]
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.
431
            self.assertEqual(expected_path, bzrlib.plugins.__path__)
432
        finally:
433
            bzrlib.plugins.__path__ = old_path
434
            if old_env != None:
435
                os.environ['BZR_PLUGIN_PATH'] = old_env
436
            else:
437
                del os.environ['BZR_PLUGIN_PATH']
2432.1.25 by Robert Collins
Return plugin module docstrings for 'bzr help plugin'.
438
439
class TestHelpIndex(tests.TestCase):
440
    """Tests for the PluginsHelpIndex class."""
441
442
    def test_default_constructable(self):
443
        index = plugin.PluginsHelpIndex()
444
445
    def test_get_topics_None(self):
446
        """Searching for None returns an empty list."""
447
        index = plugin.PluginsHelpIndex()
448
        self.assertEqual([], index.get_topics(None))
449
2475.1.1 by Martin Pool
Rename test_plugin tests and the example module used there.
450
    def test_get_topics_for_plugin(self):
451
        """Searching for plugin name gets its docstring."""
2432.1.25 by Robert Collins
Return plugin module docstrings for 'bzr help plugin'.
452
        index = plugin.PluginsHelpIndex()
2475.1.1 by Martin Pool
Rename test_plugin tests and the example module used there.
453
        # make a new plugin here for this test, even if we're run with
454
        # --no-plugins
455
        self.assertFalse(sys.modules.has_key('bzrlib.plugins.demo_module'))
456
        demo_module = FakeModule('', 'bzrlib.plugins.demo_module')
457
        sys.modules['bzrlib.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)
458
        try:
2475.1.1 by Martin Pool
Rename test_plugin tests and the example module used there.
459
            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)
460
            self.assertEqual(1, len(topics))
461
            self.assertIsInstance(topics[0], plugin.ModuleHelpTopic)
462
            self.assertEqual(demo_module, topics[0].module)
463
        finally:
2475.1.1 by Martin Pool
Rename test_plugin tests and the example module used there.
464
            del sys.modules['bzrlib.plugins.demo_module']
2432.1.25 by Robert Collins
Return plugin module docstrings for 'bzr help plugin'.
465
466
    def test_get_topics_no_topic(self):
467
        """Searching for something that is not a plugin returns []."""
468
        # test this by using a name that cannot be a plugin - its not
469
        # a valid python identifier.
470
        index = plugin.PluginsHelpIndex()
471
        self.assertEqual([], index.get_topics('nothing by this name'))
472
473
    def test_prefix(self):
474
        """PluginsHelpIndex has a prefix of 'plugins/'."""
475
        index = plugin.PluginsHelpIndex()
476
        self.assertEqual('plugins/', index.prefix)
477
2475.1.1 by Martin Pool
Rename test_plugin tests and the example module used there.
478
    def test_get_plugin_topic_with_prefix(self):
479
        """Searching for plugins/demo_module returns help."""
2432.1.25 by Robert Collins
Return plugin module docstrings for 'bzr help plugin'.
480
        index = plugin.PluginsHelpIndex()
2475.1.1 by Martin Pool
Rename test_plugin tests and the example module used there.
481
        self.assertFalse(sys.modules.has_key('bzrlib.plugins.demo_module'))
482
        demo_module = FakeModule('', 'bzrlib.plugins.demo_module')
483
        sys.modules['bzrlib.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)
484
        try:
2475.1.1 by Martin Pool
Rename test_plugin tests and the example module used there.
485
            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)
486
            self.assertEqual(1, len(topics))
487
            self.assertIsInstance(topics[0], plugin.ModuleHelpTopic)
488
            self.assertEqual(demo_module, topics[0].module)
489
        finally:
2475.1.1 by Martin Pool
Rename test_plugin tests and the example module used there.
490
            del sys.modules['bzrlib.plugins.demo_module']
2432.1.25 by Robert Collins
Return plugin module docstrings for 'bzr help plugin'.
491
492
493
class FakeModule(object):
494
    """A fake module to test with."""
495
496
    def __init__(self, doc, name):
497
        self.__doc__ = doc
498
        self.__name__ = name
499
500
501
class TestModuleHelpTopic(tests.TestCase):
502
    """Tests for the ModuleHelpTopic class."""
503
504
    def test_contruct(self):
505
        """Construction takes the module to document."""
506
        mod = FakeModule('foo', 'foo')
507
        topic = plugin.ModuleHelpTopic(mod)
508
        self.assertEqual(mod, topic.module)
509
510
    def test_get_help_text_None(self):
511
        """A ModuleHelpTopic returns the docstring for get_help_text."""
512
        mod = FakeModule(None, 'demo')
513
        topic = plugin.ModuleHelpTopic(mod)
514
        self.assertEqual("Plugin 'demo' has no docstring.\n",
515
            topic.get_help_text())
516
517
    def test_get_help_text_no_carriage_return(self):
518
        """ModuleHelpTopic.get_help_text adds a \n if needed."""
519
        mod = FakeModule('one line of help', 'demo')
520
        topic = plugin.ModuleHelpTopic(mod)
521
        self.assertEqual("one line of help\n",
522
            topic.get_help_text())
523
524
    def test_get_help_text_carriage_return(self):
525
        """ModuleHelpTopic.get_help_text adds a \n if needed."""
526
        mod = FakeModule('two lines of help\nand more\n', 'demo')
527
        topic = plugin.ModuleHelpTopic(mod)
528
        self.assertEqual("two lines of help\nand more\n",
529
            topic.get_help_text())
530
531
    def test_get_help_text_with_additional_see_also(self):
532
        mod = FakeModule('two lines of help\nand more', 'demo')
533
        topic = plugin.ModuleHelpTopic(mod)
534
        self.assertEqual("two lines of help\nand more\nSee also: bar, foo\n",
535
            topic.get_help_text(['foo', 'bar']))
2432.1.29 by Robert Collins
Add get_help_topic to ModuleHelpTopic.
536
537
    def test_get_help_topic(self):
538
        """The help topic for a plugin is its module name."""
2432.1.30 by Robert Collins
Fix the ModuleHelpTopic get_help_topic to be tested with closer to real world data and strip the bzrlib.plugins. prefix from the name.
539
        mod = FakeModule('two lines of help\nand more', 'bzrlib.plugins.demo')
2432.1.29 by Robert Collins
Add get_help_topic to ModuleHelpTopic.
540
        topic = plugin.ModuleHelpTopic(mod)
541
        self.assertEqual('demo', topic.get_help_topic())
2432.1.30 by Robert Collins
Fix the ModuleHelpTopic get_help_topic to be tested with closer to real world data and strip the bzrlib.plugins. prefix from the name.
542
        mod = FakeModule('two lines of help\nand more', 'bzrlib.plugins.foo_bar')
2432.1.29 by Robert Collins
Add get_help_topic to ModuleHelpTopic.
543
        topic = plugin.ModuleHelpTopic(mod)
544
        self.assertEqual('foo_bar', topic.get_help_topic())