~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: 2007-10-24 12:49:17 UTC
  • mfrom: (2935.1.1 ianc-integration)
  • Revision ID: pqm@pqm.ubuntu.com-20071024124917-xb75eckyxx6vkrlg
Makefile fixes - hooks.html generation & allow python to be overridden (Ian Clatworthy)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004-2006 by 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
19
19
import sys
20
20
import tempfile
21
21
 
22
 
from bzrlib import inventory
23
 
from bzrlib.builtins import merge
 
22
from bzrlib import (
 
23
    bzrdir,
 
24
    errors,
 
25
    inventory,
 
26
    repository,
 
27
    revision as _mod_revision,
 
28
    treebuilder,
 
29
    )
24
30
from bzrlib.bzrdir import BzrDir
25
31
from bzrlib.bundle.apply_bundle import install_bundle, merge_bundle
26
32
from bzrlib.bundle.bundle_data import BundleTree
27
 
from bzrlib.bundle.serializer import write_bundle, read_bundle
 
33
from bzrlib.bundle.serializer import write_bundle, read_bundle, v09, v4
 
34
from bzrlib.bundle.serializer.v08 import BundleSerializerV08
 
35
from bzrlib.bundle.serializer.v09 import BundleSerializerV09
 
36
from bzrlib.bundle.serializer.v4 import BundleSerializerV4
28
37
from bzrlib.branch import Branch
29
38
from bzrlib.diff import internal_diff
30
39
from bzrlib.errors import (BzrError, TestamentMismatch, NotABundle, BadBundle, 
31
40
                           NoSuchFile,)
32
41
from bzrlib.merge import Merge3Merger
 
42
from bzrlib.repofmt import knitrepo
33
43
from bzrlib.osutils import has_symlinks, sha_file
34
44
from bzrlib.tests import (TestCaseInTempDir, TestCaseWithTransport,
35
 
                          TestCase, TestSkipped)
 
45
                          TestCase, TestSkipped, test_commit)
36
46
from bzrlib.transform import TreeTransform
37
 
from bzrlib.workingtree import WorkingTree
38
47
 
39
48
 
40
49
class MockTree(object):
301
310
            [inventory.ROOT_ID, 'a', 'b', 'd', 'e'])
302
311
 
303
312
 
304
 
class BundleTester(TestCaseWithTransport):
 
313
class BundleTester1(TestCaseWithTransport):
 
314
 
 
315
    def test_mismatched_bundle(self):
 
316
        format = bzrdir.BzrDirMetaFormat1()
 
317
        format.repository_format = knitrepo.RepositoryFormatKnit3()
 
318
        serializer = BundleSerializerV08('0.8')
 
319
        b = self.make_branch('.', format=format)
 
320
        self.assertRaises(errors.IncompatibleBundleFormat, serializer.write, 
 
321
                          b.repository, [], {}, StringIO())
 
322
 
 
323
    def test_matched_bundle(self):
 
324
        """Don't raise IncompatibleBundleFormat for knit2 and bundle0.9"""
 
325
        format = bzrdir.BzrDirMetaFormat1()
 
326
        format.repository_format = knitrepo.RepositoryFormatKnit3()
 
327
        serializer = BundleSerializerV09('0.9')
 
328
        b = self.make_branch('.', format=format)
 
329
        serializer.write(b.repository, [], {}, StringIO())
 
330
 
 
331
    def test_mismatched_model(self):
 
332
        """Try copying a bundle from knit2 to knit1"""
 
333
        format = bzrdir.BzrDirMetaFormat1()
 
334
        format.repository_format = knitrepo.RepositoryFormatKnit3()
 
335
        source = self.make_branch_and_tree('source', format=format)
 
336
        source.commit('one', rev_id='one-id')
 
337
        source.commit('two', rev_id='two-id')
 
338
        text = StringIO()
 
339
        write_bundle(source.branch.repository, 'two-id', 'null:', text,
 
340
                     format='0.9')
 
341
        text.seek(0)
 
342
 
 
343
        format = bzrdir.BzrDirMetaFormat1()
 
344
        format.repository_format = knitrepo.RepositoryFormatKnit1()
 
345
        target = self.make_branch('target', format=format)
 
346
        self.assertRaises(errors.IncompatibleRevision, install_bundle, 
 
347
                          target.repository, read_bundle(text))
 
348
 
 
349
 
 
350
class BundleTester(object):
 
351
 
 
352
    def bzrdir_format(self):
 
353
        format = bzrdir.BzrDirMetaFormat1()
 
354
        format.repository_format = knitrepo.RepositoryFormatKnit1()
 
355
        return format
 
356
 
 
357
    def make_branch_and_tree(self, path, format=None):
 
358
        if format is None:
 
359
            format = self.bzrdir_format()
 
360
        return TestCaseWithTransport.make_branch_and_tree(self, path, format)
 
361
 
 
362
    def make_branch(self, path, format=None):
 
363
        if format is None:
 
364
            format = self.bzrdir_format()
 
365
        return TestCaseWithTransport.make_branch(self, path, format)
305
366
 
306
367
    def create_bundle_text(self, base_rev_id, rev_id):
307
368
        bundle_txt = StringIO()
308
369
        rev_ids = write_bundle(self.b1.repository, rev_id, base_rev_id, 
309
 
                               bundle_txt)
 
370
                               bundle_txt, format=self.format)
310
371
        bundle_txt.seek(0)
311
372
        self.assertEqual(bundle_txt.readline(), 
312
 
                         '# Bazaar revision bundle v0.8\n')
 
373
                         '# Bazaar revision bundle v%s\n' % self.format)
313
374
        self.assertEqual(bundle_txt.readline(), '#\n')
314
375
 
315
376
        rev = self.b1.repository.get_revision(rev_id)
316
377
        self.assertEqual(bundle_txt.readline().decode('utf-8'),
317
378
                         u'# message:\n')
318
 
 
319
 
        open(',,bundle', 'wb').write(bundle_txt.getvalue())
320
379
        bundle_txt.seek(0)
321
380
        return bundle_txt, rev_ids
322
381
 
391
450
        else:
392
451
            if not os.path.exists(checkout_dir):
393
452
                os.mkdir(checkout_dir)
394
 
        tree = BzrDir.create_standalone_workingtree(checkout_dir)
 
453
        tree = self.make_branch_and_tree(checkout_dir)
395
454
        s = StringIO()
396
 
        ancestors = write_bundle(self.b1.repository, rev_id, None, s)
 
455
        ancestors = write_bundle(self.b1.repository, rev_id, 'null:', s,
 
456
                                 format=self.format)
397
457
        s.seek(0)
398
458
        assert isinstance(s.getvalue(), str), (
399
459
            "Bundle isn't a bytestring:\n %s..." % repr(s.getvalue())[:40])
418
478
                    continue
419
479
                self.assertEqual(old_file.read(),
420
480
                                 new.get_file(inventory_id).read())
421
 
        if rev_id is not None:
 
