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