~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_repository.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-03-08 00:37:41 UTC
  • mfrom: (1594.2.4 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20060308003741-08afccbf89005e87
Merge in :
 * Knit repositories use knits
 * Nested progress bar support.
 * Ghost aware graph api.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007 Canonical Ltd
2
 
#
 
1
# (C) 2006 Canonical Ltd
 
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
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
#
 
7
 
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
#
 
12
 
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
22
also see this file.
23
23
"""
24
24
 
25
 
import md5
26
 
from stat import S_ISDIR
 
25
from stat import *
27
26
from StringIO import StringIO
28
27
 
29
28
import bzrlib
 
29
import bzrlib.bzrdir as bzrdir
 
30
import bzrlib.errors as errors
30
31
from bzrlib.errors import (NotBranchError,
31
32
                           NoSuchFile,
32
33
                           UnknownFormatError,
33
34
                           UnsupportedFormatError,
34
35
                           )
35
 
from bzrlib.index import GraphIndex, InMemoryGraphIndex
36
 
from bzrlib.repository import RepositoryFormat
37
 
from bzrlib.smart import server
38
 
from bzrlib.tests import (
39
 
    TestCase,
40
 
    TestCaseWithTransport,
41
 
    test_knit,
42
 
    )
 
36
import bzrlib.repository as repository
 
37
from bzrlib.tests import TestCase, TestCaseWithTransport
43
38
from bzrlib.transport import get_transport
 
39
from bzrlib.transport.http import HttpServer
44
40
from bzrlib.transport.memory import MemoryServer
45
 
from bzrlib.util import bencode
46
 
from bzrlib import (
47
 
    bzrdir,
48
 
    errors,
49
 
    inventory,
50
 
    repository,
51
 
    revision as _mod_revision,
52
 
    symbol_versioning,
53
 
    upgrade,
54
 
    workingtree,
55
 
    )
56
 
from bzrlib.repofmt import knitrepo, weaverepo, pack_repo
57
41
 
58
42
 
59
43
class TestDefaultFormat(TestCase):
60
44
 
61
45
    def test_get_set_default_format(self):
62
 
        old_default = bzrdir.format_registry.get('default')
63
 
        private_default = old_default().repository_format.__class__
64
46
        old_format = repository.RepositoryFormat.get_default_format()
65
 
        self.assertTrue(isinstance(old_format, private_default))
66
 
        def make_sample_bzrdir():
67
 
            my_bzrdir = bzrdir.BzrDirMetaFormat1()
68
 
            my_bzrdir.repository_format = SampleRepositoryFormat()
69
 
            return my_bzrdir
70
 
        bzrdir.format_registry.remove('default')
71
 
        bzrdir.format_registry.register('sample', make_sample_bzrdir, '')
72
 
        bzrdir.format_registry.set_default('sample')
 
47
        # default is None - we cannot create a Repository independently yet
 
48
        self.assertTrue(isinstance(old_format, repository.RepositoryFormat7))
 
49
        repository.RepositoryFormat.set_default_format(SampleRepositoryFormat())
73
50
        # creating a repository should now create an instrumented dir.
74
51
        try:
75
52
            # the default branch format is used by the meta dir format
76
53
            # which is not the default bzrdir format at this point
77
 
            dir = bzrdir.BzrDirMetaFormat1().initialize('memory:///')
 
54
            dir = bzrdir.BzrDirMetaFormat1().initialize('memory:/')
78
55
            result = dir.create_repository()
79
56
            self.assertEqual(result, 'A bzr repository dir')
80
57
        finally:
81
 
            bzrdir.format_registry.remove('default')
82
 
            bzrdir.format_registry.remove('sample')
83
 
            bzrdir.format_registry.register('default', old_default, '')
84
 
        self.assertIsInstance(repository.RepositoryFormat.get_default_format(),
85
 
                              old_format.__class__)
 
58
            repository.RepositoryFormat.set_default_format(old_format)
 
59
        self.assertEqual(old_format, repository.RepositoryFormat.get_default_format())
86
60
 
87
61
 
88
62
class SampleRepositoryFormat(repository.RepositoryFormat):
99
73
    def initialize(self, a_bzrdir, shared=False):
100
74
        """Initialize a repository in a BzrDir"""
101
75
        t = a_bzrdir.get_repository_transport(self)
102
 
        t.put_bytes('format', self.get_format_string())
 
76
        t.put('format', StringIO(self.get_format_string()))
103
77
        return 'A bzr repository dir'
104
78
 
105
79
    def is_supported(self):
123
97
            t = get_transport(url)
124
98
            found_format = repository.RepositoryFormat.find_format(dir)
125
99
            self.failUnless(isinstance(found_format, format.__class__))
126
 
        check_format(weaverepo.RepositoryFormat7(), "bar")
 
100
        check_format(repository.RepositoryFormat7(), "bar")
127
101
        
128
102
    def test_find_format_no_repository(self):
129
103
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
158
132
 
159
133
    def test_no_ancestry_weave(self):
160
134
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
161
 
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
135
        repo = repository.RepositoryFormat6().initialize(control)
162
136
        # We no longer need to create the ancestry.weave file
163
137
        # since it is *never* used.
164
138
        self.assertRaises(NoSuchFile,
165
139
                          control.transport.get,
166
140
                          'ancestry.weave')
167
141
 
168
 
    def test_exposed_versioned_files_are_marked_dirty(self):
169
 
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
170
 
        repo = weaverepo.RepositoryFormat6().initialize(control)
171
 
        repo.lock_write()
172
 
        inv = repo.get_inventory_weave()
173
 
        repo.unlock()
174
 
        self.assertRaises(errors.OutSideTransaction,
175
 
            inv.add_lines, 'foo', [], [])
176
 
 
177
142
 
178
143
class TestFormat7(TestCaseWithTransport):
179
144
    
180
145
    def test_disk_layout(self):
181
146
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
182
 
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
147
        repo = repository.RepositoryFormat7().initialize(control)
183
148
        # in case of side effects of locking.
184
149
        repo.lock_write()
185
150
        repo.unlock()
201
166
 
202
167
    def test_shared_disk_layout(self):
203
168
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
204
 
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
169
        repo = repository.RepositoryFormat7().initialize(control, shared=True)
205
170
        # we want:
206
171
        # format 'Bazaar-NG Repository format 7'
207
172
        # inventory.weave == empty_weave
224
189
    def test_creates_lockdir(self):
225
190
        """Make sure it appears to be controlled by a LockDir existence"""
226
191
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
227
 
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
192
        repo = repository.RepositoryFormat7().initialize(control, shared=True)
228
193
        t = control.get_repository_transport(None)
229
194
        # TODO: Should check there is a 'lock' toplevel directory, 
230
195
        # regardless of contents
231
196
        self.assertFalse(t.has('lock/held/info'))
232
197
        repo.lock_write()
233
 
        try:
234
 
            self.assertTrue(t.has('lock/held/info'))
235
 
        finally:
236
 
            # unlock so we don't get a warning about failing to do so
237
 
            repo.unlock()
 
198
        self.assertTrue(t.has('lock/held/info'))
238
199
 
239
200
    def test_uses_lockdir(self):
240
201
        """repo format 7 actually locks on lockdir"""
241
202
        base_url = self.get_url()
242
203
        control = bzrdir.BzrDirMetaFormat1().initialize(base_url)
243
 
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
204
        repo = repository.RepositoryFormat7().initialize(control, shared=True)
244
205
        t = control.get_repository_transport(None)
245
206
        repo.lock_write()
246
207
        repo.unlock()
254
215
 
255
216
    def test_shared_no_tree_disk_layout(self):
256
217
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
257
 
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
218
        repo = repository.RepositoryFormat7().initialize(control, shared=True)
258
219
        repo.set_make_working_trees(False)
259
220
        # we want:
260
221
        # format 'Bazaar-NG Repository format 7'
278
239
                             'W\n',
279
240
                             t.get('inventory.weave').read())
280
241
 
281
 
    def test_exposed_versioned_files_are_marked_dirty(self):
282
 
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
283
 
        repo = weaverepo.RepositoryFormat7().initialize(control)
284
 
        repo.lock_write()
285
 
        inv = repo.get_inventory_weave()
286
 
        repo.unlock()
287
 
        self.assertRaises(errors.OutSideTransaction,
288
 
            inv.add_lines, 'foo', [], [])
289
 
 
290
242
 
291
243
class TestFormatKnit1(TestCaseWithTransport):
292
244
    
293
245
    def test_disk_layout(self):
294
246
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
295
 
        repo = knitrepo.RepositoryFormatKnit1().initialize(control)
 
247
        repo = repository.RepositoryFormatKnit1().initialize(control)
296
248
        # in case of side effects of locking.
297
249
        repo.lock_write()
298
250
        repo.unlock()
310
262
        self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
311
263
        self.check_knits(t)
312
264
 
313
 
    def assertHasKnit(self, t, knit_name):
314
 
        """Assert that knit_name exists on t."""
315
 
        self.assertEqualDiff('# bzr knit index 8\n',
316
 
                             t.get(knit_name + '.kndx').read())
317
 
        # no default content
318
 
        self.assertTrue(t.has(knit_name + '.knit'))
319
 
 
320
265
    def check_knits(self, t):
321
266
        """check knit content for a repository."""
322
 
        self.assertHasKnit(t, 'inventory')
323
 
        self.assertHasKnit(t, 'revisions')
324
 
        self.assertHasKnit(t, 'signatures')
 
267
        self.assertEqualDiff('# bzr knit index 7\n',
 
268
                             t.get('inventory.kndx').read())
 
269
        # no default content
 
270
        self.assertTrue(t.has('inventory.knit'))
 
271
        self.assertEqualDiff('# bzr knit index 7\n',
 
272
                             t.get('revisions.kndx').read())
 
273
        # no default content
 
274
        self.assertTrue(t.has('revisions.knit'))
 
275
        self.assertEqualDiff('# bzr knit index 7\n',
 
276
                             t.get('signatures.kndx').read())
 
277
        # no default content
 
278
        self.assertTrue(t.has('signatures.knit'))
325
279
 
326
280
    def test_shared_disk_layout(self):
327
281
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
328
 
        repo = knitrepo.RepositoryFormatKnit1().initialize(control, shared=True)
 
282
        repo = repository.RepositoryFormatKnit1().initialize(control, shared=True)
329
283
        # we want:
330
284
        # format 'Bazaar-NG Knit Repository Format 1'
331
285
        # lock: is a directory
344
298
 
345
299
    def test_shared_no_tree_disk_layout(self):
346
300
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
347
 
        repo = knitrepo.RepositoryFormatKnit1().initialize(control, shared=True)
 
301
        repo = repository.RepositoryFormatKnit1().initialize(control, shared=True)
348
302
        repo.set_make_working_trees(False)
349
303
        # we want:
350
304
        # format 'Bazaar-NG Knit Repository Format 1'
365
319
        self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
366
320
        self.check_knits(t)
367
321
 
368
 
    def test_exposed_versioned_files_are_marked_dirty(self):
369
 
        format = bzrdir.BzrDirMetaFormat1()
370
 
        format.repository_format = knitrepo.RepositoryFormatKnit1()
371
 
        repo = self.make_repository('.', format=format)
372
 
        repo.lock_write()
373
 
        inv = repo.get_inventory_weave()
374
 
        repo.unlock()
375
 
        self.assertRaises(errors.OutSideTransaction,
376
 
            inv.add_lines, 'foo', [], [])
377
 
 
378
 
    def test_deserialise_sets_root_revision(self):
379
 
        """We must have a inventory.root.revision
380
 
 
381
 
        Old versions of the XML5 serializer did not set the revision_id for
382
 
        the whole inventory. So we grab the one from the expected text. Which
383
 
        is valid when the api is not being abused.
384
 
        """
385
 
        repo = self.make_repository('.',
386
 
                format=bzrdir.format_registry.get('knit')())
387
 
        inv_xml = '<inventory format="5">\n</inventory>\n'
388
 
        inv = repo.deserialise_inventory('test-rev-id', inv_xml)
389
 
        self.assertEqual('test-rev-id', inv.root.revision)
390
 
 
391
 
    def test_deserialise_uses_global_revision_id(self):
392
 
        """If it is set, then we re-use the global revision id"""
393
 
        repo = self.make_repository('.',
394
 
                format=bzrdir.format_registry.get('knit')())
395
 
        inv_xml = ('<inventory format="5" revision_id="other-rev-id">\n'
396
 
                   '</inventory>\n')
397
 
        # Arguably, the deserialise_inventory should detect a mismatch, and
398
 
        # raise an error, rather than silently using one revision_id over the
399
 
        # other.
400
 
        inv = repo.deserialise_inventory('test-rev-id', inv_xml)
401
 
        self.assertEqual('other-rev-id', inv.root.revision)
402
 
 
403
 
 
404
 
class KnitRepositoryStreamTests(test_knit.KnitTests):
405
 
    """Tests for knitrepo._get_stream_as_bytes."""
406
 
 
407
 
    def test_get_stream_as_bytes(self):
408
 
        # Make a simple knit
409
 
        k1 = self.make_test_knit()
410
 
        k1.add_lines('text-a', [], test_knit.split_lines(test_knit.TEXT_1))
411
 
        
412
 
        # Serialise it, check the output.
413
 
        bytes = knitrepo._get_stream_as_bytes(k1, ['text-a'])
414
 
        data = bencode.bdecode(bytes)
415
 
        format, record = data
416
 
        self.assertEqual('knit-plain', format)
417
 
        self.assertEqual(['text-a', ['fulltext'], []], record[:3])
418
 
        self.assertRecordContentEqual(k1, 'text-a', record[3])
419
 
 
420
 
    def test_get_stream_as_bytes_all(self):
421
 
        """Get a serialised data stream for all the records in a knit.
422
 
 
423
 
        Much like test_get_stream_all, except for get_stream_as_bytes.
424
 
        """
425
 
        k1 = self.make_test_knit()
426
 
        # Insert the same data as BasicKnitTests.test_knit_join, as they seem
427
 
        # to cover a range of cases (no parents, one parent, multiple parents).
428
 
        test_data = [
429
 
            ('text-a', [], test_knit.TEXT_1),
430
 
            ('text-b', ['text-a'], test_knit.TEXT_1),
431
 
            ('text-c', [], test_knit.TEXT_1),
432
 
            ('text-d', ['text-c'], test_knit.TEXT_1),
433
 
            ('text-m', ['text-b', 'text-d'], test_knit.TEXT_1),
434
 
           ]
435
 
        expected_data_list = [
436
 
            # version, options, parents
437
 
            ('text-a', ['fulltext'], []),
438
 
            ('text-b', ['line-delta'], ['text-a']),
439
 
            ('text-c', ['fulltext'], []),
440
 
            ('text-d', ['line-delta'], ['text-c']),
441
 
            ('text-m', ['line-delta'], ['text-b', 'text-d']),
442
 
            ]
443
 
        for version_id, parents, lines in test_data:
444
 
            k1.add_lines(version_id, parents, test_knit.split_lines(lines))
445
 
 
446
 
        bytes = knitrepo._get_stream_as_bytes(
447
 
            k1, ['text-a', 'text-b', 'text-c', 'text-d', 'text-m'])
448
 
 
449
 
        data = bencode.bdecode(bytes)
450
 
        format = data.pop(0)
451
 
        self.assertEqual('knit-plain', format)
452
 
 
453
 
        for expected, actual in zip(expected_data_list, data):
454
 
            expected_version = expected[0]
455
 
            expected_options = expected[1]
456
 
            expected_parents = expected[2]
457
 
            version, options, parents, bytes = actual
458
 
            self.assertEqual(expected_version, version)
459
 
            self.assertEqual(expected_options, options)
460
 
            self.assertEqual(expected_parents, parents)
461
 
            self.assertRecordContentEqual(k1, version, bytes)
462
 
 
463
 
 
464
 
class DummyRepository(object):
465
 
    """A dummy repository for testing."""
466
 
 
467
 
    _serializer = None
468
 
 
469
 
    def supports_rich_root(self):
470
 
        return False
471
 
 
472
 
 
473
 
class InterDummy(repository.InterRepository):
474
 
    """An inter-repository optimised code path for DummyRepository.
475
 
 
476
 
    This is for use during testing where we use DummyRepository as repositories
 
322
 
 
323
class InterString(repository.InterRepository):
 
324
    """An inter-repository optimised code path for strings.
 
325
 
 
326
    This is for use during testing where we use strings as repositories
477
327
    so that none of the default regsitered inter-repository classes will
478
 
    MATCH.
 
328
    match.
479
329
    """
480
330
 
481
331
    @staticmethod
482
332
    def is_compatible(repo_source, repo_target):
483
 
        """InterDummy is compatible with DummyRepository."""
484
 
        return (isinstance(repo_source, DummyRepository) and 
485
 
            isinstance(repo_target, DummyRepository))
 
333
        """InterString is compatible with strings-as-repos."""
 
334
        return isinstance(repo_source, str) and isinstance(repo_target, str)
486
335
 
487
336
 
488
337
class TestInterRepository(TestCaseWithTransport):
494
343
        # This also tests that the default registered optimised interrepository
495
344
        # classes do not barf inappropriately when a surprising repository type
496
345
        # is handed to them.
497
 
        dummy_a = DummyRepository()
498
 
        dummy_b = DummyRepository()
 
346
        dummy_a = "Repository 1."
 
347
        dummy_b = "Repository 2."
499
348
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
500
349
 
501
350
    def assertGetsDefaultInterRepository(self, repo_a, repo_b):
502
 
        """Asserts that InterRepository.get(repo_a, repo_b) -> the default.
503
 
        
504
 
        The effective default is now InterSameDataRepository because there is
505
 
        no actual sane default in the presence of incompatible data models.
506
 
        """
 
351
        """Asserts that InterRepository.get(repo_a, repo_b) -> the default."""
507
352
        inter_repo = repository.InterRepository.get(repo_a, repo_b)
508
 
        self.assertEqual(repository.InterSameDataRepository,
 
353
        self.assertEqual(repository.InterRepository,
509
354
                         inter_repo.__class__)
510
355
        self.assertEqual(repo_a, inter_repo.source)
511
356
        self.assertEqual(repo_b, inter_repo.target)
516
361
        # and that it is correctly selected when given a repository
517
362
        # pair that it returns true on for the is_compatible static method
518
363
        # check
519
 
        dummy_a = DummyRepository()
520
 
        dummy_b = DummyRepository()
521
 
        repo = self.make_repository('.')
522
 
        # hack dummies to look like repo somewhat.
523
 
        dummy_a._serializer = repo._serializer
524
 
        dummy_b._serializer = repo._serializer
525
 
        repository.InterRepository.register_optimiser(InterDummy)
 
364
        dummy_a = "Repository 1."
 
365
        dummy_b = "Repository 2."
 
366
        repository.InterRepository.register_optimiser(InterString)
526
367
        try:
527
 
            # we should get the default for something InterDummy returns False
 
368
            # we should get the default for something InterString returns False
528
369
            # to
529
 
            self.assertFalse(InterDummy.is_compatible(dummy_a, repo))
530
 
            self.assertGetsDefaultInterRepository(dummy_a, repo)
531
 
            # and we should get an InterDummy for a pair it 'likes'
532
 
            self.assertTrue(InterDummy.is_compatible(dummy_a, dummy_b))
 
370
            self.assertFalse(InterString.is_compatible(dummy_a, None))
 
371
            self.assertGetsDefaultInterRepository(dummy_a, None)
 
372
            # and we should get an InterString for a pair it 'likes'
 
373
            self.assertTrue(InterString.is_compatible(dummy_a, dummy_b))
533
374
            inter_repo = repository.InterRepository.get(dummy_a, dummy_b)
534
 
            self.assertEqual(InterDummy, inter_repo.__class__)
 
375
            self.assertEqual(InterString, inter_repo.__class__)
535
376
            self.assertEqual(dummy_a, inter_repo.source)
536
377
            self.assertEqual(dummy_b, inter_repo.target)
537
378
        finally:
538
 
            repository.InterRepository.unregister_optimiser(InterDummy)
 
379
            repository.InterRepository.unregister_optimiser(InterString)
539
380
        # now we should get the default InterRepository object again.
540
381
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
541
382
 
545
386
    def test_is_compatible_and_registered(self):
546
387
        # InterWeaveRepo is compatible when either side
547
388
        # is a format 5/6/7 branch
548
 
        from bzrlib.repofmt import knitrepo, weaverepo
549
 
        formats = [weaverepo.RepositoryFormat5(),
550
 
                   weaverepo.RepositoryFormat6(),
551
 
                   weaverepo.RepositoryFormat7()]
552
 
        incompatible_formats = [weaverepo.RepositoryFormat4(),
553
 
                                knitrepo.RepositoryFormatKnit1(),
 
389
        formats = [repository.RepositoryFormat5(),
 
390
                   repository.RepositoryFormat6(),
 
391
                   repository.RepositoryFormat7()]
 
392
        incompatible_formats = [repository.RepositoryFormat4(),
 
393
                                repository.RepositoryFormatKnit1(),
554
394
                                ]
555
395
        repo_a = self.make_repository('a')
556
396
        repo_b = self.make_repository('b')
571
411
                                                        repo_b).__class__)
572
412
 
573
413
 
574
 
class TestInterRemoteToOther(TestCaseWithTransport):
575
 
 
576
 
    def make_remote_repository(self, path, backing_format=None):
577
 
        """Make a RemoteRepository object backed by a real repository that will
578
 
        be created at the given path."""
579
 
        self.make_repository(path, format=backing_format)
580
 
        smart_server = server.SmartTCPServer_for_testing()
581
 
        smart_server.setUp()
582
 
        remote_transport = get_transport(smart_server.get_url()).clone(path)
583
 
        self.addCleanup(smart_server.tearDown)
584
 
        remote_bzrdir = bzrdir.BzrDir.open_from_transport(remote_transport)
585
 
        remote_repo = remote_bzrdir.open_repository()
586
 
        return remote_repo
587
 
 
588
 
    def test_is_compatible_same_format(self):
589
 
        """InterRemoteToOther is compatible with a remote repository and a
590
 
        second repository that have the same format."""
591
 
        local_repo = self.make_repository('local')
592
 
        remote_repo = self.make_remote_repository('remote')
593
 
        is_compatible = repository.InterRemoteToOther.is_compatible
594
 
        self.assertTrue(
595
 
            is_compatible(remote_repo, local_repo),
596
 
            "InterRemoteToOther(%r, %r) is false" % (remote_repo, local_repo))
597
 
          
598
 
    def test_is_incompatible_different_format(self):
599
 
        local_repo = self.make_repository('local', 'dirstate')
600
 
        remote_repo = self.make_remote_repository('a', 'dirstate-with-subtree')
601
 
        is_compatible = repository.InterRemoteToOther.is_compatible
602
 
        self.assertFalse(
603
 
            is_compatible(remote_repo, local_repo),
604
 
            "InterRemoteToOther(%r, %r) is true" % (local_repo, remote_repo))
605
 
 
606
 
    def test_is_incompatible_different_format_both_remote(self):
607
 
        remote_repo_a = self.make_remote_repository(
608
 
            'a', 'dirstate-with-subtree')
609
 
        remote_repo_b = self.make_remote_repository('b', 'dirstate')
610
 
        is_compatible = repository.InterRemoteToOther.is_compatible
611
 
        self.assertFalse(
612
 
            is_compatible(remote_repo_a, remote_repo_b),
613
 
            "InterRemoteToOther(%r, %r) is true"
614
 
            % (remote_repo_a, remote_repo_b))
615
 
 
616
 
 
617
414
class TestRepositoryConverter(TestCaseWithTransport):
618
415
 
619
416
    def test_convert_empty(self):
620
417
        t = get_transport(self.get_url('.'))
621
418
        t.mkdir('repository')
622
419
        repo_dir = bzrdir.BzrDirMetaFormat1().initialize('repository')
623
 
        repo = weaverepo.RepositoryFormat7().initialize(repo_dir)
624
 
        target_format = knitrepo.RepositoryFormatKnit1()
 
420
        repo = repository.RepositoryFormat7().initialize(repo_dir)
 
421
        target_format = repository.RepositoryFormatKnit1()
625
422
        converter = repository.CopyConverter(target_format)
626
423
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
627
424
        try:
630
427
            pb.finished()
631
428
        repo = repo_dir.open_repository()
632
429
        self.assertTrue(isinstance(target_format, repo._format.__class__))
633
 
 
634
 
 
635
 
class TestMisc(TestCase):
636
 
    
637
 
    def test_unescape_xml(self):
638
 
        """We get some kind of error when malformed entities are passed"""
639
 
        self.assertRaises(KeyError, repository._unescape_xml, 'foo&bar;') 
640
 
 
641
 
 
642
 
class TestRepositoryFormatKnit3(TestCaseWithTransport):
643
 
 
644
 
    def test_convert(self):
645
 
        """Ensure the upgrade adds weaves for roots"""
646
 
        format = bzrdir.BzrDirMetaFormat1()
647
 
        format.repository_format = knitrepo.RepositoryFormatKnit1()
648
 
        tree = self.make_branch_and_tree('.', format)
649
 
        tree.commit("Dull commit", rev_id="dull")
650
 
        revision_tree = tree.branch.repository.revision_tree('dull')
651
 
        self.assertRaises(errors.NoSuchFile, revision_tree.get_file_lines,
652
 
            revision_tree.inventory.root.file_id)
653
 
        format = bzrdir.BzrDirMetaFormat1()
654
 
        format.repository_format = knitrepo.RepositoryFormatKnit3()
655
 
        upgrade.Convert('.', format)
656
 
        tree = workingtree.WorkingTree.open('.')
657
 
        revision_tree = tree.branch.repository.revision_tree('dull')
658
 
        revision_tree.get_file_lines(revision_tree.inventory.root.file_id)
659
 
        tree.commit("Another dull commit", rev_id='dull2')
660
 
        revision_tree = tree.branch.repository.revision_tree('dull2')
661
 
        self.assertEqual('dull', revision_tree.inventory.root.revision)
662
 
 
663
 
    def test_exposed_versioned_files_are_marked_dirty(self):
664
 
        format = bzrdir.BzrDirMetaFormat1()
665
 
        format.repository_format = knitrepo.RepositoryFormatKnit3()
666
 
        repo = self.make_repository('.', format=format)
667
 
        repo.lock_write()
668
 
        inv = repo.get_inventory_weave()
669
 
        repo.unlock()
670
 
        self.assertRaises(errors.OutSideTransaction,
671
 
            inv.add_lines, 'foo', [], [])
672
 
 
673
 
 
674
 
class TestWithBrokenRepo(TestCaseWithTransport):
675
 
    """These tests seem to be more appropriate as interface tests?"""
676
 
 
677
 
    def make_broken_repository(self):
678
 
        # XXX: This function is borrowed from Aaron's "Reconcile can fix bad
679
 
        # parent references" branch which is due to land in bzr.dev soon.  Once
680
 
        # it does, this duplication should be removed.
681
 
        repo = self.make_repository('broken-repo')
682
 
        cleanups = []
683
 
        try:
684
 
            repo.lock_write()
685
 
            cleanups.append(repo.unlock)
686
 
            repo.start_write_group()
687
 
            cleanups.append(repo.commit_write_group)
688
 
            # make rev1a: A well-formed revision, containing 'file1'
689
 
            inv = inventory.Inventory(revision_id='rev1a')
690
 
            inv.root.revision = 'rev1a'
691
 
            self.add_file(repo, inv, 'file1', 'rev1a', [])
692
 
            repo.add_inventory('rev1a', inv, [])
693
 
            revision = _mod_revision.Revision('rev1a',
694
 
                committer='jrandom@example.com', timestamp=0,
695
 
                inventory_sha1='', timezone=0, message='foo', parent_ids=[])
696
 
            repo.add_revision('rev1a',revision, inv)
697
 
 
698
 
            # make rev1b, which has no Revision, but has an Inventory, and
699
 
            # file1
700
 
            inv = inventory.Inventory(revision_id='rev1b')
701
 
            inv.root.revision = 'rev1b'
702
 
            self.add_file(repo, inv, 'file1', 'rev1b', [])
703
 
            repo.add_inventory('rev1b', inv, [])
704
 
 
705
 
            # make rev2, with file1 and file2
706
 
            # file2 is sane
707
 
            # file1 has 'rev1b' as an ancestor, even though this is not
708
 
            # mentioned by 'rev1a', making it an unreferenced ancestor
709
 
            inv = inventory.Inventory()
710
 
            self.add_file(repo, inv, 'file1', 'rev2', ['rev1a', 'rev1b'])
711
 
            self.add_file(repo, inv, 'file2', 'rev2', [])
712
 
            self.add_revision(repo, 'rev2', inv, ['rev1a'])
713
 
 
714
 
            # make ghost revision rev1c
715
 
            inv = inventory.Inventory()
716
 
            self.add_file(repo, inv, 'file2', 'rev1c', [])
717
 
 
718
 
            # make rev3 with file2
719
 
            # file2 refers to 'rev1c', which is a ghost in this repository, so
720
 
            # file2 cannot have rev1c as its ancestor.
721
 
            inv = inventory.Inventory()
722
 
            self.add_file(repo, inv, 'file2', 'rev3', ['rev1c'])
723
 
            self.add_revision(repo, 'rev3', inv, ['rev1c'])
724
 
            return repo
725
 
        finally:
726
 
            for cleanup in reversed(cleanups):
727
 
                cleanup()
728
 
 
729
 
    def add_revision(self, repo, revision_id, inv, parent_ids):
730
 
        inv.revision_id = revision_id
731
 
        inv.root.revision = revision_id
732
 
        repo.add_inventory(revision_id, inv, parent_ids)
733
 
        revision = _mod_revision.Revision(revision_id,
734
 
            committer='jrandom@example.com', timestamp=0, inventory_sha1='',
735
 
            timezone=0, message='foo', parent_ids=parent_ids)
736
 
        repo.add_revision(revision_id,revision, inv)
737
 
 
738
 
    def add_file(self, repo, inv, filename, revision, parents):
739
 
        file_id = filename + '-id'
740
 
        entry = inventory.InventoryFile(file_id, filename, 'TREE_ROOT')
741
 
        entry.revision = revision
742
 
        entry.text_size = 0
743
 
        inv.add(entry)
744
 
        vf = repo.weave_store.get_weave_or_empty(file_id,
745
 
                                                 repo.get_transaction())
746
 
        vf.add_lines(revision, parents, ['line\n'])
747
 
 
748
 
    def test_insert_from_broken_repo(self):
749
 
        """Inserting a data stream from a broken repository won't silently
750
 
        corrupt the target repository.
751
 
        """
752
 
        broken_repo = self.make_broken_repository()
753
 
        empty_repo = self.make_repository('empty-repo')
754
 
        stream = broken_repo.get_data_stream(['rev1a', 'rev2', 'rev3'])
755
 
        empty_repo.lock_write()
756
 
        self.addCleanup(empty_repo.unlock)
757
 
        empty_repo.start_write_group()
758
 
        try:
759
 
            self.assertRaises(
760
 
                errors.KnitCorrupt, empty_repo.insert_data_stream, stream)
761
 
        finally:
762
 
            empty_repo.abort_write_group()
763
 
 
764
 
 
765
 
class TestExperimentalNoSubtrees(TestCaseWithTransport):
766
 
 
767
 
    def get_format(self):
768
 
        return bzrdir.format_registry.make_bzrdir('experimental')
769
 
 
770
 
    def test_disk_layout(self):
771
 
        format = self.get_format()
772
 
        repo = self.make_repository('.', format=format)
773
 
        # in case of side effects of locking.
774
 
        repo.lock_write()
775
 
        repo.unlock()
776
 
        t = repo.bzrdir.get_repository_transport(None)
777
 
        self.check_format(t)
778
 
        # XXX: no locks left when unlocked at the moment
779
 
        # self.assertEqualDiff('', t.get('lock').read())
780
 
        self.check_databases(t)
781
 
 
782
 
    def check_format(self, t):
783
 
        self.assertEqualDiff('Bazaar Experimental no-subtrees\n',
784
 
                             t.get('format').read())
785
 
 
786
 
    def assertHasKndx(self, t, knit_name):
787
 
        """Assert that knit_name exists on t."""
788
 
        self.assertEqualDiff('# bzr knit index 8\n',
789
 
                             t.get(knit_name + '.kndx').read())
790
 
 
791
 
    def assertHasNoKndx(self, t, knit_name):
792
 
        """Assert that knit_name has no index on t."""
793
 
        self.assertFalse(t.has(knit_name + '.kndx'))
794
 
 
795
 
    def assertHasNoKnit(self, t, knit_name):
796
 
        """Assert that knit_name exists on t."""
797
 
        # no default content
798
 
        self.assertFalse(t.has(knit_name + '.knit'))
799
 
 
800
 
    def check_databases(self, t):
801
 
        """check knit content for a repository."""
802
 
        # check conversion worked
803
 
        self.assertHasNoKndx(t, 'inventory')
804
 
        self.assertHasNoKnit(t, 'inventory')
805
 
        self.assertHasNoKndx(t, 'revisions')
806
 
        self.assertHasNoKnit(t, 'revisions')
807
 
        self.assertHasNoKndx(t, 'signatures')
808
 
        self.assertHasNoKnit(t, 'signatures')
809
 
        self.assertFalse(t.has('knits'))
810
 
        # revision-indexes file-container directory
811
 
        self.assertEqual([],
812
 
            list(GraphIndex(t, 'pack-names', None).iter_all_entries()))
813
 
        self.assertTrue(S_ISDIR(t.stat('packs').st_mode))
814
 
        self.assertTrue(S_ISDIR(t.stat('upload').st_mode))
815
 
        self.assertTrue(S_ISDIR(t.stat('indices').st_mode))
816
 
        self.assertTrue(S_ISDIR(t.stat('obsolete_packs').st_mode))
817
 
 
818
 
    def test_shared_disk_layout(self):
819
 
        format = self.get_format()
820
 
        repo = self.make_repository('.', shared=True, format=format)
821
 
        # we want:
822
 
        t = repo.bzrdir.get_repository_transport(None)
823
 
        self.check_format(t)
824
 
        # XXX: no locks left when unlocked at the moment
825
 
        # self.assertEqualDiff('', t.get('lock').read())
826
 
        # We should have a 'shared-storage' marker file.
827
 
        self.assertEqualDiff('', t.get('shared-storage').read())
828
 
        self.check_databases(t)
829
 
 
830
 
    def test_shared_no_tree_disk_layout(self):
831
 
        format = self.get_format()
832
 
        repo = self.make_repository('.', shared=True, format=format)
833
 
        repo.set_make_working_trees(False)
834
 
        # we want:
835
 
        t = repo.bzrdir.get_repository_transport(None)
836
 
        self.check_format(t)
837
 
        # XXX: no locks left when unlocked at the moment
838
 
        # self.assertEqualDiff('', t.get('lock').read())
839
 
        # We should have a 'shared-storage' marker file.
840
 
        self.assertEqualDiff('', t.get('shared-storage').read())
841
 
        # We should have a marker for the no-working-trees flag.
842
 
        self.assertEqualDiff('', t.get('no-working-trees').read())
843
 
        # The marker should go when we toggle the setting.
844
 
        repo.set_make_working_trees(True)
845
 
        self.assertFalse(t.has('no-working-trees'))
846
 
        self.check_databases(t)
847
 
 
848
 
    def test_adding_revision_creates_pack_indices(self):
849
 
        format = self.get_format()
850
 
        tree = self.make_branch_and_tree('.', format=format)
851
 
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
852
 
        self.assertEqual([],
853
 
            list(GraphIndex(trans, 'pack-names', None).iter_all_entries()))
854
 
        tree.commit('foobarbaz')
855
 
        index = GraphIndex(trans, 'pack-names', None)
856
 
        index_nodes = list(index.iter_all_entries())
857
 
        self.assertEqual(1, len(index_nodes))
858
 
        node = index_nodes[0]
859
 
        name = node[1][0]
860
 
        # the pack sizes should be listed in the index
861
 
        pack_value = node[2]
862
 
        sizes = [int(digits) for digits in pack_value.split(' ')]
863
 
        for size, suffix in zip(sizes, ['.rix', '.iix', '.tix', '.six']):
864
 
            stat = trans.stat('indices/%s%s' % (name, suffix))
865
 
            self.assertEqual(size, stat.st_size)
866
 
 
867
 
    def test_pulling_nothing_leads_to_no_new_names(self):
868
 
        format = self.get_format()
869
 
        tree1 = self.make_branch_and_tree('1', format=format)
870
 
        tree2 = self.make_branch_and_tree('2', format=format)
871
 
        tree1.branch.repository.fetch(tree2.branch.repository)
872
 
        trans = tree1.branch.repository.bzrdir.get_repository_transport(None)
873
 
        self.assertEqual([],
874
 
            list(GraphIndex(trans, 'pack-names', None).iter_all_entries()))
875
 
 
876
 
    def test_commit_across_pack_shape_boundary_autopacks(self):
877
 
        format = self.get_format()
878
 
        tree = self.make_branch_and_tree('.', format=format)
879
 
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
880
 
        # This test could be a little cheaper by replacing the packs
881
 
        # attribute on the repository to allow a different pack distribution
882
 
        # and max packs policy - so we are checking the policy is honoured
883
 
        # in the test. But for now 11 commits is not a big deal in a single
884
 
        # test.
885
 
        for x in range(9):
886
 
            tree.commit('commit %s' % x)
887
 
        # there should be 9 packs:
888
 
        index = GraphIndex(trans, 'pack-names', None)
889
 
        self.assertEqual(9, len(list(index.iter_all_entries())))
890
 
        # committing one more should coalesce to 1 of 10.
891
 
        tree.commit('commit triggering pack')
892
 
        index = GraphIndex(trans, 'pack-names', None)
893
 
        self.assertEqual(1, len(list(index.iter_all_entries())))
894
 
        # packing should not damage data
895
 
        tree = tree.bzrdir.open_workingtree()
896
 
        check_result = tree.branch.repository.check(
897
 
            [tree.branch.last_revision()])
898
 
        # XXX: Todo check packs obsoleted correctly - old packs and indices
899
 
        # in the obsolete_packs directory.
900
 
        large_pack_name = list(index.iter_all_entries())[0][1][0]
901
 
        # finally, committing again should not touch the large pack.
902
 
        tree.commit('commit not triggering pack')
903
 
        index = GraphIndex(trans, 'pack-names', None)
904
 
        self.assertEqual(2, len(list(index.iter_all_entries())))
905
 
        pack_names = [node[1][0] for node in index.iter_all_entries()]
906
 
        self.assertTrue(large_pack_name in pack_names)
907
 
 
908
 
    def test_pack_after_two_commits_packs_everything(self):
909
 
        format = self.get_format()
910
 
        tree = self.make_branch_and_tree('.', format=format)
911
 
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
912
 
        tree.commit('start')
913
 
        tree.commit('more work')
914
 
        tree.branch.repository.pack()
915
 
        # there should be 1 pack:
916
 
        index = GraphIndex(trans, 'pack-names', None)
917
 
        self.assertEqual(1, len(list(index.iter_all_entries())))
918
 
        self.assertEqual(2, len(tree.branch.repository.all_revision_ids()))
919
 
 
920
 
    def test_pack_repositories_support_multiple_write_locks(self):
921
 
        format = self.get_format()
922
 
        self.make_repository('.', shared=True, format=format)
923
 
        r1 = repository.Repository.open('.')
924
 
        r2 = repository.Repository.open('.')
925
 
        r1.lock_write()
926
 
        self.addCleanup(r1.unlock)
927
 
        r2.lock_write()
928
 
        r2.unlock()
929
 
 
930
 
    def _add_text(self, repo, fileid):
931
 
        """Add a text to the repository within a write group."""
932
 
        vf =repo.weave_store.get_weave(fileid, repo.get_transaction())
933
 
        vf.add_lines('samplerev+' + fileid, [], [])
934
 
 
935
 
    def test_concurrent_writers_merge_new_packs(self):
936
 
        format = self.get_format()
937
 
        self.make_repository('.', shared=True, format=format)
938
 
        r1 = repository.Repository.open('.')
939
 
        r2 = repository.Repository.open('.')
940
 
        r1.lock_write()
941
 
        try:
942
 
            # access enough data to load the names list
943
 
            list(r1.all_revision_ids())
944
 
            r2.lock_write()
945
 
            try:
946
 
                # access enough data to load the names list
947
 
                list(r2.all_revision_ids())
948
 
                r1.start_write_group()
949
 
                try:
950
 
                    r2.start_write_group()
951
 
                    try:
952
 
                        self._add_text(r1, 'fileidr1')
953
 
                        self._add_text(r2, 'fileidr2')
954
 
                    except:
955
 
                        r2.abort_write_group()
956
 
                        raise
957
 
                except:
958
 
                    r1.abort_write_group()
959
 
                    raise
960
 
                # both r1 and r2 have open write groups with data in them
961
 
                # created while the other's write group was open.
962
 
                # Commit both which requires a merge to the pack-names.
963
 
                try:
964
 
                    r1.commit_write_group()
965
 
                except:
966
 
                    r1.abort_write_group()
967
 
                    r2.abort_write_group()
968
 
                    raise
969
 
                r2.commit_write_group()
970
 
                # tell r1 to reload from disk
971
 
                r1._pack_collection.reset()
972
 
                # Now both repositories should know about both names
973
 
                r1._pack_collection.ensure_loaded()
974
 
                r2._pack_collection.ensure_loaded()
975
 
                self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
976
 
                self.assertEqual(2, len(r1._pack_collection.names()))
977
 
            finally:
978
 
                r1.unlock()
979
 
        finally:
980
 
            r2.unlock()
981
 
 
982
 
    def test_concurrent_writer_second_preserves_dropping_a_pack(self):
983
 
        format = self.get_format()
984
 
        self.make_repository('.', shared=True, format=format)
985
 
        r1 = repository.Repository.open('.')
986
 
        r2 = repository.Repository.open('.')
987
 
        # add a pack to drop
988
 
        r1.lock_write()
989
 
        try:
990
 
            r1.start_write_group()
991
 
            try:
992
 
                self._add_text(r1, 'fileidr1')
993
 
            except:
994
 
                r1.abort_write_group()
995
 
                raise
996
 
            else:
997
 
                r1.commit_write_group()
998
 
            r1._pack_collection.ensure_loaded()
999
 
            name_to_drop = r1._pack_collection.all_packs()[0].name
1000
 
        finally:
1001
 
            r1.unlock()
1002
 
        r1.lock_write()
1003
 
        try:
1004
 
            # access enough data to load the names list
1005
 
            list(r1.all_revision_ids())
1006
 
            r2.lock_write()
1007
 
            try:
1008
 
                # access enough data to load the names list
1009
 
                list(r2.all_revision_ids())
1010
 
                r1._pack_collection.ensure_loaded()
1011
 
                try:
1012
 
                    r2.start_write_group()
1013
 
                    try:
1014
 
                        # in r1, drop the pack
1015
 
                        r1._pack_collection._remove_pack_from_memory(
1016
 
                            r1._pack_collection.get_pack_by_name(name_to_drop))
1017
 
                        # in r2, add a pack
1018
 
                        self._add_text(r2, 'fileidr2')
1019
 
                    except:
1020
 
                        r2.abort_write_group()
1021
 
                        raise
1022
 
                except:
1023
 
                    r1._pack_collection.reset()
1024
 
                    raise
1025
 
                # r1 has a changed names list, and r2 an open write groups with
1026
 
                # changes.
1027
 
                # save r1, and then commit the r2 write group, which requires a
1028
 
                # merge to the pack-names, which should not reinstate
1029
 
                # name_to_drop
1030
 
                try:
1031
 
                    r1._pack_collection._save_pack_names()
1032
 
                    r1._pack_collection.reset()
1033
 
                except:
1034
 
                    r2.abort_write_group()
1035
 
                    raise
1036
 
                try:
1037
 
                    r2.commit_write_group()
1038
 
                except:
1039
 
                    r2.abort_write_group()
1040
 
                    raise
1041
 
                # Now both repositories should now about just one name.
1042
 
                r1._pack_collection.ensure_loaded()
1043
 
                r2._pack_collection.ensure_loaded()
1044
 
                self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
1045
 
                self.assertEqual(1, len(r1._pack_collection.names()))
1046
 
                self.assertFalse(name_to_drop in r1._pack_collection.names())
1047
 
            finally:
1048
 
                r1.unlock()
1049
 
        finally:
1050
 
            r2.unlock()
1051
 
 
1052
 
    def test_lock_write_does_not_physically_lock(self):
1053
 
        repo = self.make_repository('.', format=self.get_format())
1054
 
        repo.lock_write()
1055
 
        self.addCleanup(repo.unlock)
1056
 
        self.assertFalse(repo.get_physical_lock_status())
1057
 
 
1058
 
    def prepare_for_break_lock(self):
1059
 
        # Setup the global ui factory state so that a break-lock method call
1060
 
        # will find usable input in the input stream.
1061
 
        old_factory = bzrlib.ui.ui_factory
1062
 
        def restoreFactory():
1063
 
            bzrlib.ui.ui_factory = old_factory
1064
 
        self.addCleanup(restoreFactory)
1065
 
        bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
1066
 
        bzrlib.ui.ui_factory.stdin = StringIO("y\n")
1067
 
 
1068
 
    def test_break_lock_breaks_physical_lock(self):
1069
 
        repo = self.make_repository('.', format=self.get_format())
1070
 
        repo._pack_collection.lock_names()
1071
 
        repo2 = repository.Repository.open('.')
1072
 
        self.assertTrue(repo.get_physical_lock_status())
1073
 
        self.prepare_for_break_lock()
1074
 
        repo2.break_lock()
1075
 
        self.assertFalse(repo.get_physical_lock_status())
1076
 
 
1077
 
    def test_broken_physical_locks_error_on__unlock_names_lock(self):
1078
 
        repo = self.make_repository('.', format=self.get_format())
1079
 
        repo._pack_collection.lock_names()
1080
 
        self.assertTrue(repo.get_physical_lock_status())
1081
 
        repo2 = repository.Repository.open('.')
1082
 
        self.prepare_for_break_lock()
1083
 
        repo2.break_lock()
1084
 
        self.assertRaises(errors.LockBroken, repo._pack_collection._unlock_names)
1085
 
 
1086
 
 
1087
 
class TestExperimentalSubtrees(TestExperimentalNoSubtrees):
1088
 
 
1089
 
    def get_format(self):
1090
 
        return bzrdir.format_registry.make_bzrdir('experimental-subtree')
1091
 
 
1092
 
    def check_format(self, t):
1093
 
        self.assertEqualDiff('Bazaar Experimental subtrees\n',
1094
 
                             t.get('format').read())
1095
 
 
1096
 
 
1097
 
class TestRepositoryPackCollection(TestCaseWithTransport):
1098
 
 
1099
 
    def get_format(self):
1100
 
        return bzrdir.format_registry.make_bzrdir('experimental')
1101
 
 
1102
 
    def test__max_pack_count(self):
1103
 
        """The maximum pack count is a function of the number of revisions."""
1104
 
        format = self.get_format()
1105
 
        repo = self.make_repository('.', format=format)
1106
 
        packs = repo._pack_collection
1107
 
        # no revisions - one pack, so that we can have a revision free repo
1108
 
        # without it blowing up
1109
 
        self.assertEqual(1, packs._max_pack_count(0))
1110
 
        # after that the sum of the digits, - check the first 1-9
1111
 
        self.assertEqual(1, packs._max_pack_count(1))
1112
 
        self.assertEqual(2, packs._max_pack_count(2))
1113
 
        self.assertEqual(3, packs._max_pack_count(3))
1114
 
        self.assertEqual(4, packs._max_pack_count(4))
1115
 
        self.assertEqual(5, packs._max_pack_count(5))
1116
 
        self.assertEqual(6, packs._max_pack_count(6))
1117
 
        self.assertEqual(7, packs._max_pack_count(7))
1118
 
        self.assertEqual(8, packs._max_pack_count(8))
1119
 
        self.assertEqual(9, packs._max_pack_count(9))
1120
 
        # check the boundary cases with two digits for the next decade
1121
 
        self.assertEqual(1, packs._max_pack_count(10))
1122
 
        self.assertEqual(2, packs._max_pack_count(11))
1123
 
        self.assertEqual(10, packs._max_pack_count(19))
1124
 
        self.assertEqual(2, packs._max_pack_count(20))
1125
 
        self.assertEqual(3, packs._max_pack_count(21))
1126
 
        # check some arbitrary big numbers
1127
 
        self.assertEqual(25, packs._max_pack_count(112894))
1128
 
 
1129
 
    def test_pack_distribution_zero(self):
1130
 
        format = self.get_format()
1131
 
        repo = self.make_repository('.', format=format)
1132
 
        packs = repo._pack_collection
1133
 
        self.assertEqual([0], packs.pack_distribution(0))
1134
 
        
1135
 
    def test_pack_distribution_one_to_nine(self):
1136
 
        format = self.get_format()
1137
 
        repo = self.make_repository('.', format=format)
1138
 
        packs = repo._pack_collection
1139
 
        self.assertEqual([1],
1140
 
            packs.pack_distribution(1))
1141
 
        self.assertEqual([1, 1],
1142
 
            packs.pack_distribution(2))
1143
 
        self.assertEqual([1, 1, 1],
1144
 
            packs.pack_distribution(3))
1145
 
        self.assertEqual([1, 1, 1, 1],
1146
 
            packs.pack_distribution(4))
1147
 
        self.assertEqual([1, 1, 1, 1, 1],
1148
 
            packs.pack_distribution(5))
1149
 
        self.assertEqual([1, 1, 1, 1, 1, 1],
1150
 
            packs.pack_distribution(6))
1151
 
        self.assertEqual([1, 1, 1, 1, 1, 1, 1],
1152
 
            packs.pack_distribution(7))
1153
 
        self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1],
