~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: 2006-09-17 21:19:56 UTC
  • mfrom: (1997.1.6 bind-does-not-push-or-pull)
  • Revision ID: pqm@pqm.ubuntu.com-20060917211956-6e30d07da410fd1a
(Robert Collins) Change the Branch bind method to just bind rather than binding and pushing (fixes #43744 and #39542)

Show diffs side-by-side

added added

removed removed

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