~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_bundle.py

  • Committer: John Arbash Meinel
  • Date: 2008-03-20 15:10:05 UTC
  • mto: This revision was merged to the branch mainline in revision 3299.
  • Revision ID: john@arbash-meinel.com-20080320151005-z9lajjy69m20of17
uncommit --local in an unbound branch raises the same exception as commit --local

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2013, 2016 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
    )
33
 
from bzrlib.bundle import read_mergeable_from_url
 
30
from bzrlib.bzrdir import BzrDir
34
31
from bzrlib.bundle.apply_bundle import install_bundle, merge_bundle
35
32
from bzrlib.bundle.bundle_data import BundleTree
36
 
from bzrlib.directory_service import directories
37
33
from bzrlib.bundle.serializer import write_bundle, read_bundle, v09, v4
38
34
from bzrlib.bundle.serializer.v08 import BundleSerializerV08
39
35
from bzrlib.bundle.serializer.v09 import BundleSerializerV09
40
36
from bzrlib.bundle.serializer.v4 import BundleSerializerV4
 
37
from bzrlib.branch import Branch
 
38
from bzrlib.diff import internal_diff
 
39
from bzrlib.errors import (BzrError, TestamentMismatch, NotABundle, BadBundle, 
 
40
                           NoSuchFile,)
 
41
from bzrlib.merge import Merge3Merger
41
42
from bzrlib.repofmt import knitrepo
 
43
from bzrlib.osutils import sha_file
42
44
from bzrlib.tests import (
43
 
    features,
 
45
    SymlinkFeature,
 
46
    TestCase,
 
47
    TestCaseInTempDir,
 
48
    TestCaseWithTransport,
 
49
    TestSkipped,
44
50
    test_commit,
45
 
    test_read_bundle,
46
 
    test_server,
47
51
    )
48
52
from bzrlib.transform import TreeTransform
49
53
 
50
54
 
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
55
class MockTree(object):
68
 
 
69
56
    def __init__(self):
70
57
        from bzrlib.inventory import InventoryDirectory, ROOT_ID
71
58
        object.__init__(self)
75
62
        self.root = InventoryDirectory(ROOT_ID, '', None)
76
63
 
77
64
    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
 
65
 
 
66
    def __iter__(self):
 
67
        return self.paths.iterkeys()
89
68
 
90
69
    def __getitem__(self, file_id):
91
70
        if file_id == self.root.file_id:
103
82
        for path, file_id in self.ids.iteritems():
104
83
            yield path, self[file_id]
105
84
 
106
 
    def kind(self, file_id):
 
85
    def get_file_kind(self, file_id):
107
86
        if file_id in self.contents:
108
87
            kind = 'file'
109
88
        else:
111
90
        return kind
112
91
 
113
92
    def make_entry(self, file_id, path):
114
 
        from bzrlib.inventory import (InventoryFile , InventoryDirectory,
115
 
            InventoryLink)
 
93
        from bzrlib.inventory import (InventoryEntry, InventoryFile
 
94
                                    , InventoryDirectory, InventoryLink)
116
95
        name = os.path.basename(path)
117
 
        kind = self.kind(file_id)
 
96
        kind = self.get_file_kind(file_id)
118
97
        parent_id = self.parent_id(file_id)
119
98
        text_sha_1, text_size = self.contents_stats(file_id)
120
99
        if kind == 'directory':
121
100
            ie = InventoryDirectory(file_id, name, parent_id)
122
101
        elif kind == 'file':
123
102
            ie = InventoryFile(file_id, name, parent_id)
124
 
            ie.text_sha1 = text_sha_1
125
 
            ie.text_size = text_size
126
103
        elif kind == 'symlink':
127
104
            ie = InventoryLink(file_id, name, parent_id)
128
105
        else:
129
 
            raise errors.BzrError('unknown kind %r' % kind)
 
106
            raise BzrError('unknown kind %r' % kind)
 
107
        ie.text_sha1 = text_sha_1
 
108
        ie.text_size = text_size
130
109
        return ie
131
110
 
132
111
    def add_dir(self, file_id, path):
133
112
        self.paths[file_id] = path
134
113
        self.ids[path] = file_id
135
 
 
 
114
    
136
115
    def add_file(self, file_id, path, contents):
137
116
        self.add_dir(file_id, path)
138
117
        self.contents[file_id] = contents
152
131
        result.seek(0,0)
153
132
        return result
154
133
 
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
134
    def contents_stats(self, file_id):
165
135
        if file_id not in self.contents:
166
136
            return None, None
167
 
        text_sha1 = osutils.sha_file(self.get_file(file_id))
 
137
        text_sha1 = sha_file(self.get_file(file_id))
168
138
        return text_sha1, len(self.contents[file_id])
169
139
 
170
140
 
171
 
class BTreeTester(tests.TestCase):
 
141
class BTreeTester(TestCase):
172
142
    """A simple unittest tester for the BundleTree class."""
173
143
 
174
144
    def make_tree_1(self):
178
148
        mtree.add_file("c", "grandparent/parent/file", "Hello\n")
179
149
        mtree.add_dir("d", "grandparent/alt_parent")
180
150
        return BundleTree(mtree, ''), mtree
181
 
 
 
151
        
182
152
    def test_renames(self):
183
153
        """Ensure that file renames have the proper effect on children"""
184
154
        btree = self.make_tree_1()[0]
185
155
        self.assertEqual(btree.old_path("grandparent"), "grandparent")
186
 
        self.assertEqual(btree.old_path("grandparent/parent"),
 
156
        self.assertEqual(btree.old_path("grandparent/parent"), 
187
157
                         "grandparent/parent")
188
158
        self.assertEqual(btree.old_path("grandparent/parent/file"),
189
159
                         "grandparent/parent/file")
196
166
        self.assertEqual(btree.path2id("grandparent/parent"), "b")
197
167
        self.assertEqual(btree.path2id("grandparent/parent/file"), "c")
198
168
 
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)
 
169
        assert btree.path2id("grandparent2") is None
 
