~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_repository.py

  • Committer: John Arbash Meinel
  • Date: 2006-05-06 17:54:19 UTC
  • mto: This revision was merged to the branch mainline in revision 1705.
  • Revision ID: john@arbash-meinel.com-20060506175419-fe86b1f2a9e88a53
Updated web page url to http://bazaar-vcs.org

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007, 2008 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
16
16
 
17
17
"""Tests for the Repository facility that are not interface tests.
18
18
 
19
 
For interface tests see tests/per_repository/*.py.
 
19
For interface tests see tests/repository_implementations/*.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 S_ISDIR
 
25
from stat import *
26
26
from StringIO import StringIO
27
27
 
28
28
import bzrlib
 
29
import bzrlib.bzrdir as bzrdir
 
30
import bzrlib.errors as errors
29
31
from bzrlib.errors import (NotBranchError,
30
32
                           NoSuchFile,
31
33
                           UnknownFormatError,
32
34
                           UnsupportedFormatError,
33
35
                           )
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
 
    )
 
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
49
40
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
64
41
 
65
42
 
66
43
class TestDefaultFormat(TestCase):
67
44
 
68
45
    def test_get_set_default_format(self):
69
 
        old_default = bzrdir.format_registry.get('default')
70
 
        private_default = old_default().repository_format.__class__
71
46
        old_format = repository.RepositoryFormat.get_default_format()
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')
 
47
        self.assertTrue(isinstance(old_format, repository.RepositoryFormatKnit1))
 
48
        repository.RepositoryFormat.set_default_format(SampleRepositoryFormat())
80
49
        # creating a repository should now create an instrumented dir.
81
50
        try:
82
51
            # the default branch format is used by the meta dir format
83
52
            # which is not the default bzrdir format at this point
84
 
            dir = bzrdir.BzrDirMetaFormat1().initialize('memory:///')
 
53
            dir = bzrdir.BzrDirMetaFormat1().initialize('memory:/')
85
54
            result = dir.create_repository()
86
55
            self.assertEqual(result, 'A bzr repository dir')
87
56
        finally:
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__)
 
57
            repository.RepositoryFormat.set_default_format(old_format)
 
58
        self.assertEqual(old_format, repository.RepositoryFormat.get_default_format())
93
59
 
94
60
 
95
61
class SampleRepositoryFormat(repository.RepositoryFormat):
106
72
    def initialize(self, a_bzrdir, shared=False):
107
73
        """Initialize a repository in a BzrDir"""
108
74
        t = a_bzrdir.get_repository_transport(self)
109
 
        t.put_bytes('format', self.get_format_string())
 
75
        t.put('format', StringIO(self.get_format_string()))
110
76
        return 'A bzr repository dir'
111
77
 
112
78
    def is_supported(self):
130
96
            t = get_transport(url)
131
97
            found_format = repository.RepositoryFormat.find_format(dir)
132
98
            self.failUnless(isinstance(found_format, format.__class__))
133
 
        check_format(weaverepo.RepositoryFormat7(), "bar")
 
99
        check_format(repository.RepositoryFormat7(), "bar")
134
100
        
135
101
    def test_find_format_no_repository(self):
136
102
        dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
163
129
 
164
130
class TestFormat6(TestCaseWithTransport):
165
131
 
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
 
 
184
132
    def test_no_ancestry_weave(self):
185
133
        control = bzrdir.BzrDirFormat6().initialize(self.get_url())
186
 
        repo = weaverepo.RepositoryFormat6().initialize(control)
 
134
        repo = repository.RepositoryFormat6().initialize(control)
187
135
        # We no longer need to create the ancestry.weave file
188
136
        # since it is *never* used.
189
137
        self.assertRaises(NoSuchFile,
190
138
                          control.transport.get,
191
139
                          'ancestry.weave')
192
140
 
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
 
 
198
141
 
199
142
class TestFormat7(TestCaseWithTransport):
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
 
 
 
143
    
219
144
    def test_disk_layout(self):
220
145
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
221
 
        repo = weaverepo.RepositoryFormat7().initialize(control)
 
146
        repo = repository.RepositoryFormat7().initialize(control)
222
147
        # in case of side effects of locking.
223
148
        repo.lock_write()
224
149
        repo.unlock()
237
162
                             'w\n'
238
163
                             'W\n',
239
164
                             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())
259
165
 
260
166
    def test_shared_disk_layout(self):
261
167
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
262
 
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
168
        repo = repository.RepositoryFormat7().initialize(control, shared=True)
263
169
        # we want:
264
170
        # format 'Bazaar-NG Repository format 7'
265
171
        # inventory.weave == empty_weave
282
188
    def test_creates_lockdir(self):
283
189
        """Make sure it appears to be controlled by a LockDir existence"""
284
190
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
285
 
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
191
        repo = repository.RepositoryFormat7().initialize(control, shared=True)
286
192
        t = control.get_repository_transport(None)
287
193
        # TODO: Should check there is a 'lock' toplevel directory, 
288
194
        # regardless of contents
298
204
        """repo format 7 actually locks on lockdir"""
299
205
        base_url = self.get_url()
300
206
        control = bzrdir.BzrDirMetaFormat1().initialize(base_url)
301
 
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
207
        repo = repository.RepositoryFormat7().initialize(control, shared=True)
302
208
        t = control.get_repository_transport(None)
303
209
        repo.lock_write()
304
210
        repo.unlock()
312
218
 
313
219
    def test_shared_no_tree_disk_layout(self):
314
220
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
315
 
        repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
 
221
        repo = repository.RepositoryFormat7().initialize(control, shared=True)
316
222
        repo.set_make_working_trees(False)
317
223
        # we want:
318
224
        # format 'Bazaar-NG Repository format 7'
336
242
                             'W\n',
337
243
                             t.get('inventory.weave').read())
338
244
 
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
 
 
344
245
 
345
246
class TestFormatKnit1(TestCaseWithTransport):
346
247
    
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
 
 
359
248
    def test_disk_layout(self):
360
249
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
361
 
        repo = knitrepo.RepositoryFormatKnit1().initialize(control)
 
250
        repo = repository.RepositoryFormatKnit1().initialize(control)
362
251
        # in case of side effects of locking.
363
252
        repo.lock_write()
364
253
        repo.unlock()
375
264
        # self.assertEqualDiff('', t.get('lock').read())
376
265
        self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
377
266
        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  :')
386
267
 
387
 
    def assertHasKnit(self, t, knit_name, extra_content=''):
 
268
    def assertHasKnit(self, t, knit_name):
388
269
        """Assert that knit_name exists on t."""
389
 
        self.assertEqualDiff('# bzr knit index 8\n' + extra_content,
 
270
        self.assertEqualDiff('# bzr knit index 8\n',
390
271
                             t.get(knit_name + '.kndx').read())
 
272
        # no default content
 
273
        self.assertTrue(t.has(knit_name + '.knit'))
391
274
 
392
275
    def check_knits(self, t):
393
276
        """check knit content for a repository."""
397
280
 
398
281
    def test_shared_disk_layout(self):
399
282
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
400
 
        repo = knitrepo.RepositoryFormatKnit1().initialize(control, shared=True)
 
283
        repo = repository.RepositoryFormatKnit1().initialize(control, shared=True)
401
284
        # we want:
402
285
        # format 'Bazaar-NG Knit Repository Format 1'
403
286
        # lock: is a directory
416
299
 
417
300
    def test_shared_no_tree_disk_layout(self):
418
301
        control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
419
 
        repo = knitrepo.RepositoryFormatKnit1().initialize(control, shared=True)
 
302
        repo = repository.RepositoryFormatKnit1().initialize(control, shared=True)
420
303
        repo.set_make_working_trees(False)
421
304
        # we want:
422
305
        # format 'Bazaar-NG Knit Repository Format 1'
437
320
        self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
438
321
        self.check_knits(t)
439
322
 
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
 
    _serializer = None
477
 
 
478
 
    def supports_rich_root(self):
479
 
        return False
480
 
 
481
 
 
482
 
class InterDummy(repository.InterRepository):
483
 
    """An inter-repository optimised code path for DummyRepository.
