~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_repository.py

  • Committer: Ian Clatworthy
  • Date: 2009-01-19 02:24:15 UTC
  • mto: This revision was merged to the branch mainline in revision 3944.
  • Revision ID: ian.clatworthy@canonical.com-20090119022415-mo0mcfeiexfktgwt
apply jam's log --short fix (Ian Clatworthy)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 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
16
16
 
17
17
"""Tests for the Repository facility that are not interface tests.
18
18
 
19
 
For interface tests see tests/repository_implementations/*.py.
 
19
For interface tests see tests/per_repository/*.py.
20
20
 
21
21
For concrete class tests see this file, and for storage formats tests
22
22
also see this file.
23
23
"""
24
24
 
25
 
from stat import *
 
25
from stat import S_ISDIR
26
26
from StringIO import StringIO
27
27
 
28
28
import bzrlib
29
 
import bzrlib.bzrdir as bzrdir
30
 
import bzrlib.errors as errors
31
29
from bzrlib.errors import (NotBranchError,
32
30
                           NoSuchFile,
33
31
                           UnknownFormatError,
34
32
                           UnsupportedFormatError,
35
33
                           )
36
 
import bzrlib.repository as repository
37
 
from bzrlib.tests import TestCase, TestCaseWithTransport
38
 
from bzrlib.transport import get_transport
39
 
from bzrlib.transport.http import HttpServer
 
34
from bzrlib import graph
 
35
from bzrlib.btree_index import BTreeBuilder, BTreeGraphIndex
 
36
from bzrlib.index import GraphIndex, InMemoryGraphIndex
 
37
from bzrlib.repository import RepositoryFormat
 
38
from bzrlib.smart import server
 
39
from bzrlib.tests import (
 
40
    TestCase,
 
41
    TestCaseWithTransport,
 
42
    TestSkipped,
 
43
    test_knit,
 
44
    )
 
45
from bzrlib.transport import (
 
46
    fakenfs,
 
47
    get_transport,
 
48
    )
40
49
from bzrlib.transport.memory import MemoryServer
 
50
from bzrlib.util import bencode
 
51
from bzrlib import (
 
52
    bzrdir,
 
53
    errors,
 
54
    inventory,
 
55
    osutils,
 
56
    progress,
 
57
    repository,
 
58
    revision as _mod_revision,
 
59
    symbol_versioning,
 
60
    upgrade,
 
61
    workingtree,
 
62
    )
 
63
from bzrlib.repofmt import knitrepo, weaverepo, pack_repo
41
64
 
42
65
 
43
66
class TestDefaultFormat(TestCase):
44
67
 
45
68
    def test_get_set_default_format(self):
 
69
        old_default = bzrdir.format_registry.get('default')
 
70
        private_default = old_default().repository_format.__class__
46
71
        old_format = repository.RepositoryFormat.get_default_format()
47
 
        self.assertTrue(isinstance(old_format, repository.RepositoryFormatKnit1))
48
 
        repository.RepositoryFormat.set_default_format(SampleRepositoryFormat())
 
72
        self.assertTrue(isinstance(old_format, private_default))
 
73
        def make_sample_bzrdir():
 
74
            my_bzrdir = bzrdir.BzrDirMetaFormat1()
 
75
            my_bzrdir.repository_format = SampleRepositoryFormat()
 
76
            return my_bzrdir
 
77
        bzrdir.format_registry.remove('default')
 
78
        bzrdir.format_registry.register('sample', make_sample_bzrdir, '')
 
79
        bzrdir.format_registry.set_default('sample')
49
80
        # creating a repository should now create an instrumented dir.
50
81
        try:
51
82
            # the default branch format is used by the meta dir format
54
85
            result = dir.create_repository()
55
86
            self.assertEqual(result, 'A bzr repository dir')
56
87
        finally:
57
 
            repository.RepositoryFormat.set_default_format(old_format)
58
 
        self.assertEqual(old_format, repository.RepositoryFormat.get_default_format())
 
88
            bzrdir.format_registry.remove('default')
 
89
            bzrdir.format_registry.remove('sample')
 
90
            bzrdir.format_registry.register('default', old_default, '')
 
91
        self.assertIsInstance(repository.RepositoryFormat.get_default_format(),
 
92
                              old_format.__class__)
59
93
 
60
94
 
61
95
class SampleRepositoryFormat(repository.RepositoryFormat):
72
106
    def initialize(self, a_bzrdir, shared=False):
73
107
        """Initialize a repository in a BzrDir"""
74
108
        t = a_bzrdir.get_repository_transport(self)
75
 
        t.put('format', StringIO(self.get_format_string()))
 
109
        t.put_bytes('format', self.get_format_string())
76
110
        return 'A bzr repository dir'
77
111
 
78
112
    def is_supported(self):
96
130
            t = get_transport(url)
97
131
            found_format = repository.RepositoryFormat.find_format(dir)
98
132
            self.failUnless(isinstance(found_format, format.__class__))
99
 
        check_format(repository.RepositoryFormat7(), "bar")
 
133
        check_format(weaverepo.RepositoryFormat7(), "bar")
100
134
        
101
135
    def test_find_format_no_repository(self):
102
136
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
129
163
 
130
164
class TestFormat6(TestCaseWithTransport):
131
165
 
 
166
    def test_attribute__fetch_order(self):
 
167
        """Weaves need topological data insertion."""
 
168
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
169
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
170
        self.assertEqual('topological', repo._fetch_order)
 
171
 
 
172
    def test_attribute__fetch_uses_deltas(self):
 
173
        """Weaves do not reuse deltas."""
 
174
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
175
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
176
        self.assertEqual(False, repo._fetch_uses_deltas)
 
177
 
 
178
    def test_attribute__fetch_reconcile(self):
 
179
        """Weave repositories need a reconcile after fetch."""
 
180
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
181
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
182
        self.assertEqual(True, repo._fetch_reconcile)
 
183
 
132
184
    def test_no_ancestry_weave(self):
133
185
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
134
 
        repo = repository.RepositoryFormat6().initialize(control)
 
186
        repo = weaverepo.RepositoryFormat6().initialize(control)
135
187
        # We no longer need to create the ancestry.weave file
136
188
        # since it is *never* used.
137
189
        self.assertRaises(NoSuchFile,
138
190
                          control.transport.get,
139
191
                          'ancestry.weave')
