~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_bundle.py

  • Committer: Vincent Ladeuil
  • Date: 2007-10-09 20:32:29 UTC
  • mto: (2903.1.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 2904.
  • Revision ID: v.ladeuil+lp@free.fr-20071009203229-5k200m1g7mf4jf9l
Fix 149019 by using a proper line number when reporting errors.

* bzrlib/util/configobj/configobj.py:
(ConfigObj._handle_error): Trivial fix. Since cur_index is
0-based, line number was off by one.

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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
from cStringIO import StringIO
18
18
import os
19
 
import socket
20
19
import sys
21
 
import threading
 
20
import tempfile
22
21
 
23
22
from bzrlib import (
24
23
    bzrdir,
25
 
    diff,
26
24
    errors,
27
25
    inventory,
28
 
    merge,
29
 
    osutils,
30
26
    repository,
31
27
    revision as _mod_revision,
32
 
    tests,
33
28
    treebuilder,
34
29
    )
35
 
from bzrlib.bundle import read_mergeable_from_url
 
30
from bzrlib.bzrdir import BzrDir
36
31
from bzrlib.bundle.apply_bundle import install_bundle, merge_bundle
37
32
from bzrlib.bundle.bundle_data import BundleTree
38
 
from bzrlib.bzrdir import BzrDir
39
 
from bzrlib.directory_service import directories
40
33
from bzrlib.bundle.serializer import write_bundle, read_bundle, v09, v4
41
34
from bzrlib.bundle.serializer.v08 import BundleSerializerV08
42
35
from bzrlib.bundle.serializer.v09 import BundleSerializerV09
43
36
from bzrlib.bundle.serializer.v4 import BundleSerializerV4
44
37
from bzrlib.branch import Branch
 
38
from bzrlib.diff import internal_diff
 
39
from bzrlib.errors import (BzrError, TestamentMismatch, NotABundle, BadBundle, 
 
40
                           NoSuchFile,)
 
41
from bzrlib.merge import Merge3Merger
45
42
from bzrlib.repofmt import knitrepo
46
 
from bzrlib.tests import (
47
 
    test_read_bundle,
48
 
    test_commit,
49
 
    )
 
43
from bzrlib.osutils import has_symlinks, sha_file
 
44
from bzrlib.tests import (TestCaseInTempDir, TestCaseWithTransport,
 
45
                          TestCase, TestSkipped, test_commit)
50
46
from bzrlib.transform import TreeTransform
51
47
 
52
48
 
101
97
        elif kind == 'symlink':
102
98
            ie = InventoryLink(file_id, name, parent_id)
103
99
        else:
104
 
            raise errors.BzrError('unknown kind %r' % kind)
 
100
            raise BzrError('unknown kind %r' % kind)
105
101
        ie.text_sha1 = text_sha_1
106
102
        ie.text_size = text_size
107
103
        return ie
109
105
    def add_dir(self, file_id, path):
110
106
        self.paths[file_id] = path
111
107
        self.ids[path] = file_id
112
 
 
 
108
    
113
109
    def add_file(self, file_id, path, contents):
114
110
        self.add_dir(file_id, path)
115
111
        self.contents[file_id] = contents
132
128
    def contents_stats(self, file_id):
133
129
        if file_id not in self.contents:
134
130
            return None, None
135
 
        text_sha1 = osutils.sha_file(self.get_file(file_id))
 
131
        text_sha1 = sha_file(self.get_file(file_id))
136
132
        return text_sha1, len(self.contents[file_id])
137
133
 
138
134
 
139
 
class BTreeTester(tests.TestCase):
 
135
class BTreeTester(TestCase):
140
136
    """A simple unittest tester for the BundleTree class."""
141
137
 
142
138
    def make_tree_1(self):
146
142
        mtree.add_file("c", "grandparent/parent/file", "Hello\n")
147
143
        mtree.add_dir("d", "grandparent/alt_parent")
148
144
        return BundleTree(mtree, ''), mtree
149
 
 
 
145
        
150
146
    def test_renames(self):
151
147
        """Ensure that file renames have the proper effect on children"""
152
148
        btree = self.make_tree_1()[0]
153
149
        self.assertEqual(btree.old_path("grandparent"), "grandparent")
154
 
        self.assertEqual(btree.old_path("grandparent/parent"),
 
150
        self.assertEqual(btree.old_path("grandparent/parent"), 
155
151
                         "grandparent/parent")
156
152
        self.assertEqual(btree.old_path("grandparent/parent/file"),
157
153
                         "grandparent/parent/file")
164
160
        self.assertEqual(btree.path2id("grandparent/parent"), "b")
165
161
        self.assertEqual(btree.path2id("grandparent/parent/file"), "c")
166
162
 
167
 
        self.assertTrue(btree.path2id("grandparent2") is None)
168
 
        self.assertTrue(btree.path2id("grandparent2/parent") is None)
169
 
        self.assertTrue(btree.path2id("grandparent2/parent/file") is None)
 
163
        assert btree.path2id("grandparent2") is None
 
164
        assert btree.path2id("grandparent2/parent") is None
 
165
        assert btree.path2id("grandparent2/parent/file") is None
170
166
 
171
167
        btree.note_rename("grandparent", "grandparent2")
172
 
        self.assertTrue(btree.old_path("grandparent") is None)
173
 
        self.assertTrue(btree.old_path("grandparent/parent") is None)
174
 
        self.assertTrue(btree.old_path("grandparent/parent/file") is None)
 
168
        assert btree.old_path("grandparent") is None
 
169
        assert btree.old_path("grandparent/parent") is None
 
170
        assert btree.old_path("grandparent/parent/file") is None
175
171
 
176
172
        self.assertEqual(btree.id2path("a"), "grandparent2")
177
173
        self.assertEqual(btree.id2path("b"), "grandparent2/parent")
181
177
        self.assertEqual(btree.path2id("grandparent2/parent"), "b")
182
178
        self.assertEqual(btree.path2id("grandparent2/parent/file"), "c")
183
179
 
184
 
        self.assertTrue(btree.path2id("grandparent") is None)
185
 
        self.assertTrue(btree.path2id("grandparent/parent") is None)
186
 
        self.assertTrue(btree.path2id("grandparent/parent/file") is None)
 
180
        assert btree.path2id("grandparent") is None
 
181
        assert btree.path2id("grandparent/parent") is None
 
182
        assert btree.path2id("grandparent/parent/file") is None
187
183
 
188
184
        btree.note_rename("grandparent/parent", "grandparent2/parent2")
189
185
        self.assertEqual(btree.id2path("a"), "grandparent2")
194
190
        self.assertEqual(btree.path2id("grandparent2/parent2"), "b")
195
191
        self.assertEqual(btree.path2id("grandparent2/parent2/file"), "c")
196
192
 
197
 
        self.assertTrue(btree.path2id("grandparent2/parent") is None)
198
 
        self.assertTrue(btree.path2id("grandparent2/parent/file") is None)
 
193
        assert btree.path2id("grandparent2/parent") is None
 
194
        assert btree.path2id("grandparent2/parent/file") is None
199
195
 
200
 
        btree.note_rename("grandparent/parent/file",
 
196
        btree.note_rename("grandparent/parent/file", 
201
197
                          "grandparent2/parent2/file2")
202
198
        self.assertEqual(btree.id2path("a"), "grandparent2")
203
199
        self.assertEqual(btree.id2path("b"), "grandparent2/parent2")
207
203
        self.assertEqual(btree.path2id("grandparent2/parent2"), "b")
208
204
        self.assertEqual(btree.path2id("grandparent2/parent2/file2"), "c")
209
205
 
210
 
        self.assertTrue(btree.path2id("grandparent2/parent2/file") is None)
 
206
        assert btree.path2id("grandparent2/parent2/file") is None
211
207
 
212
208
    def test_moves(self):
213
209
        """Ensure that file moves have the proper effect on children"""
214
210
        btree = self.make_tree_1()[0]
215
 
        btree.note_rename("grandparent/parent/file",
 
211
        btree.note_rename("grandparent/parent/file", 
216
212
                          "grandparent/alt_parent/file")
217
213
        self.assertEqual(btree.id2path("c"), "grandparent/alt_parent/file")
218
214
        self.assertEqual(btree.path2id("grandparent/alt_parent/file"), "c")
219
 
        self.assertTrue(btree.path2id("grandparent/parent/file") is None)
 
215
        assert btree.path2id("grandparent/parent/file") is None
220
216
 
221
217
    def unified_diff(self, old, new):
222
218
        out = StringIO()
223
 
        diff.internal_diff("old", old, "new", new, out)
 
219
        internal_diff("old", old, "new", new, out)
224
220
        out.seek(0,0)
225
221
        return out.read()
226
222
 
227
223
    def make_tree_2(self):
228
224
        btree = self.make_tree_1()[0]
229
 
        btree.note_rename("grandparent/parent/file",
 
225
        btree.note_rename("grandparent/parent/file", 
230
226
                          "grandparent/alt_parent/file")
231
 
        self.assertTrue(btree.id2path("e") is None)
232
 
        self.assertTrue(btree.path2id("grandparent/parent/file") is None)
 
227
        assert btree.id2path("e") is None
 
228
        assert btree.path2id("grandparent/parent/file") is None
233
229
        btree.note_id("e", "grandparent/parent/file")
234
230
        return btree
235
231
 
261
257
    def make_tree_3(self):
262
258
        btree, mtree = self.make_tree_1()
263
259
        mtree.add_file("e", "grandparent/parent/topping", "Anchovies\n")
264
 
        btree.note_rename("grandparent/parent/file",
 
260
        btree.note_rename("grandparent/parent/file", 
265
261
                          "grandparent/alt_parent/file")
266
 
        btree.note_rename("grandparent/parent/topping",
 
262
        btree.note_rename("grandparent/parent/topping", 
267
263
                          "grandparent/alt_parent/stopping")
268
264
        return btree
269
265
 
293
289
        btree = self.make_tree_1()[0]
294
290
        self.assertEqual(btree.get_file("c").read(), "Hello\n")
295
291
        btree.note_deletion("grandparent/parent/file")
296
 
        self.assertTrue(btree.id2path("c") is None)
297
 
        self.assertTrue(btree.path2id("grandparent/parent/file") is None)
 
292
        assert btree.id2path("c") is None
 
293
        assert btree.path2id("grandparent/parent/file") is None
298
294
 
299
295
    def sorted_ids(self, tree):
300
296
        ids = list(tree)
308
304
            [inventory.ROOT_ID, 'a', 'b', 'c', 'd'])
309
305
        btree.note_deletion("grandparent/parent/file")
310
306
        btree.note_id("e", "grandparent/alt_parent/fool", kind="directory")
311
 
        btree.note_last_changed("grandparent/alt_parent/fool",
 
307
        btree.note_last_changed("grandparent/alt_parent/fool", 
312
308
                                "revisionidiguess")
313
309
        self.assertEqual(self.sorted_ids(btree),
314
310
            [inventory.ROOT_ID, 'a', 'b', 'd', 'e'])
315
311
 
316
312
 
317
 
class BundleTester1(tests.TestCaseWithTransport):
 
313
class BundleTester1(TestCaseWithTransport):
318
314
 
319
315
    def test_mismatched_bundle(self):
320
316
        format = bzrdir.BzrDirMetaFormat1()
321
317
        format.repository_format = knitrepo.RepositoryFormatKnit3()
322
318
        serializer = BundleSerializerV08('0.8')
323
319
        b = self.make_branch('.', format=format)
324
 
        self.assertRaises(errors.IncompatibleBundleFormat, serializer.write,
 
320
        self.assertRaises(errors.IncompatibleBundleFormat, serializer.write, 
325
321
                          b.repository, [], {}, StringIO())
326
322
 
327
323
    def test_matched_bundle(self):
347
343
        format = bzrdir.BzrDirMetaFormat1()
348
344
        format.repository_format = knitrepo.RepositoryFormatKnit1()
349
345
        target = self.make_branch('target', format=format)
350
 
        self.assertRaises(errors.IncompatibleRevision, install_bundle,
 
346
        self.assertRaises(errors.IncompatibleRevision, install_bundle, 
351
347
                          target.repository, read_bundle(text))
352
348
 
353
349
 
361
357
    def make_branch_and_tree(self, path, format=None):
362
358
        if format is None:
363
359
            format = self.bzrdir_format()
364
 
        return tests.TestCaseWithTransport.make_branch_and_tree(
365
 
            self, path, format)
 
360
        return TestCaseWithTransport.make_branch_and_tree(self, path, format)
366
361
 
367
362
    def make_branch(self, path, format=None):
368
363
        if format is None:
369
364
            format = self.bzrdir_format()
370
 
        return tests.TestCaseWithTransport.make_branch(self, path, format)
 
365
        return TestCaseWithTransport.make_branch(self, path, format)
371
366
 
372
367
    def create_bundle_text(self, base_rev_id, rev_id):
373
368
        bundle_txt = StringIO()
374
 
        rev_ids = write_bundle(self.b1.repository, rev_id, base_rev_id,
 
369
        rev_ids = write_bundle(self.b1.repository, rev_id, base_rev_id, 
375
370
                               bundle_txt, format=self.format)
376
371
        bundle_txt.seek(0)
377
 
        self.assertEqual(bundle_txt.readline(),
 
372
        self.assertEqual(bundle_txt.readline(), 
378
373
                         '# Bazaar revision bundle v%s\n' % self.format)
379
374
        self.assertEqual(bundle_txt.readline(), '#\n')
380
375
 
388
383
        """Create a bundle from base_rev_id -> rev_id in built-in branch.
389
384
        Make sure that the text generated is valid, and that it
390
385
        can be applied against the base, and generate the same information.
391
 
 
392
 
        :return: The in-memory bundle
 
386
        
 
387
        :return: The in-memory bundle 
393
388
        """
394
389
        bundle_txt, rev_ids = self.create_bundle_text(base_rev_id, rev_id)
395
390
 
396
 
        # This should also validate the generated bundle
 
391
        # This should also validate the generated bundle 
397
392
        bundle = read_bundle(bundle_txt)
398
393
        repository = self.b1.repository
399
394
        for bundle_rev in bundle.real_revisions:
403
398
            # it
404
399
            branch_rev = repository.get_revision(bundle_rev.revision_id)
405
400
            for a in ('inventory_sha1', 'revision_id', 'parent_ids',
406
 
                      'timestamp', 'timezone', 'message', 'committer',
 
401
                      'timestamp', 'timezone', 'message', 'committer', 
407
402
                      'parent_ids', 'properties'):
408
 
                self.assertEqual(getattr(branch_rev, a),
 
403
                self.assertEqual(getattr(branch_rev, a), 
409
404
                                 getattr(bundle_rev, a))
410
 
            self.assertEqual(len(branch_rev.parent_ids),
 
405
            self.assertEqual(len(branch_rev.parent_ids), 
411
406
                             len(bundle_rev.parent_ids))
412
 
        self.assertEqual(rev_ids,
 
407
        self.assertEqual(rev_ids, 
413
408
                         [r.revision_id for r in bundle.real_revisions])
414
409
        self.valid_apply_bundle(base_rev_id, bundle,
415
410
                                   checkout_dir=checkout_dir)
419
414
    def get_invalid_bundle(self, base_rev_id, rev_id):
420
415
        """Create a bundle from base_rev_id -> rev_id in built-in branch.
421
416
        Munge the text so that it's invalid.
422
 
 
 
417
        
423
418
        :return: The in-memory bundle
424
419
        """
425
420
        bundle_txt, rev_ids = self.create_bundle_text(base_rev_id, rev_id)
426
 
        new_text = bundle_txt.getvalue().replace('executable:no',
 
421
        new_text = bundle_txt.getvalue().replace('executable:no', 
427
422
                                               'executable:yes')
428
423
        bundle_txt = StringIO(new_text)
429
424
        bundle = read_bundle(bundle_txt)
430
425
        self.valid_apply_bundle(base_rev_id, bundle)
431
 
        return bundle
 
426
        return bundle 
432
427
 
433
428
    def test_non_bundle(self):
434
 
        self.assertRaises(errors.NotABundle,
435
 
                          read_bundle, StringIO('#!/bin/sh\n'))
 
429
        self.assertRaises(NotABundle, read_bundle, StringIO('#!/bin/sh\n'))
436
430
 
437
431
    def test_malformed(self):
438
 
        self.assertRaises(errors.BadBundle, read_bundle,
 
432
        self.assertRaises(BadBundle, read_bundle, 
439
433
                          StringIO('# Bazaar revision bundle v'))
440
434
 
441
435
    def test_crlf_bundle(self):
442
436
        try:
443
437
            read_bundle(StringIO('# Bazaar revision bundle v0.8\r\n'))
444
 
        except errors.BadBundle:
 
438
        except BadBundle:
445
439
            # It is currently permitted for bundles with crlf line endings to
446
440
            # make read_bundle raise a BadBundle, but this should be fixed.
447
441
            # Anything else, especially NotABundle, is an error.
452
446
        """
453
447
 
454
448
        if checkout_dir is None:
455
 
            checkout_dir = osutils.mkdtemp(prefix='test-branch-', dir='.')
 
449
            checkout_dir = tempfile.mkdtemp(prefix='test-branch-', dir='.')
456
450
        else:
457
451
            if not os.path.exists(checkout_dir):
458
452
                os.mkdir(checkout_dir)
461
455
        ancestors = write_bundle(self.b1.repository, rev_id, 'null:', s,
462
456
                                 format=self.format)
463
457
        s.seek(0)
464
 
        self.assertIsInstance(s.getvalue(), str)
 
458
        assert isinstance(s.getvalue(), str), (
 
459
            "Bundle isn't a bytestring:\n %s..." % repr(s.getvalue())[:40])
465
460
        install_bundle(tree.branch.repository, read_bundle(s))
466
461
        for ancestor in ancestors:
467
462
            old = self.b1.repository.revision_tree(ancestor)
468
463
            new = tree.branch.repository.revision_tree(ancestor)
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()
 
464
 
 
465
            # Check that there aren't any inventory level changes
 
466
            delta = new.changes_from(old)
 
467
            self.assertFalse(delta.has_changed(),
 
468
                             'Revision %s not copied correctly.'
 
469
                             % (ancestor,))
 
470
 
 
471
            # Now check that the file contents are all correct
 
472
            for inventory_id in old:
 
473
                try:
 
474
                    old_file = old.get_file(inventory_id)
 
475
                except NoSuchFile:
 
476
                    continue
 
477
                if old_file is None:
 
478
                    continue
 
479
                self.assertEqual(old_file.read(),
 
480
                                 new.get_file(inventory_id).read())
491
481
        if not _mod_revision.is_null(rev_id):
492
482
            rh = self.b1.revision_history()
493
483
            tree.branch.set_revision_history(rh[:rh.index(rev_id)+1])
502
492
        sure everything matches the builtin branch.
503
493
        """
504
494
        to_tree = self.get_checkout(base_rev_id, checkout_dir=checkout_dir)
505
 
        to_tree.lock_write()
506
 
        try:
507
 
            self._valid_apply_bundle(base_rev_id, info, to_tree)
508
 
        finally:
509
 
            to_tree.unlock()
510
 
 
511
 
    def _valid_apply_bundle(self, base_rev_id, info, to_tree):
512
495
        original_parents = to_tree.get_parent_ids()
513
496
        repository = to_tree.branch.repository
514
497
        original_parents = to_tree.get_parent_ids()
515
498
        self.assertIs(repository.has_revision(base_rev_id), True)
516
499
        for rev in info.real_revisions:
517
500
            self.assert_(not repository.has_revision(rev.revision_id),
518
 
                'Revision {%s} present before applying bundle'
 
501
                'Revision {%s} present before applying bundle' 
519
502
                % rev.revision_id)
520
 
        merge_bundle(info, to_tree, True, merge.Merge3Merger, False, False)
 
503
        merge_bundle(info, to_tree, True, Merge3Merger, False, False)
521
504
 
522
505
        for rev in info.real_revisions:
523
506
            self.assert_(repository.has_revision(rev.revision_id),
524
 
                'Missing revision {%s} after applying bundle'
 
507
                'Missing revision {%s} after applying bundle' 
525
508
                % rev.revision_id)
526
509
 
527
510
        self.assert_(to_tree.branch.repository.has_revision(info.target))
533
516
        rev = info.real_revisions[-1]
534
517
        base_tree = self.b1.repository.revision_tree(rev.revision_id)
535
518
        to_tree = to_tree.branch.repository.revision_tree(rev.revision_id)
536
 
 
 
519
        
537
520
        # TODO: make sure the target tree is identical to base tree
538
521
        #       we might also check the working tree.
539
522
 
599
582
 
600
583
        bundle = self.get_valid_bundle('a@cset-0-1', 'a@cset-0-2')
601
584
 
602
 
        # Check a rollup bundle
 
585
        # Check a rollup bundle 
603
586
        bundle = self.get_valid_bundle('null:', 'a@cset-0-2')
604
587
 
605
588
        # Now delete entries
613
596
        tt.set_executability(False, trans_id)
614
597
        tt.apply()
615
598
        self.tree1.commit('removed', rev_id='a@cset-0-3')
616
 
 
 
599
        
617
600
        bundle = self.get_valid_bundle('a@cset-0-2', 'a@cset-0-3')
618
 
        self.assertRaises((errors.TestamentMismatch,
 
601
        self.assertRaises((TestamentMismatch,
619
602
            errors.VersionedFileInvalidChecksum), self.get_invalid_bundle,
620
603
            'a@cset-0-2', 'a@cset-0-3')
621
 
        # Check a rollup bundle
 
604
        # Check a rollup bundle 
622
605
        bundle = self.get_valid_bundle('null:', 'a@cset-0-3')
623
606
 
624
607
        # Now move the directory
626
609
        self.tree1.commit('rename dir', rev_id='a@cset-0-4')
627
610
 
628
611
        bundle = self.get_valid_bundle('a@cset-0-3', 'a@cset-0-4')
629
 
        # Check a rollup bundle
 
612
        # Check a rollup bundle 
630
613
        bundle = self.get_valid_bundle('null:', 'a@cset-0-4')
631
614
 
632
615
        # Modified files
634
617
        open('b1/sub/dir/ pre space', 'ab').write(
635
618
             '\r\nAdding some\r\nDOS format lines\r\n')
636
619
        open('b1/sub/dir/nolastnewline.txt', 'ab').write('\n')
637
 
        self.tree1.rename_one('sub/dir/ pre space',
 
620
        self.tree1.rename_one('sub/dir/ pre space', 
638
621
                              'sub/ start space')
639
622
        self.tree1.commit('Modified files', rev_id='a@cset-0-5')
640
623
        bundle = self.get_valid_bundle('a@cset-0-4', 'a@cset-0-5')
657
640
                          verbose=False)
658
641
        bundle = self.get_valid_bundle('a@cset-0-6', 'a@cset-0-7')
659
642
 
660
 
    def _test_symlink_bundle(self, link_name, link_target, new_link_target):
661
 
        link_id = 'link-1'
662
 
 
663
 
        self.requireFeature(tests.SymlinkFeature)
 
643
    def test_symlink_bundle(self):
 
644
        if not has_symlinks():
 
645
            raise TestSkipped("No symlink support")
664
646
        self.tree1 = self.make_branch_and_tree('b1')
665
647
        self.b1 = self.tree1.branch
666
 
 
667
648
        tt = TreeTransform(self.tree1)
668
 
        tt.new_symlink(link_name, tt.root, link_target, link_id)
 
649
        tt.new_symlink('link', tt.root, 'bar/foo', 'link-1')
669
650
        tt.apply()
670
651
        self.tree1.commit('add symlink', rev_id='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
 
 
 
652
        self.get_valid_bundle('null:', 'l@cset-0-1')
677
653
        tt = TreeTransform(self.tree1)
678
 
        trans_id = tt.trans_id_tree_file_id(link_id)
 
654
        trans_id = tt.trans_id_tree_file_id('link-1')
679
655
        tt.adjust_path('link2', tt.root, trans_id)
680
656
        tt.delete_contents(trans_id)
681
 
        tt.create_symlink(new_link_target, trans_id)
 
657
        tt.create_symlink('mars', trans_id)
682
658
        tt.apply()
683
659
        self.tree1.commit('rename and change symlink', rev_id='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
 
 
 
660
        self.get_valid_bundle('l@cset-0-1', 'l@cset-0-2')
691
661
        tt = TreeTransform(self.tree1)
692
 
        trans_id = tt.trans_id_tree_file_id(link_id)
 
662
        trans_id = tt.trans_id_tree_file_id('link-1')
693
663
        tt.delete_contents(trans_id)
694
664
        tt.create_symlink('jupiter', trans_id)
695
665
        tt.apply()
696
666
        self.tree1.commit('just change symlink target', rev_id='l@cset-0-3')
697
 
        bundle = self.get_valid_bundle('l@cset-0-2', 'l@cset-0-3')
698
 
 
 
667
        self.get_valid_bundle('l@cset-0-2', 'l@cset-0-3')
699
668
        tt = TreeTransform(self.tree1)
700
 
        trans_id = tt.trans_id_tree_file_id(link_id)
 
669
        trans_id = tt.trans_id_tree_file_id('link-1')
701
670
        tt.delete_contents(trans_id)
702
671
        tt.apply()
703
672
        self.tree1.commit('Delete symlink', rev_id='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}')
 
673
        self.get_valid_bundle('l@cset-0-3', 'l@cset-0-4')
714
674
 
715
675
    def test_binary_bundle(self):
716
676
        self.tree1 = self.make_branch_and_tree('b1')
717
677
        self.b1 = self.tree1.branch
718
678
        tt = TreeTransform(self.tree1)
719
 
 
 
679
        
720
680
        # Add
721
681
        tt.new_file('file', tt.root, '\x00\n\x00\r\x01\n\x02\r\xff', 'binary-1')
722
682
        tt.new_file('file2', tt.root, '\x01\n\x02\r\x03\n\x04\r\xff',
814
774
        return bundle_file.getvalue()
815
775
 
816
776
    def test_unicode_bundle(self):
817
 
        self.requireFeature(tests.UnicodeFilenameFeature)
818
777
        # Handle international characters
819
778
        os.mkdir('b1')
820
 
        f = open(u'b1/with Dod\N{Euro Sign}', 'wb')
 
779
        try:
 
780
            f = open(u'b1/with Dod\xe9', 'wb')
 
781
        except UnicodeEncodeError:
 
782
            raise TestSkipped("Filesystem doesn't support unicode")
821
783
 
822
784
        self.tree1 = self.make_branch_and_tree('b1')
823
785
        self.b1 = self.tree1.branch
827
789
            u'William Dod\xe9\n').encode('utf-8'))
828
790
        f.close()
829
791
 
830
 
        self.tree1.add([u'with Dod\N{Euro Sign}'], ['withdod-id'])
 
792
        self.tree1.add([u'with Dod\xe9'], ['withdod-id'])
831
793
        self.tree1.commit(u'i18n commit from William Dod\xe9',
832
794
                          rev_id='i18n-1', committer=u'William Dod\xe9')
833
795
 
 
796
        if sys.platform == 'darwin':
 
797
            from bzrlib.workingtree import WorkingTree3
 
798
            if type(self.tree1) is WorkingTree3:
 
799
                self.knownFailure("Bug #141438: fails for WorkingTree3 on OSX")
 
800
 
 
801
            # On Mac the '\xe9' gets changed to 'e\u0301'
 
802
            self.assertEqual([u'.bzr', u'with Dode\u0301'],
 
803
                             sorted(os.listdir(u'b1')))
 
804
            delta = self.tree1.changes_from(self.tree1.basis_tree())
 
805
            self.assertEqual([(u'with Dod\xe9', 'withdod-id', 'file')],
 
806
                             delta.removed)
 
807
            self.knownFailure("Mac OSX doesn't preserve unicode"
 
808
                              " combining characters.")
 
809
 
834
810
        # Add
835
811
        bundle = self.get_valid_bundle('null:', 'i18n-1')
836
812
 
837
813
        # Modified
838
 
        f = open(u'b1/with Dod\N{Euro Sign}', 'wb')
 
814
        f = open(u'b1/with Dod\xe9', 'wb')
839
815
        f.write(u'Modified \xb5\n'.encode('utf8'))
840
816
        f.close()
841
817
        self.tree1.commit(u'modified', rev_id='i18n-2')
842
818
 
843
819
        bundle = self.get_valid_bundle('i18n-1', 'i18n-2')
844
 
 
 
820
        
845
821
        # Renamed
846
 
        self.tree1.rename_one(u'with Dod\N{Euro Sign}', u'B\N{Euro Sign}gfors')
 
822
        self.tree1.rename_one(u'with Dod\xe9', u'B\xe5gfors')
847
823
        self.tree1.commit(u'renamed, the new i18n man', rev_id='i18n-3',
848
824
                          committer=u'Erik B\xe5gfors')
849
825
 
850
826
        bundle = self.get_valid_bundle('i18n-2', 'i18n-3')
851
827
 
852
828
        # Removed
853
 
        self.tree1.remove([u'B\N{Euro Sign}gfors'])
 
829
        self.tree1.remove([u'B\xe5gfors'])
854
830
        self.tree1.commit(u'removed', rev_id='i18n-4')
855
831
 
856
832
        bundle = self.get_valid_bundle('i18n-3', 'i18n-4')
861
837
 
862
838
    def test_whitespace_bundle(self):
863
839
        if sys.platform in ('win32', 'cygwin'):
864
 
            raise tests.TestSkipped('Windows doesn\'t support filenames'
865
 
                                    ' with tabs or trailing spaces')
 
840
            raise TestSkipped('Windows doesn\'t support filenames'
 
841
                              ' with tabs or trailing spaces')
866
842
        self.tree1 = self.make_branch_and_tree('b1')
867
843
        self.b1 = self.tree1.branch
868
844
 
893
869
        self.tree1.commit('removed', rev_id='white-4')
894
870
 
895
871
        bundle = self.get_valid_bundle('white-3', 'white-4')
896
 
 
 
872
        
897
873
        # Now test a complet roll-up
898
874
        bundle = self.get_valid_bundle('null:', 'white-4')
899
875
 
912
888
                          timezone=19800, timestamp=1152544886.0)
913
889
 
914
890
        bundle = self.get_valid_bundle('null:', 'tz-1')
915
 
 
 
891
        
916
892
        rev = bundle.revisions[0]
917
893
        self.assertEqual('Mon 2006-07-10 20:51:26.000000000 +0530', rev.date)
918
894
        self.assertEqual(19800, rev.timezone)
1024
1000
        self.assertNotContainsRe(inv_text, 'format="5"')
1025
1001
        self.assertContainsRe(inv_text, 'format="7"')
1026
1002
 
1027
 
    def make_repo_with_installed_revisions(self):
1028
 
        tree = self.make_simple_tree('knit')
1029
 
        tree.commit('hello', rev_id='rev1')
1030
 
        tree.commit('hello', rev_id='rev2')
1031
 
        bundle = read_bundle(self.create_bundle_text('null:', 'rev2')[0])
1032
 
        repo = self.make_repository('repo', format='dirstate-with-subtree')
1033
 
        bundle.install_revisions(repo)
1034
 
        return repo
1035
 
 
1036
1003
    def test_across_models(self):
1037
 
        repo = self.make_repo_with_installed_revisions()
 
1004
        tree = self.make_simple_tree('knit')
 
1005
        tree.commit('hello', rev_id='rev1')
 
1006
        tree.commit('hello', rev_id='rev2')
 
1007
        bundle = read_bundle(self.create_bundle_text('null:', 'rev2')[0])
 
1008
        repo = self.make_repository('repo', format='dirstate-with-subtree')
 
1009
        bundle.install_revisions(repo)
1038
1010
        inv = repo.get_inventory('rev2')
1039
1011
        self.assertEqual('rev2', inv.root.revision)
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')]))
1046
 
 
1047
 
    def test_inv_hash_across_serializers(self):
1048
 
        repo = self.make_repo_with_installed_revisions()
1049
 
        recorded_inv_sha1 = repo.get_inventory_sha1('rev2')
1050
 
        xml = repo.get_inventory_xml('rev2')
1051
 
        self.assertEqual(osutils.sha_string(xml), recorded_inv_sha1)
 
1012
        root_vf = repo.weave_store.get_weave(inv.root.file_id,
 
1013
                                             repo.get_transaction())
 
1014
        self.assertEqual(root_vf.versions(), ['rev1', 'rev2'])
1052
1015
 
1053
1016
    def test_across_models_incompatible(self):
1054
1017
        tree = self.make_simple_tree('dirstate-with-subtree')
1057
1020
        try:
1058
1021
            bundle = read_bundle(self.create_bundle_text('null:', 'rev1')[0])
1059
1022
        except errors.IncompatibleBundleFormat:
1060
 
            raise tests.TestSkipped("Format 0.8 doesn't work with knit3")
 
1023
            raise TestSkipped("Format 0.8 doesn't work with knit3")
1061
1024
        repo = self.make_repository('repo', format='knit')
1062
1025
        bundle.install_revisions(repo)
1063
1026
 
1084
1047
        try:
1085
1048
            bundle = read_bundle(self.create_bundle_text('null:', 'rev1')[0])
1086
1049
        except errors.IncompatibleBundleFormat:
1087
 
            raise tests.TestSkipped("Format 0.8 doesn't work with knit3")
 
1050
            raise TestSkipped("Format 0.8 doesn't work with knit3")
1088
1051
        if isinstance(bundle, v09.BundleInfo09):
1089
 
            raise tests.TestSkipped("Format 0.9 doesn't work with subtrees")
 
1052
            raise TestSkipped("Format 0.9 doesn't work with subtrees")
1090
1053
        repo = self.make_repository('repo', format='knit')
1091
1054
        self.assertRaises(errors.IncompatibleRevision,
1092
1055
                          bundle.install_revisions, repo)
1099
1062
        try:
1100
1063
            self.tree1.commit('Revision/id/with/slashes', rev_id='rev/id')
1101
1064
        except ValueError:
1102
 
            raise tests.TestSkipped(
1103
 
                "Repository doesn't support revision ids with slashes")
 
1065
            raise TestSkipped("Repository doesn't support revision ids with"
 
1066
                              " slashes")
1104
1067
        bundle = self.get_valid_bundle('null:', 'rev/id')
1105
1068
 
1106
1069
    def test_skip_file(self):
1122
1085
        self.tree1.commit('rev3', rev_id='rev3')
1123
1086
        bundle = self.get_valid_bundle('reva', 'rev3')
1124
1087
        if getattr(bundle, 'get_bundle_reader', None) is None:
1125
 
            raise tests.TestSkipped('Bundle format cannot provide reader')
 
1088
            raise TestSkipped('Bundle format cannot provide reader')
1126
1089
        # be sure that file1 comes before file2
1127
1090
        for b, m, k, r, f in bundle.get_bundle_reader().iter_records():
1128
1091
            if f == 'file3-id':
1131
1094
        bundle.install_revisions(target.branch.repository)
1132
1095
 
1133
1096
 
1134
 
class V08BundleTester(BundleTester, tests.TestCaseWithTransport):
 
1097
class V08BundleTester(BundleTester, TestCaseWithTransport):
1135
1098
 
1136
1099
    format = '0.8'
1137
1100
 
1270
1233
        return format
1271
1234
 
1272
1235
 
1273
 
class V4BundleTester(BundleTester, tests.TestCaseWithTransport):
 
1236
class V4BundleTester(BundleTester, TestCaseWithTransport):
1274
1237
 
1275
1238
    format = '4'
1276
1239
 
1278
1241
        """Create a bundle from base_rev_id -> rev_id in built-in branch.
1279
1242
        Make sure that the text generated is valid, and that it
1280
1243
        can be applied against the base, and generate the same information.
1281
 
 
1282
 
        :return: The in-memory bundle
 
1244
        
 
1245
        :return: The in-memory bundle 
1283
1246
        """
1284
1247
        bundle_txt, rev_ids = self.create_bundle_text(base_rev_id, rev_id)
1285
1248
 
1286
 
        # This should also validate the generated bundle
 
1249
        # This should also validate the generated bundle 
1287
1250
        bundle = read_bundle(bundle_txt)
1288
1251
        repository = self.b1.repository
1289
1252
        for bundle_rev in bundle.real_revisions:
1293
1256
            # it
1294
1257
            branch_rev = repository.get_revision(bundle_rev.revision_id)
1295
1258
            for a in ('inventory_sha1', 'revision_id', 'parent_ids',
1296
 
                      'timestamp', 'timezone', 'message', 'committer',
 
1259
                      'timestamp', 'timezone', 'message', 'committer', 
1297
1260
                      'parent_ids', 'properties'):
1298
 
                self.assertEqual(getattr(branch_rev, a),
 
1261
                self.assertEqual(getattr(branch_rev, a), 
1299
1262
                                 getattr(bundle_rev, a))
1300
 
            self.assertEqual(len(branch_rev.parent_ids),
 
1263
            self.assertEqual(len(branch_rev.parent_ids), 
1301
1264
                             len(bundle_rev.parent_ids))
1302
1265
        self.assertEqual(set(rev_ids),
1303
1266
                         set([r.revision_id for r in bundle.real_revisions]))
1329
1292
 
1330
1293
    def create_bundle_text(self, base_rev_id, rev_id):
1331
1294
        bundle_txt = StringIO()
1332
 
        rev_ids = write_bundle(self.b1.repository, rev_id, base_rev_id,
 
1295
        rev_ids = write_bundle(self.b1.repository, rev_id, base_rev_id, 
1333
1296
                               bundle_txt, format=self.format)
1334
1297
        bundle_txt.seek(0)
1335
 
        self.assertEqual(bundle_txt.readline(),
 
1298
        self.assertEqual(bundle_txt.readline(), 
1336
1299
                         '# Bazaar revision bundle v%s\n' % self.format)
1337
1300
        self.assertEqual(bundle_txt.readline(), '#\n')
1338
1301
        rev = self.b1.repository.get_revision(rev_id)
1358
1321
        tree2 = self.make_branch_and_tree('target')
1359
1322
        target_repo = tree2.branch.repository
1360
1323
        install_bundle(target_repo, serializer.read(s))
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)
 
1324
        vf = target_repo.weave_store.get_weave('fileid-2',
 
1325
            target_repo.get_transaction())
 
1326
        self.assertEqual('contents1\nstatic\n', vf.get_text('rev1'))
 
1327
        self.assertEqual('contents2\nstatic\n', vf.get_text('rev2'))
1371
1328
        rtree = target_repo.revision_tree('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',),)])
 
1329
        inventory_vf = target_repo.get_inventory_weave()
 
1330
        self.assertEqual(['rev1'], inventory_vf.get_parents('rev2'))
1377
1331
        self.assertEqual('changed file',
1378
1332
                         target_repo.get_revision('rev2').message)
1379
1333
 
1481
1435
        self.check_valid(bundle)
1482
1436
 
1483
1437
 
1484
 
class MungedBundleTesterV09(tests.TestCaseWithTransport, MungedBundleTester):
 
1438
class MungedBundleTesterV09(TestCaseWithTransport, MungedBundleTester):
1485
1439
 
1486
1440
    format = '0.9'
1487
1441
 
1519
1473
        self.check_valid(bundle)
1520
1474
 
1521
1475
 
1522
 
class MungedBundleTesterV4(tests.TestCaseWithTransport, MungedBundleTester):
 
1476
class MungedBundleTesterV4(TestCaseWithTransport, MungedBundleTester):
1523
1477
 
1524
1478
    format = '4'
1525
1479
 
1526
1480
 
1527
 
class TestBundleWriterReader(tests.TestCase):
 
1481
class TestBundleWriterReader(TestCase):
1528
1482
 
1529
1483
    def test_roundtrip_record(self):
1530
1484
        fileobj = StringIO()
1592
1546
        record = record_iter.next()
1593
1547
        self.assertEqual((None, {'foo': 'bar', 'storage_kind': 'header'},
1594
1548
            'info', None, None), record)
1595
 
        self.assertRaises(errors.BadBundle, record_iter.next)
1596
 
 
1597
 
 
1598
 
class TestReadMergeableFromUrl(tests.TestCaseWithTransport):
1599
 
 
1600
 
    def test_read_mergeable_skips_local(self):
1601
 
        """A local bundle named like the URL should not be read.
1602
 
        """
1603
 
        out, wt = test_read_bundle.create_bundle_file(self)
1604
 
        class FooService(object):
1605
 
            """A directory service that always returns source"""
1606
 
 
1607
 
            def look_up(self, name, url):
1608
 
                return 'source'
1609
 
        directories.register('foo:', FooService, 'Testing directory service')
1610
 
        self.addCleanup(lambda: directories.remove('foo:'))
1611
 
        self.build_tree_contents([('./foo:bar', out.getvalue())])
1612
 
        self.assertRaises(errors.NotABundle, read_mergeable_from_url,
1613
 
                          'foo:bar')
1614
 
 
1615
 
    def test_infinite_redirects_are_not_a_bundle(self):
1616
 
        """If a URL causes TooManyRedirections then NotABundle is raised.
1617
 
        """
1618
 
        from bzrlib.tests.blackbox.test_push import RedirectingMemoryServer
1619
 
        server = RedirectingMemoryServer()
1620
 
        server.setUp()
1621
 
        url = server.get_url() + 'infinite-loop'
1622
 
        self.addCleanup(server.tearDown)
1623
 
        self.assertRaises(errors.NotABundle, read_mergeable_from_url, url)
1624
 
 
1625
 
    def test_smart_server_connection_reset(self):
1626
 
        """If a smart server connection fails during the attempt to read a
1627
 
        bundle, then the ConnectionReset error should be propagated.
1628
 
        """
1629
 
        # Instantiate a server that will provoke a ConnectionReset
1630
 
        sock_server = _DisconnectingTCPServer()
1631
 
        sock_server.setUp()
1632
 
        self.addCleanup(sock_server.tearDown)
1633
 
        # We don't really care what the url is since the server will close the
1634
 
        # connection without interpreting it
1635
 
        url = sock_server.get_url()
1636
 
        self.assertRaises(errors.ConnectionReset, read_mergeable_from_url, url)
1637
 
 
1638
 
 
1639
 
class _DisconnectingTCPServer(object):
1640
 
    """A TCP server that immediately closes any connection made to it."""
1641
 
 
1642
 
    def setUp(self):
1643
 
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1644
 
        self.sock.bind(('127.0.0.1', 0))
1645
 
        self.sock.listen(1)
1646
 
        self.port = self.sock.getsockname()[1]
1647
 
        self.thread = threading.Thread(
1648
 
            name='%s (port %d)' % (self.__class__.__name__, self.port),
1649
 
            target=self.accept_and_close)
1650
 
        self.thread.start()
1651
 
 
1652
 
    def accept_and_close(self):
1653
 
        conn, addr = self.sock.accept()
1654
 
        conn.shutdown(socket.SHUT_RDWR)
1655
 
        conn.close()
1656
 
 
1657
 
    def get_url(self):
1658
 
        return 'bzr://127.0.0.1:%d/' % (self.port,)
1659
 
 
1660
 
    def tearDown(self):
1661
 
        try:
1662
 
            # make sure the thread dies by connecting to the listening socket,
1663
 
            # just in case the test failed to do so.
1664
 
            conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1665
 
            conn.connect(self.sock.getsockname())
1666
 
            conn.close()
1667
 
        except socket.error:
1668
 
            pass
1669
 
        self.sock.close()
1670
 
        self.thread.join()
1671
 
 
 
1549
        self.assertRaises(BadBundle, record_iter.next)