481
        if not _mod_revision.is_null(rev_id):
422
482
            rh = self.b1.revision_history()
423
483
            tree.branch.set_revision_history(rh[:rh.index(rev_id)+1])
424
484
            tree.update()
425
485
            delta = tree.changes_from(self.b1.repository.revision_tree(rev_id))
426
486
            self.assertFalse(delta.has_changed(),
427
 
                             'Working tree has modifications')
 
487
                             'Working tree has modifications: %s' % delta)
428
488
        return tree
429
489
 
430
490
    def valid_apply_bundle(self, base_rev_id, info, checkout_dir=None):
432
492
        sure everything matches the builtin branch.
433
493
        """
434
494
        to_tree = self.get_checkout(base_rev_id, checkout_dir=checkout_dir)
 
495
        original_parents = to_tree.get_parent_ids()
435
496
        repository = to_tree.branch.repository
 
497
        original_parents = to_tree.get_parent_ids()
436
498
        self.assertIs(repository.has_revision(base_rev_id), True)
437
499
        for rev in info.real_revisions:
438
500
            self.assert_(not repository.has_revision(rev.revision_id),
448
510
        self.assert_(to_tree.branch.repository.has_revision(info.target))
449
511
        # Do we also want to verify that all the texts have been added?
450
512
 
451
 
        self.assert_(info.target in to_tree.pending_merges())
452
 
 
 
513
        self.assertEqual(original_parents + [info.target],
 
514
            to_tree.get_parent_ids())
453
515
 
454
516
        rev = info.real_revisions[-1]
455
517
        base_tree = self.b1.repository.revision_tree(rev.revision_id)
483
545
        self.tree1.add('one')
484
546
        self.tree1.commit('add one', rev_id='a@cset-0-1')
485
547
 
486
 
        bundle = self.get_valid_bundle(None, 'a@cset-0-1')
487
 
        # FIXME: The current write_bundle api no longer supports
488
 
        #        setting a custom summary message
489
 
        #        We should re-introduce the ability, and update
490
 
        #        the tests to make sure it works.
491
 
        # bundle = self.get_valid_bundle(None, 'a@cset-0-1',
492
 
        #         message='With a specialized message')
 
548
        bundle = self.get_valid_bundle('null:', 'a@cset-0-1')
493
549
 
494
550
        # Make sure we can handle files with spaces, tabs, other
495
551
        # bogus characters
508
564
        tt = TreeTransform(self.tree1)
509
565
        tt.new_file('executable', tt.root, '#!/bin/sh\n', 'exe-1', True)
510
566
        tt.apply()
 
567
        # have to fix length of file-id so that we can predictably rewrite
 
568
        # a (length-prefixed) record containing it later.
 
569
        self.tree1.add('with space.txt', 'withspace-id')
511
570
        self.tree1.add([
512
 
                'with space.txt'
513
 
                , 'dir'
 
571
                  'dir'
514
572
                , 'dir/filein subdir.c'
515
573
                , 'dir/WithCaps.txt'
516
574
                , 'dir/ pre space'
525
583
        bundle = self.get_valid_bundle('a@cset-0-1', 'a@cset-0-2')
526
584
 
527
585
        # Check a rollup bundle 
528
 
        bundle = self.get_valid_bundle(None, 'a@cset-0-2')
 
586
        bundle = self.get_valid_bundle('null:', 'a@cset-0-2')
529
587
 
530
588
        # Now delete entries
531
589
        self.tree1.remove(
540
598
        self.tree1.commit('removed', rev_id='a@cset-0-3')
541
599
        
542
600
        bundle = self.get_valid_bundle('a@cset-0-2', 'a@cset-0-3')
543
 
        self.assertRaises(TestamentMismatch, self.get_invalid_bundle, 
544
 
                          'a@cset-0-2', 'a@cset-0-3')
 
601
        self.assertRaises((TestamentMismatch,
 
602
            errors.VersionedFileInvalidChecksum), self.get_invalid_bundle,
 
603
            'a@cset-0-2', 'a@cset-0-3')
545
604
        # Check a rollup bundle 
546
 
        bundle = self.get_valid_bundle(None, 'a@cset-0-3')
 
605
        bundle = self.get_valid_bundle('null:', 'a@cset-0-3')
547
606
 
548
607
        # Now move the directory
549
608
        self.tree1.rename_one('dir', 'sub/dir')
551
610
 
552
611
        bundle = self.get_valid_bundle('a@cset-0-3', 'a@cset-0-4')
553
612
        # Check a rollup bundle 
554
 
        bundle = self.get_valid_bundle(None, 'a@cset-0-4')
 
613
        bundle = self.get_valid_bundle('null:', 'a@cset-0-4')
555
614
 
556
615
        # Modified files
557
616
        open('b1/sub/dir/WithCaps.txt', 'ab').write('\nAdding some text\n')
558
 
        open('b1/sub/dir/ pre space', 'ab').write('\r\nAdding some\r\nDOS format lines\r\n')
 
617
        open('b1/sub/dir/ pre space', 'ab').write(
 
618
             '\r\nAdding some\r\nDOS format lines\r\n')
559
619
        open('b1/sub/dir/nolastnewline.txt', 'ab').write('\n')
560
620
        self.tree1.rename_one('sub/dir/ pre space', 
561
621
                              'sub/ start space')
569
629
                          verbose=False)
570
630
        bundle = self.get_valid_bundle('a@cset-0-5', 'a@cset-0-6')
571
631
        other = self.get_checkout('a@cset-0-5')
 
632
        tree1_inv = self.tree1.branch.repository.get_inventory_xml(
 
633
            'a@cset-0-5')
 
634
        tree2_inv = other.branch.repository.get_inventory_xml('a@cset-0-5')
 
635
        self.assertEqualDiff(tree1_inv, tree2_inv)
572
636
        other.rename_one('sub/dir/nolastnewline.txt', 'sub/nolastnewline.txt')
573
637
        other.commit('rename file', rev_id='a@cset-0-6b')
574
 
        merge([other.basedir, -1], [None, None], this_dir=self.tree1.basedir)
 
638
        self.tree1.merge_from_branch(other.branch)
575
639
        self.tree1.commit(u'Merge', rev_id='a@cset-0-7',
576
640
                          verbose=False)
577
641
        bundle = self.get_valid_bundle('a@cset-0-6', 'a@cset-0-7')
579
643
    def test_symlink_bundle(self):
580
644
        if not has_symlinks():
581
645
            raise TestSkipped("No symlink support")
582
 
        self.tree1 = BzrDir.create_standalone_workingtree('b1')
 
646
        self.tree1 = self.make_branch_and_tree('b1')
583
647
        self.b1 = self.tree1.branch
584
648
        tt = TreeTransform(self.tree1)
585
649
        tt.new_symlink('link', tt.root, 'bar/foo', 'link-1')
586
650
        tt.apply()
587
651
        self.tree1.commit('add symlink', rev_id='l@cset-0-1')
588
 
        self.get_valid_bundle(None, 'l@cset-0-1')
 
652
        self.get_valid_bundle('null:', 'l@cset-0-1')
589
653
        tt = TreeTransform(self.tree1)
590
654
        trans_id = tt.trans_id_tree_file_id('link-1')
591
655
        tt.adjust_path('link2', tt.root, trans_id)
609
673
        self.get_valid_bundle('l@cset-0-3', 'l@cset-0-4')
610
674
 
611
675
    def test_binary_bundle(self):
612
 
        self.tree1 = BzrDir.create_standalone_workingtree('b1')
 
676
        self.tree1 = self.make_branch_and_tree('b1')
613
677
        self.b1 = self.tree1.branch
614
678
        tt = TreeTransform(self.tree1)
615
679
        
616
680
        # Add
617
681
        tt.new_file('file', tt.root, '\x00\n\x00\r\x01\n\x02\r\xff', 'binary-1')
618
 
        tt.new_file('file2', tt.root, '\x01\n\x02\r\x03\n\x04\r\xff', 'binary-2')
 
682
        tt.new_file('file2', tt.root, '\x01\n\x02\r\x03\n\x04\r\xff',
 
683
            'binary-2')
619
684
        tt.apply()
620
685
        self.tree1.commit('add binary', rev_id='b@cset-0-1')
621
 
        self.get_valid_bundle(None, 'b@cset-0-1')
 
686
        self.get_valid_bundle('null:', 'b@cset-0-1')
622
687
 
623
688
        # Delete
624
689
        tt = TreeTransform(self.tree1)
648
713
        self.get_valid_bundle('b@cset-0-3', 'b@cset-0-4')
649
714
 
650
715
        # Rollup
651
 
        self.get_valid_bundle(None, 'b@cset-0-4')
 
716
        self.get_valid_bundle('null:', 'b@cset-0-4')
652
717
 
653
718
    def test_last_modified(self):
654
 
        self.tree1 = BzrDir.create_standalone_workingtree('b1')
 
719
        self.tree1 = self.make_branch_and_tree('b1')
655
720
        self.b1 = self.tree1.branch
656
721
        tt = TreeTransform(self.tree1)
657
722
        tt.new_file('file', tt.root, 'file', 'file')
672
737
        tt.create_file('file2', trans_id)
673
738
        tt.apply()
674
739
        other.commit('modify text in another tree', rev_id='a@lmod-0-2b')
675
 
        merge([other.basedir, -1], [None, None], this_dir=self.tree1.basedir)
 
740
        self.tree1.merge_from_branch(other.branch)
676
741
        self.tree1.commit(u'Merge', rev_id='a@lmod-0-3',
677
742
                          verbose=False)
678
743
        self.tree1.commit(u'Merge', rev_id='a@lmod-0-4')
679
744
        bundle = self.get_valid_bundle('a@lmod-0-2a', 'a@lmod-0-4')
680
745
 
681
746
    def test_hide_history(self):
682
 
        self.tree1 = BzrDir.create_standalone_workingtree('b1')
 
747
        self.tree1 = self.make_branch_and_tree('b1')
683
748
        self.b1 = self.tree1.branch
684
749
 
685
750
        open('b1/one', 'wb').write('one\n')
691
756
        self.tree1.commit('modify', rev_id='a@cset-0-3')
692
757
        bundle_file = StringIO()
693
758
        rev_ids = write_bundle(self.tree1.branch.repository, 'a@cset-0-3',
 
759
                               'a@cset-0-1', bundle_file, format=self.format)
 
760
        self.assertNotContainsRe(bundle_file.getvalue(), '\btwo\b')
 
761
        self.assertContainsRe(self.get_raw(bundle_file), 'one')
 
762
        self.assertContainsRe(self.get_raw(bundle_file), 'three')
 
763
 
 
764
    def test_bundle_same_basis(self):
 
765
        """Ensure using the basis as the target doesn't cause an error"""
 
