/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.2 by John Arbash Meinel
Allow caching basic kernel-like trees
36
    _cached_kernel_like_tree = None
37
    _cached_kernel_like_added_tree = None
1908.2.3 by John Arbash Meinel
Support caching a committed kernel-like tree, and mark hardlinked trees as readonly.
38
    _cached_kernel_like_committed_tree = None
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
39
40
    def make_kernel_like_tree(self, url=None, root='.',
41
                              hardlink_working=False):
1725.2.5 by Robert Collins
Bugfix create_branch_convenience at the root of a file system to not loop
42
        """Setup a temporary tree roughly like a kernel tree.
43
        
44
        :param url: Creat the kernel like tree as a lightweight checkout
45
        of a new branch created at url.
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
46
        :param hardlink_working: instead of creating a new copy of all files
47
            just hardlink the working tree. Tests must request this, because
48
            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
49
        """
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
50
        if url is not None:
51
            b = bzrdir.BzrDir.create_branch_convenience(url)
52
            d = bzrdir.BzrDir.create(root)
53
            bzrlib.branch.BranchReferenceFormat().initialize(d, b)
54
            tree = d.create_workingtree()
55
        else:
56
            tree = bzrdir.BzrDir.create_standalone_workingtree(root)
57
58
        self._link_or_copy_kernel_files(root=root, do_link=hardlink_working)
59
        return tree
60
61
    def _make_kernel_files(self, root='.'):
1714.1.4 by Robert Collins
Add new benchmarks for status and commit.
62
        # a kernel tree has ~10000 and 500 directory, with most files around 
63
        # 3-4 levels deep. 
64
        # we simulate this by three levels of dirs named 0-7, givin 512 dirs,
65
        # and 20 files each.
66
        files = []
67
        for outer in range(8):
68
            files.append("%s/" % outer)
69
            for middle in range(8):
70
                files.append("%s/%s/" % (outer, middle))
71
                for inner in range(8):
72
                    prefix = "%s/%s/%s/" % (outer, middle, inner)
73
                    files.append(prefix)
74
                    files.extend([prefix + str(foo) for foo in range(20)])
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
75
        cwd = osutils.getcwd()
76
        os.chdir(root)
1714.1.4 by Robert Collins
Add new benchmarks for status and commit.
77
        self.build_tree(files)
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
78
        os.chdir(cwd)
79
80
    def _link_or_copy_kernel_files(self, root, do_link=True):
81
        """Hardlink the kernel files from the cached location.
82
83
        If the platform doesn't correctly support hardlinking files, it
84
        reverts to just creating new ones.
85
        """
86
87
        if not osutils.hardlinks_good() or not do_link:
88
            # Turns out that 'shutil.copytree()' is no faster than
89
            # just creating them. Probably the python overhead.
90
            # Plain _make_kernel_files takes 5s
91
            # cp -a takes 3s
92
            # using hardlinks takes < 1s.
93
            self._make_kernel_files(root=root)
94
            return
95
96
        if Benchmark._cached_kernel_like_tree is None:
97
            cache_dir = osutils.pathjoin(self.TEST_ROOT,
98
                                         'cached_kernel_like_tree')
99
            os.mkdir(cache_dir)
100
            self._make_kernel_files(root=cache_dir)
1908.2.3 by John Arbash Meinel
Support caching a committed kernel-like tree, and mark hardlinked trees as readonly.
101
            self._protect_files(cache_dir)
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
102
            Benchmark._cached_kernel_like_tree = cache_dir
103
104
        # Hardlinking the target directory is *much* faster (7s => <1s).
105
        osutils.copy_tree(Benchmark._cached_kernel_like_tree, root,
106
                          handlers={'file':os.link})
107
1908.2.3 by John Arbash Meinel
Support caching a committed kernel-like tree, and mark hardlinked trees as readonly.
108
    def _clone_tree(self, source, dest, link_bzr=False, link_working=True):
109
        """Copy the contents from a given location to another location.
110
        Optionally hardlink certain pieces of the tree.
111
112
        :param source: The directory to copy
113
        :param dest: The destination
114
        :param link_bzr: Should the .bzr/ files be hardlinked?
115
        :param link_working: Should the working tree be hardlinked?
116
        """
117
        # We use shutil.copyfile so that we don't copy permissions
118
        # because most of our source trees are marked readonly to
119
        # prevent modifying in the case of hardlinks
120
        handlers = {'file':shutil.copyfile}
121
        if osutils.hardlinks_good():
122
            if link_working:
123
                if link_bzr:
124
                    handlers = {'file':os.link}
125
                else:
126
                    # Don't hardlink files inside bzr
127
                    def file_handler(source, dest):
128
                        if '.bzr/' in source:
129
                            shutil.copyfile(source, dest)
130
                        else:
131
                            os.link(source, dest)
132
                    handlers = {'file':file_handler}
133
            elif link_bzr:
134
                # Only link files inside .bzr/
135
                def file_handler(source, dest):
136
                    if '.bzr/' in source:
137
                        os.link(source, dest)
138
                    else:
139
                        shutil.copyfile(source, dest)
140
                handlers = {'file':file_handler}
141
        osutils.copy_tree(source, dest, handlers=handlers)
142
143
    def _protect_files(self, root):
144
        """Chmod all files underneath 'root' to prevent writing
145
146
        :param root: The base directory to modify
147
        """
148
        for dirinfo, entries in osutils.walkdirs(root):
149
            for relpath, name, kind, st, abspath in entries:
150
                if kind == 'file':
151
                    os.chmod(abspath, 0440)
152
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
153
    def make_kernel_like_added_tree(self, root='.',
154
                                    hardlink_working=True):
155
        """Make a kernel like tree, with all files added
156
157
        :param root: Where to create the files
158
        :param hardlink_working: Instead of copying all of the working tree
159
            files, just hardlink them to the cached files. Tests can unlink
160
            files that they will change.
161
        """
1908.2.3 by John Arbash Meinel
Support caching a committed kernel-like tree, and mark hardlinked trees as readonly.
162
        # There isn't much underneath .bzr, so we don't support hardlinking
163
        # it. Testing showed there wasn't much gain, and there is potentially
164
        # a problem if someone modifies something underneath us.
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
165
        if Benchmark._cached_kernel_like_added_tree is None:
166
            cache_dir = osutils.pathjoin(self.TEST_ROOT,
167
                                         'cached_kernel_like_added_tree')
168
            # Get a basic tree with working files
169
            tree = self.make_kernel_like_tree(root=cache_dir,
170
                                              hardlink_working=True)
171
            # Add everything to it
172
            add.smart_add_tree(tree, [cache_dir], recurse=True, save=True)
1908.2.3 by John Arbash Meinel
Support caching a committed kernel-like tree, and mark hardlinked trees as readonly.
173
174
            self._protect_files(cache_dir+'/.bzr')
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
175
            Benchmark._cached_kernel_like_added_tree = cache_dir
176
1908.2.3 by John Arbash Meinel
Support caching a committed kernel-like tree, and mark hardlinked trees as readonly.
177
        self._clone_tree(Benchmark._cached_kernel_like_added_tree, root,
178
                         link_working=hardlink_working)
179
        return workingtree.WorkingTree.open(root)
180
181
    def make_kernel_like_committed_tree(self, root='.',
182
                                    hardlink_working=True,
183
                                    hardlink_bzr=False):
184
        """Make a kernel like tree, with all files added and committed
185
186
        :param root: Where to create the files
187
        :param hardlink_working: Instead of copying all of the working tree
188
            files, just hardlink them to the cached files. Tests can unlink
189
            files that they will change.
190
        :param hardlink_bzr: Hardlink the .bzr directory. For readonly 
191
            operations this is safe, and shaves off a lot of setup time
192
        """
193
        if Benchmark._cached_kernel_like_committed_tree is None:
194
            cache_dir = osutils.pathjoin(self.TEST_ROOT,
195
                                         'cached_kernel_like_committed_tree')
196
            # Get a basic tree with working files
197
            tree = self.make_kernel_like_added_tree(root=cache_dir,
198
                                                    hardlink_working=True)
199
            tree.commit('first post', rev_id='r1')
200
201
            self._protect_files(cache_dir+'/.bzr')
202
            Benchmark._cached_kernel_like_committed_tree = cache_dir
203
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
204
        # Now we have a cached tree, just copy it
1908.2.3 by John Arbash Meinel
Support caching a committed kernel-like tree, and mark hardlinked trees as readonly.
205
        self._clone_tree(Benchmark._cached_kernel_like_committed_tree, root,
206
                         link_bzr=hardlink_bzr,
207
                         link_working=hardlink_working)
1908.2.2 by John Arbash Meinel
Allow caching basic kernel-like trees
208
        return workingtree.WorkingTree.open(root)
1707.2.2 by Robert Collins
Start on bench_add, an add benchtest.
209
1756.1.2 by Aaron Bentley
Show logs using get_revisions
210
    def make_many_commit_tree(self, directory_name='.'):
1756.2.21 by Aaron Bentley
Clean up merge log benchmark
211
        """Create a tree with many commits.
1756.1.2 by Aaron Bentley
Show logs using get_revisions
212
        
213
        No files change are included.
214
        """
1711.2.82 by John Arbash Meinel
minor fix from Jan Balster to get --benchmark to work again
215
        tree = bzrdir.BzrDir.create_standalone_workingtree(directory_name)
