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_layout(self):
933
format = self.get_format()
934
tree = self.make_branch_and_tree('.', format=format)
935
trans = tree.branch.repository.bzrdir.get_repository_transport(None)
936
tree.commit('start', rev_id='1')
937
tree.commit('more work', rev_id='2')
938
tree.branch.repository.pack()
940
self.addCleanup(tree.unlock)
941
pack = tree.branch.repository._pack_collection.get_pack_by_name(
942
tree.branch.repository._pack_collection.names()[0])
943
# revision access tends to be tip->ancestor, so ordering that way on
944
# disk is a good idea.
945
for _1, key, val, refs in pack.revision_index.iter_all_entries():
947
pos_1 = int(val[1:].split()[0])
949
pos_2 = int(val[1:].split()[0])
950
self.assertTrue(pos_2 < pos_1)
952
def test_pack_repositories_support_multiple_write_locks(self):
953
format = self.get_format()
954
self.make_repository('.', shared=True, format=format)
955
r1 = repository.Repository.open('.')
956
r2 = repository.Repository.open('.')
958
self.addCleanup(r1.unlock)
962
def _add_text(self, repo, fileid):
963
"""Add a text to the repository within a write group."""
964
vf =repo.weave_store.get_weave(fileid, repo.get_transaction())
965
vf.add_lines('samplerev+' + fileid, [], [])
967
def test_concurrent_writers_merge_new_packs(self):
968
format = self.get_format()
969
self.make_repository('.', shared=True, format=format)
970
r1 = repository.Repository.open('.')
971
r2 = repository.Repository.open('.')
974
# access enough data to load the names list
975
list(r1.all_revision_ids())
978
# access enough data to load the names list
979
list(r2.all_revision_ids())
980
r1.start_write_group()
982
r2.start_write_group()
984
self._add_text(r1, 'fileidr1')
985
self._add_text(r2, 'fileidr2')
987
r2.abort_write_group()
990
r1.abort_write_group()
992
# both r1 and r2 have open write groups with data in them
993
# created while the other's write group was open.
994
# Commit both which requires a merge to the pack-names.
996
r1.commit_write_group()
998
r1.abort_write_group()
999
r2.abort_write_group()
1001
r2.commit_write_group()
1002
# tell r1 to reload from disk
1003
r1._pack_collection.reset()
1004
# Now both repositories should know about both names
1005
r1._pack_collection.ensure_loaded()
1006
r2._pack_collection.ensure_loaded()
1007
self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
1008
self.assertEqual(2, len(r1._pack_collection.names()))
1014
def test_concurrent_writer_second_preserves_dropping_a_pack(self):
1015
format = self.get_format()
1016
self.make_repository('.', shared=True, format=format)
1017
r1 = repository.Repository.open('.')
1018
r2 = repository.Repository.open('.')
1019
# add a pack to drop
1022
r1.start_write_group()
1024
self._add_text(r1, 'fileidr1')
1026
r1.abort_write_group()
1029
r1.commit_write_group()
1030
r1._pack_collection.ensure_loaded()
1031
name_to_drop = r1._pack_collection.all_packs()[0].name
1036
# access enough data to load the names list
1037
list(r1.all_revision_ids())
1040
# access enough data to load the names list
1041
list(r2.all_revision_ids())
1042
r1._pack_collection.ensure_loaded()
1044
r2.start_write_group()
1046
# in r1, drop the pack
1047
r1._pack_collection._remove_pack_from_memory(
1048
r1._pack_collection.get_pack_by_name(name_to_drop))
1050
self._add_text(r2, 'fileidr2')
1052
r2.abort_write_group()
1055
r1._pack_collection.reset()
1057
# r1 has a changed names list, and r2 an open write groups with
1059
# save r1, and then commit the r2 write group, which requires a
1060
# merge to the pack-names, which should not reinstate
1063
r1._pack_collection._save_pack_names()
1064
r1._pack_collection.reset()
1066
r2.abort_write_group()
1069
r2.commit_write_group()
1071
r2.abort_write_group()
1073
# Now both repositories should now about just one name.
1074
r1._pack_collection.ensure_loaded()
1075
r2._pack_collection.ensure_loaded()
1076
self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
1077
self.assertEqual(1, len(r1._pack_collection.names()))
1078
self.assertFalse(name_to_drop in r1._pack_collection.names())
1084
def test_lock_write_does_not_physically_lock(self):
1085
repo = self.make_repository('.', format=self.get_format())
1087
self.addCleanup(repo.unlock)
1088
self.assertFalse(repo.get_physical_lock_status())
1090
def prepare_for_break_lock(self):
1091
# Setup the global ui factory state so that a break-lock method call
1092
# will find usable input in the input stream.
1093
old_factory = bzrlib.ui.ui_factory
1094
def restoreFactory():
1095
bzrlib.ui.ui_factory = old_factory
1096
self.addCleanup(restoreFactory)
1097
bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
1098
bzrlib.ui.ui_factory.stdin = StringIO("y\n")
1100
def test_break_lock_breaks_physical_lock(self):
1101
repo = self.make_repository('.', format=self.get_format())
1102
repo._pack_collection.lock_names()
1103
repo2 = repository.Repository.open('.')
1104
self.assertTrue(repo.get_physical_lock_status())
1105
self.prepare_for_break_lock()
1107
self.assertFalse(repo.get_physical_lock_status())
1109
def test_broken_physical_locks_error_on__unlock_names_lock(self):
1110
repo = self.make_repository('.', format=self.get_format())
1111
repo._pack_collection.lock_names()
1112
self.assertTrue(repo.get_physical_lock_status())
1113
repo2 = repository.Repository.open('.')
1114
self.prepare_for_break_lock()
1116
self.assertRaises(errors.LockBroken, repo._pack_collection._unlock_names)
1118
def test_fetch_without_find_ghosts_ignores_ghosts(self):
1119
# we want two repositories at this point:
1120
# one with a revision that is a ghost in the other
1122
# 'ghost' is present in has_ghost, 'ghost' is absent in 'missing_ghost'.
1123
# 'references' is present in both repositories, and 'tip' is present
1124
# just in has_ghost.
1125
# has_ghost missing_ghost
1126
#------------------------------
1128
# 'references' 'references'
1130
# In this test we fetch 'tip' which should not fetch 'ghost'
1131
has_ghost = self.make_repository('has_ghost', format=self.get_format())
1132
missing_ghost = self.make_repository('missing_ghost',
1133
format=self.get_format())
1135
def add_commit(repo, revision_id, parent_ids):
1137
repo.start_write_group()
1138
inv = inventory.Inventory(revision_id=revision_id)
1139
inv.root.revision = revision_id
1140
root_id = inv.root.file_id
1141
sha1 = repo.add_inventory(revision_id, inv, [])
1142
vf = repo.weave_store.get_weave_or_empty(root_id,
1143
repo.get_transaction())
1144
vf.add_lines(revision_id, [], [])
1145
rev = bzrlib.revision.Revision(timestamp=0,
1147
committer="Foo Bar <foo@example.com>",
1149
inventory_sha1=sha1,
1150
revision_id=revision_id)
1151
rev.parent_ids = parent_ids
1152
repo.add_revision(revision_id, rev)
1153
repo.commit_write_group()
1155
add_commit(has_ghost, 'ghost', [])
1156
add_commit(has_ghost, 'references', ['ghost'])
1157
add_commit(missing_ghost, 'references', ['ghost'])
1158
add_commit(has_ghost, 'tip', ['references'])
1159
missing_ghost.fetch(has_ghost, 'tip')
1160
# missing ghost now has tip and not ghost.
1161
rev = missing_ghost.get_revision('tip')
1162
inv = missing_ghost.get_inventory('tip')
1163
self.assertRaises(errors.NoSuchRevision,
1164
missing_ghost.get_revision, 'ghost')
1165
self.assertRaises(errors.RevisionNotPresent,
1166
missing_ghost.get_inventory, 'ghost')
1169
class TestKnitPackSubtrees(TestKnitPackNoSubtrees):
1171
def get_format(self):
1172
return bzrdir.format_registry.make_bzrdir(
1173
'pack-0.92-subtree')
1175
def check_format(self, t):
1176
self.assertEqualDiff(
1177
"Bazaar pack repository format 1 with subtree support (needs bzr 0.92)\n",
1178
t.get('format').read())
1181
class TestRepositoryPackCollection(TestCaseWithTransport):
1183
def get_format(self):
1184
return bzrdir.format_registry.make_bzrdir('pack-0.92')
1186
def test__max_pack_count(self):
1187
"""The maximum pack count is a function of the number of revisions."""
1188
format = self.get_format()
1189
repo = self.make_repository('.', format=format)
1190
packs = repo._pack_collection
1191
# no revisions - one pack, so that we can have a revision free repo
1192
# without it blowing up
1193
self.assertEqual(1, packs._max_pack_count(0))
1194
# after that the sum of the digits, - check the first 1-9
1195
self.assertEqual(1, packs._max_pack_count(1))
1196
self.assertEqual(2, packs._max_pack_count(2))
1197
self.assertEqual(3, packs._max_pack_count(3))
1198
self.assertEqual(4, packs._max_pack_count(4))
1199
self.assertEqual(5, packs._max_pack_count(5))
1200
self.assertEqual(6, packs._max_pack_count(6))
1201
self.assertEqual(7, packs._max_pack_count(7))
1202
self.assertEqual(8, packs._max_pack_count(8))
1203
self.assertEqual(9, packs._max_pack_count(9))
1204
# check the boundary cases with two digits for the next decade
1205
self.assertEqual(1, packs._max_pack_count(10))
1206
self.assertEqual(2, packs._max_pack_count(11))
1207
self.assertEqual(10, packs._max_pack_count(19))
1208
self.assertEqual(2, packs._max_pack_count(20))
1209
self.assertEqual(3, packs._max_pack_count(21))
1210
# check some arbitrary big numbers
1211
self.assertEqual(25, packs._max_pack_count(112894))
1213
def test_pack_distribution_zero(self):
1214
format = self.get_format()
1215
repo = self.make_repository('.', format=format)
1216
packs = repo._pack_collection
1217
self.assertEqual([0], packs.pack_distribution(0))
1219
def test_ensure_loaded_unlocked(self):
1220
format = self.get_format()
1221
repo = self.make_repository('.', format=format)
1222
self.assertRaises(errors.ObjectNotLocked,
1223
repo._pack_collection.ensure_loaded)
1225
def test_pack_distribution_one_to_nine(self):
1226
format = self.get_format()
1227
repo = self.make_repository('.', format=format)
1228
packs = repo._pack_collection
1229
self.assertEqual([1],
1230
packs.pack_distribution(1))
1231
self.assertEqual([1, 1],
1232
packs.pack_distribution(2))
1233
self.assertEqual([1, 1, 1],
1234
packs.pack_distribution(3))
1235
self.assertEqual([1, 1, 1, 1],
1236
packs.pack_distribution(4))
1237
self.assertEqual([1, 1, 1, 1, 1],
1238
packs.pack_distribution(5))
1239
self.assertEqual([1, 1, 1, 1, 1, 1],
1240
packs.pack_distribution(6))
1241
self.assertEqual([1, 1, 1, 1, 1, 1, 1],
1242
packs.pack_distribution(7))
1243
self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1],
1244
packs.pack_distribution(8))
1245
self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1, 1],
1246
packs.pack_distribution(9))
1248
def test_pack_distribution_stable_at_boundaries(self):
1249
"""When there are multi-rev packs the counts are stable."""
1250
format = self.get_format()
1251
repo = self.make_repository('.', format=format)
1252
packs = repo._pack_collection
1254
self.assertEqual([10], packs.pack_distribution(10))
1255
self.assertEqual([10, 1], packs.pack_distribution(11))
1256
self.assertEqual([10, 10], packs.pack_distribution(20))
1257
self.assertEqual([10, 10, 1], packs.pack_distribution(21))
1259
self.assertEqual([100], packs.pack_distribution(100))
1260
self.assertEqual([100, 1], packs.pack_distribution(101))
1261
self.assertEqual([100, 10, 1], packs.pack_distribution(111))
1262
self.assertEqual([100, 100], packs.pack_distribution(200))
1263
self.assertEqual([100, 100, 1], packs.pack_distribution(201))
1264
self.assertEqual([100, 100, 10, 1], packs.pack_distribution(211))
1266
def test_plan_pack_operations_2009_revisions_skip_all_packs(self):
1267
format = self.get_format()
1268
repo = self.make_repository('.', format=format)
1269
packs = repo._pack_collection
1270
existing_packs = [(2000, "big"), (9, "medium")]
1271
# rev count - 2009 -> 2x1000 + 9x1
1272
pack_operations = packs.plan_autopack_combinations(
1273
existing_packs, [1000, 1000, 1, 1, 1, 1, 1, 1, 1, 1, 1])
1274
self.assertEqual([], pack_operations)
1276
def test_plan_pack_operations_2010_revisions_skip_all_packs(self):
1277
format = self.get_format()
1278
repo = self.make_repository('.', format=format)
1279
packs = repo._pack_collection
1280
existing_packs = [(2000, "big"), (9, "medium"), (1, "single")]
1281
# rev count - 2010 -> 2x1000 + 1x10
1282
pack_operations = packs.plan_autopack_combinations(
1283
existing_packs, [1000, 1000, 10])
1284
self.assertEqual([], pack_operations)
1286
def test_plan_pack_operations_2010_combines_smallest_two(self):
1287
format = self.get_format()
1288
repo = self.make_repository('.', format=format)
1289
packs = repo._pack_collection
1290
existing_packs = [(1999, "big"), (9, "medium"), (1, "single2"),
1292
# rev count - 2010 -> 2x1000 + 1x10 (3)
1293
pack_operations = packs.plan_autopack_combinations(
1294
existing_packs, [1000, 1000, 10])
1295
self.assertEqual([[2, ["single2", "single1"]], [0, []]], pack_operations)
1297
def test_all_packs_none(self):
1298
format = self.get_format()
1299
tree = self.make_branch_and_tree('.', format=format)
1301
self.addCleanup(tree.unlock)
1302
packs = tree.branch.repository._pack_collection
1303
packs.ensure_loaded()
1304
self.assertEqual([], packs.all_packs())
1306
def test_all_packs_one(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()
1315
packs.get_pack_by_name(packs.names()[0])],
1318
def test_all_packs_two(self):
1319
format = self.get_format()
1320
tree = self.make_branch_and_tree('.', format=format)
1321
tree.commit('start')
1322
tree.commit('continue')
1324
self.addCleanup(tree.unlock)
1325
packs = tree.branch.repository._pack_collection
1326
packs.ensure_loaded()
1328
packs.get_pack_by_name(packs.names()[0]),
1329
packs.get_pack_by_name(packs.names()[1]),
1330
], packs.all_packs())
1332
def test_get_pack_by_name(self):
1333
format = self.get_format()
1334
tree = self.make_branch_and_tree('.', format=format)
1335
tree.commit('start')
1337
self.addCleanup(tree.unlock)
1338
packs = tree.branch.repository._pack_collection
1339
packs.ensure_loaded()
1340
name = packs.names()[0]
1341
pack_1 = packs.get_pack_by_name(name)
1342
# the pack should be correctly initialised
1343
rev_index = GraphIndex(packs._index_transport, name + '.rix',
1344
packs._names[name][0])
1345
inv_index = GraphIndex(packs._index_transport, name + '.iix',
1346
packs._names[name][1])
1347
txt_index = GraphIndex(packs._index_transport, name + '.tix',
1348
packs._names[name][2])
1349
sig_index = GraphIndex(packs._index_transport, name + '.six',
1350
packs._names[name][3])
1351
self.assertEqual(pack_repo.ExistingPack(packs._pack_transport,
1352
name, rev_index, inv_index, txt_index, sig_index), pack_1)
1353
# and the same instance should be returned on successive calls.
1354
self.assertTrue(pack_1 is packs.get_pack_by_name(name))
1357
class TestPack(TestCaseWithTransport):
1358
"""Tests for the Pack object."""
1360
def assertCurrentlyEqual(self, left, right):
1361
self.assertTrue(left == right)
1362
self.assertTrue(right == left)
1363
self.assertFalse(left != right)
1364
self.assertFalse(right != left)
1366
def assertCurrentlyNotEqual(self, left, right):
1367
self.assertFalse(left == right)
1368
self.assertFalse(right == left)
1369
self.assertTrue(left != right)
1370
self.assertTrue(right != left)
1372
def test___eq____ne__(self):
1373
left = pack_repo.ExistingPack('', '', '', '', '', '')
1374
right = pack_repo.ExistingPack('', '', '', '', '', '')
1375
self.assertCurrentlyEqual(left, right)
1376
# change all attributes and ensure equality changes as we do.
1377
left.revision_index = 'a'
1378
self.assertCurrentlyNotEqual(left, right)
1379
right.revision_index = 'a'
1380
self.assertCurrentlyEqual(left, right)
1381
left.inventory_index = 'a'
1382
self.assertCurrentlyNotEqual(left, right)
1383
right.inventory_index = 'a'
1384
self.assertCurrentlyEqual(left, right)
1385
left.text_index = 'a'
1386
self.assertCurrentlyNotEqual(left, right)
1387
right.text_index = 'a'
1388
self.assertCurrentlyEqual(left, right)
1389
left.signature_index = 'a'
1390
self.assertCurrentlyNotEqual(left, right)
1391
right.signature_index = 'a'
1392
self.assertCurrentlyEqual(left, right)
1394
self.assertCurrentlyNotEqual(left, right)
1396
self.assertCurrentlyEqual(left, right)
1397
left.transport = 'a'
1398
self.assertCurrentlyNotEqual(left, right)
1399
right.transport = 'a'
1400
self.assertCurrentlyEqual(left, right)
1402
def test_file_name(self):
1403
pack = pack_repo.ExistingPack('', 'a_name', '', '', '', '')
1404
self.assertEqual('a_name.pack', pack.file_name())
1407
class TestNewPack(TestCaseWithTransport):
1408
"""Tests for pack_repo.NewPack."""
1410
def test_new_instance_attributes(self):
1411
upload_transport = self.get_transport('upload')
1412
pack_transport = self.get_transport('pack')
1413
index_transport = self.get_transport('index')
1414
upload_transport.mkdir('.')
1415
pack = pack_repo.NewPack(upload_transport, index_transport,
1417
self.assertIsInstance(pack.revision_index, InMemoryGraphIndex)
1418
self.assertIsInstance(pack.inventory_index, InMemoryGraphIndex)
1419
self.assertIsInstance(pack._hash, type(md5.new()))
1420
self.assertTrue(pack.upload_transport is upload_transport)
1421
self.assertTrue(pack.index_transport is index_transport)
1422
self.assertTrue(pack.pack_transport is pack_transport)
1423
self.assertEqual(None, pack.index_sizes)
1424
self.assertEqual(20, len(pack.random_name))
1425
self.assertIsInstance(pack.random_name, str)
1426
self.assertIsInstance(pack.start_time, float)
1429
class TestPacker(TestCaseWithTransport):
1430
"""Tests for the packs repository Packer class."""
1432
# To date, this class has been factored out and nothing new added to it;
1433
# thus there are not yet any tests.