/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_repository.py

  • Committer: Andrew Bennetts
  • Date: 2008-09-08 12:59:00 UTC
  • mfrom: (3695 +trunk)
  • mto: This revision was merged to the branch mainline in revision 3756.
  • Revision ID: andrew.bennetts@canonical.com-20080908125900-8ywtsr7jqyyatjz0
Merge from bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2006, 2007, 2008 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
39
39
from bzrlib.tests import (
40
40
    TestCase,
41
41
    TestCaseWithTransport,
 
42
    TestSkipped,
42
43
    test_knit,
43
44
    )
44
 
from bzrlib.transport import get_transport
 
45
from bzrlib.transport import (
 
46
    fakenfs,
 
47
    get_transport,
 
48
    )
45
49
from bzrlib.transport.memory import MemoryServer
46
50
from bzrlib.util import bencode
47
51
from bzrlib import (
158
162
 
159
163
class TestFormat6(TestCaseWithTransport):
160
164
 
 
165
    def test_attribute__fetch_order(self):
 
166
        """Weaves need topological data insertion."""
 
167
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
168
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
169
        self.assertEqual('topological', repo._fetch_order)
 
170
 
 
171
    def test_attribute__fetch_uses_deltas(self):
 
172
        """Weaves do not reuse deltas."""
 
173
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
174
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
175
        self.assertEqual(False, repo._fetch_uses_deltas)
 
176
 
 
177
    def test_attribute__fetch_reconcile(self):
 
178
        """Weave repositories need a reconcile after fetch."""
 
179
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
180
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
181
        self.assertEqual(True, repo._fetch_reconcile)
 
182
 
161
183
    def test_no_ancestry_weave(self):
162
184
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
163
185
        repo = weaverepo.RepositoryFormat6().initialize(control)
167
189
                          control.transport.get,
168
190
                          'ancestry.weave')
169
191
 
170
 
    def test_exposed_versioned_files_are_marked_dirty(self):
171
 
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
172
 
        repo = weaverepo.RepositoryFormat6().initialize(control)
173
 
        repo.lock_write()
174
 
        inv = repo.get_inventory_weave()
175
 
        repo.unlock()
176
 
        self.assertRaises(errors.OutSideTransaction,
177
 
            inv.add_lines, 'foo', [], [])
178
 
 
179
192
    def test_supports_external_lookups(self):
180
193
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
181
194
        repo = weaverepo.RepositoryFormat6().initialize(control)
183
196
 
184
197
 
185
198
class TestFormat7(TestCaseWithTransport):
186
 
    
 
199
 
 
200
    def test_attribute__fetch_order(self):
 
201
        """Weaves need topological data insertion."""
 
202
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
203
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
204
        self.assertEqual('topological', repo._fetch_order)
 
205
 
 
206
    def test_attribute__fetch_uses_deltas(self):
 
207
        """Weaves do not reuse deltas."""
 
208
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
209
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
210
        self.assertEqual(False, repo._fetch_uses_deltas)
 
211
 
 
212
    def test_attribute__fetch_reconcile(self):
 
213
        """Weave repositories need a reconcile after fetch."""
 
214
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
215
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
216
        self.assertEqual(True, repo._fetch_reconcile)
 
217
 
187
218
    def test_disk_layout(self):
188
219
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
189
220
        repo = weaverepo.RepositoryFormat7().initialize(control)
205
236
                             'w\n'
206
237
                             'W\n',
207
238
                             t.get('inventory.weave').read())
 
239
        # Creating a file with id Foo:Bar results in a non-escaped file name on
 
240
        # disk.
 
241
        control.create_branch()
 
242
        tree = control.create_workingtree()
 
243
        tree.add(['foo'], ['Foo:Bar'], ['file'])
 
244
        tree.put_file_bytes_non_atomic('Foo:Bar', 'content\n')
 
245
        tree.commit('first post', rev_id='first')
 
246
        self.assertEqualDiff(
 
247
            '# bzr weave file v5\n'
 
248
            'i\n'
 
249
            '1 7fe70820e08a1aac0ef224d9c66ab66831cc4ab1\n'
 
250
            'n first\n'
 
251
            '\n'
 
252
            'w\n'
 
253
            '{ 0\n'
 
254
            '. content\n'
 
255
            '}\n'
 
256
            'W\n',
 
257
            t.get('weaves/74/Foo%3ABar.weave').read())
208
258
 
209
259
    def test_shared_disk_layout(self):
210
260
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
285
335
                             'W\n',
286
336
                             t.get('inventory.weave').read())
287
337
 
288
 
    def test_exposed_versioned_files_are_marked_dirty(self):
289
 
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
290
 
        repo = weaverepo.RepositoryFormat7().initialize(control)
291
 
        repo.lock_write()
292
 
        inv = repo.get_inventory_weave()
293
 
        repo.unlock()
294
 
        self.assertRaises(errors.OutSideTransaction,
295
 
            inv.add_lines, 'foo', [], [])
296
 
 
297
338
    def test_supports_external_lookups(self):
298
339
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
299
340
        repo = weaverepo.RepositoryFormat7().initialize(control)
302
343
 
303
344
class TestFormatKnit1(TestCaseWithTransport):
304
345
    
 
346
    def test_attribute__fetch_order(self):
 
347
        """Knits need topological data insertion."""
 
