/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1707.2.2 by Robert Collins
Start on bench_add, an add benchtest.
1
# Copyright (C) 2006 by Canonical Ltd
2
#
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.
7
#
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.
12
#
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
18
"""Benchmark test suite for bzr."""
19
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
20
import os
21
import shutil
22
23
from bzrlib import (
24
    add,
25
    bzrdir,
26
    osutils,
27
    plugin,
28
    workingtree,
29
    )
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
30
from bzrlib.tests.TestUtil import TestLoader
1714.1.4 by Robert Collins
Add new benchmarks for status and commit.
31
from bzrlib.tests.blackbox import ExternalBase
32
1841.1.1 by John Arbash Meinel
Allow plugins to provide benchmarks just like they do tests
33
1714.1.4 by Robert Collins
Add new benchmarks for status and commit.
34
class Benchmark(ExternalBase):
35
1908.2.4 by John Arbash Meinel
Add the ability to specify a benchmark cache directory.
36
    CACHE_ROOT = None
37
38
    def get_cache_dir(self, extra):
39
        """Get the directory to use for caching the given object."""
40
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)
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
47
48
    def make_kernel_like_tree(self, url=None, root='.',
49
                              hardlink_working=False):
1725.2.5 by Robert Collins
Bugfix create_branch_convenience at the root of a file system to not loop
50
        """Setup a temporary tree roughly like a kernel tree.
51
        
52
        :param url: Creat the kernel like tree as a lightweight checkout
53
        of a new branch created at url.
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
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
1725.2.5 by Robert Collins
Bugfix create_branch_convenience at the root of a file system to not loop
57
        """
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
58
        if url is not None:
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()
63
        else:
64
            tree = bzrdir.BzrDir.create_standalone_workingtree(root)
65
66
        self._link_or_copy_kernel_files(root=root, do_link=hardlink_working)
67
        return tree
68
69
    def _make_kernel_files(self, root='.'):
1714.1.4 by Robert Collins
Add new benchmarks for status and commit.
70
        # a kernel tree has ~10000 and 500 directory, with most files around 
71
        # 3-4 levels deep. 
72
        # we simulate this by three levels of dirs named 0-7, givin 512 dirs,
73
        # and 20 files each.
74
        files = []
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)
81
                    files.append(prefix)
82
                    files.extend([prefix + str(foo) for foo in range(20)])
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
83
        cwd = osutils.getcwd()
84
        os.chdir(root)
1714.1.4 by Robert Collins
Add new benchmarks for status and commit.
85
        self.build_tree(files)
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
86
        os.chdir(cwd)
87
1908.2.5 by John Arbash Meinel
Updated bench_bench tests to test exactly what we really want to test
88
    def _cache_kernel_like_tree(self):
89
        """Create the kernel_like_tree cache dir if it doesn't exist"""
90
        cache_dir, is_cached = self.get_cache_dir('kernel_like_tree')
91
        if is_cached:
92
            return cache_dir
93
        os.mkdir(cache_dir)
94
        self._make_kernel_files(root=cache_dir)
95
        self._protect_files(cache_dir)
96
        return cache_dir
97
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
98
    def _link_or_copy_kernel_files(self, root, do_link=True):
99
        """Hardlink the kernel files from the cached location.
100
101
        If the platform doesn't correctly support hardlinking files, it
102
        reverts to just creating new ones.
103
        """
104
105
        if not osutils.hardlinks_good() or not do_link:
106
            # Turns out that 'shutil.copytree()' is no faster than
107
            # just creating them. Probably the python overhead.
108
            # Plain _make_kernel_files takes 5s
109
            # cp -a takes 3s
110
            # using hardlinks takes < 1s.
111
            self._make_kernel_files(root=root)
112
            return
113
1908.2.5 by John Arbash Meinel
Updated bench_bench tests to test exactly what we really want to test
114
        cache_dir = self._cache_kernel_like_tree()
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
115
116
        # Hardlinking the target directory is *much* faster (7s => <1s).
1908.2.4 by John Arbash Meinel
Add the ability to specify a benchmark cache directory.
117
        osutils.copy_tree(cache_dir, root,
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
118
                          handlers={'file':os.link})
119
1908.2.9 by John Arbash Meinel
Allow pre-warming the hash-cache
120
    def _clone_tree(self, source, dest, link_bzr=False, link_working=True,
121
                    hot_cache=True):
1908.2.3 by John Arbash Meinel
Support caching a committed kernel-like tree, and mark hardlinked trees as readonly.
122
        """Copy the contents from a given location to another location.
123
        Optionally hardlink certain pieces of the tree.
124
125
        :param source: The directory to copy
126
        :param dest: The destination
127
        :param link_bzr: Should the .bzr/ files be hardlinked?
128
        :param link_working: Should the working tree be hardlinked?
1908.2.9 by John Arbash Meinel
Allow pre-warming the hash-cache
129
        :param hot_cache: Update the hash-cache when you are done
1908.2.3 by John Arbash Meinel
Support caching a committed kernel-like tree, and mark hardlinked trees as readonly.
130
        """
