1
# Copyright (C) 2010, 2011 Canonical Ltd
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
"""Tests for how many modules are loaded in executing various commands."""
22
from testtools import content
25
plugins as _mod_plugins,
28
from bzrlib.controldir import ControlDir
29
from bzrlib.smart import medium
30
from bzrlib.transport import remote
32
from bzrlib.plugin import (
36
from bzrlib.tests import (
37
TestCaseWithTransport,
40
old_format_modules = [
41
'bzrlib.repofmt.knitrepo',
42
'bzrlib.repofmt.knitpack_repo',
43
'bzrlib.plugins.weave_fmt.branch',
44
'bzrlib.plugins.weave_fmt.bzrdir',
45
'bzrlib.plugins.weave_fmt.repository',
46
'bzrlib.plugins.weave_fmt.workingtree',
56
class ImportTariffTestCase(TestCaseWithTransport):
57
"""Check how many modules are loaded for some representative scenarios.
59
See the Testing Guide in the developer documentation for more explanation.
62
We must respect the setup used by the selftest command regarding
63
plugins. This allows the user to control which plugins are in effect while
64
running these tests and respect the import policies defined here.
66
When failures are encountered for a given plugin, they can generally be
67
addressed by using lazy import or lazy hook registration.
71
self.preserved_env_vars = {}
72
for name in ('BZR_PLUGIN_PATH', 'BZR_DISABLE_PLUGINS', 'BZR_PLUGINS_AT'
74
self.preserved_env_vars[name] = os.environ.get(name)
75
super(ImportTariffTestCase, self).setUp()
77
def start_bzr_subprocess_with_import_check(self, args, stderr_file=None):
78
"""Run a bzr process and capture the imports.
80
This is fairly expensive because we start a subprocess, so we aim to
81
cover representative rather than exhaustive cases.
83
# We use PYTHON_VERBOSE rather than --profile-imports because in
84
# experimentation the profile-imports output seems to not always show
85
# the modules you'd expect; this can be debugged but python -v seems
86
# more likely to always show everything. And we use the environment
87
# variable rather than 'python -v' in the hope it will work even if
88
# bzr is frozen and python is not explicitly specified. -- mbp 20100208
89
env_changes = dict(PYTHONVERBOSE='1', **self.preserved_env_vars)
90
trace.mutter('Setting env for bzr subprocess: %r', env_changes)
91
kwargs = dict(env_changes=env_changes,
92
allow_plugins=(not are_plugins_disabled()))
94
# We don't want to update the whole call chain so we insert stderr
96
kwargs['stderr'] = stderr_file
97
return self.start_bzr_subprocess(args, **kwargs)
99
def check_forbidden_modules(self, err, forbidden_imports):
100
"""Check for forbidden modules in stderr.
102
:param err: Standard error
103
:param forbidden_imports: List of forbidden modules
105
self.addDetail('subprocess_stderr',
106
content.Content(content.ContentType("text", "plain"),
110
for module_name in forbidden_imports:
111
if err.find("\nimport %s " % module_name) != -1:
112
bad_modules.append(module_name)
115
self.fail("command loaded forbidden modules %r"
118
def finish_bzr_subprocess_with_import_check(self, process,
119
args, forbidden_imports):
120
"""Finish subprocess and check specific modules have not been
123
:param forbidden_imports: List of fully-qualified Python module names
124
that should not be loaded while running this command.
126
(out, err) = self.finish_bzr_subprocess(process,
127
universal_newlines=False, process_args=args)
128
self.check_forbidden_modules(err, forbidden_imports)
131
def run_command_check_imports(self, args, forbidden_imports):
132
"""Run bzr ARGS in a subprocess and check its imports.
134
This is fairly expensive because we start a subprocess, so we aim to
135
cover representative rather than exhaustive cases.
137
:param forbidden_imports: List of fully-qualified Python module names
138
that should not be loaded while running this command.
140
process = self.start_bzr_subprocess_with_import_check(args)
141
self.finish_bzr_subprocess_with_import_check(process, args,
145
class TestImportTariffs(ImportTariffTestCase):
146
"""Basic import tariff tests for some common bzr commands"""
148
def test_import_tariffs_working(self):
149
# check some guaranteed-true and false imports to be sure we're
150
# measuring correctly
151
self.make_branch_and_tree('.')
152
self.run_command_check_imports(['st'],
153
['nonexistentmodulename', 'anothernonexistentmodule'])
154
self.assertRaises(AssertionError,
155
self.run_command_check_imports,
159
def test_simple_local(self):
160
# 'st' in a default format working tree shouldn't need many modules
161
self.make_branch_and_tree('.')
162
self.run_command_check_imports(['st'], [
166
'bzrlib.bundle.commands',
167
'bzrlib.cmd_version_info',
168
'bzrlib.externalcommand',
171
# foreign branch plugins import the foreign_vcs_registry from
172
# bzrlib.foreign so it can't be blacklisted
177
'bzrlib.merge_directive',
179
'bzrlib.patiencediff',
182
'bzrlib.sign_my_commits',
184
'bzrlib.smart.client',
185
'bzrlib.smart.medium',
186
'bzrlib.smart.server',
188
'bzrlib.version_info_formats.format_rio',
189
'bzrlib.xml_serializer',
201
] + old_format_modules)
202
# TODO: similar test for repository-only operations, checking we avoid
203
# loading wt-specific stuff
205
# See https://bugs.launchpad.net/bzr/+bug/553017
207
def test_help_commands(self):
208
# See https://bugs.launchpad.net/bzr/+bug/663773
209
self.run_command_check_imports(['help', 'commands'], [
213
def test_simple_serve(self):
214
# 'serve' in a default format working tree shouldn't need many modules
215
tree = self.make_branch_and_tree('.')
216
# Capture the bzr serve process' stderr in a file to avoid deadlocks
217
# while the smart client interacts with it.
218
stderr_file = open('bzr-serve.stderr', 'w')
219
process = self.start_bzr_subprocess_with_import_check(['serve',
220
'--inet', '-d', tree.basedir], stderr_file=stderr_file)
221
url = 'bzr://localhost/'
223
client_medium = medium.SmartSimplePipesClientMedium(
224
process.stdout, process.stdin, url)
225
transport = remote.RemoteTransport(url, medium=client_medium)
226
branch = ControlDir.open_from_transport(transport).open_branch()
227
process.stdin.close()
228
# Hide stdin from the subprocess module, so it won't fail to close it.
230
(out, err) = self.finish_bzr_subprocess(process,
231
universal_newlines=False)
233
with open('bzr-serve.stderr', 'r') as stderr_file:
234
err = stderr_file.read()
235
self.check_forbidden_modules(err,
239
'bzrlib.bundle.commands',
240
'bzrlib.cmd_version_info',
242
'bzrlib._dirstate_helpers_py',
243
'bzrlib._dirstate_helpers_pyx',
244
'bzrlib.externalcommand',
247
# foreign branch plugins import the foreign_vcs_registry from
248
# bzrlib.foreign so it can't be blacklisted
253
'bzrlib.merge_directive',
255
'bzrlib.patiencediff',
258
'bzrlib.sign_my_commits',
259
'bzrlib.smart.client',
261
'bzrlib.version_info_formats.format_rio',
262
'bzrlib.workingtree_4',
263
'bzrlib.xml_serializer',
272
] + old_format_modules)