170
        assert btree.path2id("grandparent2/parent") is None
 
171
        assert btree.path2id("grandparent2/parent/file") is None
202
172
 
203
173
        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)
 
174
        assert btree.old_path("grandparent") is None
 
175
        assert btree.old_path("grandparent/parent") is None
 
176
        assert btree.old_path("grandparent/parent/file") is None
207
177
 
208
178
        self.assertEqual(btree.id2path("a"), "grandparent2")
209
179
        self.assertEqual(btree.id2path("b"), "grandparent2/parent")
213
183
        self.assertEqual(btree.path2id("grandparent2/parent"), "b")
214
184
        self.assertEqual(btree.path2id("grandparent2/parent/file"), "c")
215
185
 
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)
 
186
        assert btree.path2id("grandparent") is None
 
187
        assert btree.path2id("grandparent/parent") is None
 
188
        assert btree.path2id("grandparent/parent/file") is None
219
189
 
220
190
        btree.note_rename("grandparent/parent", "grandparent2/parent2")
221
191
        self.assertEqual(btree.id2path("a"), "grandparent2")
226
196
        self.assertEqual(btree.path2id("grandparent2/parent2"), "b")
227
197
        self.assertEqual(btree.path2id("grandparent2/parent2/file"), "c")
228
198
 
229
 
        self.assertTrue(btree.path2id("grandparent2/parent") is None)
230
 
        self.assertTrue(btree.path2id("grandparent2/parent/file") is None)
 
199
        assert btree.path2id("grandparent2/parent") is None
 
200
        assert btree.path2id("grandparent2/parent/file") is None
231
201
 
