~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_bundle.py

  • Committer: Robert Collins
  • Date: 2006-08-08 23:19:29 UTC
  • mfrom: (1884 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1912.
  • Revision ID: robertc@robertcollins.net-20060808231929-4e3e298190214b3a
current status

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
 
from StringIO import StringIO
 
17
from cStringIO import StringIO
 
18
import os
 
19
import sys
 
20
import tempfile
18
21
 
19
22
from bzrlib.builtins import merge
20
23
from bzrlib.bzrdir import BzrDir
21
24
from bzrlib.bundle.apply_bundle import install_bundle, merge_bundle
22
 
from bzrlib.bundle.read_bundle import BundleTree, BundleReader
23
 
from bzrlib.bundle.serializer import write_bundle
 
25
from bzrlib.bundle.bundle_data import BundleTree
 
26
from bzrlib.bundle.serializer import write_bundle, read_bundle
 
27
from bzrlib.branch import Branch
24
28
from bzrlib.diff import internal_diff
25
 
from bzrlib.errors import BzrError, TestamentMismatch, NotABundle
 
29
from bzrlib.delta import compare_trees
 
30
from bzrlib.errors import BzrError, TestamentMismatch, NotABundle, BadBundle
26
31
from bzrlib.merge import Merge3Merger
27
32
from bzrlib.osutils import has_symlinks, sha_file
28
 
from bzrlib.tests import TestCaseInTempDir, TestCase, TestSkipped
 
33
from bzrlib.tests import (TestCaseInTempDir, TestCaseWithTransport,
 
34
                          TestCase, TestSkipped)
29
35
from bzrlib.transform import TreeTransform
30
36
from bzrlib.workingtree import WorkingTree
31
37
 
51
57
            return self.make_entry(file_id, self.paths[file_id])
52
58
 
53
59
    def parent_id(self, file_id):
54
 
        from os.path import dirname
55
 
        parent_dir = dirname(self.paths[file_id])
 
60
        parent_dir = os.path.dirname(self.paths[file_id])
56
61
        if parent_dir == "":
57
62
            return None
58
63
        return self.ids[parent_dir]
69
74
        return kind
70
75
 
71
76
    def make_entry(self, file_id, path):
72
 
        from os.path import basename
73
77
        from bzrlib.inventory import (InventoryEntry, InventoryFile
74
78
                                    , InventoryDirectory, InventoryLink)
75
 
        name = basename(path)
 
79
        name = os.path.basename(path)
76
80
        kind = self.get_file_kind(file_id)
77
81
        parent_id = self.parent_id(file_id)
78
82
        text_sha_1, text_size = self.contents_stats(file_id)
294
298
        self.assertEqual(self.sorted_ids(btree), ['a', 'b', 'd', 'e'])
295
299
 
296
300
 
297
 
class CSetTester(TestCaseInTempDir):
298
 
 
299
 
    def get_valid_bundle(self, base_rev_id, rev_id, checkout_dir=None,
300
 
                         message=None):
301
 
        """Create a bundle from base_rev_id -> rev_id in built-in branch.
302
 
        Make sure that the text generated is valid, and that it
303
 
        can be applied against the base, and generate the same information.
304
 
        
305
 
        :return: The in-memory bundle 
306
 
        """
307
 
        from cStringIO import StringIO
308
 
 
 
301
class BundleTester(TestCaseWithTransport):
 
302
 
 
303
    def create_bundle_text(self, base_rev_id, rev_id):
309
304
        bundle_txt = StringIO()
310
305
        rev_ids = write_bundle(self.b1.repository, rev_id, base_rev_id, 
311
306
                               bundle_txt)
312
307
        bundle_txt.seek(0)
313
308
        self.assertEqual(bundle_txt.readline(), 
314
 
                         '# Bazaar revision bundle v0.7\n')
 
309
                         '# Bazaar revision bundle v0.8\n')
315
310
        self.assertEqual(bundle_txt.readline(), '#\n')
316
311
 
317
312
        rev = self.b1.repository.get_revision(rev_id)
320
315
 
321
316
        open(',,bundle', 'wb').write(bundle_txt.getvalue())
322
317
        bundle_txt.seek(0)
 
318
        return bundle_txt, rev_ids
 
319
 
 
320
    def get_valid_bundle(self, base_rev_id, rev_id, checkout_dir=None):
 
