~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_bundle.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-05-06 11:40:10 UTC
  • mfrom: (3400.1.3 trivial)
  • Revision ID: pqm@pqm.ubuntu.com-20080506114010-jwclr2qtiekvawjg
Remove erroneous creation of branch-name file in cmd_branch

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2004, 2005, 2006, 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
from cStringIO import StringIO
18
18
import os
19
 
import SocketServer
20
19
import sys
 
20
import tempfile
21
21
 
22
22
from bzrlib import (
23
23
    bzrdir,
24
 
    diff,
25
24
    errors,
26
25
    inventory,
27
 
    merge,
28
 
    osutils,
 
26
    repository,
29
27
    revision as _mod_revision,
30
 
    tests,
31
28
    treebuilder,
32
29
    )
 
30
from bzrlib.bzrdir import BzrDir
33
31
from bzrlib.bundle import read_mergeable_from_url
34
32
from bzrlib.bundle.apply_bundle import install_bundle, merge_bundle
35
33
from bzrlib.bundle.bundle_data import BundleTree
38
36
from bzrlib.bundle.serializer.v08 import BundleSerializerV08
39
37
from bzrlib.bundle.serializer.v09 import BundleSerializerV09
40
38
from bzrlib.bundle.serializer.v4 import BundleSerializerV4
 
39
from bzrlib.branch import Branch
 
40
from bzrlib.diff import internal_diff
 
41
from bzrlib.errors import (BzrError, TestamentMismatch, NotABundle, BadBundle, 
 
42
                           NoSuchFile,)
 
43
from bzrlib.merge import Merge3Merger
41
44
from bzrlib.repofmt import knitrepo
 
45
from bzrlib.osutils import sha_file, sha_string
42
46
from bzrlib.tests import (
43
 
    features,
 
47
    SymlinkFeature,
 
48
    TestCase,
 
49
    TestCaseInTempDir,
 
50
    TestCaseWithTransport,
 
51
    TestSkipped,
 
52
    test_read_bundle,
44
53
    test_commit,
45
 
    test_read_bundle,
46
 
    test_server,
47
54
    )
48
55
from bzrlib.transform import TreeTransform
49
56
 
50
57
 
51
 
def get_text(vf, key):
52
 
    """Get the fulltext for a given revision id that is present in the vf"""
53
 
    stream = vf.get_record_stream([key], 'unordered', True)
54
 
    record = stream.next()
55
 
    return record.get_bytes_as('fulltext')
56
 
 
57
 
 
58
 
def get_inventory_text(repo, revision_id):
59
 
    """Get the fulltext for the inventory at revision id"""
60
 
    repo.lock_read()
61
 
    try:
62
 
        return get_text(repo.inventories, (revision_id,))
63
 
    finally:
64
 
        repo.unlock()
65
 
 
66
 
 
67
58
class MockTree(object):
68
 
 
69
59
    def __init__(self):
70
60
        from bzrlib.inventory import InventoryDirectory, ROOT_ID
71
61
        object.__init__(self)
75
65
        self.root = InventoryDirectory(ROOT_ID, '', None)
76
66
 
77
67
    inventory = property(lambda x:x)
78
 
    root_inventory = property(lambda x:x)
79
 
 
80
 
    def get_root_id(self):
81
 
        return self.root.file_id
82
 
 
83
 
    def all_file_ids(self):
84
 
        return set(self.paths.keys())
85
 
 
86
 
    def is_executable(self, file_id):
87
 
        # Not all the files are executable.
88
 
        return False
 
68
 
 
69
    def __iter__(self):
 
70
        return self.paths.iterkeys()
89
71
 
90
72
    def __getitem__(self, file_id):
91
73
        if file_id == self.root.file_id:
103
85
        for path, file_id in self.ids.iteritems():
104
86
            yield path, self[file_id]
105
87
 
106
 
    def kind(self, file_id):
 
88
    def get_file_kind(self, file_id):
107
89
        if file_id in self.contents:
108
90
            kind = 'file'
109
91
        else:
111
93
        return kind
112
94
 
113
95
    def make_entry(self, file_id, path):
114
 
        from bzrlib.inventory import (InventoryFile , InventoryDirectory,
115
 
            InventoryLink)
 
96
        from bzrlib.inventory import (InventoryEntry, InventoryFile
 
97
                                    , InventoryDirectory, InventoryLink)
116
98
        name = os.path.basename(path)
117
 
        kind = self.kind(file_id)
 
99
        kind = self.get_file_kind(file_id)
118
100
        parent_id = self.parent_id(file_id)
119
101
        text_sha_1, text_size = self.contents_stats(file_id)
120
102
        if kind == 'directory':
121
103
            ie = InventoryDirectory(file_id, name, parent_id)
122
104
        elif kind == 'file':
123
105
            ie = InventoryFile(file_id, name, parent_id)
124
 
            ie.text_sha1 = text_sha_1
125
 
            ie.text_size = text_size
126
106
        elif kind == 'symlink':
127
107
            ie = InventoryLink(file_id, name, parent_id)
128
108
        else:
129
 
            raise errors.BzrError('unknown kind %r' % kind)
 
109
            raise BzrError('unknown kind %r' % kind)
 
110
        ie.text_sha1 = text_sha_1
 
111
        ie.text_size = text_size
130
112
        return ie
131
113
 
132
114
    def add_dir(self, file_id, path):
133
115
        self.paths[file_id] = path
134
116
        self.ids[path] = file_id
135
 
 
 
117
    
136
118
    def add_file(self, file_id, path, contents):
137
119
        self.add_dir(file_id, path)
138
120
        self.contents[file_id] = contents
152
134
        result.seek(0,0)
153
135
        return result
154
136
 
155
 
    def get_file_revision(self, file_id):
156
 
        return self.inventory[file_id].revision
157
 
 
158
 
    def get_file_size(self, file_id):
159
 
        return self.inventory[file_id].text_size
160
 
 
161
 
    def get_file_sha1(self, file_id):
162
 
        return self.inventory[file_id].text_sha1
163
 
 
164
137
    def contents_stats(self, file_id):
165
138
        if file_id not in self.contents:
166
139
            return None, None
167
 
        text_sha1 = osutils.sha_file(self.get_file(file_id))
 
140
        text_sha1 = sha_file(self.get_file(file_id))
168
141
        return text_sha1, len(self.contents[file_id])
169
142
 
170
143
 
171
 
class BTreeTester(tests.TestCase):
 
144
class BTreeTester(TestCase):
172
145
    """A simple unittest tester for the BundleTree class."""
173
146
 
174
147
    def make_tree_1(self):
178
151
        mtree.add_file("c", "grandparent/parent/file", "Hello\n")
179
152
        mtree.add_dir("d", "grandparent/alt_parent")
180
153
        return BundleTree(mtree, ''), mtree
181
 
 
 
154
        
182
155
    def test_renames(self):
183
156
        """Ensure that file renames have the proper effect on children"""
184
157
        btree = self.make_tree_1()[0]
185
158
        self.assertEqual(btree.old_path("grandparent"), "grandparent")