484
 
 
485
 
    This is for use during testing where we use DummyRepository as repositories
 
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
486
328
    so that none of the default regsitered inter-repository classes will
487
 
    MATCH.
 
329
    match.
488
330
    """
489
331
 
490
332
    @staticmethod
491
333
    def is_compatible(repo_source, repo_target):
492
 
        """InterDummy is compatible with DummyRepository."""
493
 
        return (isinstance(repo_source, DummyRepository) and 
494
 
            isinstance(repo_target, DummyRepository))
 
334
        """InterString is compatible with strings-as-repos."""
 
335
        return isinstance(repo_source, str) and isinstance(repo_target, str)
495
336
 
496
337
 
497
338
class TestInterRepository(TestCaseWithTransport):
503
344
        # This also tests that the default registered optimised interrepository
504
345
        # classes do not barf inappropriately when a surprising repository type
505
346
        # is handed to them.
506
 
        dummy_a = DummyRepository()
507
 
        dummy_b = DummyRepository()
 
347
        dummy_a = "Repository 1."
 
348
        dummy_b = "Repository 2."
508
349
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
509
350
 
510
351
    def assertGetsDefaultInterRepository(self, repo_a, repo_b):
511
 
        """Asserts that InterRepository.get(repo_a, repo_b) -> the default.