140
192
 
 
193
    def test_supports_external_lookups(self):
 
194
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
 
195
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
196
        self.assertFalse(repo._format.supports_external_lookups)
 
197
 
141
198
 
142
199
class TestFormat7(TestCaseWithTransport):
143
 
    
 
200
 
 
201
    def test_attribute__fetch_order(self):
 
202
        """Weaves need topological data insertion."""
 
203
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
204
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
205
        self.assertEqual('topological', repo._fetch_order)
 
206
 
 
207
    def test_attribute__fetch_uses_deltas(self):
 
208
        """Weaves do not reuse deltas."""
 
209
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
210
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
211
        self.assertEqual(False, repo._fetch_uses_deltas)
 
212
 
 
213
    def test_attribute__fetch_reconcile(self):
 
214
        """Weave repositories need a reconcile after fetch."""
 
215
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
216
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
217
        self.assertEqual(True, repo._fetch_reconcile)
 
218
 
144
219
    def test_disk_layout(self):
145
220
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
146
 
        repo = repository.RepositoryFormat7().initialize(control)
 
221
        repo = weaverepo.RepositoryFormat7().initialize(control)
147
222
        # in case of side effects of locking.
148
223
        repo.lock_write()
149
224
        repo.unlock()
162
237
                             'w\n'
163
238
                             'W\n',
164
239
                             t.get('inventory.weave').read())
 
240
        # Creating a file with id Foo:Bar results in a non-escaped file name on
 
241
        # disk.
 
242
        control.create_branch()
 
243
        tree = control.create_workingtree()
 
244
        tree.add(['foo'], ['Foo:Bar'], ['file'])
 
245
        tree.put_file_bytes_non_atomic('Foo:Bar', 'content\n')
 
246
        tree.commit('first post', rev_id='first')
 
247
        self.assertEqualDiff(
 
248
            '# bzr weave file v5\n'
 
249
            'i\n'
 
250
            '1 7fe70820e08a1aac0ef224d9c66ab66831cc4ab1\n'
 
251
            'n first\n'
 
252
            '\n'
 
253
            'w\n'
 
254
            '{ 0\n'
 
255
            '. content\n'
 
256
            '}\n'
 
257
            'W\n',
 
258
            t.get('weaves/74/Foo%3ABar.weave').read())
165
259
 
166
260
    def test_shared_disk_layout(self):
167
261
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
168
 
        repo = repository.RepositoryFormat7().initialize(control, shared=True)
 
262
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
169
263
        # we want:
170
264
        # format 'Bazaar-NG Repository format 7'
171
265
        # inventory.weave == empty_weave
188
282
    def test_creates_lockdir(self):
189
283
        """Make sure it appears to be controlled by a LockDir existence"""
190
284
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
191
 
        repo = repository.RepositoryFormat7().initialize(control, shared=True)
 
285
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
192
286
        t = control.get_repository_transport(None)
193
287
        # TODO: Should check there is a 'lock' toplevel directory, 
194
288
        # regardless of contents
204
298
        """repo format 7 actually locks on lockdir"""
205
299
        base_url = self.get_url()
206
300
        control = bzrdir.BzrDirMetaFormat1().initialize(base_url)
207
 
        repo = repository.RepositoryFormat7().initialize(control, shared=True)
 
301
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
208
302
        t = control.get_repository_transport(None)
209
303
        repo.lock_write()
210
304
        repo.unlock()
218
312
 
219
313
    def test_shared_no_tree_disk_layout(self):
220
314
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
221
 
        repo = repository.RepositoryFormat7().initialize(control, shared=True)
 
315
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
222
316
        repo.set_make_working_trees(False)
223
317
        # we want:
224
318
        # format 'Bazaar-NG Repository format 7'
242
336
                             'W\n',
243
337
                             t.get('inventory.weave').read())
244
338
 
 
339
    def test_supports_external_lookups(self):
 
340
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
 
341
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
342
        self.assertFalse(repo._format.supports_external_lookups)
 
343
 
245
344
 
246
345
class TestFormatKnit1(TestCaseWithTransport):
247
346
    
 
347
    def test_attribute__fetch_order(self):
 
348
        """Knits need topological data insertion."""
 
349
        repo = self.make_repository('.',
 
350
                format=bzrdir.format_registry.get('knit')())
 
351
        self.assertEqual('topological', repo._fetch_order)
 
352
 
 
353
    def test_attribute__fetch_uses_deltas(self):
 
354
        """Knits reuse deltas."""
 
355
        repo = self.make_repository('.',
 
356
                format=bzrdir.format_registry.get('knit')())
 
357
        self.assertEqual(True, repo._fetch_uses_deltas)
 
358
 
248
359
    def test_disk_layout(self):
249
360
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
250
 
        repo = repository.RepositoryFormatKnit1().initialize(control)
 
361
        repo = knitrepo.RepositoryFormatKnit1().initialize(control)
251
362
        # in case of side effects of locking.
252
363
        repo.lock_write()
253
364
        repo.unlock()
264
375
        # self.assertEqualDiff('', t.get('lock').read())
265
376
        self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
266
377
        self.check_knits(t)
 
378
        # Check per-file knits.
 
379
        branch = control.create_branch()
 
380
        tree = control.create_workingtree()
 
381
        tree.add(['foo'], ['Nasty-IdC:'], ['file'])
 
382
        tree.put_file_bytes_non_atomic('Nasty-IdC:', '')
 
383
        tree.commit('1st post', rev_id='foo')
 
384
        self.assertHasKnit(t, 'knits/e8/%254easty-%2549d%2543%253a',
 
385
            '\nfoo fulltext 0 81  :')
267
386
 
268
 
    def assertHasKnit(self, t, knit_name):
 
387
    def assertHasKnit(self, t, knit_name, extra_content=''):
269
388
        """Assert that knit_name exists on t."""