186
 
        self.assertEqual(btree.old_path("grandparent/parent"),
 
159
        self.assertEqual(btree.old_path("grandparent/parent"), 
187
160
                         "grandparent/parent")
188
161
        self.assertEqual(btree.old_path("grandparent/parent/file"),
189
162
                         "grandparent/parent/file")
196
169
        self.assertEqual(btree.path2id("grandparent/parent"), "b")
197
170
        self.assertEqual(btree.path2id("grandparent/parent/file"), "c")
198
171
 
199
 
        self.assertTrue(btree.path2id("grandparent2") is None)
200
 
        self.assertTrue(btree.path2id("grandparent2/parent") is None)
201
 
        self.assertTrue(btree.path2id("grandparent2/parent/file") is None)
 
172
        assert btree.path2id("grandparent2") is None
 
173
        assert btree.path2id("grandparent2/parent") is None
 
174
        assert btree.path2id("grandparent2/parent/file") is None
202
175
 
203
176
        btree.note_rename("grandparent", "grandparent2")
204
 
        self.assertTrue(btree.old_path("grandparent") is None)
205
 
        self.assertTrue(btree.old_path("grandparent/parent") is None)
206
 
        self.assertTrue(btree.old_path("grandparent/parent/file") is None)
 
177
        assert btree.old_path("grandparent") is None
 
178
        assert btree.old_path("grandparent/parent") is None
 
179
        assert btree.old_path("grandparent/parent/file") is None
207
180
 
208
181
        self.assertEqual(btree.id2path("a"), "grandparent2")
209
182
        self.assertEqual(btree.id2path("b"), "grandparent2/parent")
213
186
        self.assertEqual(btree.path2id("grandparent2/parent"), "b")
214
187
        self.assertEqual(btree.path2id("grandparent2/parent/file"), "c")
215
188
 
216
 
        self.assertTrue(btree.path2id("grandparent") is None)
217
 
        self.assertTrue(btree.path2id("grandparent/parent") is None)
218
 
        self.assertTrue(btree.path2id("grandparent/parent/file") is None)
 
189
        assert btree.path2id("grandparent") is None
 
190
        assert btree.path2id("grandparent/parent") is None
 
191
        assert btree.path2id("grandparent/parent/file") is None
219
192
 
220
193
        btree.note_rename("grandparent/parent", "grandparent2/parent2")
221
194
        self.assertEqual(btree.id2path("a"), "grandparent2")
226
199
        self.assertEqual(btree.path2id("grandparent2/parent2"), "b")
227
200
        self.assertEqual(btree.path2id("grandparent2/parent2/file"), "c")
228
201
 
229
 
        self.assertTrue(btree.path2id("grandparent2/parent") is None)
230
 
        self.assertTrue(btree.path2id("grandparent2/parent/file") is None)
 
202
        assert btree.path2id("grandparent2/parent") is None
 
203
        assert btree.path2id("grandparent2/parent/file") is None
231
204
 
232
 
        btree.note_rename("grandparent/parent/file",
 
205
        btree.note_rename("grandparent/parent/file", 
233
206
                          "grandparent2/parent2/file2")
234
207
        self.assertEqual(btree.id2path("a"), "grandparent2")
235
208
        self.assertEqual(btree.id2path("b"), "grandparent2/parent2")
239
212
        self.assertEqual(btree.path2id("grandparent2/parent2"), "b")
240
213
        self.assertEqual(btree.path2id("grandparent2/parent2/file2"), "c")
241
214
 
242
 
        self.assertTrue(btree.path2id("grandparent2/parent2/file") is None)
 
215
        assert btree.path2id("grandparent2/parent2/file") is None
243
216
 
244
217
    def test_moves(self):
245
218
        """Ensure that file moves have the proper effect on children"""
246
219
        btree = self.make_tree_1()[0]
247
 
        btree.note_rename("grandparent/parent/file",
 
220
        btree.note_rename("grandparent/parent/file", 
248
221
                          "grandparent/alt_parent/file")
249
222
        self.assertEqual(btree.id2path("c"), "grandparent/alt_parent/file")
250
223
        self.assertEqual(btree.path2id("grandparent/alt_parent/file"), "c")
251
 
        self.assertTrue(btree.path2id("grandparent/parent/file") is None)
 
224
        assert btree.path2id("grandparent/parent/file") is None
252
225
 
253
226
    def unified_diff(self, old, new):
254
227
        out = StringIO()
255
 
        diff.internal_diff("old", old, "new", new, out)
 
228
        internal_diff("old", old, "new", new, out)
256
229
        out.seek(0,0)
257
230
        return out.read()
258
231
 
259
232
    def make_tree_2(self):
260
233
        btree = self.make_tree_1()[0]
261
 
        btree.note_rename("grandparent/parent/file",
 
234
        btree.note_rename("grandparent/parent/file", 
262
235
                          "grandparent/alt_parent/file")
263
 
        self.assertTrue(btree.id2path("e") is None)
264
 
        self.assertTrue(btree.path2id("grandparent/parent/file") is None)
 
236
        assert btree.id2path("e") is None
 
237
        assert btree.path2id("grandparent/parent/file") is None
265
238
        btree.note_id("e", "grandparent/parent/file")
266
239
        return btree
267
240
 
293
266
    def make_tree_3(self):
294
267
        btree, mtree = self.make_tree_1()
295
268
        mtree.add_file("e", "grandparent/parent/topping", "Anchovies\n")
296
 
        btree.note_rename("grandparent/parent/file",
 
269
        btree.note_rename("grandparent/parent/file", 
297
270
                          "grandparent/alt_parent/file")
298
 
        btree.note_rename("grandparent/parent/topping",
 
271
        btree.note_rename("grandparent/parent/topping", 
299
272
                          "grandparent/alt_parent/stopping")
300
273
        return btree
301
274
 
325
298
        btree = self.make_tree_1()[0]
326
299
        self.assertEqual(btree.get_file("c").read(), "Hello\n")
327
300
        btree.note_deletion("grandparent/parent/file")
328
 
        self.assertTrue(btree.id2path("c") is None)
329
 
        self.assertTrue(btree.path2id("grandparent/parent/file") is None)
 
301
        assert btree.id2path("c") is None
 
302
        assert btree.path2id("grandparent/parent/file") is None
330
303
 
331
304
    def sorted_ids(self, tree):
332
 
        ids = list(tree.all_file_ids())
 
305
        ids = list(tree)
333
306
        ids.sort()
334
307
        return ids
335
308
 
340
313
            [inventory.ROOT_ID, 'a', 'b', 'c', 'd'])
341
314
        btree.note_deletion("grandparent/parent/file")
342
315
        btree.note_id("e", "grandparent/alt_parent/fool", kind="directory")
343
 
        btree.note_last_changed("grandparent/alt_parent/fool",
 
316
        btree.note_last_changed("grandparent/alt_parent/fool", 
344
317
                                "revisionidiguess")
345
318
        self.assertEqual(self.sorted_ids(btree),
346
319
            [inventory.ROOT_ID, 'a', 'b', 'd', 'e'])
347
320
 
348
321
 
349
 
class BundleTester1(tests.TestCaseWithTransport):
 
322
class BundleTester1(TestCaseWithTransport):
350
323
 
351
324
    def test_mismatched_bundle(self):
352
325
        format = bzrdir.BzrDirMetaFormat1()
353
326
        format.repository_format = knitrepo.RepositoryFormatKnit3()
354
327
        serializer = BundleSerializerV08('0.8')
355
328
        b = self.make_branch('.', format=format)
356
 
        self.assertRaises(errors.IncompatibleBundleFormat, serializer.write,
 
329
        self.assertRaises(errors.IncompatibleBundleFormat, serializer.write, 
357
330
                          b.repository, [], {}, StringIO())
358
331
 
359
332
    def test_matched_bundle(self):
379
352
        format = bzrdir.BzrDirMetaFormat1()
380
353
        format.repository_format = knitrepo.RepositoryFormatKnit1()
381
354
        target = self.make_branch('target', format=format)
382
 
        self.assertRaises(errors.IncompatibleRevision, install_bundle,
 
355
        self.assertRaises(errors.IncompatibleRevision, install_bundle, 
383
356
                          target.repository, read_bundle(text))
384
357
 
385
358
 
393
366
    def make_branch_and_tree(self, path, format=None):
394
367
        if format is None:
395
368
            format = self.bzrdir_format()
396
 
        return tests.TestCaseWithTransport.make_branch_and_tree(
397
 
            self, path, format)
 
369
        return TestCaseWithTransport.make_branch_and_tree(self, path, format)
398
370
 
399
371
    def make_branch(self, path, format=None):
400
372
        if format is None:
401
373
            format = self.bzrdir_format()
402
 
        return tests.TestCaseWithTransport.make_branch(self, path, format)
 
374
        return TestCaseWithTransport.make_branch(self, path, format)
403
375
 
404
376
    def create_bundle_text(self, base_rev_id, rev_id):
405
377
        bundle_txt = StringIO()
406
 
        rev_ids = write_bundle(self.b1.repository, rev_id, base_rev_id,
 
378
        rev_ids = write_bundle(self.b1.repository, rev_id, base_rev_id, 
407
379
                               bundle_txt, format=self.format)
408
380
        bundle_txt.seek(0)
409
 
        self.assertEqual(bundle_txt.readline(),
 
381
        self.assertEqual(bundle_txt.readline(), 
410
382
                         '# Bazaar revision bundle v%s\n' % self.format)
411
383
        self.assertEqual(bundle_txt.readline(), '#\n')
412
384
 
420
392
        """Create a bundle from base_rev_id -> rev_id in built-in branch.
421
393
        Make sure that the text generated is valid, and that it
422
394
        can be applied against the base, and generate the same information.
423
 
 
424
 
        :return: The in-memory bundle
 
395
        
 
396
        :return: The in-memory bundle 
425
397
        """
426
398
        bundle_txt, rev_ids = self.create_bundle_text(base_rev_id, rev_id)
427
399
 
428
 
        # This should also validate the generated bundle
 
400
        # This should also validate the generated bundle 
429
401
        bundle = read_bundle(bundle_txt)
430
402
        repository = self.b1.repository
431
403
        for bundle_rev in bundle.real_revisions:
435
407
            # it
436
408
            branch_rev = repository.get_revision(bundle_rev.revision_id)
437
409
            for a in ('inventory_sha1', 'revision_id', 'parent_ids',
438
 
                      'timestamp', 'timezone', 'message', 'committer',
 
410
                      'timestamp', 'timezone', 'message', 'committer', 
439
411
                      'parent_ids', 'properties'):
440
 
                self.assertEqual(getattr(branch_rev, a),
 
412
                self.assertEqual(getattr(branch_rev, a), 
441
413
                                 getattr(bundle_rev, a))
442
 
            self.assertEqual(len(branch_rev.parent_ids),
 
414
            self.assertEqual(len(branch_rev.parent_ids), 
443
415
                             len(bundle_rev.parent_ids))
444
 
        self.assertEqual(rev_ids,
 
416
        self.assertEqual(rev_ids, 
445
417
                         [r.revision_id for r in bundle.real_revisions])
446
418
        self.valid_apply_bundle(base_rev_id, bundle,
447
419
                                   checkout_dir=checkout_dir)
451
423
    def get_invalid_bundle(self, base_rev_id, rev_id):
452
424
        """Create a bundle from base_rev_id -> rev_id in built-in branch.
453
425
        Munge the text so that it's invalid.
454
 
 
 
426
        
455
427
        :return: The in-memory bundle
456
428
        """
457
429
        bundle_txt, rev_ids = self.create_bundle_text(base_rev_id, rev_id)
458
 
        new_text = bundle_txt.getvalue().replace('executable:no',
 
430
        new_text = bundle_txt.getvalue().replace('executable:no', 
459
431
                                               'executable:yes')
460
432
        bundle_txt = StringIO(new_text)
461
433
        bundle = read_bundle(bundle_txt)
462
434
        self.valid_apply_bundle(base_rev_id, bundle)
463
 
        return bundle
 
435
        return bundle 
464
436
 
465
437
    def test_non_bundle(self):
466
 
        self.assertRaises(errors.NotABundle,
467
 
                          read_bundle, StringIO('#!/bin/sh\n'))
 
438
        self.assertRaises(NotABundle, read_bundle, StringIO('#!/bin/sh\n'))
468
439
 
469
440
    def test_malformed(self):
470
 
        self.assertRaises(errors.BadBundle, read_bundle,
 
441
        self.assertRaises(BadBundle, read_bundle, 
471
442
                          StringIO('# Bazaar revision bundle v'))
472
443
 
473
444
    def test_crlf_bundle(self):
474
445
        try:
475
446
            read_bundle(StringIO('# Bazaar revision bundle v0.8\r\n'))
476
 
        except errors.BadBundle:
 
447
        except BadBundle:
477
448
            # It is currently permitted for bundles with crlf line endings to
478
449
            # make read_bundle raise a BadBundle, but this should be fixed.
479
450
            # Anything else, especially NotABundle, is an error.
484
455
        """
485
456
 
486
457
        if checkout_dir is None:
487
 
            checkout_dir = osutils.mkdtemp(prefix='test-branch-', dir='.')
 
458
            checkout_dir = tempfile.mkdtemp(prefix='test-branch-', dir='.')
488
459
        else:
489
460
            if not os.path.exists(checkout_dir):
490
461
                os.mkdir(checkout_dir)
493
464
        ancestors = write_bundle(self.b1.repository, rev_id, 'null:', s,
494
465
                                 format=self.format)
495
466
        s.seek(0)
496
 
        self.assertIsInstance(s.getvalue(), str)
 
467
        assert isinstance(s.getvalue(), str), (
 
468
            "Bundle isn't a bytestring:\n %s..." % repr(s.getvalue())[:40])
497
469
        install_bundle(tree.branch.repository, read_bundle(s))
498
470
        for ancestor in ancestors:
499
471
            old = self.b1.repository.revision_tree(ancestor)
500
472
            new = tree.branch.repository.revision_tree(ancestor)
501
 
            old.lock_read()
502
 
            new.lock_read()
503
 
            try:
504
 
                # Check that there aren't any inventory level changes
505
 
                delta = new.changes_from(old)
506
 
                self.assertFalse(delta.has_changed(),
507
 
                                 'Revision %s not copied correctly.'
508
 
                                 % (ancestor,))
509
 
 
510
 
                # Now check that the file contents are all correct
511
 
                for inventory_id in old.all_file_ids():
512
 
                    try:
513
 
                        old_file = old.get_file(inventory_id)
514
 
                    except errors.NoSuchFile:
515
 
                        continue
516
 
                    if old_file is None:
517
 
                        continue
518
 
                    self.assertEqual(old_file.read(),
519
 
                                     new.get_file(inventory_id).read())
520
 
            finally:
521
 
                new.unlock()
522
 
                old.unlock()
 
473
 
 
474
            # Check that there aren't any inventory level changes
 
475
            delta = new.changes_from(old)
 
476
            self.assertFalse(delta.has_changed(),
 
477
                             'Revision %s not copied correctly.'
 
478
                             % (ancestor,))
 
479
 
 
480
            # Now check that the file contents are all correct
 
481
            for inventory_id in old:
 
482
                try:
 
483
                    old_file = old.get_file(inventory_id)
 
484
                except NoSuchFile:
 
485
                    continue
 
486
                if old_file is None:
 
487
                    continue
 
488
                self.assertEqual(old_file.read(),
 
489
                                 new.get_file(inventory_id).read())
523
490
        if not _mod_revision.is_null(rev_id):
524
 
            tree.branch.generate_revision_history(rev_id)
 
491
            rh = self.b1.revision_history()
 
492
            tree.branch.set_revision_history(rh[:rh.index(rev_id)+1])
525
493
            tree.update()
526
494
            delta = tree.changes_from(self.b1.repository.revision_tree(rev_id))
527
495
            self.assertFalse(delta.has_changed(),
546
514
        self.assertIs(repository.has_revision(base_rev_id), True)
547
515
        for rev in info.real_revisions:
548
516
            self.assert_(not repository.has_revision(rev.revision_id),
549
 
                'Revision {%s} present before applying bundle'
 
517
                'Revision {%s} present before applying bundle' 
550
518
                % rev.revision_id)
551
 
        merge_bundle(info, to_tree, True, merge.Merge3Merger, False, False)
 
519
        merge_bundle(info, to_tree, True, Merge3Merger, False, False)
552
520
 
553
521
        for rev in info.real_revisions:
554
522
            self.assert_(repository.has_revision(rev.revision_id),
555
 
                'Missing revision {%s} after applying bundle'
 
523
                'Missing revision {%s} after applying bundle' 
556
524
                % rev.revision_id)
557
525
 
558
526
        self.assert_(to_tree.branch.repository.has_revision(info.target))
564
532
        rev = info.real_revisions[-1]
565
533
        base_tree = self.b1.repository.revision_tree(rev.revision_id)
566
534
        to_tree = to_tree.branch.repository.revision_tree(rev.revision_id)
567
 
 
 
535
        
568
536
        # TODO: make sure the target tree is identical to base tree
569
537
        #       we might also check the working tree.
570
538
 
589
557
        self.tree1 = self.make_branch_and_tree('b1')
590
558
        self.b1 = self.tree1.branch
591
559
 
592
 
        self.build_tree_contents([('b1/one', 'one\n')])
593
 
        self.tree1.add('one', 'one-id')
594
 
        self.tree1.set_root_id('root-id')
 
560
        open('b1/one', 'wb').write('one\n')
 
561
        self.tree1.add('one')
595
562
        self.tree1.commit('add one', rev_id='a@cset-0-1')
596
563
 
597
564
        bundle = self.get_valid_bundle('null:', 'a@cset-0-1')
608
575
                , 'b1/sub/sub/'
609
576
                , 'b1/sub/sub/nonempty.txt'
610
577
                ])
611
 
        self.build_tree_contents([('b1/sub/sub/emptyfile.txt', ''),
612
 
                                  ('b1/dir/nolastnewline.txt', 'bloop')])
 
578
        open('b1/sub/sub/emptyfile.txt', 'wb').close()
 
579
        open('b1/dir/nolastnewline.txt', 'wb').write('bloop')
613
580
        tt = TreeTransform(self.tree1)
614
581
        tt.new_file('executable', tt.root, '#!/bin/sh\n', 'exe-1', True)
615
582
        tt.apply()
631
598
 
632
599
        bundle = self.get_valid_bundle('a@cset-0-1', 'a@cset-0-2')
633
600
 
634
 
        # Check a rollup bundle
 
601
        # Check a rollup bundle 
635
602
        bundle = self.get_valid_bundle('null:', 'a@cset-0-2')
636
603
 
637
604
        # Now delete entries
645
612
        tt.set_executability(False, trans_id)
646
613
        tt.apply()
647
614
        self.tree1.commit('removed', rev_id='a@cset-0-3')
648
 
 
 
615
        
649
616
        bundle = self.get_valid_bundle('a@cset-0-2', 'a@cset-0-3')
650
 
        self.assertRaises((errors.TestamentMismatch,
651
 
            errors.VersionedFileInvalidChecksum,
652
 
            errors.BadBundle), self.get_invalid_bundle,
 
617
        self.assertRaises((TestamentMismatch,
 
618
            errors.VersionedFileInvalidChecksum), self.get_invalid_bundle,
653
619
            'a@cset-0-2', 'a@cset-0-3')
654
 
        # Check a rollup bundle
 
620
        # Check a rollup bundle 
655
621
        bundle = self.get_valid_bundle('null:', 'a@cset-0-3')
656
622
 
657
623
        # Now move the directory
659
625
        self.tree1.commit('rename dir', rev_id='a@cset-0-4')
660
626
 
661
627
        bundle = self.get_valid_bundle('a@cset-0-3', 'a@cset-0-4')
662
 
        # Check a rollup bundle
 
628
        # Check a rollup bundle 
663
629
        bundle = self.get_valid_bundle('null:', 'a@cset-0-4')
664
630
 
665
631
        # Modified files
666
 
        with open('b1/sub/dir/WithCaps.txt', 'ab') as f: f.write('\nAdding some text\n')
667
 
        with open('b1/sub/dir/ pre space', 'ab') as f: f.write(
 
632
        open('b1/sub/dir/WithCaps.txt', 'ab').write('\nAdding some text\n')
 
633
        open('b1/sub/dir/ pre space', 'ab').write(
668
634
             '\r\nAdding some\r\nDOS format lines\r\n')
669
 
        with open('b1/sub/dir/nolastnewline.txt', 'ab') as f: f.write('\n')
670
 
        self.tree1.rename_one('sub/dir/ pre space',
 
635
        open('b1/sub/dir/nolastnewline.txt', 'ab').write('\n')
 
636
        self.tree1.rename_one('sub/dir/ pre space', 
671
637
                              'sub/ start space')
672
638
        self.tree1.commit('Modified files', rev_id='a@cset-0-5')
673
639
        bundle = self.get_valid_bundle('a@cset-0-4', 'a@cset-0-5')
679
645
                          verbose=False)
680
646
        bundle = self.get_valid_bundle('a@cset-0-5', 'a@cset-0-6')
681
647
        other = self.get_checkout('a@cset-0-5')
682
 
        tree1_inv = get_inventory_text(self.tree1.branch.repository,
683
 
                                       'a@cset-0-5')
684
 
        tree2_inv = get_inventory_text(other.branch.repository,
685
 
                                       'a@cset-0-5')
 
648
        tree1_inv = self.tree1.branch.repository.get_inventory_xml(
 
649
            'a@cset-0-5')
 
650
        tree2_inv = other.branch.repository.get_inventory_xml('a@cset-0-5')
686
651
        self.assertEqualDiff(tree1_inv, tree2_inv)
687
652
        other.rename_one('sub/dir/nolastnewline.txt', 'sub/nolastnewline.txt')
688
653
        other.commit('rename file', rev_id='a@cset-0-6b')
691
656
                          verbose=False)
692
657
        bundle = self.get_valid_bundle('a@cset-0-6', 'a@cset-0-7')
693
658
 
694
 
    def _test_symlink_bundle(self, link_name, link_target, new_link_target):
695
 
        link_id = 'link-1'
696
 
 
697
 
        self.requireFeature(features.SymlinkFeature)
 
659
    def test_symlink_bundle(self):
 
660
        self.requireFeature(SymlinkFeature)
698
661
        self.tree1 = self.make_branch_and_tree('b1')
699
662
        self.b1 = self.tree1.branch
700
 
 
701
663
        tt = TreeTransform(self.tree1)
702
 
        tt.new_symlink(link_name, tt.root, link_target, link_id)
 
664
        tt.new_symlink('link', tt.root, 'bar/foo', 'link-1')
703
665
        tt.apply()
704
666
        self.tree1.commit('add symlink', rev_id='l@cset-0-1')
705
 
        bundle = self.get_valid_bundle('null:', 'l@cset-0-1')
706
 
        if getattr(bundle ,'revision_tree', None) is not None:
707
 
            # Not all bundle formats supports revision_tree
708
 
            bund_tree = bundle.revision_tree(self.b1.repository, 'l@cset-0-1')
709
 
            self.assertEqual(link_target, bund_tree.get_symlink_target(link_id))
710
 
 
 
667
        self.get_valid_bundle('null:', 'l@cset-0-1')
711
668
        tt = TreeTransform(self.tree1)
712
 
        trans_id = tt.trans_id_tree_file_id(link_id)
 
669
        trans_id = tt.trans_id_tree_file_id('link-1')
713
670
        tt.adjust_path('link2', tt.root, trans_id)
714
671
        tt.delete_contents(trans_id)
715
 
        tt.create_symlink(new_link_target, trans_id)
 
672
        tt.create_symlink('mars', trans_id)
716
673
        tt.apply()
717
674
        self.tree1.commit('rename and change symlink', rev_id='l@cset-0-2')
718
 
        bundle = self.get_valid_bundle('l@cset-0-1', 'l@cset-0-2')
719
 
        if getattr(bundle ,'revision_tree', None) is not None:
720
 
            # Not all bundle formats supports revision_tree
721
 
            bund_tree = bundle.revision_tree(self.b1.repository, 'l@cset-0-2')
722
 
            self.assertEqual(new_link_target,
723
 
                             bund_tree.get_symlink_target(link_id))
724
 
 
 
675
        self.get_valid_bundle('l@cset-0-1', 'l@cset-0-2')
725
676
        tt = TreeTransform(self.tree1)
726
 
        trans_id = tt.trans_id_tree_file_id(link_id)
 
677
        trans_id = tt.trans_id_tree_file_id('link-1')
727
678
        tt.delete_contents(trans_id)
728
679
        tt.create_symlink('jupiter', trans_id)
729
680
        tt.apply()
730
681
        self.tree1.commit('just change symlink target', rev_id='l@cset-0-3')
731
 
        bundle = self.get_valid_bundle('l@cset-0-2', 'l@cset-0-3')
732
 
 
 
682
        self.get_valid_bundle('l@cset-0-2', 'l@cset-0-3')
733
683
        tt = TreeTransform(self.tree1)
734
 
        trans_id = tt.trans_id_tree_file_id(link_id)
 
684
        trans_id = tt.trans_id_tree_file_id('link-1')
735
685
        tt.delete_contents(trans_id)
736
686
        tt.apply()
737
687
        self.tree1.commit('Delete symlink', rev_id='l@cset-0-4')
738
 
        bundle = self.get_valid_bundle('l@cset-0-3', 'l@cset-0-4')
739
 
 
740
 
    def test_symlink_bundle(self):
741
 
        self._test_symlink_bundle('link', 'bar/foo', 'mars')
742
 
 
743
 
    def test_unicode_symlink_bundle(self):
744
 
        self.requireFeature(features.UnicodeFilenameFeature)
745
 
        self._test_symlink_bundle(u'\N{Euro Sign}link',
746
 
                                  u'bar/\N{Euro Sign}foo',
747
 
                                  u'mars\N{Euro Sign}')
 
688
        self.get_valid_bundle('l@cset-0-3', 'l@cset-0-4')
748
689
 
749
690
    def test_binary_bundle(self):
750
691
        self.tree1 = self.make_branch_and_tree('b1')
751
692
        self.b1 = self.tree1.branch
752
693
        tt = TreeTransform(self.tree1)
753
 
 
 
694
        
754
695
        # Add
755
696
        tt.new_file('file', tt.root, '\x00\n\x00\r\x01\n\x02\r\xff', 'binary-1')
756
697
        tt.new_file('file2', tt.root, '\x01\n\x02\r\x03\n\x04\r\xff',
821
762
        self.tree1 = self.make_branch_and_tree('b1')
822
763
        self.b1 = self.tree1.branch
823
764
 
824
 
        with open('b1/one', 'wb') as f: f.write('one\n')
 
765
        open('b1/one', 'wb').write('one\n')
825
766
        self.tree1.add('one')
826
767
        self.tree1.commit('add file', rev_id='a@cset-0-1')
827
 
        with open('b1/one', 'wb') as f: f.write('two\n')
 
768
        open('b1/one', 'wb').write('two\n')
828
769
        self.tree1.commit('modify', rev_id='a@cset-0-2')
829
 
        with open('b1/one', 'wb') as f: f.write('three\n')
 
770
        open('b1/one', 'wb').write('three\n')
830
771
        self.tree1.commit('modify', rev_id='a@cset-0-3')
831
772
        bundle_file = StringIO()
832
773
        rev_ids = write_bundle(self.tree1.branch.repository, 'a@cset-0-3',
848
789
        return bundle_file.getvalue()
849
790
 
850
791
    def test_unicode_bundle(self):
851
 
        self.requireFeature(features.UnicodeFilenameFeature)
852
792
        # Handle international characters
853
793
        os.mkdir('b1')
854
 
        f = open(u'b1/with Dod\N{Euro Sign}', 'wb')
 
794
        try:
 
795
            f = open(u'b1/with Dod\xe9', 'wb')
 
796
        except UnicodeEncodeError:
 
797
            raise TestSkipped("Filesystem doesn't support unicode")
855
798
 
856
799
        self.tree1 = self.make_branch_and_tree('b1')
857
800
        self.b1 = self.tree1.branch
861
804
            u'William Dod\xe9\n').encode('utf-8'))
862
805
        f.close()
863
806
 
864
 
        self.tree1.add([u'with Dod\N{Euro Sign}'], ['withdod-id'])
 
807
        self.tree1.add([u'with Dod\xe9'], ['withdod-id'])
865
808
        self.tree1.commit(u'i18n commit from William Dod\xe9',
866
809
                          rev_id='i18n-1', committer=u'William Dod\xe9')
867
810
 
 
811
        if sys.platform == 'darwin':
 
812
            from bzrlib.workingtree import WorkingTree3
 
813
            if type(self.tree1) is WorkingTree3:
 
814
                self.knownFailure("Bug #141438: fails for WorkingTree3 on OSX")
 
815
 
 
816
            # On Mac the '\xe9' gets changed to 'e\u0301'
 
817
            self.assertEqual([u'.bzr', u'with Dode\u0301'],
 
818
                             sorted(os.listdir(u'b1')))
 
819
            delta = self.tree1.changes_from(self.tree1.basis_tree())
 
820
            self.assertEqual([(u'with Dod\xe9', 'withdod-id', 'file')],
 
821
                             delta.removed)
 
822
            self.knownFailure("Mac OSX doesn't preserve unicode"
 
823
                              " combining characters.")
 
824
 
868
825
        # Add
869
826
        bundle = self.get_valid_bundle('null:', 'i18n-1')
870
827
 
871
828
        # Modified
872
 
        f = open(u'b1/with Dod\N{Euro Sign}', 'wb')
 
829
        f = open(u'b1/with Dod\xe9', 'wb')
873
830
        f.write(u'Modified \xb5\n'.encode('utf8'))
874
831
        f.close()
875
832
        self.tree1.commit(u'modified', rev_id='i18n-2')
876
833
 
877
834
        bundle = self.get_valid_bundle('i18n-1', 'i18n-2')
878
 
 
 
835
        
879
836
        # Renamed
880
 
        self.tree1.rename_one(u'with Dod\N{Euro Sign}', u'B\N{Euro Sign}gfors')
 
837
        self.tree1.rename_one(u'with Dod\xe9', u'B\xe5gfors')
881
838
        self.tree1.commit(u'renamed, the new i18n man', rev_id='i18n-3',
882
839
                          committer=u'Erik B\xe5gfors')
883
840
 
884
841
        bundle = self.get_valid_bundle('i18n-2', 'i18n-3')
885
842
 
886
843
        # Removed
887
 
        self.tree1.remove([u'B\N{Euro Sign}gfors'])
 
844
        self.tree1.remove([u'B\xe5gfors'])
888
845
        self.tree1.commit(u'removed', rev_id='i18n-4')
889
846
 
890
847
        bundle = self.get_valid_bundle('i18n-3', 'i18n-4')
895
852
 
896
853
    def test_whitespace_bundle(self):
897
854
        if sys.platform in ('win32', 'cygwin'):
898
 
            raise tests.TestSkipped('Windows doesn\'t support filenames'
899
 
                                    ' with tabs or trailing spaces')
 
855
            raise TestSkipped('Windows doesn\'t support filenames'
 
856
                              ' with tabs or trailing spaces')
900
857
        self.tree1 = self.make_branch_and_tree('b1')
901
858
        self.b1 = self.tree1.branch
902
859
 
911
868
        bundle = self.get_valid_bundle('null:', 'white-1')
912
869
 
913
870
        # Modified
914
 
        with open('b1/trailing space ', 'ab') as f: f.write('add some text\n')
 
871
        open('b1/trailing space ', 'ab').write('add some text\n')
915
872
        self.tree1.commit('add text', rev_id='white-2')
916
873
 
917
874
        bundle = self.get_valid_bundle('white-1', 'white-2')
927
884
        self.tree1.commit('removed', rev_id='white-4')
928
885
 
929
886
        bundle = self.get_valid_bundle('white-3', 'white-4')
930
 
 
 
887
        
931
888
        # Now test a complet roll-up
932
889
        bundle = self.get_valid_bundle('null:', 'white-4')
933
890
 
946
903
                          timezone=19800, timestamp=1152544886.0)
947
904
 
948
905
        bundle = self.get_valid_bundle('null:', 'tz-1')
949
 
 
 
906
        
950
907
        rev = bundle.revisions[0]
951
908
        self.assertEqual('Mon 2006-07-10 20:51:26.000000000 +0530', rev.date)
952
909
        self.assertEqual(19800, rev.timezone)
959
916
        self.tree1.commit('message', rev_id='revid1')
960
917
        bundle = self.get_valid_bundle('null:', 'revid1')
961
918
        tree = self.get_bundle_tree(bundle, 'revid1')
962
 
        root_revision = tree.get_file_revision(tree.get_root_id())
963
 
        self.assertEqual('revid1', root_revision)
 
919
        self.assertEqual('revid1', tree.inventory.root.revision)
964
920
 
965
921
    def test_install_revisions(self):
966
922
        self.tree1 = self.make_branch_and_tree('b1')
1055
1011
        bundle = read_bundle(self.create_bundle_text('null:', 'rev2')[0])
1056
1012
        repo = self.make_repository('repo', format='dirstate-with-subtree')
1057
1013
        bundle.install_revisions(repo)
1058
 
        inv_text = repo._get_inventory_xml('rev2')
 
1014
        inv_text = repo.get_inventory_xml('rev2')
1059
1015
        self.assertNotContainsRe(inv_text, 'format="5"')
1060
1016
        self.assertContainsRe(inv_text, 'format="7"')
1061
1017
 
1072
1028
        repo = self.make_repo_with_installed_revisions()
1073
1029
        inv = repo.get_inventory('rev2')
1074
1030
        self.assertEqual('rev2', inv.root.revision)
1075
 
        root_id = inv.root.file_id
1076
 
        repo.lock_read()
1077
 
        self.addCleanup(repo.unlock)
1078
 
        self.assertEqual({(root_id, 'rev1'):(),
1079
 
            (root_id, 'rev2'):((root_id, 'rev1'),)},
1080
 
            repo.texts.get_parent_map([(root_id, 'rev1'), (root_id, 'rev2')]))
 
1031
        root_vf = repo.weave_store.get_weave(inv.root.file_id,
 
1032
                                             repo.get_transaction())
 
1033
        self.assertEqual(root_vf.versions(), ['rev1', 'rev2'])
1081
1034
 
1082
1035
    def test_inv_hash_across_serializers(self):
1083
1036
        repo = self.make_repo_with_installed_revisions()
1084
 
        recorded_inv_sha1 = repo.get_revision('rev2').inventory_sha1
1085
 
        xml = repo._get_inventory_xml('rev2')
1086
 
        self.assertEqual(osutils.sha_string(xml), recorded_inv_sha1)
 
1037
        recorded_inv_sha1 = repo.get_inventory_sha1('rev2')
 
1038
        xml = repo.get_inventory_xml('rev2')
 
1039
        self.assertEqual(sha_string(xml), recorded_inv_sha1)
1087
1040
 
1088
1041
    def test_across_models_incompatible(self):
1089
1042
        tree = self.make_simple_tree('dirstate-with-subtree')
1092
1045
        try:
1093
1046
            bundle = read_bundle(self.create_bundle_text('null:', 'rev1')[0])
1094
1047
        except errors.IncompatibleBundleFormat:
1095
 
            raise tests.TestSkipped("Format 0.8 doesn't work with knit3")
 
1048
            raise TestSkipped("Format 0.8 doesn't work with knit3")
1096
1049
        repo = self.make_repository('repo', format='knit')
1097
1050
        bundle.install_revisions(repo)
1098
1051
 
1119
1072
        try:
1120
1073
            bundle = read_bundle(self.create_bundle_text('null:', 'rev1')[0])
1121
1074
        except errors.IncompatibleBundleFormat:
1122
 
            raise tests.TestSkipped("Format 0.8 doesn't work with knit3")
 
1075
            raise TestSkipped("Format 0.8 doesn't work with knit3")
1123
1076
        if isinstance(bundle, v09.BundleInfo09):
1124
 
            raise tests.TestSkipped("Format 0.9 doesn't work with subtrees")
 
1077
            raise TestSkipped("Format 0.9 doesn't work with subtrees")
1125
1078
        repo = self.make_repository('repo', format='knit')
1126
1079
        self.assertRaises(errors.IncompatibleRevision,
1127
1080
                          bundle.install_revisions, repo)
1134
1087
        try:
1135
1088
            self.tree1.commit('Revision/id/with/slashes', rev_id='rev/id')
1136
1089
        except ValueError:
1137
 
            raise tests.TestSkipped(
1138
 
                "Repository doesn't support revision ids with slashes")
 
1090
            raise TestSkipped("Repository doesn't support revision ids with"
 
1091
                              " slashes")
1139
1092
        bundle = self.get_valid_bundle('null:', 'rev/id')
1140
1093
 
1141
1094
    def test_skip_file(self):
1157
1110
        self.tree1.commit('rev3', rev_id='rev3')
1158
1111
        bundle = self.get_valid_bundle('reva', 'rev3')
1159
1112
        if getattr(bundle, 'get_bundle_reader', None) is None:
1160
 
            raise tests.TestSkipped('Bundle format cannot provide reader')
 
1113
            raise TestSkipped('Bundle format cannot provide reader')
1161
1114
        # be sure that file1 comes before file2
1162
1115
        for b, m, k, r, f in bundle.get_bundle_reader().iter_records():
1163
1116
            if f == 'file3-id':
1166
1119
        bundle.install_revisions(target.branch.repository)
1167
1120
 
1168
1121
 
1169
 
class V08BundleTester(BundleTester, tests.TestCaseWithTransport):
 
1122
class V08BundleTester(BundleTester, TestCaseWithTransport):
1170
1123
 
1171
1124
    format = '0.8'
1172
1125
 
1305
1258
        return format
1306
1259
 
1307
1260
 
1308
 
class V4BundleTester(BundleTester, tests.TestCaseWithTransport):
 
1261
class V4BundleTester(BundleTester, TestCaseWithTransport):
1309
1262
 
1310
1263
    format = '4'
1311
1264
 
1313
1266
        """Create a bundle from base_rev_id -> rev_id in built-in branch.
1314
1267
        Make sure that the text generated is valid, and that it
1315
1268
        can be applied against the base, and generate the same information.
1316
 
 
1317
 
        :return: The in-memory bundle
 
1269
        
 
1270
        :return: The in-memory bundle 
1318
1271
        """
1319
1272
        bundle_txt, rev_ids = self.create_bundle_text(base_rev_id, rev_id)
1320
1273
 
1321
 
        # This should also validate the generated bundle
 
1274
        # This should also validate the generated bundle 
1322
1275
        bundle = read_bundle(bundle_txt)
1323
1276
        repository = self.b1.repository
1324
1277
        for bundle_rev in bundle.real_revisions:
1328
1281
            # it
1329
1282
            branch_rev = repository.get_revision(bundle_rev.revision_id)
1330
1283
            for a in ('inventory_sha1', 'revision_id', 'parent_ids',
1331
 
                      'timestamp', 'timezone', 'message', 'committer',
 
1284
                      'timestamp', 'timezone', 'message', 'committer', 
1332
1285
                      'parent_ids', 'properties'):
1333
 
                self.assertEqual(getattr(branch_rev, a),
 
1286
                self.assertEqual(getattr(branch_rev, a), 
1334
1287
                                 getattr(bundle_rev, a))
1335
 
            self.assertEqual(len(branch_rev.parent_ids),
 
1288
            self.assertEqual(len(branch_rev.parent_ids), 
1336
1289
                             len(bundle_rev.parent_ids))
1337
1290
        self.assertEqual(set(rev_ids),
1338
1291
                         set([r.revision_id for r in bundle.real_revisions]))
1352
1305
        new_text = self.get_raw(StringIO(''.join(bundle_txt)))
1353
1306
        new_text = new_text.replace('<file file_id="exe-1"',
1354
1307
                                    '<file executable="y" file_id="exe-1"')
1355
 
        new_text = new_text.replace('B260', 'B275')
 
1308
        new_text = new_text.replace('B222', 'B237')
1356
1309
        bundle_txt = StringIO()
1357
1310
        bundle_txt.write(serializer._get_bundle_header('4'))
1358
1311
        bundle_txt.write('\n')
1364
1317
 
1365
1318
    def create_bundle_text(self, base_rev_id, rev_id):
1366
1319
        bundle_txt = StringIO()
1367
 
        rev_ids = write_bundle(self.b1.repository, rev_id, base_rev_id,
 
1320
        rev_ids = write_bundle(self.b1.repository, rev_id, base_rev_id, 
1368
1321
                               bundle_txt, format=self.format)
1369
1322
        bundle_txt.seek(0)
1370
 
        self.assertEqual(bundle_txt.readline(),
 
1323
        self.assertEqual(bundle_txt.readline(), 
1371
1324
                         '# Bazaar revision bundle v%s\n' % self.format)
1372
1325
        self.assertEqual(bundle_txt.readline(), '#\n')
1373
1326
        rev = self.b1.repository.get_revision(rev_id)
1393
1346
        tree2 = self.make_branch_and_tree('target')
1394
1347
        target_repo = tree2.branch.repository
1395
1348
        install_bundle(target_repo, serializer.read(s))
1396
 
        target_repo.lock_read()
1397
 
        self.addCleanup(target_repo.unlock)
1398
 
        # Turn the 'iterators_of_bytes' back into simple strings for comparison
1399
 
        repo_texts = dict((i, ''.join(content)) for i, content
1400
 
                          in target_repo.iter_files_bytes(
1401
 
                                [('fileid-2', 'rev1', '1'),
1402
 
                                 ('fileid-2', 'rev2', '2')]))
1403
 
        self.assertEqual({'1':'contents1\nstatic\n',
1404
 
                          '2':'contents2\nstatic\n'},
1405
 
                         repo_texts)
 
1349
        vf = target_repo.weave_store.get_weave('fileid-2',
 
1350
            target_repo.get_transaction())
 
1351
        self.assertEqual('contents1\nstatic\n', vf.get_text('rev1'))
 
1352
        self.assertEqual('contents2\nstatic\n', vf.get_text('rev2'))
1406
1353
        rtree = target_repo.revision_tree('rev2')
1407
 
        inventory_vf = target_repo.inventories
1408
 
        # If the inventory store has a graph, it must match the revision graph.
1409
 
        self.assertSubset(
1410
 
            [inventory_vf.get_parent_map([('rev2',)])[('rev2',)]],
1411
 
            [None, (('rev1',),)])
 
1354
        inventory_vf = target_repo.get_inventory_weave()
 
1355
        self.assertEqual({'rev2':('rev1',)},
 
1356
            inventory_vf.get_parent_map(['rev2']))
1412
1357
        self.assertEqual('changed file',
1413
1358
                         target_repo.get_revision('rev2').message)
1414
1359
 
1428
1373
        branch = tree_a.branch
1429
1374
        repo_a = branch.repository
1430
1375
        tree_a.commit("base", allow_pointless=True, rev_id='A')
1431
 
        self.assertFalse(branch.repository.has_signature_for_revision_id('A'))
 
1376
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
1432
1377
        try:
1433
1378
            from bzrlib.testament import Testament
1434
1379
            # monkey patch gpg signing mechanism
1435
1380
            bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
1436
 
            new_config = test_commit.MustSignConfig()
1437
 
            commit.Commit(config_stack=new_config).commit(message="base",
 
1381
            new_config = test_commit.MustSignConfig(branch)
 
1382
            commit.Commit(config=new_config).commit(message="base",
1438
1383
                                                    allow_pointless=True,
1439
1384
                                                    rev_id='B',
1440
1385
                                                    working_tree=tree_a)
1458
1403
        install_bundle(repo_b, serializer.read(s))
1459
1404
 
1460
1405
 
1461
 
class V4_2aBundleTester(V4BundleTester):
 
1406
class V4WeaveBundleTester(V4BundleTester):
1462
1407
 
1463
1408
    def bzrdir_format(self):
1464
 
        return '2a'
1465
 
 
1466
 
    def get_invalid_bundle(self, base_rev_id, rev_id):
1467
 
        """Create a bundle from base_rev_id -> rev_id in built-in branch.
1468
 
        Munge the text so that it's invalid.
1469
 
 
1470
 
        :return: The in-memory bundle
1471
 
        """
1472
 
        from bzrlib.bundle import serializer
1473
 
        bundle_txt, rev_ids = self.create_bundle_text(base_rev_id, rev_id)
1474
 
        new_text = self.get_raw(StringIO(''.join(bundle_txt)))
1475
 
        # We are going to be replacing some text to set the executable bit on a
1476
 
        # file. Make sure the text replacement actually works correctly.
1477
 
        self.assertContainsRe(new_text, '(?m)B244\n\ni 1\n<inventory')
1478
 
        new_text = new_text.replace('<file file_id="exe-1"',
1479
 
                                    '<file executable="y" file_id="exe-1"')
1480
 
        new_text = new_text.replace('B244', 'B259')
1481
 
        bundle_txt = StringIO()
1482
 
        bundle_txt.write(serializer._get_bundle_header('4'))
1483
 
        bundle_txt.write('\n')
1484
 
        bundle_txt.write(new_text.encode('bz2'))
1485
 
        bundle_txt.seek(0)
1486
 
        bundle = read_bundle(bundle_txt)
1487
 
        self.valid_apply_bundle(base_rev_id, bundle)
1488
 
        return bundle
1489
 
 
1490
 
    def make_merged_branch(self):
1491
 
        builder = self.make_branch_builder('source')
1492
 
        builder.start_series()
1493
 
        builder.build_snapshot('a@cset-0-1', None, [
1494
 
            ('add', ('', 'root-id', 'directory', None)),
1495
 
            ('add', ('file', 'file-id', 'file', 'original content\n')),
1496
 
            ])
1497
 
        builder.build_snapshot('a@cset-0-2a', ['a@cset-0-1'], [
1498
 
            ('modify', ('file-id', 'new-content\n')),
1499
 
            ])
1500
 
        builder.build_snapshot('a@cset-0-2b', ['a@cset-0-1'], [
1501
 
            ('add', ('other-file', 'file2-id', 'file', 'file2-content\n')),
1502
 
            ])
1503
 
        builder.build_snapshot('a@cset-0-3', ['a@cset-0-2a', 'a@cset-0-2b'], [
1504
 
            ('add', ('other-file', 'file2-id', 'file', 'file2-content\n')),
1505
 
            ])
1506
 
        builder.finish_series()
1507
 
        self.b1 = builder.get_branch()
1508
 
        self.b1.lock_read()
1509
 
        self.addCleanup(self.b1.unlock)
1510
 
 
1511
 
    def make_bundle_just_inventories(self, base_revision_id,
1512
 
                                     target_revision_id,
1513
 
                                     revision_ids):
1514
 
        sio = StringIO()
1515
 
        writer = v4.BundleWriteOperation(base_revision_id, target_revision_id,
1516
 
                                         self.b1.repository, sio)
1517
 
        writer.bundle.begin()
1518
 
        writer._add_inventory_mpdiffs_from_serializer(revision_ids)
1519
 
        writer.bundle.end()
1520
 
        sio.seek(0)
1521
 
        return sio
1522
 
 
1523
 
    def test_single_inventory_multiple_parents_as_xml(self):
1524
 
        self.make_merged_branch()
1525
 
        sio = self.make_bundle_just_inventories('a@cset-0-1', 'a@cset-0-3',
1526
 
                                                ['a@cset-0-3'])
1527
 
        reader = v4.BundleReader(sio, stream_input=False)
1528
 
        records = list(reader.iter_records())
1529
 
        self.assertEqual(1, len(records))
1530
 
        (bytes, metadata, repo_kind, revision_id,
1531
 
         file_id) = records[0]
1532
 
        self.assertIs(None, file_id)
1533
 
        self.assertEqual('a@cset-0-3', revision_id)
1534
 
        self.assertEqual('inventory', repo_kind)
1535
 
        self.assertEqual({'parents': ['a@cset-0-2a', 'a@cset-0-2b'],
1536
 
                          'sha1': '09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1537
 
                          'storage_kind': 'mpdiff',
1538
 
                         }, metadata)
1539
 
        # We should have an mpdiff that takes some lines from both parents.
1540
 
        self.assertEqualDiff(
1541
 
            'i 1\n'
1542
 
            '<inventory format="10" revision_id="a@cset-0-3">\n'
1543
 
            '\n'
1544
 
            'c 0 1 1 2\n'
1545
 
            'c 1 3 3 2\n', bytes)
1546
 
 
1547
 
    def test_single_inv_no_parents_as_xml(self):
1548
 
        self.make_merged_branch()
1549
 
        sio = self.make_bundle_just_inventories('null:', 'a@cset-0-1',
1550
 
                                                ['a@cset-0-1'])
1551
 
        reader = v4.BundleReader(sio, stream_input=False)
1552
 
        records = list(reader.iter_records())
1553
 
        self.assertEqual(1, len(records))
1554
 
        (bytes, metadata, repo_kind, revision_id,
1555
 
         file_id) = records[0]
1556
 
        self.assertIs(None, file_id)
1557
 
        self.assertEqual('a@cset-0-1', revision_id)
1558
 
        self.assertEqual('inventory', repo_kind)
1559
 
        self.assertEqual({'parents': [],
1560
 
                          'sha1': 'a13f42b142d544aac9b085c42595d304150e31a2',
1561
 
                          'storage_kind': 'mpdiff',
1562
 
                         }, metadata)
1563
 
        # We should have an mpdiff that takes some lines from both parents.
1564
 
        self.assertEqualDiff(
1565
 
            'i 4\n'
1566
 
            '<inventory format="10" revision_id="a@cset-0-1">\n'
1567
 
            '<directory file_id="root-id" name=""'
1568
 
                ' revision="a@cset-0-1" />\n'
1569
 
            '<file file_id="file-id" name="file" parent_id="root-id"'
1570
 
                ' revision="a@cset-0-1"'
1571
 
                ' text_sha1="09c2f8647e14e49e922b955c194102070597c2d1"'
1572
 
                ' text_size="17" />\n'
1573
 
            '</inventory>\n'
1574
 
            '\n', bytes)
1575
 
 
1576
 
    def test_multiple_inventories_as_xml(self):
1577
 
        self.make_merged_branch()
1578
 
        sio = self.make_bundle_just_inventories('a@cset-0-1', 'a@cset-0-3',
1579
 
            ['a@cset-0-2a', 'a@cset-0-2b', 'a@cset-0-3'])
1580
 
        reader = v4.BundleReader(sio, stream_input=False)
1581
 
        records = list(reader.iter_records())
1582
 
        self.assertEqual(3, len(records))
1583
 
        revision_ids = [rev_id for b, m, k, rev_id, f in records]
1584
 
        self.assertEqual(['a@cset-0-2a', 'a@cset-0-2b', 'a@cset-0-3'],
1585
 
                         revision_ids)
1586
 
        metadata_2a = records[0][1]
1587
 
        self.assertEqual({'parents': ['a@cset-0-1'],
1588
 
                          'sha1': '1e105886d62d510763e22885eec733b66f5f09bf',
1589
 
                          'storage_kind': 'mpdiff',
1590
 
                         }, metadata_2a)
1591
 
        metadata_2b = records[1][1]
1592
 
        self.assertEqual({'parents': ['a@cset-0-1'],
1593
 
                          'sha1': 'f03f12574bdb5ed2204c28636c98a8547544ccd8',
1594
 
                          'storage_kind': 'mpdiff',
1595
 
                         }, metadata_2b)
1596
 
        metadata_3 = records[2][1]
1597
 
        self.assertEqual({'parents': ['a@cset-0-2a', 'a@cset-0-2b'],
1598
 
                          'sha1': '09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1599
 
                          'storage_kind': 'mpdiff',
1600
 
                         }, metadata_3)
1601
 
        bytes_2a = records[0][0]
1602
 
        self.assertEqualDiff(
1603
 
            'i 1\n'
1604
 
            '<inventory format="10" revision_id="a@cset-0-2a">\n'
1605
 
            '\n'
1606
 
            'c 0 1 1 1\n'
1607
 
            'i 1\n'
1608
 
            '<file file_id="file-id" name="file" parent_id="root-id"'
1609
 
                ' revision="a@cset-0-2a"'
1610
 
                ' text_sha1="50f545ff40e57b6924b1f3174b267ffc4576e9a9"'
1611
 
                ' text_size="12" />\n'
1612
 
            '\n'
1613
 
            'c 0 3 3 1\n', bytes_2a)
1614
 
        bytes_2b = records[1][0]
1615
 
        self.assertEqualDiff(
1616
 
            'i 1\n'
1617
 
            '<inventory format="10" revision_id="a@cset-0-2b">\n'
1618
 
            '\n'
1619
 
            'c 0 1 1 2\n'
1620
 
            'i 1\n'
1621
 
            '<file file_id="file2-id" name="other-file" parent_id="root-id"'
1622
 
                ' revision="a@cset-0-2b"'
1623
 
                ' text_sha1="b46c0c8ea1e5ef8e46fc8894bfd4752a88ec939e"'
1624
 
                ' text_size="14" />\n'
1625
 
            '\n'
1626
 
            'c 0 3 4 1\n', bytes_2b)
1627
 
        bytes_3 = records[2][0]
1628
 
        self.assertEqualDiff(
1629
 
            'i 1\n'
1630
 
            '<inventory format="10" revision_id="a@cset-0-3">\n'
1631
 
            '\n'
1632
 
            'c 0 1 1 2\n'
1633
 
            'c 1 3 3 2\n', bytes_3)
1634
 
 
1635
 
    def test_creating_bundle_preserves_chk_pages(self):
1636
 
        self.make_merged_branch()
1637
 
        target = self.b1.bzrdir.sprout('target',
1638
 
                                       revision_id='a@cset-0-2a').open_branch()
1639
 
        bundle_txt, rev_ids = self.create_bundle_text('a@cset-0-2a',
1640
 
                                                      'a@cset-0-3')
1641
 
        self.assertEqual(['a@cset-0-2b', 'a@cset-0-3'], rev_ids)
1642
 
        bundle = read_bundle(bundle_txt)
1643
 
        target.lock_write()
1644
 
        self.addCleanup(target.unlock)
1645
 
        install_bundle(target.repository, bundle)
1646
 
        inv1 = self.b1.repository.inventories.get_record_stream([
1647
 
            ('a@cset-0-3',)], 'unordered',
1648
 
            True).next().get_bytes_as('fulltext')
1649
 
        inv2 = target.repository.inventories.get_record_stream([
1650
 
            ('a@cset-0-3',)], 'unordered',
1651
 
            True).next().get_bytes_as('fulltext')
1652
 
        self.assertEqualDiff(inv1, inv2)
 
1409
        return 'metaweave'
1653
1410
 
1654
1411
 
1655
1412
class MungedBundleTester(object):
1704
1461
        self.check_valid(bundle)
1705
1462
 
1706
1463
 
1707
 
class MungedBundleTesterV09(tests.TestCaseWithTransport, MungedBundleTester):
 
1464
class MungedBundleTesterV09(TestCaseWithTransport, MungedBundleTester):
1708
1465
 
1709
1466
    format = '0.9'
1710
1467
 
1742
1499
        self.check_valid(bundle)
1743
1500
 
1744
1501
 
1745
 
class MungedBundleTesterV4(tests.TestCaseWithTransport, MungedBundleTester):
 
1502
class MungedBundleTesterV4(TestCaseWithTransport, MungedBundleTester):
1746
1503
 
1747
1504
    format = '4'
1748
1505
 
1749
1506
 
1750
 
class TestBundleWriterReader(tests.TestCase):
 
1507
class TestBundleWriterReader(TestCase):
1751
1508
 
1752
1509
    def test_roundtrip_record(self):
1753
1510
        fileobj = StringIO()
1815
1572
        record = record_iter.next()
1816
1573
        self.assertEqual((None, {'foo': 'bar', 'storage_kind': 'header'},
1817
1574
            'info', None, None), record)
1818
 
        self.assertRaises(errors.BadBundle, record_iter.next)
1819
 
 
1820
 
 
1821
 
class TestReadMergeableFromUrl(tests.TestCaseWithTransport):
 
1575
        self.assertRaises(BadBundle, record_iter.next)
 
1576
 
 
1577
 
 
1578
class TestReadMergeableFromUrl(TestCaseWithTransport):
1822
1579
 
1823
1580
    def test_read_mergeable_skips_local(self):
1824
1581
        """A local bundle named like the URL should not be read.
1830
1587
            def look_up(self, name, url):
1831
1588
                return 'source'
1832
1589
        directories.register('foo:', FooService, 'Testing directory service')
1833
 
        self.addCleanup(directories.remove, 'foo:')
 
1590
        self.addCleanup(lambda: directories.remove('foo:'))
1834
1591
        self.build_tree_contents([('./foo:bar', out.getvalue())])
1835
1592
        self.assertRaises(errors.NotABundle, read_mergeable_from_url,
1836
1593
                          'foo:bar')
1837
 
 
1838
 
    def test_infinite_redirects_are_not_a_bundle(self):
1839
 
        """If a URL causes TooManyRedirections then NotABundle is raised.
1840
 
        """
1841
 
        from bzrlib.tests.blackbox.test_push import RedirectingMemoryServer
1842
 
        server = RedirectingMemoryServer()
1843
 
        self.start_server(server)
1844
 
        url = server.get_url() + 'infinite-loop'
1845
 
        self.assertRaises(errors.NotABundle, read_mergeable_from_url, url)
1846
 
 
1847
 
    def test_smart_server_connection_reset(self):
1848
 
        """If a smart server connection fails during the attempt to read a
1849
 
        bundle, then the ConnectionReset error should be propagated.
1850
 
        """
1851
 
        # Instantiate a server that will provoke a ConnectionReset
1852
 
        sock_server = DisconnectingServer()
1853
 
        self.start_server(sock_server)
1854
 
        # We don't really care what the url is since the server will close the
1855
 
        # connection without interpreting it
1856
 
        url = sock_server.get_url()
1857
 
        self.assertRaises(errors.ConnectionReset, read_mergeable_from_url, url)
1858
 
 
1859
 
 
1860
 
class DisconnectingHandler(SocketServer.BaseRequestHandler):
1861
 
    """A request handler that immediately closes any connection made to it."""
1862
 
 
1863
 
    def handle(self):
1864
 
        self.request.close()
1865
 
 
1866
 
 
1867
 
class DisconnectingServer(test_server.TestingTCPServerInAThread):
1868
 
 
1869
 
    def __init__(self):
1870
 
        super(DisconnectingServer, self).__init__(
1871
 
            ('127.0.0.1', 0),
1872
 
            test_server.TestingTCPServer,
1873
 
            DisconnectingHandler)
1874
 
 
1875
 
    def get_url(self):
1876
 
        """Return the url of the server"""
1877
 
        return "bzr://%s:%d/" % self.server.server_address