1154
 
            packs.pack_distribution(8))
1155
 
        self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1, 1],
1156
 
            packs.pack_distribution(9))
1157
 
 
1158
 
    def test_pack_distribution_stable_at_boundaries(self):
1159
 
        """When there are multi-rev packs the counts are stable."""
1160
 
        format = self.get_format()
1161
 
        repo = self.make_repository('.', format=format)
1162
 
        packs = repo._pack_collection
1163
 
        # in 10s:
1164
 
        self.assertEqual([10], packs.pack_distribution(10))
1165
 
        self.assertEqual([10, 1], packs.pack_distribution(11))
1166
 
        self.assertEqual([10, 10], packs.pack_distribution(20))
1167
 
        self.assertEqual([10, 10, 1], packs.pack_distribution(21))
1168
 
        # 100s
1169
 
        self.assertEqual([100], packs.pack_distribution(100))
1170
 
        self.assertEqual([100, 1], packs.pack_distribution(101))
1171
 
        self.assertEqual([100, 10, 1], packs.pack_distribution(111))
1172
 
        self.assertEqual([100, 100], packs.pack_distribution(200))
1173
 
        self.assertEqual([100, 100, 1], packs.pack_distribution(201))
1174
 
        self.assertEqual([100, 100, 10, 1], packs.pack_distribution(211))