766
        self.tree1 = self.make_branch_and_tree('b1')
 
767
        self.tree1.commit('add file', rev_id='a@cset-0-1')
 
768
        bundle_file = StringIO()
 
769
        rev_ids = write_bundle(self.tree1.branch.repository, 'a@cset-0-1',
694
770
                               'a@cset-0-1', bundle_file)
695
 
        self.assertNotContainsRe(bundle_file.getvalue(), 'two')
696
 
        self.assertContainsRe(bundle_file.getvalue(), 'one')
697
 
        self.assertContainsRe(bundle_file.getvalue(), 'three')
 
771
 
 
772
    @staticmethod
 
773
    def get_raw(bundle_file):
 
774
        return bundle_file.getvalue()
698
775
 
699
776
    def test_unicode_bundle(self):
700
777
        # Handle international characters
712
789
            u'William Dod\xe9\n').encode('utf-8'))
713
790
        f.close()
714
791
 
715
 
        self.tree1.add([u'with Dod\xe9'])
716
 
        self.tree1.commit(u'i18n commit from William Dod\xe9', 
 
792
        self.tree1.add([u'with Dod\xe9'], ['withdod-id'])
 
793
        self.tree1.commit(u'i18n commit from William Dod\xe9',
717
794
                          rev_id='i18n-1', committer=u'William Dod\xe9')
718
795
 
 
796
        if sys.platform == 'darwin':
 
797
            from bzrlib.workingtree import WorkingTree3
 
798
            if type(self.tree1) is WorkingTree3:
 
799
                self.knownFailure("Bug #141438: fails for WorkingTree3 on OSX")
 
800
 
 
801
            # On Mac the '\xe9' gets changed to 'e\u0301'
 
802
            self.assertEqual([u'.bzr', u'with Dode\u0301'],
 
803
                             sorted(os.listdir(u'b1')))
 
804
            delta = self.tree1.changes_from(self.tree1.basis_tree())
 
805
            self.assertEqual([(u'with Dod\xe9', 'withdod-id', 'file')],
 
806
                             delta.removed)
 
807
            self.knownFailure("Mac OSX doesn't preserve unicode"
 
808
                              " combining characters.")
 
809
 
719
810
        # Add
720
 
        bundle = self.get_valid_bundle(None, 'i18n-1')
 
811
        bundle = self.get_valid_bundle('null:', 'i18n-1')
721
812
 
722
813
        # Modified
723
814
        f = open(u'b1/with Dod\xe9', 'wb')
741
832
        bundle = self.get_valid_bundle('i18n-3', 'i18n-4')
742
833
 
743
834
        # Rollup
744
 
        bundle = self.get_valid_bundle(None, 'i18n-4')
 
835
        bundle = self.get_valid_bundle('null:', 'i18n-4')
745
836
 
746
837
 
747
838
    def test_whitespace_bundle(self):
759
850
        # Added
760
851
        self.tree1.commit('funky whitespace', rev_id='white-1')
761
852
 
762
 
        bundle = self.get_valid_bundle(None, 'white-1')
 
853
        bundle = self.get_valid_bundle('null:', 'white-1')
763
854
 
764
855
        # Modified
765
856
        open('b1/trailing space ', 'ab').write('add some text\n')
780
871
        bundle = self.get_valid_bundle('white-3', 'white-4')
781
872
        
782
873
        # Now test a complet roll-up
783
 
        bundle = self.get_valid_bundle(None, 'white-4')
 
874
        bundle = self.get_valid_bundle('null:', 'white-4')
784
875
 
785
876
    def test_alt_timezone_bundle(self):
786
 
        self.tree1 = self.make_branch_and_tree('b1')
 
877
        self.tree1 = self.make_branch_and_memory_tree('b1')
787
878
        self.b1 = self.tree1.branch
 
879
        builder = treebuilder.TreeBuilder()
788
880
 
789
 
        self.build_tree(['b1/newfile'])
790
 
        self.tree1.add(['newfile'])
 
881
        self.tree1.lock_write()
 
882
        builder.start_tree(self.tree1)
 
883
        builder.build(['newfile'])
 
884
        builder.finish_tree()
791
885
 
792
886
        # Asia/Colombo offset = 5 hours 30 minutes
793
887
        self.tree1.commit('non-hour offset timezone', rev_id='tz-1',
794
888
                          timezone=19800, timestamp=1152544886.0)
795
889
 
796
 
        bundle = self.get_valid_bundle(None, 'tz-1')
 
890
        bundle = self.get_valid_bundle('null:', 'tz-1')
797
891
        
798
892
        rev = bundle.revisions[0]
799
893
        self.assertEqual('Mon 2006-07-10 20:51:26.000000000 +0530', rev.date)
800
894
        self.assertEqual(19800, rev.timezone)
801
895
        self.assertEqual(1152544886.0, rev.timestamp)
 
896
        self.tree1.unlock()
802
897
 
803
898
    def test_bundle_root_id(self):
804
899
        self.tree1 = self.make_branch_and_tree('b1')
805
900
        self.b1 = self.tree1.branch
806
901
        self.tree1.commit('message', rev_id='revid1')
807
 
        bundle = self.get_valid_bundle(None, 'revid1')
808
 
        tree = bundle.revision_tree(self.b1.repository, 'revid1')
 
902
        bundle = self.get_valid_bundle('null:', 'revid1')
 
903
        tree = self.get_bundle_tree(bundle, 'revid1')
809
904
        self.assertEqual('revid1', tree.inventory.root.revision)
810
905
 
811
 
 
812
 
class MungedBundleTester(TestCaseWithTransport):
 
906
    def test_install_revisions(self):
 
907
        self.tree1 = self.make_branch_and_tree('b1')
 
908
        self.b1 = self.tree1.branch
 
909
        self.tree1.commit('message', rev_id='rev2a')
 
910
        bundle = self.get_valid_bundle('null:', 'rev2a')
 
911
        branch2 = self.make_branch('b2')
 
912
        self.assertFalse(branch2.repository.has_revision('rev2a'))
 
913
        target_revision = bundle.install_revisions(branch2.repository)
 
914
        self.assertTrue(branch2.repository.has_revision('rev2a'))
 
915
        self.assertEqual('rev2a', target_revision)
 
916
 
 
917
    def test_bundle_empty_property(self):
 
918
        """Test serializing revision properties with an empty value."""
 
919
        tree = self.make_branch_and_memory_tree('tree')
 
920
        tree.lock_write()
 
921
        self.addCleanup(tree.unlock)
 
922
        tree.add([''], ['TREE_ROOT'])
 
923
        tree.commit('One', revprops={'one':'two', 'empty':''}, rev_id='rev1')
 
924
        self.b1 = tree.branch
 
925
        bundle_sio, revision_ids = self.create_bundle_text('null:', 'rev1')
 
926
        bundle = read_bundle(bundle_sio)
 
927
        revision_info = bundle.revisions[0]
 
928
        self.assertEqual('rev1', revision_info.revision_id)
 
929
        rev = revision_info.as_revision()
 
930
        self.assertEqual({'branch-nick':'tree', 'empty':'', 'one':'two'},
 
931
                         rev.properties)
 
932
 
 
933
    def test_bundle_sorted_properties(self):
 
934
        """For stability the writer should write properties in sorted order."""
 
935
        tree = self.make_branch_and_memory_tree('tree')
 
936
        tree.lock_write()
 
937
        self.addCleanup(tree.unlock)
 
938
 
 
939
        tree.add([''], ['TREE_ROOT'])
 
940
        tree.commit('One', rev_id='rev1',
 
941
                    revprops={'a':'4', 'b':'3', 'c':'2', 'd':'1'})
 
942
        self.b1 = tree.branch
 
943
        bundle_sio, revision_ids = self.create_bundle_text('null:', 'rev1')
 
944
        bundle = read_bundle(bundle_sio)
 
945
        revision_info = bundle.revisions[0]
 
946
        self.assertEqual('rev1', revision_info.revision_id)
 
947
        rev = revision_info.as_revision()
 
948
        self.assertEqual({'branch-nick':'tree', 'a':'4', 'b':'3', 'c':'2',
 
949
                          'd':'1'}, rev.properties)
 
950
 
 
951
    def test_bundle_unicode_properties(self):
 
952
        """We should be able to round trip a non-ascii property."""
 
953
        tree = self.make_branch_and_memory_tree('tree')
 
954
        tree.lock_write()
 
955
        self.addCleanup(tree.unlock)
 
956
 
 
957
        tree.add([''], ['TREE_ROOT'])
 
958
        # Revisions themselves do not require anything about revision property
 
959
        # keys, other than that they are a basestring, and do not contain
 
960
        # whitespace.
 
961
        # However, Testaments assert than they are str(), and thus should not
 
962
        # be Unicode.
 
963
        tree.commit('One', rev_id='rev1',
 
964
                    revprops={'omega':u'\u03a9', 'alpha':u'\u03b1'})
 
965
        self.b1 = tree.branch
 
966
        bundle_sio, revision_ids = self.create_bundle_text('null:', 'rev1')
 
967
        bundle = read_bundle(bundle_sio)
 
968
        revision_info = bundle.revisions[0]
 
969
        self.assertEqual('rev1', revision_info.revision_id)
 
970
        rev = revision_info.as_revision()
 
971
        self.assertEqual({'branch-nick':'tree', 'omega':u'\u03a9',
 
972
                          'alpha':u'\u03b1'}, rev.properties)
 
973
 
 
974
    def test_bundle_with_ghosts(self):
 
975
        tree = self.make_branch_and_tree('tree')
 
976
        self.b1 = tree.branch
 
977
        self.build_tree_contents([('tree/file', 'content1')])
 
978
        tree.add(['file'])
 
979
        tree.commit('rev1')
 
980
        self.build_tree_contents([('tree/file', 'content2')])
 
981
        tree.add_parent_tree_id('ghost')
 
982
        tree.commit('rev2', rev_id='rev2')
 
983
        bundle = self.get_valid_bundle('null:', 'rev2')
 
984
 
 
985
    def make_simple_tree(self, format=None):
 
986
        tree = self.make_branch_and_tree('b1', format=format)
 
987
        self.b1 = tree.branch
 
988
        self.build_tree(['b1/file'])
 
989
        tree.add('file')
 
990
        return tree
 
991
 
 
992
    def test_across_serializers(self):
 
993
        tree = self.make_simple_tree('knit')
 
994
        tree.commit('hello', rev_id='rev1')
 
995
        tree.commit('hello', rev_id='rev2')
 
996
        bundle = read_bundle(self.create_bundle_text('null:', 'rev2')[0])
 
997
        repo = self.make_repository('repo', format='dirstate-with-subtree')
 
998
        bundle.install_revisions(repo)
 
999
        inv_text = repo.get_inventory_xml('rev2')
 
1000
        self.assertNotContainsRe(inv_text, 'format="5"')
 
1001
        self.assertContainsRe(inv_text, 'format="7"')
 
1002
 
 
1003
    def test_across_models(self):
 
1004
        tree = self.make_simple_tree('knit')
 
1005
        tree.commit('hello', rev_id='rev1')
 
1006
        tree.commit('hello', rev_id='rev2')
 
1007
        bundle = read_bundle(self.create_bundle_text('null:', 'rev2')[0])
 
1008
        repo = self.make_repository('repo', format='dirstate-with-subtree')
 
1009
        bundle.install_revisions(repo)
 
1010
        inv = repo.get_inventory('rev2')
 
1011
        self.assertEqual('rev2', inv.root.revision)
 
1012
        root_vf = repo.weave_store.get_weave(inv.root.file_id,
 
1013
                                             repo.get_transaction())
 
1014
        self.assertEqual(root_vf.versions(), ['rev1', 'rev2'])
 
1015
 
 
1016
    def test_across_models_incompatible(self):
 
1017
        tree = self.make_simple_tree('dirstate-with-subtree')
 
1018
        tree.commit('hello', rev_id='rev1')
 
1019
        tree.commit('hello', rev_id='rev2')
 
1020
        try:
 
1021
            bundle = read_bundle(self.create_bundle_text('null:', 'rev1')[0])
 
1022
        except errors.IncompatibleBundleFormat:
 
1023
            raise TestSkipped("Format 0.8 doesn't work with knit3")
 
1024
        repo = self.make_repository('repo', format='knit')
 
1025
        bundle.install_revisions(repo)
 
1026
 
 
1027
        bundle = read_bundle(self.create_bundle_text('null:', 'rev2')[0])
 
1028
        self.assertRaises(errors.IncompatibleRevision,
 
1029
                          bundle.install_revisions, repo)
 
1030
 
 
1031
    def test_get_merge_request(self):
 
1032
        tree = self.make_simple_tree()
 
1033
        tree.commit('hello', rev_id='rev1')
 
1034
        tree.commit('hello', rev_id='rev2')
 
1035
        bundle = read_bundle(self.create_bundle_text('null:', 'rev1')[0])
 
1036
        result = bundle.get_merge_request(tree.branch.repository)
 
1037
        self.assertEqual((None, 'rev1', 'inapplicable'), result)
 
1038
 
 
1039
    def test_with_subtree(self):
 
1040
        tree = self.make_branch_and_tree('tree',
 
1041
                                         format='dirstate-with-subtree')
 
1042
        self.b1 = tree.branch
 
1043
        subtree = self.make_branch_and_tree('tree/subtree',
 
1044
                                            format='dirstate-with-subtree')
 
1045
        tree.add('subtree')
 
1046
        tree.commit('hello', rev_id='rev1')
 
1047
        try:
 
1048
            bundle = read_bundle(self.create_bundle_text('null:', 'rev1')[0])
 
1049
        except errors.IncompatibleBundleFormat:
 
1050
            raise TestSkipped("Format 0.8 doesn't work with knit3")
 
1051
        if isinstance(bundle, v09.BundleInfo09):
 
1052
            raise TestSkipped("Format 0.9 doesn't work with subtrees")
 
1053
        repo = self.make_repository('repo', format='knit')
 
1054
        self.assertRaises(errors.IncompatibleRevision,
 
1055
                          bundle.install_revisions, repo)
 
1056
        repo2 = self.make_repository('repo2', format='dirstate-with-subtree')
 
1057
        bundle.install_revisions(repo2)
 
1058
 
 
1059
    def test_revision_id_with_slash(self):
 
1060
        self.tree1 = self.make_branch_and_tree('tree')
 
1061
        self.b1 = self.tree1.branch
 
1062
        try:
 
1063
            self.tree1.commit('Revision/id/with/slashes', rev_id='rev/id')
 
1064
        except ValueError:
 
1065
            raise TestSkipped("Repository doesn't support revision ids with"
 
1066
                              " slashes")
 
1067
        bundle = self.get_valid_bundle('null:', 'rev/id')
 
1068
 
 
1069
    def test_skip_file(self):
 
1070
        """Make sure we don't accidentally write to the wrong versionedfile"""
 
1071
        self.tree1 = self.make_branch_and_tree('tree')
 
1072
        self.b1 = self.tree1.branch
 
1073
        # rev1 is not present in bundle, done by fetch
 
1074
        self.build_tree_contents([('tree/file2', 'contents1')])
 
1075
        self.tree1.add('file2', 'file2-id')
 
1076
        self.tree1.commit('rev1', rev_id='reva')
 
1077
        self.build_tree_contents([('tree/file3', 'contents2')])
 
1078
        # rev2 is present in bundle, and done by fetch
 
1079
        # having file1 in the bunle causes file1's versionedfile to be opened.
 
1080
        self.tree1.add('file3', 'file3-id')
 
1081
        self.tree1.commit('rev2')
 
1082
        # Updating file2 should not cause an attempt to add to file1's vf
 
1083
        target = self.tree1.bzrdir.sprout('target').open_workingtree()
 
1084
        self.build_tree_contents([('tree/file2', 'contents3')])
 
1085
        self.tree1.commit('rev3', rev_id='rev3')
 
1086
        bundle = self.get_valid_bundle('reva', 'rev3')
 
1087
        if getattr(bundle, 'get_bundle_reader', None) is None:
 
1088
            raise TestSkipped('Bundle format cannot provide reader')
 
1089
        # be sure that file1 comes before file2
 
1090
        for b, m, k, r, f in bundle.get_bundle_reader().iter_records():
 
1091
            if f == 'file3-id':
 
1092
                break
 
1093
            self.assertNotEqual(f, 'file2-id')
 
1094
        bundle.install_revisions(target.branch.repository)
 
1095
 
 
1096
 
 
1097
class V08BundleTester(BundleTester, TestCaseWithTransport):
 
1098
 
 
1099
    format = '0.8'
 
1100
 
 
1101
    def test_bundle_empty_property(self):
 
1102
        """Test serializing revision properties with an empty value."""
 
1103
        tree = self.make_branch_and_memory_tree('tree')
 
1104
        tree.lock_write()
 
1105
        self.addCleanup(tree.unlock)
 
1106
        tree.add([''], ['TREE_ROOT'])
 
1107
        tree.commit('One', revprops={'one':'two', 'empty':''}, rev_id='rev1')
 
1108
        self.b1 = tree.branch
 
1109
        bundle_sio, revision_ids = self.create_bundle_text('null:', 'rev1')
 
1110
        self.assertContainsRe(bundle_sio.getvalue(),
 
1111
                              '# properties:\n'
 
1112
                              '#   branch-nick: tree\n'
 
1113
                              '#   empty: \n'
 
1114
                              '#   one: two\n'
 
1115
                             )
 
1116
        bundle = read_bundle(bundle_sio)
 
1117
        revision_info = bundle.revisions[0]
 
1118
        self.assertEqual('rev1', revision_info.revision_id)
 
1119
        rev = revision_info.as_revision()
 
1120
        self.assertEqual({'branch-nick':'tree', 'empty':'', 'one':'two'},
 
1121
                         rev.properties)
 
1122
 
 
1123
    def get_bundle_tree(self, bundle, revision_id):
 
1124
        repository = self.make_repository('repo')
 
1125
        return bundle.revision_tree(repository, 'revid1')
 
1126
 
 
1127
    def test_bundle_empty_property_alt(self):
 
1128
        """Test serializing revision properties with an empty value.
 
1129
 
 
1130
        Older readers had a bug when reading an empty property.
 
1131
        They assumed that all keys ended in ': \n'. However they would write an
 
1132
        empty value as ':\n'. This tests make sure that all newer bzr versions
 
1133
        can handle th second form.
 
1134
        """
 
1135
        tree = self.make_branch_and_memory_tree('tree')
 
1136
        tree.lock_write()
 
1137
        self.addCleanup(tree.unlock)
 
1138
        tree.add([''], ['TREE_ROOT'])
 
1139
        tree.commit('One', revprops={'one':'two', 'empty':''}, rev_id='rev1')
 
1140
        self.b1 = tree.branch
 
1141
        bundle_sio, revision_ids = self.create_bundle_text('null:', 'rev1')
 
1142
        txt = bundle_sio.getvalue()
 
1143
        loc = txt.find('#   empty: ') + len('#   empty:')
 
1144
        # Create a new bundle, which strips the trailing space after empty
 
1145
        bundle_sio = StringIO(txt[:loc] + txt[loc+1:])
 
1146
 
 
1147
        self.assertContainsRe(bundle_sio.getvalue(),
 
1148
                              '# properties:\n'
 
1149
                              '#   branch-nick: tree\n'
 
1150
                              '#   empty:\n'
 
1151
                              '#   one: two\n'
 
1152
                             )
 
1153
        bundle = read_bundle(bundle_sio)
 
1154
        revision_info = bundle.revisions[0]
 
1155
        self.assertEqual('rev1', revision_info.revision_id)
 
1156
        rev = revision_info.as_revision()
 
1157
        self.assertEqual({'branch-nick':'tree', 'empty':'', 'one':'two'},
 
1158
                         rev.properties)
 
1159
 
 
1160
    def test_bundle_sorted_properties(self):
 
1161
        """For stability the writer should write properties in sorted order."""
 
1162
        tree = self.make_branch_and_memory_tree('tree')
 
1163
        tree.lock_write()
 
1164
        self.addCleanup(tree.unlock)
 
1165
 
 
1166
        tree.add([''], ['TREE_ROOT'])
 
1167
        tree.commit('One', rev_id='rev1',
 
1168
                    revprops={'a':'4', 'b':'3', 'c':'2', 'd':'1'})
 
1169
        self.b1 = tree.branch
 
1170
        bundle_sio, revision_ids = self.create_bundle_text('null:', 'rev1')
 
1171
        self.assertContainsRe(bundle_sio.getvalue(),
 
1172
                              '# properties:\n'
 
1173
                              '#   a: 4\n'
 
1174
                              '#   b: 3\n'
 
1175
                              '#   branch-nick: tree\n'
 
1176
                              '#   c: 2\n'
 
1177
                              '#   d: 1\n'
 
1178
                             )
 
1179
        bundle = read_bundle(bundle_sio)
 
1180
        revision_info = bundle.revisions[0]
 
1181
        self.assertEqual('rev1', revision_info.revision_id)
 
1182
        rev = revision_info.as_revision()
 
1183
        self.assertEqual({'branch-nick':'tree', 'a':'4', 'b':'3', 'c':'2',
 
1184
                          'd':'1'}, rev.properties)
 
1185
 
 
1186
    def test_bundle_unicode_properties(self):
 
1187
        """We should be able to round trip a non-ascii property."""
 
1188
        tree = self.make_branch_and_memory_tree('tree')
 
1189
        tree.lock_write()
 
1190
        self.addCleanup(tree.unlock)
 
1191
 
 
1192
        tree.add([''], ['TREE_ROOT'])
 
1193
        # Revisions themselves do not require anything about revision property
 
1194
        # keys, other than that they are a basestring, and do not contain
 
1195
        # whitespace.
 
1196
        # However, Testaments assert than they are str(), and thus should not
 
1197
        # be Unicode.
 
1198
        tree.commit('One', rev_id='rev1',
 
1199
                    revprops={'omega':u'\u03a9', 'alpha':u'\u03b1'})
 
1200
        self.b1 = tree.branch
 
1201
        bundle_sio, revision_ids = self.create_bundle_text('null:', 'rev1')
 
1202
        self.assertContainsRe(bundle_sio.getvalue(),
 
1203
                              '# properties:\n'
 
1204
                              '#   alpha: \xce\xb1\n'
 
1205
                              '#   branch-nick: tree\n'
 
1206
                              '#   omega: \xce\xa9\n'
 
1207
                             )
 
1208
        bundle = read_bundle(bundle_sio)
 
1209
        revision_info = bundle.revisions[0]
 
1210
        self.assertEqual('rev1', revision_info.revision_id)
 
1211
        rev = revision_info.as_revision()
 
1212
        self.assertEqual({'branch-nick':'tree', 'omega':u'\u03a9',
 
1213
                          'alpha':u'\u03b1'}, rev.properties)
 
1214
 
 
1215
 
 
1216
class V09BundleKnit2Tester(V08BundleTester):
 
1217
 
 
1218
    format = '0.9'
 
1219
 
 
1220
    def bzrdir_format(self):
 
1221
        format = bzrdir.BzrDirMetaFormat1()
 
1222
        format.repository_format = knitrepo.RepositoryFormatKnit3()
 
1223
        return format
 
1224
 
 
1225
 
 
1226
class V09BundleKnit1Tester(V08BundleTester):
 
1227
 
 
1228
    format = '0.9'
 
1229
 
 
1230
    def bzrdir_format(self):
 
1231
        format = bzrdir.BzrDirMetaFormat1()
 
1232
        format.repository_format = knitrepo.RepositoryFormatKnit1()
 
1233
        return format
 
1234
 
 
1235
 
 
1236
class V4BundleTester(BundleTester, TestCaseWithTransport):
 
1237
 
 
1238
    format = '4'
 
1239
 
 
1240
    def get_valid_bundle(self, base_rev_id, rev_id, checkout_dir=None):
 
1241
        """Create a bundle from base_rev_id -> rev_id in built-in branch.
 
1242
        Make sure that the text generated is valid, and that it
 
1243
        can be applied against the base, and generate the same information.
 
1244
        
 
1245
        :return: The in-memory bundle 
 
1246
        """
 
1247
        bundle_txt, rev_ids = self.create_bundle_text(base_rev_id, rev_id)
 
1248
 
 
1249
        # This should also validate the generated bundle 
 
1250
        bundle = read_bundle(bundle_txt)
 
1251
        repository = self.b1.repository
 
1252
        for bundle_rev in bundle.real_revisions:
 
1253
            # These really should have already been checked when we read the
 
1254
            # bundle, since it computes the sha1 hash for the revision, which
 
1255
            # only will match if everything is okay, but lets be explicit about
 
1256
            # it
 
1257
            branch_rev = repository.get_revision(bundle_rev.revision_id)
 
1258
            for a in ('inventory_sha1', 'revision_id', 'parent_ids',
 
1259
                      'timestamp', 'timezone', 'message', 'committer', 
 
1260
                      'parent_ids', 'properties'):
 
1261
                self.assertEqual(getattr(branch_rev, a), 
 
1262
                                 getattr(bundle_rev, a))
 
1263
            self.assertEqual(len(branch_rev.parent_ids), 
 
1264
                             len(bundle_rev.parent_ids))
 
1265
        self.assertEqual(set(rev_ids),
 
1266
                         set([r.revision_id for r in bundle.real_revisions]))
 
1267
        self.valid_apply_bundle(base_rev_id, bundle,
 
1268
                                   checkout_dir=checkout_dir)
 
1269
 
 
1270
        return bundle
 
1271
 
 
1272
    def get_invalid_bundle(self, base_rev_id, rev_id):
 
1273
        """Create a bundle from base_rev_id -> rev_id in built-in branch.
 
1274
        Munge the text so that it's invalid.
 
1275
 
 
1276
        :return: The in-memory bundle
 
1277
        """
 
1278
        from bzrlib.bundle import serializer
 
1279
        bundle_txt, rev_ids = self.create_bundle_text(base_rev_id, rev_id)
 
1280
        new_text = self.get_raw(StringIO(''.join(bundle_txt)))
 
1281
        new_text = new_text.replace('<file file_id="exe-1"',
 
1282
                                    '<file executable="y" file_id="exe-1"')
 
1283
        new_text = new_text.replace('B222', 'B237')
 
1284
        bundle_txt = StringIO()
 
1285
        bundle_txt.write(serializer._get_bundle_header('4'))
 
1286
        bundle_txt.write('\n')
 
1287
        bundle_txt.write(new_text.encode('bz2'))
 
1288
        bundle_txt.seek(0)
 
1289
        bundle = read_bundle(bundle_txt)
 
1290
        self.valid_apply_bundle(base_rev_id, bundle)
 
1291
        return bundle
 
1292
 
 
1293
    def create_bundle_text(self, base_rev_id, rev_id):
 
1294
        bundle_txt = StringIO()
 
1295
        rev_ids = write_bundle(self.b1.repository, rev_id, base_rev_id, 
 
1296
                               bundle_txt, format=self.format)
 
1297
        bundle_txt.seek(0)
 
1298
        self.assertEqual(bundle_txt.readline(), 
 
1299
                         '# Bazaar revision bundle v%s\n' % self.format)
 
1300
        self.assertEqual(bundle_txt.readline(), '#\n')
 
1301
        rev = self.b1.repository.get_revision(rev_id)
 
1302
        bundle_txt.seek(0)
 
1303
        return bundle_txt, rev_ids
 
1304
 
 
1305
    def get_bundle_tree(self, bundle, revision_id):
 
1306
        repository = self.make_repository('repo')
 
1307
        bundle.install_revisions(repository)
 
1308
        return repository.revision_tree(revision_id)
 
1309
 
 
1310
    def test_creation(self):
 
1311
        tree = self.make_branch_and_tree('tree')
 
1312
        self.build_tree_contents([('tree/file', 'contents1\nstatic\n')])
 
1313
        tree.add('file', 'fileid-2')
 
1314
        tree.commit('added file', rev_id='rev1')
 
1315
        self.build_tree_contents([('tree/file', 'contents2\nstatic\n')])
 
1316
        tree.commit('changed file', rev_id='rev2')
 
1317
        s = StringIO()
 
1318
        serializer = BundleSerializerV4('1.0')
 
1319
        serializer.write(tree.branch.repository, ['rev1', 'rev2'], {}, s)
 
1320
        s.seek(0)
 
1321
        tree2 = self.make_branch_and_tree('target')
 
1322
        target_repo = tree2.branch.repository
 
1323
        install_bundle(target_repo, serializer.read(s))
 
1324
        vf = target_repo.weave_store.get_weave('fileid-2',
 
1325
            target_repo.get_transaction())
 
1326
        self.assertEqual('contents1\nstatic\n', vf.get_text('rev1'))
 
1327
        self.assertEqual('contents2\nstatic\n', vf.get_text('rev2'))
 
1328
        rtree = target_repo.revision_tree('rev2')
 
1329
        inventory_vf = target_repo.get_inventory_weave()
 
1330
        self.assertEqual(['rev1'], inventory_vf.get_parents('rev2'))
 
1331
        self.assertEqual('changed file',
 
1332
                         target_repo.get_revision('rev2').message)
 
1333
 
 
1334
    @staticmethod
 
1335
    def get_raw(bundle_file):
 
1336
        bundle_file.seek(0)
 
1337
        line = bundle_file.readline()
 
1338
        line = bundle_file.readline()
 
1339
        lines = bundle_file.readlines()
 
1340
        return ''.join(lines).decode('bz2')
 
1341
 
 
1342
    def test_copy_signatures(self):
 
1343
        tree_a = self.make_branch_and_tree('tree_a')
 
1344
        import bzrlib.gpg
 
1345
        import bzrlib.commit as commit
 
1346
        oldstrategy = bzrlib.gpg.GPGStrategy
 
1347
        branch = tree_a.branch
 
1348
        repo_a = branch.repository
 
1349
        tree_a.commit("base", allow_pointless=True, rev_id='A')
 
1350
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
 
1351
        try:
 
1352
            from bzrlib.testament import Testament
 
1353
            # monkey patch gpg signing mechanism
 
1354
            bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
 
1355
            new_config = test_commit.MustSignConfig(branch)
 
1356
            commit.Commit(config=new_config).commit(message="base",
 
1357
                                                    allow_pointless=True,
 
1358
                                                    rev_id='B',
 
1359
                                                    working_tree=tree_a)
 
1360
            def sign(text):
 
1361
                return bzrlib.gpg.LoopbackGPGStrategy(None).sign(text)
 
1362
            self.assertTrue(repo_a.has_signature_for_revision_id('B'))
 
1363
        finally:
 
1364
            bzrlib.gpg.GPGStrategy = oldstrategy
 
1365
        tree_b = self.make_branch_and_tree('tree_b')
 
1366
        repo_b = tree_b.branch.repository
 
1367
        s = StringIO()
 
1368
        serializer = BundleSerializerV4('4')
 
1369
        serializer.write(tree_a.branch.repository, ['A', 'B'], {}, s)
 
1370
        s.seek(0)
 
1371
        install_bundle(repo_b, serializer.read(s))
 
1372
        self.assertTrue(repo_b.has_signature_for_revision_id('B'))
 
1373
        self.assertEqual(repo_b.get_signature_text('B'),
 
1374
                         repo_a.get_signature_text('B'))
 
1375
        s.seek(0)
 
1376
        # ensure repeat installs are harmless
 
1377
        install_bundle(repo_b, serializer.read(s))
 
1378
 
 
1379
 
 
1380
class V4WeaveBundleTester(V4BundleTester):
 
1381
 
 
1382
    def bzrdir_format(self):
 
1383
        return 'metaweave'
 
1384
 
 
1385
 
 
1386
class MungedBundleTester(object):
813
1387
 
814
1388
    def build_test_bundle(self):
815
1389
        wt = self.make_branch_and_tree('b1')
824
1398
 
825
1399
        bundle_txt = StringIO()
826
1400
        rev_ids = write_bundle(wt.branch.repository, 'a@cset-0-2',
827
 
                               'a@cset-0-1', bundle_txt)
828
 
        self.assertEqual(['a@cset-0-2'], rev_ids)
 
1401
                               'a@cset-0-1', bundle_txt, self.format)
 