321
        """Create a bundle from base_rev_id -> rev_id in built-in branch.
 
322
        Make sure that the text generated is valid, and that it
 
323
        can be applied against the base, and generate the same information.
 
324
        
 
325
        :return: The in-memory bundle 
 
326
        """
 
327
        bundle_txt, rev_ids = self.create_bundle_text(base_rev_id, rev_id)
 
328
 
323
329
        # This should also validate the generated bundle 
324
 
        bundle = BundleReader(bundle_txt)
 
330
        bundle = read_bundle(bundle_txt)
325
331
        repository = self.b1.repository
326
 
        for bundle_rev in bundle.info.real_revisions:
 
332
        for bundle_rev in bundle.real_revisions:
327
333
            # These really should have already been checked when we read the
328
334
            # bundle, since it computes the sha1 hash for the revision, which
329
335
            # only will match if everything is okay, but lets be explicit about
337
343
            self.assertEqual(len(branch_rev.parent_ids), 
338
344
                             len(bundle_rev.parent_ids))
339
345
        self.assertEqual(rev_ids, 
340
 
                         [r.revision_id for r in bundle.info.real_revisions])
 
346
                         [r.revision_id for r in bundle.real_revisions])
341
347
        self.valid_apply_bundle(base_rev_id, bundle,
342
348
                                   checkout_dir=checkout_dir)
343
349
 
349
355
        
350
356
        :return: The in-memory bundle
351
357
        """
352
 
        from cStringIO import StringIO
353
 
 
354
 
        bundle_txt = StringIO()
355
 
        rev_ids = write_bundle(self.b1.repository, rev_id, base_rev_id, 
356
 
                               bundle_txt)
357
 
        bundle_txt.seek(0)
358
 
        open(',,bundle', 'wb').write(bundle_txt.getvalue())
359
 
        bundle_txt.seek(0)
 
358
        bundle_txt, rev_ids = self.create_bundle_text(base_rev_id, rev_id)
360
359
        new_text = bundle_txt.getvalue().replace('executable:no', 
361
360
                                               'executable:yes')
362
361
        bundle_txt = StringIO(new_text)
363
 
        bundle = BundleReader(bundle_txt)
 
362
        bundle = read_bundle(bundle_txt)
364
363
        self.valid_apply_bundle(base_rev_id, bundle)
365
364
        return bundle 
366
365
 
367
366
    def test_non_bundle(self):
368
 
        self.assertRaises(NotABundle, BundleReader, StringIO('#!/bin/sh\n'))
 
367
        self.assertRaises(NotABundle, read_bundle, StringIO('#!/bin/sh\n'))
 
368
 
 
369
    def test_malformed(self):
 
370
        self.assertRaises(BadBundle, read_bundle, 
 
371
                          StringIO('# Bazaar revision bundle v'))
 
372
 
 
373
    def test_crlf_bundle(self):
 
374
        try:
 
375
            read_bundle(StringIO('# Bazaar revision bundle v0.8\r\n'))
 
376
        except BadBundle:
 
377
            # It is currently permitted for bundles with crlf line endings to
 
378
            # make read_bundle raise a BadBundle, but this should be fixed.
 
379
            # Anything else, especially NotABundle, is an error.
 
380
            pass
369
381
 
370
382
    def get_checkout(self, rev_id, checkout_dir=None):
371
383
        """Get a new tree, with the specified revision in it.