1175
 
 
1176
 
    def test_plan_pack_operations_2009_revisions_skip_all_packs(self):
1177
 
        format = self.get_format()
1178
 
        repo = self.make_repository('.', format=format)
1179
 
        packs = repo._pack_collection
1180
 
        existing_packs = [(2000, "big"), (9, "medium")]
1181
 
        # rev count - 2009 -> 2x1000 + 9x1
1182
 
        pack_operations = packs.plan_autopack_combinations(
1183
 
            existing_packs, [1000, 1000, 1, 1, 1, 1, 1, 1, 1, 1, 1])
1184
 
        self.assertEqual([], pack_operations)
1185
 
 
1186
 
    def test_plan_pack_operations_2010_revisions_skip_all_packs(self):
1187
 
        format = self.get_format()
1188
 
        repo = self.make_repository('.', format=format)
1189
 
        packs = repo._pack_collection
1190
 
        existing_packs = [(2000, "big"), (9, "medium"), (1, "single")]
1191
 
        # rev count - 2010 -> 2x1000 + 1x10
1192
 
        pack_operations = packs.plan_autopack_combinations(
1193
 
            existing_packs, [1000, 1000, 10])
1194
 
        self.assertEqual([], pack_operations)
1195
 
 
1196
 
    def test_plan_pack_operations_2010_combines_smallest_two(self):