1402
        self.assertEqual(set(['a@cset-0-2']), set(rev_ids))
829
1403
        bundle_txt.seek(0, 0)
830
1404
        return bundle_txt
831
1405
 
860
1434
        bundle = read_bundle(bundle_txt)
861
1435
        self.check_valid(bundle)
862
1436
 
 
1437
 
 
1438
class MungedBundleTesterV09(TestCaseWithTransport, MungedBundleTester):
 
1439
 
 
1440
    format = '0.9'
 
1441
 
863
1442
    def test_missing_trailing_whitespace(self):
864
1443
        bundle_txt = self.build_test_bundle()
865
1444
 
893
1472
        bundle = read_bundle(bundle_txt)
894
1473
        self.check_valid(bundle)
895
1474
 
 
1475
 
 
1476
class MungedBundleTesterV4(TestCaseWithTransport, MungedBundleTester):
 
1477
 
 
1478
    format = '4'
 
1479
 
 
1480
 
 
1481
class TestBundleWriterReader(TestCase):
 
1482
 
 
1483
    def test_roundtrip_record(self):
 
1484
        fileobj = StringIO()
 
1485
        writer = v4.BundleWriter(fileobj)
 
1486
        writer.begin()
 
1487
        writer.add_info_record(foo='bar')
 