270
 
        self.assertEqualDiff('# bzr knit index 8\n',
 
389
        self.assertEqualDiff('# bzr knit index 8\n' + extra_content,
271
390
                             t.get(knit_name + '.kndx').read())
272
 
        # no default content
273
 
        self.assertTrue(t.has(knit_name + '.knit'))
274
391
 
275
392
    def check_knits(self, t):
276
393
        """check knit content for a repository."""
280
397
 
281
398
    def test_shared_disk_layout(self):
282
399
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
283
 
        repo = repository.RepositoryFormatKnit1().initialize(control, shared=True)
 
400
        repo = knitrepo.RepositoryFormatKnit1().initialize(control, shared=True)
284
401
        # we want:
285
402
        # format 'Bazaar-NG Knit Repository Format 1'
286
403
        # lock: is a directory
299
416
 
300
417
    def test_shared_no_tree_disk_layout(self):
301
418
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
302
 
        repo = repository.RepositoryFormatKnit1().initialize(control, shared=True)
 
419
        repo = knitrepo.RepositoryFormatKnit1().initialize(control, shared=True)
303
420
        repo.set_make_working_trees(False)
304
421
        # we want:
305
422
        # format 'Bazaar-NG Knit Repository Format 1'
320
437
        self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
321
438
        self.check_knits(t)
322
439
 
323
 
 
324
 
class InterString(repository.InterRepository):
325
 
    """An inter-repository optimised code path for strings.
326
 
 
327
 
    This is for use during testing where we use strings as repositories
 
440
    def test_deserialise_sets_root_revision(self):
 
441
        """We must have a inventory.root.revision
 
442
 
 
443
        Old versions of the XML5 serializer did not set the revision_id for
 
444
        the whole inventory. So we grab the one from the expected text. Which
 
445
        is valid when the api is not being abused.
 
446
        """
 
447
        repo = self.make_repository('.',
 
448
                format=bzrdir.format_registry.get('knit')())
 
449
        inv_xml = '<inventory format="5">\n</inventory>\n'
 
450
        inv = repo.deserialise_inventory('test-rev-id', inv_xml)
 
451
        self.assertEqual('test-rev-id', inv.root.revision)
 
452
 
 
453
    def test_deserialise_uses_global_revision_id(self):
 
454
        """If it is set, then we re-use the global revision id"""
 
455
        repo = self.make_repository('.',
 
456
                format=bzrdir.format_registry.get('knit')())
 
457
        inv_xml = ('<inventory format="5" revision_id="other-rev-id">\n'
 
458
                   '</inventory>\n')
 
459
        # Arguably, the deserialise_inventory should detect a mismatch, and
 
460
        # raise an error, rather than silently using one revision_id over the
 
461
        # other.
 
462
        self.assertRaises(AssertionError, repo.deserialise_inventory,
 
463
            'test-rev-id', inv_xml)
 
464
        inv = repo.deserialise_inventory('other-rev-id', inv_xml)
 
465
        self.assertEqual('other-rev-id', inv.root.revision)
 
466
 
 
467
    def test_supports_external_lookups(self):
 
468
        repo = self.make_repository('.',
 
469
                format=bzrdir.format_registry.get('knit')())
 
470
        self.assertFalse(repo._format.supports_external_lookups)
 
471
 
 
472
 
 
473
class DummyRepository(object):
 
474
    """A dummy repository for testing."""
 
475
 
 
476
    _format = None
 
477
    _serializer = None
 
478
 
 
479
    def supports_rich_root(self):
 
480
        return False
 
481
 
 
482
    def get_graph(self):
 
483
        raise NotImplementedError
 
484
 
 
485
    def get_parent_map(self, revision_ids):
 
486
        raise NotImplementedError
 
487
 
 
488
 
 
489
class InterDummy(repository.InterRepository):
 
490
    """An inter-repository optimised code path for DummyRepository.
 
491
 
 
492
    This is for use during testing where we use DummyRepository as repositories
328
493
    so that none of the default regsitered inter-repository classes will
329
 
    match.
 
494
    MATCH.
330
495
    """
331
496
 
332
497
    @staticmethod
333
498
    def is_compatible(repo_source, repo_target):
334
 
        """InterString is compatible with strings-as-repos."""
335
 
        return isinstance(repo_source, str) and isinstance(repo_target, str)
 
499
        """InterDummy is compatible with DummyRepository."""
 
500
        return (isinstance(repo_source, DummyRepository) and 
 
501
            isinstance(repo_target, DummyRepository))
336
502
 
337
503
 
338
504
class TestInterRepository(TestCaseWithTransport):
344
510
        # This also tests that the default registered optimised interrepository
345
511
        # classes do not barf inappropriately when a surprising repository type
346
512
        # is handed to them.
347
 
        dummy_a = "Repository 1."
348
 
        dummy_b = "Repository 2."
 
513
        dummy_a = DummyRepository()
 
514
        dummy_b = DummyRepository()
349
515
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
350
516
 
351
517
    def assertGetsDefaultInterRepository(self, repo_a, repo_b):
352
 
        """Asserts that InterRepository.get(repo_a, repo_b) -> the default."""
 
518
        """Asserts that InterRepository.get(repo_a, repo_b) -> the default.
 
519
        
 
520
        The effective default is now InterSameDataRepository because there is
 
521
        no actual sane default in the presence of incompatible data models.
 
522
        """
353
523
        inter_repo = repository.InterRepository.get(repo_a, repo_b)
354
 
        self.assertEqual(repository.InterRepository,
 
524
        self.assertEqual(repository.InterSameDataRepository,
355
525
                         inter_repo.__class__)
356
526
        self.assertEqual(repo_a, inter_repo.source)
357
527
        self.assertEqual(repo_b, inter_repo.target)
362
532
        # and that it is correctly selected when given a repository
363
533
        # pair that it returns true on for the is_compatible static method
364
534
        # check
365
 
        dummy_a = "Repository 1."
366
 
        dummy_b = "Repository 2."
367
 
        repository.InterRepository.register_optimiser(InterString)
 
535
        dummy_a = DummyRepository()
 
536
        dummy_b = DummyRepository()
 
537
        repo = self.make_repository('.')
 
538
        # hack dummies to look like repo somewhat.
 
539
        dummy_a._serializer = repo._serializer
 
540
        dummy_b._serializer = repo._serializer
 
541
        repository.InterRepository.register_optimiser(InterDummy)
368
542
        try:
369
 
            # we should get the default for something InterString returns False
 
543
            # we should get the default for something InterDummy returns False
370
544
            # to
371
 
            self.assertFalse(InterString.is_compatible(dummy_a, None))
372
 
            self.assertGetsDefaultInterRepository(dummy_a, None)
373
 
            # and we should get an InterString for a pair it 'likes'
374
 
            self.assertTrue(InterString.is_compatible(dummy_a, dummy_b))
 
545
            self.assertFalse(InterDummy.is_compatible(dummy_a, repo))
 
546
            self.assertGetsDefaultInterRepository(dummy_a, repo)
 
547
            # and we should get an InterDummy for a pair it 'likes'
 
548
            self.assertTrue(InterDummy.is_compatible(dummy_a, dummy_b))
375
549
            inter_repo = repository.InterRepository.get(dummy_a, dummy_b)
376
 
            self.assertEqual(InterString, inter_repo.__class__)
 
550
            self.assertEqual(InterDummy, inter_repo.__class__)
377
551
            self.assertEqual(dummy_a, inter_repo.source)
378
552
            self.assertEqual(dummy_b, inter_repo.target)
379
553
        finally:
380
 
            repository.InterRepository.unregister_optimiser(InterString)
 
554
            repository.InterRepository.unregister_optimiser(InterDummy)
381
555
        # now we should get the default InterRepository object again.
382
556
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
383
557
 
387
561
    def test_is_compatible_and_registered(self):
388
562
        # InterWeaveRepo is compatible when either side
389
563
        # is a format 5/6/7 branch
390
 
        formats = [repository.RepositoryFormat5(),
391
 
                   repository.RepositoryFormat6(),
392
 
                   repository.RepositoryFormat7()]
393
 
        incompatible_formats = [repository.RepositoryFormat4(),
394
 
                                repository.RepositoryFormatKnit1(),
 
564
        from bzrlib.repofmt import knitrepo, weaverepo
 
565
        formats = [weaverepo.RepositoryFormat5(),
 
566
                   weaverepo.RepositoryFormat6(),
 
567
                   weaverepo.RepositoryFormat7()]
 
568
        incompatible_formats = [weaverepo.RepositoryFormat4(),
 
569
                                knitrepo.RepositoryFormatKnit1(),
395
570
                                ]
396
571
        repo_a = self.make_repository('a')
397
572
        repo_b = self.make_repository('b')
418
593
        t = get_transport(self.get_url('.'))
419
594
        t.mkdir('repository')
420
595
        repo_dir = bzrdir.BzrDirMetaFormat1().initialize('repository')
421
 
        repo = repository.RepositoryFormat7().initialize(repo_dir)
422
 
        target_format = repository.RepositoryFormatKnit1()
 
596
        repo = weaverepo.RepositoryFormat7().initialize(repo_dir)
 
597
        target_format = knitrepo.RepositoryFormatKnit1()
423
598
        converter = repository.CopyConverter(target_format)
424
599
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
425
600
        try:
428
603
            pb.finished()
429
604
        repo = repo_dir.open_repository()
430
605
        self.assertTrue(isinstance(target_format, repo._format.__class__))
 
606
 
 
607
 
 
608
class TestMisc(TestCase):
 
609
    
 
610
    def test_unescape_xml(self):
 
611
        """We get some kind of error when malformed entities are passed"""
 
612
        self.assertRaises(KeyError, repository._unescape_xml, 'foo&bar;') 
 
613
 
 
614
 
 
615
class TestRepositoryFormatKnit3(TestCaseWithTransport):
 
616
 
 
617
    def test_attribute__fetch_order(self):
 
618
        """Knits need topological data insertion."""
 
619
        format = bzrdir.BzrDirMetaFormat1()
 
620
        format.repository_format = knitrepo.RepositoryFormatKnit3()
 
621
        repo = self.make_repository('.', format=format)
 
622
        self.assertEqual('topological', repo._fetch_order)
 
623
 
 
624
    def test_attribute__fetch_uses_deltas(self):
 
625
        """Knits reuse deltas."""
 
626
        format = bzrdir.BzrDirMetaFormat1()
 
627
        format.repository_format = knitrepo.RepositoryFormatKnit3()
 
628
        repo = self.make_repository('.', format=format)
 
629
        self.assertEqual(True, repo._fetch_uses_deltas)
 
630
 
 
631
    def test_convert(self):
 
632
        """Ensure the upgrade adds weaves for roots"""
 
633
        format = bzrdir.BzrDirMetaFormat1()
 
634
        format.repository_format = knitrepo.RepositoryFormatKnit1()
 
635
        tree = self.make_branch_and_tree('.', format)
 
636
        tree.commit("Dull commit", rev_id="dull")
 
637
        revision_tree = tree.branch.repository.revision_tree('dull')
 
638
        revision_tree.lock_read()
 
639
        try:
 
640
            self.assertRaises(errors.NoSuchFile, revision_tree.get_file_lines,
 
641
                revision_tree.inventory.root.file_id)
 
642
        finally:
 
643
            revision_tree.unlock()
 
644
        format = bzrdir.BzrDirMetaFormat1()
 
645
        format.repository_format = knitrepo.RepositoryFormatKnit3()
 
646
        upgrade.Convert('.', format)
 
647
        tree = workingtree.WorkingTree.open('.')
 
648
        revision_tree = tree.branch.repository.revision_tree('dull')
 
649
        revision_tree.lock_read()
 
650
        try:
 
651
            revision_tree.get_file_lines(revision_tree.inventory.root.file_id)
 
652
        finally:
 
653
            revision_tree.unlock()
 
654
        tree.commit("Another dull commit", rev_id='dull2')
 
655
        revision_tree = tree.branch.repository.revision_tree('dull2')
 
656
        revision_tree.lock_read()
 
657
        self.addCleanup(revision_tree.unlock)
 
658
        self.assertEqual('dull', revision_tree.inventory.root.revision)
 
659
 
 
660
    def test_supports_external_lookups(self):
 
661
        format = bzrdir.BzrDirMetaFormat1()
 
662
        format.repository_format = knitrepo.RepositoryFormatKnit3()
 
663
        repo = self.make_repository('.', format=format)
 
664
        self.assertFalse(repo._format.supports_external_lookups)
 
665
 
 
666
 
 
667
class TestWithBrokenRepo(TestCaseWithTransport):
 
668
    """These tests seem to be more appropriate as interface tests?"""
 
669
 
 
670
    def make_broken_repository(self):
 
671
        # XXX: This function is borrowed from Aaron's "Reconcile can fix bad
 
672
        # parent references" branch which is due to land in bzr.dev soon.  Once
 
673
        # it does, this duplication should be removed.
 
674
        repo = self.make_repository('broken-repo')
 
675
        cleanups = []
 
676
        try:
 
677
            repo.lock_write()
 
678
            cleanups.append(repo.unlock)
 
679
            repo.start_write_group()
 
680
            cleanups.append(repo.commit_write_group)
 
681
            # make rev1a: A well-formed revision, containing 'file1'
 
682
            inv = inventory.Inventory(revision_id='rev1a')
 
683
            inv.root.revision = 'rev1a'
 
684
            self.add_file(repo, inv, 'file1', 'rev1a', [])
 
685
            repo.add_inventory('rev1a', inv, [])
 
686
            revision = _mod_revision.Revision('rev1a',
 
687
                committer='jrandom@example.com', timestamp=0,
 
688
                inventory_sha1='', timezone=0, message='foo', parent_ids=[])
 
689
            repo.add_revision('rev1a',revision, inv)
 
690
 
 
691
            # make rev1b, which has no Revision, but has an Inventory, and
 
692
            # file1
 
693
            inv = inventory.Inventory(revision_id='rev1b')
 
694
            inv.root.revision = 'rev1b'
 
695
            self.add_file(repo, inv, 'file1', 'rev1b', [])
 
696
            repo.add_inventory('rev1b', inv, [])
 
697
 
 
698
            # make rev2, with file1 and file2
 
699
            # file2 is sane
 
700
            # file1 has 'rev1b' as an ancestor, even though this is not
 
701
            # mentioned by 'rev1a', making it an unreferenced ancestor
 
702
            inv = inventory.Inventory()
 
703
            self.add_file(repo, inv, 'file1', 'rev2', ['rev1a', 'rev1b'])
 
704
            self.add_file(repo, inv, 'file2', 'rev2', [])
 
705
            self.add_revision(repo, 'rev2', inv, ['rev1a'])
 
706
 
 
707
            # make ghost revision rev1c
 
708
            inv = inventory.Inventory()
 
709
            self.add_file(repo, inv, 'file2', 'rev1c', [])
 
710
 
 
711
            # make rev3 with file2
 
712
            # file2 refers to 'rev1c', which is a ghost in this repository, so
 
713
            # file2 cannot have rev1c as its ancestor.
 
714
            inv = inventory.Inventory()
 
715
            self.add_file(repo, inv, 'file2', 'rev3', ['rev1c'])
 
716
            self.add_revision(repo, 'rev3', inv, ['rev1c'])
 
717
            return repo
 
718
        finally:
 
719
            for cleanup in reversed(cleanups):
 
720
                cleanup()
 
721
 
 
722
    def add_revision(self, repo, revision_id, inv, parent_ids):
 
723
        inv.revision_id = revision_id
 
724
        inv.root.revision = revision_id
 
725
        repo.add_inventory(revision_id, inv, parent_ids)
 
726
        revision = _mod_revision.Revision(revision_id,
 
727
            committer='jrandom@example.com', timestamp=0, inventory_sha1='',
 
728
            timezone=0, message='foo', parent_ids=parent_ids)
 
729
        repo.add_revision(revision_id,revision, inv)
 
730
 
 
731
    def add_file(self, repo, inv, filename, revision, parents):
 
732
        file_id = filename + '-id'
 
733
        entry = inventory.InventoryFile(file_id, filename, 'TREE_ROOT')
 
734
        entry.revision = revision
 
735
        entry.text_size = 0
 
736
        inv.add(entry)
 
737
        text_key = (file_id, revision)
 
738
        parent_keys = [(file_id, parent) for parent in parents]
 
739
        repo.texts.add_lines(text_key, parent_keys, ['line\n'])
 
740
 
 
741
    def test_insert_from_broken_repo(self):
 
742
        """Inserting a data stream from a broken repository won't silently
 
743
        corrupt the target repository.
 
744
        """
 
745
        broken_repo = self.make_broken_repository()
 
746
        empty_repo = self.make_repository('empty-repo')
 
747
        self.assertRaises((errors.RevisionNotPresent, errors.BzrCheckError),
 
748
                          empty_repo.fetch, broken_repo)
 
749
 
 
750
 
 
751
class TestRepositoryPackCollection(TestCaseWithTransport):
 
752
 
 
753
    def get_format(self):
 
754
        return bzrdir.format_registry.make_bzrdir('pack-0.92')
 
755
 
 
756
    def get_packs(self):
 
757
        format = self.get_format()
 
758
        repo = self.make_repository('.', format=format)
 
759
        return repo._pack_collection
 
760
 
 
761
    def make_packs_and_alt_repo(self, write_lock=False):
 
762
        """Create a pack repo with 3 packs, and access it via a second repo."""
 
763
        tree = self.make_branch_and_tree('.')
 
764
        tree.lock_write()
 
765
        self.addCleanup(tree.unlock)
 
766
        rev1 = tree.commit('one')
 
767
        rev2 = tree.commit('two')
 
768
        rev3 = tree.commit('three')
 
769
        r = repository.Repository.open('.')
 
770
        if write_lock:
 
771
            r.lock_write()
 
772
        else:
 
773
            r.lock_read()
 
774
        self.addCleanup(r.unlock)
 
775
        packs = r._pack_collection
 
776
        packs.ensure_loaded()
 
777
        return tree, r, packs, [rev1, rev2, rev3]
 
778
 
 
779
    def test__max_pack_count(self):
 
780
        """The maximum pack count is a function of the number of revisions."""
 
781
        # no revisions - one pack, so that we can have a revision free repo
 
782
        # without it blowing up
 
783
        packs = self.get_packs()
 
784
        self.assertEqual(1, packs._max_pack_count(0))
 
785
        # after that the sum of the digits, - check the first 1-9
 
786
        self.assertEqual(1, packs._max_pack_count(1))
 
787
        self.assertEqual(2, packs._max_pack_count(2))
 
788
        self.assertEqual(3, packs._max_pack_count(3))
 
789
        self.assertEqual(4, packs._max_pack_count(4))
 
790
        self.assertEqual(5, packs._max_pack_count(5))
 
791
        self.assertEqual(6, packs._max_pack_count(6))
 
792
        self.assertEqual(7, packs._max_pack_count(7))
 
793
        self.assertEqual(8, packs._max_pack_count(8))
 
794
        self.assertEqual(9, packs._max_pack_count(9))
 
795
        # check the boundary cases with two digits for the next decade
 
796
        self.assertEqual(1, packs._max_pack_count(10))
 
797
        self.assertEqual(2, packs._max_pack_count(11))
 
798
        self.assertEqual(10, packs._max_pack_count(19))
 
799
        self.assertEqual(2, packs._max_pack_count(20))
 
800
        self.assertEqual(3, packs._max_pack_count(21))
 
801
        # check some arbitrary big numbers
 
802
        self.assertEqual(25, packs._max_pack_count(112894))
 
803
 
 
804
    def test_pack_distribution_zero(self):
 
805
        packs = self.get_packs()
 
806
        self.assertEqual([0], packs.pack_distribution(0))
 
807
 
 
808
    def test_ensure_loaded_unlocked(self):
 
809
        packs = self.get_packs()
 
810
        self.assertRaises(errors.ObjectNotLocked,
 
811
                          packs.ensure_loaded)
 
812
 
 
813
    def test_pack_distribution_one_to_nine(self):
 
814
        packs = self.get_packs()
 
815
        self.assertEqual([1],
 
816
            packs.pack_distribution(1))
 
817
        self.assertEqual([1, 1],
 
818
            packs.pack_distribution(2))
 
819
        self.assertEqual([1, 1, 1],
 
820
            packs.pack_distribution(3))
 
821
        self.assertEqual([1, 1, 1, 1],
 
822
            packs.pack_distribution(4))
 
823
        self.assertEqual([1, 1, 1, 1, 1],
 
824
            packs.pack_distribution(5))
 
825
        self.assertEqual([1, 1, 1, 1, 1, 1],
 
826
            packs.pack_distribution(6))
 
827
        self.assertEqual([1, 1, 1, 1, 1, 1, 1],
 
828
            packs.pack_distribution(7))
 
829
        self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1],
 
830
            packs.pack_distribution(8))
 
831
        self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1, 1],
 
832
            packs.pack_distribution(9))
 
833
 
 
834
    def test_pack_distribution_stable_at_boundaries(self):
 
835
        """When there are multi-rev packs the counts are stable."""
 
836
        packs = self.get_packs()
 
837
        # in 10s:
 
838
        self.assertEqual([10], packs.pack_distribution(10))
 
839
        self.assertEqual([10, 1], packs.pack_distribution(11))
 
840
        self.assertEqual([10, 10], packs.pack_distribution(20))
 
841
        self.assertEqual([10, 10, 1], packs.pack_distribution(21))
 
842
        # 100s
 
843
        self.assertEqual([100], packs.pack_distribution(100))
 
844
        self.assertEqual([100, 1], packs.pack_distribution(101))
 
845
        self.assertEqual([100, 10, 1], packs.pack_distribution(111))
 
846
        self.assertEqual([100, 100], packs.pack_distribution(200))
 
847
        self.assertEqual([100, 100, 1], packs.pack_distribution(201))
 
848
        self.assertEqual([100, 100, 10, 1], packs.pack_distribution(211))
 
849
 
 
850
    def test_plan_pack_operations_2009_revisions_skip_all_packs(self):
 
851
        packs = self.get_packs()
 
852
        existing_packs = [(2000, "big"), (9, "medium")]
 
853
        # rev count - 2009 -> 2x1000 + 9x1
 
854
        pack_operations = packs.plan_autopack_combinations(
 
855
            existing_packs, [1000, 1000, 1, 1, 1, 1, 1, 1, 1, 1, 1])
 
856
        self.assertEqual([], pack_operations)
 
857
 
 
858
    def test_plan_pack_operations_2010_revisions_skip_all_packs(self):
 
859
        packs = self.get_packs()
 
860
        existing_packs = [(2000, "big"), (9, "medium"), (1, "single")]
 
861
        # rev count - 2010 -> 2x1000 + 1x10
 
862
        pack_operations = packs.plan_autopack_combinations(
 
863
            existing_packs, [1000, 1000, 10])
 
864
        self.assertEqual([], pack_operations)
 
865
 
 
866
    def test_plan_pack_operations_2010_combines_smallest_two(self):
 
867
        packs = self.get_packs()
 
868
        existing_packs = [(1999, "big"), (9, "medium"), (1, "single2"),
 
869
            (1, "single1")]
 
870
        # rev count - 2010 -> 2x1000 + 1x10 (3)
 
871
        pack_operations = packs.plan_autopack_combinations(
 
872
            existing_packs, [1000, 1000, 10])
 
873
        self.assertEqual([[2, ["single2", "single1"]]], pack_operations)
 
874
 
 
875
    def test_plan_pack_operations_creates_a_single_op(self):
 
876
        packs = self.get_packs()
 
877
        existing_packs = [(50, 'a'), (40, 'b'), (30, 'c'), (10, 'd'),
 
878
                          (10, 'e'), (6, 'f'), (4, 'g')]
 
879
        # rev count 150 -> 1x100 and 5x10
 
880
        # The two size 10 packs do not need to be touched. The 50, 40, 30 would
 
881
        # be combined into a single 120 size pack, and the 6 & 4 would
 
882
        # becombined into a size 10 pack. However, if we have to rewrite them,
 
883
        # we save a pack file with no increased I/O by putting them into the
 
884
        # same file.
 
885
        distribution = packs.pack_distribution(150)
 
886
        pack_operations = packs.plan_autopack_combinations(existing_packs,
 
887
                                                           distribution)
 
888
        self.assertEqual([[130, ['a', 'b', 'c', 'f', 'g']]], pack_operations)
 
889
 
 
890
    def test_all_packs_none(self):
 
891
        format = self.get_format()
 
892
        tree = self.make_branch_and_tree('.', format=format)
 
893
        tree.lock_read()
 
894
        self.addCleanup(tree.unlock)
 
895
        packs = tree.branch.repository._pack_collection
 
896
        packs.ensure_loaded()
 
897
        self.assertEqual([], packs.all_packs())
 
898
 
 
899
    def test_all_packs_one(self):
 
900
        format = self.get_format()
 
901
        tree = self.make_branch_and_tree('.', format=format)
 
902
        tree.commit('start')
 
903
        tree.lock_read()
 
904
        self.addCleanup(tree.unlock)
 
905
        packs = tree.branch.repository._pack_collection
 
906
        packs.ensure_loaded()
 
907
        self.assertEqual([
 
908
            packs.get_pack_by_name(packs.names()[0])],
 
909
            packs.all_packs())
 
910
 
 
911
    def test_all_packs_two(self):
 
912
        format = self.get_format()
 
913
        tree = self.make_branch_and_tree('.', format=format)
 
914
        tree.commit('start')
 
915
        tree.commit('continue')
 
916
        tree.lock_read()
 
917
        self.addCleanup(tree.unlock)
 
918
        packs = tree.branch.repository._pack_collection
 
919
        packs.ensure_loaded()
 
920
        self.assertEqual([
 
921
            packs.get_pack_by_name(packs.names()[0]),
 
922
            packs.get_pack_by_name(packs.names()[1]),
 
923
            ], packs.all_packs())
 
924
 
 
925
    def test_get_pack_by_name(self):
 
926
        format = self.get_format()
 
927
        tree = self.make_branch_and_tree('.', format=format)
 
928
        tree.commit('start')
 
929
        tree.lock_read()
 
930
        self.addCleanup(tree.unlock)
 
931
        packs = tree.branch.repository._pack_collection
 
932
        packs.ensure_loaded()
 
933
        name = packs.names()[0]
 
934
        pack_1 = packs.get_pack_by_name(name)
 
935
        # the pack should be correctly initialised
 
936
        sizes = packs._names[name]
 
937
        rev_index = GraphIndex(packs._index_transport, name + '.rix', sizes[0])
 
938
        inv_index = GraphIndex(packs._index_transport, name + '.iix', sizes[1])
 
939
        txt_index = GraphIndex(packs._index_transport, name + '.tix', sizes[2])
 
940
        sig_index = GraphIndex(packs._index_transport, name + '.six', sizes[3])
 
941
        self.assertEqual(pack_repo.ExistingPack(packs._pack_transport,
 
942
            name, rev_index, inv_index, txt_index, sig_index), pack_1)
 
943
        # and the same instance should be returned on successive calls.
 
944
        self.assertTrue(pack_1 is packs.get_pack_by_name(name))
 
945
 
 
946
    def test_reload_pack_names_new_entry(self):
 
947
        tree, r, packs, revs = self.make_packs_and_alt_repo()
 
948
        names = packs.names()
 
949
        # Add a new pack file into the repository
 
950
        rev4 = tree.commit('four')
 
951
        new_names = tree.branch.repository._pack_collection.names()
 
952
        new_name = set(new_names).difference(names)
 
953
        self.assertEqual(1, len(new_name))
 
954
        new_name = new_name.pop()
 
955
        # The old collection hasn't noticed yet
 
956
        self.assertEqual(names, packs.names())
 
957
        self.assertTrue(packs.reload_pack_names())
 
958
        self.assertEqual(new_names, packs.names())
 
959
        # And the repository can access the new revision
 
960
        self.assertEqual({rev4:(revs[-1],)}, r.get_parent_map([rev4]))
 
961
        self.assertFalse(packs.reload_pack_names())
 
962
 
 
963
    def test_reload_pack_names_added_and_removed(self):
 
964
        tree, r, packs, revs = self.make_packs_and_alt_repo()
 
965
        names = packs.names()
 
966
        # Now repack the whole thing
 
967
        tree.branch.repository.pack()
 
968
        new_names = tree.branch.repository._pack_collection.names()
 
969
        # The other collection hasn't noticed yet
 
970
        self.assertEqual(names, packs.names())
 
971
        self.assertTrue(packs.reload_pack_names())
 
972
        self.assertEqual(new_names, packs.names())
 
973
        self.assertEqual({revs[-1]:(revs[-2],)}, r.get_parent_map([revs[-1]]))
 
974
        self.assertFalse(packs.reload_pack_names())
 
975
 
 
976
    def test_autopack_reloads_and_stops(self):
 
977
        tree, r, packs, revs = self.make_packs_and_alt_repo(write_lock=True)
 
978
        # After we have determined what needs to be autopacked, trigger a
 
979
        # full-pack via the other repo which will cause us to re-evaluate and
 
980
        # decide we don't need to do anything
 
981
        orig_execute = packs._execute_pack_operations
 
982
        def _munged_execute_pack_ops(*args, **kwargs):
 
983
            tree.branch.repository.pack()
 
984
            return orig_execute(*args, **kwargs)
 
985
        packs._execute_pack_operations = _munged_execute_pack_ops
 
986
        packs._max_pack_count = lambda x: 1
 
987
        packs.pack_distribution = lambda x: [10]
 
988
        self.assertFalse(packs.autopack())
 
989
        self.assertEqual(1, len(packs.names()))
 
990
        self.assertEqual(tree.branch.repository._pack_collection.names(),
 
991
                         packs.names())
 
992
 
 
993
 
 
994
class TestPack(TestCaseWithTransport):
 
995
    """Tests for the Pack object."""
 
996
 
 
997
    def assertCurrentlyEqual(self, left, right):
 
998
        self.assertTrue(left == right)
 
999
        self.assertTrue(right == left)
 
1000
        self.assertFalse(left != right)
 
1001
        self.assertFalse(right != left)
 
1002
 
 
1003
    def assertCurrentlyNotEqual(self, left, right):
 
1004
        self.assertFalse(left == right)
 
1005
        self.assertFalse(right == left)
 
1006
        self.assertTrue(left != right)
 
1007
        self.assertTrue(right != left)
 
1008
 
 
1009
    def test___eq____ne__(self):
 
1010
        left = pack_repo.ExistingPack('', '', '', '', '', '')
 
1011
        right = pack_repo.ExistingPack('', '', '', '', '', '')
 
1012
        self.assertCurrentlyEqual(left, right)
 
1013
        # change all attributes and ensure equality changes as we do.
 
1014
        left.revision_index = 'a'
 
1015
        self.assertCurrentlyNotEqual(left, right)
 
1016
        right.revision_index = 'a'
 
1017
        self.assertCurrentlyEqual(left, right)
 
1018
        left.inventory_index = 'a'
 
1019
        self.assertCurrentlyNotEqual(left, right)
 
1020
        right.inventory_index = 'a'
 
1021
        self.assertCurrentlyEqual(left, right)
 
1022
        left.text_index = 'a'
 
1023
        self.assertCurrentlyNotEqual(left, right)
 
1024
        right.text_index = 'a'
 
1025
        self.assertCurrentlyEqual(left, right)
 
1026
        left.signature_index = 'a'
 
1027
        self.assertCurrentlyNotEqual(left, right)
 
1028
        right.signature_index = 'a'
 
1029
        self.assertCurrentlyEqual(left, right)
 
1030
        left.name = 'a'
 
1031
        self.assertCurrentlyNotEqual(left, right)
 
1032
        right.name = 'a'
 
1033
        self.assertCurrentlyEqual(left, right)
 
1034
        left.transport = 'a'
 
1035
        self.assertCurrentlyNotEqual(left, right)
 
1036
        right.transport = 'a'
 
1037
        self.assertCurrentlyEqual(left, right)
 
1038
 
 
1039
    def test_file_name(self):
 
1040
        pack = pack_repo.ExistingPack('', 'a_name', '', '', '', '')
 
1041
        self.assertEqual('a_name.pack', pack.file_name())
 
1042
 
 
1043
 
 
1044
class TestNewPack(TestCaseWithTransport):
 
1045
    """Tests for pack_repo.NewPack."""
 
1046
 
 
1047
    def test_new_instance_attributes(self):
 
1048
        upload_transport = self.get_transport('upload')
 
1049
        pack_transport = self.get_transport('pack')
 
1050
        index_transport = self.get_transport('index')
 
1051
        upload_transport.mkdir('.')
 
1052
        collection = pack_repo.RepositoryPackCollection(repo=None,
 
1053
            transport=self.get_transport('.'),
 
1054
            index_transport=index_transport,
 
1055
            upload_transport=upload_transport,
 
1056
            pack_transport=pack_transport,
 
1057
            index_builder_class=BTreeBuilder,
 
1058
            index_class=BTreeGraphIndex)
 
1059
        pack = pack_repo.NewPack(collection)
 
1060
        self.assertIsInstance(pack.revision_index, BTreeBuilder)
 
1061
        self.assertIsInstance(pack.inventory_index, BTreeBuilder)
 
1062
        self.assertIsInstance(pack._hash, type(osutils.md5()))
 
1063
        self.assertTrue(pack.upload_transport is upload_transport)
 
1064
        self.assertTrue(pack.index_transport is index_transport)
 
1065
        self.assertTrue(pack.pack_transport is pack_transport)
 
1066
        self.assertEqual(None, pack.index_sizes)
 
1067
        self.assertEqual(20, len(pack.random_name))
 
1068
        self.assertIsInstance(pack.random_name, str)
 
1069
        self.assertIsInstance(pack.start_time, float)
 
1070
 
 
1071
 
 
1072
class TestPacker(TestCaseWithTransport):
 
1073
    """Tests for the packs repository Packer class."""
 
1074
 
 
1075
    def test_pack_optimizes_pack_order(self):
 
1076
        builder = self.make_branch_builder('.')
 
1077
        builder.start_series()
 
1078
        builder.build_snapshot('A', None, [
 
1079
            ('add', ('', 'root-id', 'directory', None)),
 
1080
            ('add', ('f', 'f-id', 'file', 'content\n'))])
 
1081
        builder.build_snapshot('B', ['A'],
 
1082
            [('modify', ('f-id', 'new-content\n'))])
 
1083
        builder.build_snapshot('C', ['B'],
 
1084
            [('modify', ('f-id', 'third-content\n'))])
 
1085
        builder.build_snapshot('D', ['C'],
 
1086
            [('modify', ('f-id', 'fourth-content\n'))])
 
1087
        b = builder.get_branch()
 
1088
        b.lock_read()
 
1089
        builder.finish_series()
 
1090
        self.addCleanup(b.unlock)
 
1091
        # At this point, we should have 4 pack files available
 
1092
        # Because of how they were built, they correspond to
 
1093
        # ['D', 'C', 'B', 'A']
 
1094
        packs = b.repository._pack_collection.packs
 
1095
        packer = pack_repo.Packer(b.repository._pack_collection,
 
1096
                                  packs, 'testing',
 
1097
                                  revision_ids=['B', 'C'])
 
1098
        # Now, when we are copying the B & C revisions, their pack files should
 
1099
        # be moved to the front of the stack
 
1100
        # The new ordering moves B & C to the front of the .packs attribute,
 
1101
        # and leaves the others in the original order.
 
1102
        new_packs = [packs[1], packs[2], packs[0], packs[3]]
 
1103
        new_pack = packer.pack()
 
1104
        self.assertEqual(new_packs, packer.packs)
 
1105
 
 
1106
 
 
1107
class TestOptimisingPacker(TestCaseWithTransport):
 
1108
    """Tests for the OptimisingPacker class."""
 
1109
 
 
1110
    def get_pack_collection(self):
 
1111
        repo = self.make_repository('.')
 
1112
        return repo._pack_collection
 
1113
 
 
1114
    def test_open_pack_will_optimise(self):
 
1115
        packer = pack_repo.OptimisingPacker(self.get_pack_collection(),
 
1116
                                            [], '.test')
 
1117
        new_pack = packer.open_pack()
 
1118
        self.assertIsInstance(new_pack, pack_repo.NewPack)
 
1119
        self.assertTrue(new_pack.revision_index._optimize_for_size)
 
1120
        self.assertTrue(new_pack.inventory_index._optimize_for_size)
 
1121
        self.assertTrue(new_pack.text_index._optimize_for_size)
 
1122
        self.assertTrue(new_pack.signature_index._optimize_for_size)
 
1123
 
 
1124
 
 
1125
class TestInterDifferingSerializer(TestCaseWithTransport):
 
1126
 
 
1127
    def test_progress_bar(self):
 
1128
        tree = self.make_branch_and_tree('tree')
 
1129
        tree.commit('rev1', rev_id='rev-1')
 
1130
        tree.commit('rev2', rev_id='rev-2')
 
1131
        tree.commit('rev3', rev_id='rev-3')
 
1132
        repo = self.make_repository('repo')
 
1133
        inter_repo = repository.InterDifferingSerializer(
 
1134
            tree.branch.repository, repo)
 
1135
        pb = progress.InstrumentedProgress(to_file=StringIO())
 
1136
        pb.never_throttle = True
 
1137
        inter_repo.fetch('rev-1', pb)
 
1138
        self.assertEqual('Transferring revisions', pb.last_msg)
 
1139
        self.assertEqual(1, pb.last_cnt)
 
1140
        self.assertEqual(1, pb.last_total)
 
1141
        inter_repo.fetch('rev-3', pb)
 
1142
        self.assertEqual(2, pb.last_cnt)
 
1143
        self.assertEqual(2, pb.last_total)