131
        # We use shutil.copyfile so that we don't copy permissions
132
        # because most of our source trees are marked readonly to
133
        # prevent modifying in the case of hardlinks
134
        handlers = {'file':shutil.copyfile}
135
        if osutils.hardlinks_good():
136
            if link_working:
137
                if link_bzr:
138
                    handlers = {'file':os.link}
139
                else:
140
                    # Don't hardlink files inside bzr
141
                    def file_handler(source, dest):
142
                        if '.bzr/' in source:
143
                            shutil.copyfile(source, dest)
144
                        else:
145
                            os.link(source, dest)
146
                    handlers = {'file':file_handler}
147
            elif link_bzr:
148
                # Only link files inside .bzr/
149
                def file_handler(source, dest):
150
                    if '.bzr/' in source:
151
                        os.link(source, dest)
152
                    else:
153
                        shutil.copyfile(source, dest)
154
                handlers = {'file':file_handler}
155
        osutils.copy_tree(source, dest, handlers=handlers)
1908.2.9 by John Arbash Meinel
Allow pre-warming the hash-cache
156
        tree = workingtree.WorkingTree.open(dest)
157
        if hot_cache:
158
            tree.lock_write()
159
            try:
160
                # tree._hashcache.scan() just checks and removes
161
                # entries that are out of date
162
                # we need to actually store new ones
163
                for path, ie in tree.inventory.iter_entries_by_dir():
164
                    tree.get_file_sha1(ie.file_id, path)
165
            finally:
166
                tree.unlock()
167
        # If we didn't iterate the tree, the hash cache is technically
168
        # invalid, and it would be better to remove it, but there is
169
        # no public api for that.
170
        return tree
1908.2.3 by John Arbash Meinel
Support caching a committed kernel-like tree, and mark hardlinked trees as readonly.
171
172
    def _protect_files(self, root):
173
        """Chmod all files underneath 'root' to prevent writing
174
175
        :param root: The base directory to modify
176
        """
177
        for dirinfo, entries in osutils.walkdirs(root):
178
            for relpath, name, kind, st, abspath in entries:
179
                if kind == 'file':
180
                    os.chmod(abspath, 0440)
181
1908.2.5 by John Arbash Meinel
Updated bench_bench tests to test exactly what we really want to test
182
    def _cache_kernel_like_added_tree(self):
183
        cache_dir, is_cached = self.get_cache_dir('kernel_like_added_tree')
184
        if is_cached:
185
            return cache_dir
186
187
        # Get a basic tree with working files
188
        tree = self.make_kernel_like_tree(root=cache_dir,
189
                                          hardlink_working=True)
190
        # Add everything to it
1908.2.9 by John Arbash Meinel
Allow pre-warming the hash-cache
191
        tree.lock_write()
192
        try:
193
            add.smart_add_tree(tree, [cache_dir], recurse=True, save=True)
194
            self._protect_files(cache_dir+'/.bzr')
195
        finally:
196
            tree.unlock()
1908.2.5 by John Arbash Meinel
Updated bench_bench tests to test exactly what we really want to test
197
198
        return cache_dir
199
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
200
    def make_kernel_like_added_tree(self, root='.',
1908.2.9 by John Arbash Meinel
Allow pre-warming the hash-cache
201
                                    hardlink_working=True,
202
                                    hot_cache=True):
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
203
        """Make a kernel like tree, with all files added
204
205
        :param root: Where to create the files
206
        :param hardlink_working: Instead of copying all of the working tree
207
            files, just hardlink them to the cached files. Tests can unlink
208
            files that they will change.
1908.2.9 by John Arbash Meinel
Allow pre-warming the hash-cache
209
        :param hot_cache: Run through the newly created tree and make sure
210
            the stat-cache is correct. The old way of creating a freshly
211
            added tree always had a hot cache.
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
212
        """
1908.2.3 by John Arbash Meinel
Support caching a committed kernel-like tree, and mark hardlinked trees as readonly.
213
        # There isn't much underneath .bzr, so we don't support hardlinking
214
        # it. Testing showed there wasn't much gain, and there is potentially
215
        # a problem if someone modifies something underneath us.
1908.2.5 by John Arbash Meinel
Updated bench_bench tests to test exactly what we really want to test
216
        cache_dir = self._cache_kernel_like_added_tree()
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
217
1908.2.9 by John Arbash Meinel
Allow pre-warming the hash-cache
218
        return self._clone_tree(cache_dir, root,
219
                                link_working=hardlink_working,
220
                                hot_cache=hot_cache)
