~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_bundle.py

  • Committer: Vincent Ladeuil
  • Date: 2009-06-22 12:52:39 UTC
  • mto: (4471.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 4472.
  • Revision ID: v.ladeuil+lp@free.fr-20090622125239-kabo9smxt9c3vnir
Use a consistent scheme for naming pyrex source files.

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
from cStringIO import StringIO
18
18
import os
 
19
import socket
19
20
import sys
20
 
import tempfile
 
21
import threading
21
22
 
22
23
from bzrlib import (
23
24
    bzrdir,
 
25
    diff,
24
26
    errors,
25
27
    inventory,
 
28
    merge,
 
29
    osutils,
26
30
    repository,
27
31
    revision as _mod_revision,
 
32
    tests,
28
33
    treebuilder,
29
34
    )
30
 
from bzrlib.bzrdir import BzrDir
31
35
from bzrlib.bundle import read_mergeable_from_url
32
36
from bzrlib.bundle.apply_bundle import install_bundle, merge_bundle
33
37
from bzrlib.bundle.bundle_data import BundleTree
 
38
from bzrlib.bzrdir import BzrDir
34
39
from bzrlib.directory_service import directories
35
40
from bzrlib.bundle.serializer import write_bundle, read_bundle, v09, v4
36
41
from bzrlib.bundle.serializer.v08 import BundleSerializerV08
37
42
from bzrlib.bundle.serializer.v09 import BundleSerializerV09
38
43
from bzrlib.bundle.serializer.v4 import BundleSerializerV4
39
44
from bzrlib.branch import Branch
40
 
from bzrlib.diff import internal_diff
41
 
from bzrlib.errors import (BzrError, TestamentMismatch, NotABundle, BadBundle, 
42
 
                           NoSuchFile,)
43
 
from bzrlib.merge import Merge3Merger
44
45
from bzrlib.repofmt import knitrepo
45
 
from bzrlib.osutils import sha_file, sha_string
46
46
from bzrlib.tests import (
47
 
    SymlinkFeature,
48
 
    TestCase,
49
 
    TestCaseInTempDir,
50
 
    TestCaseWithTransport,
51
 
    TestSkipped,
52
47
    test_read_bundle,
53
48
    test_commit,
54
49
    )
106
101
        elif kind == 'symlink':
107
102
            ie = InventoryLink(file_id, name, parent_id)
108
103
        else:
109
 
            raise BzrError('unknown kind %r' % kind)
 
104
            raise errors.BzrError('unknown kind %r' % kind)
110
105
        ie.text_sha1 = text_sha_1
111
106
        ie.text_size = text_size
112
107
        return ie
114
109
    def add_dir(self, file_id, path):
115
110
        self.paths[file_id] = path
116
111
        self.ids[path] = file_id
117
 
    
 
112
 
118
113
    def add_file(self, file_id, path, contents):
119
114
        self.add_dir(file_id, path)
120
115
        self.contents[file_id] = contents
137
132
    def contents_stats(self, file_id):
138
133
        if file_id not in self.contents:
139
134
            return None, None
140
 
        text_sha1 = sha_file(self.get_file(file_id))
 
135
        text_sha1 = osutils.sha_file(self.get_file(file_id))
141
136
        return text_sha1, len(self.contents[file_id])
142
137
 
143
138
 
144
 
class BTreeTester(TestCase):
 
139
class BTreeTester(tests.TestCase):
145
140
    """A simple unittest tester for the BundleTree class."""
146
141
 
147
142
    def make_tree_1(self):
151
146
        mtree.add_file("c", "grandparent/parent/file", "Hello\n")
152
147
        mtree.add_dir("d", "grandparent/alt_parent")
153
148
        return BundleTree(mtree, ''), mtree
154
 
        
 
149
 
155
150
    def test_renames(self):
156
151
        """Ensure that file renames have the proper effect on children"""
157
152
        btree = self.make_tree_1()[0]
158
153
        self.assertEqual(btree.old_path("grandparent"), "grandparent")
159
 
        self.assertEqual(btree.old_path("grandparent/parent"), 
 
154
        self.assertEqual(btree.old_path("grandparent/parent"),
160
155
                         "grandparent/parent")
161
156
        self.assertEqual(btree.old_path("grandparent/parent/file"),
162
157
                         "grandparent/parent/file")
202
197
        self.assertTrue(btree.path2id("grandparent2/parent") is None)
203
198
        self.assertTrue(btree.path2id("grandparent2/parent/file") is None)
204
199
 
205
 
        btree.note_rename("grandparent/parent/file", 
 
200
        btree.note_rename("grandparent/parent/file",
206
201
                          "grandparent2/parent2/file2")
207
202
        self.assertEqual(btree.id2path("a"), "grandparent2")
208
203
        self.assertEqual(btree.id2path("b"), "grandparent2/parent2")
217
212
    def test_moves(self):
218
213
        """Ensure that file moves have the proper effect on children"""
219
214
        btree = self.make_tree_1()[0]
220
 
        btree.note_rename("grandparent/parent/file", 
 
215
        btree.note_rename("grandparent/parent/file",
221
216
                          "grandparent/alt_parent/file")
222
217
        self.assertEqual(btree.id2path("c"), "grandparent/alt_parent/file")
223
218
        self.assertEqual(btree.path2id("grandparent/alt_parent/file"), "c")
225
220
 
226
221
    def unified_diff(self, old, new):
227
222
        out = StringIO()
228
 
        internal_diff("old", old, "new", new, out)
 
223
        diff.internal_diff("old", old, "new", new, out)
229
224
        out.seek(0,0)
230
225
        return out.read()
231
226
 
232
227
    def make_tree_2(self):
233
228
        btree = self.make_tree_1()[0]
234
 
        btree.note_rename("grandparent/parent/file", 
 
229
        btree.note_rename("grandparent/parent/file",
235
230
                          "grandparent/alt_parent/file")
236
231
        self.assertTrue(btree.id2path("e") is None)
237
232
        self.assertTrue(btree.path2id("grandparent/parent/file") is None)
266
261
    def make_tree_3(self):
267
262
        btree, mtree = self.make_tree_1()
268
263
        mtree.add_file("e", "grandparent/parent/topping", "Anchovies\n")
269
 
        btree.note_rename("grandparent/parent/file", 
 
264
        btree.note_rename("grandparent/parent/file",
270
265
                          "grandparent/alt_parent/file")
271
 
        btree.note_rename("grandparent/parent/topping", 
 
266
        btree.note_rename("grandparent/parent/topping",
272
267
                          "grandparent/alt_parent/stopping")
273
268
        return btree
274
269
 
313
308
            [inventory.ROOT_ID, 'a', 'b', 'c', 'd'])
314
309
        btree.note_deletion("grandparent/parent/file")
315
310
        btree.note_id("e", "grandparent/alt_parent/fool", kind="directory")
316
 
        btree.note_last_changed("grandparent/alt_parent/fool", 
 
311
        btree.note_last_changed("grandparent/alt_parent/fool",
317
312
                                "revisionidiguess")
318
313
        self.assertEqual(self.sorted_ids(btree),
319
314
            [inventory.ROOT_ID, 'a', 'b', 'd', 'e'])
320
315
 
321
316
 
322
 
class BundleTester1(TestCaseWithTransport):
 
317
class BundleTester1(tests.TestCaseWithTransport):
323
318
 
324
319
    def test_mismatched_bundle(self):
325
320
        format = bzrdir.BzrDirMetaFormat1()
326
321
        format.repository_format = knitrepo.RepositoryFormatKnit3()
327
322
        serializer = BundleSerializerV08('0.8')
328
323
        b = self.make_branch('.', format=format)
329
 
        self.assertRaises(errors.IncompatibleBundleFormat, serializer.write, 
 
324
        self.assertRaises(errors.IncompatibleBundleFormat, serializer.write,
330
325
                          b.repository, [], {}, StringIO())
331
326
 
332
327
    def test_matched_bundle(self):
352
347
        format = bzrdir.BzrDirMetaFormat1()
353
348
        format.repository_format = knitrepo.RepositoryFormatKnit1()
354
349
        target = self.make_branch('target', format=format)
355
 
        self.assertRaises(errors.IncompatibleRevision, install_bundle, 
 
350
        self.assertRaises(errors.IncompatibleRevision, install_bundle,
356
351
                          target.repository, read_bundle(text))
357
352
 
358
353
 
366
361
    def make_branch_and_tree(self, path, format=None):
367
362
        if format is None:
368
363
            format = self.bzrdir_format()
369
 
        return TestCaseWithTransport.make_branch_and_tree(self, path, format)
 
364
        return tests.TestCaseWithTransport.make_branch_and_tree(
 
365
            self, path, format)
370
366
 
371
367
    def make_branch(self, path, format=None):
372
368
        if format is None:
373
369
            format = self.bzrdir_format()
374
 
        return TestCaseWithTransport.make_branch(self, path, format)
 
370
        return tests.TestCaseWithTransport.make_branch(self, path, format)
375
371
 
376
372
    def create_bundle_text(self, base_rev_id, rev_id):
377
373
        bundle_txt = StringIO()
378
 
        rev_ids = write_bundle(self.b1.repository, rev_id, base_rev_id, 
 
374
        rev_ids = write_bundle(self.b1.repository, rev_id, base_rev_id,
379
375
                               bundle_txt, format=self.format)
380
376
        bundle_txt.seek(0)
381
 
        self.assertEqual(bundle_txt.readline(), 
 
377
        self.assertEqual(bundle_txt.readline(),
382
378
                         '# Bazaar revision bundle v%s\n' % self.format)
383
379
        self.assertEqual(bundle_txt.readline(), '#\n')
384
380
 
392
388
        """Create a bundle from base_rev_id -> rev_id in built-in branch.
393
389
        Make sure that the text generated is valid, and that it
394
390
        can be applied against the base, and generate the same information.
395
 
        
396
 
        :return: The in-memory bundle 
 
391
 
 
392
        :return: The in-memory bundle
397
393
        """
398
394
        bundle_txt, rev_ids = self.create_bundle_text(base_rev_id, rev_id)
399
395
 
400
 
        # This should also validate the generated bundle 
 
396
        # This should also validate the generated bundle
401
397
        bundle = read_bundle(bundle_txt)
402
398
        repository = self.b1.repository
403
399
        for bundle_rev in bundle.real_revisions:
407
403
            # it
408
404
            branch_rev = repository.get_revision(bundle_rev.revision_id)
409
405
            for a in ('inventory_sha1', 'revision_id', 'parent_ids',
410
 
                      'timestamp', 'timezone', 'message', 'committer', 
 
406
                      'timestamp', 'timezone', 'message', 'committer',
411
407
                      'parent_ids', 'properties'):
412
 
                self.assertEqual(getattr(branch_rev, a), 
 
408
                self.assertEqual(getattr(branch_rev, a),
413
409
                                 getattr(bundle_rev, a))
414
 
            self.assertEqual(len(branch_rev.parent_ids), 
 
410
            self.assertEqual(len(branch_rev.parent_ids),
415
411
                             len(bundle_rev.parent_ids))
416
 
        self.assertEqual(rev_ids, 
 
412
        self.assertEqual(rev_ids,
417
413
                         [r.revision_id for r in bundle.real_revisions])
418
414
        self.valid_apply_bundle(base_rev_id, bundle,
419
415
                                   checkout_dir=checkout_dir)
423
419
    def get_invalid_bundle(self, base_rev_id, rev_id):
424
420
        """Create a bundle from base_rev_id -> rev_id in built-in branch.
425
421
        Munge the text so that it's invalid.
426
 
        
 
422
 
427
423
        :return: The in-memory bundle
428
424
        """
429
425
        bundle_txt, rev_ids = self.create_bundle_text(base_rev_id, rev_id)
430
 
        new_text = bundle_txt.getvalue().replace('executable:no', 
 
426
        new_text = bundle_txt.getvalue().replace('executable:no',
431
427
                                               'executable:yes')
432
428
        bundle_txt = StringIO(new_text)
433
429
        bundle = read_bundle(bundle_txt)
434
430
        self.valid_apply_bundle(base_rev_id, bundle)
435
 
        return bundle 
 
431
        return bundle
436
432
 
437
433
    def test_non_bundle(self):
438
 
        self.assertRaises(NotABundle, read_bundle, StringIO('#!/bin/sh\n'))
 
434
        self.assertRaises(errors.NotABundle,
 
435
                          read_bundle, StringIO('#!/bin/sh\n'))
439
436
 
440
437
    def test_malformed(self):
441
 
        self.assertRaises(BadBundle, read_bundle, 
 
438
        self.assertRaises(errors.BadBundle, read_bundle,
442
439
                          StringIO('# Bazaar revision bundle v'))
443
440
 
444
441
    def test_crlf_bundle(self):
445
442
        try:
446
443
            read_bundle(StringIO('# Bazaar revision bundle v0.8\r\n'))
447
 
        except BadBundle:
 
444
        except errors.BadBundle:
448
445
            # It is currently permitted for bundles with crlf line endings to
449
446
            # make read_bundle raise a BadBundle, but this should be fixed.
450
447
            # Anything else, especially NotABundle, is an error.
455
452
        """
456
453
 
457
454
        if checkout_dir is None:
458
 
            checkout_dir = tempfile.mkdtemp(prefix='test-branch-', dir='.')
 
455
            checkout_dir = osutils.mkdtemp(prefix='test-branch-', dir='.')
459
456
        else:
460
457
            if not os.path.exists(checkout_dir):
461
458
                os.mkdir(checkout_dir)
469
466
        for ancestor in ancestors:
470
467
            old = self.b1.repository.revision_tree(ancestor)
471
468
            new = tree.branch.repository.revision_tree(ancestor)
472
 
 
473
 
            # Check that there aren't any inventory level changes
474
 
            delta = new.changes_from(old)
475
 
            self.assertFalse(delta.has_changed(),
476
 
                             'Revision %s not copied correctly.'
477
 
                             % (ancestor,))
478
 
 
479
 
            # Now check that the file contents are all correct
480
 
            for inventory_id in old:
481
 
                try:
482
 
                    old_file = old.get_file(inventory_id)
483
 
                except NoSuchFile:
484
 
                    continue
485
 
                if old_file is None:
486
 
                    continue
487
 
                self.assertEqual(old_file.read(),
488
 
                                 new.get_file(inventory_id).read())
 
469
            old.lock_read()
 
470
            new.lock_read()
 
471
            try:
 
472
                # Check that there aren't any inventory level changes
 
473
                delta = new.changes_from(old)
 
474
                self.assertFalse(delta.has_changed(),
 
475
                                 'Revision %s not copied correctly.'
 
476
                                 % (ancestor,))
 
477
 
 
478
                # Now check that the file contents are all correct
 
479
                for inventory_id in old:
 
480
                    try:
 
481
                        old_file = old.get_file(inventory_id)
 
482
                    except errors.NoSuchFile:
 
483
                        continue
 
484
                    if old_file is None:
 
485
                        continue
 
486
                    self.assertEqual(old_file.read(),
 
487
                                     new.get_file(inventory_id).read())
 
488
            finally:
 
489
                new.unlock()
 
490
                old.unlock()
489
491
        if not _mod_revision.is_null(rev_id):
490
492
            rh = self.b1.revision_history()
491
493
            tree.branch.set_revision_history(rh[:rh.index(rev_id)+1])
513
515
        self.assertIs(repository.has_revision(base_rev_id), True)
514
516
        for rev in info.real_revisions:
515
517
            self.assert_(not repository.has_revision(rev.revision_id),
516
 
                'Revision {%s} present before applying bundle' 
 
518
                'Revision {%s} present before applying bundle'
517
519
                % rev.revision_id)
518
 
        merge_bundle(info, to_tree, True, Merge3Merger, False, False)
 
520
        merge_bundle(info, to_tree, True, merge.Merge3Merger, False, False)
519
521
 
520
522
        for rev in info.real_revisions:
521
523
            self.assert_(repository.has_revision(rev.revision_id),
522
 
                'Missing revision {%s} after applying bundle' 
 
524
                'Missing revision {%s} after applying bundle'
523
525
                % rev.revision_id)
524
526
 
525
527
        self.assert_(to_tree.branch.repository.has_revision(info.target))
531
533
        rev = info.real_revisions[-1]
532
534
        base_tree = self.b1.repository.revision_tree(rev.revision_id)
533
535
        to_tree = to_tree.branch.repository.revision_tree(rev.revision_id)
534
 
        
 
536
 
535
537
        # TODO: make sure the target tree is identical to base tree
536
538
        #       we might also check the working tree.
537
539
 
597
599
 
598
600
        bundle = self.get_valid_bundle('a@cset-0-1', 'a@cset-0-2')
599
601
 
600
 
        # Check a rollup bundle 
 
602
        # Check a rollup bundle
601
603
        bundle = self.get_valid_bundle('null:', 'a@cset-0-2')
602
604
 
603
605
        # Now delete entries
611
613
        tt.set_executability(False, trans_id)
612
614
        tt.apply()
613
615
        self.tree1.commit('removed', rev_id='a@cset-0-3')
614
 
        
 
616
 
615
617
        bundle = self.get_valid_bundle('a@cset-0-2', 'a@cset-0-3')
616
 
        self.assertRaises((TestamentMismatch,
 
618
        self.assertRaises((errors.TestamentMismatch,
617
619
            errors.VersionedFileInvalidChecksum), self.get_invalid_bundle,
618
620
            'a@cset-0-2', 'a@cset-0-3')
619
 
        # Check a rollup bundle 
 
621
        # Check a rollup bundle
620
622
        bundle = self.get_valid_bundle('null:', 'a@cset-0-3')
621
623
 
622
624
        # Now move the directory
624
626
        self.tree1.commit('rename dir', rev_id='a@cset-0-4')
625
627
 
626
628
        bundle = self.get_valid_bundle('a@cset-0-3', 'a@cset-0-4')
627
 
        # Check a rollup bundle 
 
629
        # Check a rollup bundle
628
630
        bundle = self.get_valid_bundle('null:', 'a@cset-0-4')
629
631
 
630
632
        # Modified files
632
634
        open('b1/sub/dir/ pre space', 'ab').write(
633
635
             '\r\nAdding some\r\nDOS format lines\r\n')
634
636
        open('b1/sub/dir/nolastnewline.txt', 'ab').write('\n')
635
 
        self.tree1.rename_one('sub/dir/ pre space', 
 
637
        self.tree1.rename_one('sub/dir/ pre space',
636
638
                              'sub/ start space')
637
639
        self.tree1.commit('Modified files', rev_id='a@cset-0-5')
638
640
        bundle = self.get_valid_bundle('a@cset-0-4', 'a@cset-0-5')
655
657
                          verbose=False)
656
658
        bundle = self.get_valid_bundle('a@cset-0-6', 'a@cset-0-7')
657
659
 
658
 
    def test_symlink_bundle(self):
659
 
        self.requireFeature(SymlinkFeature)
 
660
    def _test_symlink_bundle(self, link_name, link_target, new_link_target):
 
661
        link_id = 'link-1'
 
662
 
 
663
        self.requireFeature(tests.SymlinkFeature)
660
664
        self.tree1 = self.make_branch_and_tree('b1')
661
665
        self.b1 = self.tree1.branch
 
666
 
662
667
        tt = TreeTransform(self.tree1)
663
 
        tt.new_symlink('link', tt.root, 'bar/foo', 'link-1')
 
668
        tt.new_symlink(link_name, tt.root, link_target, link_id)
664
669
        tt.apply()
665
670
        self.tree1.commit('add symlink', rev_id='l@cset-0-1')
666
 
        self.get_valid_bundle('null:', 'l@cset-0-1')
 
671
        bundle = self.get_valid_bundle('null:', 'l@cset-0-1')
 
672
        if getattr(bundle ,'revision_tree', None) is not None:
 
673
            # Not all bundle formats supports revision_tree
 
674
            bund_tree = bundle.revision_tree(self.b1.repository, 'l@cset-0-1')
 
675
            self.assertEqual(link_target, bund_tree.get_symlink_target(link_id))
 
676
 
667
677
        tt = TreeTransform(self.tree1)
668
 
        trans_id = tt.trans_id_tree_file_id('link-1')
 
678
        trans_id = tt.trans_id_tree_file_id(link_id)
669
679
        tt.adjust_path('link2', tt.root, trans_id)
670
680
        tt.delete_contents(trans_id)
671
 
        tt.create_symlink('mars', trans_id)
 
681
        tt.create_symlink(new_link_target, trans_id)
672
682
        tt.apply()
673
683
        self.tree1.commit('rename and change symlink', rev_id='l@cset-0-2')
674
 
        self.get_valid_bundle('l@cset-0-1', 'l@cset-0-2')
 
684
        bundle = self.get_valid_bundle('l@cset-0-1', 'l@cset-0-2')
 
685
        if getattr(bundle ,'revision_tree', None) is not None:
 
686
            # Not all bundle formats supports revision_tree
 
687
            bund_tree = bundle.revision_tree(self.b1.repository, 'l@cset-0-2')
 
688
            self.assertEqual(new_link_target,
 
689
                             bund_tree.get_symlink_target(link_id))
 
690
 
675
691
        tt = TreeTransform(self.tree1)
676
 
        trans_id = tt.trans_id_tree_file_id('link-1')
 
692
        trans_id = tt.trans_id_tree_file_id(link_id)
677
693
        tt.delete_contents(trans_id)
678
694
        tt.create_symlink('jupiter', trans_id)
679
695
        tt.apply()
680
696
        self.tree1.commit('just change symlink target', rev_id='l@cset-0-3')
681
 
        self.get_valid_bundle('l@cset-0-2', 'l@cset-0-3')
 
697
        bundle = self.get_valid_bundle('l@cset-0-2', 'l@cset-0-3')
 
698
 
682
699
        tt = TreeTransform(self.tree1)
683
 
        trans_id = tt.trans_id_tree_file_id('link-1')
 
700
        trans_id = tt.trans_id_tree_file_id(link_id)
684
701
        tt.delete_contents(trans_id)
685
702
        tt.apply()
686
703
        self.tree1.commit('Delete symlink', rev_id='l@cset-0-4')
687
 
        self.get_valid_bundle('l@cset-0-3', 'l@cset-0-4')
 
704
        bundle = self.get_valid_bundle('l@cset-0-3', 'l@cset-0-4')
 
705
 
 
706
    def test_symlink_bundle(self):
 
707
        self._test_symlink_bundle('link', 'bar/foo', 'mars')
 
708
 
 
709
    def test_unicode_symlink_bundle(self):
 
710
        self.requireFeature(tests.UnicodeFilenameFeature)
 
711
        self._test_symlink_bundle(u'\N{Euro Sign}link',
 
712
                                  u'bar/\N{Euro Sign}foo',
 
713
                                  u'mars\N{Euro Sign}')
688
714
 
689
715
    def test_binary_bundle(self):
690
716
        self.tree1 = self.make_branch_and_tree('b1')
691
717
        self.b1 = self.tree1.branch
692
718
        tt = TreeTransform(self.tree1)
693
 
        
 
719
 
694
720
        # Add
695
721
        tt.new_file('file', tt.root, '\x00\n\x00\r\x01\n\x02\r\xff', 'binary-1')
696
722
        tt.new_file('file2', tt.root, '\x01\n\x02\r\x03\n\x04\r\xff',
788
814
        return bundle_file.getvalue()
789
815
 
790
816
    def test_unicode_bundle(self):
 
817
        self.requireFeature(tests.UnicodeFilenameFeature)
791
818
        # Handle international characters
792
819
        os.mkdir('b1')
793
 
        try:
794
 
            f = open(u'b1/with Dod\xe9', 'wb')
795
 
        except UnicodeEncodeError:
796
 
            raise TestSkipped("Filesystem doesn't support unicode")
 
820
        f = open(u'b1/with Dod\N{Euro Sign}', 'wb')
797
821
 
798
822
        self.tree1 = self.make_branch_and_tree('b1')
799
823
        self.b1 = self.tree1.branch
803
827
            u'William Dod\xe9\n').encode('utf-8'))
804
828
        f.close()
805
829
 
806
 
        self.tree1.add([u'with Dod\xe9'], ['withdod-id'])
 
830
        self.tree1.add([u'with Dod\N{Euro Sign}'], ['withdod-id'])
807
831
        self.tree1.commit(u'i18n commit from William Dod\xe9',
808
832
                          rev_id='i18n-1', committer=u'William Dod\xe9')
809
833
 
810
 
        if sys.platform == 'darwin':
811
 
            from bzrlib.workingtree import WorkingTree3
812
 
            if type(self.tree1) is WorkingTree3:
813
 
                self.knownFailure("Bug #141438: fails for WorkingTree3 on OSX")
814
 
 
815
 
            # On Mac the '\xe9' gets changed to 'e\u0301'
816
 
            self.assertEqual([u'.bzr', u'with Dode\u0301'],
817
 
                             sorted(os.listdir(u'b1')))
818
 
            delta = self.tree1.changes_from(self.tree1.basis_tree())
819
 
            self.assertEqual([(u'with Dod\xe9', 'withdod-id', 'file')],
820
 
                             delta.removed)
821
 
            self.knownFailure("Mac OSX doesn't preserve unicode"
822
 
                              " combining characters.")
823
 
 
824
834
        # Add
825
835
        bundle = self.get_valid_bundle('null:', 'i18n-1')
826
836
 
827
837
        # Modified
828
 
        f = open(u'b1/with Dod\xe9', 'wb')
 
838
        f = open(u'b1/with Dod\N{Euro Sign}', 'wb')
829
839
        f.write(u'Modified \xb5\n'.encode('utf8'))
830
840
        f.close()
831
841
        self.tree1.commit(u'modified', rev_id='i18n-2')
832
842
 
833
843
        bundle = self.get_valid_bundle('i18n-1', 'i18n-2')
834
 
        
 
844
 
835
845
        # Renamed
836
 
        self.tree1.rename_one(u'with Dod\xe9', u'B\xe5gfors')
 
846
        self.tree1.rename_one(u'with Dod\N{Euro Sign}', u'B\N{Euro Sign}gfors')
837
847
        self.tree1.commit(u'renamed, the new i18n man', rev_id='i18n-3',
838
848
                          committer=u'Erik B\xe5gfors')
839
849
 
840
850
        bundle = self.get_valid_bundle('i18n-2', 'i18n-3')
841
851
 
842
852
        # Removed
843
 
        self.tree1.remove([u'B\xe5gfors'])
 
853
        self.tree1.remove([u'B\N{Euro Sign}gfors'])
844
854
        self.tree1.commit(u'removed', rev_id='i18n-4')
845
855
 
846
856
        bundle = self.get_valid_bundle('i18n-3', 'i18n-4')
851
861
 
852
862
    def test_whitespace_bundle(self):
853
863
        if sys.platform in ('win32', 'cygwin'):
854
 
            raise TestSkipped('Windows doesn\'t support filenames'
855
 
                              ' with tabs or trailing spaces')
 
864
            raise tests.TestSkipped('Windows doesn\'t support filenames'
 
865
                                    ' with tabs or trailing spaces')
856
866
        self.tree1 = self.make_branch_and_tree('b1')
857
867
        self.b1 = self.tree1.branch
858
868
 
883
893
        self.tree1.commit('removed', rev_id='white-4')
884
894
 
885
895
        bundle = self.get_valid_bundle('white-3', 'white-4')
886
 
        
 
896
 
887
897
        # Now test a complet roll-up
888
898
        bundle = self.get_valid_bundle('null:', 'white-4')
889
899
 
902
912
                          timezone=19800, timestamp=1152544886.0)
903
913
 
904
914
        bundle = self.get_valid_bundle('null:', 'tz-1')
905
 
        
 
915
 
906
916
        rev = bundle.revisions[0]
907
917
        self.assertEqual('Mon 2006-07-10 20:51:26.000000000 +0530', rev.date)
908
918
        self.assertEqual(19800, rev.timezone)
1027
1037
        repo = self.make_repo_with_installed_revisions()
1028
1038
        inv = repo.get_inventory('rev2')
1029
1039
        self.assertEqual('rev2', inv.root.revision)
1030
 
        root_vf = repo.weave_store.get_weave(inv.root.file_id,
1031
 
                                             repo.get_transaction())
1032
 
        self.assertEqual(root_vf.versions(), ['rev1', 'rev2'])
 
1040
        root_id = inv.root.file_id
 
1041
        repo.lock_read()
 
1042
        self.addCleanup(repo.unlock)
 
1043
        self.assertEqual({(root_id, 'rev1'):(),
 
1044
            (root_id, 'rev2'):((root_id, 'rev1'),)},
 
1045
            repo.texts.get_parent_map([(root_id, 'rev1'), (root_id, 'rev2')]))
1033
1046
 
1034
1047
    def test_inv_hash_across_serializers(self):
1035
1048
        repo = self.make_repo_with_installed_revisions()
1036
1049
        recorded_inv_sha1 = repo.get_inventory_sha1('rev2')
1037
1050
        xml = repo.get_inventory_xml('rev2')
1038
 
        self.assertEqual(sha_string(xml), recorded_inv_sha1)
 
1051
        self.assertEqual(osutils.sha_string(xml), recorded_inv_sha1)
1039
1052
 
1040
1053
    def test_across_models_incompatible(self):
1041
1054
        tree = self.make_simple_tree('dirstate-with-subtree')
1044
1057
        try:
1045
1058
            bundle = read_bundle(self.create_bundle_text('null:', 'rev1')[0])
1046
1059
        except errors.IncompatibleBundleFormat:
1047
 
            raise TestSkipped("Format 0.8 doesn't work with knit3")
 
1060
            raise tests.TestSkipped("Format 0.8 doesn't work with knit3")
1048
1061
        repo = self.make_repository('repo', format='knit')
1049
1062
        bundle.install_revisions(repo)
1050
1063
 
1071
1084
        try:
1072
1085
            bundle = read_bundle(self.create_bundle_text('null:', 'rev1')[0])
1073
1086
        except errors.IncompatibleBundleFormat:
1074
 
            raise TestSkipped("Format 0.8 doesn't work with knit3")
 
1087
            raise tests.TestSkipped("Format 0.8 doesn't work with knit3")
1075
1088
        if isinstance(bundle, v09.BundleInfo09):
1076
 
            raise TestSkipped("Format 0.9 doesn't work with subtrees")
 
1089
            raise tests.TestSkipped("Format 0.9 doesn't work with subtrees")
1077
1090
        repo = self.make_repository('repo', format='knit')
1078
1091
        self.assertRaises(errors.IncompatibleRevision,
1079
1092
                          bundle.install_revisions, repo)
1086
1099
        try:
1087
1100
            self.tree1.commit('Revision/id/with/slashes', rev_id='rev/id')
1088
1101
        except ValueError:
1089
 
            raise TestSkipped("Repository doesn't support revision ids with"
1090
 
                              " slashes")
 
1102
            raise tests.TestSkipped(
 
1103
                "Repository doesn't support revision ids with slashes")
1091
1104
        bundle = self.get_valid_bundle('null:', 'rev/id')
1092
1105
 
1093
1106
    def test_skip_file(self):
1109
1122
        self.tree1.commit('rev3', rev_id='rev3')
1110
1123
        bundle = self.get_valid_bundle('reva', 'rev3')
1111
1124
        if getattr(bundle, 'get_bundle_reader', None) is None:
1112
 
            raise TestSkipped('Bundle format cannot provide reader')
 
1125
            raise tests.TestSkipped('Bundle format cannot provide reader')
1113
1126
        # be sure that file1 comes before file2
1114
1127
        for b, m, k, r, f in bundle.get_bundle_reader().iter_records():
1115
1128
            if f == 'file3-id':
1118
1131
        bundle.install_revisions(target.branch.repository)
1119
1132
 
1120
1133
 
1121
 
class V08BundleTester(BundleTester, TestCaseWithTransport):
 
1134
class V08BundleTester(BundleTester, tests.TestCaseWithTransport):
1122
1135
 
1123
1136
    format = '0.8'
1124
1137
 
1257
1270
        return format
1258
1271
 
1259
1272
 
1260
 
class V4BundleTester(BundleTester, TestCaseWithTransport):
 
1273
class V4BundleTester(BundleTester, tests.TestCaseWithTransport):
1261
1274
 
1262
1275
    format = '4'
1263
1276
 
1265
1278
        """Create a bundle from base_rev_id -> rev_id in built-in branch.
1266
1279
        Make sure that the text generated is valid, and that it
1267
1280
        can be applied against the base, and generate the same information.
1268
 
        
1269
 
        :return: The in-memory bundle 
 
1281
 
 
1282
        :return: The in-memory bundle
1270
1283
        """
1271
1284
        bundle_txt, rev_ids = self.create_bundle_text(base_rev_id, rev_id)
1272
1285
 
1273
 
        # This should also validate the generated bundle 
 
1286
        # This should also validate the generated bundle
1274
1287
        bundle = read_bundle(bundle_txt)
1275
1288
        repository = self.b1.repository
1276
1289
        for bundle_rev in bundle.real_revisions:
1280
1293
            # it
1281
1294
            branch_rev = repository.get_revision(bundle_rev.revision_id)
1282
1295
            for a in ('inventory_sha1', 'revision_id', 'parent_ids',
1283
 
                      'timestamp', 'timezone', 'message', 'committer', 
 
1296
                      'timestamp', 'timezone', 'message', 'committer',
1284
1297
                      'parent_ids', 'properties'):
1285
 
                self.assertEqual(getattr(branch_rev, a), 
 
1298
                self.assertEqual(getattr(branch_rev, a),
1286
1299
                                 getattr(bundle_rev, a))
1287
 
            self.assertEqual(len(branch_rev.parent_ids), 
 
1300
            self.assertEqual(len(branch_rev.parent_ids),
1288
1301
                             len(bundle_rev.parent_ids))
1289
1302
        self.assertEqual(set(rev_ids),
1290
1303
                         set([r.revision_id for r in bundle.real_revisions]))
1316
1329
 
1317
1330
    def create_bundle_text(self, base_rev_id, rev_id):
1318
1331
        bundle_txt = StringIO()
1319
 
        rev_ids = write_bundle(self.b1.repository, rev_id, base_rev_id, 
 
1332
        rev_ids = write_bundle(self.b1.repository, rev_id, base_rev_id,
1320
1333
                               bundle_txt, format=self.format)
1321
1334
        bundle_txt.seek(0)
1322
 
        self.assertEqual(bundle_txt.readline(), 
 
1335
        self.assertEqual(bundle_txt.readline(),
1323
1336
                         '# Bazaar revision bundle v%s\n' % self.format)
1324
1337
        self.assertEqual(bundle_txt.readline(), '#\n')
1325
1338
        rev = self.b1.repository.get_revision(rev_id)
1345
1358
        tree2 = self.make_branch_and_tree('target')
1346
1359
        target_repo = tree2.branch.repository
1347
1360
        install_bundle(target_repo, serializer.read(s))
1348
 
        vf = target_repo.weave_store.get_weave('fileid-2',
1349
 
            target_repo.get_transaction())
1350
 
        self.assertEqual('contents1\nstatic\n', vf.get_text('rev1'))
1351
 
        self.assertEqual('contents2\nstatic\n', vf.get_text('rev2'))
 
1361
        target_repo.lock_read()
 
1362
        self.addCleanup(target_repo.unlock)
 
1363
        # Turn the 'iterators_of_bytes' back into simple strings for comparison
 
1364
        repo_texts = dict((i, ''.join(content)) for i, content
 
1365
                          in target_repo.iter_files_bytes(
 
1366
                                [('fileid-2', 'rev1', '1'),
 
1367
                                 ('fileid-2', 'rev2', '2')]))
 
1368
        self.assertEqual({'1':'contents1\nstatic\n',
 
1369
                          '2':'contents2\nstatic\n'},
 
1370
                         repo_texts)
1352
1371
        rtree = target_repo.revision_tree('rev2')
1353
 
        inventory_vf = target_repo.get_inventory_weave()
1354
 
        self.assertEqual({'rev2':('rev1',)},
1355
 
            inventory_vf.get_parent_map(['rev2']))
 
1372
        inventory_vf = target_repo.inventories
 
1373
        # If the inventory store has a graph, it must match the revision graph.
 
1374
        self.assertSubset(
 
1375
            [inventory_vf.get_parent_map([('rev2',)])[('rev2',)]],
 
1376
            [None, (('rev1',),)])
1356
1377
        self.assertEqual('changed file',
1357
1378
                         target_repo.get_revision('rev2').message)
1358
1379
 
1460
1481
        self.check_valid(bundle)
1461
1482
 
1462
1483
 
1463
 
class MungedBundleTesterV09(TestCaseWithTransport, MungedBundleTester):
 
1484
class MungedBundleTesterV09(tests.TestCaseWithTransport, MungedBundleTester):
1464
1485
 
1465
1486
    format = '0.9'
1466
1487
 
1498
1519
        self.check_valid(bundle)
1499
1520
 
1500
1521
 
1501
 
class MungedBundleTesterV4(TestCaseWithTransport, MungedBundleTester):
 
1522
class MungedBundleTesterV4(tests.TestCaseWithTransport, MungedBundleTester):
1502
1523
 
1503
1524
    format = '4'
1504
1525
 
1505
1526
 
1506
 
class TestBundleWriterReader(TestCase):
 
1527
class TestBundleWriterReader(tests.TestCase):
1507
1528
 
1508
1529
    def test_roundtrip_record(self):
1509
1530
        fileobj = StringIO()
1571
1592
        record = record_iter.next()
1572
1593
        self.assertEqual((None, {'foo': 'bar', 'storage_kind': 'header'},
1573
1594
            'info', None, None), record)
1574
 
        self.assertRaises(BadBundle, record_iter.next)
1575
 
 
1576
 
 
1577
 
class TestReadMergeableFromUrl(TestCaseWithTransport):
 
1595
        self.assertRaises(errors.BadBundle, record_iter.next)
 
1596
 
 
1597
 
 
1598
class TestReadMergeableFromUrl(tests.TestCaseWithTransport):
1578
1599
 
1579
1600
    def test_read_mergeable_skips_local(self):
1580
1601
        """A local bundle named like the URL should not be read.
1590
1611
        self.build_tree_contents([('./foo:bar', out.getvalue())])
1591
1612
        self.assertRaises(errors.NotABundle, read_mergeable_from_url,
1592
1613
                          'foo:bar')
 
1614
 
 
1615
    def test_smart_server_connection_reset(self):
 
1616
        """If a smart server connection fails during the attempt to read a
 
1617
        bundle, then the ConnectionReset error should be propagated.
 
1618
        """
 
1619
        # Instantiate a server that will provoke a ConnectionReset
 
1620
        sock_server = _DisconnectingTCPServer()
 
1621
        sock_server.setUp()
 
1622
        self.addCleanup(sock_server.tearDown)
 
1623
        # We don't really care what the url is since the server will close the
 
1624
        # connection without interpreting it
 
1625
        url = sock_server.get_url()
 
1626
        self.assertRaises(errors.ConnectionReset, read_mergeable_from_url, url)
 
1627
 
 
1628
 
 
1629
class _DisconnectingTCPServer(object):
 
1630
    """A TCP server that immediately closes any connection made to it."""
 
1631
 
 
1632
    def setUp(self):
 
1633
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
1634
        self.sock.bind(('127.0.0.1', 0))
 
1635
        self.sock.listen(1)
 
1636
        self.port = self.sock.getsockname()[1]
 
1637
        self.thread = threading.Thread(
 
1638
            name='%s (port %d)' % (self.__class__.__name__, self.port),
 
1639
            target=self.accept_and_close)
 
1640
        self.thread.start()
 
1641
 
 
1642
    def accept_and_close(self):
 
1643
        conn, addr = self.sock.accept()
 
1644
        conn.shutdown(socket.SHUT_RDWR)
 
1645
        conn.close()
 
1646
 
 
1647
    def get_url(self):
 
1648
        return 'bzr://127.0.0.1:%d/' % (self.port,)
 
1649
 
 
1650
    def tearDown(self):
 
1651
        try:
 
1652
            # make sure the thread dies by connecting to the listening socket,
 
1653
            # just in case the test failed to do so.
 
1654
            conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
1655
            conn.connect(self.sock.getsockname())
 
1656
            conn.close()
 
1657
        except socket.error:
 
1658
            pass
 
1659
        self.sock.close()
 
1660
        self.thread.join()
 
1661