~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_repository.py

  • Committer: John Arbash Meinel
  • Date: 2009-07-08 15:43:51 UTC
  • mto: This revision was merged to the branch mainline in revision 4521.
  • Revision ID: john@arbash-meinel.com-20090708154351-u0t41fwjqm28pbnu
Add comments in the finally sections as to why we want them.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2011 Canonical Ltd
 
1
# Copyright (C) 2006, 2007, 2008, 2009 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
23
23
"""
24
24
 
25
25
from stat import S_ISDIR
 
26
from StringIO import StringIO
26
27
 
27
28
import bzrlib
28
 
from bzrlib.errors import (
29
 
    UnknownFormatError,
30
 
    UnsupportedFormatError,
31
 
    )
 
29
from bzrlib.errors import (NotBranchError,
 
30
                           NoSuchFile,
 
31
                           UnknownFormatError,
 
32
                           UnsupportedFormatError,
 
33
                           )
32
34
from bzrlib import (
33
 
    btree_index,
34
35
    graph,
35
 
    symbol_versioning,
36
36
    tests,
37
 
    transport,
38
37
    )
 
38
from bzrlib.branchbuilder import BranchBuilder
39
39
from bzrlib.btree_index import BTreeBuilder, BTreeGraphIndex
40
 
from bzrlib.index import GraphIndex
 
40
from bzrlib.index import GraphIndex, InMemoryGraphIndex
41
41
from bzrlib.repository import RepositoryFormat
 
42
from bzrlib.smart import server
42
43
from bzrlib.tests import (
43
44
    TestCase,
44
45
    TestCaseWithTransport,
45
 
    )
 
46
    TestSkipped,
 
47
    test_knit,
 
48
    )
 
49
from bzrlib.transport import (
 
50
    fakenfs,
 
51
    get_transport,
 
52
    )
 
53
from bzrlib.transport.memory import MemoryServer
46
54
from bzrlib import (
 
55
    bencode,
47
56
    bzrdir,
48
57
    errors,
49
58
    inventory,
50
59
    osutils,
 
60
    progress,
51
61
    repository,
52
62
    revision as _mod_revision,
 
63
    symbol_versioning,
53
64
    upgrade,
54
 
    versionedfile,
55
 
    vf_repository,
56
65
    workingtree,
57
66
    )
58
67
from bzrlib.repofmt import (
59
68
    groupcompress_repo,
60
69
    knitrepo,
61
 
    knitpack_repo,
62
70
    pack_repo,
 
71
    weaverepo,
63
72
    )
64
73
 
65
74
 
68
77
    def test_get_set_default_format(self):
69
78
        old_default = bzrdir.format_registry.get('default')
70
79
        private_default = old_default().repository_format.__class__
71
 
        old_format = repository.format_registry.get_default()
 
80
        old_format = repository.RepositoryFormat.get_default_format()
72
81
        self.assertTrue(isinstance(old_format, private_default))
73
82
        def make_sample_bzrdir():
74
83
            my_bzrdir = bzrdir.BzrDirMetaFormat1()
88
97
            bzrdir.format_registry.remove('default')
89
98
            bzrdir.format_registry.remove('sample')
90
99
            bzrdir.format_registry.register('default', old_default, '')
91
 
        self.assertIsInstance(repository.format_registry.get_default(),
 
100
        self.assertIsInstance(repository.RepositoryFormat.get_default_format(),
92
101
                              old_format.__class__)
93
102
 
94
103
 
116
125
        return "opened repository."
117
126
 
118
127
 
119
 
class SampleExtraRepositoryFormat(repository.RepositoryFormat):
120
 
    """A sample format that can not be used in a metadir