1197
 
        format = self.get_format()
1198
 
        repo = self.make_repository('.', format=format)
1199
 
        packs = repo._pack_collection
1200
 
        existing_packs = [(1999, "big"), (9, "medium"), (1, "single2"),
1201
 
            (1, "single1")]
1202
 
        # rev count - 2010 -> 2x1000 + 1x10 (3)
1203
 
        pack_operations = packs.plan_autopack_combinations(
1204
 
            existing_packs, [1000, 1000, 10])
1205
 
        self.assertEqual([[2, ["single2", "single1"]], [0, []]], pack_operations)
1206
 
 
1207
 
    def test_all_packs_none(self):
1208
 
        format = self.get_format()
1209
 
        tree = self.make_branch_and_tree('.', format=format)
1210
 
        tree.lock_read()
1211
 
        self.addCleanup(tree.unlock)
1212
 
        packs = tree.branch.repository._pack_collection
1213
 
        packs.ensure_loaded()
1214
 
        self.assertEqual([], packs.all_packs())
1215
 
 
1216
 
    def test_all_packs_one(self):
1217
 
        format = self.get_format()
1218
 
        tree = self.make_branch_and_tree('.', format=format)
1219
 
        tree.commit('start')
1220
 
        tree.lock_read()
1221
 
        self.addCleanup(tree.unlock)
