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
52
revision as _mod_revision,
57
from bzrlib.repofmt import knitrepo, weaverepo, pack_repo
60
class TestDefaultFormat(TestCase):
62
def test_get_set_default_format(self):
63
old_default = bzrdir.format_registry.get('default')
64
private_default = old_default().repository_format.__class__
65
old_format = repository.RepositoryFormat.get_default_format()
66
self.assertTrue(isinstance(old_format, private_default))
67
def make_sample_bzrdir():
68
my_bzrdir = bzrdir.BzrDirMetaFormat1()
69
my_bzrdir.repository_format = SampleRepositoryFormat()
71
bzrdir.format_registry.remove('default')
72
bzrdir.format_registry.register('sample', make_sample_bzrdir, '')
73
bzrdir.format_registry.set_default('sample')
74
# creating a repository should now create an instrumented dir.
76
# the default branch format is used by the meta dir format
77
# which is not the default bzrdir format at this point
78
dir = bzrdir.BzrDirMetaFormat1().initialize('memory:///')
79
result = dir.create_repository()
80
self.assertEqual(result, 'A bzr repository dir')
82
bzrdir.format_registry.remove('default')
83
bzrdir.format_registry.remove('sample')
84
bzrdir.format_registry.register('default', old_default, '')
85
self.assertIsInstance(repository.RepositoryFormat.get_default_format(),
89
class SampleRepositoryFormat(repository.RepositoryFormat):
92
this format is initializable, unsupported to aid in testing the
93
open and open(unsupported=True) routines.
96
def get_format_string(self):
97
"""See RepositoryFormat.get_format_string()."""
98
return "Sample .bzr repository format."
100
def initialize(self, a_bzrdir, shared=False):
101
"""Initialize a repository in a BzrDir"""
102
t = a_bzrdir.get_repository_transport(self)
103
t.put_bytes('format', self.get_format_string())
104
return 'A bzr repository dir'
106
def is_supported(self):
109
def open(self, a_bzrdir, _found=False):
110
return "opened repository."
113
class TestRepositoryFormat(TestCaseWithTransport):
114
"""Tests for the Repository format detection used by the bzr meta dir facility.BzrBranchFormat facility."""
116
def test_find_format(self):
117
# is the right format object found for a repository?
118
# create a branch with a few known format objects.
119
# this is not quite the same as
120
self.build_tree(["foo/", "bar/"])
121
def check_format(format, url):
122
dir = format._matchingbzrdir.initialize(url)
123
format.initialize(dir)
124
t = get_transport(url)
125
found_format = repository.RepositoryFormat.find_format(dir)
126
self.failUnless(isinstance(found_format, format.__class__))
127
check_format(weaverepo.RepositoryFormat7(), "bar")
129
def test_find_format_no_repository(self):
130
dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
131
self.assertRaises(errors.NoRepositoryPresent,
132
repository.RepositoryFormat.find_format,
135
def test_find_format_unknown_format(self):
136
dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
137
SampleRepositoryFormat().initialize(dir)
138
self.assertRaises(UnknownFormatError,
139
repository.RepositoryFormat.find_format,
142
def test_register_unregister_format(self):
143
format = SampleRepositoryFormat()
145
dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
147
format.initialize(dir)
148
# register a format for it.
149
repository.RepositoryFormat.register_format(format)
150
# which repository.Open will refuse (not supported)
151
self.assertRaises(UnsupportedFormatError, repository.Repository.open, self.get_url())
152
# but open(unsupported) will work
153
self.assertEqual(format.open(dir), "opened repository.")
154
# unregister the format
155
repository.RepositoryFormat.unregister_format(format)
158
class TestFormat6(TestCaseWithTransport):
160
def test_no_ancestry_weave(self):
161
control = bzrdir.BzrDirFormat6().initialize(self.get_url())
162
repo = weaverepo.RepositoryFormat6().initialize(control)
163
# We no longer need to create the ancestry.weave file
164
# since it is *never* used.
165
self.assertRaises(NoSuchFile,
166
control.transport.get,
169
def test_exposed_versioned_files_are_marked_dirty(self):
170
control = bzrdir.BzrDirFormat6().initialize(self.get_url())
171
repo = weaverepo.RepositoryFormat6().initialize(control)
173
inv = repo.get_inventory_weave()
175
self.assertRaises(errors.OutSideTransaction,
176
inv.add_lines, 'foo', [], [])
179
class TestFormat7(TestCaseWithTransport):
181
def test_disk_layout(self):
182
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
183
repo = weaverepo.RepositoryFormat7().initialize(control)
184
# in case of side effects of locking.
188
# format 'Bazaar-NG Repository format 7'
190
# inventory.weave == empty_weave
191
# empty revision-store directory
192
# empty weaves directory
193
t = control.get_repository_transport(None)
194
self.assertEqualDiff('Bazaar-NG Repository format 7',
195
t.get('format').read())
196
self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
197
self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
198
self.assertEqualDiff('# bzr weave file v5\n'
201
t.get('inventory.weave').read())
203
def test_shared_disk_layout(self):
204
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
205
repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
207
# format 'Bazaar-NG Repository format 7'
208
# inventory.weave == empty_weave
209
# empty revision-store directory
210
# empty weaves directory
211
# a 'shared-storage' marker file.
212
# lock is not present when unlocked
213
t = control.get_repository_transport(None)
214
self.assertEqualDiff('Bazaar-NG Repository format 7',
215
t.get('format').read())
216
self.assertEqualDiff('', t.get('shared-storage').read())
217
self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
218
self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
219
self.assertEqualDiff('# bzr weave file v5\n'
222
t.get('inventory.weave').read())
223
self.assertFalse(t.has('branch-lock'))
225
def test_creates_lockdir(self):
226
"""Make sure it appears to be controlled by a LockDir existence"""
227
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
228
repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
229
t = control.get_repository_transport(None)
230
# TODO: Should check there is a 'lock' toplevel directory,
231
# regardless of contents
232
self.assertFalse(t.has('lock/held/info'))
235
self.assertTrue(t.has('lock/held/info'))
237
# unlock so we don't get a warning about failing to do so
240
def test_uses_lockdir(self):
241
"""repo format 7 actually locks on lockdir"""
242
base_url = self.get_url()
243
control = bzrdir.BzrDirMetaFormat1().initialize(base_url)
244
repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
245
t = control.get_repository_transport(None)
249
# make sure the same lock is created by opening it
250
repo = repository.Repository.open(base_url)
252
self.assertTrue(t.has('lock/held/info'))
254
self.assertFalse(t.has('lock/held/info'))
256
def test_shared_no_tree_disk_layout(self):
257
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
258
repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
259
repo.set_make_working_trees(False)
261
# format 'Bazaar-NG Repository format 7'
263
# inventory.weave == empty_weave
264
# empty revision-store directory
265
# empty weaves directory
266
# a 'shared-storage' marker file.
267
t = control.get_repository_transport(None)
268
self.assertEqualDiff('Bazaar-NG Repository format 7',
269
t.get('format').read())
270
## self.assertEqualDiff('', t.get('lock').read())
271
self.assertEqualDiff('', t.get('shared-storage').read())
272
self.assertEqualDiff('', t.get('no-working-trees').read())
273
repo.set_make_working_trees(True)
274
self.assertFalse(t.has('no-working-trees'))
275
self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
276
self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
277
self.assertEqualDiff('# bzr weave file v5\n'
280
t.get('inventory.weave').read())
282
def test_exposed_versioned_files_are_marked_dirty(self):
283
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
284
repo = weaverepo.RepositoryFormat7().initialize(control)
286
inv = repo.get_inventory_weave()
288
self.assertRaises(errors.OutSideTransaction,
289
inv.add_lines, 'foo', [], [])
292
class TestFormatKnit1(TestCaseWithTransport):
294
def test_disk_layout(self):
295
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
296
repo = knitrepo.RepositoryFormatKnit1().initialize(control)
297
# in case of side effects of locking.
301
# format 'Bazaar-NG Knit Repository Format 1'
302
# lock: is a directory
303
# inventory.weave == empty_weave
304
# empty revision-store directory
305
# empty weaves directory
306
t = control.get_repository_transport(None)
307
self.assertEqualDiff('Bazaar-NG Knit Repository Format 1',
308
t.get('format').read())
309
# XXX: no locks left when unlocked at the moment
310
# self.assertEqualDiff('', t.get('lock').read())
311
self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
314
def assertHasKnit(self, t, knit_name):
315
"""Assert that knit_name exists on t."""
316
self.assertEqualDiff('# bzr knit index 8\n',
317
t.get(knit_name + '.kndx').read())
319
self.assertTrue(t.has(knit_name + '.knit'))
321
def check_knits(self, t):
322
"""check knit content for a repository."""
323
self.assertHasKnit(t, 'inventory')
324
self.assertHasKnit(t, 'revisions')
325
self.assertHasKnit(t, 'signatures')
327
def test_shared_disk_layout(self):
328
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
329
repo = knitrepo.RepositoryFormatKnit1().initialize(control, shared=True)
331
# format 'Bazaar-NG Knit Repository Format 1'
332
# lock: is a directory
333
# inventory.weave == empty_weave
334
# empty revision-store directory
335
# empty weaves directory
336
# a 'shared-storage' marker file.
337
t = control.get_repository_transport(None)
338
self.assertEqualDiff('Bazaar-NG Knit Repository Format 1',
339
t.get('format').read())
340
# XXX: no locks left when unlocked at the moment
341
# self.assertEqualDiff('', t.get('lock').read())
342
self.assertEqualDiff('', t.get('shared-storage').read())
343
self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
346
def test_shared_no_tree_disk_layout(self):
347
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
348
repo = knitrepo.RepositoryFormatKnit1().initialize(control, shared=True)
349
repo.set_make_working_trees(False)
351
# format 'Bazaar-NG Knit Repository Format 1'
353
# inventory.weave == empty_weave
354
# empty revision-store directory
355
# empty weaves directory
356
# a 'shared-storage' marker file.
357
t = control.get_repository_transport(None)
358
self.assertEqualDiff('Bazaar-NG Knit Repository Format 1',
359
t.get('format').read())
360
# XXX: no locks left when unlocked at the moment
361
# self.assertEqualDiff('', t.get('lock').read())
362
self.assertEqualDiff('', t.get('shared-storage').read())
363
self.assertEqualDiff('', t.get('no-working-trees').read())
364
repo.set_make_working_trees(True)
365
self.assertFalse(t.has('no-working-trees'))
366
self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
369
def test_exposed_versioned_files_are_marked_dirty(self):
370
format = bzrdir.BzrDirMetaFormat1()
371
format.repository_format = knitrepo.RepositoryFormatKnit1()
372
repo = self.make_repository('.', format=format)
374
inv = repo.get_inventory_weave()
376
self.assertRaises(errors.OutSideTransaction,
377
inv.add_lines, 'foo', [], [])
379
def test_deserialise_sets_root_revision(self):
380
"""We must have a inventory.root.revision
382
Old versions of the XML5 serializer did not set the revision_id for
383
the whole inventory. So we grab the one from the expected text. Which
384
is valid when the api is not being abused.
386
repo = self.make_repository('.',
387
format=bzrdir.format_registry.get('knit')())
388
inv_xml = '<inventory format="5">\n</inventory>\n'
389
inv = repo.deserialise_inventory('test-rev-id', inv_xml)
390
self.assertEqual('test-rev-id', inv.root.revision)
392
def test_deserialise_uses_global_revision_id(self):
393
"""If it is set, then we re-use the global revision id"""
394
repo = self.make_repository('.',
395
format=bzrdir.format_registry.get('knit')())
396
inv_xml = ('<inventory format="5" revision_id="other-rev-id">\n'
398
# Arguably, the deserialise_inventory should detect a mismatch, and
399
# raise an error, rather than silently using one revision_id over the
401
self.assertRaises(AssertionError, repo.deserialise_inventory,
402
'test-rev-id', inv_xml)
403
inv = repo.deserialise_inventory('other-rev-id', inv_xml)
404
self.assertEqual('other-rev-id', inv.root.revision)
407
class KnitRepositoryStreamTests(test_knit.KnitTests):
408
"""Tests for knitrepo._get_stream_as_bytes."""
410
def test_get_stream_as_bytes(self):
412
k1 = self.make_test_knit()
413
k1.add_lines('text-a', [], test_knit.split_lines(test_knit.TEXT_1))
415
# Serialise it, check the output.
416
bytes = knitrepo._get_stream_as_bytes(k1, ['text-a'])
417
data = bencode.bdecode(bytes)
418
format, record = data
419
self.assertEqual('knit-plain', format)
420
self.assertEqual(['text-a', ['fulltext'], []], record[:3])
421
self.assertRecordContentEqual(k1, 'text-a', record[3])
423
def test_get_stream_as_bytes_all(self):
424
"""Get a serialised data stream for all the records in a knit.
426
Much like test_get_stream_all, except for get_stream_as_bytes.
428
k1 = self.make_test_knit()
429
# Insert the same data as BasicKnitTests.test_knit_join, as they seem
430
# to cover a range of cases (no parents, one parent, multiple parents).
432
('text-a', [], test_knit.TEXT_1),
433
('text-b', ['text-a'], test_knit.TEXT_1),
434
('text-c', [], test_knit.TEXT_1),
435
('text-d', ['text-c'], test_knit.TEXT_1),
436
('text-m', ['text-b', 'text-d'], test_knit.TEXT_1),
438
# This test is actually a bit strict as the order in which they're
439
# returned is not defined. This matches the current (deterministic)
441
expected_data_list = [
442
# version, options, parents
443
('text-a', ['fulltext'], []),
444
('text-b', ['line-delta'], ['text-a']),
445
('text-m', ['line-delta'], ['text-b', 'text-d']),
446
('text-c', ['fulltext'], []),
447
('text-d', ['line-delta'], ['text-c']),
449
for version_id, parents, lines in test_data:
450
k1.add_lines(version_id, parents, test_knit.split_lines(lines))
452
bytes = knitrepo._get_stream_as_bytes(
453
k1, ['text-a', 'text-b', 'text-m', 'text-c', 'text-d', ])
455
data = bencode.bdecode(bytes)
457
self.assertEqual('knit-plain', format)
459
for expected, actual in zip(expected_data_list, data):
460
expected_version = expected[0]
461
expected_options = expected[1]
462
expected_parents = expected[2]
463
version, options, parents, bytes = actual
464
self.assertEqual(expected_version, version)
465
self.assertEqual(expected_options, options)
466
self.assertEqual(expected_parents, parents)
467
self.assertRecordContentEqual(k1, version, bytes)
470
class DummyRepository(object):
471
"""A dummy repository for testing."""
475
def supports_rich_root(self):
479
class InterDummy(repository.InterRepository):
480
"""An inter-repository optimised code path for DummyRepository.
482
This is for use during testing where we use DummyRepository as repositories
483
so that none of the default regsitered inter-repository classes will
488
def is_compatible(repo_source, repo_target):
489
"""InterDummy is compatible with DummyRepository."""
490
return (isinstance(repo_source, DummyRepository) and
491
isinstance(repo_target, DummyRepository))
494
class TestInterRepository(TestCaseWithTransport):
496
def test_get_default_inter_repository(self):
497
# test that the InterRepository.get(repo_a, repo_b) probes
498
# for a inter_repo class where is_compatible(repo_a, repo_b) returns
499
# true and returns a default inter_repo otherwise.
500
# This also tests that the default registered optimised interrepository
501
# classes do not barf inappropriately when a surprising repository type
503
dummy_a = DummyRepository()
504
dummy_b = DummyRepository()
505
self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
507
def assertGetsDefaultInterRepository(self, repo_a, repo_b):
508
"""Asserts that InterRepository.get(repo_a, repo_b) -> the default.
510
The effective default is now InterSameDataRepository because there is
511
no actual sane default in the presence of incompatible data models.
513
inter_repo = repository.InterRepository.get(repo_a, repo_b)
514
self.assertEqual(repository.InterSameDataRepository,
515
inter_repo.__class__)
516
self.assertEqual(repo_a, inter_repo.source)
517
self.assertEqual(repo_b, inter_repo.target)
519
def test_register_inter_repository_class(self):
520
# test that a optimised code path provider - a
521
# InterRepository subclass can be registered and unregistered
522
# and that it is correctly selected when given a repository
523
# pair that it returns true on for the is_compatible static method
525
dummy_a = DummyRepository()
526
dummy_b = DummyRepository()
527
repo = self.make_repository('.')
528
# hack dummies to look like repo somewhat.
529
dummy_a._serializer = repo._serializer
530
dummy_b._serializer = repo._serializer
531
repository.InterRepository.register_optimiser(InterDummy)
533
# we should get the default for something InterDummy returns False
535
self.assertFalse(InterDummy.is_compatible(dummy_a, repo))
536
self.assertGetsDefaultInterRepository(dummy_a, repo)
537
# and we should get an InterDummy for a pair it 'likes'
538
self.assertTrue(InterDummy.is_compatible(dummy_a, dummy_b))
539
inter_repo = repository.InterRepository.get(dummy_a, dummy_b)
540
self.assertEqual(InterDummy, inter_repo.__class__)
541
self.assertEqual(dummy_a, inter_repo.source)
542
self.assertEqual(dummy_b, inter_repo.target)
544
repository.InterRepository.unregister_optimiser(InterDummy)
545
# now we should get the default InterRepository object again.
546
self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
549
class TestInterWeaveRepo(TestCaseWithTransport):
551
def test_is_compatible_and_registered(self):
552
# InterWeaveRepo is compatible when either side
553
# is a format 5/6/7 branch
554
from bzrlib.repofmt import knitrepo, weaverepo
555
formats = [weaverepo.RepositoryFormat5(),
556
weaverepo.RepositoryFormat6(),
557
weaverepo.RepositoryFormat7()]
558
incompatible_formats = [weaverepo.RepositoryFormat4(),
559
knitrepo.RepositoryFormatKnit1(),
561
repo_a = self.make_repository('a')
562
repo_b = self.make_repository('b')
563
is_compatible = repository.InterWeaveRepo.is_compatible
564
for source in incompatible_formats:
565
# force incompatible left then right
566
repo_a._format = source
567
repo_b._format = formats[0]
568
self.assertFalse(is_compatible(repo_a, repo_b))
569
self.assertFalse(is_compatible(repo_b, repo_a))
570
for source in formats:
571
repo_a._format = source
572
for target in formats:
573
repo_b._format = target
574
self.assertTrue(is_compatible(repo_a, repo_b))
575
self.assertEqual(repository.InterWeaveRepo,
576
repository.InterRepository.get(repo_a,
580
class TestInterRemoteToOther(TestCaseWithTransport):
582
def make_remote_repository(self, path, backing_format=None):
583
"""Make a RemoteRepository object backed by a real repository that will
584
be created at the given path."""
585
self.make_repository(path, format=backing_format)
586
smart_server = server.SmartTCPServer_for_testing()
588
remote_transport = get_transport(smart_server.get_url()).clone(path)
589
self.addCleanup(smart_server.tearDown)
590
remote_bzrdir = bzrdir.BzrDir.open_from_transport(remote_transport)
591
remote_repo = remote_bzrdir.open_repository()
594
def test_is_compatible_same_format(self):
595
"""InterRemoteToOther is compatible with a remote repository and a
596
second repository that have the same format."""
597
local_repo = self.make_repository('local')
598
remote_repo = self.make_remote_repository('remote')
599
is_compatible = repository.InterRemoteToOther.is_compatible
601
is_compatible(remote_repo, local_repo),
602
"InterRemoteToOther(%r, %r) is false" % (remote_repo, local_repo))
604
def test_is_incompatible_different_format(self):
605
local_repo = self.make_repository('local', 'dirstate')
606
remote_repo = self.make_remote_repository('a', 'dirstate-with-subtree')
607
is_compatible = repository.InterRemoteToOther.is_compatible
609
is_compatible(remote_repo, local_repo),
610
"InterRemoteToOther(%r, %r) is true" % (local_repo, remote_repo))
612
def test_is_incompatible_different_format_both_remote(self):
613
remote_repo_a = self.make_remote_repository(
614
'a', 'dirstate-with-subtree')
615
remote_repo_b = self.make_remote_repository('b', 'dirstate')
616
is_compatible = repository.InterRemoteToOther.is_compatible
618
is_compatible(remote_repo_a, remote_repo_b),
619
"InterRemoteToOther(%r, %r) is true"
620
% (remote_repo_a, remote_repo_b))
623
class TestRepositoryConverter(TestCaseWithTransport):
625
def test_convert_empty(self):
626
t = get_transport(self.get_url('.'))
627
t.mkdir('repository')
628
repo_dir = bzrdir.BzrDirMetaFormat1().initialize('repository')
629
repo = weaverepo.RepositoryFormat7().initialize(repo_dir)
630
target_format = knitrepo.RepositoryFormatKnit1()
631
converter = repository.CopyConverter(target_format)
632
pb = bzrlib.ui.ui_factory.nested_progress_bar()
634
converter.convert(repo, pb)
637
repo = repo_dir.open_repository()
638
self.assertTrue(isinstance(target_format, repo._format.__class__))
641
class TestMisc(TestCase):
643
def test_unescape_xml(self):
644
"""We get some kind of error when malformed entities are passed"""
645
self.assertRaises(KeyError, repository._unescape_xml, 'foo&bar;')
648
class TestRepositoryFormatKnit3(TestCaseWithTransport):
650
def test_convert(self):
651
"""Ensure the upgrade adds weaves for roots"""
652
format = bzrdir.BzrDirMetaFormat1()
653
format.repository_format = knitrepo.RepositoryFormatKnit1()
654
tree = self.make_branch_and_tree('.', format)
655
tree.commit("Dull commit", rev_id="dull")
656
revision_tree = tree.branch.repository.revision_tree('dull')
657
self.assertRaises(errors.NoSuchFile, revision_tree.get_file_lines,
658
revision_tree.inventory.root.file_id)
659
format = bzrdir.BzrDirMetaFormat1()
660
format.repository_format = knitrepo.RepositoryFormatKnit3()
661
upgrade.Convert('.', format)
662
tree = workingtree.WorkingTree.open('.')
663
revision_tree = tree.branch.repository.revision_tree('dull')
664
revision_tree.get_file_lines(revision_tree.inventory.root.file_id)
665
tree.commit("Another dull commit", rev_id='dull2')
666
revision_tree = tree.branch.repository.revision_tree('dull2')
667
self.assertEqual('dull', revision_tree.inventory.root.revision)
669
def test_exposed_versioned_files_are_marked_dirty(self):
670
format = bzrdir.BzrDirMetaFormat1()
671
format.repository_format = knitrepo.RepositoryFormatKnit3()
672
repo = self.make_repository('.', format=format)
674
inv = repo.get_inventory_weave()
676
self.assertRaises(errors.OutSideTransaction,
677
inv.add_lines, 'foo', [], [])
680
class TestWithBrokenRepo(TestCaseWithTransport):
681
"""These tests seem to be more appropriate as interface tests?"""
683
def make_broken_repository(self):
684
# XXX: This function is borrowed from Aaron's "Reconcile can fix bad
685
# parent references" branch which is due to land in bzr.dev soon. Once
686
# it does, this duplication should be removed.
687
repo = self.make_repository('broken-repo')
691
cleanups.append(repo.unlock)
692
repo.start_write_group()
693
cleanups.append(repo.commit_write_group)
694
# make rev1a: A well-formed revision, containing 'file1'
695
inv = inventory.Inventory(revision_id='rev1a')
696
inv.root.revision = 'rev1a'
697
self.add_file(repo, inv, 'file1', 'rev1a', [])
698
repo.add_inventory('rev1a', inv, [])
699
revision = _mod_revision.Revision('rev1a',
700
committer='jrandom@example.com', timestamp=0,
701
inventory_sha1='', timezone=0, message='foo', parent_ids=[])
702
repo.add_revision('rev1a',revision, inv)
704
# make rev1b, which has no Revision, but has an Inventory, and
706
inv = inventory.Inventory(revision_id='rev1b')
707
inv.root.revision = 'rev1b'
708
self.add_file(repo, inv, 'file1', 'rev1b', [])
709
repo.add_inventory('rev1b', inv, [])
711
# make rev2, with file1 and file2
713
# file1 has 'rev1b' as an ancestor, even though this is not
714
# mentioned by 'rev1a', making it an unreferenced ancestor
715
inv = inventory.Inventory()
716
self.add_file(repo, inv, 'file1', 'rev2', ['rev1a', 'rev1b'])
717
self.add_file(repo, inv, 'file2', 'rev2', [])
718
self.add_revision(repo, 'rev2', inv, ['rev1a'])
720
# make ghost revision rev1c
721
inv = inventory.Inventory()
722
self.add_file(repo, inv, 'file2', 'rev1c', [])
724
# make rev3 with file2
725
# file2 refers to 'rev1c', which is a ghost in this repository, so
726
# file2 cannot have rev1c as its ancestor.
727
inv = inventory.Inventory()
728
self.add_file(repo, inv, 'file2', 'rev3', ['rev1c'])
729
self.add_revision(repo, 'rev3', inv, ['rev1c'])
732
for cleanup in reversed(cleanups):
735
def add_revision(self, repo, revision_id, inv, parent_ids):
736
inv.revision_id = revision_id
737
inv.root.revision = revision_id
738
repo.add_inventory(revision_id, inv, parent_ids)
739
revision = _mod_revision.Revision(revision_id,
740
committer='jrandom@example.com', timestamp=0, inventory_sha1='',
741
timezone=0, message='foo', parent_ids=parent_ids)
742
repo.add_revision(revision_id,revision, inv)
744
def add_file(self, repo, inv, filename, revision, parents):
745
file_id = filename + '-id'
746
entry = inventory.InventoryFile(file_id, filename, 'TREE_ROOT')
747
entry.revision = revision
750
vf = repo.weave_store.get_weave_or_empty(file_id,
751
repo.get_transaction())
752
vf.add_lines(revision, parents, ['line\n'])
754
def test_insert_from_broken_repo(self):
755
"""Inserting a data stream from a broken repository won't silently
756
corrupt the target repository.
758
broken_repo = self.make_broken_repository()
759
empty_repo = self.make_repository('empty-repo')
760
stream = broken_repo.get_data_stream(['rev1a', 'rev2', 'rev3'])
761
empty_repo.lock_write()
762
self.addCleanup(empty_repo.unlock)
763
empty_repo.start_write_group()
766
errors.KnitCorrupt, empty_repo.insert_data_stream, stream)
768
empty_repo.abort_write_group()
771
class TestKnitPackNoSubtrees(TestCaseWithTransport):
773
def get_format(self):
774
return bzrdir.format_registry.make_bzrdir('pack-0.92')
776
def test_disk_layout(self):
777
format = self.get_format()
778
repo = self.make_repository('.', format=format)
779
# in case of side effects of locking.
782
t = repo.bzrdir.get_repository_transport(None)
784
# XXX: no locks left when unlocked at the moment
785
# self.assertEqualDiff('', t.get('lock').read())
786
self.check_databases(t)
788
def check_format(self, t):
789
self.assertEqualDiff(
790
"Bazaar pack repository format 1 (needs bzr 0.92)\n",
791
t.get('format').read())
793
def assertHasKndx(self, t, knit_name):
794
"""Assert that knit_name exists on t."""
795
self.assertEqualDiff('# bzr knit index 8\n',
796
t.get(knit_name + '.kndx').read())
798
def assertHasNoKndx(self, t, knit_name):
799
"""Assert that knit_name has no index on t."""
800
self.assertFalse(t.has(knit_name + '.kndx'))
802
def assertHasNoKnit(self, t, knit_name):
803
"""Assert that knit_name exists on t."""
805
self.assertFalse(t.has(knit_name + '.knit'))
807
def check_databases(self, t):
808
"""check knit content for a repository."""
809
# check conversion worked
810
self.assertHasNoKndx(t, 'inventory')
811
self.assertHasNoKnit(t, 'inventory')
812
self.assertHasNoKndx(t, 'revisions')
813
self.assertHasNoKnit(t, 'revisions')
814
self.assertHasNoKndx(t, 'signatures')
815
self.assertHasNoKnit(t, 'signatures')
816
self.assertFalse(t.has('knits'))
817
# revision-indexes file-container directory
819
list(GraphIndex(t, 'pack-names', None).iter_all_entries()))
820
self.assertTrue(S_ISDIR(t.stat('packs').st_mode))
821
self.assertTrue(S_ISDIR(t.stat('upload').st_mode))
822
self.assertTrue(S_ISDIR(t.stat('indices').st_mode))
823
self.assertTrue(S_ISDIR(t.stat('obsolete_packs').st_mode))
825
def test_shared_disk_layout(self):
826
format = self.get_format()
827
repo = self.make_repository('.', shared=True, format=format)
829
t = repo.bzrdir.get_repository_transport(None)
831
# XXX: no locks left when unlocked at the moment
832
# self.assertEqualDiff('', t.get('lock').read())
833
# We should have a 'shared-storage' marker file.
834
self.assertEqualDiff('', t.get('shared-storage').read())
835
self.check_databases(t)
837
def test_shared_no_tree_disk_layout(self):
838
format = self.get_format()
839
repo = self.make_repository('.', shared=True, format=format)
840
repo.set_make_working_trees(False)
842
t = repo.bzrdir.get_repository_transport(None)
844
# XXX: no locks left when unlocked at the moment
845
# self.assertEqualDiff('', t.get('lock').read())
846
# We should have a 'shared-storage' marker file.
847
self.assertEqualDiff('', t.get('shared-storage').read())
848
# We should have a marker for the no-working-trees flag.
849
self.assertEqualDiff('', t.get('no-working-trees').read())
850
# The marker should go when we toggle the setting.
851
repo.set_make_working_trees(True)
852
self.assertFalse(t.has('no-working-trees'))
853
self.check_databases(t)
855
def test_adding_revision_creates_pack_indices(self):
856
format = self.get_format()
857
tree = self.make_branch_and_tree('.', format=format)
858
trans = tree.branch.repository.bzrdir.get_repository_transport(None)
860
list(GraphIndex(trans, 'pack-names', None).iter_all_entries()))
861
tree.commit('foobarbaz')
862
index = GraphIndex(trans, 'pack-names', None)
863
index_nodes = list(index.iter_all_entries())
864
self.assertEqual(1, len(index_nodes))
865
node = index_nodes[0]
867
# the pack sizes should be listed in the index
869
sizes = [int(digits) for digits in pack_value.split(' ')]
870
for size, suffix in zip(sizes, ['.rix', '.iix', '.tix', '.six']):
871
stat = trans.stat('indices/%s%s' % (name, suffix))
872
self.assertEqual(size, stat.st_size)
874
def test_pulling_nothing_leads_to_no_new_names(self):
875
format = self.get_format()
876
tree1 = self.make_branch_and_tree('1', format=format)
877
tree2 = self.make_branch_and_tree('2', format=format)
878
tree1.branch.repository.fetch(tree2.branch.repository)
879
trans = tree1.branch.repository.bzrdir.get_repository_transport(None)
881
list(GraphIndex(trans, 'pack-names', None).iter_all_entries()))
883
def test_commit_across_pack_shape_boundary_autopacks(self):
884
format = self.get_format()
885
tree = self.make_branch_and_tree('.', format=format)
886
trans = tree.branch.repository.bzrdir.get_repository_transport(None)
887
# This test could be a little cheaper by replacing the packs
888
# attribute on the repository to allow a different pack distribution
889
# and max packs policy - so we are checking the policy is honoured
890
# in the test. But for now 11 commits is not a big deal in a single
893
tree.commit('commit %s' % x)
894
# there should be 9 packs:
895
index = GraphIndex(trans, 'pack-names', None)
896
self.assertEqual(9, len(list(index.iter_all_entries())))
897
# insert some files in obsolete_packs which should be removed by pack.
898
trans.put_bytes('obsolete_packs/foo', '123')
899
trans.put_bytes('obsolete_packs/bar', '321')
900
# committing one more should coalesce to 1 of 10.
901
tree.commit('commit triggering pack')
902
index = GraphIndex(trans, 'pack-names', None)
903
self.assertEqual(1, len(list(index.iter_all_entries())))
904
# packing should not damage data
905
tree = tree.bzrdir.open_workingtree()
906
check_result = tree.branch.repository.check(
907
[tree.branch.last_revision()])
908
# We should have 50 (10x5) files in the obsolete_packs directory.
909
obsolete_files = list(trans.list_dir('obsolete_packs'))
910
self.assertFalse('foo' in obsolete_files)
911
self.assertFalse('bar' in obsolete_files)
912
self.assertEqual(50, len(obsolete_files))
913
# XXX: Todo check packs obsoleted correctly - old packs and indices
914
# in the obsolete_packs directory.
915
large_pack_name = list(index.iter_all_entries())[0][1][0]
916
# finally, committing again should not touch the large pack.
917
tree.commit('commit not triggering pack')
918
index = GraphIndex(trans, 'pack-names', None)
919
self.assertEqual(2, len(list(index.iter_all_entries())))
920
pack_names = [node[1][0] for node in index.iter_all_entries()]
921
self.assertTrue(large_pack_name in pack_names)
923
def test_pack_after_two_commits_packs_everything(self):
924
format = self.get_format()
925
tree = self.make_branch_and_tree('.', format=format)
926
trans = tree.branch.repository.bzrdir.get_repository_transport(None)
928
tree.commit('more work')
929
tree.branch.repository.pack()
930
# there should be 1 pack:
931
index = GraphIndex(trans, 'pack-names', None)
932
self.assertEqual(1, len(list(index.iter_all_entries())))
933
self.assertEqual(2, len(tree.branch.repository.all_revision_ids()))
935
def test_pack_layout(self):
936
format = self.get_format()
937
tree = self.make_branch_and_tree('.', format=format)
938
trans = tree.branch.repository.bzrdir.get_repository_transport(None)
939
tree.commit('start', rev_id='1')
940
tree.commit('more work', rev_id='2')
941
tree.branch.repository.pack()
943
self.addCleanup(tree.unlock)
944
pack = tree.branch.repository._pack_collection.get_pack_by_name(
945
tree.branch.repository._pack_collection.names()[0])
946
# revision access tends to be tip->ancestor, so ordering that way on
947
# disk is a good idea.
948
for _1, key, val, refs in pack.revision_index.iter_all_entries():
950
pos_1 = int(val[1:].split()[0])
952
pos_2 = int(val[1:].split()[0])
953
self.assertTrue(pos_2 < pos_1)
955
def test_pack_repositories_support_multiple_write_locks(self):
956
format = self.get_format()
957
self.make_repository('.', shared=True, format=format)
958
r1 = repository.Repository.open('.')
959
r2 = repository.Repository.open('.')
961
self.addCleanup(r1.unlock)
965
def _add_text(self, repo, fileid):
966
"""Add a text to the repository within a write group."""
967
vf =repo.weave_store.get_weave(fileid, repo.get_transaction())
968
vf.add_lines('samplerev+' + fileid, [], [])
970
def test_concurrent_writers_merge_new_packs(self):
971
format = self.get_format()
972
self.make_repository('.', shared=True, format=format)
973
r1 = repository.Repository.open('.')
974
r2 = repository.Repository.open('.')
977
# access enough data to load the names list
978
list(r1.all_revision_ids())
981
# access enough data to load the names list
982
list(r2.all_revision_ids())
983
r1.start_write_group()
985
r2.start_write_group()
987
self._add_text(r1, 'fileidr1')
988
self._add_text(r2, 'fileidr2')
990
r2.abort_write_group()
993
r1.abort_write_group()
995
# both r1 and r2 have open write groups with data in them
996
# created while the other's write group was open.
997
# Commit both which requires a merge to the pack-names.
999
r1.commit_write_group()
1001
r1.abort_write_group()
1002
r2.abort_write_group()
1004
r2.commit_write_group()
1005
# tell r1 to reload from disk
1006
r1._pack_collection.reset()
1007
# Now both repositories should know about both names
1008
r1._pack_collection.ensure_loaded()
1009
r2._pack_collection.ensure_loaded()
1010
self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
1011
self.assertEqual(2, len(r1._pack_collection.names()))
1017
def test_concurrent_writer_second_preserves_dropping_a_pack(self):
1018
format = self.get_format()
1019
self.make_repository('.', shared=True, format=format)
1020
r1 = repository.Repository.open('.')
1021
r2 = repository.Repository.open('.')
1022
# add a pack to drop
1025
r1.start_write_group()
1027
self._add_text(r1, 'fileidr1')
1029
r1.abort_write_group()
1032
r1.commit_write_group()
1033
r1._pack_collection.ensure_loaded()
1034
name_to_drop = r1._pack_collection.all_packs()[0].name
1039
# access enough data to load the names list
1040
list(r1.all_revision_ids())
1043
# access enough data to load the names list
1044
list(r2.all_revision_ids())
1045
r1._pack_collection.ensure_loaded()
1047
r2.start_write_group()
1049
# in r1, drop the pack
1050
r1._pack_collection._remove_pack_from_memory(
1051
r1._pack_collection.get_pack_by_name(name_to_drop))
1053
self._add_text(r2, 'fileidr2')
1055
r2.abort_write_group()
1058
r1._pack_collection.reset()
1060
# r1 has a changed names list, and r2 an open write groups with
1062
# save r1, and then commit the r2 write group, which requires a
1063
# merge to the pack-names, which should not reinstate
1066
r1._pack_collection._save_pack_names()
1067
r1._pack_collection.reset()
1069
r2.abort_write_group()
1072
r2.commit_write_group()
1074
r2.abort_write_group()
1076
# Now both repositories should now about just one name.
1077
r1._pack_collection.ensure_loaded()
1078
r2._pack_collection.ensure_loaded()
1079
self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
1080
self.assertEqual(1, len(r1._pack_collection.names()))
1081
self.assertFalse(name_to_drop in r1._pack_collection.names())
1087
def test_lock_write_does_not_physically_lock(self):
1088
repo = self.make_repository('.', format=self.get_format())
1090
self.addCleanup(repo.unlock)
1091
self.assertFalse(repo.get_physical_lock_status())
1093
def prepare_for_break_lock(self):
1094
# Setup the global ui factory state so that a break-lock method call
1095
# will find usable input in the input stream.
1096
old_factory = bzrlib.ui.ui_factory
1097
def restoreFactory():
1098
bzrlib.ui.ui_factory = old_factory
1099
self.addCleanup(restoreFactory)
1100
bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
1101
bzrlib.ui.ui_factory.stdin = StringIO("y\n")
1103
def test_break_lock_breaks_physical_lock(self):
1104
repo = self.make_repository('.', format=self.get_format())
1105
repo._pack_collection.lock_names()
1106
repo2 = repository.Repository.open('.')
1107
self.assertTrue(repo.get_physical_lock_status())
1108
self.prepare_for_break_lock()
1110
self.assertFalse(repo.get_physical_lock_status())
1112
def test_broken_physical_locks_error_on__unlock_names_lock(self):
1113
repo = self.make_repository('.', format=self.get_format())
1114
repo._pack_collection.lock_names()
1115
self.assertTrue(repo.get_physical_lock_status())
1116
repo2 = repository.Repository.open('.')
1117
self.prepare_for_break_lock()
1119
self.assertRaises(errors.LockBroken, repo._pack_collection._unlock_names)
1121
def test_fetch_without_find_ghosts_ignores_ghosts(self):
1122
# we want two repositories at this point:
1123
# one with a revision that is a ghost in the other
1125
# 'ghost' is present in has_ghost, 'ghost' is absent in 'missing_ghost'.
1126
# 'references' is present in both repositories, and 'tip' is present
1127
# just in has_ghost.
1128
# has_ghost missing_ghost
1129
#------------------------------
1131
# 'references' 'references'
1133
# In this test we fetch 'tip' which should not fetch 'ghost'
1134
has_ghost = self.make_repository('has_ghost', format=self.get_format())
1135
missing_ghost = self.make_repository('missing_ghost',
1136
format=self.get_format())
1138
def add_commit(repo, revision_id, parent_ids):
1140
repo.start_write_group()
1141
inv = inventory.Inventory(revision_id=revision_id)
1142
inv.root.revision = revision_id
1143
root_id = inv.root.file_id
1144
sha1 = repo.add_inventory(revision_id, inv, [])
1145
vf = repo.weave_store.get_weave_or_empty(root_id,
1146
repo.get_transaction())
1147
vf.add_lines(revision_id, [], [])
1148
rev = bzrlib.revision.Revision(timestamp=0,
1150
committer="Foo Bar <foo@example.com>",
1152
inventory_sha1=sha1,
1153
revision_id=revision_id)
1154
rev.parent_ids = parent_ids
1155
repo.add_revision(revision_id, rev)
1156
repo.commit_write_group()
1158
add_commit(has_ghost, 'ghost', [])
1159
add_commit(has_ghost, 'references', ['ghost'])
1160
add_commit(missing_ghost, 'references', ['ghost'])
1161
add_commit(has_ghost, 'tip', ['references'])
1162
missing_ghost.fetch(has_ghost, 'tip')
1163
# missing ghost now has tip and not ghost.
1164
rev = missing_ghost.get_revision('tip')
1165
inv = missing_ghost.get_inventory('tip')
1166
self.assertRaises(errors.NoSuchRevision,
1167
missing_ghost.get_revision, 'ghost')
1168
self.assertRaises(errors.RevisionNotPresent,
1169
missing_ghost.get_inventory, 'ghost')
1172
class TestKnitPackSubtrees(TestKnitPackNoSubtrees):
1174
def get_format(self):
1175
return bzrdir.format_registry.make_bzrdir(
1176
'pack-0.92-subtree')
1178
def check_format(self, t):
1179
self.assertEqualDiff(
1180
"Bazaar pack repository format 1 with subtree support (needs bzr 0.92)\n",
1181
t.get('format').read())
1184
class TestRepositoryPackCollection(TestCaseWithTransport):
1186
def get_format(self):
1187
return bzrdir.format_registry.make_bzrdir('pack-0.92')
1189
def test__max_pack_count(self):
1190
"""The maximum pack count is a function of the number of revisions."""
1191
format = self.get_format()
1192
repo = self.make_repository('.', format=format)
1193
packs = repo._pack_collection
1194
# no revisions - one pack, so that we can have a revision free repo
1195
# without it blowing up
1196
self.assertEqual(1, packs._max_pack_count(0))
1197
# after that the sum of the digits, - check the first 1-9
1198
self.assertEqual(1, packs._max_pack_count(1))
1199
self.assertEqual(2, packs._max_pack_count(2))
1200
self.assertEqual(3, packs._max_pack_count(3))
1201
self.assertEqual(4, packs._max_pack_count(4))
1202
self.assertEqual(5, packs._max_pack_count(5))
1203
self.assertEqual(6, packs._max_pack_count(6))
1204
self.assertEqual(7, packs._max_pack_count(7))
1205
self.assertEqual(8, packs._max_pack_count(8))
1206
self.assertEqual(9, packs._max_pack_count(9))
1207
# check the boundary cases with two digits for the next decade
1208
self.assertEqual(1, packs._max_pack_count(10))
1209
self.assertEqual(2, packs._max_pack_count(11))
1210
self.assertEqual(10, packs._max_pack_count(19))
1211
self.assertEqual(2, packs._max_pack_count(20))
1212
self.assertEqual(3, packs._max_pack_count(21))
1213
# check some arbitrary big numbers
1214
self.assertEqual(25, packs._max_pack_count(112894))
1216
def test_pack_distribution_zero(self):
1217
format = self.get_format()
1218
repo = self.make_repository('.', format=format)
1219
packs = repo._pack_collection
1220
self.assertEqual([0], packs.pack_distribution(0))
1222
def test_ensure_loaded_unlocked(self):
1223
format = self.get_format()
1224
repo = self.make_repository('.', format=format)
1225
self.assertRaises(errors.ObjectNotLocked,
1226
repo._pack_collection.ensure_loaded)
1228
def test_pack_distribution_one_to_nine(self):
1229
format = self.get_format()
1230
repo = self.make_repository('.', format=format)
1231
packs = repo._pack_collection
1232
self.assertEqual([1],
1233
packs.pack_distribution(1))
1234
self.assertEqual([1, 1],
1235
packs.pack_distribution(2))
1236
self.assertEqual([1, 1, 1],
1237
packs.pack_distribution(3))
1238
self.assertEqual([1, 1, 1, 1],
1239
packs.pack_distribution(4))
1240
self.assertEqual([1, 1, 1, 1, 1],
1241
packs.pack_distribution(5))
1242
self.assertEqual([1, 1, 1, 1, 1, 1],
1243
packs.pack_distribution(6))
1244
self.assertEqual([1, 1, 1, 1, 1, 1, 1],
1245
packs.pack_distribution(7))
1246
self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1],
1247
packs.pack_distribution(8))
1248
self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1, 1],
1249
packs.pack_distribution(9))
1251
def test_pack_distribution_stable_at_boundaries(self):
1252
"""When there are multi-rev packs the counts are stable."""
1253
format = self.get_format()
1254
repo = self.make_repository('.', format=format)
1255
packs = repo._pack_collection
1257
self.assertEqual([10], packs.pack_distribution(10))
1258
self.assertEqual([10, 1], packs.pack_distribution(11))
1259
self.assertEqual([10, 10], packs.pack_distribution(20))
1260
self.assertEqual([10, 10, 1], packs.pack_distribution(21))
1262
self.assertEqual([100], packs.pack_distribution(100))
1263
self.assertEqual([100, 1], packs.pack_distribution(101))
1264
self.assertEqual([100, 10, 1], packs.pack_distribution(111))
1265
self.assertEqual([100, 100], packs.pack_distribution(200))
1266
self.assertEqual([100, 100, 1], packs.pack_distribution(201))
1267
self.assertEqual([100, 100, 10, 1], packs.pack_distribution(211))
1269
def test_plan_pack_operations_2009_revisions_skip_all_packs(self):
1270
format = self.get_format()
1271
repo = self.make_repository('.', format=format)
1272
packs = repo._pack_collection
1273
existing_packs = [(2000, "big"), (9, "medium")]
1274
# rev count - 2009 -> 2x1000 + 9x1
1275
pack_operations = packs.plan_autopack_combinations(
1276
existing_packs, [1000, 1000, 1, 1, 1, 1, 1, 1, 1, 1, 1])
1277
self.assertEqual([], pack_operations)
1279
def test_plan_pack_operations_2010_revisions_skip_all_packs(self):
1280
format = self.get_format()
1281
repo = self.make_repository('.', format=format)
1282
packs = repo._pack_collection
1283
existing_packs = [(2000, "big"), (9, "medium"), (1, "single")]
1284
# rev count - 2010 -> 2x1000 + 1x10
1285
pack_operations = packs.plan_autopack_combinations(
1286
existing_packs, [1000, 1000, 10])
1287
self.assertEqual([], pack_operations)
1289
def test_plan_pack_operations_2010_combines_smallest_two(self):
1290
format = self.get_format()
1291
repo = self.make_repository('.', format=format)
1292
packs = repo._pack_collection
1293
existing_packs = [(1999, "big"), (9, "medium"), (1, "single2"),
1295
# rev count - 2010 -> 2x1000 + 1x10 (3)
1296
pack_operations = packs.plan_autopack_combinations(
1297
existing_packs, [1000, 1000, 10])
1298
self.assertEqual([[2, ["single2", "single1"]], [0, []]], pack_operations)
1300
def test_all_packs_none(self):
1301
format = self.get_format()
1302
tree = self.make_branch_and_tree('.', format=format)
1304
self.addCleanup(tree.unlock)
1305
packs = tree.branch.repository._pack_collection
1306
packs.ensure_loaded()
1307
self.assertEqual([], packs.all_packs())
1309
def test_all_packs_one(self):
1310
format = self.get_format()
1311
tree = self.make_branch_and_tree('.', format=format)
1312
tree.commit('start')
1314
self.addCleanup(tree.unlock)
1315
packs = tree.branch.repository._pack_collection
1316
packs.ensure_loaded()
1318
packs.get_pack_by_name(packs.names()[0])],
1321
def test_all_packs_two(self):
1322
format = self.get_format()
1323
tree = self.make_branch_and_tree('.', format=format)
1324
tree.commit('start')
1325
tree.commit('continue')
1327
self.addCleanup(tree.unlock)
1328
packs = tree.branch.repository._pack_collection
1329
packs.ensure_loaded()
1331
packs.get_pack_by_name(packs.names()[0]),
1332
packs.get_pack_by_name(packs.names()[1]),
1333
], packs.all_packs())
1335
def test_get_pack_by_name(self):
1336
format = self.get_format()
1337
tree = self.make_branch_and_tree('.', format=format)
1338
tree.commit('start')
1340
self.addCleanup(tree.unlock)
1341
packs = tree.branch.repository._pack_collection
1342
packs.ensure_loaded()
1343
name = packs.names()[0]
1344
pack_1 = packs.get_pack_by_name(name)
1345
# the pack should be correctly initialised
1346
rev_index = GraphIndex(packs._index_transport, name + '.rix',
1347
packs._names[name][0])
1348
inv_index = GraphIndex(packs._index_transport, name + '.iix',
1349
packs._names[name][1])
1350
txt_index = GraphIndex(packs._index_transport, name + '.tix',
1351
packs._names[name][2])
1352
sig_index = GraphIndex(packs._index_transport, name + '.six',
1353
packs._names[name][3])
1354
self.assertEqual(pack_repo.ExistingPack(packs._pack_transport,
1355
name, rev_index, inv_index, txt_index, sig_index), pack_1)
1356
# and the same instance should be returned on successive calls.
1357
self.assertTrue(pack_1 is packs.get_pack_by_name(name))
1360
class TestPack(TestCaseWithTransport):
1361
"""Tests for the Pack object."""
1363
def assertCurrentlyEqual(self, left, right):
1364
self.assertTrue(left == right)
1365
self.assertTrue(right == left)
1366
self.assertFalse(left != right)
1367
self.assertFalse(right != left)
1369
def assertCurrentlyNotEqual(self, left, right):
1370
self.assertFalse(left == right)
1371
self.assertFalse(right == left)
1372
self.assertTrue(left != right)
1373
self.assertTrue(right != left)
1375
def test___eq____ne__(self):
1376
left = pack_repo.ExistingPack('', '', '', '', '', '')
1377
right = pack_repo.ExistingPack('', '', '', '', '', '')
1378
self.assertCurrentlyEqual(left, right)
1379
# change all attributes and ensure equality changes as we do.
1380
left.revision_index = 'a'
1381
self.assertCurrentlyNotEqual(left, right)
1382
right.revision_index = 'a'
1383
self.assertCurrentlyEqual(left, right)
1384
left.inventory_index = 'a'
1385
self.assertCurrentlyNotEqual(left, right)
1386
right.inventory_index = 'a'
1387
self.assertCurrentlyEqual(left, right)
1388
left.text_index = 'a'
1389
self.assertCurrentlyNotEqual(left, right)
1390
right.text_index = 'a'
1391
self.assertCurrentlyEqual(left, right)
1392
left.signature_index = 'a'
1393
self.assertCurrentlyNotEqual(left, right)
1394
right.signature_index = 'a'
1395
self.assertCurrentlyEqual(left, right)
1397
self.assertCurrentlyNotEqual(left, right)
1399
self.assertCurrentlyEqual(left, right)
1400
left.transport = 'a'
1401
self.assertCurrentlyNotEqual(left, right)
1402
right.transport = 'a'
1403
self.assertCurrentlyEqual(left, right)
1405
def test_file_name(self):
1406
pack = pack_repo.ExistingPack('', 'a_name', '', '', '', '')
1407
self.assertEqual('a_name.pack', pack.file_name())
1410
class TestNewPack(TestCaseWithTransport):
1411
"""Tests for pack_repo.NewPack."""
1413
def test_new_instance_attributes(self):
1414
upload_transport = self.get_transport('upload')
1415
pack_transport = self.get_transport('pack')
1416
index_transport = self.get_transport('index')
1417
upload_transport.mkdir('.')
1418
pack = pack_repo.NewPack(upload_transport, index_transport,
1420
self.assertIsInstance(pack.revision_index, InMemoryGraphIndex)
1421
self.assertIsInstance(pack.inventory_index, InMemoryGraphIndex)
1422
self.assertIsInstance(pack._hash, type(md5.new()))
1423
self.assertTrue(pack.upload_transport is upload_transport)
1424
self.assertTrue(pack.index_transport is index_transport)
1425
self.assertTrue(pack.pack_transport is pack_transport)
1426
self.assertEqual(None, pack.index_sizes)
1427
self.assertEqual(20, len(pack.random_name))
1428
self.assertIsInstance(pack.random_name, str)
1429
self.assertIsInstance(pack.start_time, float)
1432
class TestPacker(TestCaseWithTransport):
1433
"""Tests for the packs repository Packer class."""
1435
# To date, this class has been factored out and nothing new added to it;
1436
# thus there are not yet any tests.
1439
class TestInterDifferingSerializer(TestCaseWithTransport):
1441
def test_progress_bar(self):
1442
tree = self.make_branch_and_tree('tree')
1443
tree.commit('rev1', rev_id='rev-1')
1444
tree.commit('rev2', rev_id='rev-2')
1445
tree.commit('rev3', rev_id='rev-3')
1446
repo = self.make_repository('repo')
1447
inter_repo = repository.InterDifferingSerializer(
1448
tree.branch.repository, repo)
1449
pb = progress.InstrumentedProgress(to_file=StringIO())
1450
pb.never_throttle = True
1451
inter_repo.fetch('rev-1', pb)
1452
self.assertEqual('Transferring revisions', pb.last_msg)
1453
self.assertEqual(1, pb.last_cnt)
1454
self.assertEqual(1, pb.last_total)
1455
inter_repo.fetch('rev-3', pb)
1456
self.assertEqual(2, pb.last_cnt)
1457
self.assertEqual(2, pb.last_total)