1
# Copyright (C) 2006 by 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
"""Benchmark test suite for bzr."""
30
from bzrlib.tests.TestUtil import TestLoader
31
from bzrlib.tests.blackbox import ExternalBase
34
class Benchmark(ExternalBase):
38
def get_cache_dir(self, extra):
39
"""Get the directory to use for caching the given object."""
41
if Benchmark.CACHE_ROOT is None:
42
Benchmark.CACHE_ROOT = osutils.pathjoin(self.TEST_ROOT, 'CACHE')
43
if not os.path.isdir(Benchmark.CACHE_ROOT):
44
os.mkdir(Benchmark.CACHE_ROOT)
45
cache_dir = osutils.pathjoin(self.CACHE_ROOT, extra)
46
return cache_dir, os.path.exists(cache_dir)
48
def make_kernel_like_tree(self, url=None, root='.',
49
hardlink_working=False):
50
"""Setup a temporary tree roughly like a kernel tree.
52
:param url: Creat the kernel like tree as a lightweight checkout
53
of a new branch created at url.
54
:param hardlink_working: instead of creating a new copy of all files
55
just hardlink the working tree. Tests must request this, because
56
they must break links if they want to change the files
59
b = bzrdir.BzrDir.create_branch_convenience(url)
60
d = bzrdir.BzrDir.create(root)
61
bzrlib.branch.BranchReferenceFormat().initialize(d, b)
62
tree = d.create_workingtree()
64
tree = bzrdir.BzrDir.create_standalone_workingtree(root)
66
self._link_or_copy_kernel_files(root=root, do_link=hardlink_working)
69
def _make_kernel_files(self, root='.'):
70
# a kernel tree has ~10000 and 500 directory, with most files around
72
# we simulate this by three levels of dirs named 0-7, givin 512 dirs,
75
for outer in range(8):
76
files.append("%s/" % outer)
77
for middle in range(8):
78
files.append("%s/%s/" % (outer, middle))
79
for inner in range(8):
80
prefix = "%s/%s/%s/" % (outer, middle, inner)
82
files.extend([prefix + str(foo) for foo in range(20)])
83
cwd = osutils.getcwd()
85
self.build_tree(files)
88
def _link_or_copy_kernel_files(self, root, do_link=True):
89
"""Hardlink the kernel files from the cached location.
91
If the platform doesn't correctly support hardlinking files, it
92
reverts to just creating new ones.
95
if not osutils.hardlinks_good() or not do_link:
96
# Turns out that 'shutil.copytree()' is no faster than
97
# just creating them. Probably the python overhead.
98
# Plain _make_kernel_files takes 5s
100
# using hardlinks takes < 1s.
101
self._make_kernel_files(root=root)
104
cache_dir, is_cached = self.get_cache_dir('kernel_like_tree')
107
self._make_kernel_files(root=cache_dir)
108
self._protect_files(cache_dir)
110
# Hardlinking the target directory is *much* faster (7s => <1s).
111
osutils.copy_tree(cache_dir, root,
112
handlers={'file':os.link})
114
def _clone_tree(self, source, dest, link_bzr=False, link_working=True):
115
"""Copy the contents from a given location to another location.
116
Optionally hardlink certain pieces of the tree.
118
:param source: The directory to copy
119
:param dest: The destination
120
:param link_bzr: Should the .bzr/ files be hardlinked?
121
:param link_working: Should the working tree be hardlinked?
123
# We use shutil.copyfile so that we don't copy permissions
124
# because most of our source trees are marked readonly to
125
# prevent modifying in the case of hardlinks
126
handlers = {'file':shutil.copyfile}
127
if osutils.hardlinks_good():
130
handlers = {'file':os.link}
132
# Don't hardlink files inside bzr
133
def file_handler(source, dest):
134
if '.bzr/' in source:
135
shutil.copyfile(source, dest)
137
os.link(source, dest)
138
handlers = {'file':file_handler}
140
# Only link files inside .bzr/
141
def file_handler(source, dest):
142
if '.bzr/' in source:
143
os.link(source, dest)
145
shutil.copyfile(source, dest)
146
handlers = {'file':file_handler}
147
osutils.copy_tree(source, dest, handlers=handlers)
149
def _protect_files(self, root):
150
"""Chmod all files underneath 'root' to prevent writing
152
:param root: The base directory to modify
154
for dirinfo, entries in osutils.walkdirs(root):
155
for relpath, name, kind, st, abspath in entries:
157
os.chmod(abspath, 0440)
159
def make_kernel_like_added_tree(self, root='.',
160
hardlink_working=True):
161
"""Make a kernel like tree, with all files added
163
:param root: Where to create the files
164
:param hardlink_working: Instead of copying all of the working tree
165
files, just hardlink them to the cached files. Tests can unlink
166
files that they will change.
168
# There isn't much underneath .bzr, so we don't support hardlinking
169
# it. Testing showed there wasn't much gain, and there is potentially
170
# a problem if someone modifies something underneath us.
171
cache_dir, is_cached = self.get_cache_dir('kernel_like_added_tree')
173
# Get a basic tree with working files
174
tree = self.make_kernel_like_tree(root=cache_dir,
175
hardlink_working=True)
176
# Add everything to it
177
add.smart_add_tree(tree, [cache_dir], recurse=True, save=True)
179
self._protect_files(cache_dir+'/.bzr')
181
self._clone_tree(cache_dir, root,
182
link_working=hardlink_working)
183
return workingtree.WorkingTree.open(root)
185
def make_kernel_like_committed_tree(self, root='.',
186
hardlink_working=True,
188
"""Make a kernel like tree, with all files added and committed
190
:param root: Where to create the files
191
:param hardlink_working: Instead of copying all of the working tree
192
files, just hardlink them to the cached files. Tests can unlink
193
files that they will change.
194
:param hardlink_bzr: Hardlink the .bzr directory. For readonly
195
operations this is safe, and shaves off a lot of setup time
197
cache_dir, is_cached = self.get_cache_dir('kernel_like_committed_tree')
199
# Get a basic tree with working files
200
tree = self.make_kernel_like_added_tree(root=cache_dir,
201
hardlink_working=True)
202
tree.commit('first post', rev_id='r1')
204
self._protect_files(cache_dir+'/.bzr')
206
# Now we have a cached tree, just copy it
207
self._clone_tree(cache_dir, root,
208
link_bzr=hardlink_bzr,
209
link_working=hardlink_working)
210
return workingtree.WorkingTree.open(root)
212
def make_many_commit_tree(self, directory_name='.'):
213
"""Create a tree with many commits.
215
No files change are included.
217
tree = bzrdir.BzrDir.create_standalone_workingtree(directory_name)
219
tree.branch.lock_write()
220
tree.branch.repository.lock_write()
222
for i in xrange(1000):
223
tree.commit('no-changes commit %d' % i)
227
tree.branch.repository.unlock()
234
def make_heavily_merged_tree(self, directory_name='.'):
235
"""Create a tree in which almost every commit is a merge.
237
No files change are included. This produces two trees,
238
one of which is returned. Except for the first commit, every
239
commit in its revision-history is a merge another commit in the other
242
tree = bzrdir.BzrDir.create_standalone_workingtree(directory_name)
245
tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
248
for i in xrange(250):
249
revision_id = tree.commit('no-changes commit %d-a' % i)
250
tree2.branch.fetch(tree.branch, revision_id)
251
tree2.set_pending_merges([revision_id])
252
revision_id = tree2.commit('no-changes commit %d-b' % i)
253
tree.branch.fetch(tree2.branch, revision_id)
254
tree.set_pending_merges([revision_id])
255
tree.set_pending_merges([])
264
"""Build and return a TestSuite which contains benchmark tests only."""
266
'bzrlib.benchmarks.bench_add',
267
'bzrlib.benchmarks.bench_bench',
268
'bzrlib.benchmarks.bench_checkout',
269
'bzrlib.benchmarks.bench_commit',
270
'bzrlib.benchmarks.bench_inventory',
271
'bzrlib.benchmarks.bench_log',
272
'bzrlib.benchmarks.bench_osutils',
273
'bzrlib.benchmarks.bench_rocks',
274
'bzrlib.benchmarks.bench_status',
275
'bzrlib.benchmarks.bench_transform',
276
'bzrlib.benchmarks.bench_workingtree',
278
suite = TestLoader().loadTestsFromModuleNames(testmod_names)
280
# Load any benchmarks from plugins
281
for name, module in plugin.all_plugins().items():
282
if getattr(module, 'bench_suite', None) is not None:
283
suite.addTest(module.bench_suite())