1222
 
        packs = tree.branch.repository._pack_collection
1223
 
        packs.ensure_loaded()
1224
 
        self.assertEqual([
1225
 
            packs.get_pack_by_name(packs.names()[0])],
1226
 
            packs.all_packs())
1227
 
 
1228
 
    def test_all_packs_two(self):
1229
 
        format = self.get_format()
1230
 
        tree = self.make_branch_and_tree('.', format=format)
1231
 
        tree.commit('start')
1232
 
        tree.commit('continue')
1233
 
        tree.lock_read()
1234
 
        self.addCleanup(tree.unlock)
1235
 
        packs = tree.branch.repository._pack_collection
1236
 
        packs.ensure_loaded()
1237
 
        self.assertEqual([
1238
 
            packs.get_pack_by_name(packs.names()[0]),
1239
 
            packs.get_pack_by_name(packs.names()[1]),
1240
 
            ], packs.all_packs())
1241
 
 
1242
 
    def test_get_pack_by_name(self):
1243
 
        format = self.get_format()
1244
 
        tree = self.make_branch_and_tree('.', format=format)
1245
 
        tree.commit('start')
1246
 
        tree.lock_read()
1247
 
        self.addCleanup(tree.unlock)
1248
 
        packs = tree.branch.repository._pack_collection
