1
# Copyright (C) 2006, 2007 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""Tests for the Repository facility that are not interface tests.
19
For interface tests see tests/repository_implementations/*.py.
21
For concrete class tests see this file, and for storage formats tests
26
from stat import S_ISDIR
27
from StringIO import StringIO
30
from bzrlib.errors import (NotBranchError,
33
UnsupportedFormatError,
35
from bzrlib.index import GraphIndex, InMemoryGraphIndex
36
from bzrlib.repository import RepositoryFormat
37
from bzrlib.smart import server
38
from bzrlib.tests import (
40
TestCaseWithTransport,
43
from bzrlib.transport import get_transport
44
from bzrlib.transport.memory import MemoryServer
45
from bzrlib.util import bencode
51
revision as _mod_revision,
56
from bzrlib.repofmt import knitrepo, weaverepo, pack_repo
59
class TestDefaultFormat(TestCase):
61
def test_get_set_default_format(self):
62
old_default = bzrdir.format_registry.get('default')
63
private_default = old_default().repository_format.__class__
64
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()
70
bzrdir.format_registry.remove('default')
71
bzrdir.format_registry.register('sample', make_sample_bzrdir, '')
72
bzrdir.format_registry.set_default('sample')
73
# creating a repository should now create an instrumented dir.
75
# the default branch format is used by the meta dir format
76
# which is not the default bzrdir format at this point
77
dir = bzrdir.BzrDirMetaFormat1().initialize('memory:///')
78
result = dir.create_repository()
79
self.assertEqual(result, 'A bzr repository dir')
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(),
88
class SampleRepositoryFormat(repository.RepositoryFormat):
91
this format is initializable, unsupported to aid in testing the
92
open and open(unsupported=True) routines.
95
def get_format_string(self):
96
"""See RepositoryFormat.get_format_string()."""
97
return "Sample .bzr repository format."
99
def initialize(self, a_bzrdir, shared=False):
100
"""Initialize a repository in a BzrDir"""
101
t = a_bzrdir.get_repository_transport(self)
102
t.put_bytes('format', self.get_format_string())
103
return 'A bzr repository dir'
105
def is_supported(self):
108
def open(self, a_bzrdir, _found=False):
109
return "opened repository."
112
class TestRepositoryFormat(TestCaseWithTransport):
113
"""Tests for the Repository format detection used by the bzr meta dir facility.BzrBranchFormat facility."""
115
def test_find_format(self):
116
# is the right format object found for a repository?
117
# create a branch with a few known format objects.
118
# this is not quite the same as
119
self.build_tree(["foo/", "bar/"])
120
def check_format(format, url):
121
dir = format._matchingbzrdir.initialize(url)
122
format.initialize(dir)
123
t = get_transport(url)
124
found_format = repository.RepositoryFormat.find_format(dir)
125
self.failUnless(isinstance(found_format, format.__class__))
126
check_format(weaverepo.RepositoryFormat7(), "bar")
128
def test_find_format_no_repository(self):
129
dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
130
self.assertRaises(errors.NoRepositoryPresent,
131
repository.RepositoryFormat.find_format,
134
def test_find_format_unknown_format(self):
135
dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
136
SampleRepositoryFormat().initialize(dir)
137
self.assertRaises(UnknownFormatError,
138
repository.RepositoryFormat.find_format,
141
def test_register_unregister_format(self):
142
format = SampleRepositoryFormat()
144
dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
146
format.initialize(dir)
147
# register a format for it.
148
repository.RepositoryFormat.register_format(format)
149
# which repository.Open will refuse (not supported)
150
self.assertRaises(UnsupportedFormatError, repository.Repository.open, self.get_url())
151
# but open(unsupported) will work
152
self.assertEqual(format.open(dir), "opened repository.")
153
# unregister the format
154
repository.RepositoryFormat.unregister_format(format)
157
class TestFormat6(TestCaseWithTransport):
159
def test_no_ancestry_weave(self):
160
control = bzrdir.BzrDirFormat6().initialize(self.get_url())
161
repo = weaverepo.RepositoryFormat6().initialize(control)
162
# We no longer need to create the ancestry.weave file
163
# since it is *never* used.
164
self.assertRaises(NoSuchFile,
165
control.transport.get,
168
def test_exposed_versioned_files_are_marked_dirty(self):
169
control = bzrdir.BzrDirFormat6().initialize(self.get_url())
170
repo = weaverepo.RepositoryFormat6().initialize(control)
172
inv = repo.get_inventory_weave()
174
self.assertRaises(errors.OutSideTransaction,
175
inv.add_lines, 'foo', [], [])
178
class TestFormat7(TestCaseWithTransport):
180
def test_disk_layout(self):
181
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
182
repo = weaverepo.RepositoryFormat7().initialize(control)
183
# in case of side effects of locking.
187
# format 'Bazaar-NG Repository format 7'
189
# inventory.weave == empty_weave
190
# empty revision-store directory
191
# empty weaves directory
192
t = control.get_repository_transport(None)
193
self.assertEqualDiff('Bazaar-NG Repository format 7',
194
t.get('format').read())
195
self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
196
self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
197
self.assertEqualDiff('# bzr weave file v5\n'
200
t.get('inventory.weave').read())
202
def test_shared_disk_layout(self):
203
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
204
repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
206
# format 'Bazaar-NG Repository format 7'
207
# inventory.weave == empty_weave
208
# empty revision-store directory
209
# empty weaves directory
210
# a 'shared-storage' marker file.
211
# lock is not present when unlocked
212
t = control.get_repository_transport(None)
213
self.assertEqualDiff('Bazaar-NG Repository format 7',
214
t.get('format').read())
215
self.assertEqualDiff('', t.get('shared-storage').read())
216
self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
217
self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
218
self.assertEqualDiff('# bzr weave file v5\n'
221
t.get('inventory.weave').read())
222
self.assertFalse(t.has('branch-lock'))
224
def test_creates_lockdir(self):
225
"""Make sure it appears to be controlled by a LockDir existence"""
226
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
227
repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
228
t = control.get_repository_transport(None)
229
# TODO: Should check there is a 'lock' toplevel directory,
230
# regardless of contents
231
self.assertFalse(t.has('lock/held/info'))
234
self.assertTrue(t.has('lock/held/info'))
236
# unlock so we don't get a warning about failing to do so
239
def test_uses_lockdir(self):
240
"""repo format 7 actually locks on lockdir"""
241
base_url = self.get_url()
242
control = bzrdir.BzrDirMetaFormat1().initialize(base_url)
243
repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
244
t = control.get_repository_transport(None)
248
# make sure the same lock is created by opening it
249
repo = repository.Repository.open(base_url)
251
self.assertTrue(t.has('lock/held/info'))
253
self.assertFalse(t.has('lock/held/info'))
255
def test_shared_no_tree_disk_layout(self):
256
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
257
repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
258
repo.set_make_working_trees(False)
260
# format 'Bazaar-NG Repository format 7'
262
# inventory.weave == empty_weave
263
# empty revision-store directory
264
# empty weaves directory
265
# a 'shared-storage' marker file.
266
t = control.get_repository_transport(None)
267
self.assertEqualDiff('Bazaar-NG Repository format 7',
268
t.get('format').read())
269
## self.assertEqualDiff('', t.get('lock').read())
270
self.assertEqualDiff('', t.get('shared-storage').read())
271
self.assertEqualDiff('', t.get('no-working-trees').read())
272
repo.set_make_working_trees(True)
273
self.assertFalse(t.has('no-working-trees'))
274
self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
275
self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
276
self.assertEqualDiff('# bzr weave file v5\n'
279
t.get('inventory.weave').read())
281
def test_exposed_versioned_files_are_marked_dirty(self):
282
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
283
repo = weaverepo.RepositoryFormat7().initialize(control)
285
inv = repo.get_inventory_weave()
287
self.assertRaises(errors.OutSideTransaction,
288
inv.add_lines, 'foo', [], [])
291
class TestFormatKnit1(TestCaseWithTransport):
293
def test_disk_layout(self):
294
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
295
repo = knitrepo.RepositoryFormatKnit1().initialize(control)
296
# in case of side effects of locking.
300
# format 'Bazaar-NG Knit Repository Format 1'
301
# lock: is a directory
302
# inventory.weave == empty_weave
303
# empty revision-store directory
304
# empty weaves directory
305
t = control.get_repository_transport(None)
306
self.assertEqualDiff('Bazaar-NG Knit Repository Format 1',
307
t.get('format').read())
308
# XXX: no locks left when unlocked at the moment
309
# self.assertEqualDiff('', t.get('lock').read())
310
self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
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())
318
self.assertTrue(t.has(knit_name + '.knit'))
320
def check_knits(self, t):
321
"""check knit content for a repository."""
322
self.assertHasKnit(t, 'inventory')
323
self.assertHasKnit(t, 'revisions')
324
self.assertHasKnit(t, 'signatures')
326
def test_shared_disk_layout(self):
327
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
328
repo = knitrepo.RepositoryFormatKnit1().initialize(control, shared=True)
330
# format 'Bazaar-NG Knit Repository Format 1'
331
# lock: is a directory
332
# inventory.weave == empty_weave
333
# empty revision-store directory
334
# empty weaves directory
335
# a 'shared-storage' marker file.
336
t = control.get_repository_transport(None)
337
self.assertEqualDiff('Bazaar-NG Knit Repository Format 1',
338
t.get('format').read())
339
# XXX: no locks left when unlocked at the moment
340
# self.assertEqualDiff('', t.get('lock').read())
341
self.assertEqualDiff('', t.get('shared-storage').read())
342
self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
345
def test_shared_no_tree_disk_layout(self):
346
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
347
repo = knitrepo.RepositoryFormatKnit1().initialize(control, shared=True)
348
repo.set_make_working_trees(False)
350
# format 'Bazaar-NG Knit Repository Format 1'
352
# inventory.weave == empty_weave
353
# empty revision-store directory
354
# empty weaves directory
355
# a 'shared-storage' marker file.
356
t = control.get_repository_transport(None)
357
self.assertEqualDiff('Bazaar-NG Knit Repository Format 1',
358
t.get('format').read())
359
# XXX: no locks left when unlocked at the moment
360
# self.assertEqualDiff('', t.get('lock').read())
361
self.assertEqualDiff('', t.get('shared-storage').read())
362
self.assertEqualDiff('', t.get('no-working-trees').read())
363
repo.set_make_working_trees(True)
364
self.assertFalse(t.has('no-working-trees'))
365
self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
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)
373
inv = repo.get_inventory_weave()
375
self.assertRaises(errors.OutSideTransaction,
376
inv.add_lines, 'foo', [], [])
378
def test_deserialise_sets_root_revision(self):
379
"""We must have a inventory.root.revision
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.
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)
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'
397
# Arguably, the deserialise_inventory should detect a mismatch, and
398
# raise an error, rather than silently using one revision_id over the
400
inv = repo.deserialise_inventory('test-rev-id', inv_xml)
401
self.assertEqual('other-rev-id', inv.root.revision)
404
class KnitRepositoryStreamTests(test_knit.KnitTests):
405
"""Tests for knitrepo._get_stream_as_bytes."""
407
def test_get_stream_as_bytes(self):
409
k1 = self.make_test_knit()
410
k1.add_lines('text-a', [], test_knit.split_lines(test_knit.TEXT_1))
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])
420
def test_get_stream_as_bytes_all(self):
421
"""Get a serialised data stream for all the records in a knit.
423
Much like test_get_stream_all, except for get_stream_as_bytes.
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).
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),
435
# This test is actually a bit strict as the order in which they're
436
# returned is not defined. This matches the current (deterministic)
438
expected_data_list = [
439
# version, options, parents
440
('text-a', ['fulltext'], []),
441
('text-b', ['line-delta'], ['text-a']),
442
('text-m', ['line-delta'], ['text-b', 'text-d']),
443
('text-c', ['fulltext'], []),
444
('text-d', ['line-delta'], ['text-c']),
446
for version_id, parents, lines in test_data:
447
k1.add_lines(version_id, parents, test_knit.split_lines(lines))
449
bytes = knitrepo._get_stream_as_bytes(
450
k1, ['text-a', 'text-b', 'text-m', 'text-c', 'text-d', ])
452
data = bencode.bdecode(bytes)
454
self.assertEqual('knit-plain', format)
456
for expected, actual in zip(expected_data_list, data):
457
expected_version = expected[0]
458
expected_options = expected[1]
459
expected_parents = expected[2]
460
version, options, parents, bytes = actual
461
self.assertEqual(expected_version, version)
462
self.assertEqual(expected_options, options)
463
self.assertEqual(expected_parents, parents)
464
self.assertRecordContentEqual(k1, version, bytes)
467
class DummyRepository(object):
468
"""A dummy repository for testing."""
472
def supports_rich_root(self):
476
class InterDummy(repository.InterRepository):
477
"""An inter-repository optimised code path for DummyRepository.
479
This is for use during testing where we use DummyRepository as repositories
480
so that none of the default regsitered inter-repository classes will
485
def is_compatible(repo_source, repo_target):
486
"""InterDummy is compatible with DummyRepository."""
487
return (isinstance(repo_source, DummyRepository) and
488
isinstance(repo_target, DummyRepository))
491
class TestInterRepository(TestCaseWithTransport):
493
def test_get_default_inter_repository(self):
494
# test that the InterRepository.get(repo_a, repo_b) probes
495
# for a inter_repo class where is_compatible(repo_a, repo_b) returns
496
# true and returns a default inter_repo otherwise.
497
# This also tests that the default registered optimised interrepository
498
# classes do not barf inappropriately when a surprising repository type
500
dummy_a = DummyRepository()
501
dummy_b = DummyRepository()
502
self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
504
def assertGetsDefaultInterRepository(self, repo_a, repo_b):
505
"""Asserts that InterRepository.get(repo_a, repo_b) -> the default.
507
The effective default is now InterSameDataRepository because there is
508
no actual sane default in the presence of incompatible data models.
510
inter_repo = repository.InterRepository.get(repo_a, repo_b)
511
self.assertEqual(repository.InterSameDataRepository,
512
inter_repo.__class__)
513
self.assertEqual(repo_a, inter_repo.source)
514
self.assertEqual(repo_b, inter_repo.target)
516
def test_register_inter_repository_class(self):
517
# test that a optimised code path provider - a
518
# InterRepository subclass can be registered and unregistered
519
# and that it is correctly selected when given a repository
520
# pair that it returns true on for the is_compatible static method
522
dummy_a = DummyRepository()
523
dummy_b = DummyRepository()
524
repo = self.make_repository('.')
525
# hack dummies to look like repo somewhat.
526
dummy_a._serializer = repo._serializer
527
dummy_b._serializer = repo._serializer
528
repository.InterRepository.register_optimiser(InterDummy)
530
# we should get the default for something InterDummy returns False
532
self.assertFalse(InterDummy.is_compatible(dummy_a, repo))
533
self.assertGetsDefaultInterRepository(dummy_a, repo)
534
# and we should get an InterDummy for a pair it 'likes'
535
self.assertTrue(InterDummy.is_compatible(dummy_a, dummy_b))
536
inter_repo = repository.InterRepository.get(dummy_a, dummy_b)
537
self.assertEqual(InterDummy, inter_repo.__class__)
538
self.assertEqual(dummy_a, inter_repo.source)
539
self.assertEqual(dummy_b, inter_repo.target)
541
repository.InterRepository.unregister_optimiser(InterDummy)
542
# now we should get the default InterRepository object again.
543
self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
546
class TestInterWeaveRepo(TestCaseWithTransport):
548
def test_is_compatible_and_registered(self):
549
# InterWeaveRepo is compatible when either side
550
# is a format 5/6/7 branch
551
from bzrlib.repofmt import knitrepo, weaverepo
552
formats = [weaverepo.RepositoryFormat5(),
553
weaverepo.RepositoryFormat6(),
554
weaverepo.RepositoryFormat7()]
555
incompatible_formats = [weaverepo.RepositoryFormat4(),
556
knitrepo.RepositoryFormatKnit1(),
558
repo_a = self.make_repository('a')
559
repo_b = self.make_repository('b')
560
is_compatible = repository.InterWeaveRepo.is_compatible
561
for source in incompatible_formats:
562
# force incompatible left then right
563
repo_a._format = source
564
repo_b._format = formats[0]
565
self.assertFalse(is_compatible(repo_a, repo_b))
566
self.assertFalse(is_compatible(repo_b, repo_a))
567
for source in formats:
568
repo_a._format = source
569
for target in formats:
570
repo_b._format = target
571
self.assertTrue(is_compatible(repo_a, repo_b))
572
self.assertEqual(repository.InterWeaveRepo,
573
repository.InterRepository.get(repo_a,
577
class TestInterRemoteToOther(TestCaseWithTransport):
579
def make_remote_repository(self, path, backing_format=None):
580
"""Make a RemoteRepository object backed by a real repository that will
581
be created at the given path."""
582
self.make_repository(path, format=backing_format)
583
smart_server = server.SmartTCPServer_for_testing()
585
remote_transport = get_transport(smart_server.get_url()).clone(path)
586
self.addCleanup(smart_server.tearDown)
587
remote_bzrdir = bzrdir.BzrDir.open_from_transport(remote_transport)
588
remote_repo = remote_bzrdir.open_repository()
591
def test_is_compatible_same_format(self):
592
"""InterRemoteToOther is compatible with a remote repository and a
593
second repository that have the same format."""
594
local_repo = self.make_repository('local')
595
remote_repo = self.make_remote_repository('remote')
596
is_compatible = repository.InterRemoteToOther.is_compatible
598
is_compatible(remote_repo, local_repo),
599
"InterRemoteToOther(%r, %r) is false" % (remote_repo, local_repo))
601
def test_is_incompatible_different_format(self):
602
local_repo = self.make_repository('local', 'dirstate')
603
remote_repo = self.make_remote_repository('a', 'dirstate-with-subtree')
604
is_compatible = repository.InterRemoteToOther.is_compatible
606
is_compatible(remote_repo, local_repo),
607
"InterRemoteToOther(%r, %r) is true" % (local_repo, remote_repo))
609
def test_is_incompatible_different_format_both_remote(self):
610
remote_repo_a = self.make_remote_repository(
611
'a', 'dirstate-with-subtree')
612
remote_repo_b = self.make_remote_repository('b', 'dirstate')
613
is_compatible = repository.InterRemoteToOther.is_compatible
615
is_compatible(remote_repo_a, remote_repo_b),
616
"InterRemoteToOther(%r, %r) is true"
617
% (remote_repo_a, remote_repo_b))
620
class TestRepositoryConverter(TestCaseWithTransport):
622
def test_convert_empty(self):
623
t = get_transport(self.get_url('.'))
624
t.mkdir('repository')
625
repo_dir = bzrdir.BzrDirMetaFormat1().initialize('repository')
626
repo = weaverepo.RepositoryFormat7().initialize(repo_dir)
627
target_format = knitrepo.RepositoryFormatKnit1()
628
converter = repository.CopyConverter(target_format)
629
pb = bzrlib.ui.ui_factory.nested_progress_bar()
631
converter.convert(repo, pb)
634
repo = repo_dir.open_repository()
635
self.assertTrue(isinstance(target_format, repo._format.__class__))
638
class TestMisc(TestCase):
640
def test_unescape_xml(self):
641
"""We get some kind of error when malformed entities are passed"""
642
self.assertRaises(KeyError, repository._unescape_xml, 'foo&bar;')
645
class TestRepositoryFormatKnit3(TestCaseWithTransport):
647
def test_convert(self):
648
"""Ensure the upgrade adds weaves for roots"""
649
format = bzrdir.BzrDirMetaFormat1()
650
format.repository_format = knitrepo.RepositoryFormatKnit1()
651
tree = self.make_branch_and_tree('.', format)
652
tree.commit("Dull commit", rev_id="dull")
653
revision_tree = tree.branch.repository.revision_tree('dull')
654
self.assertRaises(errors.NoSuchFile, revision_tree.get_file_lines,
655
revision_tree.inventory.root.file_id)
656
format = bzrdir.BzrDirMetaFormat1()
657
format.repository_format = knitrepo.RepositoryFormatKnit3()
658
upgrade.Convert('.', format)
659
tree = workingtree.WorkingTree.open('.')
660
revision_tree = tree.branch.repository.revision_tree('dull')
661
revision_tree.get_file_lines(revision_tree.inventory.root.file_id)
662
tree.commit("Another dull commit", rev_id='dull2')
663
revision_tree = tree.branch.repository.revision_tree('dull2')
664
self.assertEqual('dull', revision_tree.inventory.root.revision)
666
def test_exposed_versioned_files_are_marked_dirty(self):
667
format = bzrdir.BzrDirMetaFormat1()
668
format.repository_format = knitrepo.RepositoryFormatKnit3()
669
repo = self.make_repository('.', format=format)
671
inv = repo.get_inventory_weave()
673
self.assertRaises(errors.OutSideTransaction,
674
inv.add_lines, 'foo', [], [])
677
class TestWithBrokenRepo(TestCaseWithTransport):
678
"""These tests seem to be more appropriate as interface tests?"""
680
def make_broken_repository(self):
681
# XXX: This function is borrowed from Aaron's "Reconcile can fix bad
682
# parent references" branch which is due to land in bzr.dev soon. Once
683
# it does, this duplication should be removed.
684
repo = self.make_repository('broken-repo')
688
cleanups.append(repo.unlock)
689
repo.start_write_group()
690
cleanups.append(repo.commit_write_group)
691
# make rev1a: A well-formed revision, containing 'file1'
692
inv = inventory.Inventory(revision_id='rev1a')
693
inv.root.revision = 'rev1a'
694
self.add_file(repo, inv, 'file1', 'rev1a', [])
695
repo.add_inventory('rev1a', inv, [])
696
revision = _mod_revision.Revision('rev1a',
697
committer='jrandom@example.com', timestamp=0,
698
inventory_sha1='', timezone=0, message='foo', parent_ids=[])
699
repo.add_revision('rev1a',revision, inv)
701
# make rev1b, which has no Revision, but has an Inventory, and
703
inv = inventory.Inventory(revision_id='rev1b')
704
inv.root.revision = 'rev1b'
705
self.add_file(repo, inv, 'file1', 'rev1b', [])
706
repo.add_inventory('rev1b', inv, [])
708
# make rev2, with file1 and file2
710
# file1 has 'rev1b' as an ancestor, even though this is not
711
# mentioned by 'rev1a', making it an unreferenced ancestor
712
inv = inventory.Inventory()
713
self.add_file(repo, inv, 'file1', 'rev2', ['rev1a', 'rev1b'])
714
self.add_file(repo, inv, 'file2', 'rev2', [])
715
self.add_revision(repo, 'rev2', inv, ['rev1a'])
717
# make ghost revision rev1c
718
inv = inventory.Inventory()
719
self.add_file(repo, inv, 'file2', 'rev1c', [])
721
# make rev3 with file2
722
# file2 refers to 'rev1c', which is a ghost in this repository, so
723
# file2 cannot have rev1c as its ancestor.
724
inv = inventory.Inventory()
725
self.add_file(repo, inv, 'file2', 'rev3', ['rev1c'])
726
self.add_revision(repo, 'rev3', inv, ['rev1c'])
729
for cleanup in reversed(cleanups):
732
def add_revision(self, repo, revision_id, inv, parent_ids):
733
inv.revision_id = revision_id
734
inv.root.revision = revision_id
735
repo.add_inventory(revision_id, inv, parent_ids)
736
revision = _mod_revision.Revision(revision_id,
737
committer='jrandom@example.com', timestamp=0, inventory_sha1='',
738
timezone=0, message='foo', parent_ids=parent_ids)
739
repo.add_revision(revision_id,revision, inv)
741
def add_file(self, repo, inv, filename, revision, parents):
742
file_id = filename + '-id'
743
entry = inventory.InventoryFile(file_id, filename, 'TREE_ROOT')
744
entry.revision = revision
747
vf = repo.weave_store.get_weave_or_empty(file_id,
748
repo.get_transaction())
749
vf.add_lines(revision, parents, ['line\n'])
751
def test_insert_from_broken_repo(self):
752
"""Inserting a data stream from a broken repository won't silently
753
corrupt the target repository.
755
broken_repo = self.make_broken_repository()
756
empty_repo = self.make_repository('empty-repo')
757
stream = broken_repo.get_data_stream(['rev1a', 'rev2', 'rev3'])
758
empty_repo.lock_write()
759
self.addCleanup(empty_repo.unlock)
760
empty_repo.start_write_group()
763
errors.KnitCorrupt, empty_repo.insert_data_stream, stream)
765
empty_repo.abort_write_group()
768
class TestKnitPackNoSubtrees(TestCaseWithTransport):
770
def get_format(self):
771
return bzrdir.format_registry.make_bzrdir('pack-0.92')
773
def test_disk_layout(self):
774
format = self.get_format()
775
repo = self.make_repository('.', format=format)
776
# in case of side effects of locking.
779
t = repo.bzrdir.get_repository_transport(None)
781
# XXX: no locks left when unlocked at the moment
782
# self.assertEqualDiff('', t.get('lock').read())
783
self.check_databases(t)
785
def check_format(self, t):
786
self.assertEqualDiff(
787
"Bazaar pack repository format 1 (needs bzr 0.92)\n",
788
t.get('format').read())
790
def assertHasKndx(self, t, knit_name):
791
"""Assert that knit_name exists on t."""
792
self.assertEqualDiff('# bzr knit index 8\n',
793
t.get(knit_name + '.kndx').read())
795
def assertHasNoKndx(self, t, knit_name):
796
"""Assert that knit_name has no index on t."""
797
self.assertFalse(t.has(knit_name + '.kndx'))
799
def assertHasNoKnit(self, t, knit_name):
800
"""Assert that knit_name exists on t."""
802
self.assertFalse(t.has(knit_name + '.knit'))
804
def check_databases(self, t):
805
"""check knit content for a repository."""
806
# check conversion worked
807
self.assertHasNoKndx(t, 'inventory')
808
self.assertHasNoKnit(t, 'inventory')
809
self.assertHasNoKndx(t, 'revisions')
810
self.assertHasNoKnit(t, 'revisions')
811
self.assertHasNoKndx(t, 'signatures')
812
self.assertHasNoKnit(t, 'signatures')
813
self.assertFalse(t.has('knits'))
814
# revision-indexes file-container directory
816
list(GraphIndex(t, 'pack-names', None).iter_all_entries()))
817
self.assertTrue(S_ISDIR(t.stat('packs').st_mode))
818
self.assertTrue(S_ISDIR(t.stat('upload').st_mode))
819
self.assertTrue(S_ISDIR(t.stat('indices').st_mode))
820
self.assertTrue(S_ISDIR(t.stat('obsolete_packs').st_mode))
822
def test_shared_disk_layout(self):
823
format = self.get_format()
824
repo = self.make_repository('.', shared=True, format=format)
826
t = repo.bzrdir.get_repository_transport(None)
828
# XXX: no locks left when unlocked at the moment
829
# self.assertEqualDiff('', t.get('lock').read())
830
# We should have a 'shared-storage' marker file.
831
self.assertEqualDiff('', t.get('shared-storage').read())
832
self.check_databases(t)
834
def test_shared_no_tree_disk_layout(self):
835
format = self.get_format()
836
repo = self.make_repository('.', shared=True, format=format)
837
repo.set_make_working_trees(False)
839
t = repo.bzrdir.get_repository_transport(None)
841
# XXX: no locks left when unlocked at the moment
842
# self.assertEqualDiff('', t.get('lock').read())
843
# We should have a 'shared-storage' marker file.
844
self.assertEqualDiff('', t.get('shared-storage').read())
845
# We should have a marker for the no-working-trees flag.
846
self.assertEqualDiff('', t.get('no-working-trees').read())
847
# The marker should go when we toggle the setting.
848
repo.set_make_working_trees(True)
849
self.assertFalse(t.has('no-working-trees'))
850
self.check_databases(t)
852
def test_adding_revision_creates_pack_indices(self):
853
format = self.get_format()
854
tree = self.make_branch_and_tree('.', format=format)
855
trans = tree.branch.repository.bzrdir.get_repository_transport(None)
857
list(GraphIndex(trans, 'pack-names', None).iter_all_entries()))
858
tree.commit('foobarbaz')
859
index = GraphIndex(trans, 'pack-names', None)
860
index_nodes = list(index.iter_all_entries())
861
self.assertEqual(1, len(index_nodes))
862
node = index_nodes[0]
864
# the pack sizes should be listed in the index
866
sizes = [int(digits) for digits in pack_value.split(' ')]
867
for size, suffix in zip(sizes, ['.rix', '.iix', '.tix', '.six']):
868
stat = trans.stat('indices/%s%s' % (name, suffix))
869
self.assertEqual(size, stat.st_size)
871
def test_pulling_nothing_leads_to_no_new_names(self):
872
format = self.get_format()
873
tree1 = self.make_branch_and_tree('1', format=format)
874
tree2 = self.make_branch_and_tree('2', format=format)
875
tree1.branch.repository.fetch(tree2.branch.repository)
876
trans = tree1.branch.repository.bzrdir.get_repository_transport(None)
878
list(GraphIndex(trans, 'pack-names', None).iter_all_entries()))
880
def test_commit_across_pack_shape_boundary_autopacks(self):
881
format = self.get_format()
882
tree = self.make_branch_and_tree('.', format=format)
883
trans = tree.branch.repository.bzrdir.get_repository_transport(None)
884
# This test could be a little cheaper by replacing the packs
885
# attribute on the repository to allow a different pack distribution
886
# and max packs policy - so we are checking the policy is honoured
887
# in the test. But for now 11 commits is not a big deal in a single
890
tree.commit('commit %s' % x)
891
# there should be 9 packs:
892
index = GraphIndex(trans, 'pack-names', None)
893
self.assertEqual(9, len(list(index.iter_all_entries())))
894
# insert some files in obsolete_packs which should be removed by pack.
895
trans.put_bytes('obsolete_packs/foo', '123')
896
trans.put_bytes('obsolete_packs/bar', '321')
897
# committing one more should coalesce to 1 of 10.
898
tree.commit('commit triggering pack')
899
index = GraphIndex(trans, 'pack-names', None)
900
self.assertEqual(1, len(list(index.iter_all_entries())))
901
# packing should not damage data
902
tree = tree.bzrdir.open_workingtree()
903
check_result = tree.branch.repository.check(
904
[tree.branch.last_revision()])
905
# We should have 50 (10x5) files in the obsolete_packs directory.
906
obsolete_files = list(trans.list_dir('obsolete_packs'))
907
self.assertFalse('foo' in obsolete_files)
908
self.assertFalse('bar' in obsolete_files)
909
self.assertEqual(50, len(obsolete_files))
910
# XXX: Todo check packs obsoleted correctly - old packs and indices
911
# in the obsolete_packs directory.
912
large_pack_name = list(index.iter_all_entries())[0][1][0]
913
# finally, committing again should not touch the large pack.
914
tree.commit('commit not triggering pack')
915
index = GraphIndex(trans, 'pack-names', None)
916
self.assertEqual(2, len(list(index.iter_all_entries())))
917
pack_names = [node[1][0] for node in index.iter_all_entries()]
918
self.assertTrue(large_pack_name in pack_names)
920
def test_pack_after_two_commits_packs_everything(self):
921
format = self.get_format()
922
tree = self.make_branch_and_tree('.', format=format)
923
trans = tree.branch.repository.bzrdir.get_repository_transport(None)
925
tree.commit('more work')
926
tree.branch.repository.pack()
927
# there should be 1 pack:
928
index = GraphIndex(trans, 'pack-names', None)
929
self.assertEqual(1, len(list(index.iter_all_entries())))
930
self.assertEqual(2, len(tree.branch.repository.all_revision_ids()))
932
def test_pack_repositories_support_multiple_write_locks(self):
933
format = self.get_format()
934
self.make_repository('.', shared=True, format=format)
935
r1 = repository.Repository.open('.')
936
r2 = repository.Repository.open('.')
938
self.addCleanup(r1.unlock)
942
def _add_text(self, repo, fileid):
943
"""Add a text to the repository within a write group."""
944
vf =repo.weave_store.get_weave(fileid, repo.get_transaction())
945
vf.add_lines('samplerev+' + fileid, [], [])
947
def test_concurrent_writers_merge_new_packs(self):
948
format = self.get_format()
949
self.make_repository('.', shared=True, format=format)
950
r1 = repository.Repository.open('.')
951
r2 = repository.Repository.open('.')
954
# access enough data to load the names list
955
list(r1.all_revision_ids())
958
# access enough data to load the names list
959
list(r2.all_revision_ids())
960
r1.start_write_group()
962
r2.start_write_group()
964
self._add_text(r1, 'fileidr1')
965
self._add_text(r2, 'fileidr2')
967
r2.abort_write_group()
970
r1.abort_write_group()
972
# both r1 and r2 have open write groups with data in them
973
# created while the other's write group was open.
974
# Commit both which requires a merge to the pack-names.
976
r1.commit_write_group()
978
r1.abort_write_group()
979
r2.abort_write_group()
981
r2.commit_write_group()
982
# tell r1 to reload from disk
983
r1._pack_collection.reset()
984
# Now both repositories should know about both names
985
r1._pack_collection.ensure_loaded()
986
r2._pack_collection.ensure_loaded()
987
self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
988
self.assertEqual(2, len(r1._pack_collection.names()))
994
def test_concurrent_writer_second_preserves_dropping_a_pack(self):
995
format = self.get_format()
996
self.make_repository('.', shared=True, format=format)
997
r1 = repository.Repository.open('.')
998
r2 = repository.Repository.open('.')
1002
r1.start_write_group()
1004
self._add_text(r1, 'fileidr1')
1006
r1.abort_write_group()
1009
r1.commit_write_group()
1010
r1._pack_collection.ensure_loaded()
1011
name_to_drop = r1._pack_collection.all_packs()[0].name
1016
# access enough data to load the names list
1017
list(r1.all_revision_ids())
1020
# access enough data to load the names list
1021
list(r2.all_revision_ids())
1022
r1._pack_collection.ensure_loaded()
1024
r2.start_write_group()
1026
# in r1, drop the pack
1027
r1._pack_collection._remove_pack_from_memory(
1028
r1._pack_collection.get_pack_by_name(name_to_drop))
1030
self._add_text(r2, 'fileidr2')
1032
r2.abort_write_group()
1035
r1._pack_collection.reset()
1037
# r1 has a changed names list, and r2 an open write groups with
1039
# save r1, and then commit the r2 write group, which requires a
1040
# merge to the pack-names, which should not reinstate
1043
r1._pack_collection._save_pack_names()
1044
r1._pack_collection.reset()
1046
r2.abort_write_group()
1049
r2.commit_write_group()
1051
r2.abort_write_group()
1053
# Now both repositories should now about just one name.
1054
r1._pack_collection.ensure_loaded()
1055
r2._pack_collection.ensure_loaded()
1056
self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
1057
self.assertEqual(1, len(r1._pack_collection.names()))
1058
self.assertFalse(name_to_drop in r1._pack_collection.names())
1064
def test_lock_write_does_not_physically_lock(self):
1065
repo = self.make_repository('.', format=self.get_format())
1067
self.addCleanup(repo.unlock)
1068
self.assertFalse(repo.get_physical_lock_status())
1070
def prepare_for_break_lock(self):
1071
# Setup the global ui factory state so that a break-lock method call
1072
# will find usable input in the input stream.
1073
old_factory = bzrlib.ui.ui_factory
1074
def restoreFactory():
1075
bzrlib.ui.ui_factory = old_factory
1076
self.addCleanup(restoreFactory)
1077
bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
1078
bzrlib.ui.ui_factory.stdin = StringIO("y\n")
1080
def test_break_lock_breaks_physical_lock(self):
1081
repo = self.make_repository('.', format=self.get_format())
1082
repo._pack_collection.lock_names()
1083
repo2 = repository.Repository.open('.')
1084
self.assertTrue(repo.get_physical_lock_status())
1085
self.prepare_for_break_lock()
1087
self.assertFalse(repo.get_physical_lock_status())
1089
def test_broken_physical_locks_error_on__unlock_names_lock(self):
1090
repo = self.make_repository('.', format=self.get_format())
1091
repo._pack_collection.lock_names()
1092
self.assertTrue(repo.get_physical_lock_status())
1093
repo2 = repository.Repository.open('.')
1094
self.prepare_for_break_lock()
1096
self.assertRaises(errors.LockBroken, repo._pack_collection._unlock_names)
1098
def test_fetch_without_find_ghosts_ignores_ghosts(self):
1099
# we want two repositories at this point:
1100
# one with a revision that is a ghost in the other
1102
# 'ghost' is present in has_ghost, 'ghost' is absent in 'missing_ghost'.
1103
# 'references' is present in both repositories, and 'tip' is present
1104
# just in has_ghost.
1105
# has_ghost missing_ghost
1106
#------------------------------
1108
# 'references' 'references'
1110
# In this test we fetch 'tip' which should not fetch 'ghost'
1111
has_ghost = self.make_repository('has_ghost', format=self.get_format())
1112
missing_ghost = self.make_repository('missing_ghost',
1113
format=self.get_format())
1115
def add_commit(repo, revision_id, parent_ids):
1117
repo.start_write_group()
1118
inv = inventory.Inventory(revision_id=revision_id)
1119
inv.root.revision = revision_id
1120
root_id = inv.root.file_id
1121
sha1 = repo.add_inventory(revision_id, inv, [])
1122
vf = repo.weave_store.get_weave_or_empty(root_id,
1123
repo.get_transaction())
1124
vf.add_lines(revision_id, [], [])
1125
rev = bzrlib.revision.Revision(timestamp=0,
1127
committer="Foo Bar <foo@example.com>",
1129
inventory_sha1=sha1,
1130
revision_id=revision_id)
1131
rev.parent_ids = parent_ids
1132
repo.add_revision(revision_id, rev)
1133
repo.commit_write_group()
1135
add_commit(has_ghost, 'ghost', [])
1136
add_commit(has_ghost, 'references', ['ghost'])
1137
add_commit(missing_ghost, 'references', ['ghost'])
1138
add_commit(has_ghost, 'tip', ['references'])
1139
missing_ghost.fetch(has_ghost, 'tip')
1140
# missing ghost now has tip and not ghost.
1141
rev = missing_ghost.get_revision('tip')
1142
inv = missing_ghost.get_inventory('tip')
1143
self.assertRaises(errors.NoSuchRevision,
1144
missing_ghost.get_revision, 'ghost')
1145
self.assertRaises(errors.RevisionNotPresent,
1146
missing_ghost.get_inventory, 'ghost')
1149
class TestKnitPackSubtrees(TestKnitPackNoSubtrees):
1151
def get_format(self):
1152
return bzrdir.format_registry.make_bzrdir(
1153
'pack-0.92-subtree')
1155
def check_format(self, t):
1156
self.assertEqualDiff(
1157
"Bazaar pack repository format 1 with subtree support (needs bzr 0.92)\n",
1158
t.get('format').read())
1161
class TestRepositoryPackCollection(TestCaseWithTransport):
1163
def get_format(self):
1164
return bzrdir.format_registry.make_bzrdir('pack-0.92')
1166
def test__max_pack_count(self):
1167
"""The maximum pack count is a function of the number of revisions."""
1168
format = self.get_format()
1169
repo = self.make_repository('.', format=format)
1170
packs = repo._pack_collection
1171
# no revisions - one pack, so that we can have a revision free repo
1172
# without it blowing up
1173
self.assertEqual(1, packs._max_pack_count(0))
1174
# after that the sum of the digits, - check the first 1-9
1175
self.assertEqual(1, packs._max_pack_count(1))
1176
self.assertEqual(2, packs._max_pack_count(2))
1177
self.assertEqual(3, packs._max_pack_count(3))
1178
self.assertEqual(4, packs._max_pack_count(4))
1179
self.assertEqual(5, packs._max_pack_count(5))
1180
self.assertEqual(6, packs._max_pack_count(6))
1181
self.assertEqual(7, packs._max_pack_count(7))
1182
self.assertEqual(8, packs._max_pack_count(8))
1183
self.assertEqual(9, packs._max_pack_count(9))
1184
# check the boundary cases with two digits for the next decade
1185
self.assertEqual(1, packs._max_pack_count(10))
1186
self.assertEqual(2, packs._max_pack_count(11))
1187
self.assertEqual(10, packs._max_pack_count(19))
1188
self.assertEqual(2, packs._max_pack_count(20))
1189
self.assertEqual(3, packs._max_pack_count(21))
1190
# check some arbitrary big numbers
1191
self.assertEqual(25, packs._max_pack_count(112894))
1193
def test_pack_distribution_zero(self):
1194
format = self.get_format()
1195
repo = self.make_repository('.', format=format)
1196
packs = repo._pack_collection
1197
self.assertEqual([0], packs.pack_distribution(0))
1199
def test_pack_distribution_one_to_nine(self):
1200
format = self.get_format()
1201
repo = self.make_repository('.', format=format)
1202
packs = repo._pack_collection
1203
self.assertEqual([1],
1204
packs.pack_distribution(1))
1205
self.assertEqual([1, 1],
1206
packs.pack_distribution(2))
1207
self.assertEqual([1, 1, 1],
1208
packs.pack_distribution(3))
1209
self.assertEqual([1, 1, 1, 1],
1210
packs.pack_distribution(4))
1211
self.assertEqual([1, 1, 1, 1, 1],
1212
packs.pack_distribution(5))
1213
self.assertEqual([1, 1, 1, 1, 1, 1],
1214
packs.pack_distribution(6))
1215
self.assertEqual([1, 1, 1, 1, 1, 1, 1],
1216
packs.pack_distribution(7))
1217
self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1],
1218
packs.pack_distribution(8))
1219
self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1, 1],
1220
packs.pack_distribution(9))
1222
def test_pack_distribution_stable_at_boundaries(self):
1223
"""When there are multi-rev packs the counts are stable."""
1224
format = self.get_format()
1225
repo = self.make_repository('.', format=format)
1226
packs = repo._pack_collection
1228
self.assertEqual([10], packs.pack_distribution(10))
1229
self.assertEqual([10, 1], packs.pack_distribution(11))
1230
self.assertEqual([10, 10], packs.pack_distribution(20))
1231
self.assertEqual([10, 10, 1], packs.pack_distribution(21))
1233
self.assertEqual([100], packs.pack_distribution(100))
1234
self.assertEqual([100, 1], packs.pack_distribution(101))
1235
self.assertEqual([100, 10, 1], packs.pack_distribution(111))
1236
self.assertEqual([100, 100], packs.pack_distribution(200))
1237
self.assertEqual([100, 100, 1], packs.pack_distribution(201))
1238
self.assertEqual([100, 100, 10, 1], packs.pack_distribution(211))
1240
def test_plan_pack_operations_2009_revisions_skip_all_packs(self):
1241
format = self.get_format()
1242
repo = self.make_repository('.', format=format)
1243
packs = repo._pack_collection
1244
existing_packs = [(2000, "big"), (9, "medium")]
1245
# rev count - 2009 -> 2x1000 + 9x1
1246
pack_operations = packs.plan_autopack_combinations(
1247
existing_packs, [1000, 1000, 1, 1, 1, 1, 1, 1, 1, 1, 1])
1248
self.assertEqual([], pack_operations)
1250
def test_plan_pack_operations_2010_revisions_skip_all_packs(self):
1251
format = self.get_format()
1252
repo = self.make_repository('.', format=format)
1253
packs = repo._pack_collection
1254
existing_packs = [(2000, "big"), (9, "medium"), (1, "single")]
1255
# rev count - 2010 -> 2x1000 + 1x10
1256
pack_operations = packs.plan_autopack_combinations(
1257
existing_packs, [1000, 1000, 10])
1258
self.assertEqual([], pack_operations)
1260
def test_plan_pack_operations_2010_combines_smallest_two(self):
1261
format = self.get_format()
1262
repo = self.make_repository('.', format=format)
1263
packs = repo._pack_collection
1264
existing_packs = [(1999, "big"), (9, "medium"), (1, "single2"),
1266
# rev count - 2010 -> 2x1000 + 1x10 (3)
1267
pack_operations = packs.plan_autopack_combinations(
1268
existing_packs, [1000, 1000, 10])
1269
self.assertEqual([[2, ["single2", "single1"]], [0, []]], pack_operations)
1271
def test_all_packs_none(self):
1272
format = self.get_format()
1273
tree = self.make_branch_and_tree('.', format=format)
1275
self.addCleanup(tree.unlock)
1276
packs = tree.branch.repository._pack_collection
1277
packs.ensure_loaded()
1278
self.assertEqual([], packs.all_packs())
1280
def test_all_packs_one(self):
1281
format = self.get_format()
1282
tree = self.make_branch_and_tree('.', format=format)
1283
tree.commit('start')
1285
self.addCleanup(tree.unlock)
1286
packs = tree.branch.repository._pack_collection
1287
packs.ensure_loaded()
1289
packs.get_pack_by_name(packs.names()[0])],
1292
def test_all_packs_two(self):
1293
format = self.get_format()
1294
tree = self.make_branch_and_tree('.', format=format)
1295
tree.commit('start')
1296
tree.commit('continue')
1298
self.addCleanup(tree.unlock)
1299
packs = tree.branch.repository._pack_collection
1300
packs.ensure_loaded()
1302
packs.get_pack_by_name(packs.names()[0]),
1303
packs.get_pack_by_name(packs.names()[1]),
1304
], packs.all_packs())
1306
def test_get_pack_by_name(self):
1307
format = self.get_format()
1308
tree = self.make_branch_and_tree('.', format=format)
1309
tree.commit('start')
1311
self.addCleanup(tree.unlock)
1312
packs = tree.branch.repository._pack_collection
1313
packs.ensure_loaded()
1314
name = packs.names()[0]
1315
pack_1 = packs.get_pack_by_name(name)
1316
# the pack should be correctly initialised
1317
rev_index = GraphIndex(packs._index_transport, name + '.rix',
1318
packs._names[name][0])
1319
inv_index = GraphIndex(packs._index_transport, name + '.iix',
1320
packs._names[name][1])
1321
txt_index = GraphIndex(packs._index_transport, name + '.tix',
1322
packs._names[name][2])
1323
sig_index = GraphIndex(packs._index_transport, name + '.six',
1324
packs._names[name][3])
1325
self.assertEqual(pack_repo.ExistingPack(packs._pack_transport,
1326
name, rev_index, inv_index, txt_index, sig_index), pack_1)
1327
# and the same instance should be returned on successive calls.
1328
self.assertTrue(pack_1 is packs.get_pack_by_name(name))
1331
class TestPack(TestCaseWithTransport):
1332
"""Tests for the Pack object."""
1334
def assertCurrentlyEqual(self, left, right):
1335
self.assertTrue(left == right)
1336
self.assertTrue(right == left)
1337
self.assertFalse(left != right)
1338
self.assertFalse(right != left)
1340
def assertCurrentlyNotEqual(self, left, right):
1341
self.assertFalse(left == right)
1342
self.assertFalse(right == left)
1343
self.assertTrue(left != right)
1344
self.assertTrue(right != left)
1346
def test___eq____ne__(self):
1347
left = pack_repo.ExistingPack('', '', '', '', '', '')
1348
right = pack_repo.ExistingPack('', '', '', '', '', '')
1349
self.assertCurrentlyEqual(left, right)
1350
# change all attributes and ensure equality changes as we do.
1351
left.revision_index = 'a'
1352
self.assertCurrentlyNotEqual(left, right)
1353
right.revision_index = 'a'
1354
self.assertCurrentlyEqual(left, right)
1355
left.inventory_index = 'a'
1356
self.assertCurrentlyNotEqual(left, right)
1357
right.inventory_index = 'a'
1358
self.assertCurrentlyEqual(left, right)
1359
left.text_index = 'a'
1360
self.assertCurrentlyNotEqual(left, right)
1361
right.text_index = 'a'
1362
self.assertCurrentlyEqual(left, right)
1363
left.signature_index = 'a'
1364
self.assertCurrentlyNotEqual(left, right)
1365
right.signature_index = 'a'
1366
self.assertCurrentlyEqual(left, right)
1368
self.assertCurrentlyNotEqual(left, right)
1370
self.assertCurrentlyEqual(left, right)
1371
left.transport = 'a'
1372
self.assertCurrentlyNotEqual(left, right)
1373
right.transport = 'a'
1374
self.assertCurrentlyEqual(left, right)
1376
def test_file_name(self):
1377
pack = pack_repo.ExistingPack('', 'a_name', '', '', '', '')
1378
self.assertEqual('a_name.pack', pack.file_name())
1381
class TestNewPack(TestCaseWithTransport):
1382
"""Tests for pack_repo.NewPack."""
1384
def test_new_instance_attributes(self):
1385
upload_transport = self.get_transport('upload')
1386
pack_transport = self.get_transport('pack')
1387
index_transport = self.get_transport('index')
1388
upload_transport.mkdir('.')
1389
pack = pack_repo.NewPack(upload_transport, index_transport,
1391
self.assertIsInstance(pack.revision_index, InMemoryGraphIndex)
1392
self.assertIsInstance(pack.inventory_index, InMemoryGraphIndex)
1393
self.assertIsInstance(pack._hash, type(md5.new()))
1394
self.assertTrue(pack.upload_transport is upload_transport)
1395
self.assertTrue(pack.index_transport is index_transport)
1396
self.assertTrue(pack.pack_transport is pack_transport)
1397
self.assertEqual(None, pack.index_sizes)
1398
self.assertEqual(20, len(pack.random_name))
1399
self.assertIsInstance(pack.random_name, str)
1400
self.assertIsInstance(pack.start_time, float)
1403
class TestPacker(TestCaseWithTransport):
1404
"""Tests for the packs repository Packer class."""
1406
# To date, this class has been factored out and nothing new added to it;
1407
# thus there are not yet any tests.