348
        repo = self.make_repository('.',
 
349
                format=bzrdir.format_registry.get('knit')())
 
350
        self.assertEqual('topological', repo._fetch_order)
 
351
 
 
352
    def test_attribute__fetch_uses_deltas(self):
 
353
        """Knits reuse deltas."""
 
354
        repo = self.make_repository('.',
 
355
                format=bzrdir.format_registry.get('knit')())
 
356
        self.assertEqual(True, repo._fetch_uses_deltas)
 
357
 
305
358
    def test_disk_layout(self):
306
359
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
307
360
        repo = knitrepo.RepositoryFormatKnit1().initialize(control)
321
374
        # self.assertEqualDiff('', t.get('lock').read())
322
375
        self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
323
376
        self.check_knits(t)
 
377
        # Check per-file knits.
 
378
        branch = control.create_branch()
 
379
        tree = control.create_workingtree()
 
380
        tree.add(['foo'], ['Nasty-IdC:'], ['file'])
 
381
        tree.put_file_bytes_non_atomic('Nasty-IdC:', '')
 
382
        tree.commit('1st post', rev_id='foo')
 
383
        self.assertHasKnit(t, 'knits/e8/%254easty-%2549d%2543%253a',
 
384
            '\nfoo fulltext 0 81  :')
324
385
 
325
 
    def assertHasKnit(self, t, knit_name):
 
386
    def assertHasKnit(self, t, knit_name, extra_content=''):
326
387
        """Assert that knit_name exists on t."""