1249
 
        packs.ensure_loaded()
1250
 
        name = packs.names()[0]
1251
 
        pack_1 = packs.get_pack_by_name(name)
1252
 
        # the pack should be correctly initialised
1253
 
        rev_index = GraphIndex(packs._index_transport, name + '.rix',
1254
 
            packs._names[name][0])
1255
 
        inv_index = GraphIndex(packs._index_transport, name + '.iix',
1256
 
            packs._names[name][1])
1257
 
        txt_index = GraphIndex(packs._index_transport, name + '.tix',
1258
 
            packs._names[name][2])
1259
 
        sig_index = GraphIndex(packs._index_transport, name + '.six',
1260
 
            packs._names[name][3])
1261
 
        self.assertEqual(pack_repo.ExistingPack(packs._pack_transport,
1262
 
            name, rev_index, inv_index, txt_index, sig_index), pack_1)
1263
 
        # and the same instance should be returned on successive calls.
1264
 
        self.assertTrue(pack_1 is packs.get_pack_by_name(name))
1265
 
 
1266
 
 
1267
 
class TestPack(TestCaseWithTransport):
1268
 
    """Tests for the Pack object."""
1269
 
 
1270
 
    def assertCurrentlyEqual(self, left, right):
1271
 
        self.assertTrue(left == right)