232
 
        btree.note_rename("grandparent/parent/file",
 
202
        btree.note_rename("grandparent/parent/file", 
233
203
                          "grandparent2/parent2/file2")
234
204
        self.assertEqual(btree.id2path("a"), "grandparent2")
235
205
        self.assertEqual(btree.id2path("b"), "grandparent2/parent2")
239
209
        self.assertEqual(btree.path2id("grandparent2/parent2"), "b")
240
210
        self.assertEqual(btree.path2id("grandparent2/parent2/file2"), "c")
241
211
 
242
 
        self.assertTrue(btree.path2id("grandparent2/parent2/file") is None)
 
212
        assert btree.path2id("grandparent2/parent2/file") is None
243
213
 
244
214
    def test_moves(self):
245
215
        """Ensure that file moves have the proper effect on children"""
246
216
        btree = self.make_tree_1()[0]
247
 
        btree.note_rename("grandparent/parent/file",
 
217
        btree.note_rename("grandparent/parent/file", 
248
218
                          "grandparent/alt_parent/file")
249
219
        self.assertEqual(btree.id2path("c"), "grandparent/alt_parent/file")
250
220
        self.assertEqual(btree.path2id("grandparent/alt_parent/file"), "c")
251
 
        self.assertTrue(btree.path2id("grandparent/parent/file") is None)
 
221
        assert btree.path2id("grandparent/parent/file") is None
252
222
 
253
223
    def unified_diff(self, old, new):
254
224
        out = StringIO()
255
 
        diff.internal_diff("old", old, "new", new, out)
 
225
        internal_diff("old", old, "new", new, out)
256
226
        out.seek(0,0)
257
227
        return out.read()
258
228
 
259
229
    def make_tree_2(self):
260
230
        btree = self.make_tree_1()[0]
261
 
        btree.note_rename("grandparent/parent/file",
 
231
        btree.note_rename("grandparent/parent/file", 
262
232
                          "grandparent/alt_parent/file")
263
 
        self.assertTrue(btree.id2path("e") is None)
264
 
        self.assertTrue(btree.path2id("grandparent/parent/file") is None)
 
233
        assert btree.id2path("e") is None
 
234
        assert btree.path2id("grandparent/parent/file") is None
265
235
        btree.note_id("e", "grandparent/parent/file")
266
236
        return btree
267
237
 
293
263
    def make_tree_3(self):
294
264
        btree, mtree = self.make_tree_1()
295
265
        mtree.add_file("e", "grandparent/parent/topping", "Anchovies\n")
296
 
        btree.note_rename("grandparent/parent/file",
 
266
        btree.note_rename("grandparent/parent/file", 
297
267
                          "grandparent/alt_parent/file")
298
 
        btree.note_rename("grandparent/parent/topping",
 
268
        btree.note_rename("grandparent/parent/topping", 
299
269
                          "grandparent/alt_parent/stopping")
300
270
        return btree
301
271
 
325
295
        btree = self.make_tree_1()[0]
326
296
        self.assertEqual(btree.get_file("c").read(), "Hello\n")
327
297
        btree.note_deletion("grandparent/parent/file")
328
 
        self.assertTrue(btree.id2path("c") is None)
329
 
        self.assertTrue(btree.path2id("grandparent/parent/file") is None)
 
298
        assert btree.id2path("c") is None
 
299
        assert btree.path2id("grandparent/parent/file") is None
330
300
 
331
301
    def sorted_ids(self, tree):
332
 
        ids = list(tree.all_file_ids())
 
302
        ids = list(tree)
333
303
        ids.sort()
334
304
        return ids
335
305
 
340
310
            [inventory.ROOT_ID, 'a', 'b', 'c', 'd'])
341
311
        btree.note_deletion("grandparent/parent/file")
342
312
        btree.note_id("e", "grandparent/alt_parent/fool", kind="directory")
343
 
        btree.note_last_changed("grandparent/alt_parent/fool",
 
313
        btree.note_last_changed("grandparent/alt_parent/fool", 
344
314
                                "revisionidiguess")
345
315
        self.assertEqual(self.sorted_ids(btree),
346
316
            [inventory.ROOT_ID, 'a', 'b', 'd', 'e'])
347
317
 
348
318
 
349
 
class BundleTester1(tests.TestCaseWithTransport):
 
319
class BundleTester1(TestCaseWithTransport):
350
320
 
351
321
    def test_mismatched_bundle(self):
352
322
        format = bzrdir.BzrDirMetaFormat1()
353
323
        format.repository_format = knitrepo.RepositoryFormatKnit3()
354
324
        serializer = BundleSerializerV08('0.8')
355
325
        b = self.make_branch('.', format=format)
356
 
        self.assertRaises(errors.IncompatibleBundleFormat, serializer.write,
 
326
        self.assertRaises(errors.IncompatibleBundleFormat, serializer.write, 
357
327
                          b.repository, [], {}, StringIO())
358
328
 
359
329
    def test_matched_bundle(self):
379
349
        format = bzrdir.BzrDirMetaFormat1()
380
350
        format.repository_format = knitrepo.RepositoryFormatKnit1()
381
351
        target = self.make_branch('target', format=format)
382
 
        self.assertRaises(errors.IncompatibleRevision, install_bundle,
 
352
        self.assertRaises(errors.IncompatibleRevision, install_bundle, 
383
353
                          target.repository, read_bundle(text))
384
354
 
385
355
 
393
363
    def make_branch_and_tree(self, path, format=None):
394
364
        if format is None:
395
365
            format = self.bzrdir_format()
396
 
        return tests.TestCaseWithTransport.make_branch_and_tree(
397
 
            self, path, format)
 
366
        return TestCaseWithTransport.make_branch_and_tree(self, path, format)
398
367
 
399
368
    def make_branch(self, path, format=None):
400
369
        if format is None:
401
370
            format = self.bzrdir_format()
402
 
        return tests.TestCaseWithTransport.make_branch(self, path, format)
 
371
        return TestCaseWithTransport.make_branch(self, path, format)
403
372
 
404
373
    def create_bundle_text(self, base_rev_id, rev_id):
405
374
        bundle_txt = StringIO()
406
 
        rev_ids = write_bundle(self.b1.repository, rev_id, base_rev_id,
 
375
        rev_ids = write_bundle(self.b1.repository, rev_id, base_rev_id, 
407
376
                               bundle_txt, format=self.format)
408
377
        bundle_txt.seek(0)
409
 
        self.assertEqual(bundle_txt.readline(),
 
378
        self.assertEqual(bundle_txt.readline(), 
410
379
                         '# Bazaar revision bundle v%s\n' % self.format)
411
380
        self.assertEqual(bundle_txt.readline(), '#\n')
412
381
 
420
389
        """Create a bundle from base_rev_id -> rev_id in built-in branch.
421
390
        Make sure that the text generated is valid, and that it
422
391
        can be applied against the base, and generate the same information.
423
 
 
424
 
        :return: The in-memory bundle
 
392
        
 
393
        :return: The in-memory bundle 
425
394
        """
426
395
        bundle_txt, rev_ids = self.create_bundle_text(base_rev_id, rev_id)
427
396
 
428
 
        # This should also validate the generated bundle
 
397
        # This should also validate the generated bundle 
429
398
        bundle = read_bundle(bundle_txt)
430
399
        repository = self.b1.repository
431
400
        for bundle_rev in bundle.real_revisions:
435
404
            # it
436
405
            branch_rev = repository.get_revision(bundle_rev.revision_id)
437
406
            for a in ('inventory_sha1', 'revision_id', 'parent_ids',
438
 
                      'timestamp', 'timezone', 'message', 'committer',
 
407
                      'timestamp', 'timezone', 'message', 'committer', 
439
408
                      'parent_ids', 'properties'):
440
 
                self.assertEqual(getattr(branch_rev, a),
 
409
                self.assertEqual(getattr(branch_rev, a), 
441
410
                                 getattr(bundle_rev, a))
442
 
            self.assertEqual(len(branch_rev.parent_ids),
 
411
            self.assertEqual(len(branch_rev.parent_ids), 
443
412
                             len(bundle_rev.parent_ids))
444
 
        self.assertEqual(rev_ids,
 
413
        self.assertEqual(rev_ids, 
445
414
                         [r.revision_id for r in bundle.real_revisions])
446
415
        self.valid_apply_bundle(base_rev_id, bundle,
447
416
                                   checkout_dir=checkout_dir)
451
420
    def get_invalid_bundle(self, base_rev_id, rev_id):
452
421
        """Create a bundle from base_rev_id -> rev_id in built-in branch.
453
422
        Munge the text so that it's invalid.
454
 
 
 
423
        
455
424
        :return: The in-memory bundle
456
425
        """
457
426
        bundle_txt, rev_ids = self.create_bundle_text(base_rev_id, rev_id)
458
 
        new_text = bundle_txt.getvalue().replace('executable:no',
 
427
        new_text = bundle_txt.getvalue().replace('executable:no', 
459
428
                                               'executable:yes')
460
429
        bundle_txt = StringIO(new_text)
461
430
        bundle = read_bundle(bundle_txt)
462
431
        self.valid_apply_bundle(base_rev_id, bundle)
463
 
        return bundle
 
432
        return bundle 
464
433
 
465
434
    def test_non_bundle(self):
466
 
        self.assertRaises(errors.NotABundle,
467
 
                          read_bundle, StringIO('#!/bin/sh\n'))
 
435
        self.assertRaises(NotABundle, read_bundle, StringIO('#!/bin/sh\n'))
468
436
 
469
437
    def test_malformed(self):
470
 
        self.assertRaises(errors.BadBundle, read_bundle,
 
438
        self.assertRaises(BadBundle, read_bundle, 
471
439
                          StringIO('# Bazaar revision bundle v'))
472
440
 
473
441
    def test_crlf_bundle(self):
474
442
        try:
475
443
            read_bundle(StringIO('# Bazaar revision bundle v0.8\r\n'))
476
 
        except errors.BadBundle:
 
444
        except BadBundle:
477
445
            # It is currently permitted for bundles with crlf line endings to
478
446
            # make read_bundle raise a BadBundle, but this should be fixed.
479
447
            # Anything else, especially NotABundle, is an error.
484
452
        """
485
453
 
486
454
        if checkout_dir is None:
487
 
            checkout_dir = osutils.mkdtemp(prefix='test-branch-', dir='.')
 
455
            checkout_dir = tempfile.mkdtemp(prefix='test-branch-', dir='.')
488
456
        else:
489
457
            if not os.path.exists(checkout_dir):
490
458
                os.mkdir(checkout_dir)
493
461
        ancestors = write_bundle(self.b1.repository, rev_id, 'null:', s,
494
462
                                 format=self.format)
495
463
        s.seek(0)
496
 
        self.assertIsInstance(s.getvalue(), str)
 
464
        assert isinstance(s.getvalue(), str), (
 
465
            "Bundle isn't a bytestring:\n %s..." % repr(s.getvalue())[:40])
497
466
        install_bundle(tree.branch.repository, read_bundle(s))
498
467
        for ancestor in ancestors:
499
468
            old = self.b1.repository.revision_tree(ancestor)
500
469
            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()
 
470
 
 
471
            # Check that there aren't any inventory level changes
 
472
            delta = new.changes_from(old)
 
473
            self.assertFalse(delta.has_changed(),
 
474
                             'Revision %s not copied correctly.'
 
475
                             % (ancestor,))
 
476
 
 
477
            # Now check that the file contents are all correct
 
478
            for inventory_id in old:
 
479
                try:
 
480
                    old_file = old.get_file(inventory_id)
 
481
                except NoSuchFile:
 
482
                    continue
 
483
                if old_file is None:
 
484
                    continue
 
485
                self.assertEqual(old_file.read(),
 
486
                                 new.get_file(inventory_id).read())
523
487
        if not _mod_revision.is_null(rev_id):
524
 
            tree.branch.generate_revision_history(rev_id)
 
488
            rh = self.b1.revision_history()
 
489
            tree.branch.set_revision_history(rh[:rh.index(rev_id)+1])
525
490
            tree.update()
526
491
            delta = tree.changes_from(self.b1.repository.revision_tree(rev_id))
527
492
            self.assertFalse(delta.has_changed(),
545
510
        original_parents = to_tree.get_parent_ids()
546
511
        self.assertIs(repository.has_revision(base_rev_id), True)
547
512
        for rev in info.real_revisions:
548
 
            self.assertTrue(not repository.has_revision(rev.revision_id),
549
 
                            'Revision {%s} present before applying bundle'
550
 
                            % rev.revision_id)
551
 
        merge_bundle(info, to_tree, True, merge.Merge3Merger, False, False)
 
513
            self.assert_(not repository.has_revision(rev.revision_id),
 
514
                'Revision {%s} present before applying bundle' 
 
515
                % rev.revision_id)
 
516
        merge_bundle(info, to_tree, True, Merge3Merger, False, False)
552
517
 
553
518
        for rev in info.real_revisions:
554
 
            self.assertTrue(repository.has_revision(rev.revision_id),
555
 
                            'Missing revision {%s} after applying bundle'
556
 
                            % rev.revision_id)
 
519
            self.assert_(repository.has_revision(rev.revision_id),
 
520
                'Missing revision {%s} after applying bundle' 
 
521
                % rev.revision_id)
557
522
 
558
 
        self.assertTrue(to_tree.branch.repository.has_revision(info.target))
 
523
        self.assert_(to_tree.branch.repository.has_revision(info.target))
559
524
        # Do we also want to verify that all the texts have been added?
560
525
 
561
526
        self.assertEqual(original_parents + [info.target],
562
 
                         to_tree.get_parent_ids())
 
527
            to_tree.get_parent_ids())
563
528
 
564
529
        rev = info.real_revisions[-1]
565
530
        base_tree = self.b1.repository.revision_tree(rev.revision_id)
566
531
        to_tree = to_tree.branch.repository.revision_tree(rev.revision_id)
567
 
 
 
532
        
568
533
        # TODO: make sure the target tree is identical to base tree
569
534
        #       we might also check the working tree.
570
535
 
589
554
        self.tree1 = self.make_branch_and_tree('b1')
590
555
        self.b1 = self.tree1.branch
591
556
 
592
 
        self.build_tree_contents([('b1/one', 'one\n')])
593
 
        self.tree1.add('one', 'one-id')
594
 
        self.tree1.set_root_id('root-id')
 
557
        open('b1/one', 'wb').write('one\n')
 
558
        self.tree1.add('one')
595
559
        self.tree1.commit('add one', rev_id='a@cset-0-1')
596
560
 
597
561
        bundle = self.get_valid_bundle('null:', 'a@cset-0-1')
608
572
                , 'b1/sub/sub/'
609
573
                , 'b1/sub/sub/nonempty.txt'
610
574
                ])
611
 
        self.build_tree_contents([('b1/sub/sub/emptyfile.txt', ''),
612
 
                                  ('b1/dir/nolastnewline.txt', 'bloop')])
 
575
        open('b1/sub/sub/emptyfile.txt', 'wb').close()
 
576
        open('b1/dir/nolastnewline.txt', 'wb').write('bloop')
613
577
        tt = TreeTransform(self.tree1)
614
578
        tt.new_file('executable', tt.root, '#!/bin/sh\n', 'exe-1', True)
615
579
        tt.apply()
631
595
 
632
596
        bundle = self.get_valid_bundle('a@cset-0-1', 'a@cset-0-2')
633
597
 
634
 
        # Check a rollup bundle
 
598
        # Check a rollup bundle 
635
599
        bundle = self.get_valid_bundle('null:', 'a@cset-0-2')
636
600
 
637
601
        # Now delete entries
645
609
        tt.set_executability(False, trans_id)
646
610
        tt.apply()
647
611
        self.tree1.commit('removed', rev_id='a@cset-0-3')
648
 
 
 
612
        
649
613
        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,
 
614
        self.assertRaises((TestamentMismatch,
 
615
            errors.VersionedFileInvalidChecksum), self.get_invalid_bundle,
653
616
            'a@cset-0-2', 'a@cset-0-3')
654
 
        # Check a rollup bundle
 
617
        # Check a rollup bundle 
655
618
        bundle = self.get_valid_bundle('null:', 'a@cset-0-3')
656
619
 
657
620
        # Now move the directory
659
622
        self.tree1.commit('rename dir', rev_id='a@cset-0-4')
660
623
 
661
624
        bundle = self.get_valid_bundle('a@cset-0-3', 'a@cset-0-4')
662
 
        # Check a rollup bundle
 
625
        # Check a rollup bundle 
663
626
        bundle = self.get_valid_bundle('null:', 'a@cset-0-4')
664
627
 
665
628
        # 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(
 
629
        open('b1/sub/dir/WithCaps.txt', 'ab').write('\nAdding some text\n')
 
630
        open('b1/sub/dir/ pre space', 'ab').write(
668
631
             '\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',
 
632
        open('b1/sub/dir/nolastnewline.txt', 'ab').write('\n')
 
633
        self.tree1.rename_one('sub/dir/ pre space', 
671
634
                              'sub/ start space')
672
635
        self.tree1.commit('Modified files', rev_id='a@cset-0-5')
673
636
        bundle = self.get_valid_bundle('a@cset-0-4', 'a@cset-0-5')
679
642
                          verbose=False)
680
643
        bundle = self.get_valid_bundle('a@cset-0-5', 'a@cset-0-6')
681
644
        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')
 
645
        tree1_inv = self.tree1.branch.repository.get_inventory_xml(
 
646
            'a@cset-0-5')
 
647
        tree2_inv = other.branch.repository.get_inventory_xml('a@cset-0-5')
686
648
        self.assertEqualDiff(tree1_inv, tree2_inv)
687
649
        other.rename_one('sub/dir/nolastnewline.txt', 'sub/nolastnewline.txt')
688
650
        other.commit('rename file', rev_id='a@cset-0-6b')
691
653
                          verbose=False)
692
654
        bundle = self.get_valid_bundle('a@cset-0-6', 'a@cset-0-7')
693
655
 
694
 
    def _test_symlink_bundle(self, link_name, link_target, new_link_target):
695
 
        link_id = 'link-1'
696
 
 
697
 
        self.requireFeature(features.SymlinkFeature)
 
656
    def test_symlink_bundle(self):
 
657
        self.requireFeature(SymlinkFeature)
698
658
        self.tree1 = self.make_branch_and_tree('b1')
699
659
        self.b1 = self.tree1.branch
700
 
 
701
660
        tt = TreeTransform(self.tree1)
702
 
        tt.new_symlink(link_name, tt.root, link_target, link_id)
 
661
        tt.new_symlink('link', tt.root, 'bar/foo', 'link-1')
703
662
        tt.apply()
704
663
        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
 
 
 
664
        self.get_valid_bundle('null:', 'l@cset-0-1')
711
665
        tt = TreeTransform(self.tree1)
712
 
        trans_id = tt.trans_id_tree_file_id(link_id)
 
666
        trans_id = tt.trans_id_tree_file_id('link-1')
713
667
        tt.adjust_path('link2', tt.root, trans_id)
714
668
        tt.delete_contents(trans_id)
715
 
        tt.create_symlink(new_link_target, trans_id)
 
669
        tt.create_symlink('mars', trans_id)
716
670
        tt.apply()
717
671
        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
 
 
 
672
        self.get_valid_bundle('l@cset-0-1', 'l@cset-0-2')
725
673
        tt = TreeTransform(self.tree1)
726
 
        trans_id = tt.trans_id_tree_file_id(link_id)
 
674
        trans_id = tt.trans_id_tree_file_id('link-1')
727
675
        tt.delete_contents(trans_id)
728
676
        tt.create_symlink('jupiter', trans_id)
729
677
        tt.apply()
730
678
        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
 
 
 
679
        self.get_valid_bundle('l@cset-0-2', 'l@cset-0-3')
733
680
        tt = TreeTransform(self.tree1)
734
 
        trans_id = tt.trans_id_tree_file_id(link_id)
 
681
        trans_id = tt.trans_id_tree_file_id('link-1')
735
682
        tt.delete_contents(trans_id)
736
683
        tt.apply()
737
684
        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}')
 
685
        self.get_valid_bundle('l@cset-0-3', 'l@cset-0-4')
748
686
 
749
687
    def test_binary_bundle(self):
750
688
        self.tree1 = self.make_branch_and_tree('b1')
751
689
        self.b1 = self.tree1.branch
752
690
        tt = TreeTransform(self.tree1)
753
 
 
 
691
        
754
692
        # Add
755
693
        tt.new_file('file', tt.root, '\x00\n\x00\r\x01\n\x02\r\xff', 'binary-1')
756
694
        tt.new_file('file2', tt.root, '\x01\n\x02\r\x03\n\x04\r\xff',
821
759
        self.tree1 = self.make_branch_and_tree('b1')
822
760
        self.b1 = self.tree1.branch
823
761
 
824
 
        with open('b1/one', 'wb') as f: f.write('one\n')
 
762
        open('b1/one', 'wb').write('one\n')
825
763
        self.tree1.add('one')
826
764
        self.tree1.commit('add file', rev_id='a@cset-0-1')
827
 
        with open('b1/one', 'wb') as f: f.write('two\n')
 
765
        open('b1/one', 'wb').write('two\n')
828
766
        self.tree1.commit('modify', rev_id='a@cset-0-2')
829
 
        with open('b1/one', 'wb') as f: f.write('three\n')
 
767
        open('b1/one', 'wb').write('three\n')
830
768
        self.tree1.commit('modify', rev_id='a@cset-0-3')
831
769
        bundle_file = StringIO()
832
770
        rev_ids = write_bundle(self.tree1.branch.repository, 'a@cset-0-3',
848
786
        return bundle_file.getvalue()
849
787
 
850
788
    def test_unicode_bundle(self):
851
 
        self.requireFeature(features.UnicodeFilenameFeature)
852
789
        # Handle international characters
853
790
        os.mkdir('b1')
854
 
        f = open(u'b1/with Dod\N{Euro Sign}', 'wb')
 
791
        try:
 
792
            f = open(u'b1/with Dod\xe9', 'wb')
 
793
        except UnicodeEncodeError:
 
794
            raise TestSkipped("Filesystem doesn't support unicode")
855
795
 
856
796
        self.tree1 = self.make_branch_and_tree('b1')
857
797
        self.b1 = self.tree1.branch
861
801
            u'William Dod\xe9\n').encode('utf-8'))
862
802
        f.close()
863
803
 
864
 
        self.tree1.add([u'with Dod\N{Euro Sign}'], ['withdod-id'])
 
804
        self.tree1.add([u'with Dod\xe9'], ['withdod-id'])
865
805
        self.tree1.commit(u'i18n commit from William Dod\xe9',
866
806
                          rev_id='i18n-1', committer=u'William Dod\xe9')
867
807
 
 
808
        if sys.platform == 'darwin':
 
809
            from bzrlib.workingtree import WorkingTree3
 
810
            if type(self.tree1) is WorkingTree3:
 
811
                self.knownFailure("Bug #141438: fails for WorkingTree3 on OSX")
 
812
 
 
813
            # On Mac the '\xe9' gets changed to 'e\u0301'
 
814
            self.assertEqual([u'.bzr', u'with Dode\u0301'],
 
815
                             sorted(os.listdir(u'b1')))
 
816
            delta = self.tree1.changes_from(self.tree1.basis_tree())
 
817
            self.assertEqual([(u'with Dod\xe9', 'withdod-id', 'file')],
 
818
                             delta.removed)
 
819
            self.knownFailure("Mac OSX doesn't preserve unicode"
 
820
                              " combining characters.")
 
821
 
868
822
        # Add
869
823
        bundle = self.get_valid_bundle('null:', 'i18n-1')
870
824
 
871
825
        # Modified
872
 
        f = open(u'b1/with Dod\N{Euro Sign}', 'wb')
 
826
        f = open(u'b1/with Dod\xe9', 'wb')
873
827
        f.write(u'Modified \xb5\n'.encode('utf8'))
874
828
        f.close()
875
829
        self.tree1.commit(u'modified', rev_id='i18n-2')
876
830
 
877
831
        bundle = self.get_valid_bundle('i18n-1', 'i18n-2')
878
 
 
 
832
        
879
833
        # Renamed
880
 
        self.tree1.rename_one(u'with Dod\N{Euro Sign}', u'B\N{Euro Sign}gfors')
 
834
        self.tree1.rename_one(u'with Dod\xe9', u'B\xe5gfors')
881
835
        self.tree1.commit(u'renamed, the new i18n man', rev_id='i18n-3',
882
836
                          committer=u'Erik B\xe5gfors')
883
837
 
884
838
        bundle = self.get_valid_bundle('i18n-2', 'i18n-3')
885
839
 
886
840
        # Removed
887
 
        self.tree1.remove([u'B\N{Euro Sign}gfors'])
 
841
        self.tree1.remove([u'B\xe5gfors'])
888
842
        self.tree1.commit(u'removed', rev_id='i18n-4')
889
843
 
890
844
        bundle = self.get_valid_bundle('i18n-3', 'i18n-4')
895
849
 
896
850
    def test_whitespace_bundle(self):
897
851
        if sys.platform in ('win32', 'cygwin'):
898
 
            raise tests.TestSkipped('Windows doesn\'t support filenames'
899
 
                                    ' with tabs or trailing spaces')
 
852
            raise TestSkipped('Windows doesn\'t support filenames'
 
853
                              ' with tabs or trailing spaces')
900
854
        self.tree1 = self.make_branch_and_tree('b1')
901
855
        self.b1 = self.tree1.branch
902
856
 
911
865
        bundle = self.get_valid_bundle('null:', 'white-1')
912
866
 
913
867
        # Modified
914
 
        with open('b1/trailing space ', 'ab') as f: f.write('add some text\n')
 
868
        open('b1/trailing space ', 'ab').write('add some text\n')
915
869
        self.tree1.commit('add text', rev_id='white-2')
916
870
 
917
871
        bundle = self.get_valid_bundle('white-1', 'white-2')
927
881
        self.tree1.commit('removed', rev_id='white-4')
928
882
 
929
883
        bundle = self.get_valid_bundle('white-3', 'white-4')
930
 
 
 
884
        
931
885
        # Now test a complet roll-up
932
886
        bundle = self.get_valid_bundle('null:', 'white-4')
933
887
 
946
900
                          timezone=19800, timestamp=1152544886.0)
947
901
 
948
902
        bundle = self.get_valid_bundle('null:', 'tz-1')
949
 
 
 
903
        
950
904
        rev = bundle.revisions[0]
951
905
        self.assertEqual('Mon 2006-07-10 20:51:26.000000000 +0530', rev.date)
952
906
        self.assertEqual(19800, rev.timezone)
959
913
        self.tree1.commit('message', rev_id='revid1')
960
914
        bundle = self.get_valid_bundle('null:', 'revid1')
961
915
        tree = self.get_bundle_tree(bundle, 'revid1')
962
 
        root_revision = tree.get_file_revision(tree.get_root_id())
963
 
        self.assertEqual('revid1', root_revision)
 
916
        self.assertEqual('revid1', tree.inventory.root.revision)
964
917
 
965
918
    def test_install_revisions(self):
966
919
        self.tree1 = self.make_branch_and_tree('b1')
1055
1008
        bundle = read_bundle(self.create_bundle_text('null:', 'rev2')[0])
1056
1009
        repo = self.make_repository('repo', format='dirstate-with-subtree')
1057
1010
        bundle.install_revisions(repo)
1058
 
        inv_text = repo._get_inventory_xml('rev2')
 
1011
        inv_text = repo.get_inventory_xml('rev2')
1059
1012
        self.assertNotContainsRe(inv_text, 'format="5"')
1060
1013
        self.assertContainsRe(inv_text, 'format="7"')
1061
1014
 
1062
 
    def make_repo_with_installed_revisions(self):
1063
 
        tree = self.make_simple_tree('knit')
1064
 
        tree.commit('hello', rev_id='rev1')
1065
 
        tree.commit('hello', rev_id='rev2')
1066
 
        bundle = read_bundle(self.create_bundle_text('null:', 'rev2')[0])
1067
 
        repo = self.make_repository('repo', format='dirstate-with-subtree')
1068
 
        bundle.install_revisions(repo)
1069
 
        return repo
1070
 
 
1071
1015
    def test_across_models(self):
1072
 
        repo = self.make_repo_with_installed_revisions()
 
1016
        tree = self.make_simple_tree('knit')
 
1017
        tree.commit('hello', rev_id='rev1')
 
1018
        tree.commit('hello', rev_id='rev2')
 
1019
        bundle = read_bundle(self.create_bundle_text('null:', 'rev2')[0])
 
1020
        repo = self.make_repository('repo', format='dirstate-with-subtree')
 
1021
        bundle.install_revisions(repo)
1073
1022
        inv = repo.get_inventory('rev2')
1074
1023
        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')]))
1081
 
 
1082
 
    def test_inv_hash_across_serializers(self):
1083
 
        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)
 
1024
        root_vf = repo.weave_store.get_weave(inv.root.file_id,
 
1025
                                             repo.get_transaction())
 
1026
        self.assertEqual(root_vf.versions(), ['rev1', 'rev2'])
1087
1027
 
1088
1028
    def test_across_models_incompatible(self):
1089
1029
        tree = self.make_simple_tree('dirstate-with-subtree')
1092
1032
        try:
1093
1033
            bundle = read_bundle(self.create_bundle_text('null:', 'rev1')[0])
1094
1034
        except errors.IncompatibleBundleFormat:
1095
 
            raise tests.TestSkipped("Format 0.8 doesn't work with knit3")
 
1035
            raise TestSkipped("Format 0.8 doesn't work with knit3")
1096
1036
        repo = self.make_repository('repo', format='knit')
1097
1037
        bundle.install_revisions(repo)
1098
1038
 
1119
1059
        try:
1120
1060
            bundle = read_bundle(self.create_bundle_text('null:', 'rev1')[0])
1121
1061
        except errors.IncompatibleBundleFormat:
1122
 
            raise tests.TestSkipped("Format 0.8 doesn't work with knit3")
 
1062
            raise TestSkipped("Format 0.8 doesn't work with knit3")
1123
1063
        if isinstance(bundle, v09.BundleInfo09):
1124
 
            raise tests.TestSkipped("Format 0.9 doesn't work with subtrees")
 
1064
            raise TestSkipped("Format 0.9 doesn't work with subtrees")
1125
1065
        repo = self.make_repository('repo', format='knit')
1126
1066
        self.assertRaises(errors.IncompatibleRevision,
1127
1067
                          bundle.install_revisions, repo)
1134
1074
        try:
1135
1075
            self.tree1.commit('Revision/id/with/slashes', rev_id='rev/id')
1136
1076
        except ValueError:
1137
 
            raise tests.TestSkipped(
1138
 
                "Repository doesn't support revision ids with slashes")
 
1077
            raise TestSkipped("Repository doesn't support revision ids with"
 
1078
                              " slashes")
1139
1079
        bundle = self.get_valid_bundle('null:', 'rev/id')
1140
1080
 
1141
1081
    def test_skip_file(self):
1157
1097
        self.tree1.commit('rev3', rev_id='rev3')
1158
1098
        bundle = self.get_valid_bundle('reva', 'rev3')
1159
1099
        if getattr(bundle, 'get_bundle_reader', None) is None:
1160
 
            raise tests.TestSkipped('Bundle format cannot provide reader')
 
1100
            raise TestSkipped('Bundle format cannot provide reader')
1161
1101
        # be sure that file1 comes before file2
1162
1102
        for b, m, k, r, f in bundle.get_bundle_reader().iter_records():
1163
1103
            if f == 'file3-id':
1166
1106
        bundle.install_revisions(target.branch.repository)
1167
1107
 
1168
1108
 
1169
 
class V08BundleTester(BundleTester, tests.TestCaseWithTransport):
 
1109
class V08BundleTester(BundleTester, TestCaseWithTransport):
1170
1110
 
1171
1111
    format = '0.8'
1172
1112
 
1305
1245
        return format
1306
1246
 
1307
1247
 
1308
 
class V4BundleTester(BundleTester, tests.TestCaseWithTransport):
 
1248
class V4BundleTester(BundleTester, TestCaseWithTransport):
1309
1249
 
1310
1250
    format = '4'
1311
1251
 
1313
1253
        """Create a bundle from base_rev_id -> rev_id in built-in branch.
1314
1254
        Make sure that the text generated is valid, and that it
1315
1255
        can be applied against the base, and generate the same information.
1316
 
 
1317
 
        :return: The in-memory bundle
 
1256
        
 
1257
        :return: The in-memory bundle 
1318
1258
        """
1319
1259
        bundle_txt, rev_ids = self.create_bundle_text(base_rev_id, rev_id)
1320
1260
 
1321
 
        # This should also validate the generated bundle
 
1261
        # This should also validate the generated bundle 
1322
1262
        bundle = read_bundle(bundle_txt)
1323
1263
        repository = self.b1.repository
1324
1264
        for bundle_rev in bundle.real_revisions:
1328
1268
            # it
1329
1269
            branch_rev = repository.get_revision(bundle_rev.revision_id)
1330
1270
            for a in ('inventory_sha1', 'revision_id', 'parent_ids',
1331
 
                      'timestamp', 'timezone', 'message', 'committer',
 
1271
                      'timestamp', 'timezone', 'message', 'committer', 
1332
1272
                      'parent_ids', 'properties'):
1333
 
                self.assertEqual(getattr(branch_rev, a),
 
1273
                self.assertEqual(getattr(branch_rev, a), 
1334
1274
                                 getattr(bundle_rev, a))
1335
 
            self.assertEqual(len(branch_rev.parent_ids),
 
1275
            self.assertEqual(len(branch_rev.parent_ids), 
1336
1276
                             len(bundle_rev.parent_ids))
1337
1277
        self.assertEqual(set(rev_ids),
1338
1278
                         set([r.revision_id for r in bundle.real_revisions]))
1352
1292
        new_text = self.get_raw(StringIO(''.join(bundle_txt)))
1353
1293
        new_text = new_text.replace('<file file_id="exe-1"',
1354
1294
                                    '<file executable="y" file_id="exe-1"')
1355
 
        new_text = new_text.replace('B260', 'B275')
 
1295
        new_text = new_text.replace('B222', 'B237')
1356
1296
        bundle_txt = StringIO()
1357
1297
        bundle_txt.write(serializer._get_bundle_header('4'))
1358
1298
        bundle_txt.write('\n')
1364
1304
 
1365
1305
    def create_bundle_text(self, base_rev_id, rev_id):
1366
1306
        bundle_txt = StringIO()
1367
 
        rev_ids = write_bundle(self.b1.repository, rev_id, base_rev_id,
 
1307
        rev_ids = write_bundle(self.b1.repository, rev_id, base_rev_id, 
1368
1308
                               bundle_txt, format=self.format)
1369
1309
        bundle_txt.seek(0)
1370
 
        self.assertEqual(bundle_txt.readline(),
 
1310
        self.assertEqual(bundle_txt.readline(), 
1371
1311
                         '# Bazaar revision bundle v%s\n' % self.format)
1372
1312
        self.assertEqual(bundle_txt.readline(), '#\n')
1373
1313
        rev = self.b1.repository.get_revision(rev_id)
1393
1333
        tree2 = self.make_branch_and_tree('target')
1394
1334
        target_repo = tree2.branch.repository
1395
1335
        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)
 
1336
        vf = target_repo.weave_store.get_weave('fileid-2',
 
1337
            target_repo.get_transaction())
 
1338
        self.assertEqual('contents1\nstatic\n', vf.get_text('rev1'))
 
1339
        self.assertEqual('contents2\nstatic\n', vf.get_text('rev2'))
1406
1340
        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',),)])
 
1341
        inventory_vf = target_repo.get_inventory_weave()
 
1342
        self.assertEqual(['rev1'], inventory_vf.get_parents('rev2'))
1412
1343
        self.assertEqual('changed file',
1413
1344
                         target_repo.get_revision('rev2').message)
1414
1345
 
1428
1359
        branch = tree_a.branch
1429
1360
        repo_a = branch.repository
1430
1361
        tree_a.commit("base", allow_pointless=True, rev_id='A')
1431
 
        self.assertFalse(branch.repository.has_signature_for_revision_id('A'))
 
1362
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
1432
1363
        try:
1433
1364
            from bzrlib.testament import Testament
1434
1365
            # monkey patch gpg signing mechanism
1435
1366
            bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
1436
 
            new_config = test_commit.MustSignConfig()
1437
 
            commit.Commit(config_stack=new_config).commit(message="base",
 
1367
            new_config = test_commit.MustSignConfig(branch)
 
1368
            commit.Commit(config=new_config).commit(message="base",
1438
1369
                                                    allow_pointless=True,
1439
1370
                                                    rev_id='B',
1440
1371
                                                    working_tree=tree_a)
1458
1389
        install_bundle(repo_b, serializer.read(s))
1459
1390
 
1460
1391
 
1461
 
class V4_2aBundleTester(V4BundleTester):
 
1392
class V4WeaveBundleTester(V4BundleTester):
1462
1393
 
1463
1394
    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)
 
1395
        return 'metaweave'
1653
1396
 
1654
1397
 
1655
1398
class MungedBundleTester(object):
1704
1447
        self.check_valid(bundle)
1705
1448
 
1706
1449
 
1707
 
class MungedBundleTesterV09(tests.TestCaseWithTransport, MungedBundleTester):
 
1450
class MungedBundleTesterV09(TestCaseWithTransport, MungedBundleTester):
1708
1451
 
1709
1452
    format = '0.9'
1710
1453
 
1742
1485
        self.check_valid(bundle)
1743
1486
 
1744
1487
 
1745
 
class MungedBundleTesterV4(tests.TestCaseWithTransport, MungedBundleTester):
 
1488
class MungedBundleTesterV4(TestCaseWithTransport, MungedBundleTester):
1746
1489
 
1747
1490
    format = '4'
1748
1491
 
1749
1492
 
1750
 
class TestBundleWriterReader(tests.TestCase):
 
1493
class TestBundleWriterReader(TestCase):
1751
1494
 
1752
1495
    def test_roundtrip_record(self):
1753
1496
        fileobj = StringIO()
1815
1558
        record = record_iter.next()
1816
1559
        self.assertEqual((None, {'foo': 'bar', 'storage_kind': 'header'},
1817
1560
            'info', None, None), record)
1818
 
        self.assertRaises(errors.BadBundle, record_iter.next)
1819
 
 
1820
 
 
1821
 
class TestReadMergeableFromUrl(tests.TestCaseWithTransport):
1822
 
 
1823
 
    def test_read_mergeable_skips_local(self):
1824
 
        """A local bundle named like the URL should not be read.
1825
 
        """
1826
 
        out, wt = test_read_bundle.create_bundle_file(self)
1827
 
        class FooService(object):
1828
 
            """A directory service that always returns source"""
1829
 
 
1830
 
            def look_up(self, name, url):
1831
 
                return 'source'
1832
 
        directories.register('foo:', FooService, 'Testing directory service')
1833
 
        self.addCleanup(directories.remove, 'foo:')
1834
 
        self.build_tree_contents([('./foo:bar', out.getvalue())])
1835
 
        self.assertRaises(errors.NotABundle, read_mergeable_from_url,
1836
 
                          '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
 
1561
        self.assertRaises(BadBundle, record_iter.next)