372
384
        """
373
 
        from bzrlib.branch import Branch
374
 
        import tempfile
375
385
 
376
386
        if checkout_dir is None:
377
387
            checkout_dir = tempfile.mkdtemp(prefix='test-branch-', dir='.')
378
388
        else:
379
 
            import os
380
389
            if not os.path.exists(checkout_dir):
381
390
                os.mkdir(checkout_dir)
382
391
        tree = BzrDir.create_standalone_workingtree(checkout_dir)
385
394
        s.seek(0)
386
395
        assert isinstance(s.getvalue(), str), (
387
396
            "Bundle isn't a bytestring:\n %s..." % repr(s.getvalue())[:40])
388
 
        install_bundle(tree.branch.repository, BundleReader(s))
 
397
        install_bundle(tree.branch.repository, read_bundle(s))
389
398
        for ancestor in ancestors:
390
399
            old = self.b1.repository.revision_tree(ancestor)
391
400
            new = tree.branch.repository.revision_tree(ancestor)
 
401
 
 
402
            # Check that there aren't any inventory level changes
 
403
            delta = compare_trees(old, new)
 
404
            self.assertFalse(delta.has_changed(),
 
405
                             'Revision %s not copied correctly.'
 
406
                             % (ancestor,))
 
407
 
 
408
            # Now check that the file contents are all correct
392
409
            for inventory_id in old:
393
410
                try:
394
411
                    old_file = old.get_file(inventory_id)
402
419
            rh = self.b1.revision_history()
403
420
            tree.branch.set_revision_history(rh[:rh.index(rev_id)+1])
404
421
            tree.update()
 
422
            delta = compare_trees(self.b1.repository.revision_tree(rev_id),
 
423
                                  tree)
 
424
            self.assertFalse(delta.has_changed(),
 
425
                             'Working tree has modifications')
405
426
        return tree
406
427
 
407
 
    def valid_apply_bundle(self, base_rev_id, reader, checkout_dir=None):
 
428
    def valid_apply_bundle(self, base_rev_id, info, checkout_dir=None):
408
429
        """Get the base revision, apply the changes, and make
409
430
        sure everything matches the builtin branch.