121
 
 
122
 
    """
123
 
 
124
 
    def get_format_string(self):
125
 
        raise NotImplementedError
126
 
 
127
 
 
128
128
class TestRepositoryFormat(TestCaseWithTransport):
129
129
    """Tests for the Repository format detection used by the bzr meta dir facility.BzrBranchFormat facility."""
130
130
 
136
136
        def check_format(format, url):
137
137
            dir = format._matchingbzrdir.initialize(url)
138
138
            format.initialize(dir)
139
 
            t = transport.get_transport(url)
 
139
            t = get_transport(url)
140
140
            found_format = repository.RepositoryFormat.find_format(dir)
141
 
            self.assertIsInstance(found_format, format.__class__)
142
 
        check_format(repository.format_registry.get_default(), "bar")
 
141
            self.failUnless(isinstance(found_format, format.__class__))
 
142
        check_format(weaverepo.RepositoryFormat7(), "bar")
143
143
 
144
144
    def test_find_format_no_repository(self):
145
145
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
155
155
                          dir)
156
156
 
157
157
    def test_register_unregister_format(self):
158
 
        # Test deprecated format registration functions
159
158
        format = SampleRepositoryFormat()
160
159
        # make a control dir
161
160
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
162
161
        # make a repo
163
162
        format.initialize(dir)
164
163
        # register a format for it.
165
 
        self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
166
 
            repository.RepositoryFormat.register_format, format)
 
164
        repository.RepositoryFormat.register_format(format)
167
165
        # which repository.Open will refuse (not supported)
168
 
        self.assertRaises(UnsupportedFormatError, repository.Repository.open,
169
 
            self.get_url())
 
166
        self.assertRaises(UnsupportedFormatError, repository.Repository.open, self.get_url())
170
167
        # but open(unsupported) will work
171
168
        self.assertEqual(format.open(dir), "opened repository.")
172
169
        # unregister the format
173
 
        self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
174
 
            repository.RepositoryFormat.unregister_format, format)
175
 
 
176
 
 
177
 
class TestRepositoryFormatRegistry(TestCase):
178
 
 
179
 
    def setUp(self):
180
 
        super(TestRepositoryFormatRegistry, self).setUp()
181
 
        self.registry = repository.RepositoryFormatRegistry()
182
 
 
183
 
    def test_register_unregister_format(self):
184
 
        format = SampleRepositoryFormat()
185
 
        self.registry.register(format)
186
 
        self.assertEquals(format, self.registry.get("Sample .bzr repository format."))
187
 
        self.registry.remove(format)
188
 
        self.assertRaises(KeyError, self.registry.get, "Sample .bzr repository format.")
189
 
 
190
 
    def test_get_all(self):
191
 
        format = SampleRepositoryFormat()
192
 
        self.assertEquals([], self.registry._get_all())
193
 
        self.registry.register(format)
194
 
        self.assertEquals([format], self.registry._get_all())
195
 
 
196
 
    def test_register_extra(self):
197
 
        format = SampleExtraRepositoryFormat()
198
 
        self.assertEquals([], self.registry._get_all())
199
 
        self.registry.register_extra(format)
200
 
        self.assertEquals([format], self.registry._get_all())
201
 
 
202
 
    def test_register_extra_lazy(self):
203
 
        self.assertEquals([], self.registry._get_all())
204
 
        self.registry.register_extra_lazy("bzrlib.tests.test_repository",
205
 
            "SampleExtraRepositoryFormat")
206
 
        formats = self.registry._get_all()
207
 
        self.assertEquals(1, len(formats))
208
 
        self.assertIsInstance(formats[0], SampleExtraRepositoryFormat)
 
170
        repository.RepositoryFormat.unregister_format(format)
 
171
 
 
172
 
 
173
class TestFormat6(TestCaseWithTransport):
 
174
 
 
175
    def test_attribute__fetch_order(self):
 
176
        """Weaves need topological data insertion."""
 
177
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
178
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
179
        self.assertEqual('topological', repo._format._fetch_order)
 
180
 
 
181
    def test_attribute__fetch_uses_deltas(self):
 
182
        """Weaves do not reuse deltas."""
 
183
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
184
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
185
        self.assertEqual(False, repo._format._fetch_uses_deltas)
 
186
 
 
187
    def test_attribute__fetch_reconcile(self):
 
188
        """Weave repositories need a reconcile after fetch."""
 
189
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
190
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
191
        self.assertEqual(True, repo._format._fetch_reconcile)
 
192
 
 
193
    def test_no_ancestry_weave(self):
 
194
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
195
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
196
        # We no longer need to create the ancestry.weave file
 
197
        # since it is *never* used.
 
198
        self.assertRaises(NoSuchFile,
 
199
                          control.transport.get,
 
200
                          'ancestry.weave')
 
201
 
 
202
    def test_supports_external_lookups(self):
 
203
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
204
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
205
        self.assertFalse(repo._format.supports_external_lookups)
 
206
 
 
207
 
 
208
class TestFormat7(TestCaseWithTransport):
 
209
 
 
210
    def test_attribute__fetch_order(self):
 
211
        """Weaves need topological data insertion."""
 
212
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
213
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
214
        self.assertEqual('topological', repo._format._fetch_order)
 
215
 
 
216
    def test_attribute__fetch_uses_deltas(self):
 
217
        """Weaves do not reuse deltas."""
 
218
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
219
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
220
        self.assertEqual(False, repo._format._fetch_uses_deltas)
 
221
 
 
222
    def test_attribute__fetch_reconcile(self):
 
223
        """Weave repositories need a reconcile after fetch."""
 
224
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
225
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
226
        self.assertEqual(True, repo._format._fetch_reconcile)
 
227
 
 
228
    def test_disk_layout(self):
 
229
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
230
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
231
        # in case of side effects of locking.
 
232
        repo.lock_write()
 
233
        repo.unlock()
 
234
        # we want:
 
235
        # format 'Bazaar-NG Repository format 7'
 
236
        # lock ''
 
237
        # inventory.weave == empty_weave
 
238
        # empty revision-store directory
 
239
        # empty weaves directory
 
240
        t = control.get_repository_transport(None)
 
241
        self.assertEqualDiff('Bazaar-NG Repository format 7',
 
242
                             t.get('format').read())
 
243
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
 
244
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
 
245
        self.assertEqualDiff('# bzr weave file v5\n'
 
246
                             'w\n'
 
247
                             'W\n',
 
248
                             t.get('inventory.weave').read())
 
249
        # Creating a file with id Foo:Bar results in a non-escaped file name on
 
250
        # disk.
 
251
        control.create_branch()
 
252
        tree = control.create_workingtree()
 
253
        tree.add(['foo'], ['Foo:Bar'], ['file'])
 
254
        tree.put_file_bytes_non_atomic('Foo:Bar', 'content\n')
 
255
        tree.commit('first post', rev_id='first')
 
256
        self.assertEqualDiff(
 
257
            '# bzr weave file v5\n'
 
258
            'i\n'
 
259
            '1 7fe70820e08a1aac0ef224d9c66ab66831cc4ab1\n'
 
260
            'n first\n'
 
261
            '\n'
 
262
            'w\n'
 
263
            '{ 0\n'
 
264
            '. content\n'
 
265
            '}\n'
 
266
            'W\n',
 
267
            t.get('weaves/74/Foo%3ABar.weave').read())
 
268
 
 
269
    def test_shared_disk_layout(self):
 
270
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
271
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
272
        # we want:
 
273
        # format 'Bazaar-NG Repository format 7'
 
274
        # inventory.weave == empty_weave
 
275
        # empty revision-store directory
 
276
        # empty weaves directory
 
277
        # a 'shared-storage' marker file.
 
278
        # lock is not present when unlocked
 
279
        t = control.get_repository_transport(None)
 
280
        self.assertEqualDiff('Bazaar-NG Repository format 7',
 
281
                             t.get('format').read())
 
282
        self.assertEqualDiff('', t.get('shared-storage').read())
 
283
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
 
284
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
 
285
        self.assertEqualDiff('# bzr weave file v5\n'
 
286
                             'w\n'
 
287
                             'W\n',
 
288
                             t.get('inventory.weave').read())
 
289
        self.assertFalse(t.has('branch-lock'))
 
290
 
 
291
    def test_creates_lockdir(self):
 
292
        """Make sure it appears to be controlled by a LockDir existence"""
 
293
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
294
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
295
        t = control.get_repository_transport(None)
 
296
        # TODO: Should check there is a 'lock' toplevel directory,
 
297
        # regardless of contents
 
298
        self.assertFalse(t.has('lock/held/info'))
 
299
        repo.lock_write()
 
300
        try:
 
301
            self.assertTrue(t.has('lock/held/info'))
 
302
        finally:
 
303
            # unlock so we don't get a warning about failing to do so
 
304
            repo.unlock()
 
305
 
 
306
    def test_uses_lockdir(self):
 
307
        """repo format 7 actually locks on lockdir"""
 
308
        base_url = self.get_url()
 
309
        control = bzrdir.BzrDirMetaFormat1().initialize(base_url)
 
310
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
311
        t = control.get_repository_transport(None)
 
312
        repo.lock_write()
 
313
        repo.unlock()
 
314
        del repo
 
315
        # make sure the same lock is created by opening it
 
316
        repo = repository.Repository.open(base_url)
 
317
        repo.lock_write()
 
318
        self.assertTrue(t.has('lock/held/info'))
 
319
        repo.unlock()
 
320
        self.assertFalse(t.has('lock/held/info'))
 
321
 
 
322
    def test_shared_no_tree_disk_layout(self):
 
323
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
324
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
325
        repo.set_make_working_trees(False)
 
326
        # we want:
 
327
        # format 'Bazaar-NG Repository format 7'
 
328
        # lock ''
 
329
        # inventory.weave == empty_weave
 
330
        # empty revision-store directory
 
331
        # empty weaves directory
 
332
        # a 'shared-storage' marker file.
 
333
        t = control.get_repository_transport(None)
 
334
        self.assertEqualDiff('Bazaar-NG Repository format 7',
 
335
                             t.get('format').read())
 
336
        ## self.assertEqualDiff('', t.get('lock').read())
 
337
        self.assertEqualDiff('', t.get('shared-storage').read())
 
338
        self.assertEqualDiff('', t.get('no-working-trees').read())
 
339
        repo.set_make_working_trees(True)
 
340
        self.assertFalse(t.has('no-working-trees'))
 
341
        self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
 
342
        self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
 
343
        self.assertEqualDiff('# bzr weave file v5\n'
 
344
                             'w\n'
 
345
                             'W\n',
 
346
                             t.get('inventory.weave').read())
 
347
 
 
348
    def test_supports_external_lookups(self):
 
349
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
350
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
351
        self.assertFalse(repo._format.supports_external_lookups)
209
352
 
210
353
 
211
354
class TestFormatKnit1(TestCaseWithTransport):
313
456
        repo = self.make_repository('.',
314
457
                format=bzrdir.format_registry.get('knit')())
315
458
        inv_xml = '<inventory format="5">\n</inventory>\n'
316
 
        inv = repo._deserialise_inventory('test-rev-id', inv_xml)
 
459
        inv = repo.deserialise_inventory('test-rev-id', inv_xml)
317
460
        self.assertEqual('test-rev-id', inv.root.revision)
318
461
 
319
462
    def test_deserialise_uses_global_revision_id(self):
325
468
        # Arguably, the deserialise_inventory should detect a mismatch, and
326
469
        # raise an error, rather than silently using one revision_id over the
327
470
        # other.
328
 
        self.assertRaises(AssertionError, repo._deserialise_inventory,
 
471
        self.assertRaises(AssertionError, repo.deserialise_inventory,
329
472
            'test-rev-id', inv_xml)
330
 
        inv = repo._deserialise_inventory('other-rev-id', inv_xml)
 
473
        inv = repo.deserialise_inventory('other-rev-id', inv_xml)
331
474
        self.assertEqual('other-rev-id', inv.root.revision)
332
475
 
333
476
    def test_supports_external_lookups(self):
343
486
    _serializer = None
344
487
 
345
488
    def supports_rich_root(self):
346
 
        if self._format is not None:
347
 
            return self._format.rich_root_data
348
489
        return False
349
490
 
350
491
    def get_graph(self):
379
520
        # classes do not barf inappropriately when a surprising repository type
380
521
        # is handed to them.
381
522
        dummy_a = DummyRepository()
382
 
        dummy_a._format = RepositoryFormat()
383
 
        dummy_a._format.supports_full_versioned_files = True
384
523
        dummy_b = DummyRepository()
385
 
        dummy_b._format = RepositoryFormat()
386
 
        dummy_b._format.supports_full_versioned_files = True
387
524
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
388
525
 
389
526
    def assertGetsDefaultInterRepository(self, repo_a, repo_b):
393
530
        no actual sane default in the presence of incompatible data models.
394
531
        """