512
 
        
513
 
        The effective default is now InterSameDataRepository because there is
514
 
        no actual sane default in the presence of incompatible data models.
515
 
        """
 
352
        """Asserts that InterRepository.get(repo_a, repo_b) -> the default."""
516
353
        inter_repo = repository.InterRepository.get(repo_a, repo_b)
517
 
        self.assertEqual(repository.InterSameDataRepository,
 
354
        self.assertEqual(repository.InterRepository,
518
355
                         inter_repo.__class__)
519
356
        self.assertEqual(repo_a, inter_repo.source)
520
357
        self.assertEqual(repo_b, inter_repo.target)
525
362
        # and that it is correctly selected when given a repository
526
363
        # pair that it returns true on for the is_compatible static method
527
364
        # check
528
 
        dummy_a = DummyRepository()
529
 
        dummy_b = DummyRepository()
530
 
        repo = self.make_repository('.')
531
 
        # hack dummies to look like repo somewhat.
532
 
        dummy_a._serializer = repo._serializer
533
 
        dummy_b._serializer = repo._serializer
534
 
        repository.InterRepository.register_optimiser(InterDummy)
 
365
        dummy_a = "Repository 1."
 
366
        dummy_b = "Repository 2."
 
367
        repository.InterRepository.register_optimiser(InterString)
535
368
        try:
536
 
            # we should get the default for something InterDummy returns False
 
369
            # we should get the default for something InterString returns False
537
370
            # to
538
 
            self.assertFalse(InterDummy.is_compatible(dummy_a, repo))
539
 
            self.assertGetsDefaultInterRepository(dummy_a, repo)
540
 
            # and we should get an InterDummy for a pair it 'likes'
541
 
            self.assertTrue(InterDummy.is_compatible(dummy_a, dummy_b))
 
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))
542
375
            inter_repo = repository.InterRepository.get(dummy_a, dummy_b)
543
 
            self.assertEqual(InterDummy, inter_repo.__class__)
 
376
            self.assertEqual(InterString, inter_repo.__class__)
544
377
            self.assertEqual(dummy_a, inter_repo.source)
545
378
            self.assertEqual(dummy_b, inter_repo.target)
546
379
        finally:
547
 
            repository.InterRepository.unregister_optimiser(InterDummy)
 
380
            repository.InterRepository.unregister_optimiser(InterString)
548
381
        # now we should get the default InterRepository object again.
549
382
        self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
550
383
 
554
387
    def test_is_compatible_and_registered(self):
555
388
        # InterWeaveRepo is compatible when either side
556
389
        # is a format 5/6/7 branch
557
 
        from bzrlib.repofmt import knitrepo, weaverepo
558
 
        formats = [weaverepo.RepositoryFormat5(),
559
 
                   weaverepo.RepositoryFormat6(),
560
 
                   weaverepo.RepositoryFormat7()]
561
 
        incompatible_formats = [weaverepo.RepositoryFormat4(),
562
 
                                knitrepo.RepositoryFormatKnit1(),
 
390
        formats = [repository.RepositoryFormat5(),
 
391
                   repository.RepositoryFormat6(),
 
392
                   repository.RepositoryFormat7()]
 
393
        incompatible_formats = [repository.RepositoryFormat4(),
 
394
                                repository.RepositoryFormatKnit1(),
563
395
                                ]
564
396
        repo_a = self.make_repository('a')
565
397
        repo_b = self.make_repository('b')
586
418
        t = get_transport(self.get_url('.'))
587
419
        t.mkdir('repository')
588
420
        repo_dir = bzrdir.BzrDirMetaFormat1().initialize('repository')
589
 
        repo = weaverepo.RepositoryFormat7().initialize(repo_dir)
590
 
        target_format = knitrepo.RepositoryFormatKnit1()
 
421
        repo = repository.RepositoryFormat7().initialize(repo_dir)
 
422
        target_format = repository.RepositoryFormatKnit1()
591
423
        converter = repository.CopyConverter(target_format)
592
424
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
593
425
        try:
596
428
            pb.finished()
597
429
        repo = repo_dir.open_repository()
598
430
        self.assertTrue(isinstance(target_format, repo._format.__class__))
599
 
 
600
 
 
601
 
class TestMisc(TestCase):
602
 
    
603
 
    def test_unescape_xml(self):
604
 
        """We get some kind of error when malformed entities are passed"""
605
 
        self.assertRaises(KeyError, repository._unescape_xml, 'foo&bar;') 
606
 
 
607
 
 
608
 
class TestRepositoryFormatKnit3(TestCaseWithTransport):
609
 
 
610
 
    def test_attribute__fetch_order(self):
611
 
        """Knits need topological data insertion."""
612
 
        format = bzrdir.BzrDirMetaFormat1()
613
 
        format.repository_format = knitrepo.RepositoryFormatKnit3()
614
 
        repo = self.make_repository('.', format=format)
615
 
        self.assertEqual('topological', repo._fetch_order)
616
 
 
617
 
    def test_attribute__fetch_uses_deltas(self):
618
 
        """Knits reuse deltas."""
619
 
        format = bzrdir.BzrDirMetaFormat1()
620
 
        format.repository_format = knitrepo.RepositoryFormatKnit3()
621
 
        repo = self.make_repository('.', format=format)
622
 
        self.assertEqual(True, repo._fetch_uses_deltas)
623
 
 
624
 
    def test_convert(self):
625
 
        """Ensure the upgrade adds weaves for roots"""
626
 
        format = bzrdir.BzrDirMetaFormat1()
627
 
        format.repository_format = knitrepo.RepositoryFormatKnit1()
628
 
        tree = self.make_branch_and_tree('.', format)
629
 
        tree.commit("Dull commit", rev_id="dull")
630
 
        revision_tree = tree.branch.repository.revision_tree('dull')
631
 
        revision_tree.lock_read()
632
 
        try:
633
 
            self.assertRaises(errors.NoSuchFile, revision_tree.get_file_lines,
634
 
                revision_tree.inventory.root.file_id)
635
 
        finally:
636
 
            revision_tree.unlock()
637
 
        format = bzrdir.BzrDirMetaFormat1()
638
 
        format.repository_format = knitrepo.RepositoryFormatKnit3()
639
 
        upgrade.Convert('.', format)
640
 
        tree = workingtree.WorkingTree.open('.')
641
 
        revision_tree = tree.branch.repository.revision_tree('dull')
642
 
        revision_tree.lock_read()
643
 
        try:
644
 
            revision_tree.get_file_lines(revision_tree.inventory.root.file_id)
645
 
        finally:
646
 
            revision_tree.unlock()
647
 
        tree.commit("Another dull commit", rev_id='dull2')
648
 
        revision_tree = tree.branch.repository.revision_tree('dull2')
649
 
        revision_tree.lock_read()
650
 
        self.addCleanup(revision_tree.unlock)
651
 
        self.assertEqual('dull', revision_tree.inventory.root.revision)
652
 
 
653
 
    def test_supports_external_lookups(self):
654
 
        format = bzrdir.BzrDirMetaFormat1()
655
 
        format.repository_format = knitrepo.RepositoryFormatKnit3()
656
 
        repo = self.make_repository('.', format=format)
657
 
        self.assertFalse(repo._format.supports_external_lookups)
658
 
 
659
 
 
660
 
class TestWithBrokenRepo(TestCaseWithTransport):
661
 
    """These tests seem to be more appropriate as interface tests?"""
662
 
 
663
 
    def make_broken_repository(self):
664
 
        # XXX: This function is borrowed from Aaron's "Reconcile can fix bad
665
 
        # parent references" branch which is due to land in bzr.dev soon.  Once
666
 
        # it does, this duplication should be removed.
667
 
        repo = self.make_repository('broken-repo')
668
 
        cleanups = []
669
 
        try:
670
 
            repo.lock_write()
671
 
            cleanups.append(repo.unlock)
672
 
            repo.start_write_group()
673
 
            cleanups.append(repo.commit_write_group)
674
 
            # make rev1a: A well-formed revision, containing 'file1'
675
 
            inv = inventory.Inventory(revision_id='rev1a')
676
 
            inv.root.revision = 'rev1a'
677
 
            self.add_file(repo, inv, 'file1', 'rev1a', [])
678
 
            repo.add_inventory('rev1a', inv, [])
679
 
            revision = _mod_revision.Revision('rev1a',
680
 
                committer='jrandom@example.com', timestamp=0,
681
 
                inventory_sha1='', timezone=0, message='foo', parent_ids=[])
682
 
            repo.add_revision('rev1a',revision, inv)
683
 
 
684
 
            # make rev1b, which has no Revision, but has an Inventory, and
685
 
            # file1
686
 
            inv = inventory.Inventory(revision_id='rev1b')
687
 
            inv.root.revision = 'rev1b'
688
 
            self.add_file(repo, inv, 'file1', 'rev1b', [])
689
 
            repo.add_inventory('rev1b', inv, [])
690
 
 
691
 
            # make rev2, with file1 and file2
692
 
            # file2 is sane
693
 
            # file1 has 'rev1b' as an ancestor, even though this is not
694
 
            # mentioned by 'rev1a', making it an unreferenced ancestor
695
 
            inv = inventory.Inventory()
696
 
            self.add_file(repo, inv, 'file1', 'rev2', ['rev1a', 'rev1b'])
697
 
            self.add_file(repo, inv, 'file2', 'rev2', [])
698
 
            self.add_revision(repo, 'rev2', inv, ['rev1a'])
699
 
 
700
 
            # make ghost revision rev1c
701
 
            inv = inventory.Inventory()
702
 
            self.add_file(repo, inv, 'file2', 'rev1c', [])
703
 
 
704
 
            # make rev3 with file2
705
 
            # file2 refers to 'rev1c', which is a ghost in this repository, so
706
 
            # file2 cannot have rev1c as its ancestor.
707
 
            inv = inventory.Inventory()
708
 
            self.add_file(repo, inv, 'file2', 'rev3', ['rev1c'])
709
 
            self.add_revision(repo, 'rev3', inv, ['rev1c'])
710
 
            return repo
711
 
        finally:
712
 
            for cleanup in reversed(cleanups):
713
 
                cleanup()
714
 
 
715
 
    def add_revision(self, repo, revision_id, inv, parent_ids):
716
 
        inv.revision_id = revision_id
717
 
        inv.root.revision = revision_id
718
 
        repo.add_inventory(revision_id, inv, parent_ids)
719
 
        revision = _mod_revision.Revision(revision_id,
720
 
            committer='jrandom@example.com', timestamp=0, inventory_sha1='',
721
 
            timezone=0, message='foo', parent_ids=parent_ids)
722
 
        repo.add_revision(revision_id,revision, inv)
723
 
 
724
 
    def add_file(self, repo, inv, filename, revision, parents):
725
 
        file_id = filename + '-id'
726
 
        entry = inventory.InventoryFile(file_id, filename, 'TREE_ROOT')
727
 
        entry.revision = revision
728
 
        entry.text_size = 0
729
 
        inv.add(entry)
730
 
        text_key = (file_id, revision)
731
 
        parent_keys = [(file_id, parent) for parent in parents]
732
 
        repo.texts.add_lines(text_key, parent_keys, ['line\n'])
733
 
 
734
 
    def test_insert_from_broken_repo(self):
735
 
        """Inserting a data stream from a broken repository won't silently