327
 
        self.assertEqualDiff('# bzr knit index 8\n',
 
388
        self.assertEqualDiff('# bzr knit index 8\n' + extra_content,
328
389
                             t.get(knit_name + '.kndx').read())
329
 
        # no default content
330
 
        self.assertTrue(t.has(knit_name + '.knit'))
331
390
 
332
391
    def check_knits(self, t):
333
392
        """check knit content for a repository."""
377
436
        self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
378
437
        self.check_knits(t)
379
438
 
380
 
    def test_exposed_versioned_files_are_marked_dirty(self):
381
 
        format = bzrdir.BzrDirMetaFormat1()
382
 
        format.repository_format = knitrepo.RepositoryFormatKnit1()
383
 
        repo = self.make_repository('.', format=format)
384
 
        repo.lock_write()
385
 
        inv = repo.get_inventory_weave()
386
 
        repo.unlock()
387
 
        self.assertRaises(errors.OutSideTransaction,
388
 
            inv.add_lines, 'foo', [], [])
389
 
 
390
439
    def test_deserialise_sets_root_revision(self):
391
440
        """We must have a inventory.root.revision
392
441
 
420
469
        self.assertFalse(repo._format.supports_external_lookups)
421
470
 
422
471
 
423
 
class KnitRepositoryStreamTests(test_knit.KnitTests):
424
 
    """Tests for knitrepo._get_stream_as_bytes."""
425
 
 
426
 
    def test_get_stream_as_bytes(self):
427
 
        # Make a simple knit
428
 
        k1 = self.make_test_knit()
429
 
        k1.add_lines('text-a', [], test_knit.split_lines(test_knit.TEXT_1))
430
 
        
431
 
        # Serialise it, check the output.
432
 
        bytes = knitrepo._get_stream_as_bytes(k1, ['text-a'])
433
 
        data = bencode.bdecode(bytes)
434
 
        format, record = data
435
 
        self.assertEqual('knit-plain', format)
436
 
        self.assertEqual(['text-a', ['fulltext'], []], record[:3])
437
 
        self.assertRecordContentEqual(k1, 'text-a', record[3])
438
 
 
439
 
    def test_get_stream_as_bytes_all(self):
440
 
        """Get a serialised data stream for all the records in a knit.
441
 
 
442
 
        Much like test_get_stream_all, except for get_stream_as_bytes.
443
 
        """
444
 
        k1 = self.make_test_knit()
445
 
        # Insert the same data as BasicKnitTests.test_knit_join, as they seem
446
 
        # to cover a range of cases (no parents, one parent, multiple parents).
447
 
        test_data = [
448
 
            ('text-a', [], test_knit.TEXT_1),
449
 
            ('text-b', ['text-a'], test_knit.TEXT_1),
450
 
            ('text-c', [], test_knit.TEXT_1),
451
 
            ('text-d', ['text-c'], test_knit.TEXT_1),
452
 
            ('text-m', ['text-b', 'text-d'], test_knit.TEXT_1),
453
 
           ]
454
 
        # This test is actually a bit strict as the order in which they're
455
 
        # returned is not defined.  This matches the current (deterministic)
456
 
        # behaviour.
457
 
        expected_data_list = [
458
 
            # version, options, parents
459
 
            ('text-a', ['fulltext'], []),
460
 
            ('text-b', ['line-delta'], ['text-a']),
461
 
            ('text-m', ['line-delta'], ['text-b', 'text-d']),
462
 
            ('text-c', ['fulltext'], []),
463
 
            ('text-d', ['line-delta'], ['text-c']),
464
 
            ]
465
 
        for version_id, parents, lines in test_data:
466
 
            k1.add_lines(version_id, parents, test_knit.split_lines(lines))
467
 
 
468
 
        bytes = knitrepo._get_stream_as_bytes(
469
 
            k1, ['text-a', 'text-b', 'text-m', 'text-c', 'text-d', ])
470
 
 
471
 
        data = bencode.bdecode(bytes)
472
 
        format = data.pop(0)
473
 
        self.assertEqual('knit-plain', format)
474
 
 
475
 
        for expected, actual in zip(expected_data_list, data):
476
 
            expected_version = expected[0]
477
 
            expected_options = expected[1]
478
 
            expected_parents = expected[2]
479
 
            version, options, parents, bytes = actual
480
 
            self.assertEqual(expected_version, version)
481
 
            self.assertEqual(expected_options, options)
482
 
            self.assertEqual(expected_parents, parents)
483
 
            self.assertRecordContentEqual(k1, version, bytes)
484
 
 
485
 
 
486
472
class DummyRepository(object):
487
473
    """A dummy repository for testing."""
488
474
 
593
579
                                                        repo_b).__class__)
594
580
 
595
581
 
596
 
class TestInterRemoteToOther(TestCaseWithTransport):
597
 
 
598
 
    def make_remote_repository(self, path, backing_format=None):
599
 
        """Make a RemoteRepository object backed by a real repository that will
600
 
        be created at the given path."""
601
 
        self.make_repository(path, format=backing_format)
602
 
        smart_server = server.SmartTCPServer_for_testing()
603
 
        smart_server.setUp()
604
 
        remote_transport = get_transport(smart_server.get_url()).clone(path)
605
 
        self.addCleanup(smart_server.tearDown)
606
 
        remote_bzrdir = bzrdir.BzrDir.open_from_transport(remote_transport)
607
 
        remote_repo = remote_bzrdir.open_repository()
608
 
        return remote_repo
609
 
 
610
 
    def test_is_compatible_same_format(self):
611
 
        """InterRemoteToOther is compatible with a remote repository and a
612
 
        second repository that have the same format."""
613
 
        local_repo = self.make_repository('local')
614
 
        remote_repo = self.make_remote_repository('remote')
615
 
        is_compatible = repository.InterRemoteToOther.is_compatible
616
 
        self.assertTrue(
617
 
            is_compatible(remote_repo, local_repo),
618
 
            "InterRemoteToOther(%r, %r) is false" % (remote_repo, local_repo))
619
 
          
620
 
    def test_is_incompatible_different_format(self):
621
 
        local_repo = self.make_repository('local', 'dirstate')
622
 
        remote_repo = self.make_remote_repository('a', 'dirstate-with-subtree')
623
 
        is_compatible = repository.InterRemoteToOther.is_compatible
624
 
        self.assertFalse(
625
 
            is_compatible(remote_repo, local_repo),
626
 
            "InterRemoteToOther(%r, %r) is true" % (local_repo, remote_repo))
627
 
 
628
 
    def test_is_incompatible_different_format_both_remote(self):
629
 
        remote_repo_a = self.make_remote_repository(
630
 
            'a', 'dirstate-with-subtree')
631
 
        remote_repo_b = self.make_remote_repository('b', 'dirstate')
632
 
        is_compatible = repository.InterRemoteToOther.is_compatible
633
 
        self.assertFalse(
634
 
            is_compatible(remote_repo_a, remote_repo_b),
635
 
            "InterRemoteToOther(%r, %r) is true"
636
 
            % (remote_repo_a, remote_repo_b))
637
 
 
638
 
 
639
582
class TestRepositoryConverter(TestCaseWithTransport):
640
583
 
641
584
    def test_convert_empty(self):
663
606
 
664
607
class TestRepositoryFormatKnit3(TestCaseWithTransport):
665
608
 
 
609
    def test_attribute__fetch_order(self):
 
610
        """Knits need topological data insertion."""
 
611
        format = bzrdir.BzrDirMetaFormat1()
 
612
        format.repository_format = knitrepo.RepositoryFormatKnit3()
 
613
        repo = self.make_repository('.', format=format)
 
614
        self.assertEqual('topological', repo._fetch_order)
 
615
 
 
616
    def test_attribute__fetch_uses_deltas(self):
 
617
        """Knits reuse deltas."""
 
618
        format = bzrdir.BzrDirMetaFormat1()
 
619
        format.repository_format = knitrepo.RepositoryFormatKnit3()
 
620
        repo = self.make_repository('.', format=format)
 
621
        self.assertEqual(True, repo._fetch_uses_deltas)
 
622
 
666
623
    def test_convert(self):
667
624
        """Ensure the upgrade adds weaves for roots"""
668
625
        format = bzrdir.BzrDirMetaFormat1()
670
627
        tree = self.make_branch_and_tree('.', format)
671
628
        tree.commit("Dull commit", rev_id="dull")
672
629
        revision_tree = tree.branch.repository.revision_tree('dull')
673
 
        self.assertRaises(errors.NoSuchFile, revision_tree.get_file_lines,
674
 
            revision_tree.inventory.root.file_id)
 
630
        revision_tree.lock_read()
 
631
        try:
 
632
            self.assertRaises(errors.NoSuchFile, revision_tree.get_file_lines,
 
633
                revision_tree.inventory.root.file_id)
 
634
        finally:
 
635
            revision_tree.unlock()
675
636
        format = bzrdir.BzrDirMetaFormat1()
676
637
        format.repository_format = knitrepo.RepositoryFormatKnit3()
677
638
        upgrade.Convert('.', format)
678
639
        tree = workingtree.WorkingTree.open('.')
679
640
        revision_tree = tree.branch.repository.revision_tree('dull')
680
 
        revision_tree.get_file_lines(revision_tree.inventory.root.file_id)
 
641
        revision_tree.lock_read()
 
642
        try:
 
643
            revision_tree.get_file_lines(revision_tree.inventory.root.file_id)
 
644
        finally:
 
645
            revision_tree.unlock()
681
646
        tree.commit("Another dull commit", rev_id='dull2')
682
647
        revision_tree = tree.branch.repository.revision_tree('dull2')
 
648
        revision_tree.lock_read()
 
649
        self.addCleanup(revision_tree.unlock)
683
650
        self.assertEqual('dull', revision_tree.inventory.root.revision)
684
651
 
685
 
    def test_exposed_versioned_files_are_marked_dirty(self):
686
 
        format = bzrdir.BzrDirMetaFormat1()
687
 
        format.repository_format = knitrepo.RepositoryFormatKnit3()
688
 
        repo = self.make_repository('.', format=format)
689
 
        repo.lock_write()
690
 
        inv = repo.get_inventory_weave()
691
 
        repo.unlock()
692
 
        self.assertRaises(errors.OutSideTransaction,
693
 
            inv.add_lines, 'foo', [], [])
694
 
 
695
652
    def test_supports_external_lookups(self):
696
653
        format = bzrdir.BzrDirMetaFormat1()
697
654
        format.repository_format = knitrepo.RepositoryFormatKnit3()
769
726
        entry.revision = revision
770
727
        entry.text_size = 0
771
728
        inv.add(entry)
772
 
        vf = repo.weave_store.get_weave_or_empty(file_id,
773
 
                                                 repo.get_transaction())
774
 
        vf.add_lines(revision, parents, ['line\n'])
 
729
        text_key = (file_id, revision)
 
730
        parent_keys = [(file_id, parent) for parent in parents]
 
731
        repo.texts.add_lines(text_key, parent_keys, ['line\n'])
775
732
 
776
733
    def test_insert_from_broken_repo(self):
777
734
        """Inserting a data stream from a broken repository won't silently
779
736
        """
780
737
        broken_repo = self.make_broken_repository()
781
738
        empty_repo = self.make_repository('empty-repo')
782
 
        search = graph.SearchResult(set(['rev1a', 'rev2', 'rev3']),
783
 
            set(), 3, ['rev1a', 'rev2', 'rev3'])
784
 
        stream = broken_repo.get_data_stream_for_search(search)
785
 
        empty_repo.lock_write()
786
 
        self.addCleanup(empty_repo.unlock)
787
 
        empty_repo.start_write_group()
788
 
        try:
789
 
            self.assertRaises(
790
 
                errors.KnitCorrupt, empty_repo.insert_data_stream, stream)
791
 
        finally:
792
 
            empty_repo.abort_write_group()
793
 
 
794
 
 
795
 
class TestKnitPackNoSubtrees(TestCaseWithTransport):
796
 
 
797
 
    def get_format(self):
798
 
        return bzrdir.format_registry.make_bzrdir('pack-0.92')
799
 
 
800
 
    def test_disk_layout(self):
801
 
        format = self.get_format()
802
 
        repo = self.make_repository('.', format=format)
803
 
        # in case of side effects of locking.
804
 
        repo.lock_write()
805
 
        repo.unlock()
806
 
        t = repo.bzrdir.get_repository_transport(None)
807
 
        self.check_format(t)
808
 
        # XXX: no locks left when unlocked at the moment
809
 
        # self.assertEqualDiff('', t.get('lock').read())
810
 
        self.check_databases(t)
811
 
 
812
 
    def check_format(self, t):
813
 
        self.assertEqualDiff(
814
 
            "Bazaar pack repository format 1 (needs bzr 0.92)\n",
815
 
                             t.get('format').read())
816
 
 
817
 
    def assertHasKndx(self, t, knit_name):
818
 
        """Assert that knit_name exists on t."""
819
 
        self.assertEqualDiff('# bzr knit index 8\n',
820
 
                             t.get(knit_name + '.kndx').read())
821
 
 
822
 
    def assertHasNoKndx(self, t, knit_name):
823
 
        """Assert that knit_name has no index on t."""
824
 
        self.assertFalse(t.has(knit_name + '.kndx'))
825
 
 
826
 
    def assertHasNoKnit(self, t, knit_name):
827
 
        """Assert that knit_name exists on t."""
828
 
        # no default content
829
 
        self.assertFalse(t.has(knit_name + '.knit'))
830
 
 
831
 
    def check_databases(self, t):
832
 
        """check knit content for a repository."""
833
 
        # check conversion worked
834
 
        self.assertHasNoKndx(t, 'inventory')
835
 
        self.assertHasNoKnit(t, 'inventory')
836
 
        self.assertHasNoKndx(t, 'revisions')
837
 
        self.assertHasNoKnit(t, 'revisions')
838
 
        self.assertHasNoKndx(t, 'signatures')
839
 
        self.assertHasNoKnit(t, 'signatures')
840
 
        self.assertFalse(t.has('knits'))
841
 
        # revision-indexes file-container directory
842
 
        self.assertEqual([],
843
 
            list(GraphIndex(t, 'pack-names', None).iter_all_entries()))
844
 
        self.assertTrue(S_ISDIR(t.stat('packs').st_mode))
845
 
        self.assertTrue(S_ISDIR(t.stat('upload').st_mode))
846
 
        self.assertTrue(S_ISDIR(t.stat('indices').st_mode))
847
 
        self.assertTrue(S_ISDIR(t.stat('obsolete_packs').st_mode))
848
 
 
849
 
    def test_shared_disk_layout(self):
850
 
        format = self.get_format()
851
 
        repo = self.make_repository('.', shared=True, format=format)
852
 
        # we want:
853
 
        t = repo.bzrdir.get_repository_transport(None)
854
 
        self.check_format(t)
855
 
        # XXX: no locks left when unlocked at the moment
856
 
        # self.assertEqualDiff('', t.get('lock').read())
857
 
        # We should have a 'shared-storage' marker file.
858
 
        self.assertEqualDiff('', t.get('shared-storage').read())
859
 
        self.check_databases(t)
860
 
 
861
 
    def test_shared_no_tree_disk_layout(self):
862
 
        format = self.get_format()
863
 
        repo = self.make_repository('.', shared=True, format=format)
864
 
        repo.set_make_working_trees(False)
865
 
        # we want:
866
 
        t = repo.bzrdir.get_repository_transport(None)
867
 
        self.check_format(t)
868
 
        # XXX: no locks left when unlocked at the moment
869
 
        # self.assertEqualDiff('', t.get('lock').read())
870
 
        # We should have a 'shared-storage' marker file.
871
 
        self.assertEqualDiff('', t.get('shared-storage').read())
872
 
        # We should have a marker for the no-working-trees flag.
873
 
        self.assertEqualDiff('', t.get('no-working-trees').read())
874
 
        # The marker should go when we toggle the setting.
875
 
        repo.set_make_working_trees(True)
876
 
        self.assertFalse(t.has('no-working-trees'))
877
 
        self.check_databases(t)
878
 
 
879
 
    def test_adding_revision_creates_pack_indices(self):
880
 
        format = self.get_format()
881
 
        tree = self.make_branch_and_tree('.', format=format)
882
 
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
883
 
        self.assertEqual([],
884
 
            list(GraphIndex(trans, 'pack-names', None).iter_all_entries()))
885
 
        tree.commit('foobarbaz')
886
 
        index = GraphIndex(trans, 'pack-names', None)
887
 
        index_nodes = list(index.iter_all_entries())
888
 
        self.assertEqual(1, len(index_nodes))
889
 
        node = index_nodes[0]
890
 
        name = node[1][0]
891
 
        # the pack sizes should be listed in the index
892
 
        pack_value = node[2]
893
 
        sizes = [int(digits) for digits in pack_value.split(' ')]
894
 
        for size, suffix in zip(sizes, ['.rix', '.iix', '.tix', '.six']):
895
 
            stat = trans.stat('indices/%s%s' % (name, suffix))
896
 
            self.assertEqual(size, stat.st_size)
897
 
 
898
 
    def test_pulling_nothing_leads_to_no_new_names(self):
899
 
        format = self.get_format()
900
 
        tree1 = self.make_branch_and_tree('1', format=format)
901
 
        tree2 = self.make_branch_and_tree('2', format=format)
902
 
        tree1.branch.repository.fetch(tree2.branch.repository)
903
 
        trans = tree1.branch.repository.bzrdir.get_repository_transport(None)
904
 
        self.assertEqual([],
905
 
            list(GraphIndex(trans, 'pack-names', None).iter_all_entries()))
906
 
 
907
 
    def test_commit_across_pack_shape_boundary_autopacks(self):
908
 
        format = self.get_format()
909
 
        tree = self.make_branch_and_tree('.', format=format)
910
 
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
911
 
        # This test could be a little cheaper by replacing the packs
912
 
        # attribute on the repository to allow a different pack distribution
913
 
        # and max packs policy - so we are checking the policy is honoured
914
 
        # in the test. But for now 11 commits is not a big deal in a single
915
 
        # test.
916
 
        for x in range(9):
917
 
            tree.commit('commit %s' % x)
918
 
        # there should be 9 packs:
919
 
        index = GraphIndex(trans, 'pack-names', None)
920
 
        self.assertEqual(9, len(list(index.iter_all_entries())))
921
 
        # insert some files in obsolete_packs which should be removed by pack.
922
 
        trans.put_bytes('obsolete_packs/foo', '123')
923
 
        trans.put_bytes('obsolete_packs/bar', '321')
924
 
        # committing one more should coalesce to 1 of 10.
925
 
        tree.commit('commit triggering pack')
926
 
        index = GraphIndex(trans, 'pack-names', None)
927
 
        self.assertEqual(1, len(list(index.iter_all_entries())))
928
 
        # packing should not damage data
929
 
        tree = tree.bzrdir.open_workingtree()
930
 
        check_result = tree.branch.repository.check(
931
 
            [tree.branch.last_revision()])
932
 
        # We should have 50 (10x5) files in the obsolete_packs directory.
933
 
        obsolete_files = list(trans.list_dir('obsolete_packs'))
934
 
        self.assertFalse('foo' in obsolete_files)
935
 
        self.assertFalse('bar' in obsolete_files)
936
 
        self.assertEqual(50, len(obsolete_files))
937
 
        # XXX: Todo check packs obsoleted correctly - old packs and indices
938
 
        # in the obsolete_packs directory.
939
 
        large_pack_name = list(index.iter_all_entries())[0][1][0]
940
 
        # finally, committing again should not touch the large pack.
941
 
        tree.commit('commit not triggering pack')
942
 
        index = GraphIndex(trans, 'pack-names', None)
943
 
        self.assertEqual(2, len(list(index.iter_all_entries())))
944
 
        pack_names = [node[1][0] for node in index.iter_all_entries()]
945
 
        self.assertTrue(large_pack_name in pack_names)
946
 
 
947
 
    def test_pack_after_two_commits_packs_everything(self):
948
 
        format = self.get_format()
949
 
        tree = self.make_branch_and_tree('.', format=format)
950
 
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
951
 
        tree.commit('start')
952
 
        tree.commit('more work')
953
 
        tree.branch.repository.pack()
954
 
        # there should be 1 pack:
955
 
        index = GraphIndex(trans, 'pack-names', None)
956
 
        self.assertEqual(1, len(list(index.iter_all_entries())))
957
 
        self.assertEqual(2, len(tree.branch.repository.all_revision_ids()))
958
 
 
959
 
    def test_pack_layout(self):
960
 
        format = self.get_format()
961
 
        tree = self.make_branch_and_tree('.', format=format)
962
 
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
963
 
        tree.commit('start', rev_id='1')
964
 
        tree.commit('more work', rev_id='2')
965
 
        tree.branch.repository.pack()
966
 
        tree.lock_read()
967
 
        self.addCleanup(tree.unlock)
968
 
        pack = tree.branch.repository._pack_collection.get_pack_by_name(
969
 
            tree.branch.repository._pack_collection.names()[0])
970
 
        # revision access tends to be tip->ancestor, so ordering that way on 
971
 
        # disk is a good idea.
972
 
        for _1, key, val, refs in pack.revision_index.iter_all_entries():
973
 
            if key == ('1',):
974
 
                pos_1 = int(val[1:].split()[0])
975
 
            else:
976
 
                pos_2 = int(val[1:].split()[0])
977
 
        self.assertTrue(pos_2 < pos_1)
978
 
 
979
 
    def test_pack_repositories_support_multiple_write_locks(self):
980
 
        format = self.get_format()
981
 
        self.make_repository('.', shared=True, format=format)
982
 
        r1 = repository.Repository.open('.')
983
 
        r2 = repository.Repository.open('.')
984
 
        r1.lock_write()
985
 
        self.addCleanup(r1.unlock)
986
 
        r2.lock_write()
987
 
        r2.unlock()
988
 
 
989
 
    def _add_text(self, repo, fileid):
990
 
        """Add a text to the repository within a write group."""
991
 
        vf =repo.weave_store.get_weave(fileid, repo.get_transaction())
992
 
        vf.add_lines('samplerev+' + fileid, [], [])
993
 
 
994
 
    def test_concurrent_writers_merge_new_packs(self):
995
 
        format = self.get_format()
996
 
        self.make_repository('.', shared=True, format=format)
997
 
        r1 = repository.Repository.open('.')
998
 
        r2 = repository.Repository.open('.')
999
 
        r1.lock_write()
1000
 
        try:
1001
 
            # access enough data to load the names list
1002
 
            list(r1.all_revision_ids())
1003
 
            r2.lock_write()
1004
 
            try:
1005
 
                # access enough data to load the names list
1006
 
                list(r2.all_revision_ids())
1007
 
                r1.start_write_group()
1008
 
                try:
1009
 
                    r2.start_write_group()
1010
 
                    try:
1011
 
                        self._add_text(r1, 'fileidr1')
1012
 
                        self._add_text(r2, 'fileidr2')
1013
 
                    except:
1014
 
                        r2.abort_write_group()
1015
 
                        raise
1016
 
                except:
1017
 
                    r1.abort_write_group()
1018
 
                    raise
1019
 
                # both r1 and r2 have open write groups with data in them
1020
 
                # created while the other's write group was open.
1021
 
                # Commit both which requires a merge to the pack-names.
1022
 
                try:
1023
 
                    r1.commit_write_group()
1024
 
                except:
1025
 
                    r1.abort_write_group()
1026
 
                    r2.abort_write_group()
1027
 
                    raise
1028
 
                r2.commit_write_group()
1029
 
                # tell r1 to reload from disk
1030
 
                r1._pack_collection.reset()
1031
 
                # Now both repositories should know about both names
1032
 
                r1._pack_collection.ensure_loaded()
1033
 
                r2._pack_collection.ensure_loaded()
1034
 
                self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
1035
 
                self.assertEqual(2, len(r1._pack_collection.names()))
1036
 
            finally:
1037
 
                r2.unlock()
1038
 
        finally:
1039
 
            r1.unlock()
1040
 
 
1041
 
    def test_concurrent_writer_second_preserves_dropping_a_pack(self):
1042
 
        format = self.get_format()
1043
 
        self.make_repository('.', shared=True, format=format)
1044
 
        r1 = repository.Repository.open('.')
1045
 
        r2 = repository.Repository.open('.')
1046
 
        # add a pack to drop
1047
 
        r1.lock_write()
1048
 
        try:
1049
 
            r1.start_write_group()
1050
 
            try:
1051
 
                self._add_text(r1, 'fileidr1')
1052
 
            except:
1053
 
                r1.abort_write_group()
1054
 
                raise
1055
 
            else:
1056
 
                r1.commit_write_group()
1057
 
            r1._pack_collection.ensure_loaded()
1058
 
            name_to_drop = r1._pack_collection.all_packs()[0].name
1059
 
        finally:
1060
 
            r1.unlock()
1061
 
        r1.lock_write()
1062
 
        try:
1063
 
            # access enough data to load the names list
1064
 
            list(r1.all_revision_ids())
1065
 
            r2.lock_write()
1066
 
            try:
1067
 
                # access enough data to load the names list
1068
 
                list(r2.all_revision_ids())
1069
 
                r1._pack_collection.ensure_loaded()
1070
 
                try:
1071
 
                    r2.start_write_group()
1072
 
                    try:
1073
 
                        # in r1, drop the pack
1074
 
                        r1._pack_collection._remove_pack_from_memory(
1075
 
                            r1._pack_collection.get_pack_by_name(name_to_drop))
1076
 
                        # in r2, add a pack
1077
 
                        self._add_text(r2, 'fileidr2')
1078
 
                    except:
1079
 
                        r2.abort_write_group()
1080
 
                        raise
1081
 
                except:
1082
 
                    r1._pack_collection.reset()
1083
 
                    raise
1084
 
                # r1 has a changed names list, and r2 an open write groups with
1085
 
                # changes.
1086
 
                # save r1, and then commit the r2 write group, which requires a
1087
 
                # merge to the pack-names, which should not reinstate
1088
 
                # name_to_drop
1089
 
                try:
1090
 
                    r1._pack_collection._save_pack_names()
1091
 
                    r1._pack_collection.reset()
1092
 
                except:
1093
 
                    r2.abort_write_group()
1094
 
                    raise
1095
 
                try:
1096
 
                    r2.commit_write_group()
1097
 
                except:
1098
 
                    r2.abort_write_group()
1099
 
                    raise
1100
 
                # Now both repositories should now about just one name.
1101
 
                r1._pack_collection.ensure_loaded()
1102
 
                r2._pack_collection.ensure_loaded()
1103
 
                self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
1104
 
                self.assertEqual(1, len(r1._pack_collection.names()))
1105
 
                self.assertFalse(name_to_drop in r1._pack_collection.names())
1106
 
            finally:
1107
 
                r2.unlock()
1108
 
        finally:
1109
 
            r1.unlock()
1110
 
 
1111
 
    def test_lock_write_does_not_physically_lock(self):
1112
 
        repo = self.make_repository('.', format=self.get_format())
1113
 
        repo.lock_write()
1114
 
        self.addCleanup(repo.unlock)
1115
 
        self.assertFalse(repo.get_physical_lock_status())
1116
 
 
1117
 
    def prepare_for_break_lock(self):
1118
 
        # Setup the global ui factory state so that a break-lock method call
1119
 
        # will find usable input in the input stream.
1120
 
        old_factory = bzrlib.ui.ui_factory
1121
 
        def restoreFactory():
1122
 
            bzrlib.ui.ui_factory = old_factory
1123
 
        self.addCleanup(restoreFactory)
1124
 
        bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
1125
 
        bzrlib.ui.ui_factory.stdin = StringIO("y\n")
1126
 
 
1127
 
    def test_break_lock_breaks_physical_lock(self):
1128
 
        repo = self.make_repository('.', format=self.get_format())
1129
 
        repo._pack_collection.lock_names()
1130
 
        repo2 = repository.Repository.open('.')
1131
 
        self.assertTrue(repo.get_physical_lock_status())
1132
 
        self.prepare_for_break_lock()
1133
 
        repo2.break_lock()
1134
 
        self.assertFalse(repo.get_physical_lock_status())
1135
 
 
1136
 
    def test_broken_physical_locks_error_on__unlock_names_lock(self):
1137
 
        repo = self.make_repository('.', format=self.get_format())
1138
 
        repo._pack_collection.lock_names()
1139
 
        self.assertTrue(repo.get_physical_lock_status())
1140
 
        repo2 = repository.Repository.open('.')
1141
 
        self.prepare_for_break_lock()
1142
 
        repo2.break_lock()
1143
 
        self.assertRaises(errors.LockBroken, repo._pack_collection._unlock_names)
1144
 
 
1145
 
    def test_fetch_without_find_ghosts_ignores_ghosts(self):
1146
 
        # we want two repositories at this point:
1147
 
        # one with a revision that is a ghost in the other
1148
 
        # repository.
1149
 
        # 'ghost' is present in has_ghost, 'ghost' is absent in 'missing_ghost'.
1150
 
        # 'references' is present in both repositories, and 'tip' is present
1151
 
        # just in has_ghost.
1152
 
        # has_ghost       missing_ghost
1153
 
        #------------------------------
1154
 
        # 'ghost'             -
1155
 
        # 'references'    'references'
1156
 
        # 'tip'               -
1157
 
        # In this test we fetch 'tip' which should not fetch 'ghost'
1158
 
        has_ghost = self.make_repository('has_ghost', format=self.get_format())
1159
 
        missing_ghost = self.make_repository('missing_ghost',
1160
 
            format=self.get_format())
1161
 
 
1162
 
        def add_commit(repo, revision_id, parent_ids):
1163
 
            repo.lock_write()
1164
 
            repo.start_write_group()
1165
 
            inv = inventory.Inventory(revision_id=revision_id)
1166
 
            inv.root.revision = revision_id
1167
 
            root_id = inv.root.file_id
1168
 
            sha1 = repo.add_inventory(revision_id, inv, [])
1169
 
            vf = repo.weave_store.get_weave_or_empty(root_id,
1170
 
                repo.get_transaction())
1171
 
            vf.add_lines(revision_id, [], [])
1172
 
            rev = bzrlib.revision.Revision(timestamp=0,
1173
 
                                           timezone=None,
1174
 
                                           committer="Foo Bar <foo@example.com>",
1175
 
                                           message="Message",
1176
 
                                           inventory_sha1=sha1,
1177
 
                                           revision_id=revision_id)
1178
 
            rev.parent_ids = parent_ids
1179
 
            repo.add_revision(revision_id, rev)
1180
 
            repo.commit_write_group()
1181
 
            repo.unlock()
1182
 
        add_commit(has_ghost, 'ghost', [])
1183
 
        add_commit(has_ghost, 'references', ['ghost'])
1184
 
        add_commit(missing_ghost, 'references', ['ghost'])
1185
 
        add_commit(has_ghost, 'tip', ['references'])
1186
 
        missing_ghost.fetch(has_ghost, 'tip')
1187
 
        # missing ghost now has tip and not ghost.
1188
 
        rev = missing_ghost.get_revision('tip')
1189
 
        inv = missing_ghost.get_inventory('tip')
1190
 
        self.assertRaises(errors.NoSuchRevision,
1191
 
            missing_ghost.get_revision, 'ghost')
1192
 
        self.assertRaises(errors.RevisionNotPresent,
1193
 
            missing_ghost.get_inventory, 'ghost')
1194
 
 
1195
 
    def test_supports_external_lookups(self):
1196
 
        repo = self.make_repository('.', format=self.get_format())
1197
 
        self.assertFalse(repo._format.supports_external_lookups)
1198
 
 
1199
 
 
1200
 
class TestKnitPackSubtrees(TestKnitPackNoSubtrees):
1201
 
 
1202
 
    def get_format(self):
1203
 
        return bzrdir.format_registry.make_bzrdir(
1204
 
            'pack-0.92-subtree')
1205
 
 
1206
 
    def check_format(self, t):
1207
 
        self.assertEqualDiff(
1208
 
            "Bazaar pack repository format 1 with subtree support (needs bzr 0.92)\n",
1209
 
            t.get('format').read())
1210
 
 
1211
 
 
1212
 
class TestDevelopment0(TestKnitPackNoSubtrees):
1213
 
 
1214
 
    def get_format(self):
1215
 
        return bzrdir.format_registry.make_bzrdir(
1216
 
            'development')
1217
 
 
1218
 
    def check_format(self, t):
1219
 
        self.assertEqualDiff(
1220
 
            "Bazaar development format 0 (needs bzr.dev from before 1.3)\n",
1221
 
            t.get('format').read())
1222
 
 
1223
 
 
1224
 
class TestDevelopment0Subtree(TestKnitPackNoSubtrees):
1225
 
 
1226
 
    def get_format(self):
1227
 
        return bzrdir.format_registry.make_bzrdir(
1228
 
            'development-subtree')
1229
 
 
1230
 
    def check_format(self, t):
1231
 
        self.assertEqualDiff(
1232
 
            "Bazaar development format 0 with subtree support "
1233
 
            "(needs bzr.dev from before 1.3)\n",
1234
 
            t.get('format').read())
 
739
        self.assertRaises(errors.RevisionNotPresent, empty_repo.fetch, broken_repo)
1235
740
 
1236
741
 
1237
742
class TestRepositoryPackCollection(TestCaseWithTransport):
1396
901
        name = packs.names()[0]
1397
902
        pack_1 = packs.get_pack_by_name(name)
1398
903
        # the pack should be correctly initialised
1399
 
        rev_index = GraphIndex(packs._index_transport, name + '.rix',
1400
 
            packs._names[name][0])
1401
 
        inv_index = GraphIndex(packs._index_transport, name + '.iix',
1402
 
            packs._names[name][1])
1403
 
        txt_index = GraphIndex(packs._index_transport, name + '.tix',
1404
 
            packs._names[name][2])
1405
 
        sig_index = GraphIndex(packs._index_transport, name + '.six',
1406
 
            packs._names[name][3])
 
904
        sizes = packs._names[name]
 
905
        rev_index = GraphIndex(packs._index_transport, name + '.rix', sizes[0])
 
906
        inv_index = GraphIndex(packs._index_transport, name + '.iix', sizes[1])
 
907
        txt_index = GraphIndex(packs._index_transport, name + '.tix', sizes[2])
 
908
        sig_index = GraphIndex(packs._index_transport, name + '.six', sizes[3])
1407
909
        self.assertEqual(pack_repo.ExistingPack(packs._pack_transport,
1408
910
            name, rev_index, inv_index, txt_index, sig_index), pack_1)
1409
911
        # and the same instance should be returned on successive calls.