1756.1.2 by Aaron Bentley
Show logs using get_revisions
216
        tree.lock_write()
217
        tree.branch.lock_write()
218
        tree.branch.repository.lock_write()
219
        try:
220
            for i in xrange(1000):
221
                tree.commit('no-changes commit %d' % i)
222
        finally:
223
            try:
224
                try:
225
                    tree.branch.repository.unlock()
226
                finally:
227
                    tree.branch.unlock()
228
            finally:
229
                tree.unlock()
230
        return tree
231
1756.2.19 by Aaron Bentley
Add benchmarks for merged trees
232
    def make_heavily_merged_tree(self, directory_name='.'):
1756.2.21 by Aaron Bentley
Clean up merge log benchmark
233
        """Create a tree in which almost every commit is a merge.
234
       
235
        No files change are included.  This produces two trees, 
236
        one of which is returned.  Except for the first commit, every
237
        commit in its revision-history is a merge another commit in the other
238
        tree.
1756.2.19 by Aaron Bentley
Add benchmarks for merged trees
239
        """
1711.2.82 by John Arbash Meinel
minor fix from Jan Balster to get --benchmark to work again
240
        tree = bzrdir.BzrDir.create_standalone_workingtree(directory_name)
1756.2.19 by Aaron Bentley
Add benchmarks for merged trees
241
        tree.lock_write()
242
        try:
1756.2.21 by Aaron Bentley
Clean up merge log benchmark
243
            tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
244
            tree2.lock_write()
1756.2.19 by Aaron Bentley
Add benchmarks for merged trees
245
            try:
1756.2.21 by Aaron Bentley
Clean up merge log benchmark
246
                for i in xrange(250):
247
                    revision_id = tree.commit('no-changes commit %d-a' % i)
248
                    tree2.branch.fetch(tree.branch, revision_id)
249
                    tree2.set_pending_merges([revision_id])
250
                    revision_id = tree2.commit('no-changes commit %d-b' % i)
251
                    tree.branch.fetch(tree2.branch, revision_id)
252
                    tree.set_pending_merges([revision_id])
253
                tree.set_pending_merges([])
1756.2.19 by Aaron Bentley
Add benchmarks for merged trees
254
            finally:
255
                tree.unlock()
1756.2.21 by Aaron Bentley
Clean up merge log benchmark
256
        finally:
257
            tree2.unlock()
1756.2.19 by Aaron Bentley
Add benchmarks for merged trees
258
        return tree
259
1707.2.2 by Robert Collins
Start on bench_add, an add benchtest.
260
261
def test_suite():
262
    """Build and return a TestSuite which contains benchmark tests only."""
263
    testmod_names = [ \
264
                   'bzrlib.benchmarks.bench_add',
1755.2.1 by Robert Collins
Add a benchmark for make_kernel_like_tree.
265
                   'bzrlib.benchmarks.bench_bench',
1714.1.4 by Robert Collins
Add new benchmarks for status and commit.
266
                   'bzrlib.benchmarks.bench_checkout',
1714.1.5 by Robert Collins
Add commit benchmark.
267
                   'bzrlib.benchmarks.bench_commit',
1757.2.10 by Robert Collins
Give all inventory entries __slots__ that are useful with the current codebase.
268
                   'bzrlib.benchmarks.bench_inventory',
1756.1.7 by Aaron Bentley
Merge bzr.dev
269
                   'bzrlib.benchmarks.bench_log',
1756.1.2 by Aaron Bentley
Show logs using get_revisions
270
                   'bzrlib.benchmarks.bench_osutils',
1752.1.2 by Aaron Bentley
Benchmark the rocks command
271
                   'bzrlib.benchmarks.bench_rocks',
1714.1.4 by Robert Collins
Add new benchmarks for status and commit.
272
                   'bzrlib.benchmarks.bench_status',
1534.10.33 by Aaron Bentley
Add canonicalize_path benchmark
273
                   'bzrlib.benchmarks.bench_transform',
1732.1.11 by John Arbash Meinel
Trying multiple things to get WorkingTree.list_files time down
274
                   'bzrlib.benchmarks.bench_workingtree',
1707.2.2 by Robert Collins
Start on bench_add, an add benchtest.
275
                   ]
1841.1.1 by John Arbash Meinel
Allow plugins to provide benchmarks just like they do tests
276
    suite = TestLoader().loadTestsFromModuleNames(testmod_names) 
277
278
    # Load any benchmarks from plugins
1711.2.78 by John Arbash Meinel
Cleanup the imports in bzrlib.benchmark
279
    for name, module in plugin.all_plugins().items():
280
        if getattr(module, 'bench_suite', None) is not None:
281
            suite.addTest(module.bench_suite())
1841.1.1 by John Arbash Meinel
Allow plugins to provide benchmarks just like they do tests
282
283
    return suite