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
inv = repo.deserialise_inventory('test-rev-id', inv_xml)
402
self.assertEqual('other-rev-id', inv.root.revision)
405
class KnitRepositoryStreamTests(test_knit.KnitTests):
406
"""Tests for knitrepo._get_stream_as_bytes."""
408
def test_get_stream_as_bytes(self):
410
k1 = self.make_test_knit()
411
k1.add_lines('text-a', [], test_knit.split_lines(test_knit.TEXT_1))
413
# Serialise it, check the output.
414
bytes = knitrepo._get_stream_as_bytes(k1, ['text-a'])
415
data = bencode.bdecode(bytes)
416
format, record = data
417
self.assertEqual('knit-plain', format)
418
self.assertEqual(['text-a', ['fulltext'], []], record[:3])
419
self.assertRecordContentEqual(k1, 'text-a', record[3])
421
def test_get_stream_as_bytes_all(self):
422
"""Get a serialised data stream for all the records in a knit.
424
Much like test_get_stream_all, except for get_stream_as_bytes.
426
k1 = self.make_test_knit()
427
# Insert the same data as BasicKnitTests.test_knit_join, as they seem
428
# to cover a range of cases (no parents, one parent, multiple parents).
430
('text-a', [], test_knit.TEXT_1),
431
('text-b', ['text-a'], test_knit.TEXT_1),
432
('text-c', [], test_knit.TEXT_1),
433
('text-d', ['text-c'], test_knit.TEXT_1),
434
('text-m', ['text-b', 'text-d'], test_knit.TEXT_1),
436
# This test is actually a bit strict as the order in which they're
437
# returned is not defined. This matches the current (deterministic)
439
expected_data_list = [
440
# version, options, parents
441
('text-a', ['fulltext'], []),
442
('text-b', ['line-delta'], ['text-a']),
443
('text-m', ['line-delta'], ['text-b', 'text-d']),
444
('text-c', ['fulltext'], []),
445
('text-d', ['line-delta'], ['text-c']),
447
for version_id, parents, lines in test_data:
448
k1.add_lines(version_id, parents, test_knit.split_lines(lines))
450
bytes = knitrepo._get_stream_as_bytes(
451
k1, ['text-a', 'text-b', 'text-m', 'text-c', 'text-d', ])
453
data = bencode.bdecode(bytes)
455
self.assertEqual('knit-plain', format)
457
for expected, actual in zip(expected_data_list, data):
458
expected_version = expected[0]
459
expected_options = expected[1]
460
expected_parents = expected[2]
461
version, options, parents, bytes = actual
462
self.assertEqual(expected_version, version)
463
self.assertEqual(expected_options, options)
464
self.assertEqual(expected_parents, parents)
465
self.assertRecordContentEqual(k1, version, bytes)
468
class DummyRepository(object):
469
"""A dummy repository for testing."""
473
def supports_rich_root(self):
477
class InterDummy(repository.InterRepository):
478
"""An inter-repository optimised code path for DummyRepository.
480
This is for use during testing where we use DummyRepository as repositories
481
so that none of the default regsitered inter-repository classes will
486
def is_compatible(repo_source, repo_target):
487
"""InterDummy is compatible with DummyRepository."""
488
return (isinstance(repo_source, DummyRepository) and
489
isinstance(repo_target, DummyRepository))
492
class TestInterRepository(TestCaseWithTransport):
494
def test_get_default_inter_repository(self):
495
# test that the InterRepository.get(repo_a, repo_b) probes
496
# for a inter_repo class where is_compatible(repo_a, repo_b) returns
497
# true and returns a default inter_repo otherwise.
498
# This also tests that the default registered optimised interrepository
499
# classes do not barf inappropriately when a surprising repository type
501
dummy_a = DummyRepository()
502
dummy_b = DummyRepository()
503
self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
505
def assertGetsDefaultInterRepository(self, repo_a, repo_b):
506
"""Asserts that InterRepository.get(repo_a, repo_b) -> the default.
508
The effective default is now InterSameDataRepository because there is
509
no actual sane default in the presence of incompatible data models.
511
inter_repo = repository.InterRepository.get(repo_a, repo_b)
512
self.assertEqual(repository.InterSameDataRepository,
513
inter_repo.__class__)
514
self.assertEqual(repo_a, inter_repo.source)
515
self.assertEqual(repo_b, inter_repo.target)
517
def test_register_inter_repository_class(self):
518
# test that a optimised code path provider - a
519
# InterRepository subclass can be registered and unregistered
520
# and that it is correctly selected when given a repository
521
# pair that it returns true on for the is_compatible static method
523
dummy_a = DummyRepository()
524
dummy_b = DummyRepository()
525
repo = self.make_repository('.')
526
# hack dummies to look like repo somewhat.
527
dummy_a._serializer = repo._serializer
528
dummy_b._serializer = repo._serializer
529
repository.InterRepository.register_optimiser(InterDummy)
531
# we should get the default for something InterDummy returns False
533
self.assertFalse(InterDummy.is_compatible(dummy_a, repo))
534
self.assertGetsDefaultInterRepository(dummy_a, repo)
535
# and we should get an InterDummy for a pair it 'likes'
536
self.assertTrue(InterDummy.is_compatible(dummy_a, dummy_b))
537
inter_repo = repository.InterRepository.get(dummy_a, dummy_b)
538
self.assertEqual(InterDummy, inter_repo.__class__)
539
self.assertEqual(dummy_a, inter_repo.source)
540
self.assertEqual(dummy_b, inter_repo.target)
542
repository.InterRepository.unregister_optimiser(InterDummy)
543
# now we should get the default InterRepository object again.
544
self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
547
class TestInterWeaveRepo(TestCaseWithTransport):
549
def test_is_compatible_and_registered(self):
550
# InterWeaveRepo is compatible when either side
551
# is a format 5/6/7 branch
552
from bzrlib.repofmt import knitrepo, weaverepo
553
formats = [weaverepo.RepositoryFormat5(),
554
weaverepo.RepositoryFormat6(),
555
weaverepo.RepositoryFormat7()]
556
incompatible_formats = [weaverepo.RepositoryFormat4(),
557
knitrepo.RepositoryFormatKnit1(),
559
repo_a = self.make_repository('a')
560
repo_b = self.make_repository('b')
561
is_compatible = repository.InterWeaveRepo.is_compatible
562
for source in incompatible_formats:
563
# force incompatible left then right
564
repo_a._format = source
565
repo_b._format = formats[0]
566
self.assertFalse(is_compatible(repo_a, repo_b))
567
self.assertFalse(is_compatible(repo_b, repo_a))
568
for source in formats:
569
repo_a._format = source
570
for target in formats:
571
repo_b._format = target
572
self.assertTrue(is_compatible(repo_a, repo_b))
573
self.assertEqual(repository.InterWeaveRepo,
574
repository.InterRepository.get(repo_a,
578
class TestInterRemoteToOther(TestCaseWithTransport):
580
def make_remote_repository(self, path, backing_format=None):
581
"""Make a RemoteRepository object backed by a real repository that will
582
be created at the given path."""
583
self.make_repository(path, format=backing_format)
584
smart_server = server.SmartTCPServer_for_testing()
586
remote_transport = get_transport(smart_server.get_url()).clone(path)
587
self.addCleanup(smart_server.tearDown)
588
remote_bzrdir = bzrdir.BzrDir.open_from_transport(remote_transport)
589
remote_repo = remote_bzrdir.open_repository()
592
def test_is_compatible_same_format(self):
593
"""InterRemoteToOther is compatible with a remote repository and a
594
second repository that have the same format."""
595
local_repo = self.make_repository('local')
596
remote_repo = self.make_remote_repository('remote')
597
is_compatible = repository.InterRemoteToOther.is_compatible
599
is_compatible(remote_repo, local_repo),
600
"InterRemoteToOther(%r, %r) is false" % (remote_repo, local_repo))
602
def test_is_incompatible_different_format(self):
603
local_repo = self.make_repository('local', 'dirstate')
604
remote_repo = self.make_remote_repository('a', 'dirstate-with-subtree')
605
is_compatible = repository.InterRemoteToOther.is_compatible
607
is_compatible(remote_repo, local_repo),
608
"InterRemoteToOther(%r, %r) is true" % (local_repo, remote_repo))
610
def test_is_incompatible_different_format_both_remote(self):
611
remote_repo_a = self.make_remote_repository(
612
'a', 'dirstate-with-subtree')
613
remote_repo_b = self.make_remote_repository('b', 'dirstate')
614
is_compatible = repository.InterRemoteToOther.is_compatible
616
is_compatible(remote_repo_a, remote_repo_b),
617
"InterRemoteToOther(%r, %r) is true"
618
% (remote_repo_a, remote_repo_b))
621
class TestRepositoryConverter(TestCaseWithTransport):
623
def test_convert_empty(self):
624
t = get_transport(self.get_url('.'))
625
t.mkdir('repository')
626
repo_dir = bzrdir.BzrDirMetaFormat1().initialize('repository')
627
repo = weaverepo.RepositoryFormat7().initialize(repo_dir)
628
target_format = knitrepo.RepositoryFormatKnit1()
629
converter = repository.CopyConverter(target_format)
630
pb = bzrlib.ui.ui_factory.nested_progress_bar()
632
converter.convert(repo, pb)
635
repo = repo_dir.open_repository()
636
self.assertTrue(isinstance(target_format, repo._format.__class__))
639
class TestMisc(TestCase):
641
def test_unescape_xml(self):
642
"""We get some kind of error when malformed entities are passed"""
643
self.assertRaises(KeyError, repository._unescape_xml, 'foo&bar;')
646
class TestRepositoryFormatKnit3(TestCaseWithTransport):
648
def test_convert(self):
649
"""Ensure the upgrade adds weaves for roots"""
650
format = bzrdir.BzrDirMetaFormat1()
651
format.repository_format = knitrepo.RepositoryFormatKnit1()
652
tree = self.make_branch_and_tree('.', format)
653
tree.commit("Dull commit", rev_id="dull")
654
revision_tree = tree.branch.repository.revision_tree('dull')
655
self.assertRaises(errors.NoSuchFile, revision_tree.get_file_lines,
656
revision_tree.inventory.root.file_id)
657
format = bzrdir.BzrDirMetaFormat1()
658
format.repository_format = knitrepo.RepositoryFormatKnit3()
659
upgrade.Convert('.', format)
660
tree = workingtree.WorkingTree.open('.')
661
revision_tree = tree.branch.repository.revision_tree('dull')
662
revision_tree.get_file_lines(revision_tree.inventory.root.file_id)
663
tree.commit("Another dull commit", rev_id='dull2')
664
revision_tree = tree.branch.repository.revision_tree('dull2')
665
self.assertEqual('dull', revision_tree.inventory.root.revision)
667
def test_exposed_versioned_files_are_marked_dirty(self):
668
format = bzrdir.BzrDirMetaFormat1()
669
format.repository_format = knitrepo.RepositoryFormatKnit3()
670
repo = self.make_repository('.', format=format)
672
inv = repo.get_inventory_weave()
674
self.assertRaises(errors.OutSideTransaction,
675
inv.add_lines, 'foo', [], [])
678
class TestWithBrokenRepo(TestCaseWithTransport):
679
"""These tests seem to be more appropriate as interface tests?"""
681
def make_broken_repository(self):
682
# XXX: This function is borrowed from Aaron's "Reconcile can fix bad
683
# parent references" branch which is due to land in bzr.dev soon. Once
684
# it does, this duplication should be removed.
685
repo = self.make_repository('broken-repo')
689
cleanups.append(repo.unlock)
690
repo.start_write_group()
691
cleanups.append(repo.commit_write_group)
692
# make rev1a: A well-formed revision, containing 'file1'
693
inv = inventory.Inventory(revision_id='rev1a')
694
inv.root.revision = 'rev1a'
695
self.add_file(repo, inv, 'file1', 'rev1a', [])
696
repo.add_inventory('rev1a', inv, [])
697
revision = _mod_revision.Revision('rev1a',
698
committer='jrandom@example.com', timestamp=0,
699
inventory_sha1='', timezone=0, message='foo', parent_ids=[])
700
repo.add_revision('rev1a',revision, inv)
702
# make rev1b, which has no Revision, but has an Inventory, and
704
inv = inventory.Inventory(revision_id='rev1b')
705
inv.root.revision = 'rev1b'
706
self.add_file(repo, inv, 'file1', 'rev1b', [])
707
repo.add_inventory('rev1b', inv, [])
709
# make rev2, with file1 and file2
711
# file1 has 'rev1b' as an ancestor, even though this is not
712
# mentioned by 'rev1a', making it an unreferenced ancestor
713
inv = inventory.Inventory()
714
self.add_file(repo, inv, 'file1', 'rev2', ['rev1a', 'rev1b'])
715
self.add_file(repo, inv, 'file2', 'rev2', [])
716
self.add_revision(repo, 'rev2', inv, ['rev1a'])
718
# make ghost revision rev1c
719
inv = inventory.Inventory()
720
self.add_file(repo, inv, 'file2', 'rev1c', [])
722
# make rev3 with file2
723
# file2 refers to 'rev1c', which is a ghost in this repository, so
724
# file2 cannot have rev1c as its ancestor.
725
inv = inventory.Inventory()
726
self.add_file(repo, inv, 'file2', 'rev3', ['rev1c'])
727
self.add_revision(repo, 'rev3', inv, ['rev1c'])
730
for cleanup in reversed(cleanups):
733
def add_revision(self, repo, revision_id, inv, parent_ids):
734
inv.revision_id = revision_id
735
inv.root.revision = revision_id
736
repo.add_inventory(revision_id, inv, parent_ids)
737
revision = _mod_revision.Revision(revision_id,
738
committer='jrandom@example.com', timestamp=0, inventory_sha1='',
739
timezone=0, message='foo', parent_ids=parent_ids)
740
repo.add_revision(revision_id,revision, inv)
742
def add_file(self, repo, inv, filename, revision, parents):
743
file_id = filename + '-id'
744
entry = inventory.InventoryFile(file_id, filename, 'TREE_ROOT')
745
entry.revision = revision
748
vf = repo.weave_store.get_weave_or_empty(file_id,
749
repo.get_transaction())
750
vf.add_lines(revision, parents, ['line\n'])
752
def test_insert_from_broken_repo(self):
753
"""Inserting a data stream from a broken repository won't silently
754
corrupt the target repository.
756
broken_repo = self.make_broken_repository()
757
empty_repo = self.make_repository('empty-repo')
758
stream = broken_repo.get_data_stream(['rev1a', 'rev2', 'rev3'])
759
empty_repo.lock_write()
760
self.addCleanup(empty_repo.unlock)
761
empty_repo.start_write_group()
764
errors.KnitCorrupt, empty_repo.insert_data_stream, stream)
766
empty_repo.abort_write_group()
769
class TestKnitPackNoSubtrees(TestCaseWithTransport):
771
def get_format(self):
772
return bzrdir.format_registry.make_bzrdir('pack-0.92')
774
def test_disk_layout(self):
775
format = self.get_format()
776
repo = self.make_repository('.', format=format)
777
# in case of side effects of locking.
780
t = repo.bzrdir.get_repository_transport(None)
782
# XXX: no locks left when unlocked at the moment
783
# self.assertEqualDiff('', t.get('lock').read())
784
self.check_databases(t)
786
def check_format(self, t):
787
self.assertEqualDiff(
788
"Bazaar pack repository format 1 (needs bzr 0.92)\n",
789
t.get('format').read())
791
def assertHasKndx(self, t, knit_name):
792
"""Assert that knit_name exists on t."""
793
self.assertEqualDiff('# bzr knit index 8\n',
794
t.get(knit_name + '.kndx').read())
796
def assertHasNoKndx(self, t, knit_name):
797
"""Assert that knit_name has no index on t."""
798
self.assertFalse(t.has(knit_name + '.kndx'))
800
def assertHasNoKnit(self, t, knit_name):
801
"""Assert that knit_name exists on t."""
803
self.assertFalse(t.has(knit_name + '.knit'))
805
def check_databases(self, t):
806
"""check knit content for a repository."""
807
# check conversion worked
808
self.assertHasNoKndx(t, 'inventory')
809
self.assertHasNoKnit(t, 'inventory')
810
self.assertHasNoKndx(t, 'revisions')
811
self.assertHasNoKnit(t, 'revisions')
812
self.assertHasNoKndx(t, 'signatures')
813
self.assertHasNoKnit(t, 'signatures')
814
self.assertFalse(t.has('knits'))
815
# revision-indexes file-container directory
817
list(GraphIndex(t, 'pack-names', None).iter_all_entries()))
818
self.assertTrue(S_ISDIR(t.stat('packs').st_mode))
819
self.assertTrue(S_ISDIR(t.stat('upload').st_mode))
820
self.assertTrue(S_ISDIR(t.stat('indices').st_mode))
821
self.assertTrue(S_ISDIR(t.stat('obsolete_packs').st_mode))
823
def test_shared_disk_layout(self):
824
format = self.get_format()
825
repo = self.make_repository('.', shared=True, format=format)
827
t = repo.bzrdir.get_repository_transport(None)
829
# XXX: no locks left when unlocked at the moment
830
# self.assertEqualDiff('', t.get('lock').read())
831
# We should have a 'shared-storage' marker file.
832
self.assertEqualDiff('', t.get('shared-storage').read())
833
self.check_databases(t)
835
def test_shared_no_tree_disk_layout(self):
836
format = self.get_format()
837
repo = self.make_repository('.', shared=True, format=format)
838
repo.set_make_working_trees(False)
840
t = repo.bzrdir.get_repository_transport(None)
842
# XXX: no locks left when unlocked at the moment
843
# self.assertEqualDiff('', t.get('lock').read())
844
# We should have a 'shared-storage' marker file.
845
self.assertEqualDiff('', t.get('shared-storage').read())
846
# We should have a marker for the no-working-trees flag.
847
self.assertEqualDiff('', t.get('no-working-trees').read())
848
# The marker should go when we toggle the setting.
849
repo.set_make_working_trees(True)
850
self.assertFalse(t.has('no-working-trees'))
851
self.check_databases(t)
853
def test_adding_revision_creates_pack_indices(self):
854
format = self.get_format()
855
tree = self.make_branch_and_tree('.', format=format)
856
trans = tree.branch.repository.bzrdir.get_repository_transport(None)
858
list(GraphIndex(trans, 'pack-names', None).iter_all_entries()))
859
tree.commit('foobarbaz')
860
index = GraphIndex(trans, 'pack-names', None)
861
index_nodes = list(index.iter_all_entries())
862
self.assertEqual(1, len(index_nodes))
863
node = index_nodes[0]
865
# the pack sizes should be listed in the index
867
sizes = [int(digits) for digits in pack_value.split(' ')]
868
for size, suffix in zip(sizes, ['.rix', '.iix', '.tix', '.six']):
869
stat = trans.stat('indices/%s%s' % (name, suffix))
870
self.assertEqual(size, stat.st_size)
872
def test_pulling_nothing_leads_to_no_new_names(self):
873
format = self.get_format()
874
tree1 = self.make_branch_and_tree('1', format=format)
875
tree2 = self.make_branch_and_tree('2', format=format)
876
tree1.branch.repository.fetch(tree2.branch.repository)
877
trans = tree1.branch.repository.bzrdir.get_repository_transport(None)
879
list(GraphIndex(trans, 'pack-names', None).iter_all_entries()))
881
def test_commit_across_pack_shape_boundary_autopacks(self):
882
format = self.get_format()
883
tree = self.make_branch_and_tree('.', format=format)
884
trans = tree.branch.repository.bzrdir.get_repository_transport(None)
885
# This test could be a little cheaper by replacing the packs
886
# attribute on the repository to allow a different pack distribution
887
# and max packs policy - so we are checking the policy is honoured
888
# in the test. But for now 11 commits is not a big deal in a single
891
tree.commit('commit %s' % x)
892
# there should be 9 packs:
893
index = GraphIndex(trans, 'pack-names', None)
894
self.assertEqual(9, len(list(index.iter_all_entries())))
895
# insert some files in obsolete_packs which should be removed by pack.
896
trans.put_bytes('obsolete_packs/foo', '123')
897
trans.put_bytes('obsolete_packs/bar', '321')
898
# committing one more should coalesce to 1 of 10.
899
tree.commit('commit triggering pack')
900
index = GraphIndex(trans, 'pack-names', None)
901
self.assertEqual(1, len(list(index.iter_all_entries())))
902
# packing should not damage data
903
tree = tree.bzrdir.open_workingtree()
904
check_result = tree.branch.repository.check(
905
[tree.branch.last_revision()])
906
# We should have 50 (10x5) files in the obsolete_packs directory.
907
obsolete_files = list(trans.list_dir('obsolete_packs'))
908
self.assertFalse('foo' in obsolete_files)
909
self.assertFalse('bar' in obsolete_files)
910
self.assertEqual(50, len(obsolete_files))
911
# XXX: Todo check packs obsoleted correctly - old packs and indices
912
# in the obsolete_packs directory.
913
large_pack_name = list(index.iter_all_entries())[0][1][0]
914
# finally, committing again should not touch the large pack.
915
tree.commit('commit not triggering pack')
916
index = GraphIndex(trans, 'pack-names', None)
917
self.assertEqual(2, len(list(index.iter_all_entries())))
918
pack_names = [node[1][0] for node in index.iter_all_entries()]
919
self.assertTrue(large_pack_name in pack_names)
921
def test_pack_after_two_commits_packs_everything(self):
922
format = self.get_format()
923
tree = self.make_branch_and_tree('.', format=format)
924
trans = tree.branch.repository.bzrdir.get_repository_transport(None)
926
tree.commit('more work')
927
tree.branch.repository.pack()
928
# there should be 1 pack:
929
index = GraphIndex(trans, 'pack-names', None)
930
self.assertEqual(1, len(list(index.iter_all_entries())))
931
self.assertEqual(2, len(tree.branch.repository.all_revision_ids()))
933
def test_pack_layout(self):
934
format = self.get_format()
935
tree = self.make_branch_and_tree('.', format=format)
936
trans = tree.branch.repository.bzrdir.get_repository_transport(None)
937
tree.commit('start', rev_id='1')
938
tree.commit('more work', rev_id='2')
939
tree.branch.repository.pack()
941
self.addCleanup(tree.unlock)
942
pack = tree.branch.repository._pack_collection.get_pack_by_name(
943
tree.branch.repository._pack_collection.names()[0])
944
# revision access tends to be tip->ancestor, so ordering that way on
945
# disk is a good idea.
946
for _1, key, val, refs in pack.revision_index.iter_all_entries():
948
pos_1 = int(val[1:].split()[0])
950
pos_2 = int(val[1:].split()[0])
951
self.assertTrue(pos_2 < pos_1)
953
def test_pack_repositories_support_multiple_write_locks(self):
954
format = self.get_format()
955
self.make_repository('.', shared=True, format=format)
956
r1 = repository.Repository.open('.')
957
r2 = repository.Repository.open('.')
959
self.addCleanup(r1.unlock)
963
def _add_text(self, repo, fileid):
964
"""Add a text to the repository within a write group."""
965
vf =repo.weave_store.get_weave(fileid, repo.get_transaction())
966
vf.add_lines('samplerev+' + fileid, [], [])
968
def test_concurrent_writers_merge_new_packs(self):
969
format = self.get_format()
970
self.make_repository('.', shared=True, format=format)
971
r1 = repository.Repository.open('.')
972
r2 = repository.Repository.open('.')
975
# access enough data to load the names list
976
list(r1.all_revision_ids())
979
# access enough data to load the names list
980
list(r2.all_revision_ids())
981
r1.start_write_group()
983
r2.start_write_group()
985
self._add_text(r1, 'fileidr1')
986
self._add_text(r2, 'fileidr2')
988
r2.abort_write_group()
991
r1.abort_write_group()
993
# both r1 and r2 have open write groups with data in them
994
# created while the other's write group was open.
995
# Commit both which requires a merge to the pack-names.
997
r1.commit_write_group()
999
r1.abort_write_group()
1000
r2.abort_write_group()
1002
r2.commit_write_group()
1003
# tell r1 to reload from disk
1004
r1._pack_collection.reset()
1005
# Now both repositories should know about both names
1006
r1._pack_collection.ensure_loaded()
1007
r2._pack_collection.ensure_loaded()
1008
self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
1009
self.assertEqual(2, len(r1._pack_collection.names()))
1015
def test_concurrent_writer_second_preserves_dropping_a_pack(self):
1016
format = self.get_format()
1017
self.make_repository('.', shared=True, format=format)
1018
r1 = repository.Repository.open('.')
1019
r2 = repository.Repository.open('.')
1020
# add a pack to drop
1023
r1.start_write_group()
1025
self._add_text(r1, 'fileidr1')
1027
r1.abort_write_group()
1030
r1.commit_write_group()
1031
r1._pack_collection.ensure_loaded()
1032
name_to_drop = r1._pack_collection.all_packs()[0].name
1037
# access enough data to load the names list
1038
list(r1.all_revision_ids())
1041
# access enough data to load the names list
1042
list(r2.all_revision_ids())
1043
r1._pack_collection.ensure_loaded()
1045
r2.start_write_group()
1047
# in r1, drop the pack
1048
r1._pack_collection._remove_pack_from_memory(
1049
r1._pack_collection.get_pack_by_name(name_to_drop))
1051
self._add_text(r2, 'fileidr2')
1053
r2.abort_write_group()
1056
r1._pack_collection.reset()
1058
# r1 has a changed names list, and r2 an open write groups with
1060
# save r1, and then commit the r2 write group, which requires a
1061
# merge to the pack-names, which should not reinstate
1064
r1._pack_collection._save_pack_names()
1065
r1._pack_collection.reset()
1067
r2.abort_write_group()
1070
r2.commit_write_group()
1072
r2.abort_write_group()
1074
# Now both repositories should now about just one name.
1075
r1._pack_collection.ensure_loaded()
1076
r2._pack_collection.ensure_loaded()
1077
self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
1078
self.assertEqual(1, len(r1._pack_collection.names()))
1079
self.assertFalse(name_to_drop in r1._pack_collection.names())
1085
def test_lock_write_does_not_physically_lock(self):
1086
repo = self.make_repository('.', format=self.get_format())
1088
self.addCleanup(repo.unlock)
1089
self.assertFalse(repo.get_physical_lock_status())
1091
def prepare_for_break_lock(self):
1092
# Setup the global ui factory state so that a break-lock method call
1093
# will find usable input in the input stream.
1094
old_factory = bzrlib.ui.ui_factory
1095
def restoreFactory():
1096
bzrlib.ui.ui_factory = old_factory
1097
self.addCleanup(restoreFactory)
1098
bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
1099
bzrlib.ui.ui_factory.stdin = StringIO("y\n")
1101
def test_break_lock_breaks_physical_lock(self):
1102
repo = self.make_repository('.', format=self.get_format())
1103
repo._pack_collection.lock_names()
1104
repo2 = repository.Repository.open('.')
1105
self.assertTrue(repo.get_physical_lock_status())
1106
self.prepare_for_break_lock()
1108
self.assertFalse(repo.get_physical_lock_status())
1110
def test_broken_physical_locks_error_on__unlock_names_lock(self):
1111
repo = self.make_repository('.', format=self.get_format())
1112
repo._pack_collection.lock_names()
1113
self.assertTrue(repo.get_physical_lock_status())
1114
repo2 = repository.Repository.open('.')
1115
self.prepare_for_break_lock()
1117
self.assertRaises(errors.LockBroken, repo._pack_collection._unlock_names)
1119
def test_fetch_without_find_ghosts_ignores_ghosts(self):
1120
# we want two repositories at this point:
1121
# one with a revision that is a ghost in the other
1123
# 'ghost' is present in has_ghost, 'ghost' is absent in 'missing_ghost'.
1124
# 'references' is present in both repositories, and 'tip' is present
1125
# just in has_ghost.
1126
# has_ghost missing_ghost
1127
#------------------------------
1129
# 'references' 'references'
1131
# In this test we fetch 'tip' which should not fetch 'ghost'
1132
has_ghost = self.make_repository('has_ghost', format=self.get_format())
1133
missing_ghost = self.make_repository('missing_ghost',
1134
format=self.get_format())
1136
def add_commit(repo, revision_id, parent_ids):
1138
repo.start_write_group()
1139
inv = inventory.Inventory(revision_id=revision_id)
1140
inv.root.revision = revision_id
1141
root_id = inv.root.file_id
1142
sha1 = repo.add_inventory(revision_id, inv, [])
1143
vf = repo.weave_store.get_weave_or_empty(root_id,
1144
repo.get_transaction())
1145
vf.add_lines(revision_id, [], [])
1146
rev = bzrlib.revision.Revision(timestamp=0,
1148
committer="Foo Bar <foo@example.com>",
1150
inventory_sha1=sha1,
1151
revision_id=revision_id)
1152
rev.parent_ids = parent_ids
1153
repo.add_revision(revision_id, rev)
1154
repo.commit_write_group()
1156
add_commit(has_ghost, 'ghost', [])
1157
add_commit(has_ghost, 'references', ['ghost'])
1158
add_commit(missing_ghost, 'references', ['ghost'])
1159
add_commit(has_ghost, 'tip', ['references'])
1160
missing_ghost.fetch(has_ghost, 'tip')
1161
# missing ghost now has tip and not ghost.
1162
rev = missing_ghost.get_revision('tip')
1163
inv = missing_ghost.get_inventory('tip')
1164
self.assertRaises(errors.NoSuchRevision,
1165
missing_ghost.get_revision, 'ghost')
1166
self.assertRaises(errors.RevisionNotPresent,
1167
missing_ghost.get_inventory, 'ghost')
1170
class TestKnitPackSubtrees(TestKnitPackNoSubtrees):
1172
def get_format(self):
1173
return bzrdir.format_registry.make_bzrdir(
1174
'pack-0.92-subtree')
1176
def check_format(self, t):
1177
self.assertEqualDiff(
1178
"Bazaar pack repository format 1 with subtree support (needs bzr 0.92)\n",
1179
t.get('format').read())
1182
class TestRepositoryPackCollection(TestCaseWithTransport):
1184
def get_format(self):
1185
return bzrdir.format_registry.make_bzrdir('pack-0.92')
1187
def test__max_pack_count(self):
1188
"""The maximum pack count is a function of the number of revisions."""
1189
format = self.get_format()
1190
repo = self.make_repository('.', format=format)
1191
packs = repo._pack_collection
1192
# no revisions - one pack, so that we can have a revision free repo
1193
# without it blowing up
1194
self.assertEqual(1, packs._max_pack_count(0))
1195
# after that the sum of the digits, - check the first 1-9
1196
self.assertEqual(1, packs._max_pack_count(1))
1197
self.assertEqual(2, packs._max_pack_count(2))
1198
self.assertEqual(3, packs._max_pack_count(3))
1199
self.assertEqual(4, packs._max_pack_count(4))
1200
self.assertEqual(5, packs._max_pack_count(5))
1201
self.assertEqual(6, packs._max_pack_count(6))
1202
self.assertEqual(7, packs._max_pack_count(7))
1203
self.assertEqual(8, packs._max_pack_count(8))
1204
self.assertEqual(9, packs._max_pack_count(9))
1205
# check the boundary cases with two digits for the next decade
1206
self.assertEqual(1, packs._max_pack_count(10))
1207
self.assertEqual(2, packs._max_pack_count(11))
1208
self.assertEqual(10, packs._max_pack_count(19))
1209
self.assertEqual(2, packs._max_pack_count(20))
1210
self.assertEqual(3, packs._max_pack_count(21))
1211
# check some arbitrary big numbers
1212
self.assertEqual(25, packs._max_pack_count(112894))
1214
def test_pack_distribution_zero(self):
1215
format = self.get_format()
1216
repo = self.make_repository('.', format=format)
1217
packs = repo._pack_collection
1218
self.assertEqual([0], packs.pack_distribution(0))
1220
def test_ensure_loaded_unlocked(self):
1221
format = self.get_format()
1222
repo = self.make_repository('.', format=format)
1223
self.assertRaises(errors.ObjectNotLocked,
1224
repo._pack_collection.ensure_loaded)
1226
def test_pack_distribution_one_to_nine(self):
1227
format = self.get_format()
1228
repo = self.make_repository('.', format=format)
1229
packs = repo._pack_collection
1230
self.assertEqual([1],
1231
packs.pack_distribution(1))
1232
self.assertEqual([1, 1],
1233
packs.pack_distribution(2))
1234
self.assertEqual([1, 1, 1],
1235
packs.pack_distribution(3))
1236
self.assertEqual([1, 1, 1, 1],
1237
packs.pack_distribution(4))
1238
self.assertEqual([1, 1, 1, 1, 1],
1239
packs.pack_distribution(5))
1240
self.assertEqual([1, 1, 1, 1, 1, 1],
1241
packs.pack_distribution(6))
1242
self.assertEqual([1, 1, 1, 1, 1, 1, 1],
1243
packs.pack_distribution(7))
1244
self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1],
1245
packs.pack_distribution(8))
1246
self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1, 1],
1247
packs.pack_distribution(9))
1249
def test_pack_distribution_stable_at_boundaries(self):
1250
"""When there are multi-rev packs the counts are stable."""
1251
format = self.get_format()
1252
repo = self.make_repository('.', format=format)
1253
packs = repo._pack_collection
1255
self.assertEqual([10], packs.pack_distribution(10))
1256
self.assertEqual([10, 1], packs.pack_distribution(11))
1257
self.assertEqual([10, 10], packs.pack_distribution(20))
1258
self.assertEqual([10, 10, 1], packs.pack_distribution(21))
1260
self.assertEqual([100], packs.pack_distribution(100))
1261
self.assertEqual([100, 1], packs.pack_distribution(101))
1262
self.assertEqual([100, 10, 1], packs.pack_distribution(111))
1263
self.assertEqual([100, 100], packs.pack_distribution(200))
1264
self.assertEqual([100, 100, 1], packs.pack_distribution(201))
1265
self.assertEqual([100, 100, 10, 1], packs.pack_distribution(211))
1267
def test_plan_pack_operations_2009_revisions_skip_all_packs(self):
1268
format = self.get_format()
1269
repo = self.make_repository('.', format=format)
1270
packs = repo._pack_collection
1271
existing_packs = [(2000, "big"), (9, "medium")]
1272
# rev count - 2009 -> 2x1000 + 9x1
1273
pack_operations = packs.plan_autopack_combinations(
1274
existing_packs, [1000, 1000, 1, 1, 1, 1, 1, 1, 1, 1, 1])
1275
self.assertEqual([], pack_operations)
1277
def test_plan_pack_operations_2010_revisions_skip_all_packs(self):
1278
format = self.get_format()
1279
repo = self.make_repository('.', format=format)
1280
packs = repo._pack_collection
1281
existing_packs = [(2000, "big"), (9, "medium"), (1, "single")]
1282
# rev count - 2010 -> 2x1000 + 1x10
1283
pack_operations = packs.plan_autopack_combinations(
1284
existing_packs, [1000, 1000, 10])
1285
self.assertEqual([], pack_operations)
1287
def test_plan_pack_operations_2010_combines_smallest_two(self):
1288
format = self.get_format()
1289
repo = self.make_repository('.', format=format)
1290
packs = repo._pack_collection
1291
existing_packs = [(1999, "big"), (9, "medium"), (1, "single2"),
1293
# rev count - 2010 -> 2x1000 + 1x10 (3)
1294
pack_operations = packs.plan_autopack_combinations(
1295
existing_packs, [1000, 1000, 10])
1296
self.assertEqual([[2, ["single2", "single1"]], [0, []]], pack_operations)
1298
def test_all_packs_none(self):
1299
format = self.get_format()
1300
tree = self.make_branch_and_tree('.', format=format)
1302
self.addCleanup(tree.unlock)
1303
packs = tree.branch.repository._pack_collection
1304
packs.ensure_loaded()
1305
self.assertEqual([], packs.all_packs())
1307
def test_all_packs_one(self):
1308
format = self.get_format()
1309
tree = self.make_branch_and_tree('.', format=format)
1310
tree.commit('start')
1312
self.addCleanup(tree.unlock)
1313
packs = tree.branch.repository._pack_collection
1314
packs.ensure_loaded()
1316
packs.get_pack_by_name(packs.names()[0])],
1319
def test_all_packs_two(self):
1320
format = self.get_format()
1321
tree = self.make_branch_and_tree('.', format=format)
1322
tree.commit('start')
1323
tree.commit('continue')
1325
self.addCleanup(tree.unlock)
1326
packs = tree.branch.repository._pack_collection
1327
packs.ensure_loaded()
1329
packs.get_pack_by_name(packs.names()[0]),
1330
packs.get_pack_by_name(packs.names()[1]),
1331
], packs.all_packs())
1333
def test_get_pack_by_name(self):
1334
format = self.get_format()
1335
tree = self.make_branch_and_tree('.', format=format)
1336
tree.commit('start')
1338
self.addCleanup(tree.unlock)
1339
packs = tree.branch.repository._pack_collection
1340
packs.ensure_loaded()
1341
name = packs.names()[0]
1342
pack_1 = packs.get_pack_by_name(name)
1343
# the pack should be correctly initialised
1344
rev_index = GraphIndex(packs._index_transport, name + '.rix',
1345
packs._names[name][0])
1346
inv_index = GraphIndex(packs._index_transport, name + '.iix',
1347
packs._names[name][1])
1348
txt_index = GraphIndex(packs._index_transport, name + '.tix',
1349
packs._names[name][2])
1350
sig_index = GraphIndex(packs._index_transport, name + '.six',
1351
packs._names[name][3])
1352
self.assertEqual(pack_repo.ExistingPack(packs._pack_transport,
1353
name, rev_index, inv_index, txt_index, sig_index), pack_1)
1354
# and the same instance should be returned on successive calls.
1355
self.assertTrue(pack_1 is packs.get_pack_by_name(name))
1358
class TestPack(TestCaseWithTransport):
1359
"""Tests for the Pack object."""
1361
def assertCurrentlyEqual(self, left, right):
1362
self.assertTrue(left == right)
1363
self.assertTrue(right == left)
1364
self.assertFalse(left != right)
1365
self.assertFalse(right != left)
1367
def assertCurrentlyNotEqual(self, left, right):
1368
self.assertFalse(left == right)
1369
self.assertFalse(right == left)
1370
self.assertTrue(left != right)
1371
self.assertTrue(right != left)
1373
def test___eq____ne__(self):
1374
left = pack_repo.ExistingPack('', '', '', '', '', '')
1375
right = pack_repo.ExistingPack('', '', '', '', '', '')
1376
self.assertCurrentlyEqual(left, right)
1377
# change all attributes and ensure equality changes as we do.
1378
left.revision_index = 'a'
1379
self.assertCurrentlyNotEqual(left, right)
1380
right.revision_index = 'a'
1381
self.assertCurrentlyEqual(left, right)
1382
left.inventory_index = 'a'
1383
self.assertCurrentlyNotEqual(left, right)
1384
right.inventory_index = 'a'
1385
self.assertCurrentlyEqual(left, right)
1386
left.text_index = 'a'
1387
self.assertCurrentlyNotEqual(left, right)
1388
right.text_index = 'a'
1389
self.assertCurrentlyEqual(left, right)
1390
left.signature_index = 'a'
1391
self.assertCurrentlyNotEqual(left, right)
1392
right.signature_index = 'a'
1393
self.assertCurrentlyEqual(left, right)
1395
self.assertCurrentlyNotEqual(left, right)
1397
self.assertCurrentlyEqual(left, right)
1398
left.transport = 'a'
1399
self.assertCurrentlyNotEqual(left, right)
1400
right.transport = 'a'
1401
self.assertCurrentlyEqual(left, right)
1403
def test_file_name(self):
1404
pack = pack_repo.ExistingPack('', 'a_name', '', '', '', '')
1405
self.assertEqual('a_name.pack', pack.file_name())
1408
class TestNewPack(TestCaseWithTransport):
1409
"""Tests for pack_repo.NewPack."""
1411
def test_new_instance_attributes(self):
1412
upload_transport = self.get_transport('upload')
1413
pack_transport = self.get_transport('pack')
1414
index_transport = self.get_transport('index')
1415
upload_transport.mkdir('.')
1416
pack = pack_repo.NewPack(upload_transport, index_transport,
1418
self.assertIsInstance(pack.revision_index, InMemoryGraphIndex)
1419
self.assertIsInstance(pack.inventory_index, InMemoryGraphIndex)
1420
self.assertIsInstance(pack._hash, type(md5.new()))
1421
self.assertTrue(pack.upload_transport is upload_transport)
1422
self.assertTrue(pack.index_transport is index_transport)
1423
self.assertTrue(pack.pack_transport is pack_transport)
1424
self.assertEqual(None, pack.index_sizes)
1425
self.assertEqual(20, len(pack.random_name))
1426
self.assertIsInstance(pack.random_name, str)
1427
self.assertIsInstance(pack.start_time, float)
1430
class TestPacker(TestCaseWithTransport):
1431
"""Tests for the packs repository Packer class."""
1433
# To date, this class has been factored out and nothing new added to it;
1434
# thus there are not yet any tests.
1437
class TestInterDifferingSerializer(TestCaseWithTransport):
1439
def test_progress_bar(self):
1440
tree = self.make_branch_and_tree('tree')
1441
tree.commit('rev1', rev_id='rev-1')
1442
tree.commit('rev2', rev_id='rev-2')
1443
tree.commit('rev3', rev_id='rev-3')
1444
repo = self.make_repository('repo')
1445
inter_repo = repository.InterDifferingSerializer(
1446
tree.branch.repository, repo)
1447
pb = progress.InstrumentedProgress(to_file=StringIO())
1448
pb.never_throttle = True
1449
inter_repo.fetch('rev-1', pb)
1450
self.assertEqual('Transferring revisions', pb.last_msg)
1451
self.assertEqual(1, pb.last_cnt)
1452
self.assertEqual(1, pb.last_total)
1453
inter_repo.fetch('rev-3', pb)
1454
self.assertEqual(2, pb.last_cnt)
1455
self.assertEqual(2, pb.last_total)