1272
 
        self.assertTrue(right == left)
1273
 
        self.assertFalse(left != right)
1274
 
        self.assertFalse(right != left)
1275
 
 
1276
 
    def assertCurrentlyNotEqual(self, left, right):
1277
 
        self.assertFalse(left == right)
1278
 
        self.assertFalse(right == left)
1279
 
        self.assertTrue(left != right)
1280
 
        self.assertTrue(right != left)
1281
 
 
1282
 
    def test___eq____ne__(self):
1283
 
        left = pack_repo.ExistingPack('', '', '', '', '', '')
1284
 
        right = pack_repo.ExistingPack('', '', '', '', '', '')
1285
 
        self.assertCurrentlyEqual(left, right)
1286
 
        # change all attributes and ensure equality changes as we do.
1287
 
        left.revision_index = 'a'
1288
 
        self.assertCurrentlyNotEqual(left, right)
1289
 
        right.revision_index = 'a'
1290
 
        self.assertCurrentlyEqual(left, right)
1291
 
        left.inventory_index = 'a'
1292
 
        self.assertCurrentlyNotEqual(left, right)
1293
 
        right.inventory_index = 'a'
1294
 
        self.assertCurrentlyEqual(left, right)
1295
 
        left.text_index = 'a'
1296
 
        self.assertCurrentlyNotEqual(left, right)
