~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/per_bzrdir/test_bzrdir.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-09-10 11:13:02 UTC
  • mfrom: (5363.2.31 controldir-2)
  • Revision ID: pqm@pqm.ubuntu.com-20100910111302-xbneyohu4we926bl
(jelmer) More work allowing the per_controldir tests to run against foreign
 formats. (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006-2010 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""Tests for bzrdir implementations - tests a bzrdir format."""
 
18
 
 
19
import errno
 
20
from stat import S_ISDIR
 
21
 
 
22
import bzrlib.branch
 
23
from bzrlib import (
 
24
    errors,
 
25
    revision as _mod_revision,
 
26
    transport,
 
27
    )
 
28
from bzrlib.tests import (
 
29
    TestSkipped,
 
30
    )
 
31
from bzrlib.tests.per_bzrdir import TestCaseWithBzrDir
 
32
from bzrlib.transport.local import (
 
33
    LocalTransport,
 
34
    )
 
35
 
 
36
 
 
37
class TestBzrDir(TestCaseWithBzrDir):
 
38
 
 
39
    # Many of these tests test for disk equality rather than checking
 
40
    # for semantic equivalence. This works well for some tests but
 
41
    # is not good at handling changes in representation or the addition
 
42
    # or removal of control data. It would be nice to for instance:
 
43
    # sprout a new branch, check that the nickname has been reset by hand
 
44
    # and then set the nickname to match the source branch, at which point
 
45
    # a semantic equivalence should pass
 
46
 
 
47
    def assertDirectoriesEqual(self, source, target, ignore_list=[]):
 
48
        """Assert that the content of source and target are identical.
 
49
 
 
50
        paths in ignore list will be completely ignored.
 
51
 
 
52
        We ignore paths that represent data which is allowed to change during
 
53
        a clone or sprout: for instance, inventory.knit contains gzip fragements
 
54
        which have timestamps in them, and as we have read the inventory from
 
55
        the source knit, the already-read data is recompressed rather than
 
56
        reading it again, which leads to changed timestamps. This is ok though,
 
57
        because the inventory.kndx file is not ignored, and the integrity of
 
58
        knit joins is tested by test_knit and test_versionedfile.
 
59
 
 
60
        :seealso: Additionally, assertRepositoryHasSameItems provides value
 
61
            rather than representation checking of repositories for
 
62
            equivalence.
 
63
        """
 
64
        files = []
 
65
        directories = ['.']
 
66
        while directories:
 
67
            dir = directories.pop()
 
68
            for path in set(source.list_dir(dir) + target.list_dir(dir)):
 
69
                path = dir + '/' + path
 
70
                if path in ignore_list:
 
71
                    continue
 
72
                try:
 
73
                    stat = source.stat(path)
 
74
                except errors.NoSuchFile:
 
75
                    self.fail('%s not in source' % path)
 
76
                if S_ISDIR(stat.st_mode):
 
77
                    self.assertTrue(S_ISDIR(target.stat(path).st_mode))
 
78
                    directories.append(path)
 
79
                else:
 
80
                    self.assertEqualDiff(source.get(path).read(),
 
81
                                         target.get(path).read(),
 
82
                                         "text for file %r differs:\n" % path)
 
83
 
 
84
    def assertRepositoryHasSameItems(self, left_repo, right_repo):
 
85
        """require left_repo and right_repo to contain the same data."""
 
86
        # XXX: TODO: Doesn't work yet, because we need to be able to compare
 
87
        # local repositories to remote ones...  but this is an as-yet unsolved
 
88
        # aspect of format management and the Remote protocols...
 
89
        # self.assertEqual(left_repo._format.__class__,
 
90
        #     right_repo._format.__class__)
 
91
        left_repo.lock_read()
 
92
        try:
 
93
            right_repo.lock_read()
 
94
            try:
 
95
                # revs
 
96
                all_revs = left_repo.all_revision_ids()
 
97
                self.assertEqual(left_repo.all_revision_ids(),
 
98
                    right_repo.all_revision_ids())
 
99
                for rev_id in left_repo.all_revision_ids():
 
100
                    self.assertEqual(left_repo.get_revision(rev_id),
 
101
                        right_repo.get_revision(rev_id))
 
102
                # Assert the revision trees (and thus the inventories) are equal
 
103
                sort_key = lambda rev_tree: rev_tree.get_revision_id()
 
104
                rev_trees_a = sorted(
 
105
                    left_repo.revision_trees(all_revs), key=sort_key)
 
106
                rev_trees_b = sorted(
 
107
                    right_repo.revision_trees(all_revs), key=sort_key)
 
108
                for tree_a, tree_b in zip(rev_trees_a, rev_trees_b):
 
109
                    self.assertEqual([], list(tree_a.iter_changes(tree_b)))
 
110
                # texts
 
111
                text_index = left_repo._generate_text_key_index()
 
112
                self.assertEqual(text_index,
 
113
                    right_repo._generate_text_key_index())
 
114
                desired_files = []
 
115
                for file_id, revision_id in text_index.iterkeys():
 
116
                    desired_files.append(
 
117
                        (file_id, revision_id, (file_id, revision_id)))
 
118
                left_texts = list(left_repo.iter_files_bytes(desired_files))
 
119
                right_texts = list(right_repo.iter_files_bytes(desired_files))
 
120
                left_texts.sort()
 
121
                right_texts.sort()
 
122
                self.assertEqual(left_texts, right_texts)
 
123
                # signatures
 
124
                for rev_id in all_revs:
 
125
                    try:
 
126
                        left_text = left_repo.get_signature_text(rev_id)
 
127
                    except errors.NoSuchRevision:
 
128
                        continue
 
129
                    right_text = right_repo.get_signature_text(rev_id)
 
130
                    self.assertEqual(left_text, right_text)
 
131
            finally:
 
132
                right_repo.unlock()
 
133
        finally:
 
134
            left_repo.unlock()
 
135
 
 
136
    def sproutOrSkip(self, from_bzrdir, to_url, revision_id=None,
 
137
                     force_new_repo=False, accelerator_tree=None,
 
138
                     create_tree_if_local=True):
 
139
        """Sprout from_bzrdir into to_url, or raise TestSkipped.
 
140
 
 
141
        A simple wrapper for from_bzrdir.sprout that translates NotLocalUrl into
 
142
        TestSkipped.  Returns the newly sprouted bzrdir.
 
143
        """
 
144
        to_transport = transport.get_transport(to_url)
 
145
        if not isinstance(to_transport, LocalTransport):
 
146
            raise TestSkipped('Cannot sprout to remote bzrdirs.')
 
147
        target = from_bzrdir.sprout(to_url, revision_id=revision_id,
 
148
                                    force_new_repo=force_new_repo,
 
149
                                    possible_transports=[to_transport],
 
150
                                    accelerator_tree=accelerator_tree,
 
151
                                    create_tree_if_local=create_tree_if_local)
 
152
        return target
 
153
 
 
154
    def skipIfNoWorkingTree(self, a_bzrdir):
 
155
        """Raises TestSkipped if a_bzrdir doesn't have a working tree.
 
156
 
 
157
        If the bzrdir does have a workingtree, this is a no-op.
 
158
        """
 
159
        try:
 
160
            a_bzrdir.open_workingtree()
 
161
        except (errors.NotLocalUrl, errors.NoWorkingTree):
 
162
            raise TestSkipped("bzrdir on transport %r has no working tree"
 
163
                              % a_bzrdir.transport)
 
164
 
 
165
    def createWorkingTreeOrSkip(self, a_bzrdir):
 
166
        """Create a working tree on a_bzrdir, or raise TestSkipped.
 
167
 
 
168
        A simple wrapper for create_workingtree that translates NotLocalUrl into
 
169
        TestSkipped.  Returns the newly created working tree.
 
170
        """
 
171
        try:
 
172
            return a_bzrdir.create_workingtree()
 
173
        except errors.NotLocalUrl:
 
174
            raise TestSkipped("cannot make working tree with transport %r"
 
175
                              % a_bzrdir.transport)
 
176
 
 
177
    def test_clone_bzrdir_repository_under_shared_force_new_repo(self):
 
178
        tree = self.make_branch_and_tree('commit_tree')
 
179
        self.build_tree(['commit_tree/foo'])
 
180
        tree.add('foo')
 
181
        tree.commit('revision 1', rev_id='1')
 
182
        dir = self.make_bzrdir('source')
 
183
        repo = dir.create_repository()
 
184
        repo.fetch(tree.branch.repository)
 
185
        self.assertTrue(repo.has_revision('1'))
 
186
        try:
 
187
            self.make_repository('target', shared=True)
 
188
        except errors.IncompatibleFormat:
 
189
            return
 
190
        target = dir.clone(self.get_url('target/child'), force_new_repo=True)
 
191
        self.assertNotEqual(dir.transport.base, target.transport.base)
 
192
        self.assertDirectoriesEqual(dir.root_transport, target.root_transport,
 
193
                                    ['./.bzr/repository',
 
194
                                     ])
 
195
        self.assertRepositoryHasSameItems(tree.branch.repository, repo)
 
196
 
 
197
    def test_clone_bzrdir_branch_and_repo(self):
 
198
        tree = self.make_branch_and_tree('commit_tree')
 
199
        self.build_tree(['commit_tree/foo'])
 
200
        tree.add('foo')
 
201
        tree.commit('revision 1')
 
202
        source = self.make_branch('source')
 
203
        tree.branch.repository.copy_content_into(source.repository)
 
204
        tree.branch.copy_content_into(source)
 
205
        dir = source.bzrdir
 
206
        target = dir.clone(self.get_url('target'))
 
207
        self.assertNotEqual(dir.transport.base, target.transport.base)
 
208
        self.assertDirectoriesEqual(dir.root_transport, target.root_transport,
 
209
                                    [
 
210
                                     './.bzr/basis-inventory-cache',
 
211
                                     './.bzr/checkout/stat-cache',
 
212
                                     './.bzr/merge-hashes',
 
213
                                     './.bzr/repository',
 
214
                                     './.bzr/stat-cache',
 
215
                                    ])
 
216
        self.assertRepositoryHasSameItems(
 
217
            tree.branch.repository, target.open_repository())
 
218
 
 
219
    def test_clone_on_transport(self):
 
220
        a_dir = self.make_bzrdir('source')
 
221
        target_transport = a_dir.root_transport.clone('..').clone('target')
 
222
        target = a_dir.clone_on_transport(target_transport)
 
223
        self.assertNotEqual(a_dir.transport.base, target.transport.base)
 
224
        self.assertDirectoriesEqual(a_dir.root_transport, target.root_transport,
 
225
                                    ['./.bzr/merge-hashes'])
 
226
 
 
227
    def test_clone_bzrdir_empty(self):
 
228
        dir = self.make_bzrdir('source')
 
229
        target = dir.clone(self.get_url('target'))
 
230
        self.assertNotEqual(dir.transport.base, target.transport.base)
 
231
        self.assertDirectoriesEqual(dir.root_transport, target.root_transport,
 
232
                                    ['./.bzr/merge-hashes'])
 
233
 
 
234
    def test_clone_bzrdir_empty_force_new_ignored(self):
 
235
        # the force_new_repo parameter should have no effect on an empty
 
236
        # bzrdir's clone logic
 
237
        dir = self.make_bzrdir('source')
 
238
        target = dir.clone(self.get_url('target'), force_new_repo=True)
 
239
        self.assertNotEqual(dir.transport.base, target.transport.base)
 
240
        self.assertDirectoriesEqual(dir.root_transport, target.root_transport,
 
241
                                    ['./.bzr/merge-hashes'])
 
242
 
 
243
    def test_clone_bzrdir_repository(self):
 
244
        tree = self.make_branch_and_tree('commit_tree')
 
245
        self.build_tree(['foo'], transport=tree.bzrdir.transport.clone('..'))
 
246
        tree.add('foo')
 
247
        tree.commit('revision 1', rev_id='1')
 
248
        dir = self.make_bzrdir('source')
 
249
        repo = dir.create_repository()
 
250
        repo.fetch(tree.branch.repository)
 
251
        self.assertTrue(repo.has_revision('1'))
 
252
        target = dir.clone(self.get_url('target'))
 
253
        self.assertNotEqual(dir.transport.base, target.transport.base)
 
254
        self.assertDirectoriesEqual(dir.root_transport, target.root_transport,
 
255
                                    [
 
256
                                     './.bzr/merge-hashes',
 
257
                                     './.bzr/repository',
 
258
                                     ])
 
259
        self.assertRepositoryHasSameItems(tree.branch.repository,
 
260
            target.open_repository())
 
261
 
 
262
    def test_clone_bzrdir_tree_branch_repo(self):
 
263
        tree = self.make_branch_and_tree('source')
 
264
        self.build_tree(['source/foo'])
 
265
        tree.add('foo')
 
266
        tree.commit('revision 1')
 
267
        dir = tree.bzrdir
 
268
        target = dir.clone(self.get_url('target'))
 
269
        self.skipIfNoWorkingTree(target)
 
270
        self.assertNotEqual(dir.transport.base, target.transport.base)
 
271
        self.assertDirectoriesEqual(dir.root_transport, target.root_transport,
 
272
                                    ['./.bzr/stat-cache',
 
273
                                     './.bzr/checkout/dirstate',
 
274
                                     './.bzr/checkout/stat-cache',
 
275
                                     './.bzr/checkout/merge-hashes',
 
276
                                     './.bzr/merge-hashes',
 
277
                                     './.bzr/repository',
 
278
                                     ])
 
279
        self.assertRepositoryHasSameItems(tree.branch.repository,
 
280
            target.open_repository())
 
281
        target.open_workingtree().revert()
 
282
 
 
283
    def test_revert_inventory(self):
 
284
        tree = self.make_branch_and_tree('source')
 
285
        self.build_tree(['source/foo'])
 
286
        tree.add('foo')
 
287
        tree.commit('revision 1')
 
288
        dir = tree.bzrdir
 
289
        target = dir.clone(self.get_url('target'))
 
290
        self.skipIfNoWorkingTree(target)
 
291
        self.assertDirectoriesEqual(dir.root_transport, target.root_transport,
 
292
                                    ['./.bzr/stat-cache',
 
293
                                     './.bzr/checkout/dirstate',
 
294
                                     './.bzr/checkout/stat-cache',
 
295
                                     './.bzr/checkout/merge-hashes',
 
296
                                     './.bzr/merge-hashes',
 
297
                                     './.bzr/repository',
 
298
                                     ])
 
299
        self.assertRepositoryHasSameItems(tree.branch.repository,
 
300
            target.open_repository())
 
301
 
 
302
        target.open_workingtree().revert()
 
303
        self.assertDirectoriesEqual(dir.root_transport, target.root_transport,
 
304
                                    ['./.bzr/stat-cache',
 
305
                                     './.bzr/checkout/dirstate',
 
306
                                     './.bzr/checkout/stat-cache',
 
307
                                     './.bzr/checkout/merge-hashes',
 
308
                                     './.bzr/merge-hashes',
 
309
                                     './.bzr/repository',
 
310
                                     ])
 
311
        self.assertRepositoryHasSameItems(tree.branch.repository,
 
312
            target.open_repository())
 
313
 
 
314
    def test_clone_bzrdir_tree_branch_reference(self):
 
315
        # a tree with a branch reference (aka a checkout)
 
316
        # should stay a checkout on clone.
 
317
        referenced_branch = self.make_branch('referencced')
 
318
        dir = self.make_bzrdir('source')
 
319
        try:
 
320
            reference = bzrlib.branch.BranchReferenceFormat().initialize(dir,
 
321
                target_branch=referenced_branch)
 
322
        except errors.IncompatibleFormat:
 
323
            # this is ok too, not all formats have to support references.
 
324
            return
 
325
        self.createWorkingTreeOrSkip(dir)
 
326
        target = dir.clone(self.get_url('target'))
 
327
        self.skipIfNoWorkingTree(target)
 
328
        self.assertNotEqual(dir.transport.base, target.transport.base)
 
329
        self.assertDirectoriesEqual(dir.root_transport, target.root_transport,
 
330
                                    ['./.bzr/stat-cache',
 
331
                                     './.bzr/checkout/stat-cache',
 
332
                                     './.bzr/checkout/merge-hashes',
 
333
                                     './.bzr/merge-hashes',
 
334
                                     './.bzr/repository/inventory.knit',
 
335
                                     ])
 
336
 
 
337
    def test_clone_bzrdir_branch_and_repo_into_shared_repo_force_new_repo(self):
 
338
        # by default cloning into a shared repo uses the shared repo.
 
339
        tree = self.make_branch_and_tree('commit_tree')
 
340
        self.build_tree(['commit_tree/foo'])
 
341
        tree.add('foo')
 
342
        tree.commit('revision 1')
 
343
        source = self.make_branch('source')
 
344
        tree.branch.repository.copy_content_into(source.repository)
 
345
        tree.branch.copy_content_into(source)
 
346
        try:
 
347
            self.make_repository('target', shared=True)
 
348
        except errors.IncompatibleFormat:
 
349
            return
 
350
        dir = source.bzrdir
 
351
        target = dir.clone(self.get_url('target/child'), force_new_repo=True)
 
352
        self.assertNotEqual(dir.transport.base, target.transport.base)
 
353
        repo = target.open_repository()
 
354
        self.assertDirectoriesEqual(dir.root_transport, target.root_transport,
 
355
                                    ['./.bzr/repository',
 
356
                                     ])
 
357
        self.assertRepositoryHasSameItems(tree.branch.repository, repo)
 
358
 
 
359
    def test_clone_bzrdir_branch_reference(self):
 
360
        # cloning should preserve the reference status of the branch in a bzrdir
 
361
        referenced_branch = self.make_branch('referencced')
 
362
        dir = self.make_bzrdir('source')
 
363
        try:
 
364
            reference = bzrlib.branch.BranchReferenceFormat().initialize(dir,
 
365
                target_branch=referenced_branch)
 
366
        except errors.IncompatibleFormat:
 
367
            # this is ok too, not all formats have to support references.
 
368
            return
 
369
        target = dir.clone(self.get_url('target'))
 
370
        self.assertNotEqual(dir.transport.base, target.transport.base)
 
371
        self.assertDirectoriesEqual(dir.root_transport, target.root_transport)
 
372
 
 
373
    def test_sprout_bzrdir_repository(self):
 
374
        tree = self.make_branch_and_tree('commit_tree')
 
375
        self.build_tree(['foo'], transport=tree.bzrdir.transport.clone('..'))
 
376
        tree.add('foo')
 
377
        tree.commit('revision 1', rev_id='1')
 
378
        dir = self.make_bzrdir('source')
 
379
        repo = dir.create_repository()
 
380
        repo.fetch(tree.branch.repository)
 
381
        self.assertTrue(repo.has_revision('1'))
 
382
        try:
 
383
            self.assertTrue(
 
384
                _mod_revision.is_null(_mod_revision.ensure_null(
 
385
                dir.open_branch().last_revision())))
 
386
        except errors.NotBranchError:
 
387
            pass
 
388
        target = dir.sprout(self.get_url('target'))
 
389
        self.assertNotEqual(dir.transport.base, target.transport.base)
 
390
        # testing inventory isn't reasonable for repositories
 
391
        self.assertDirectoriesEqual(dir.root_transport, target.root_transport,
 
392
                                    [
 
393
                                     './.bzr/branch',
 
394
                                     './.bzr/checkout',
 
395
                                     './.bzr/inventory',
 
396
                                     './.bzr/parent',
 
397
                                     './.bzr/repository/inventory.knit',
 
398
                                     ])
 
399
        try:
 
400
            local_inventory = dir.transport.local_abspath('inventory')
 
401
        except errors.NotLocalUrl:
 
402
            return
 
403
        try:
 
404
            # If we happen to have a tree, we'll guarantee everything
 
405
            # except for the tree root is the same.
 
406
            inventory_f = file(local_inventory, 'rb')
 
407
            self.addCleanup(inventory_f.close)
 
408
            self.assertContainsRe(inventory_f.read(),
 
409
                                  '<inventory format="5">\n</inventory>\n')
 
410
        except IOError, e:
 
411
            if e.errno != errno.ENOENT:
 
412
                raise
 
413
 
 
414
    def test_sprout_bzrdir_branch_and_repo(self):
 
415
        tree = self.make_branch_and_tree('commit_tree')
 
416
        self.build_tree(['commit_tree/foo'])
 
417
        tree.add('foo')
 
418
        tree.commit('revision 1')
 
419
        source = self.make_branch('source')
 
420
        tree.branch.repository.copy_content_into(source.repository)
 
421
        tree.bzrdir.open_branch().copy_content_into(source)
 
422
        dir = source.bzrdir
 
423
        target = dir.sprout(self.get_url('target'))
 
424
        self.assertNotEqual(dir.transport.base, target.transport.base)
 
425
        target_repo = target.open_repository()
 
426
        self.assertRepositoryHasSameItems(source.repository, target_repo)
 
427
        self.assertDirectoriesEqual(dir.root_transport, target.root_transport,
 
428
                                    [
 
429
                                     './.bzr/basis-inventory-cache',
 
430
                                     './.bzr/branch/branch.conf',
 
431
                                     './.bzr/branch/parent',
 
432
                                     './.bzr/checkout',
 
433
                                     './.bzr/checkout/inventory',
 
434
                                     './.bzr/checkout/stat-cache',
 
435
                                     './.bzr/inventory',
 
436
                                     './.bzr/parent',
 
437
                                     './.bzr/repository',
 
438
                                     './.bzr/stat-cache',
 
439
                                     './foo',
 
440
                                     ])
 
441
 
 
442
    def test_sprout_bzrdir_tree_branch_repo(self):
 
443
        tree = self.make_branch_and_tree('source')
 
444
        self.build_tree(['foo'], transport=tree.bzrdir.transport.clone('..'))
 
445
        tree.add('foo')
 
446
        tree.commit('revision 1')
 
447
        dir = tree.bzrdir
 
448
        target = self.sproutOrSkip(dir, self.get_url('target'))
 
449
        self.assertNotEqual(dir.transport.base, target.transport.base)
 
450
        self.assertDirectoriesEqual(dir.root_transport, target.root_transport,
 
451
                                    [
 
452
                                     './.bzr/branch/branch.conf',
 
453
                                     './.bzr/branch/parent',
 
454
                                     './.bzr/checkout/dirstate',
 
455
                                     './.bzr/checkout/stat-cache',
 
456
                                     './.bzr/checkout/inventory',
 
457
                                     './.bzr/inventory',
 
458
                                     './.bzr/parent',
 
459
                                     './.bzr/repository',
 
460
                                     './.bzr/stat-cache',
 
461
                                     ])
 
462
        self.assertRepositoryHasSameItems(
 
463
            tree.branch.repository, target.open_repository())
 
464
 
 
465
 
 
466
    def test_retire_bzrdir(self):
 
467
        bd = self.make_bzrdir('.')
 
468
        transport = bd.root_transport
 
469
        # must not overwrite existing directories
 
470
        self.build_tree(['.bzr.retired.0/', '.bzr.retired.0/junk',],
 
471
            transport=transport)
 
472
        self.failUnless(transport.has('.bzr'))
 
473
        bd.retire_bzrdir()
 
474
        self.failIf(transport.has('.bzr'))
 
475
        self.failUnless(transport.has('.bzr.retired.1'))
 
476
 
 
477
    def test_retire_bzrdir_limited(self):
 
478
        bd = self.make_bzrdir('.')
 
479
        transport = bd.root_transport
 
480
        # must not overwrite existing directories
 
481
        self.build_tree(['.bzr.retired.0/', '.bzr.retired.0/junk',],
 
482
            transport=transport)
 
483
        self.failUnless(transport.has('.bzr'))
 
484
        self.assertRaises((errors.FileExists, errors.DirectoryNotEmpty),
 
485
            bd.retire_bzrdir, limit=0)