1488
        writer._add_record("Record body", {'parents': ['1', '3'],
 
1489
            'storage_kind':'fulltext'}, 'file', 'revid', 'fileid')
 
1490
        writer.end()
 
1491
        fileobj.seek(0)
 
1492
        reader = v4.BundleReader(fileobj, stream_input=True)
 
1493
        record_iter = reader.iter_records()
 
1494
        record = record_iter.next()
 
1495
        self.assertEqual((None, {'foo': 'bar', 'storage_kind': 'header'},
 
1496
            'info', None, None), record)
 
1497
        record = record_iter.next()
 
1498
        self.assertEqual(("Record body", {'storage_kind': 'fulltext',
 
1499
                          'parents': ['1', '3']}, 'file', 'revid', 'fileid'),
 
1500
                          record)
 
1501
 
 
1502
    def test_roundtrip_record_memory_hungry(self):
 
1503
        fileobj = StringIO()
 
1504
        writer = v4.BundleWriter(fileobj)
 
1505
        writer.begin()
 
1506
        writer.add_info_record(foo='bar')
 
1507
        writer._add_record("Record body", {'parents': ['1', '3'],
 
1508
            'storage_kind':'fulltext'}, 'file', 'revid', 'fileid')
 
1509
        writer.end()
 
1510
        fileobj.seek(0)
 
