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 import graph
36
from bzrlib.index import GraphIndex, InMemoryGraphIndex
37
from bzrlib.repository import RepositoryFormat
38
from bzrlib.smart import server
39
from bzrlib.tests import (
41
TestCaseWithTransport,
44
from bzrlib.transport import get_transport
45
from bzrlib.transport.memory import MemoryServer
46
from bzrlib.util import bencode
53
revision as _mod_revision,
58
from bzrlib.repofmt import knitrepo, weaverepo, pack_repo
61
class TestDefaultFormat(TestCase):
63
def test_get_set_default_format(self):
64
old_default = bzrdir.format_registry.get('default')
65
private_default = old_default().repository_format.__class__
66
old_format = repository.RepositoryFormat.get_default_format()
67
self.assertTrue(isinstance(old_format, private_default))
68
def make_sample_bzrdir():
69
my_bzrdir = bzrdir.BzrDirMetaFormat1()
70
my_bzrdir.repository_format = SampleRepositoryFormat()
72
bzrdir.format_registry.remove('default')
73
bzrdir.format_registry.register('sample', make_sample_bzrdir, '')
74
bzrdir.format_registry.set_default('sample')
75
# creating a repository should now create an instrumented dir.
77
# the default branch format is used by the meta dir format
78
# which is not the default bzrdir format at this point
79
dir = bzrdir.BzrDirMetaFormat1().initialize('memory:///')
80
result = dir.create_repository()
81
self.assertEqual(result, 'A bzr repository dir')
83
bzrdir.format_registry.remove('default')
84
bzrdir.format_registry.remove('sample')
85
bzrdir.format_registry.register('default', old_default, '')
86
self.assertIsInstance(repository.RepositoryFormat.get_default_format(),
90
class SampleRepositoryFormat(repository.RepositoryFormat):
93
this format is initializable, unsupported to aid in testing the
94
open and open(unsupported=True) routines.
97
def get_format_string(self):
98
"""See RepositoryFormat.get_format_string()."""
99
return "Sample .bzr repository format."
101
def initialize(self, a_bzrdir, shared=False):
102
"""Initialize a repository in a BzrDir"""
103
t = a_bzrdir.get_repository_transport(self)
104
t.put_bytes('format', self.get_format_string())
105
return 'A bzr repository dir'
107
def is_supported(self):
110
def open(self, a_bzrdir, _found=False):
111
return "opened repository."
114
class TestRepositoryFormat(TestCaseWithTransport):
115
"""Tests for the Repository format detection used by the bzr meta dir facility.BzrBranchFormat facility."""
117
def test_find_format(self):
118
# is the right format object found for a repository?
119
# create a branch with a few known format objects.
120
# this is not quite the same as
121
self.build_tree(["foo/", "bar/"])
122
def check_format(format, url):
123
dir = format._matchingbzrdir.initialize(url)
124
format.initialize(dir)
125
t = get_transport(url)
126
found_format = repository.RepositoryFormat.find_format(dir)
127
self.failUnless(isinstance(found_format, format.__class__))
128
check_format(weaverepo.RepositoryFormat7(), "bar")
130
def test_find_format_no_repository(self):
131
dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
132
self.assertRaises(errors.NoRepositoryPresent,
133
repository.RepositoryFormat.find_format,
136
def test_find_format_unknown_format(self):
137
dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
138
SampleRepositoryFormat().initialize(dir)
139
self.assertRaises(UnknownFormatError,
140
repository.RepositoryFormat.find_format,
143
def test_register_unregister_format(self):
144
format = SampleRepositoryFormat()
146
dir = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
148
format.initialize(dir)
149
# register a format for it.
150
repository.RepositoryFormat.register_format(format)
151
# which repository.Open will refuse (not supported)
152
self.assertRaises(UnsupportedFormatError, repository.Repository.open, self.get_url())
153
# but open(unsupported) will work
154
self.assertEqual(format.open(dir), "opened repository.")
155
# unregister the format
156
repository.RepositoryFormat.unregister_format(format)
159
class TestFormat6(TestCaseWithTransport):
161
def test_no_ancestry_weave(self):
162
control = bzrdir.BzrDirFormat6().initialize(self.get_url())
163
repo = weaverepo.RepositoryFormat6().initialize(control)
164
# We no longer need to create the ancestry.weave file
165
# since it is *never* used.
166
self.assertRaises(NoSuchFile,
167
control.transport.get,
170
def test_supports_external_lookups(self):
171
control = bzrdir.BzrDirFormat6().initialize(self.get_url())
172
repo = weaverepo.RepositoryFormat6().initialize(control)
173
self.assertFalse(repo._format.supports_external_lookups)
176
class TestFormat7(TestCaseWithTransport):
178
def test_disk_layout(self):
179
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
180
repo = weaverepo.RepositoryFormat7().initialize(control)
181
# in case of side effects of locking.
185
# format 'Bazaar-NG Repository format 7'
187
# inventory.weave == empty_weave
188
# empty revision-store directory
189
# empty weaves directory
190
t = control.get_repository_transport(None)
191
self.assertEqualDiff('Bazaar-NG Repository format 7',
192
t.get('format').read())
193
self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
194
self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
195
self.assertEqualDiff('# bzr weave file v5\n'
198
t.get('inventory.weave').read())
199
# Creating a file with id Foo:Bar results in a non-escaped file name on
201
control.create_branch()
202
tree = control.create_workingtree()
203
tree.add(['foo'], ['Foo:Bar'], ['file'])
204
tree.put_file_bytes_non_atomic('Foo:Bar', 'content\n')
205
tree.commit('first post', rev_id='first')
206
self.assertEqualDiff(
207
'# bzr weave file v5\n'
209
'1 7fe70820e08a1aac0ef224d9c66ab66831cc4ab1\n'
217
t.get('weaves/74/Foo%3ABar.weave').read())
219
def test_shared_disk_layout(self):
220
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
221
repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
223
# format 'Bazaar-NG Repository format 7'
224
# inventory.weave == empty_weave
225
# empty revision-store directory
226
# empty weaves directory
227
# a 'shared-storage' marker file.
228
# lock is not present when unlocked
229
t = control.get_repository_transport(None)
230
self.assertEqualDiff('Bazaar-NG Repository format 7',
231
t.get('format').read())
232
self.assertEqualDiff('', t.get('shared-storage').read())
233
self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
234
self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
235
self.assertEqualDiff('# bzr weave file v5\n'
238
t.get('inventory.weave').read())
239
self.assertFalse(t.has('branch-lock'))
241
def test_creates_lockdir(self):
242
"""Make sure it appears to be controlled by a LockDir existence"""
243
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
244
repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
245
t = control.get_repository_transport(None)
246
# TODO: Should check there is a 'lock' toplevel directory,
247
# regardless of contents
248
self.assertFalse(t.has('lock/held/info'))
251
self.assertTrue(t.has('lock/held/info'))
253
# unlock so we don't get a warning about failing to do so
256
def test_uses_lockdir(self):
257
"""repo format 7 actually locks on lockdir"""
258
base_url = self.get_url()
259
control = bzrdir.BzrDirMetaFormat1().initialize(base_url)
260
repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
261
t = control.get_repository_transport(None)
265
# make sure the same lock is created by opening it
266
repo = repository.Repository.open(base_url)
268
self.assertTrue(t.has('lock/held/info'))
270
self.assertFalse(t.has('lock/held/info'))
272
def test_shared_no_tree_disk_layout(self):
273
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
274
repo = weaverepo.RepositoryFormat7().initialize(control, shared=True)
275
repo.set_make_working_trees(False)
277
# format 'Bazaar-NG Repository format 7'
279
# inventory.weave == empty_weave
280
# empty revision-store directory
281
# empty weaves directory
282
# a 'shared-storage' marker file.
283
t = control.get_repository_transport(None)
284
self.assertEqualDiff('Bazaar-NG Repository format 7',
285
t.get('format').read())
286
## self.assertEqualDiff('', t.get('lock').read())
287
self.assertEqualDiff('', t.get('shared-storage').read())
288
self.assertEqualDiff('', t.get('no-working-trees').read())
289
repo.set_make_working_trees(True)
290
self.assertFalse(t.has('no-working-trees'))
291
self.assertTrue(S_ISDIR(t.stat('revision-store').st_mode))
292
self.assertTrue(S_ISDIR(t.stat('weaves').st_mode))
293
self.assertEqualDiff('# bzr weave file v5\n'
296
t.get('inventory.weave').read())
298
def test_supports_external_lookups(self):
299
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
300
repo = weaverepo.RepositoryFormat7().initialize(control)
301
self.assertFalse(repo._format.supports_external_lookups)
304
class TestFormatKnit1(TestCaseWithTransport):
306
def test_disk_layout(self):
307
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
308
repo = knitrepo.RepositoryFormatKnit1().initialize(control)
309
# in case of side effects of locking.
313
# format 'Bazaar-NG Knit Repository Format 1'
314
# lock: is a directory
315
# inventory.weave == empty_weave
316
# empty revision-store directory
317
# empty weaves directory
318
t = control.get_repository_transport(None)
319
self.assertEqualDiff('Bazaar-NG Knit Repository Format 1',
320
t.get('format').read())
321
# XXX: no locks left when unlocked at the moment
322
# self.assertEqualDiff('', t.get('lock').read())
323
self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
325
# Check per-file knits.
326
branch = control.create_branch()
327
tree = control.create_workingtree()
328
tree.add(['foo'], ['Nasty-IdC:'], ['file'])
329
tree.put_file_bytes_non_atomic('Nasty-IdC:', '')
330
tree.commit('1st post', rev_id='foo')
331
self.assertHasKnit(t, 'knits/e8/%254easty-%2549d%2543%253a',
332
'\nfoo fulltext 0 81 :')
334
def assertHasKnit(self, t, knit_name, extra_content=''):
335
"""Assert that knit_name exists on t."""
336
self.assertEqualDiff('# bzr knit index 8\n' + extra_content,
337
t.get(knit_name + '.kndx').read())
339
def check_knits(self, t):
340
"""check knit content for a repository."""
341
self.assertHasKnit(t, 'inventory')
342
self.assertHasKnit(t, 'revisions')
343
self.assertHasKnit(t, 'signatures')
345
def test_shared_disk_layout(self):
346
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
347
repo = knitrepo.RepositoryFormatKnit1().initialize(control, shared=True)
349
# format 'Bazaar-NG Knit Repository Format 1'
350
# lock: is a directory
351
# inventory.weave == empty_weave
352
# empty revision-store directory
353
# empty weaves directory
354
# a 'shared-storage' marker file.
355
t = control.get_repository_transport(None)
356
self.assertEqualDiff('Bazaar-NG Knit Repository Format 1',
357
t.get('format').read())
358
# XXX: no locks left when unlocked at the moment
359
# self.assertEqualDiff('', t.get('lock').read())
360
self.assertEqualDiff('', t.get('shared-storage').read())
361
self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
364
def test_shared_no_tree_disk_layout(self):
365
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
366
repo = knitrepo.RepositoryFormatKnit1().initialize(control, shared=True)
367
repo.set_make_working_trees(False)
369
# format 'Bazaar-NG Knit Repository Format 1'
371
# inventory.weave == empty_weave
372
# empty revision-store directory
373
# empty weaves directory
374
# a 'shared-storage' marker file.
375
t = control.get_repository_transport(None)
376
self.assertEqualDiff('Bazaar-NG Knit Repository Format 1',
377
t.get('format').read())
378
# XXX: no locks left when unlocked at the moment
379
# self.assertEqualDiff('', t.get('lock').read())
380
self.assertEqualDiff('', t.get('shared-storage').read())
381
self.assertEqualDiff('', t.get('no-working-trees').read())
382
repo.set_make_working_trees(True)
383
self.assertFalse(t.has('no-working-trees'))
384
self.assertTrue(S_ISDIR(t.stat('knits').st_mode))
387
def test_deserialise_sets_root_revision(self):
388
"""We must have a inventory.root.revision
390
Old versions of the XML5 serializer did not set the revision_id for
391
the whole inventory. So we grab the one from the expected text. Which
392
is valid when the api is not being abused.
394
repo = self.make_repository('.',
395
format=bzrdir.format_registry.get('knit')())
396
inv_xml = '<inventory format="5">\n</inventory>\n'
397
inv = repo.deserialise_inventory('test-rev-id', inv_xml)
398
self.assertEqual('test-rev-id', inv.root.revision)
400
def test_deserialise_uses_global_revision_id(self):
401
"""If it is set, then we re-use the global revision id"""
402
repo = self.make_repository('.',
403
format=bzrdir.format_registry.get('knit')())
404
inv_xml = ('<inventory format="5" revision_id="other-rev-id">\n'
406
# Arguably, the deserialise_inventory should detect a mismatch, and
407
# raise an error, rather than silently using one revision_id over the
409
self.assertRaises(AssertionError, repo.deserialise_inventory,
410
'test-rev-id', inv_xml)
411
inv = repo.deserialise_inventory('other-rev-id', inv_xml)
412
self.assertEqual('other-rev-id', inv.root.revision)
414
def test_supports_external_lookups(self):
415
repo = self.make_repository('.',
416
format=bzrdir.format_registry.get('knit')())
417
self.assertFalse(repo._format.supports_external_lookups)
420
class DummyRepository(object):
421
"""A dummy repository for testing."""
425
def supports_rich_root(self):
429
class InterDummy(repository.InterRepository):
430
"""An inter-repository optimised code path for DummyRepository.
432
This is for use during testing where we use DummyRepository as repositories
433
so that none of the default regsitered inter-repository classes will
438
def is_compatible(repo_source, repo_target):
439
"""InterDummy is compatible with DummyRepository."""
440
return (isinstance(repo_source, DummyRepository) and
441
isinstance(repo_target, DummyRepository))
444
class TestInterRepository(TestCaseWithTransport):
446
def test_get_default_inter_repository(self):
447
# test that the InterRepository.get(repo_a, repo_b) probes
448
# for a inter_repo class where is_compatible(repo_a, repo_b) returns
449
# true and returns a default inter_repo otherwise.
450
# This also tests that the default registered optimised interrepository
451
# classes do not barf inappropriately when a surprising repository type
453
dummy_a = DummyRepository()
454
dummy_b = DummyRepository()
455
self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
457
def assertGetsDefaultInterRepository(self, repo_a, repo_b):
458
"""Asserts that InterRepository.get(repo_a, repo_b) -> the default.
460
The effective default is now InterSameDataRepository because there is
461
no actual sane default in the presence of incompatible data models.
463
inter_repo = repository.InterRepository.get(repo_a, repo_b)
464
self.assertEqual(repository.InterSameDataRepository,
465
inter_repo.__class__)
466
self.assertEqual(repo_a, inter_repo.source)
467
self.assertEqual(repo_b, inter_repo.target)
469
def test_register_inter_repository_class(self):
470
# test that a optimised code path provider - a
471
# InterRepository subclass can be registered and unregistered
472
# and that it is correctly selected when given a repository
473
# pair that it returns true on for the is_compatible static method
475
dummy_a = DummyRepository()
476
dummy_b = DummyRepository()
477
repo = self.make_repository('.')
478
# hack dummies to look like repo somewhat.
479
dummy_a._serializer = repo._serializer
480
dummy_b._serializer = repo._serializer
481
repository.InterRepository.register_optimiser(InterDummy)
483
# we should get the default for something InterDummy returns False
485
self.assertFalse(InterDummy.is_compatible(dummy_a, repo))
486
self.assertGetsDefaultInterRepository(dummy_a, repo)
487
# and we should get an InterDummy for a pair it 'likes'
488
self.assertTrue(InterDummy.is_compatible(dummy_a, dummy_b))
489
inter_repo = repository.InterRepository.get(dummy_a, dummy_b)
490
self.assertEqual(InterDummy, inter_repo.__class__)
491
self.assertEqual(dummy_a, inter_repo.source)
492
self.assertEqual(dummy_b, inter_repo.target)
494
repository.InterRepository.unregister_optimiser(InterDummy)
495
# now we should get the default InterRepository object again.
496
self.assertGetsDefaultInterRepository(dummy_a, dummy_b)
499
class TestInterWeaveRepo(TestCaseWithTransport):
501
def test_is_compatible_and_registered(self):
502
# InterWeaveRepo is compatible when either side
503
# is a format 5/6/7 branch
504
from bzrlib.repofmt import knitrepo, weaverepo
505
formats = [weaverepo.RepositoryFormat5(),
506
weaverepo.RepositoryFormat6(),
507
weaverepo.RepositoryFormat7()]
508
incompatible_formats = [weaverepo.RepositoryFormat4(),
509
knitrepo.RepositoryFormatKnit1(),
511
repo_a = self.make_repository('a')
512
repo_b = self.make_repository('b')
513
is_compatible = repository.InterWeaveRepo.is_compatible
514
for source in incompatible_formats:
515
# force incompatible left then right
516
repo_a._format = source
517
repo_b._format = formats[0]
518
self.assertFalse(is_compatible(repo_a, repo_b))
519
self.assertFalse(is_compatible(repo_b, repo_a))
520
for source in formats:
521
repo_a._format = source
522
for target in formats:
523
repo_b._format = target
524
self.assertTrue(is_compatible(repo_a, repo_b))
525
self.assertEqual(repository.InterWeaveRepo,
526
repository.InterRepository.get(repo_a,
530
class TestRepositoryConverter(TestCaseWithTransport):
532
def test_convert_empty(self):
533
t = get_transport(self.get_url('.'))
534
t.mkdir('repository')
535
repo_dir = bzrdir.BzrDirMetaFormat1().initialize('repository')
536
repo = weaverepo.RepositoryFormat7().initialize(repo_dir)
537
target_format = knitrepo.RepositoryFormatKnit1()
538
converter = repository.CopyConverter(target_format)
539
pb = bzrlib.ui.ui_factory.nested_progress_bar()
541
converter.convert(repo, pb)
544
repo = repo_dir.open_repository()
545
self.assertTrue(isinstance(target_format, repo._format.__class__))
548
class TestMisc(TestCase):
550
def test_unescape_xml(self):
551
"""We get some kind of error when malformed entities are passed"""
552
self.assertRaises(KeyError, repository._unescape_xml, 'foo&bar;')
555
class TestRepositoryFormatKnit3(TestCaseWithTransport):
557
def test_convert(self):
558
"""Ensure the upgrade adds weaves for roots"""
559
format = bzrdir.BzrDirMetaFormat1()
560
format.repository_format = knitrepo.RepositoryFormatKnit1()
561
tree = self.make_branch_and_tree('.', format)
562
tree.commit("Dull commit", rev_id="dull")
563
revision_tree = tree.branch.repository.revision_tree('dull')
564
revision_tree.lock_read()
566
self.assertRaises(errors.NoSuchFile, revision_tree.get_file_lines,
567
revision_tree.inventory.root.file_id)
569
revision_tree.unlock()
570
format = bzrdir.BzrDirMetaFormat1()
571
format.repository_format = knitrepo.RepositoryFormatKnit3()
572
upgrade.Convert('.', format)
573
tree = workingtree.WorkingTree.open('.')
574
revision_tree = tree.branch.repository.revision_tree('dull')
575
revision_tree.lock_read()
577
revision_tree.get_file_lines(revision_tree.inventory.root.file_id)
579
revision_tree.unlock()
580
tree.commit("Another dull commit", rev_id='dull2')
581
revision_tree = tree.branch.repository.revision_tree('dull2')
582
revision_tree.lock_read()
583
self.addCleanup(revision_tree.unlock)
584
self.assertEqual('dull', revision_tree.inventory.root.revision)
586
def test_supports_external_lookups(self):
587
format = bzrdir.BzrDirMetaFormat1()
588
format.repository_format = knitrepo.RepositoryFormatKnit3()
589
repo = self.make_repository('.', format=format)
590
self.assertFalse(repo._format.supports_external_lookups)
593
class TestWithBrokenRepo(TestCaseWithTransport):
594
"""These tests seem to be more appropriate as interface tests?"""
596
def make_broken_repository(self):
597
# XXX: This function is borrowed from Aaron's "Reconcile can fix bad
598
# parent references" branch which is due to land in bzr.dev soon. Once
599
# it does, this duplication should be removed.
600
repo = self.make_repository('broken-repo')
604
cleanups.append(repo.unlock)
605
repo.start_write_group()
606
cleanups.append(repo.commit_write_group)
607
# make rev1a: A well-formed revision, containing 'file1'
608
inv = inventory.Inventory(revision_id='rev1a')
609
inv.root.revision = 'rev1a'
610
self.add_file(repo, inv, 'file1', 'rev1a', [])
611
repo.add_inventory('rev1a', inv, [])
612
revision = _mod_revision.Revision('rev1a',
613
committer='jrandom@example.com', timestamp=0,
614
inventory_sha1='', timezone=0, message='foo', parent_ids=[])
615
repo.add_revision('rev1a',revision, inv)
617
# make rev1b, which has no Revision, but has an Inventory, and
619
inv = inventory.Inventory(revision_id='rev1b')
620
inv.root.revision = 'rev1b'
621
self.add_file(repo, inv, 'file1', 'rev1b', [])
622
repo.add_inventory('rev1b', inv, [])
624
# make rev2, with file1 and file2
626
# file1 has 'rev1b' as an ancestor, even though this is not
627
# mentioned by 'rev1a', making it an unreferenced ancestor
628
inv = inventory.Inventory()
629
self.add_file(repo, inv, 'file1', 'rev2', ['rev1a', 'rev1b'])
630
self.add_file(repo, inv, 'file2', 'rev2', [])
631
self.add_revision(repo, 'rev2', inv, ['rev1a'])
633
# make ghost revision rev1c
634
inv = inventory.Inventory()
635
self.add_file(repo, inv, 'file2', 'rev1c', [])
637
# make rev3 with file2
638
# file2 refers to 'rev1c', which is a ghost in this repository, so
639
# file2 cannot have rev1c as its ancestor.
640
inv = inventory.Inventory()
641
self.add_file(repo, inv, 'file2', 'rev3', ['rev1c'])
642
self.add_revision(repo, 'rev3', inv, ['rev1c'])
645
for cleanup in reversed(cleanups):
648
def add_revision(self, repo, revision_id, inv, parent_ids):
649
inv.revision_id = revision_id
650
inv.root.revision = revision_id
651
repo.add_inventory(revision_id, inv, parent_ids)
652
revision = _mod_revision.Revision(revision_id,
653
committer='jrandom@example.com', timestamp=0, inventory_sha1='',
654
timezone=0, message='foo', parent_ids=parent_ids)
655
repo.add_revision(revision_id,revision, inv)
657
def add_file(self, repo, inv, filename, revision, parents):
658
file_id = filename + '-id'
659
entry = inventory.InventoryFile(file_id, filename, 'TREE_ROOT')
660
entry.revision = revision
663
text_key = (file_id, revision)
664
parent_keys = [(file_id, parent) for parent in parents]
665
repo.texts.add_lines(text_key, parent_keys, ['line\n'])
667
def test_insert_from_broken_repo(self):
668
"""Inserting a data stream from a broken repository won't silently
669
corrupt the target repository.
671
broken_repo = self.make_broken_repository()
672
empty_repo = self.make_repository('empty-repo')
673
self.assertRaises(errors.RevisionNotPresent, empty_repo.fetch, broken_repo)
676
class TestKnitPackNoSubtrees(TestCaseWithTransport):
678
def get_format(self):
679
return bzrdir.format_registry.make_bzrdir('pack-0.92')
681
def test_disk_layout(self):
682
format = self.get_format()
683
repo = self.make_repository('.', format=format)
684
# in case of side effects of locking.
687
t = repo.bzrdir.get_repository_transport(None)
689
# XXX: no locks left when unlocked at the moment
690
# self.assertEqualDiff('', t.get('lock').read())
691
self.check_databases(t)
693
def check_format(self, t):
694
self.assertEqualDiff(
695
"Bazaar pack repository format 1 (needs bzr 0.92)\n",
696
t.get('format').read())
698
def assertHasNoKndx(self, t, knit_name):
699
"""Assert that knit_name has no index on t."""
700
self.assertFalse(t.has(knit_name + '.kndx'))
702
def assertHasNoKnit(self, t, knit_name):
703
"""Assert that knit_name exists on t."""
705
self.assertFalse(t.has(knit_name + '.knit'))
707
def check_databases(self, t):
708
"""check knit content for a repository."""
709
# check conversion worked
710
self.assertHasNoKndx(t, 'inventory')
711
self.assertHasNoKnit(t, 'inventory')
712
self.assertHasNoKndx(t, 'revisions')
713
self.assertHasNoKnit(t, 'revisions')
714
self.assertHasNoKndx(t, 'signatures')
715
self.assertHasNoKnit(t, 'signatures')
716
self.assertFalse(t.has('knits'))
717
# revision-indexes file-container directory
719
list(GraphIndex(t, 'pack-names', None).iter_all_entries()))
720
self.assertTrue(S_ISDIR(t.stat('packs').st_mode))
721
self.assertTrue(S_ISDIR(t.stat('upload').st_mode))
722
self.assertTrue(S_ISDIR(t.stat('indices').st_mode))
723
self.assertTrue(S_ISDIR(t.stat('obsolete_packs').st_mode))
725
def test_shared_disk_layout(self):
726
format = self.get_format()
727
repo = self.make_repository('.', shared=True, format=format)
729
t = repo.bzrdir.get_repository_transport(None)
731
# XXX: no locks left when unlocked at the moment
732
# self.assertEqualDiff('', t.get('lock').read())
733
# We should have a 'shared-storage' marker file.
734
self.assertEqualDiff('', t.get('shared-storage').read())
735
self.check_databases(t)
737
def test_shared_no_tree_disk_layout(self):
738
format = self.get_format()
739
repo = self.make_repository('.', shared=True, format=format)
740
repo.set_make_working_trees(False)
742
t = repo.bzrdir.get_repository_transport(None)
744
# XXX: no locks left when unlocked at the moment
745
# self.assertEqualDiff('', t.get('lock').read())
746
# We should have a 'shared-storage' marker file.
747
self.assertEqualDiff('', t.get('shared-storage').read())
748
# We should have a marker for the no-working-trees flag.
749
self.assertEqualDiff('', t.get('no-working-trees').read())
750
# The marker should go when we toggle the setting.
751
repo.set_make_working_trees(True)
752
self.assertFalse(t.has('no-working-trees'))
753
self.check_databases(t)
755
def test_adding_revision_creates_pack_indices(self):
756
format = self.get_format()
757
tree = self.make_branch_and_tree('.', format=format)
758
trans = tree.branch.repository.bzrdir.get_repository_transport(None)
760
list(GraphIndex(trans, 'pack-names', None).iter_all_entries()))
761
tree.commit('foobarbaz')
762
index = GraphIndex(trans, 'pack-names', None)
763
index_nodes = list(index.iter_all_entries())
764
self.assertEqual(1, len(index_nodes))
765
node = index_nodes[0]
767
# the pack sizes should be listed in the index
769
sizes = [int(digits) for digits in pack_value.split(' ')]
770
for size, suffix in zip(sizes, ['.rix', '.iix', '.tix', '.six']):
771
stat = trans.stat('indices/%s%s' % (name, suffix))
772
self.assertEqual(size, stat.st_size)
774
def test_pulling_nothing_leads_to_no_new_names(self):
775
format = self.get_format()
776
tree1 = self.make_branch_and_tree('1', format=format)
777
tree2 = self.make_branch_and_tree('2', format=format)
778
tree1.branch.repository.fetch(tree2.branch.repository)
779
trans = tree1.branch.repository.bzrdir.get_repository_transport(None)
781
list(GraphIndex(trans, 'pack-names', None).iter_all_entries()))
783
def test_commit_across_pack_shape_boundary_autopacks(self):
784
format = self.get_format()
785
tree = self.make_branch_and_tree('.', format=format)
786
trans = tree.branch.repository.bzrdir.get_repository_transport(None)
787
# This test could be a little cheaper by replacing the packs
788
# attribute on the repository to allow a different pack distribution
789
# and max packs policy - so we are checking the policy is honoured
790
# in the test. But for now 11 commits is not a big deal in a single
793
tree.commit('commit %s' % x)
794
# there should be 9 packs:
795
index = GraphIndex(trans, 'pack-names', None)
796
self.assertEqual(9, len(list(index.iter_all_entries())))
797
# insert some files in obsolete_packs which should be removed by pack.
798
trans.put_bytes('obsolete_packs/foo', '123')
799
trans.put_bytes('obsolete_packs/bar', '321')
800
# committing one more should coalesce to 1 of 10.
801
tree.commit('commit triggering pack')
802
index = GraphIndex(trans, 'pack-names', None)
803
self.assertEqual(1, len(list(index.iter_all_entries())))
804
# packing should not damage data
805
tree = tree.bzrdir.open_workingtree()
806
check_result = tree.branch.repository.check(
807
[tree.branch.last_revision()])
808
# We should have 50 (10x5) files in the obsolete_packs directory.
809
obsolete_files = list(trans.list_dir('obsolete_packs'))
810
self.assertFalse('foo' in obsolete_files)
811
self.assertFalse('bar' in obsolete_files)
812
self.assertEqual(50, len(obsolete_files))
813
# XXX: Todo check packs obsoleted correctly - old packs and indices
814
# in the obsolete_packs directory.
815
large_pack_name = list(index.iter_all_entries())[0][1][0]
816
# finally, committing again should not touch the large pack.
817
tree.commit('commit not triggering pack')
818
index = GraphIndex(trans, 'pack-names', None)
819
self.assertEqual(2, len(list(index.iter_all_entries())))
820
pack_names = [node[1][0] for node in index.iter_all_entries()]
821
self.assertTrue(large_pack_name in pack_names)
823
def test_pack_after_two_commits_packs_everything(self):
824
format = self.get_format()
825
tree = self.make_branch_and_tree('.', format=format)
826
trans = tree.branch.repository.bzrdir.get_repository_transport(None)
828
tree.commit('more work')
829
tree.branch.repository.pack()
830
# there should be 1 pack:
831
index = GraphIndex(trans, 'pack-names', None)
832
self.assertEqual(1, len(list(index.iter_all_entries())))
833
self.assertEqual(2, len(tree.branch.repository.all_revision_ids()))
835
def test_pack_layout(self):
836
format = self.get_format()
837
tree = self.make_branch_and_tree('.', format=format)
838
trans = tree.branch.repository.bzrdir.get_repository_transport(None)
839
tree.commit('start', rev_id='1')
840
tree.commit('more work', rev_id='2')
841
tree.branch.repository.pack()
843
self.addCleanup(tree.unlock)
844
pack = tree.branch.repository._pack_collection.get_pack_by_name(
845
tree.branch.repository._pack_collection.names()[0])
846
# revision access tends to be tip->ancestor, so ordering that way on
847
# disk is a good idea.
848
for _1, key, val, refs in pack.revision_index.iter_all_entries():
850
pos_1 = int(val[1:].split()[0])
852
pos_2 = int(val[1:].split()[0])
853
self.assertTrue(pos_2 < pos_1)
855
def test_pack_repositories_support_multiple_write_locks(self):
856
format = self.get_format()
857
self.make_repository('.', shared=True, format=format)
858
r1 = repository.Repository.open('.')
859
r2 = repository.Repository.open('.')
861
self.addCleanup(r1.unlock)
865
def _add_text(self, repo, fileid):
866
"""Add a text to the repository within a write group."""
867
repo.texts.add_lines((fileid, 'samplerev+'+fileid), [], [])
869
def test_concurrent_writers_merge_new_packs(self):
870
format = self.get_format()
871
self.make_repository('.', shared=True, format=format)
872
r1 = repository.Repository.open('.')
873
r2 = repository.Repository.open('.')
876
# access enough data to load the names list
877
list(r1.all_revision_ids())
880
# access enough data to load the names list
881
list(r2.all_revision_ids())
882
r1.start_write_group()
884
r2.start_write_group()
886
self._add_text(r1, 'fileidr1')
887
self._add_text(r2, 'fileidr2')
889
r2.abort_write_group()
892
r1.abort_write_group()
894
# both r1 and r2 have open write groups with data in them
895
# created while the other's write group was open.
896
# Commit both which requires a merge to the pack-names.
898
r1.commit_write_group()
900
r1.abort_write_group()
901
r2.abort_write_group()
903
r2.commit_write_group()
904
# tell r1 to reload from disk
905
r1._pack_collection.reset()
906
# Now both repositories should know about both names
907
r1._pack_collection.ensure_loaded()
908
r2._pack_collection.ensure_loaded()
909
self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
910
self.assertEqual(2, len(r1._pack_collection.names()))
916
def test_concurrent_writer_second_preserves_dropping_a_pack(self):
917
format = self.get_format()
918
self.make_repository('.', shared=True, format=format)
919
r1 = repository.Repository.open('.')
920
r2 = repository.Repository.open('.')
924
r1.start_write_group()
926
self._add_text(r1, 'fileidr1')
928
r1.abort_write_group()
931
r1.commit_write_group()
932
r1._pack_collection.ensure_loaded()
933
name_to_drop = r1._pack_collection.all_packs()[0].name
938
# access enough data to load the names list
939
list(r1.all_revision_ids())
942
# access enough data to load the names list
943
list(r2.all_revision_ids())
944
r1._pack_collection.ensure_loaded()
946
r2.start_write_group()
948
# in r1, drop the pack
949
r1._pack_collection._remove_pack_from_memory(
950
r1._pack_collection.get_pack_by_name(name_to_drop))
952
self._add_text(r2, 'fileidr2')
954
r2.abort_write_group()
957
r1._pack_collection.reset()
959
# r1 has a changed names list, and r2 an open write groups with
961
# save r1, and then commit the r2 write group, which requires a
962
# merge to the pack-names, which should not reinstate
965
r1._pack_collection._save_pack_names()
966
r1._pack_collection.reset()
968
r2.abort_write_group()
971
r2.commit_write_group()
973
r2.abort_write_group()
975
# Now both repositories should now about just one name.
976
r1._pack_collection.ensure_loaded()
977
r2._pack_collection.ensure_loaded()
978
self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
979
self.assertEqual(1, len(r1._pack_collection.names()))
980
self.assertFalse(name_to_drop in r1._pack_collection.names())
986
def test_lock_write_does_not_physically_lock(self):
987
repo = self.make_repository('.', format=self.get_format())
989
self.addCleanup(repo.unlock)
990
self.assertFalse(repo.get_physical_lock_status())
992
def prepare_for_break_lock(self):
993
# Setup the global ui factory state so that a break-lock method call
994
# will find usable input in the input stream.
995
old_factory = bzrlib.ui.ui_factory
996
def restoreFactory():
997
bzrlib.ui.ui_factory = old_factory
998
self.addCleanup(restoreFactory)
999
bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
1000
bzrlib.ui.ui_factory.stdin = StringIO("y\n")
1002
def test_break_lock_breaks_physical_lock(self):
1003
repo = self.make_repository('.', format=self.get_format())
1004
repo._pack_collection.lock_names()
1005
repo2 = repository.Repository.open('.')
1006
self.assertTrue(repo.get_physical_lock_status())
1007
self.prepare_for_break_lock()
1009
self.assertFalse(repo.get_physical_lock_status())
1011
def test_broken_physical_locks_error_on__unlock_names_lock(self):
1012
repo = self.make_repository('.', format=self.get_format())
1013
repo._pack_collection.lock_names()
1014
self.assertTrue(repo.get_physical_lock_status())
1015
repo2 = repository.Repository.open('.')
1016
self.prepare_for_break_lock()
1018
self.assertRaises(errors.LockBroken, repo._pack_collection._unlock_names)
1020
def test_fetch_without_find_ghosts_ignores_ghosts(self):
1021
# we want two repositories at this point:
1022
# one with a revision that is a ghost in the other
1024
# 'ghost' is present in has_ghost, 'ghost' is absent in 'missing_ghost'.
1025
# 'references' is present in both repositories, and 'tip' is present
1026
# just in has_ghost.
1027
# has_ghost missing_ghost
1028
#------------------------------
1030
# 'references' 'references'
1032
# In this test we fetch 'tip' which should not fetch 'ghost'
1033
has_ghost = self.make_repository('has_ghost', format=self.get_format())
1034
missing_ghost = self.make_repository('missing_ghost',
1035
format=self.get_format())
1037
def add_commit(repo, revision_id, parent_ids):
1039
repo.start_write_group()
1040
inv = inventory.Inventory(revision_id=revision_id)
1041
inv.root.revision = revision_id
1042
root_id = inv.root.file_id
1043
sha1 = repo.add_inventory(revision_id, inv, [])
1044
repo.texts.add_lines((root_id, revision_id), [], [])
1045
rev = bzrlib.revision.Revision(timestamp=0,
1047
committer="Foo Bar <foo@example.com>",
1049
inventory_sha1=sha1,
1050
revision_id=revision_id)
1051
rev.parent_ids = parent_ids
1052
repo.add_revision(revision_id, rev)
1053
repo.commit_write_group()
1055
add_commit(has_ghost, 'ghost', [])
1056
add_commit(has_ghost, 'references', ['ghost'])
1057
add_commit(missing_ghost, 'references', ['ghost'])
1058
add_commit(has_ghost, 'tip', ['references'])
1059
missing_ghost.fetch(has_ghost, 'tip')
1060
# missing ghost now has tip and not ghost.
1061
rev = missing_ghost.get_revision('tip')
1062
inv = missing_ghost.get_inventory('tip')
1063
self.assertRaises(errors.NoSuchRevision,
1064
missing_ghost.get_revision, 'ghost')
1065
self.assertRaises(errors.NoSuchRevision,
1066
missing_ghost.get_inventory, 'ghost')
1068
def test_supports_external_lookups(self):
1069
repo = self.make_repository('.', format=self.get_format())
1070
self.assertFalse(repo._format.supports_external_lookups)
1073
class TestKnitPackSubtrees(TestKnitPackNoSubtrees):
1075
def get_format(self):
1076
return bzrdir.format_registry.make_bzrdir(
1077
'pack-0.92-subtree')
1079
def check_format(self, t):
1080
self.assertEqualDiff(
1081
"Bazaar pack repository format 1 with subtree support (needs bzr 0.92)\n",
1082
t.get('format').read())
1085
class TestDevelopment0(TestKnitPackNoSubtrees):
1087
def get_format(self):
1088
return bzrdir.format_registry.make_bzrdir(
1091
def check_format(self, t):
1092
self.assertEqualDiff(
1093
"Bazaar development format 0 (needs bzr.dev from before 1.3)\n",
1094
t.get('format').read())
1097
class TestDevelopment0Subtree(TestKnitPackNoSubtrees):
1099
def get_format(self):
1100
return bzrdir.format_registry.make_bzrdir(
1101
'development-subtree')
1103
def check_format(self, t):
1104
self.assertEqualDiff(
1105
"Bazaar development format 0 with subtree support "
1106
"(needs bzr.dev from before 1.3)\n",
1107
t.get('format').read())
1110
class TestRepositoryPackCollection(TestCaseWithTransport):
1112
def get_format(self):
1113
return bzrdir.format_registry.make_bzrdir('pack-0.92')
1115
def test__max_pack_count(self):
1116
"""The maximum pack count is a function of the number of revisions."""
1117
format = self.get_format()
1118
repo = self.make_repository('.', format=format)
1119
packs = repo._pack_collection
1120
# no revisions - one pack, so that we can have a revision free repo
1121
# without it blowing up
1122
self.assertEqual(1, packs._max_pack_count(0))
1123
# after that the sum of the digits, - check the first 1-9
1124
self.assertEqual(1, packs._max_pack_count(1))
1125
self.assertEqual(2, packs._max_pack_count(2))
1126
self.assertEqual(3, packs._max_pack_count(3))
1127
self.assertEqual(4, packs._max_pack_count(4))
1128
self.assertEqual(5, packs._max_pack_count(5))
1129
self.assertEqual(6, packs._max_pack_count(6))
1130
self.assertEqual(7, packs._max_pack_count(7))
1131
self.assertEqual(8, packs._max_pack_count(8))
1132
self.assertEqual(9, packs._max_pack_count(9))
1133
# check the boundary cases with two digits for the next decade
1134
self.assertEqual(1, packs._max_pack_count(10))
1135
self.assertEqual(2, packs._max_pack_count(11))
1136
self.assertEqual(10, packs._max_pack_count(19))
1137
self.assertEqual(2, packs._max_pack_count(20))
1138
self.assertEqual(3, packs._max_pack_count(21))
1139
# check some arbitrary big numbers
1140
self.assertEqual(25, packs._max_pack_count(112894))
1142
def test_pack_distribution_zero(self):
1143
format = self.get_format()
1144
repo = self.make_repository('.', format=format)
1145
packs = repo._pack_collection
1146
self.assertEqual([0], packs.pack_distribution(0))
1148
def test_ensure_loaded_unlocked(self):
1149
format = self.get_format()
1150
repo = self.make_repository('.', format=format)
1151
self.assertRaises(errors.ObjectNotLocked,
1152
repo._pack_collection.ensure_loaded)
1154
def test_pack_distribution_one_to_nine(self):
1155
format = self.get_format()
1156
repo = self.make_repository('.', format=format)
1157
packs = repo._pack_collection
1158
self.assertEqual([1],
1159
packs.pack_distribution(1))
1160
self.assertEqual([1, 1],
1161
packs.pack_distribution(2))
1162
self.assertEqual([1, 1, 1],
1163
packs.pack_distribution(3))
1164
self.assertEqual([1, 1, 1, 1],
1165
packs.pack_distribution(4))
1166
self.assertEqual([1, 1, 1, 1, 1],
1167
packs.pack_distribution(5))
1168
self.assertEqual([1, 1, 1, 1, 1, 1],
1169
packs.pack_distribution(6))
1170
self.assertEqual([1, 1, 1, 1, 1, 1, 1],
1171
packs.pack_distribution(7))
1172
self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1],
1173
packs.pack_distribution(8))
1174
self.assertEqual([1, 1, 1, 1, 1, 1, 1, 1, 1],
1175
packs.pack_distribution(9))
1177
def test_pack_distribution_stable_at_boundaries(self):
1178
"""When there are multi-rev packs the counts are stable."""
1179
format = self.get_format()
1180
repo = self.make_repository('.', format=format)
1181
packs = repo._pack_collection
1183
self.assertEqual([10], packs.pack_distribution(10))
1184
self.assertEqual([10, 1], packs.pack_distribution(11))
1185
self.assertEqual([10, 10], packs.pack_distribution(20))
1186
self.assertEqual([10, 10, 1], packs.pack_distribution(21))
1188
self.assertEqual([100], packs.pack_distribution(100))
1189
self.assertEqual([100, 1], packs.pack_distribution(101))
1190
self.assertEqual([100, 10, 1], packs.pack_distribution(111))
1191
self.assertEqual([100, 100], packs.pack_distribution(200))
1192
self.assertEqual([100, 100, 1], packs.pack_distribution(201))
1193
self.assertEqual([100, 100, 10, 1], packs.pack_distribution(211))
1195
def test_plan_pack_operations_2009_revisions_skip_all_packs(self):
1196
format = self.get_format()
1197
repo = self.make_repository('.', format=format)
1198
packs = repo._pack_collection
1199
existing_packs = [(2000, "big"), (9, "medium")]
1200
# rev count - 2009 -> 2x1000 + 9x1
1201
pack_operations = packs.plan_autopack_combinations(
1202
existing_packs, [1000, 1000, 1, 1, 1, 1, 1, 1, 1, 1, 1])
1203
self.assertEqual([], pack_operations)
1205
def test_plan_pack_operations_2010_revisions_skip_all_packs(self):
1206
format = self.get_format()
1207
repo = self.make_repository('.', format=format)
1208
packs = repo._pack_collection
1209
existing_packs = [(2000, "big"), (9, "medium"), (1, "single")]
1210
# rev count - 2010 -> 2x1000 + 1x10
1211
pack_operations = packs.plan_autopack_combinations(
1212
existing_packs, [1000, 1000, 10])
1213
self.assertEqual([], pack_operations)
1215
def test_plan_pack_operations_2010_combines_smallest_two(self):
1216
format = self.get_format()
1217
repo = self.make_repository('.', format=format)
1218
packs = repo._pack_collection
1219
existing_packs = [(1999, "big"), (9, "medium"), (1, "single2"),
1221
# rev count - 2010 -> 2x1000 + 1x10 (3)
1222
pack_operations = packs.plan_autopack_combinations(
1223
existing_packs, [1000, 1000, 10])
1224
self.assertEqual([[2, ["single2", "single1"]], [0, []]], pack_operations)
1226
def test_all_packs_none(self):
1227
format = self.get_format()
1228
tree = self.make_branch_and_tree('.', format=format)
1230
self.addCleanup(tree.unlock)
1231
packs = tree.branch.repository._pack_collection
1232
packs.ensure_loaded()
1233
self.assertEqual([], packs.all_packs())
1235
def test_all_packs_one(self):
1236
format = self.get_format()
1237
tree = self.make_branch_and_tree('.', format=format)
1238
tree.commit('start')
1240
self.addCleanup(tree.unlock)
1241
packs = tree.branch.repository._pack_collection
1242
packs.ensure_loaded()
1244
packs.get_pack_by_name(packs.names()[0])],
1247
def test_all_packs_two(self):
1248
format = self.get_format()
1249
tree = self.make_branch_and_tree('.', format=format)
1250
tree.commit('start')
1251
tree.commit('continue')
1253
self.addCleanup(tree.unlock)
1254
packs = tree.branch.repository._pack_collection
1255
packs.ensure_loaded()
1257
packs.get_pack_by_name(packs.names()[0]),
1258
packs.get_pack_by_name(packs.names()[1]),
1259
], packs.all_packs())
1261
def test_get_pack_by_name(self):
1262
format = self.get_format()
1263
tree = self.make_branch_and_tree('.', format=format)
1264
tree.commit('start')
1266
self.addCleanup(tree.unlock)
1267
packs = tree.branch.repository._pack_collection
1268
packs.ensure_loaded()
1269
name = packs.names()[0]
1270
pack_1 = packs.get_pack_by_name(name)
1271
# the pack should be correctly initialised
1272
rev_index = GraphIndex(packs._index_transport, name + '.rix',
1273
packs._names[name][0])
1274
inv_index = GraphIndex(packs._index_transport, name + '.iix',
1275
packs._names[name][1])
1276
txt_index = GraphIndex(packs._index_transport, name + '.tix',
1277
packs._names[name][2])
1278
sig_index = GraphIndex(packs._index_transport, name + '.six',
1279
packs._names[name][3])
1280
self.assertEqual(pack_repo.ExistingPack(packs._pack_transport,
1281
name, rev_index, inv_index, txt_index, sig_index), pack_1)
1282
# and the same instance should be returned on successive calls.
1283
self.assertTrue(pack_1 is packs.get_pack_by_name(name))
1286
class TestPack(TestCaseWithTransport):
1287
"""Tests for the Pack object."""
1289
def assertCurrentlyEqual(self, left, right):
1290
self.assertTrue(left == right)
1291
self.assertTrue(right == left)
1292
self.assertFalse(left != right)
1293
self.assertFalse(right != left)
1295
def assertCurrentlyNotEqual(self, left, right):
1296
self.assertFalse(left == right)
1297
self.assertFalse(right == left)
1298
self.assertTrue(left != right)
1299
self.assertTrue(right != left)
1301
def test___eq____ne__(self):
1302
left = pack_repo.ExistingPack('', '', '', '', '', '')
1303
right = pack_repo.ExistingPack('', '', '', '', '', '')
1304
self.assertCurrentlyEqual(left, right)
1305
# change all attributes and ensure equality changes as we do.
1306
left.revision_index = 'a'
1307
self.assertCurrentlyNotEqual(left, right)
1308
right.revision_index = 'a'
1309
self.assertCurrentlyEqual(left, right)
1310
left.inventory_index = 'a'
1311
self.assertCurrentlyNotEqual(left, right)
1312
right.inventory_index = 'a'
1313
self.assertCurrentlyEqual(left, right)
1314
left.text_index = 'a'
1315
self.assertCurrentlyNotEqual(left, right)
1316
right.text_index = 'a'
1317
self.assertCurrentlyEqual(left, right)
1318
left.signature_index = 'a'
1319
self.assertCurrentlyNotEqual(left, right)
1320
right.signature_index = 'a'
1321
self.assertCurrentlyEqual(left, right)
1323
self.assertCurrentlyNotEqual(left, right)
1325
self.assertCurrentlyEqual(left, right)
1326
left.transport = 'a'
1327
self.assertCurrentlyNotEqual(left, right)
1328
right.transport = 'a'
1329
self.assertCurrentlyEqual(left, right)
1331
def test_file_name(self):
1332
pack = pack_repo.ExistingPack('', 'a_name', '', '', '', '')
1333
self.assertEqual('a_name.pack', pack.file_name())
1336
class TestNewPack(TestCaseWithTransport):
1337
"""Tests for pack_repo.NewPack."""
1339
def test_new_instance_attributes(self):
1340
upload_transport = self.get_transport('upload')
1341
pack_transport = self.get_transport('pack')
1342
index_transport = self.get_transport('index')
1343
upload_transport.mkdir('.')
1344
pack = pack_repo.NewPack(upload_transport, index_transport,
1346
self.assertIsInstance(pack.revision_index, InMemoryGraphIndex)
1347
self.assertIsInstance(pack.inventory_index, InMemoryGraphIndex)
1348
self.assertIsInstance(pack._hash, type(md5.new()))
1349
self.assertTrue(pack.upload_transport is upload_transport)
1350
self.assertTrue(pack.index_transport is index_transport)
1351
self.assertTrue(pack.pack_transport is pack_transport)
1352
self.assertEqual(None, pack.index_sizes)
1353
self.assertEqual(20, len(pack.random_name))
1354
self.assertIsInstance(pack.random_name, str)
1355
self.assertIsInstance(pack.start_time, float)
1358
class TestPacker(TestCaseWithTransport):
1359
"""Tests for the packs repository Packer class."""
1361
# To date, this class has been factored out and nothing new added to it;
1362
# thus there are not yet any tests.
1365
class TestInterDifferingSerializer(TestCaseWithTransport):
1367
def test_progress_bar(self):
1368
tree = self.make_branch_and_tree('tree')
1369
tree.commit('rev1', rev_id='rev-1')
1370
tree.commit('rev2', rev_id='rev-2')
1371
tree.commit('rev3', rev_id='rev-3')
1372
repo = self.make_repository('repo')
1373
inter_repo = repository.InterDifferingSerializer(
1374
tree.branch.repository, repo)
1375
pb = progress.InstrumentedProgress(to_file=StringIO())
1376
pb.never_throttle = True
1377
inter_repo.fetch('rev-1', pb)
1378
self.assertEqual('Transferring revisions', pb.last_msg)
1379
self.assertEqual(1, pb.last_cnt)
1380
self.assertEqual(1, pb.last_total)
1381
inter_repo.fetch('rev-3', pb)
1382
self.assertEqual(2, pb.last_cnt)
1383
self.assertEqual(2, pb.last_total)