410
431
        """
411
432
        to_tree = self.get_checkout(base_rev_id, checkout_dir=checkout_dir)
412
433
        repository = to_tree.branch.repository
413
434
        self.assertIs(repository.has_revision(base_rev_id), True)
414
 
        info = reader.info
415
435
        for rev in info.real_revisions:
416
436
            self.assert_(not repository.has_revision(rev.revision_id),
417
437
                'Revision {%s} present before applying bundle' 
418
438
                % rev.revision_id)
419
 
        merge_bundle(reader, to_tree, True, Merge3Merger, False, False)
 
439
        merge_bundle(info, to_tree, True, Merge3Merger, False, False)
420
440
 
421
441
        for rev in info.real_revisions:
422
442
            self.assert_(repository.has_revision(rev.revision_id),
454
474
            #         to_tree.get_file(fileid).read())
455
475
 
456
476
    def test_bundle(self):
457
 
 
458
 
        import os, sys
459
 
        pjoin = os.path.join
460
 
 
461
 
        self.tree1 = BzrDir.create_standalone_workingtree('b1')
 
477
        self.tree1 = self.make_branch_and_tree('b1')
462
478
        self.b1 = self.tree1.branch
463
479
 
464
 
        open(pjoin('b1/one'), 'wb').write('one\n')
 
480
        open('b1/one', 'wb').write('one\n')
465
481
        self.tree1.add('one')
466
482
        self.tree1.commit('add one', rev_id='a@cset-0-1')
467
483
 
468
484
        bundle = self.get_valid_bundle(None, 'a@cset-0-1')
469
 
        bundle = self.get_valid_bundle(None, 'a@cset-0-1',
470
 
                message='With a specialized message')
 
485
        # FIXME: The current write_bundle api no longer supports
 
486
        #        setting a custom summary message
 
487
        #        We should re-introduce the ability, and update
 
488
        #        the tests to make sure it works.
 
489
        # bundle = self.get_valid_bundle(None, 'a@cset-0-1',
 
490
        #         message='With a specialized message')
471
491
 
472
492
        # Make sure we can handle files with spaces, tabs, other
473
493
        # bogus characters
476
496
                , 'b1/dir/'
477
497
                , 'b1/dir/filein subdir.c'
478
498
                , 'b1/dir/WithCaps.txt'
479
 
                , 'b1/dir/trailing space '
 
499
                , 'b1/dir/ pre space'
480
500
                , 'b1/sub/'
481
501
                , 'b1/sub/sub/'
482
502
                , 'b1/sub/sub/nonempty.txt'
483
 
                # Tabs are not valid in filenames on windows
484
 
                #'b1/with\ttab.txt'
485
503
                ])
486
504
        open('b1/sub/sub/emptyfile.txt', 'wb').close()
487
505
        open('b1/dir/nolastnewline.txt', 'wb').write('bloop')
493
511
                , 'dir'
494
512
                , 'dir/filein subdir.c'
495
513
                , 'dir/WithCaps.txt'
496
 
                , 'dir/trailing space '
 
514
                , 'dir/ pre space'
497
515
                , 'dir/nolastnewline.txt'
498
516
                , 'sub'
499
517
                , 'sub/sub'
525
543
        # Check a rollup bundle 
526
544
        bundle = self.get_valid_bundle(None, 'a@cset-0-3')
527
545
 
528
 
 
529
546
        # Now move the directory
530
547
        self.tree1.rename_one('dir', 'sub/dir')
531
548
        self.tree1.commit('rename dir', rev_id='a@cset-0-4')
536
553
 
537
554
        # Modified files
538
555
        open('b1/sub/dir/WithCaps.txt', 'ab').write('\nAdding some text\n')
539
 
        open('b1/sub/dir/trailing space ', 'ab').write('\nAdding some\nDOS format lines\n')
 
556
        open('b1/sub/dir/ pre space', 'ab').write('\r\nAdding some\r\nDOS format lines\r\n')
540
557
        open('b1/sub/dir/nolastnewline.txt', 'ab').write('\n')
541
 
        self.tree1.rename_one('sub/dir/trailing space ', 
542
 
                              'sub/ start and end space ')
 
558
        self.tree1.rename_one('sub/dir/ pre space', 
 
559
                              'sub/ start space')
543
560
        self.tree1.commit('Modified files', rev_id='a@cset-0-5')
544
561
        bundle = self.get_valid_bundle('a@cset-0-4', 'a@cset-0-5')
545
562
 
546
 
        # Handle international characters
547
 
        try:
548
 
            f = open(u'b1/with Dod\xe9', 'wb')
549
 
        except UnicodeEncodeError:
550
 
            raise TestSkipped("Filesystem doesn't support unicode")
551
 
        f.write((u'A file\n'
552
 
            u'With international man of mystery\n'
553
 
            u'William Dod\xe9\n').encode('utf-8'))
554
 
        self.tree1.add([u'with Dod\xe9'])
555
 
        # BUG: (sort of) You must set verbose=False, so that python doesn't try
556
 
        #       and print the name of William Dode as part of the commit
557
 
        self.tree1.commit(u'i18n commit from William Dod\xe9', 
558
 
                          rev_id='a@cset-0-6', committer=u'William Dod\xe9',
559
 
                          verbose=False)
560
 
        bundle = self.get_valid_bundle('a@cset-0-5', 'a@cset-0-6')
561
563
        self.tree1.rename_one('sub/dir/WithCaps.txt', 'temp')
562
564
        self.tree1.rename_one('with space.txt', 'WithCaps.txt')
563
565
        self.tree1.rename_one('temp', 'with space.txt')
564
 
        self.tree1.commit(u'swap filenames', rev_id='a@cset-0-7',
 
566
        self.tree1.commit(u'swap filenames', rev_id='a@cset-0-6',
 
567
                          verbose=False)
 
568
        bundle = self.get_valid_bundle('a@cset-0-5', 'a@cset-0-6')
 
569
        other = self.get_checkout('a@cset-0-5')
 
570
        other.rename_one('sub/dir/nolastnewline.txt', 'sub/nolastnewline.txt')
 
571
        other.commit('rename file', rev_id='a@cset-0-6b')
 
572
        merge([other.basedir, -1], [None, None], this_dir=self.tree1.basedir)
 
573
        self.tree1.commit(u'Merge', rev_id='a@cset-0-7',
565
574
                          verbose=False)
566
575
        bundle = self.get_valid_bundle('a@cset-0-6', 'a@cset-0-7')
567
 
        other = self.get_checkout('a@cset-0-6')
568
 
        other.rename_one('sub/dir/nolastnewline.txt', 'sub/nolastnewline.txt')
569
 
        other.commit('rename file', rev_id='a@cset-0-7b')
570
 
        merge([other.basedir, -1], [None, None], this_dir=self.tree1.basedir)
571
 
        self.tree1.commit(u'Merge', rev_id='a@cset-0-8',
572
 
                          verbose=False)
573
 
        bundle = self.get_valid_bundle('a@cset-0-7', 'a@cset-0-8')
574
576
 
575
577
    def test_symlink_bundle(self):
576
578
        if not has_symlinks():
608
610
        self.tree1 = BzrDir.create_standalone_workingtree('b1')
609
611
        self.b1 = self.tree1.branch
610
612
        tt = TreeTransform(self.tree1)
611
 
        tt.new_file('file', tt.root, '\x00\xff', 'binary-1')
612
 
        tt.new_file('file2', tt.root, '\x00\xff', 'binary-2')
 
613
        
 
614
        # Add
 
615
        tt.new_file('file', tt.root, '\x00\n\x00\r\x01\n\x02\r\xff', 'binary-1')
 
616
        tt.new_file('file2', tt.root, '\x01\n\x02\r\x03\n\x04\r\xff', 'binary-2')
613
617
        tt.apply()
614
618
        self.tree1.commit('add binary', rev_id='b@cset-0-1')
615
619
        self.get_valid_bundle(None, 'b@cset-0-1')
 
620
 
 
621
        # Delete
616
622
        tt = TreeTransform(self.tree1)
617
623
        trans_id = tt.trans_id_tree_file_id('binary-1')
618
624
        tt.delete_contents(trans_id)
619
625
        tt.apply()
620
626
        self.tree1.commit('delete binary', rev_id='b@cset-0-2')
621
627
        self.get_valid_bundle('b@cset-0-1', 'b@cset-0-2')
 
628
 
 
629
        # Rename & modify
622
630
        tt = TreeTransform(self.tree1)
623
631
        trans_id = tt.trans_id_tree_file_id('binary-2')
624
632
        tt.adjust_path('file3', tt.root, trans_id)
625
633
        tt.delete_contents(trans_id)
626
 
        tt.create_file('filecontents\x00', trans_id)
 
634
        tt.create_file('file\rcontents\x00\n\x00', trans_id)
627
635
        tt.apply()
628
636
        self.tree1.commit('rename and modify binary', rev_id='b@cset-0-3')
629
637
        self.get_valid_bundle('b@cset-0-2', 'b@cset-0-3')
 
638
 
 
639
        # Modify
630
640
        tt = TreeTransform(self.tree1)
631
641
        trans_id = tt.trans_id_tree_file_id('binary-2')
632
642
        tt.delete_contents(trans_id)
633
 
        tt.create_file('\x00filecontents', trans_id)
 
643
        tt.create_file('\x00file\rcontents', trans_id)
634
644
        tt.apply()
635
645
        self.tree1.commit('just modify binary', rev_id='b@cset-0-4')
636
646
        self.get_valid_bundle('b@cset-0-3', 'b@cset-0-4')
637
647
 
 
648
        # Rollup
 
649
        self.get_valid_bundle(None, 'b@cset-0-4')
 
650
 
638
651
    def test_last_modified(self):
639
652
        self.tree1 = BzrDir.create_standalone_workingtree('b1')
640
653
        self.b1 = self.tree1.branch
662
675
                          verbose=False)
663
676
        self.tree1.commit(u'Merge', rev_id='a@lmod-0-4')
664
677
        bundle = self.get_valid_bundle('a@lmod-0-2a', 'a@lmod-0-4')
 
678
 
 
679
    def test_hide_history(self):
 
680
        self.tree1 = BzrDir.create_standalone_workingtree('b1')
 
681
        self.b1 = self.tree1.branch
 
682
 
 
683
        open('b1/one', 'wb').write('one\n')
 
684
        self.tree1.add('one')
 
685
        self.tree1.commit('add file', rev_id='a@cset-0-1')
 
686
        open('b1/one', 'wb').write('two\n')
 
687
        self.tree1.commit('modify', rev_id='a@cset-0-2')
 
688
        open('b1/one', 'wb').write('three\n')
 
689
        self.tree1.commit('modify', rev_id='a@cset-0-3')
 
690
        bundle_file = StringIO()
 
691
        rev_ids = write_bundle(self.tree1.branch.repository, 'a@cset-0-3',
 
692
                               'a@cset-0-1', bundle_file)
 
693
        self.assertNotContainsRe(bundle_file.getvalue(), 'two')
 
694
        self.assertContainsRe(bundle_file.getvalue(), 'one')
 
695
        self.assertContainsRe(bundle_file.getvalue(), 'three')
 
696
 
 
697
    def test_unicode_bundle(self):
 
698
        # Handle international characters
 
699
        os.mkdir('b1')
 
700
        try:
 
701
            f = open(u'b1/with Dod\xe9', 'wb')
 
702
        except UnicodeEncodeError:
 
703
            raise TestSkipped("Filesystem doesn't support unicode")
 
704
 
 
705
        self.tree1 = self.make_branch_and_tree('b1')
 
706
        self.b1 = self.tree1.branch
 
707
 
 
708
        f.write((u'A file\n'
 
709
            u'With international man of mystery\n'
 
710
            u'William Dod\xe9\n').encode('utf-8'))
 
711
        f.close()
 
712
 
 
713
        self.tree1.add([u'with Dod\xe9'])
 
714
        self.tree1.commit(u'i18n commit from William Dod\xe9', 
 
715
                          rev_id='i18n-1', committer=u'William Dod\xe9')
 
716
 
 
717
        # Add
 
718
        bundle = self.get_valid_bundle(None, 'i18n-1')
 
719
 
 
720
        # Modified
 
721
        f = open(u'b1/with Dod\xe9', 'wb')
 
722
        f.write(u'Modified \xb5\n'.encode('utf8'))
 
723
        f.close()
 
724
        self.tree1.commit(u'modified', rev_id='i18n-2')
 
725
 
 
726
        bundle = self.get_valid_bundle('i18n-1', 'i18n-2')
 
727
        
 
728
        # Renamed
 
729
        self.tree1.rename_one(u'with Dod\xe9', u'B\xe5gfors')
 
730
        self.tree1.commit(u'renamed, the new i18n man', rev_id='i18n-3',
 
731
                          committer=u'Erik B\xe5gfors')
 
732
 
 
733
        bundle = self.get_valid_bundle('i18n-2', 'i18n-3')
 
734
 
 
735
        # Removed
 
736
        self.tree1.remove([u'B\xe5gfors'])
 
737
        self.tree1.commit(u'removed', rev_id='i18n-4')
 
738
 
 
739
        bundle = self.get_valid_bundle('i18n-3', 'i18n-4')
 
740
 
 
741
        # Rollup
 
742
        bundle = self.get_valid_bundle(None, 'i18n-4')
 
743
 
 
744
 
 
745
    def test_whitespace_bundle(self):
 
746
        if sys.platform in ('win32', 'cygwin'):
 
747
            raise TestSkipped('Windows doesn\'t support filenames'
 
748
                              ' with tabs or trailing spaces')
 
749
        self.tree1 = self.make_branch_and_tree('b1')
 
750
        self.b1 = self.tree1.branch
 
751
 
 
752
        self.build_tree(['b1/trailing space '])
 
753
        self.tree1.add(['trailing space '])
 
754
        # TODO: jam 20060701 Check for handling files with '\t' characters
 
755
        #       once we actually support them
 
756
 
 
757
        # Added
 
758
        self.tree1.commit('funky whitespace', rev_id='white-1')
 
759
 
 
760
        bundle = self.get_valid_bundle(None, 'white-1')
 
761
 
 
762
        # Modified
 
763
        open('b1/trailing space ', 'ab').write('add some text\n')
 
764
        self.tree1.commit('add text', rev_id='white-2')
 
765
 
 
766
        bundle = self.get_valid_bundle('white-1', 'white-2')
 
767
 
 
768
        # Renamed
 
769
        self.tree1.rename_one('trailing space ', ' start and end space ')
 
770
        self.tree1.commit('rename', rev_id='white-3')
 
771
 
 
772
        bundle = self.get_valid_bundle('white-2', 'white-3')
 
773
 
 
774
        # Removed
 
775
        self.tree1.remove([' start and end space '])
 
776
        self.tree1.commit('removed', rev_id='white-4')
 
777
 
 
778
        bundle = self.get_valid_bundle('white-3', 'white-4')
 
779
        
 
780
        # Now test a complet roll-up
 
781
        bundle = self.get_valid_bundle(None, 'white-4')
 
782
 
 
783
    def test_alt_timezone_bundle(self):
 
784
        self.tree1 = self.make_branch_and_tree('b1')
 
785
        self.b1 = self.tree1.branch
 
786
 
 
787
        self.build_tree(['b1/newfile'])
 
788
        self.tree1.add(['newfile'])
 
789
 
 
790
        # Asia/Colombo offset = 5 hours 30 minutes
 
791
        self.tree1.commit('non-hour offset timezone', rev_id='tz-1',
 
792
                          timezone=19800, timestamp=1152544886.0)
 
793
 
 
794
        bundle = self.get_valid_bundle(None, 'tz-1')
 
795
        
 
796
        rev = bundle.revisions[0]
 
797
        self.assertEqual('Mon 2006-07-10 20:51:26.000000000 +0530', rev.date)
 
798
        self.assertEqual(19800, rev.timezone)
 
799
        self.assertEqual(1152544886.0, rev.timestamp)
 
800
 
 
801
 
 
802
class MungedBundleTester(TestCaseWithTransport):
 
803
 
 
804
    def build_test_bundle(self):
 
805
        wt = self.make_branch_and_tree('b1')
 
806
 
 
807
        self.build_tree(['b1/one'])
 
808
        wt.add('one')
 
809
        wt.commit('add one', rev_id='a@cset-0-1')
 
810
        self.build_tree(['b1/two'])
 
811
        wt.add('two')
 
812
        wt.commit('add two', rev_id='a@cset-0-2',
 
813
                  revprops={'branch-nick':'test'})
 
814
 
 
815
        bundle_txt = StringIO()
 
816
        rev_ids = write_bundle(wt.branch.repository, 'a@cset-0-2',
 
817
                               'a@cset-0-1', bundle_txt)
 
818
        self.assertEqual(['a@cset-0-2'], rev_ids)
 
819
        bundle_txt.seek(0, 0)
 
820
        return bundle_txt
 
821
 
 
822
    def check_valid(self, bundle):
 
823
        """Check that after whatever munging, the final object is valid."""
 
824
        self.assertEqual(['a@cset-0-2'],
 
825
            [r.revision_id for r in bundle.real_revisions])
 
826
 
 
827
    def test_extra_whitespace(self):
 
828
        bundle_txt = self.build_test_bundle()
 
829
 
 
830
        # Seek to the end of the file
 
831
        # Adding one extra newline used to give us
 
832
        # TypeError: float() argument must be a string or a number
 
833
        bundle_txt.seek(0, 2)
 
834
        bundle_txt.write('\n')
 
835
        bundle_txt.seek(0)
 
836
 
 
837
        bundle = read_bundle(bundle_txt)
 
838
        self.check_valid(bundle)
 
839
 
 
840
    def test_extra_whitespace_2(self):
 
841
        bundle_txt = self.build_test_bundle()
 
842
 
 
843
        # Seek to the end of the file
 
844
        # Adding two extra newlines used to give us
 
845
        # MalformedPatches: The first line of all patches should be ...
 
846
        bundle_txt.seek(0, 2)
 
847
        bundle_txt.write('\n\n')
 
848
        bundle_txt.seek(0)
 
849
 
 
850
        bundle = read_bundle(bundle_txt)
 
851
        self.check_valid(bundle)
 
852
 
 
853
    def test_missing_trailing_whitespace(self):
 
854
        bundle_txt = self.build_test_bundle()
 
855
 
 
856
        # Remove a trailing newline, it shouldn't kill the parser
 
857
        raw = bundle_txt.getvalue()
 
858
        # The contents of the bundle don't have to be this, but this
 
859
        # test is concerned with the exact case where the serializer
 
860
        # creates a blank line at the end, and fails if that
 
861
        # line is stripped
 
862
        self.assertEqual('\n\n', raw[-2:])
 
863
        bundle_txt = StringIO(raw[:-1])
 
864
 
 
865
        bundle = read_bundle(bundle_txt)
 
866
        self.check_valid(bundle)
 
867
 
 
868
    def test_opening_text(self):
 
869
        bundle_txt = self.build_test_bundle()
 
870
 
 
871
        bundle_txt = StringIO("Some random\nemail comments\n"
 
872
                              + bundle_txt.getvalue())
 
873
 
 
874
        bundle = read_bundle(bundle_txt)
 
875
        self.check_valid(bundle)
 
876
 
 
877
    def test_trailing_text(self):
 
878
        bundle_txt = self.build_test_bundle()
 
879
 
 
880
        bundle_txt = StringIO(bundle_txt.getvalue() +
 
881
                              "Some trailing\nrandom\ntext\n")
 
882
 
 
883
        bundle = read_bundle(bundle_txt)
 
884
        self.check_valid(bundle)
 
885