1297
 
        right.text_index = 'a'
1298
 
        self.assertCurrentlyEqual(left, right)
1299
 
        left.signature_index = 'a'
1300
 
        self.assertCurrentlyNotEqual(left, right)
1301
 
        right.signature_index = 'a'
1302
 
        self.assertCurrentlyEqual(left, right)
1303
 
        left.name = 'a'
1304
 
        self.assertCurrentlyNotEqual(left, right)
1305
 
        right.name = 'a'
1306
 
        self.assertCurrentlyEqual(left, right)
1307
 
        left.transport = 'a'
1308
 
        self.assertCurrentlyNotEqual(left, right)
1309
 
        right.transport = 'a'
1310
 
        self.assertCurrentlyEqual(left, right)
1311
 
 
1312
 
    def test_file_name(self):
1313
 
        pack = pack_repo.ExistingPack('', 'a_name', '', '', '', '')
1314
 
        self.assertEqual('a_name.pack', pack.file_name())
1315
 
 
1316
 
 
1317
 
class TestNewPack(TestCaseWithTransport):
1318
 
    """Tests for pack_repo.NewPack."""
1319
 
 
1320
 
    def test_new_instance_attributes(self):
1321
 
        upload_transport = self.get_transport('upload')
1322
 
        pack_transport = self.get_transport('pack')
1323
 
        index_transport = self.get_transport('index')
1324
 
        upload_transport.mkdir('.')
1325
 
        pack = pack_repo.NewPack(upload_transport, index_transport,
1326
 
            pack_transport)
1327
 
        self.assertIsInstance(pack.revision_index, InMemoryGraphIndex)
1328
 
        self.assertIsInstance(pack.inventory_index, InMemoryGraphIndex)
1329
 
        self.assertIsInstance(pack._hash, type(md5.new()))
1330
 
        self.assertTrue(pack.upload_transport is upload_transport)
1331
 
        self.assertTrue(pack.index_transport is index_transport)
1332
 
        self.assertTrue(pack.pack_transport is pack_transport)
1333
 
        self.assertEqual(None, pack.index_sizes)
1334
 
        self.assertEqual(20, len(pack.random_name))
1335
 
        self.assertIsInstance(pack.random_name, str)
1336
 
        self.assertIsInstance(pack.start_time, float)