736
 
        corrupt the target repository.
737
 
        """
738
 
        broken_repo = self.make_broken_repository()
739
 
        empty_repo = self.make_repository('empty-repo')
740
 
        self.assertRaises(errors.RevisionNotPresent, empty_repo.fetch, broken_repo)
741
 
 
742
 
 
743
 
class TestRepositoryPackCollection(TestCaseWithTransport):
744
 
 
745
 
    def get_format(self):
746
 
        return bzrdir.format_registry.make_bzrdir('pack-0.92')
747
 
 
748
 
    def get_packs(self):
749
 
        format = self.get_format()
750
 
        repo = self.make_repository('.', format=format)
751
 
        return repo._pack_collection
752
 
 
753
 
    def test__max_pack_count(self):
754
 
        """The maximum pack count is a function of the number of revisions."""
755
 
        # no revisions - one pack, so that we can have a revision free repo
756
 
        # without it blowing up
757
 
        packs = self.get_packs()
758
 
        self.assertEqual(1, packs._max_pack_count(0))
759
 
        # after that the sum of the digits, - check the first 1-9
760
 
        self.assertEqual(1, packs._max_pack_count(1))
761
 
        self.assertEqual(2, packs._max_pack_count(2))
762
 
        self.assertEqual(3, packs._max_pack_count(3))
763
 
        self.assertEqual(4, packs._max_pack_count(4))
764
 
        self.assertEqual(5, packs._max_pack_count(5))
765
 
        self.assertEqual(6, packs._max_pack_count(6))
766
 
        self.assertEqual(7, packs._max_pack_count(7))
767
 
        self.assertEqual(8, packs._max_pack_count(8))
768
 
        self.assertEqual(9, packs._max_pack_count(9))
769
 
        # check the boundary cases with two digits for the next decade
770
 
        self.assertEqual(1, packs._max_pack_count(10))
771
 
        self.assertEqual(2, packs._max_pack_count(11))
772
 
        self.assertEqual(10, packs._max_pack_count(19))
773
 
        self.assertEqual(2, packs._max_pack_count(20))
774
 
        self.assertEqual(3, packs._max_pack_count(21))
775
 
        # check some arbitrary big numbers
776
 
        self.assertEqual(25, packs._max_pack_count(112894))
777
 
 
778
 
    def test_pack_distribution_zero(self):
779
 
        packs = self.get_packs()
780
 
        self.assertEqual([0], packs.pack_distribution(0))
781
 
 
782
 
    def test_ensure_loaded_unlocked(self):
783
 
        packs = self.get_packs()
784
 
        self.assertRaises(errors.ObjectNotLocked,
785
 
                          packs.ensure_loaded)
786
 
 
787
 
    def test_pack_distribution_one_to_nine(self):
788
 
        packs = self.get_packs()
789
 
        self.assertEqual([1],
790
 
            packs.pack_distribution(1))
791
 
        self.assertEqual([1, 1],
792
 
            packs.pack_distribution(2))
793
 
        self.assertEqual([1, 1, 1],
794
 
            packs.pack_distribution(3))
795
 
        self.assertEqual([1, 1, 1, 1],
796
 
            packs.pack_distribution(4))
797
 
        self.assertEqual([1, 1, 1, 1, 1],
798
 
            packs.pack_distribution(5))
799
 
        self.assertEqual([1, 1, 1, 1, 1, 1],
800
 
            packs.pack_distribution(6))
801
 
        self.assertEqual([1, 1, 1, 1, 1, 1, 1],
802
 
            packs.pack_distribution(7))
803
 
        self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1],
804
 
            packs.pack_distribution(8))
805
 
        self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1, 1],
806
 
            packs.pack_distribution(9))
807
 
 
808
 
    def test_pack_distribution_stable_at_boundaries(self):
809
 
        """When there are multi-rev packs the counts are stable."""
810
 
        packs = self.get_packs()
811
 
        # in 10s:
812
 
        self.assertEqual([10], packs.pack_distribution(10))
813
 
        self.assertEqual([10, 1], packs.pack_distribution(11))
814
 
        self.assertEqual([10, 10], packs.pack_distribution(20))
815
 
        self.assertEqual([10, 10, 1], packs.pack_distribution(21))
816
 
        # 100s
817
 
        self.assertEqual([100], packs.pack_distribution(100))
818
 
        self.assertEqual([100, 1], packs.pack_distribution(101))
819
 
        self.assertEqual([100, 10, 1], packs.pack_distribution(111))
820
 
        self.assertEqual([100, 100], packs.pack_distribution(200))
821
 
        self.assertEqual([100, 100, 1], packs.pack_distribution(201))
822
 
        self.assertEqual([100, 100, 10, 1], packs.pack_distribution(211))
823
 
 
824
 
    def test_plan_pack_operations_2009_revisions_skip_all_packs(self):
825
 
        packs = self.get_packs()
826
 
        existing_packs = [(2000, "big"), (9, "medium")]
827
 
        # rev count - 2009 -> 2x1000 + 9x1
828
 
        pack_operations = packs.plan_autopack_combinations(
829
 
            existing_packs, [1000, 1000, 1, 1, 1, 1, 1, 1, 1, 1, 1])
830
 
        self.assertEqual([], pack_operations)
831
 
 
832
 
    def test_plan_pack_operations_2010_revisions_skip_all_packs(self):
833
 
        packs = self.get_packs()
834
 
        existing_packs = [(2000, "big"), (9, "medium"), (1, "single")]
835
 
        # rev count - 2010 -> 2x1000 + 1x10
836
 
        pack_operations = packs.plan_autopack_combinations(
837
 
            existing_packs, [1000, 1000, 10])
838
 
        self.assertEqual([], pack_operations)
839
 
 
840
 
    def test_plan_pack_operations_2010_combines_smallest_two(self):
841
 
        packs = self.get_packs()
842
 
        existing_packs = [(1999, "big"), (9, "medium"), (1, "single2"),
843
 
            (1, "single1")]
844
 
        # rev count - 2010 -> 2x1000 + 1x10 (3)
845
 
        pack_operations = packs.plan_autopack_combinations(
846
 
            existing_packs, [1000, 1000, 10])
847
 
        self.assertEqual([[2, ["single2", "single1"]]], pack_operations)
848
 
 
849
 
    def test_plan_pack_operations_creates_a_single_op(self):
850
 
        packs = self.get_packs()
851
 
        existing_packs = [(50, 'a'), (40, 'b'), (30, 'c'), (10, 'd'),
852
 
                          (10, 'e'), (6, 'f'), (4, 'g')]
853
 
        # rev count 150 -> 1x100 and 5x10
854
 
        # The two size 10 packs do not need to be touched. The 50, 40, 30 would
855
 
        # be combined into a single 120 size pack, and the 6 & 4 would
856
 
        # becombined into a size 10 pack. However, if we have to rewrite them,
857
 
        # we save a pack file with no increased I/O by putting them into the
858
 
        # same file.
859
 
        distribution = packs.pack_distribution(150)
860
 
        pack_operations = packs.plan_autopack_combinations(existing_packs,
861
 
                                                           distribution)
862
 
        self.assertEqual([[130, ['a', 'b', 'c', 'f', 'g']]], pack_operations)
863
 
 
864
 
    def test_all_packs_none(self):
865
 
        format = self.get_format()
866
 
        tree = self.make_branch_and_tree('.', format=format)
867
 
        tree.lock_read()
868
 
        self.addCleanup(tree.unlock)
869
 
        packs = tree.branch.repository._pack_collection
870
 
        packs.ensure_loaded()
871
 
        self.assertEqual([], packs.all_packs())
872
 
 
873
 
    def test_all_packs_one(self):
874
 
        format = self.get_format()
875
 
        tree = self.make_branch_and_tree('.', format=format)
876
 
        tree.commit('start')
877
 
        tree.lock_read()
878
 
        self.addCleanup(tree.unlock)
879
 
        packs = tree.branch.repository._pack_collection
880
 
        packs.ensure_loaded()
881
 
        self.assertEqual([
882
 
            packs.get_pack_by_name(packs.names()[0])],
883
 
            packs.all_packs())
884
 
 
885
 
    def test_all_packs_two(self):
886
 
        format = self.get_format()
887
 
        tree = self.make_branch_and_tree('.', format=format)
888
 
        tree.commit('start')
889
 
        tree.commit('continue')
890
 
        tree.lock_read()
891
 
        self.addCleanup(tree.unlock)
892
 
        packs = tree.branch.repository._pack_collection
893
 
        packs.ensure_loaded()
894
 
        self.assertEqual([
895
 
            packs.get_pack_by_name(packs.names()[0]),
896
 
            packs.get_pack_by_name(packs.names()[1]),
897
 
            ], packs.all_packs())
898
 
 
899
 
    def test_get_pack_by_name(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
 
        name = packs.names()[0]
908
 
        pack_1 = packs.get_pack_by_name(name)
909
 
        # the pack should be correctly initialised
910
 
        sizes = packs._names[name]
911
 
        rev_index = GraphIndex(packs._index_transport, name + '.rix', sizes[0])
912
 
        inv_index = GraphIndex(packs._index_transport, name + '.iix', sizes[1])
913
 
        txt_index = GraphIndex(packs._index_transport, name + '.tix', sizes[2])
914
 
        sig_index = GraphIndex(packs._index_transport, name + '.six', sizes[3])
915
 
        self.assertEqual(pack_repo.ExistingPack(packs._pack_transport,
916
 
            name, rev_index, inv_index, txt_index, sig_index), pack_1)
917
 
        # and the same instance should be returned on successive calls.
918
 
        self.assertTrue(pack_1 is packs.get_pack_by_name(name))
919
 
 
920
 
 
921
 
class TestPack(TestCaseWithTransport):
922
 
    """Tests for the Pack object."""
923
 
 
924
 
    def assertCurrentlyEqual(self, left, right):
925
 
        self.assertTrue(left == right)
926
 
        self.assertTrue(right == left)
927
 
        self.assertFalse(left != right)
928
 
        self.assertFalse(right != left)
929
 
 
930
 
    def assertCurrentlyNotEqual(self, left, right):
931
 
        self.assertFalse(left == right)
932
 
        self.assertFalse(right == left)
933
 
        self.assertTrue(left != right)
934
 
        self.assertTrue(right != left)
935
 
 
936
 
    def test___eq____ne__(self):
937
 
        left = pack_repo.ExistingPack('', '', '', '', '', '')
938
 
        right = pack_repo.ExistingPack('', '', '', '', '', '')
939
 
        self.assertCurrentlyEqual(left, right)
940
 
        # change all attributes and ensure equality changes as we do.
941
 
        left.revision_index = 'a'
942
 
        self.assertCurrentlyNotEqual(left, right)
943
 
        right.revision_index = 'a'
944
 
        self.assertCurrentlyEqual(left, right)
945
 
        left.inventory_index = 'a'
946
 
        self.assertCurrentlyNotEqual(left, right)
947
 
        right.inventory_index = 'a'
948
 
        self.assertCurrentlyEqual(left, right)
949
 
        left.text_index = 'a'
950
 
        self.assertCurrentlyNotEqual(left, right)
951
 
        right.text_index = 'a'
952
 
        self.assertCurrentlyEqual(left, right)
953
 
        left.signature_index = 'a'
954
 
        self.assertCurrentlyNotEqual(left, right)
955
 
        right.signature_index = 'a'
956
 
        self.assertCurrentlyEqual(left, right)
957
 
        left.name = 'a'
958
 
        self.assertCurrentlyNotEqual(left, right)
959
 
        right.name = 'a'
960
 
        self.assertCurrentlyEqual(left, right)
961
 
        left.transport = 'a'
962
 
        self.assertCurrentlyNotEqual(left, right)
963
 
        right.transport = 'a'
964
 
        self.assertCurrentlyEqual(left, right)
965
 
 
966
 
    def test_file_name(self):
967
 
        pack = pack_repo.ExistingPack('', 'a_name', '', '', '', '')
968
 
        self.assertEqual('a_name.pack', pack.file_name())
969
 
 
970
 
 
971
 
class TestNewPack(TestCaseWithTransport):
972
 
    """Tests for pack_repo.NewPack."""
973
 
 
974
 
    def test_new_instance_attributes(self):
975
 
        upload_transport = self.get_transport('upload')
976
 
        pack_transport = self.get_transport('pack')
977
 
        index_transport = self.get_transport('index')
978
 
        upload_transport.mkdir('.')
979
 
        pack = pack_repo.NewPack(upload_transport, index_transport,
980
 
            pack_transport, index_builder_class=BTreeBuilder,
981
 
            index_class=BTreeGraphIndex)
982
 
        self.assertIsInstance(pack.revision_index, BTreeBuilder)
983
 
        self.assertIsInstance(pack.inventory_index, BTreeBuilder)
984
 
        self.assertIsInstance(pack._hash, type(osutils.md5()))
985
 
        self.assertTrue(pack.upload_transport is upload_transport)
986
 
        self.assertTrue(pack.index_transport is index_transport)
987
 
        self.assertTrue(pack.pack_transport is pack_transport)
988
 
        self.assertEqual(None, pack.index_sizes)
989
 
        self.assertEqual(20, len(pack.random_name))
990
 
        self.assertIsInstance(pack.random_name, str)
991
 
        self.assertIsInstance(pack.start_time, float)
992
 
 
993
 
 
994
 
class TestPacker(TestCaseWithTransport):
995
 
    """Tests for the packs repository Packer class."""
996
 
 
997
 
    # To date, this class has been factored out and nothing new added to it;
998
 
    # thus there are not yet any tests.
999
 
 
1000
 
 
1001
 
class TestInterDifferingSerializer(TestCaseWithTransport):
1002
 
 
1003
 
    def test_progress_bar(self):
1004
 
        tree = self.make_branch_and_tree('tree')
1005
 
        tree.commit('rev1', rev_id='rev-1')
1006
 
        tree.commit('rev2', rev_id='rev-2')
1007
 
        tree.commit('rev3', rev_id='rev-3')
1008
 
        repo = self.make_repository('repo')
1009
 
        inter_repo = repository.InterDifferingSerializer(
1010
 
            tree.branch.repository, repo)
1011
 
        pb = progress.InstrumentedProgress(to_file=StringIO())
1012
 
        pb.never_throttle = True
1013
 
        inter_repo.fetch('rev-1', pb)
1014
 
        self.assertEqual('Transferring revisions', pb.last_msg)
1015
 
        self.assertEqual(1, pb.last_cnt)
1016
 
        self.assertEqual(1, pb.last_total)
1017
 
        inter_repo.fetch('rev-3', pb)
1018
 
        self.assertEqual(2, pb.last_cnt)
1019
 
        self.assertEqual(2, pb.last_total)