1511
        reader = v4.BundleReader(fileobj, stream_input=False)
 
1512
        record_iter = reader.iter_records()
 
1513
        record = record_iter.next()
 
1514
        self.assertEqual((None, {'foo': 'bar', 'storage_kind': 'header'},
 
1515
            'info', None, None), record)
 
1516
        record = record_iter.next()
 
1517
        self.assertEqual(("Record body", {'storage_kind': 'fulltext',
 
1518
                          'parents': ['1', '3']}, 'file', 'revid', 'fileid'),
 
1519
                          record)
 
1520
 
 
1521
    def test_encode_name(self):
 
1522
        self.assertEqual('revision/rev1',
 
1523
            v4.BundleWriter.encode_name('revision', 'rev1'))
 
1524
        self.assertEqual('file/rev//1/file-id-1',
 
1525
            v4.BundleWriter.encode_name('file', 'rev/1', 'file-id-1'))
 
1526
        self.assertEqual('info',
 
1527
            v4.BundleWriter.encode_name('info', None, None))
 
1528
 
 
1529
    def test_decode_name(self):
 
1530
        self.assertEqual(('revision', 'rev1', None),
 
1531
            v4.BundleReader.decode_name('revision/rev1'))
 
1532
        self.assertEqual(('file', 'rev/1', 'file-id-1'),
 
1533
            v4.BundleReader.decode_name('file/rev//1/file-id-1'))
 
1534
        self.assertEqual(('info', None, None),
 
1535
                         v4.BundleReader.decode_name('info'))
 
1536
 
 
1537
    def test_too_many_names(self):
 
1538
        fileobj = StringIO()
 
1539
        writer = v4.BundleWriter(fileobj)
 
1540
        writer.begin()
 
1541
        writer.add_info_record(foo='bar')
 
1542
        writer._container.add_bytes_record('blah', ['two', 'names'])
 
1543
        writer.end()
 
1544
        fileobj.seek(0)
 
1545
        record_iter = v4.BundleReader(fileobj).iter_records()
 
1546
        record = record_iter.next()
 
1547
        self.assertEqual((None, {'foo': 'bar', 'storage_kind': 'header'},
 
1548
            'info', None, None), record)
 
1549
        self.assertRaises(BadBundle, record_iter.next)