1908.2.3 by John Arbash Meinel
Support caching a committed kernel-like tree, and mark hardlinked trees as readonly.
221
1908.2.5 by John Arbash Meinel
Updated bench_bench tests to test exactly what we really want to test
222
    def _cache_kernel_like_committed_tree(self):
223
        cache_dir, is_cached = self.get_cache_dir('kernel_like_committed_tree')
224
        if is_cached:
225
            return cache_dir
226
227
        # Get a basic tree with working files
228
        tree = self.make_kernel_like_added_tree(root=cache_dir,
1908.2.9 by John Arbash Meinel
Allow pre-warming the hash-cache
229
                                                hardlink_working=True,
230
                                                hot_cache=False)
1908.2.5 by John Arbash Meinel
Updated bench_bench tests to test exactly what we really want to test
231
        tree.commit('first post', rev_id='r1')
232
233
        self._protect_files(cache_dir+'/.bzr')
234
        return cache_dir
235
1908.2.3 by John Arbash Meinel
Support caching a committed kernel-like tree, and mark hardlinked trees as readonly.
236
    def make_kernel_like_committed_tree(self, root='.',
237
                                    hardlink_working=True,
1908.2.9 by John Arbash Meinel
Allow pre-warming the hash-cache
238
                                    hardlink_bzr=False,
239
                                    hot_cache=True):
1908.2.3 by John Arbash Meinel
Support caching a committed kernel-like tree, and mark hardlinked trees as readonly.
240
        """Make a kernel like tree, with all files added and committed
241
242
        :param root: Where to create the files
243
        :param hardlink_working: Instead of copying all of the working tree
244
            files, just hardlink them to the cached files. Tests can unlink
245
            files that they will change.
246
        :param hardlink_bzr: Hardlink the .bzr directory. For readonly 
247
            operations this is safe, and shaves off a lot of setup time
248
        """
1908.2.5 by John Arbash Meinel
Updated bench_bench tests to test exactly what we really want to test
249
        cache_dir = self._cache_kernel_like_committed_tree()
1908.2.3 by John Arbash Meinel
Support caching a committed kernel-like tree, and mark hardlinked trees as readonly.
250
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
251
        # Now we have a cached tree, just copy it
1908.2.9 by John Arbash Meinel
Allow pre-warming the hash-cache
252
        return self._clone_tree(cache_dir, root,
253
                                link_bzr=hardlink_bzr,
254
                                link_working=hardlink_working,
255
                                hot_cache=hot_cache)
1707.2.2 by Robert Collins
Start on bench_add, an add benchtest.
256
1908.2.6 by John Arbash Meinel
Allow the many_merged and many_commit trees to be cached
257
    def _cache_many_commit_tree(self):
258
        cache_dir, is_cached = self.get_cache_dir('many_commit_tree')
259
        if is_cached:
260
            return cache_dir
261
262
        tree = bzrdir.BzrDir.create_standalone_workingtree(cache_dir)
1756.1.2 by Aaron Bentley
Show logs using get_revisions
263
        tree.lock_write()
264
        tree.branch.lock_write()
265
        tree.branch.repository.lock_write()
266
        try:
267
            for i in xrange(1000):
268
                tree.commit('no-changes commit %d' % i)
269
        finally:
270
            try:
271
                try:
272
                    tree.branch.repository.unlock()
273
                finally:
274
                    tree.branch.unlock()
275
            finally:
276
                tree.unlock()
1908.2.6 by John Arbash Meinel
Allow the many_merged and many_commit trees to be cached
277
278
        return cache_dir
279
280
    def make_many_commit_tree(self, directory_name='.',
281
                              hardlink=False):
282
        """Create a tree with many commits.
283
        
284
        No file changes are included. Not hardlinking the working tree, 
285
        because there are no working tree files.
1756.2.19 by Aaron Bentley
Add benchmarks for merged trees
286
        """
1908.2.6 by John Arbash Meinel
Allow the many_merged and many_commit trees to be cached
287
        cache_dir = self._cache_many_commit_tree()
1908.2.9 by John Arbash Meinel
Allow pre-warming the hash-cache
288
        return self._clone_tree(cache_dir, directory_name,
289
                                link_bzr=hardlink,
290
                                hot_cache=True)
1908.2.6 by John Arbash Meinel
Allow the many_merged and many_commit trees to be cached
291
292
    def _cache_heavily_merged_tree(self):
293
        cache_dir, is_cached = self.get_cache_dir('heavily_merged_tree')
294
        if is_cached:
295
            return cache_dir
296
297
        os.mkdir(cache_dir)
298
        tree = bzrdir.BzrDir.create_standalone_workingtree(
299
                cache_dir + '/tree1')