395
532
        inter_repo = repository.InterRepository.get(repo_a, repo_b)
396
 
        self.assertEqual(vf_repository.InterSameDataRepository,
 
533
        self.assertEqual(repository.InterSameDataRepository,
397
534
                         inter_repo.__class__)
398
535
        self.assertEqual(repo_a, inter_repo.source)
399
536
        self.assertEqual(repo_b, inter_repo.target)
405
542
        # pair that it returns true on for the is_compatible static method
406
543
        # check
407
544
        dummy_a = DummyRepository()
408
 
        dummy_a._format = RepositoryFormat()
409
545
        dummy_b = DummyRepository()
410
 
        dummy_b._format = RepositoryFormat()
411
546
        repo = self.make_repository('.')
412
547
        # hack dummies to look like repo somewhat.
413
548
        dummy_a._serializer = repo._serializer
414
 
        dummy_a._format.supports_tree_reference = repo._format.supports_tree_reference
415
 
        dummy_a._format.rich_root_data = repo._format.rich_root_data
416
 
        dummy_a._format.supports_full_versioned_files = repo._format.supports_full_versioned_files
417
549
        dummy_b._serializer = repo._serializer
418
 
        dummy_b._format.supports_tree_reference = repo._format.supports_tree_reference
419
 
        dummy_b._format.rich_root_data = repo._format.rich_root_data
420
 
        dummy_b._format.supports_full_versioned_files = repo._format.supports_full_versioned_files
421
550
        repository.InterRepository.register_optimiser(InterDummy)
422
551
        try:
423
552
            # we should get the default for something InterDummy returns False
436
565
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
437
566
 
438
567
 
439
 
class TestRepositoryFormat1(knitrepo.RepositoryFormatKnit1):
440
 
 
441
 
    def get_format_string(self):
442
 
        return "Test Format 1"
443
 
 
444
 
 
445
 
class TestRepositoryFormat2(knitrepo.RepositoryFormatKnit1):
446
 
 
447
 
    def get_format_string(self):
448
 
        return "Test Format 2"
 
568
class TestInterWeaveRepo(TestCaseWithTransport):
 
569
 
 
570
    def test_is_compatible_and_registered(self):
 
571
        # InterWeaveRepo is compatible when either side
 
572
        # is a format 5/6/7 branch
 
573
        from bzrlib.repofmt import knitrepo, weaverepo
 
574
        formats = [weaverepo.RepositoryFormat5(),
 
575
                   weaverepo.RepositoryFormat6(),
 
576
                   weaverepo.RepositoryFormat7()]
 
577
        incompatible_formats = [weaverepo.RepositoryFormat4(),
 
578
                                knitrepo.RepositoryFormatKnit1(),
 
579
                                ]
 
580
        repo_a = self.make_repository('a')
 
581
        repo_b = self.make_repository('b')
 
582
        is_compatible = repository.InterWeaveRepo.is_compatible
 
583
        for source in incompatible_formats:
 
584
            # force incompatible left then right
 
585
            repo_a._format = source
 
586
            repo_b._format = formats[0]
 
587
            self.assertFalse(is_compatible(repo_a, repo_b))
 
588
            self.assertFalse(is_compatible(repo_b, repo_a))
 
589
        for source in formats:
 
590
            repo_a._format = source
 
591
            for target in formats:
 
592
                repo_b._format = target
 
593
                self.assertTrue(is_compatible(repo_a, repo_b))
 
594
        self.assertEqual(repository.InterWeaveRepo,
 
595
                         repository.InterRepository.get(repo_a,
 
596
                                                        repo_b).__class__)
449
597
 
450
598
 
451
599
class TestRepositoryConverter(TestCaseWithTransport):
452
600
 
453
601
    def test_convert_empty(self):
454
 
        source_format = TestRepositoryFormat1()
455
 
        target_format = TestRepositoryFormat2()
456
 
        repository.format_registry.register(source_format)
457
 
        self.addCleanup(repository.format_registry.remove,
458
 
            source_format)
459
 
        repository.format_registry.register(target_format)
460
 
        self.addCleanup(repository.format_registry.remove,
461
 
            target_format)
462
 
        t = self.get_transport()
 
602
        t = get_transport(self.get_url('.'))
463
603
        t.mkdir('repository')
464
604
        repo_dir = bzrdir.BzrDirMetaFormat1().initialize('repository')
465
 
        repo = TestRepositoryFormat1().initialize(repo_dir)
 
605
        repo = weaverepo.RepositoryFormat7().initialize(repo_dir)
 
606
        target_format = knitrepo.RepositoryFormatKnit1()
466
607
        converter = repository.CopyConverter(target_format)
467
608
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
468
609
        try:
473
614
        self.assertTrue(isinstance(target_format, repo._format.__class__))
474
615
 
475
616
 
 
617
class TestMisc(TestCase):
 
618
 
 
619
    def test_unescape_xml(self):
 
620
        """We get some kind of error when malformed entities are passed"""
 
621
        self.assertRaises(KeyError, repository._unescape_xml, 'foo&bar;')
 
622
 
 
623
 
476
624
class TestRepositoryFormatKnit3(TestCaseWithTransport):
477
625
 
478
626
    def test_attribute__fetch_order(self):
525
673
        self.assertFalse(repo._format.supports_external_lookups)
526
674
 
527
675
 
528
 
class Test2a(tests.TestCaseWithMemoryTransport):
529
 
 
530
 
    def test_chk_bytes_uses_custom_btree_parser(self):
531
 
        mt = self.make_branch_and_memory_tree('test', format='2a')
532
 
        mt.lock_write()
533
 
        self.addCleanup(mt.unlock)
534
 
        mt.add([''], ['root-id'])
535
 
        mt.commit('first')
536
 
        index = mt.branch.repository.chk_bytes._index._graph_index._indices[0]
537
 
        self.assertEqual(btree_index._gcchk_factory, index._leaf_factory)
538
 
        # It should also work if we re-open the repo
539
 
        repo = mt.branch.repository.bzrdir.open_repository()
540
 
        repo.lock_read()
541
 
        self.addCleanup(repo.unlock)
542
 
        index = repo.chk_bytes._index._graph_index._indices[0]
543
 
        self.assertEqual(btree_index._gcchk_factory, index._leaf_factory)
544
 
 
545
 
    def test_fetch_combines_groups(self):
546
 
        builder = self.make_branch_builder('source', format='2a')
547
 
        builder.start_series()
548
 
        builder.build_snapshot('1', None, [
549
 
            ('add', ('', 'root-id', 'directory', '')),
550
 
            ('add', ('file', 'file-id', 'file', 'content\n'))])
551
 
        builder.build_snapshot('2', ['1'], [
552
 
            ('modify', ('file-id', 'content-2\n'))])
553
 
        builder.finish_series()
554
 
        source = builder.get_branch()
555
 
        target = self.make_repository('target', format='2a')
556
 
        target.fetch(source.repository)
557
 
        target.lock_read()
558
 
        self.addCleanup(target.unlock)
559
 
        details = target.texts._index.get_build_details(
560
 
            [('file-id', '1',), ('file-id', '2',)])
561
 
        file_1_details = details[('file-id', '1')]
562
 
        file_2_details = details[('file-id', '2')]
563
 
        # The index, and what to read off disk, should be the same for both
564
 
        # versions of the file.
565
 
        self.assertEqual(file_1_details[0][:3], file_2_details[0][:3])
566
 
 
567
 
    def test_fetch_combines_groups(self):
568
 
        builder = self.make_branch_builder('source', format='2a')
569
 
        builder.start_series()
570
 
        builder.build_snapshot('1', None, [
571
 
            ('add', ('', 'root-id', 'directory', '')),
572
 
            ('add', ('file', 'file-id', 'file', 'content\n'))])
573
 
        builder.build_snapshot('2', ['1'], [
574
 
            ('modify', ('file-id', 'content-2\n'))])
575
 
        builder.finish_series()
576
 
        source = builder.get_branch()
577
 
        target = self.make_repository('target', format='2a')
578
 
        target.fetch(source.repository)
579
 
        target.lock_read()
580
 
        self.addCleanup(target.unlock)
581
 
        details = target.texts._index.get_build_details(
582
 
            [('file-id', '1',), ('file-id', '2',)])
583
 
        file_1_details = details[('file-id', '1')]
584
 
        file_2_details = details[('file-id', '2')]
585
 
        # The index, and what to read off disk, should be the same for both
586
 
        # versions of the file.
587
 
        self.assertEqual(file_1_details[0][:3], file_2_details[0][:3])
588
 
 
589
 
    def test_fetch_combines_groups(self):
590
 
        builder = self.make_branch_builder('source', format='2a')
591
 
        builder.start_series()
592
 
        builder.build_snapshot('1', None, [
593
 
            ('add', ('', 'root-id', 'directory', '')),
594
 
            ('add', ('file', 'file-id', 'file', 'content\n'))])
595
 
        builder.build_snapshot('2', ['1'], [
596
 
            ('modify', ('file-id', 'content-2\n'))])
597
 
        builder.finish_series()
598
 
        source = builder.get_branch()
599
 
        target = self.make_repository('target', format='2a')
600
 
        target.fetch(source.repository)
601
 
        target.lock_read()
602
 
        self.addCleanup(target.unlock)
603
 
        details = target.texts._index.get_build_details(
604
 
            [('file-id', '1',), ('file-id', '2',)])
605
 
        file_1_details = details[('file-id', '1')]
606
 
        file_2_details = details[('file-id', '2')]
607
 
        # The index, and what to read off disk, should be the same for both
608
 
        # versions of the file.
609
 
        self.assertEqual(file_1_details[0][:3], file_2_details[0][:3])
 
676
class Test2a(TestCaseWithTransport):
610
677
 
611
678
    def test_format_pack_compresses_True(self):
612
679
        repo = self.make_repository('repo', format='2a')
613
680
        self.assertTrue(repo._format.pack_compresses)
614
681
 
615
682
    def test_inventories_use_chk_map_with_parent_base_dict(self):
616
 
        tree = self.make_branch_and_memory_tree('repo', format="2a")
617
 
        tree.lock_write()
618
 
        tree.add([''], ['TREE_ROOT'])
 
683
        tree = self.make_branch_and_tree('repo', format="2a")
619
684
        revid = tree.commit("foo")
620
 
        tree.unlock()
621
685
        tree.lock_read()
622
686
        self.addCleanup(tree.unlock)
623
687
        inv = tree.branch.repository.get_inventory(revid)
632
696
        # at 20 unchanged commits, chk pages are packed that are split into
633
697
        # two groups such that the new pack being made doesn't have all its
634
698
        # pages in the source packs (though they are in the repository).
635
 
        # Use a memory backed repository, we don't need to hit disk for this
636
 
        tree = self.make_branch_and_memory_tree('tree', format='2a')
637
 
        tree.lock_write()
638
 
        self.addCleanup(tree.unlock)
639
 
        tree.add([''], ['TREE_ROOT'])
 
699
        tree = self.make_branch_and_tree('tree', format='2a')
640
700
        for pos in range(20):
641
701
            tree.commit(str(pos))
642
702
 
643
703
    def test_pack_with_hint(self):
644
 
        tree = self.make_branch_and_memory_tree('tree', format='2a')
645
 
        tree.lock_write()
646
 
        self.addCleanup(tree.unlock)
647
 
        tree.add([''], ['TREE_ROOT'])
 
704
        tree = self.make_branch_and_tree('tree', format='2a')
648
705
        # 1 commit to leave untouched
649
706
        tree.commit('1')
650
707
        to_keep = tree.branch.repository._pack_collection.names()
673
730
        target = self.make_repository('target', format='rich-root-pack')
674
731
        stream = source._get_source(target._format)
675
732
        # We don't want the child GroupCHKStreamSource
676
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
733
        self.assertIs(type(stream), repository.StreamSource)
677
734
 
678
735
    def test_get_stream_for_missing_keys_includes_all_chk_refs(self):
679
736
        source_builder = self.make_branch_builder('source',
755
812
        source = self.make_repository('source', format='pack-0.92')
756
813
        target = self.make_repository('target', format='pack-0.92')
757
814
        stream_source = source._get_source(target._format)
758
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
815
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
759
816
 
760
817
    def test_source_to_exact_pack_rich_root_pack(self):
761
818
        source = self.make_repository('source', format='rich-root-pack')
762
819
        target = self.make_repository('target', format='rich-root-pack')
763
820
        stream_source = source._get_source(target._format)
764
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
821
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
765
822
 
766
823
    def test_source_to_exact_pack_19(self):
767
824
        source = self.make_repository('source', format='1.9')
768
825
        target = self.make_repository('target', format='1.9')
769
826
        stream_source = source._get_source(target._format)
770
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
827
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
771
828
 
772
829
    def test_source_to_exact_pack_19_rich_root(self):
773
830
        source = self.make_repository('source', format='1.9-rich-root')
774
831
        target = self.make_repository('target', format='1.9-rich-root')
775
832
        stream_source = source._get_source(target._format)
776
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
833
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
777
834
 
778
835
    def test_source_to_remote_exact_pack_19(self):
779
836
        trans = self.make_smart_server('target')
782
839
        target = self.make_repository('target', format='1.9')
783
840
        target = repository.Repository.open(trans.base)
784
841
        stream_source = source._get_source(target._format)
785
 
        self.assertIsInstance(stream_source, knitpack_repo.KnitPackStreamSource)
 
842
        self.assertIsInstance(stream_source, pack_repo.KnitPackStreamSource)
786
843
 
787
844
    def test_stream_source_to_non_exact(self):
788
845
        source = self.make_repository('source', format='pack-0.92')
789
846
        target = self.make_repository('target', format='1.9')
790
847
        stream = source._get_source(target._format)
791
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
848
        self.assertIs(type(stream), repository.StreamSource)
792
849
 
793
850
    def test_stream_source_to_non_exact_rich_root(self):
794
851
        source = self.make_repository('source', format='1.9')
795
852
        target = self.make_repository('target', format='1.9-rich-root')
796
853
        stream = source._get_source(target._format)
797
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
854
        self.assertIs(type(stream), repository.StreamSource)
798
855
 
799
856
    def test_source_to_remote_non_exact_pack_19(self):
800
857
        trans = self.make_smart_server('target')
803
860
        target = self.make_repository('target', format='1.6')
804
861
        target = repository.Repository.open(trans.base)
805
862
        stream_source = source._get_source(target._format)
806
 
        self.assertIs(type(stream_source), vf_repository.StreamSource)
 
863
        self.assertIs(type(stream_source), repository.StreamSource)
807
864
 
808
865
    def test_stream_source_to_knit(self):
809
866
        source = self.make_repository('source', format='pack-0.92')
810
867
        target = self.make_repository('target', format='dirstate')
811
868
        stream = source._get_source(target._format)
812
 
        self.assertIs(type(stream), vf_repository.StreamSource)
 
869
        self.assertIs(type(stream), repository.StreamSource)
813
870
 
814
871
 
815
872
class TestDevelopment6FindParentIdsOfRevisions(TestCaseWithTransport):
817
874
 
818
875
    def setUp(self):
819
876
        super(TestDevelopment6FindParentIdsOfRevisions, self).setUp()
820
 
        self.builder = self.make_branch_builder('source')
 
877
        self.builder = self.make_branch_builder('source',
 
878
            format='development6-rich-root')
821
879
        self.builder.start_series()
822
880
        self.builder.build_snapshot('initial', None,
823
881
            [('add', ('', 'tree-root', 'directory', None))])
888
946
            inv = inventory.Inventory(revision_id='rev1a')
889
947
            inv.root.revision = 'rev1a'
890
948
            self.add_file(repo, inv, 'file1', 'rev1a', [])
891
 
            repo.texts.add_lines((inv.root.file_id, 'rev1a'), [], [])
892
949
            repo.add_inventory('rev1a', inv, [])
893
950
            revision = _mod_revision.Revision('rev1a',
894
951
                committer='jrandom@example.com', timestamp=0,
929
986
    def add_revision(self, repo, revision_id, inv, parent_ids):
930
987
        inv.revision_id = revision_id
931
988
        inv.root.revision = revision_id
932
 
        repo.texts.add_lines((inv.root.file_id, revision_id), [], [])
933
989
        repo.add_inventory(revision_id, inv, parent_ids)
934
990
        revision = _mod_revision.Revision(revision_id,
935
991
            committer='jrandom@example.com', timestamp=0, inventory_sha1='',
952
1008
        """
953
1009
        broken_repo = self.make_broken_repository()
954
1010
        empty_repo = self.make_repository('empty-repo')
955
 
        try:
956
 
            empty_repo.fetch(broken_repo)
957
 
        except (errors.RevisionNotPresent, errors.BzrCheckError):
958
 
            # Test successful: compression parent not being copied leads to
959
 
            # error.
960
 
            return
961
 
        empty_repo.lock_read()
962
 
        self.addCleanup(empty_repo.unlock)
963
 
        text = empty_repo.texts.get_record_stream(
964
 
            [('file2-id', 'rev3')], 'topological', True).next()
965
 
        self.assertEqual('line\n', text.get_bytes_as('fulltext'))
 
1011
        # See bug https://bugs.launchpad.net/bzr/+bug/389141 for information
 
1012
        # about why this was turned into expectFailure
 
1013
        self.expectFailure('new Stream fetch fills in missing compression'
 
1014
           ' parents (bug #389141)',
 
1015
           self.assertRaises, (errors.RevisionNotPresent, errors.BzrCheckError),
 
1016
                              empty_repo.fetch, broken_repo)
 
1017
        self.assertRaises((errors.RevisionNotPresent, errors.BzrCheckError),
 
1018
                          empty_repo.fetch, broken_repo)
966
1019
 
967
1020
 
968
1021
class TestRepositoryPackCollection(TestCaseWithTransport):
977
1030
 
978
1031
    def make_packs_and_alt_repo(self, write_lock=False):
979
1032
        """Create a pack repo with 3 packs, and access it via a second repo."""
980
 
        tree = self.make_branch_and_tree('.', format=self.get_format())
 
1033
        tree = self.make_branch_and_tree('.')
981
1034
        tree.lock_write()
982
1035
        self.addCleanup(tree.unlock)
983
1036
        rev1 = tree.commit('one')
993
1046
        packs.ensure_loaded()
994
1047
        return tree, r, packs, [rev1, rev2, rev3]
995
1048
 
996
 
    def test__clear_obsolete_packs(self):
997
 
        packs = self.get_packs()
998
 
        obsolete_pack_trans = packs.transport.clone('obsolete_packs')
999
 
        obsolete_pack_trans.put_bytes('a-pack.pack', 'content\n')
1000
 
        obsolete_pack_trans.put_bytes('a-pack.rix', 'content\n')
1001
 
        obsolete_pack_trans.put_bytes('a-pack.iix', 'content\n')
1002
 
        obsolete_pack_trans.put_bytes('another-pack.pack', 'foo\n')
1003
 
        obsolete_pack_trans.put_bytes('not-a-pack.rix', 'foo\n')
1004
 
        res = packs._clear_obsolete_packs()
1005
 
        self.assertEqual(['a-pack', 'another-pack'], sorted(res))
1006
 
        self.assertEqual([], obsolete_pack_trans.list_dir('.'))
1007
 
 
1008
 
    def test__clear_obsolete_packs_preserve(self):
1009
 
        packs = self.get_packs()
1010
 
        obsolete_pack_trans = packs.transport.clone('obsolete_packs')
1011
 
        obsolete_pack_trans.put_bytes('a-pack.pack', 'content\n')
1012
 
        obsolete_pack_trans.put_bytes('a-pack.rix', 'content\n')
1013
 
        obsolete_pack_trans.put_bytes('a-pack.iix', 'content\n')
1014
 
        obsolete_pack_trans.put_bytes('another-pack.pack', 'foo\n')
1015
 
        obsolete_pack_trans.put_bytes('not-a-pack.rix', 'foo\n')
1016
 
        res = packs._clear_obsolete_packs(preserve=set(['a-pack']))
1017
 
        self.assertEqual(['a-pack', 'another-pack'], sorted(res))
1018
 
        self.assertEqual(['a-pack.iix', 'a-pack.pack', 'a-pack.rix'],
1019
 
                         sorted(obsolete_pack_trans.list_dir('.')))
1020
 
 
1021
1049
    def test__max_pack_count(self):
1022
1050
        """The maximum pack count is a function of the number of revisions."""
1023
1051
        # no revisions - one pack, so that we can have a revision free repo
1043
1071
        # check some arbitrary big numbers
1044
1072
        self.assertEqual(25, packs._max_pack_count(112894))
1045
1073
 
1046
 
    def test_repr(self):
1047
 
        packs = self.get_packs()
1048
 
        self.assertContainsRe(repr(packs),
1049
 
            'RepositoryPackCollection(.*Repository(.*))')
1050
 
 
1051
 
    def test__obsolete_packs(self):
1052
 
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1053
 
        names = packs.names()
1054
 
        pack = packs.get_pack_by_name(names[0])
1055
 
        # Schedule this one for removal
1056
 
        packs._remove_pack_from_memory(pack)
1057
 
        # Simulate a concurrent update by renaming the .pack file and one of
1058
 
        # the indices
1059
 
        packs.transport.rename('packs/%s.pack' % (names[0],),
1060
 
                               'obsolete_packs/%s.pack' % (names[0],))
1061
 
        packs.transport.rename('indices/%s.iix' % (names[0],),
1062
 
                               'obsolete_packs/%s.iix' % (names[0],))
1063
 
        # Now trigger the obsoletion, and ensure that all the remaining files
1064
 
        # are still renamed
1065
 
        packs._obsolete_packs([pack])
1066
 
        self.assertEqual([n + '.pack' for n in names[1:]],
1067
 
                         sorted(packs._pack_transport.list_dir('.')))
1068
 
        # names[0] should not be present in the index anymore
1069
 
        self.assertEqual(names[1:],
1070
 
            sorted(set([osutils.splitext(n)[0] for n in
1071
 
                        packs._index_transport.list_dir('.')])))
1072
 
 
1073
1074
    def test_pack_distribution_zero(self):
1074
1075
        packs = self.get_packs()
1075
1076
        self.assertEqual([0], packs.pack_distribution(0))
1243
1244
        self.assertEqual({revs[-1]:(revs[-2],)}, r.get_parent_map([revs[-1]]))
1244
1245
        self.assertFalse(packs.reload_pack_names())
1245
1246
 
1246
 
    def test_reload_pack_names_preserves_pending(self):
1247
 
        # TODO: Update this to also test for pending-deleted names
1248
 
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1249
 
        # We will add one pack (via start_write_group + insert_record_stream),
1250
 
        # and remove another pack (via _remove_pack_from_memory)
1251
 
        orig_names = packs.names()
1252
 
        orig_at_load = packs._packs_at_load
1253
 
        to_remove_name = iter(orig_names).next()
1254
 
        r.start_write_group()
1255
 
        self.addCleanup(r.abort_write_group)
1256
 
        r.texts.insert_record_stream([versionedfile.FulltextContentFactory(
1257
 
            ('text', 'rev'), (), None, 'content\n')])
1258
 
        new_pack = packs._new_pack
1259
 
        self.assertTrue(new_pack.data_inserted())
1260
 
        new_pack.finish()
1261
 
        packs.allocate(new_pack)
1262
 
        packs._new_pack = None
1263
 
        removed_pack = packs.get_pack_by_name(to_remove_name)
1264
 
        packs._remove_pack_from_memory(removed_pack)
1265
 
        names = packs.names()
1266
 
        all_nodes, deleted_nodes, new_nodes, _ = packs._diff_pack_names()
1267
 
        new_names = set([x[0][0] for x in new_nodes])
1268
 
        self.assertEqual(names, sorted([x[0][0] for x in all_nodes]))
1269
 
        self.assertEqual(set(names) - set(orig_names), new_names)
1270
 
        self.assertEqual(set([new_pack.name]), new_names)
1271
 
        self.assertEqual([to_remove_name],
1272
 
                         sorted([x[0][0] for x in deleted_nodes]))
1273
 
        packs.reload_pack_names()
1274
 
        reloaded_names = packs.names()
1275
 
        self.assertEqual(orig_at_load, packs._packs_at_load)
1276
 
        self.assertEqual(names, reloaded_names)
1277
 
        all_nodes, deleted_nodes, new_nodes, _ = packs._diff_pack_names()
1278
 
        new_names = set([x[0][0] for x in new_nodes])
1279
 
        self.assertEqual(names, sorted([x[0][0] for x in all_nodes]))
1280
 
        self.assertEqual(set(names) - set(orig_names), new_names)
1281
 
        self.assertEqual(set([new_pack.name]), new_names)
1282
 
        self.assertEqual([to_remove_name],
1283
 
                         sorted([x[0][0] for x in deleted_nodes]))
1284
 
 
1285
 
    def test_autopack_obsoletes_new_pack(self):
1286
 
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1287
 
        packs._max_pack_count = lambda x: 1
1288
 
        packs.pack_distribution = lambda x: [10]
1289
 
        r.start_write_group()
1290
 
        r.revisions.insert_record_stream([versionedfile.FulltextContentFactory(
1291
 
            ('bogus-rev',), (), None, 'bogus-content\n')])
1292
 
        # This should trigger an autopack, which will combine everything into a
1293
 
        # single pack file.
1294
 
        new_names = r.commit_write_group()
1295
 
        names = packs.names()
1296
 
        self.assertEqual(1, len(names))
1297
 
        self.assertEqual([names[0] + '.pack'],
1298
 
                         packs._pack_transport.list_dir('.'))
1299
 
 
1300
1247
    def test_autopack_reloads_and_stops(self):
1301
1248
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1302
1249
        # After we have determined what needs to be autopacked, trigger a
1314
1261
        self.assertEqual(tree.branch.repository._pack_collection.names(),
1315
1262
                         packs.names())
1316
1263
 
1317
 
    def test__save_pack_names(self):
1318
 
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1319
 
        names = packs.names()
1320
 
        pack = packs.get_pack_by_name(names[0])
1321
 
        packs._remove_pack_from_memory(pack)
1322
 
        packs._save_pack_names(obsolete_packs=[pack])
1323
 
        cur_packs = packs._pack_transport.list_dir('.')
1324
 
        self.assertEqual([n + '.pack' for n in names[1:]], sorted(cur_packs))
1325
 
        # obsolete_packs will also have stuff like .rix and .iix present.
1326
 
        obsolete_packs = packs.transport.list_dir('obsolete_packs')
1327
 
        obsolete_names = set([osutils.splitext(n)[0] for n in obsolete_packs])
1328
 
        self.assertEqual([pack.name], sorted(obsolete_names))
1329
 
 
1330
 
    def test__save_pack_names_already_obsoleted(self):
1331
 
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
1332
 
        names = packs.names()
1333
 
        pack = packs.get_pack_by_name(names[0])
1334
 
        packs._remove_pack_from_memory(pack)
1335
 
        # We are going to simulate a concurrent autopack by manually obsoleting
1336
 
        # the pack directly.
1337
 
        packs._obsolete_packs([pack])
1338
 
        packs._save_pack_names(clear_obsolete_packs=True,
1339
 
                               obsolete_packs=[pack])
1340
 
        cur_packs = packs._pack_transport.list_dir('.')
1341
 
        self.assertEqual([n + '.pack' for n in names[1:]], sorted(cur_packs))
1342
 
        # Note that while we set clear_obsolete_packs=True, it should not
1343
 
        # delete a pack file that we have also scheduled for obsoletion.
1344
 
        obsolete_packs = packs.transport.list_dir('obsolete_packs')
1345
 
        obsolete_names = set([osutils.splitext(n)[0] for n in obsolete_packs])
1346
 
        self.assertEqual([pack.name], sorted(obsolete_names))
1347
 
 
1348
 
 
1349
1264
 
1350
1265
class TestPack(TestCaseWithTransport):
1351
1266
    """Tests for the Pack object."""
1415
1330
            index_class=BTreeGraphIndex,
1416
1331
            use_chk_index=False)
1417
1332
        pack = pack_repo.NewPack(collection)
1418
 
        self.addCleanup(pack.abort) # Make sure the write stream gets closed
1419
1333
        self.assertIsInstance(pack.revision_index, BTreeBuilder)
1420
1334
        self.assertIsInstance(pack.inventory_index, BTreeBuilder)
1421
1335
        self.assertIsInstance(pack._hash, type(osutils.md5()))
1432
1346
    """Tests for the packs repository Packer class."""
1433
1347
 
1434
1348
    def test_pack_optimizes_pack_order(self):
1435
 
        builder = self.make_branch_builder('.', format="1.9")
 
1349
        builder = self.make_branch_builder('.')
1436
1350
        builder.start_series()
1437
1351
        builder.build_snapshot('A', None, [
1438
1352
            ('add', ('', 'root-id', 'directory', None)),
1451
1365
        # Because of how they were built, they correspond to
1452
1366
        # ['D', 'C', 'B', 'A']
1453
1367
        packs = b.repository._pack_collection.packs
1454
 
        packer = knitpack_repo.KnitPacker(b.repository._pack_collection,
 
1368
        packer = pack_repo.Packer(b.repository._pack_collection,
1455
1369
                                  packs, 'testing',
1456
1370
                                  revision_ids=['B', 'C'])
1457
1371
        # Now, when we are copying the B & C revisions, their pack files should
1471
1385
        return repo._pack_collection
1472
1386
 
1473
1387
    def test_open_pack_will_optimise(self):
1474
 
        packer = knitpack_repo.OptimisingKnitPacker(self.get_pack_collection(),
 
1388
        packer = pack_repo.OptimisingPacker(self.get_pack_collection(),
1475
1389
                                            [], '.test')
1476
1390
        new_pack = packer.open_pack()
1477
 
        self.addCleanup(new_pack.abort) # ensure cleanup
1478
1391
        self.assertIsInstance(new_pack, pack_repo.NewPack)
1479
1392
        self.assertTrue(new_pack.revision_index._optimize_for_size)
1480
1393
        self.assertTrue(new_pack.inventory_index._optimize_for_size)
1482
1395
        self.assertTrue(new_pack.signature_index._optimize_for_size)
1483
1396
 
1484
1397
 
1485
 
class TestGCCHKPacker(TestCaseWithTransport):
1486
 
 
1487
 
    def make_abc_branch(self):
1488
 
        builder = self.make_branch_builder('source')
1489
 
        builder.start_series()
1490
 
        builder.build_snapshot('A', None, [
1491
 
            ('add', ('', 'root-id', 'directory', None)),
1492
 
            ('add', ('file', 'file-id', 'file', 'content\n')),
1493
 
            ])
1494
 
        builder.build_snapshot('B', ['A'], [
1495
 
            ('add', ('dir', 'dir-id', 'directory', None))])
1496
 
        builder.build_snapshot('C', ['B'], [
1497
 
            ('modify', ('file-id', 'new content\n'))])
1498
 
        builder.finish_series()
1499
 
        return builder.get_branch()
1500
 
 
1501
 
    def make_branch_with_disjoint_inventory_and_revision(self):
1502
 
        """a repo with separate packs for a revisions Revision and Inventory.
1503
 
 
1504
 
        There will be one pack file that holds the Revision content, and one
1505
 
        for the Inventory content.
1506
 
 
1507
 
        :return: (repository,
1508
 
                  pack_name_with_rev_A_Revision,
1509
 
                  pack_name_with_rev_A_Inventory,
1510
 
                  pack_name_with_rev_C_content)
1511
 
        """
1512
 
        b_source = self.make_abc_branch()
1513
 
        b_base = b_source.bzrdir.sprout('base', revision_id='A').open_branch()
1514
 
        b_stacked = b_base.bzrdir.sprout('stacked', stacked=True).open_branch()
1515
 
        b_stacked.lock_write()
1516
 
        self.addCleanup(b_stacked.unlock)
1517
 
        b_stacked.fetch(b_source, 'B')
1518
 
        # Now re-open the stacked repo directly (no fallbacks) so that we can
1519
 
        # fill in the A rev.
1520
 
        repo_not_stacked = b_stacked.bzrdir.open_repository()
1521
 
        repo_not_stacked.lock_write()
1522
 
        self.addCleanup(repo_not_stacked.unlock)
1523
 
        # Now we should have a pack file with A's inventory, but not its
1524
 
        # Revision
1525
 
        self.assertEqual([('A',), ('B',)],
1526
 
                         sorted(repo_not_stacked.inventories.keys()))
1527
 
        self.assertEqual([('B',)],
1528
 
                         sorted(repo_not_stacked.revisions.keys()))
1529
 
        stacked_pack_names = repo_not_stacked._pack_collection.names()
1530
 
        # We have a couple names here, figure out which has A's inventory
1531
 
        for name in stacked_pack_names:
1532
 
            pack = repo_not_stacked._pack_collection.get_pack_by_name(name)
1533
 
            keys = [n[1] for n in pack.inventory_index.iter_all_entries()]
1534
 
            if ('A',) in keys:
1535
 
                inv_a_pack_name = name
1536
 
                break
1537
 
        else:
1538
 
            self.fail('Could not find pack containing A\'s inventory')
1539
 
        repo_not_stacked.fetch(b_source.repository, 'A')
1540
 
        self.assertEqual([('A',), ('B',)],
1541
 
                         sorted(repo_not_stacked.revisions.keys()))
1542
 
        new_pack_names = set(repo_not_stacked._pack_collection.names())
1543
 
        rev_a_pack_names = new_pack_names.difference(stacked_pack_names)
1544
 
        self.assertEqual(1, len(rev_a_pack_names))
1545
 
        rev_a_pack_name = list(rev_a_pack_names)[0]
1546
 
        # Now fetch 'C', so we have a couple pack files to join
1547
 
        repo_not_stacked.fetch(b_source.repository, 'C')
1548
 
        rev_c_pack_names = set(repo_not_stacked._pack_collection.names())
1549
 
        rev_c_pack_names = rev_c_pack_names.difference(new_pack_names)
1550
 
        self.assertEqual(1, len(rev_c_pack_names))
1551
 
        rev_c_pack_name = list(rev_c_pack_names)[0]
1552
 
        return (repo_not_stacked, rev_a_pack_name, inv_a_pack_name,
1553
 
                rev_c_pack_name)
1554
 
 
1555
 
    def test_pack_with_distant_inventories(self):
1556
 
        # See https://bugs.launchpad.net/bzr/+bug/437003
1557
 
        # When repacking, it is possible to have an inventory in a different
1558
 
        # pack file than the associated revision. An autopack can then come
1559
 
        # along, and miss that inventory, and complain.
1560
 
        (repo, rev_a_pack_name, inv_a_pack_name, rev_c_pack_name
1561
 
         ) = self.make_branch_with_disjoint_inventory_and_revision()
1562
 
        a_pack = repo._pack_collection.get_pack_by_name(rev_a_pack_name)
1563
 
        c_pack = repo._pack_collection.get_pack_by_name(rev_c_pack_name)
1564
 
        packer = groupcompress_repo.GCCHKPacker(repo._pack_collection,
1565
 
                    [a_pack, c_pack], '.test-pack')
1566
 
        # This would raise ValueError in bug #437003, but should not raise an
1567
 
        # error once fixed.
1568
 
        packer.pack()
1569
 
 
1570
 
    def test_pack_with_missing_inventory(self):
1571
 
        # Similar to test_pack_with_missing_inventory, but this time, we force
1572
 
        # the A inventory to actually be gone from the repository.
1573
 
        (repo, rev_a_pack_name, inv_a_pack_name, rev_c_pack_name
1574
 
         ) = self.make_branch_with_disjoint_inventory_and_revision()
1575
 
        inv_a_pack = repo._pack_collection.get_pack_by_name(inv_a_pack_name)
1576
 
        repo._pack_collection._remove_pack_from_memory(inv_a_pack)
1577
 
        packer = groupcompress_repo.GCCHKPacker(repo._pack_collection,
1578
 
            repo._pack_collection.all_packs(), '.test-pack')
1579
 
        e = self.assertRaises(ValueError, packer.pack)
1580
 
        packer.new_pack.abort()
1581
 
        self.assertContainsRe(str(e),
1582
 
            r"We are missing inventories for revisions: .*'A'")
1583
 
 
1584
 
 
1585
1398
class TestCrossFormatPacks(TestCaseWithTransport):
1586
1399
 
1587
1400
    def log_pack(self, hint=None):
1602
1415
        self.addCleanup(target.unlock)
1603
1416
        source = source_tree.branch.repository._get_source(target._format)
1604
1417
        self.orig_pack = target.pack
1605
 
        self.overrideAttr(target, "pack", self.log_pack)
 
1418
        target.pack = self.log_pack
1606
1419
        search = target.search_missing_revision_ids(
1607
 
            source_tree.branch.repository, revision_ids=[tip])
 
1420
            source_tree.branch.repository, tip)
1608
1421
        stream = source.get_stream(search)
1609
1422
        from_format = source_tree.branch.repository._format
1610
1423
        sink = target._get_sink()
1626
1439
        self.addCleanup(target.unlock)
1627
1440
        source = source_tree.branch.repository
1628
1441
        self.orig_pack = target.pack
1629
 
        self.overrideAttr(target, "pack", self.log_pack)
 
1442
        target.pack = self.log_pack
1630
1443
        target.fetch(source)
1631
1444
        if expect_pack_called:
1632
1445
            self.assertLength(1, self.calls)