1756.2.19 by Aaron Bentley
Add benchmarks for merged trees
300
        tree.lock_write()
301
        try:
1908.2.6 by John Arbash Meinel
Allow the many_merged and many_commit trees to be cached
302
            tree2 = tree.bzrdir.sprout(cache_dir + '/tree2').open_workingtree()
1756.2.21 by Aaron Bentley
Clean up merge log benchmark
303
            tree2.lock_write()
1756.2.19 by Aaron Bentley
Add benchmarks for merged trees
304
            try:
1756.2.21 by Aaron Bentley
Clean up merge log benchmark
305
                for i in xrange(250):
306
                    revision_id = tree.commit('no-changes commit %d-a' % i)
307
                    tree2.branch.fetch(tree.branch, revision_id)
308
                    tree2.set_pending_merges([revision_id])
309
                    revision_id = tree2.commit('no-changes commit %d-b' % i)
310
                    tree.branch.fetch(tree2.branch, revision_id)
311
                    tree.set_pending_merges([revision_id])
312
                tree.set_pending_merges([])
1756.2.19 by Aaron Bentley
Add benchmarks for merged trees
313
            finally:
1908.2.6 by John Arbash Meinel
Allow the many_merged and many_commit trees to be cached
314
                tree2.unlock()
1756.2.21 by Aaron Bentley
Clean up merge log benchmark
315
        finally:
1908.2.6 by John Arbash Meinel
Allow the many_merged and many_commit trees to be cached
316
            tree.unlock()
317
        return cache_dir
318
319
    def make_heavily_merged_tree(self, directory_name='.',
320
                                 hardlink=False):
321
        """Create a tree in which almost every commit is a merge.
322
       
323
        No file changes are included.  This produces two trees, 
324
        one of which is returned.  Except for the first commit, every
325
        commit in its revision-history is a merge another commit in the other
326
        tree.  Not hardlinking the working tree, because there are no working 
327
        tree files.
328
        """
329
        cache_dir = self._cache_heavily_merged_tree()
330
        tree_dir = cache_dir + '/tree1'
1908.2.9 by John Arbash Meinel
Allow pre-warming the hash-cache
331
        return self._clone_tree(tree_dir, directory_name,
332
                                link_bzr=hardlink,
333
                                hot_cache=True)
1756.2.19 by Aaron Bentley
Add benchmarks for merged trees
334
1707.2.2 by Robert Collins
Start on bench_add, an add benchtest.
335
336
def test_suite():
337
    """Build and return a TestSuite which contains benchmark tests only."""
338
    testmod_names = [ \
339
                   'bzrlib.benchmarks.bench_add',
1755.2.1 by Robert Collins
Add a benchmark for make_kernel_like_tree.
340
                   'bzrlib.benchmarks.bench_bench',
1714.1.4 by Robert Collins
Add new benchmarks for status and commit.
341
                   'bzrlib.benchmarks.bench_checkout',
1714.1.5 by Robert Collins
Add commit benchmark.
342
                   'bzrlib.benchmarks.bench_commit',
1757.2.10 by Robert Collins
Give all inventory entries __slots__ that are useful with the current codebase.
343
                   'bzrlib.benchmarks.bench_inventory',
1756.1.7 by Aaron Bentley
Merge bzr.dev
344
                   'bzrlib.benchmarks.bench_log',
1756.1.2 by Aaron Bentley
Show logs using get_revisions
345
                   'bzrlib.benchmarks.bench_osutils',
1752.1.2 by Aaron Bentley
Benchmark the rocks command
346
                   'bzrlib.benchmarks.bench_rocks',
1714.1.4 by Robert Collins
Add new benchmarks for status and commit.
347
                   'bzrlib.benchmarks.bench_status',
1534.10.33 by Aaron Bentley
Add canonicalize_path benchmark
348
                   'bzrlib.benchmarks.bench_transform',
1732.1.11 by John Arbash Meinel
Trying multiple things to get WorkingTree.list_files time down
349
                   'bzrlib.benchmarks.bench_workingtree',
1707.2.2 by Robert Collins
Start on bench_add, an add benchtest.
350
                   ]
1841.1.1 by John Arbash Meinel
Allow plugins to provide benchmarks just like they do tests
351
    suite = TestLoader().loadTestsFromModuleNames(testmod_names) 
352
353
    # Load any benchmarks from plugins
1711.2.78 by John Arbash Meinel
Cleanup the imports in bzrlib.benchmark
354
    for name, module in plugin.all_plugins().items():
355
        if getattr(module, 'bench_suite', None) is not None:
356
            suite.addTest(module.bench_suite())
1841.1.1 by John Arbash Meinel
Allow plugins to provide benchmarks just like they do tests
357
358
    return suite