1
# Copyright (C) 2005-2012, 2016 Canonical Ltd
1
# Copyright (C) 2005 by Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
11
# GNU General Public License for more details.
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26
from ..sixish import text_type
32
from ..bzr.inventory import (
40
mutable_inventory_from_tree,
44
TestCaseWithTransport,
46
from .scenarios import load_tests_apply_scenarios
49
load_tests = load_tests_apply_scenarios
52
def delta_application_scenarios():
54
('Inventory', {'apply_delta': apply_inventory_Inventory}),
56
# Working tree basis delta application
57
# Repository add_inv_by_delta.
58
# Reduce form of the per_repository test logic - that logic needs to be
59
# be able to get /just/ repositories whereas these tests are fine with
60
# just creating trees.
62
for _, format in repository.format_registry.iteritems():
63
if format.supports_full_versioned_files:
64
scenarios.append((str(format.__name__), {
65
'apply_delta': apply_inventory_Repository_add_inventory_by_delta,
67
for getter in workingtree.format_registry._get_all_lazy():
73
pass # Format with unmet dependency
74
repo_fmt = format._matchingcontroldir.repository_format
75
if not repo_fmt.supports_full_versioned_files:
78
(str(format.__class__.__name__) + ".update_basis_by_delta", {
79
'apply_delta': apply_inventory_WT_basis,
82
(str(format.__class__.__name__) + ".apply_inventory_delta", {
83
'apply_delta': apply_inventory_WT,
88
def create_texts_for_inv(repo, inv):
89
for path, ie in inv.iter_entries():
91
lines = [b'a' * ie.text_size]
94
repo.texts.add_lines((ie.file_id, ie.revision), [], lines)
97
def apply_inventory_Inventory(self, basis, delta, invalid_delta=True):
98
"""Apply delta to basis and return the result.
100
:param basis: An inventory to be used as the basis.
101
:param delta: The inventory delta to apply:
102
:return: An inventory resulting from the application.
104
basis.apply_delta(delta)
108
def apply_inventory_WT(self, basis, delta, invalid_delta=True):
109
"""Apply delta to basis and return the result.
111
This sets the tree state to be basis, and then calls apply_inventory_delta.
113
:param basis: An inventory to be used as the basis.
114
:param delta: The inventory delta to apply:
115
:return: An inventory resulting from the application.
117
control = self.make_controldir(
118
'tree', format=self.format._matchingcontroldir)
119
control.create_repository()
120
control.create_branch()
121
tree = self.format.initialize(control)
124
tree._write_inventory(basis)
127
# Fresh object, reads disk again.
128
tree = tree.controldir.open_workingtree()
131
tree.apply_inventory_delta(delta)
134
# reload tree - ensure we get what was written.
135
tree = tree.controldir.open_workingtree()
137
self.addCleanup(tree.unlock)
138
if not invalid_delta:
140
return tree.root_inventory
143
def _create_repo_revisions(repo, basis, delta, invalid_delta):
144
with repository.WriteGroup(repo):
145
rev = revision.Revision(b'basis', timestamp=0, timezone=None,
146
message="", committer="foo@example.com")
147
basis.revision_id = b'basis'
148
create_texts_for_inv(repo, basis)
149
repo.add_revision(b'basis', rev, basis)
151
# We don't want to apply the delta to the basis, because we expect
152
# the delta is invalid.
154
result_inv.revision_id = b'result'
155
target_entries = None
157
result_inv = basis.create_by_apply_delta(delta, b'result')
158
create_texts_for_inv(repo, result_inv)
159
target_entries = list(result_inv.iter_entries_by_dir())
160
rev = revision.Revision(b'result', timestamp=0, timezone=None,
161
message="", committer="foo@example.com")
162
repo.add_revision(b'result', rev, result_inv)
163
return target_entries
166
def _get_basis_entries(tree):
167
basis_tree = tree.basis_tree()
168
with basis_tree.lock_read():
169
return list(basis_tree.inventory.iter_entries_by_dir())
172
def _populate_different_tree(tree, basis, delta):
173
"""Put all entries into tree, but at a unique location."""
176
tree.add(['unique-dir'], [b'unique-dir-id'], ['directory'])
177
for path, ie in basis.iter_entries_by_dir():
178
if ie.file_id in added_ids:
180
# We want a unique path for each of these, we use the file-id
181
tree.add(['unique-dir/' + ie.file_id], [ie.file_id], [ie.kind])
182
added_ids.add(ie.file_id)
183
for old_path, new_path, file_id, ie in delta:
184
if file_id in added_ids:
186
tree.add(['unique-dir/' + file_id], [file_id], [ie.kind])
189
def apply_inventory_WT_basis(test, basis, delta, invalid_delta=True):
190
"""Apply delta to basis and return the result.
192
This sets the parent and then calls update_basis_by_delta.
193
It also puts the basis in the repository under both 'basis' and 'result' to
194
allow safety checks made by the WT to succeed, and finally ensures that all
195
items in the delta with a new path are present in the WT before calling
196
update_basis_by_delta.
198
:param basis: An inventory to be used as the basis.
199
:param delta: The inventory delta to apply:
200
:return: An inventory resulting from the application.
202
control = test.make_controldir(
203
'tree', format=test.format._matchingcontroldir)
204
control.create_repository()
205
control.create_branch()
206
tree = test.format.initialize(control)
209
target_entries = _create_repo_revisions(tree.branch.repository, basis,
210
delta, invalid_delta)
211
# Set the basis state as the trees current state
212
tree._write_inventory(basis)
213
# This reads basis from the repo and puts it into the tree's local
214
# cache, if it has one.
215
tree.set_parent_ids([b'basis'])
218
# Fresh lock, reads disk again.
219
with tree.lock_write():
220
tree.update_basis_by_delta(b'result', delta)
221
if not invalid_delta:
223
# reload tree - ensure we get what was written.
224
tree = tree.controldir.open_workingtree()
225
basis_tree = tree.basis_tree()
226
basis_tree.lock_read()
227
test.addCleanup(basis_tree.unlock)
228
basis_inv = basis_tree.root_inventory
230
basis_entries = list(basis_inv.iter_entries_by_dir())
231
test.assertEqual(target_entries, basis_entries)
235
def apply_inventory_Repository_add_inventory_by_delta(self, basis, delta,
237
"""Apply delta to basis and return the result.
239
This inserts basis as a whole inventory and then uses
240
add_inventory_by_delta to add delta.
242
:param basis: An inventory to be used as the basis.
243
:param delta: The inventory delta to apply:
244
:return: An inventory resulting from the application.
246
format = self.format()
247
control = self.make_controldir('tree', format=format._matchingcontroldir)
248
repo = format.initialize(control)
249
with repo.lock_write(), repository.WriteGroup(repo):
250
rev = revision.Revision(
251
b'basis', timestamp=0, timezone=None, message="",
252
committer="foo@example.com")
253
basis.revision_id = b'basis'
254
create_texts_for_inv(repo, basis)
255
repo.add_revision(b'basis', rev, basis)
256
with repo.lock_write(), repository.WriteGroup(repo):
257
inv_sha1 = repo.add_inventory_by_delta(
258
b'basis', delta, b'result', [b'basis'])
259
# Fresh lock, reads disk again.
260
repo = repo.controldir.open_repository()
262
self.addCleanup(repo.unlock)
263
return repo.get_inventory(b'result')
266
class TestInventoryUpdates(TestCase):
268
def test_creation_from_root_id(self):
269
# iff a root id is passed to the constructor, a root directory is made
270
inv = inventory.Inventory(root_id=b'tree-root')
271
self.assertNotEqual(None, inv.root)
272
self.assertEqual(b'tree-root', inv.root.file_id)
274
def test_add_path_of_root(self):
275
# if no root id is given at creation time, there is no root directory
276
inv = inventory.Inventory(root_id=None)
277
self.assertIs(None, inv.root)
278
# add a root entry by adding its path
279
ie = inv.add_path(u"", "directory", b"my-root")
280
ie.revision = b'test-rev'
281
self.assertEqual(b"my-root", ie.file_id)
282
self.assertIs(ie, inv.root)
284
def test_add_path(self):
285
inv = inventory.Inventory(root_id=b'tree_root')
286
ie = inv.add_path(u'hello', 'file', b'hello-id')
287
self.assertEqual(b'hello-id', ie.file_id)
288
self.assertEqual('file', ie.kind)
291
"""Make sure copy() works and creates a deep copy."""
292
inv = inventory.Inventory(root_id=b'some-tree-root')
293
ie = inv.add_path(u'hello', 'file', b'hello-id')
295
inv.root.file_id = b'some-new-root'
297
self.assertEqual(b'some-tree-root', inv2.root.file_id)
298
self.assertEqual(u'hello', inv2.get_entry(b'hello-id').name)
300
def test_copy_empty(self):
301
"""Make sure an empty inventory can be copied."""
302
inv = inventory.Inventory(root_id=None)
304
self.assertIs(None, inv2.root)
306
def test_copy_copies_root_revision(self):
307
"""Make sure the revision of the root gets copied."""
308
inv = inventory.Inventory(root_id=b'someroot')
309
inv.root.revision = b'therev'
311
self.assertEqual(b'someroot', inv2.root.file_id)
312
self.assertEqual(b'therev', inv2.root.revision)
314
def test_create_tree_reference(self):
315
inv = inventory.Inventory(b'tree-root-123')
316
inv.add(TreeReference(
317
b'nested-id', 'nested', parent_id=b'tree-root-123',
318
revision=b'rev', reference_revision=b'rev2'))
320
def test_error_encoding(self):
321
inv = inventory.Inventory(b'tree-root')
322
inv.add(InventoryFile(b'a-id', u'\u1234', b'tree-root'))
323
e = self.assertRaises(errors.InconsistentDelta, inv.add,
324
InventoryFile(b'b-id', u'\u1234', b'tree-root'))
325
self.assertContainsRe(str(e), '\\u1234')
327
def test_add_recursive(self):
328
parent = InventoryDirectory(b'src-id', 'src', b'tree-root')
329
child = InventoryFile(b'hello-id', 'hello.c', b'src-id')
330
parent.children[child.file_id] = child
331
inv = inventory.Inventory(b'tree-root')
333
self.assertEqual('src/hello.c', inv.id2path(b'hello-id'))
336
class TestDeltaApplication(TestCaseWithTransport):
338
scenarios = delta_application_scenarios()
340
def get_empty_inventory(self, reference_inv=None):
341
"""Get an empty inventory.
343
Note that tests should not depend on the revision of the root for
344
setting up test conditions, as it has to be flexible to accomodate non
345
rich root repositories.
347
:param reference_inv: If not None, get the revision for the root from
348
this inventory. This is useful for dealing with older repositories
349
that routinely discarded the root entry data. If None, the root's
350
revision is set to 'basis'.
352
inv = inventory.Inventory()
353
if reference_inv is not None:
354
inv.root.revision = reference_inv.root.revision
356
inv.root.revision = b'basis'
359
def make_file_ie(self, file_id=b'file-id', name='name', parent_id=None):
360
ie_file = inventory.InventoryFile(file_id, name, parent_id)
361
ie_file.revision = b'result'
362
ie_file.text_size = 0
363
ie_file.text_sha1 = b''
366
def test_empty_delta(self):
367
inv = self.get_empty_inventory()
369
inv = self.apply_delta(self, inv, delta)
370
inv2 = self.get_empty_inventory(inv)
371
self.assertEqual([], inv2._make_delta(inv))
373
def test_None_file_id(self):
374
inv = self.get_empty_inventory()
375
dir1 = inventory.InventoryDirectory(b'dirid', 'dir1', inv.root.file_id)
377
dir1.revision = b'result'
378
delta = [(None, u'dir1', None, dir1)]
379
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
382
def test_unicode_file_id(self):
383
inv = self.get_empty_inventory()
384
dir1 = inventory.InventoryDirectory(b'dirid', 'dir1', inv.root.file_id)
385
dir1.file_id = u'dirid'
386
dir1.revision = b'result'
387
delta = [(None, u'dir1', dir1.file_id, dir1)]
388
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
391
def test_repeated_file_id(self):
392
inv = self.get_empty_inventory()
393
file1 = inventory.InventoryFile(b'id', 'path1', inv.root.file_id)
394
file1.revision = b'result'
396
file1.text_sha1 = b""
399
delta = [(None, u'path1', b'id', file1),
400
(None, u'path2', b'id', file2)]
401
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
404
def test_repeated_new_path(self):
405
inv = self.get_empty_inventory()
406
file1 = inventory.InventoryFile(b'id1', 'path', inv.root.file_id)
407
file1.revision = b'result'
409
file1.text_sha1 = b""
411
file2.file_id = b'id2'
412
delta = [(None, u'path', b'id1', file1),
413
(None, u'path', b'id2', file2)]
414
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
417
def test_repeated_old_path(self):
418
inv = self.get_empty_inventory()
419
file1 = inventory.InventoryFile(b'id1', 'path', inv.root.file_id)
420
file1.revision = b'result'
422
file1.text_sha1 = b""
423
# We can't *create* a source inventory with the same path, but
424
# a badly generated partial delta might claim the same source twice.
425
# This would be buggy in two ways: the path is repeated in the delta,
426
# And the path for one of the file ids doesn't match the source
427
# location. Alternatively, we could have a repeated fileid, but that
428
# is separately checked for.
429
file2 = inventory.InventoryFile(b'id2', 'path2', inv.root.file_id)
430
file2.revision = b'result'
432
file2.text_sha1 = b""
435
delta = [(u'path', None, b'id1', None), (u'path', None, b'id2', None)]
436
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
439
def test_mismatched_id_entry_id(self):
440
inv = self.get_empty_inventory()
441
file1 = inventory.InventoryFile(b'id1', 'path', inv.root.file_id)
442
file1.revision = b'result'
444
file1.text_sha1 = b""
445
delta = [(None, u'path', b'id', file1)]
446
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
449
def test_mismatched_new_path_entry_None(self):
450
inv = self.get_empty_inventory()
451
delta = [(None, u'path', b'id', None)]
452
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
455
def test_mismatched_new_path_None_entry(self):
456
inv = self.get_empty_inventory()
457
file1 = inventory.InventoryFile(b'id1', 'path', inv.root.file_id)
458
file1.revision = b'result'
460
file1.text_sha1 = b""
461
delta = [(u"path", None, b'id1', file1)]
462
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
465
def test_parent_is_not_directory(self):
466
inv = self.get_empty_inventory()
467
file1 = inventory.InventoryFile(b'id1', 'path', inv.root.file_id)
468
file1.revision = b'result'
470
file1.text_sha1 = b""
471
file2 = inventory.InventoryFile(b'id2', 'path2', b'id1')
472
file2.revision = b'result'
474
file2.text_sha1 = b""
476
delta = [(None, u'path/path2', b'id2', file2)]
477
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
480
def test_parent_is_missing(self):
481
inv = self.get_empty_inventory()
482
file2 = inventory.InventoryFile(b'id2', 'path2', b'missingparent')
483
file2.revision = b'result'
485
file2.text_sha1 = b""
486
delta = [(None, u'path/path2', b'id2', file2)]
487
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
490
def test_new_parent_path_has_wrong_id(self):
491
inv = self.get_empty_inventory()
492
parent1 = inventory.InventoryDirectory(b'p-1', 'dir', inv.root.file_id)
493
parent1.revision = b'result'
494
parent2 = inventory.InventoryDirectory(
495
b'p-2', 'dir2', inv.root.file_id)
496
parent2.revision = b'result'
497
file1 = inventory.InventoryFile(b'id', 'path', b'p-2')
498
file1.revision = b'result'
500
file1.text_sha1 = b""
503
# This delta claims that file1 is at dir/path, but actually its at
504
# dir2/path if you follow the inventory parent structure.
505
delta = [(None, u'dir/path', b'id', file1)]
506
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
509
def test_old_parent_path_is_wrong(self):
510
inv = self.get_empty_inventory()
511
parent1 = inventory.InventoryDirectory(b'p-1', 'dir', inv.root.file_id)
512
parent1.revision = b'result'
513
parent2 = inventory.InventoryDirectory(
514
b'p-2', 'dir2', inv.root.file_id)
515
parent2.revision = b'result'
516
file1 = inventory.InventoryFile(b'id', 'path', b'p-2')
517
file1.revision = b'result'
519
file1.text_sha1 = b""
523
# This delta claims that file1 was at dir/path, but actually it was at
524
# dir2/path if you follow the inventory parent structure.
525
delta = [(u'dir/path', None, b'id', None)]
526
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
529
def test_old_parent_path_is_for_other_id(self):
530
inv = self.get_empty_inventory()
531
parent1 = inventory.InventoryDirectory(b'p-1', 'dir', inv.root.file_id)
532
parent1.revision = b'result'
533
parent2 = inventory.InventoryDirectory(
534
b'p-2', 'dir2', inv.root.file_id)
535
parent2.revision = b'result'
536
file1 = inventory.InventoryFile(b'id', 'path', b'p-2')
537
file1.revision = b'result'
539
file1.text_sha1 = b""
540
file2 = inventory.InventoryFile(b'id2', 'path', b'p-1')
541
file2.revision = b'result'
543
file2.text_sha1 = b""
548
# This delta claims that file1 was at dir/path, but actually it was at
549
# dir2/path if you follow the inventory parent structure. At dir/path
550
# is another entry we should not delete.
551
delta = [(u'dir/path', None, b'id', None)]
552
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
555
def test_add_existing_id_new_path(self):
556
inv = self.get_empty_inventory()
557
parent1 = inventory.InventoryDirectory(
558
b'p-1', 'dir1', inv.root.file_id)
559
parent1.revision = b'result'
560
parent2 = inventory.InventoryDirectory(
561
b'p-1', 'dir2', inv.root.file_id)
562
parent2.revision = b'result'
564
delta = [(None, u'dir2', b'p-1', parent2)]
565
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
568
def test_add_new_id_existing_path(self):
569
inv = self.get_empty_inventory()
570
parent1 = inventory.InventoryDirectory(
571
b'p-1', 'dir1', inv.root.file_id)
572
parent1.revision = b'result'
573
parent2 = inventory.InventoryDirectory(
574
b'p-2', 'dir1', inv.root.file_id)
575
parent2.revision = b'result'
577
delta = [(None, u'dir1', b'p-2', parent2)]
578
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
581
def test_remove_dir_leaving_dangling_child(self):
582
inv = self.get_empty_inventory()
583
dir1 = inventory.InventoryDirectory(b'p-1', 'dir1', inv.root.file_id)
584
dir1.revision = b'result'
585
dir2 = inventory.InventoryDirectory(b'p-2', 'child1', b'p-1')
586
dir2.revision = b'result'
587
dir3 = inventory.InventoryDirectory(b'p-3', 'child2', b'p-1')
588
dir3.revision = b'result'
592
delta = [(u'dir1', None, b'p-1', None),
593
(u'dir1/child2', None, b'p-3', None)]
594
self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
597
def test_add_file(self):
598
inv = self.get_empty_inventory()
599
file1 = inventory.InventoryFile(b'file-id', 'path', inv.root.file_id)
600
file1.revision = b'result'
602
file1.text_sha1 = b''
603
delta = [(None, u'path', b'file-id', file1)]
604
res_inv = self.apply_delta(self, inv, delta, invalid_delta=False)
605
self.assertEqual(b'file-id', res_inv.get_entry(b'file-id').file_id)
607
def test_remove_file(self):
608
inv = self.get_empty_inventory()
609
file1 = inventory.InventoryFile(b'file-id', 'path', inv.root.file_id)
610
file1.revision = b'result'
612
file1.text_sha1 = b''
614
delta = [(u'path', None, b'file-id', None)]
615
res_inv = self.apply_delta(self, inv, delta, invalid_delta=False)
616
self.assertEqual(None, res_inv.path2id('path'))
617
self.assertRaises(errors.NoSuchId, res_inv.id2path, b'file-id')
619
def test_rename_file(self):
620
inv = self.get_empty_inventory()
621
file1 = self.make_file_ie(name='path', parent_id=inv.root.file_id)
623
file2 = self.make_file_ie(name='path2', parent_id=inv.root.file_id)
624
delta = [(u'path', 'path2', b'file-id', file2)]
625
res_inv = self.apply_delta(self, inv, delta, invalid_delta=False)
626
self.assertEqual(None, res_inv.path2id('path'))
627
self.assertEqual(b'file-id', res_inv.path2id('path2'))
629
def test_replaced_at_new_path(self):
630
inv = self.get_empty_inventory()
631
file1 = self.make_file_ie(file_id=b'id1', parent_id=inv.root.file_id)
633
file2 = self.make_file_ie(file_id=b'id2', parent_id=inv.root.file_id)
634
delta = [(u'name', None, b'id1', None),
635
(None, u'name', b'id2', file2)]
636
res_inv = self.apply_delta(self, inv, delta, invalid_delta=False)
637
self.assertEqual(b'id2', res_inv.path2id('name'))
639
def test_rename_dir(self):
640
inv = self.get_empty_inventory()
641
dir1 = inventory.InventoryDirectory(
642
b'dir-id', 'dir1', inv.root.file_id)
643
dir1.revision = b'basis'
644
file1 = self.make_file_ie(parent_id=b'dir-id')
647
dir2 = inventory.InventoryDirectory(
648
b'dir-id', 'dir2', inv.root.file_id)
649
dir2.revision = b'result'
650
delta = [('dir1', 'dir2', b'dir-id', dir2)]
651
res_inv = self.apply_delta(self, inv, delta, invalid_delta=False)
652
# The file should be accessible under the new path
653
self.assertEqual(b'file-id', res_inv.path2id('dir2/name'))
655
def test_renamed_dir_with_renamed_child(self):
656
inv = self.get_empty_inventory()
657
dir1 = inventory.InventoryDirectory(
658
b'dir-id', 'dir1', inv.root.file_id)
659
dir1.revision = b'basis'
660
file1 = self.make_file_ie(b'file-id-1', 'name1', parent_id=b'dir-id')
661
file2 = self.make_file_ie(b'file-id-2', 'name2', parent_id=b'dir-id')
665
dir2 = inventory.InventoryDirectory(
666
b'dir-id', 'dir2', inv.root.file_id)
667
dir2.revision = b'result'
668
file2b = self.make_file_ie(b'file-id-2', 'name2', inv.root.file_id)
669
delta = [('dir1', 'dir2', b'dir-id', dir2),
670
('dir1/name2', 'name2', b'file-id-2', file2b)]
671
res_inv = self.apply_delta(self, inv, delta, invalid_delta=False)
672
# The file should be accessible under the new path
673
self.assertEqual(b'file-id-1', res_inv.path2id('dir2/name1'))
674
self.assertEqual(None, res_inv.path2id('dir2/name2'))
675
self.assertEqual(b'file-id-2', res_inv.path2id('name2'))
677
def test_is_root(self):
678
"""Ensure our root-checking code is accurate."""
679
inv = inventory.Inventory(b'TREE_ROOT')
680
self.assertTrue(inv.is_root(b'TREE_ROOT'))
681
self.assertFalse(inv.is_root(b'booga'))
682
inv.root.file_id = b'booga'
683
self.assertFalse(inv.is_root(b'TREE_ROOT'))
684
self.assertTrue(inv.is_root(b'booga'))
685
# works properly even if no root is set
687
self.assertFalse(inv.is_root(b'TREE_ROOT'))
688
self.assertFalse(inv.is_root(b'booga'))
690
def test_entries_for_empty_inventory(self):
691
"""Test that entries() will not fail for an empty inventory"""
692
inv = Inventory(root_id=None)
693
self.assertEqual([], inv.entries())
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
from cStringIO import StringIO
20
from bzrlib.branch import Branch
21
from bzrlib.clone import copy_branch
22
import bzrlib.errors as errors
23
from bzrlib.diff import internal_diff
24
from bzrlib.inventory import Inventory, ROOT_ID
25
import bzrlib.inventory as inventory
26
from bzrlib.osutils import has_symlinks, rename
27
from bzrlib.selftest import TestCase, TestCaseInTempDir
30
class TestInventory(TestCase):
32
def test_is_within(self):
33
from bzrlib.osutils import is_inside_any
35
SRC_FOO_C = os.path.join('src', 'foo.c')
36
for dirs, fn in [(['src', 'doc'], SRC_FOO_C),
40
self.assert_(is_inside_any(dirs, fn))
42
for dirs, fn in [(['src'], 'srccontrol'),
43
(['src'], 'srccontrol/foo')]:
44
self.assertFalse(is_inside_any(dirs, fn))
47
"""Test detection of files within selected directories."""
50
for args in [('src', 'directory', 'src-id'),
51
('doc', 'directory', 'doc-id'),
52
('src/hello.c', 'file'),
53
('src/bye.c', 'file', 'bye-id'),
54
('Makefile', 'file')]:
57
self.assertEqual(inv.path2id('src'), 'src-id')
58
self.assertEqual(inv.path2id('src/bye.c'), 'bye-id')
60
self.assert_('src-id' in inv)
63
def test_version(self):
64
"""Inventory remembers the text's version."""
66
ie = inv.add_path('foo.txt', 'file')
696
70
class TestInventoryEntry(TestCase):
698
def test_file_invalid_entry_name(self):
699
self.assertRaises(errors.InvalidEntryName, inventory.InventoryFile,
700
b'123', 'a/hello.c', ROOT_ID)
702
def test_file_backslash(self):
703
file = inventory.InventoryFile(b'123', 'h\\ello.c', ROOT_ID)
704
self.assertEquals(file.name, 'h\\ello.c')
706
72
def test_file_kind_character(self):
707
file = inventory.InventoryFile(b'123', 'hello.c', ROOT_ID)
73
file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
708
74
self.assertEqual(file.kind_character(), '')
710
76
def test_dir_kind_character(self):
711
dir = inventory.InventoryDirectory(b'123', 'hello.c', ROOT_ID)
77
dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
712
78
self.assertEqual(dir.kind_character(), '/')
714
80
def test_link_kind_character(self):
715
dir = inventory.InventoryLink(b'123', 'hello.c', ROOT_ID)
81
dir = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
716
82
self.assertEqual(dir.kind_character(), '')
718
def test_link_kind_character(self):
719
dir = TreeReference(b'123', 'hello.c', ROOT_ID)
720
self.assertEqual(dir.kind_character(), '+')
722
84
def test_dir_detect_changes(self):
723
left = inventory.InventoryDirectory(b'123', 'hello.c', ROOT_ID)
724
right = inventory.InventoryDirectory(b'123', 'hello.c', ROOT_ID)
85
left = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
87
left.executable = True
88
left.symlink_target='foo'
89
right = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
91
right.symlink_target='bar'
725
92
self.assertEqual((False, False), left.detect_changes(right))
726
93
self.assertEqual((False, False), right.detect_changes(left))
728
95
def test_file_detect_changes(self):
729
left = inventory.InventoryFile(b'123', 'hello.c', ROOT_ID)
96
left = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
730
97
left.text_sha1 = 123
731
right = inventory.InventoryFile(b'123', 'hello.c', ROOT_ID)
98
right = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
732
99
right.text_sha1 = 123
733
100
self.assertEqual((False, False), left.detect_changes(right))
734
101
self.assertEqual((False, False), right.detect_changes(left))
751
121
self.assertEqual((True, False), right.detect_changes(left))
753
123
def test_file_has_text(self):
754
file = inventory.InventoryFile(b'123', 'hello.c', ROOT_ID)
755
self.assertTrue(file.has_text())
124
file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
125
self.failUnless(file.has_text())
757
127
def test_directory_has_text(self):
758
dir = inventory.InventoryDirectory(b'123', 'hello.c', ROOT_ID)
759
self.assertFalse(dir.has_text())
128
dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
129
self.failIf(dir.has_text())
761
131
def test_link_has_text(self):
762
link = inventory.InventoryLink(b'123', 'hello.c', ROOT_ID)
763
self.assertFalse(link.has_text())
765
def test_make_entry(self):
766
self.assertIsInstance(inventory.make_entry("file", "name", ROOT_ID),
767
inventory.InventoryFile)
768
self.assertIsInstance(inventory.make_entry("symlink", "name", ROOT_ID),
769
inventory.InventoryLink)
770
self.assertIsInstance(inventory.make_entry("directory", "name", ROOT_ID),
771
inventory.InventoryDirectory)
773
def test_make_entry_non_normalized(self):
774
orig_normalized_filename = osutils.normalized_filename
777
osutils.normalized_filename = osutils._accessible_normalized_filename
778
entry = inventory.make_entry("file", u'a\u030a', ROOT_ID)
779
self.assertEqual(u'\xe5', entry.name)
780
self.assertIsInstance(entry, inventory.InventoryFile)
782
osutils.normalized_filename = osutils._inaccessible_normalized_filename
783
self.assertRaises(errors.InvalidNormalization,
784
inventory.make_entry, 'file', u'a\u030a', ROOT_ID)
786
osutils.normalized_filename = orig_normalized_filename
789
class TestDescribeChanges(TestCase):
791
def test_describe_change(self):
792
# we need to test the following change combinations:
798
# renamed/reparented and modified
799
# change kind (perhaps can't be done yet?)
800
# also, merged in combination with all of these?
801
old_a = InventoryFile(b'a-id', 'a_file', ROOT_ID)
802
old_a.text_sha1 = b'123132'
804
new_a = InventoryFile(b'a-id', 'a_file', ROOT_ID)
805
new_a.text_sha1 = b'123132'
808
self.assertChangeDescription('unchanged', old_a, new_a)
811
new_a.text_sha1 = b'abcabc'
812
self.assertChangeDescription('modified', old_a, new_a)
814
self.assertChangeDescription('added', None, new_a)
815
self.assertChangeDescription('removed', old_a, None)
816
# perhaps a bit questionable but seems like the most reasonable thing...
817
self.assertChangeDescription('unchanged', None, None)
819
# in this case it's both renamed and modified; show a rename and
821
new_a.name = 'newfilename'
822
self.assertChangeDescription('modified and renamed', old_a, new_a)
824
# reparenting is 'renaming'
825
new_a.name = old_a.name
826
new_a.parent_id = b'somedir-id'
827
self.assertChangeDescription('modified and renamed', old_a, new_a)
829
# reset the content values so its not modified
830
new_a.text_size = old_a.text_size
831
new_a.text_sha1 = old_a.text_sha1
832
new_a.name = old_a.name
834
new_a.name = 'newfilename'
835
self.assertChangeDescription('renamed', old_a, new_a)
837
# reparenting is 'renaming'
838
new_a.name = old_a.name
839
new_a.parent_id = b'somedir-id'
840
self.assertChangeDescription('renamed', old_a, new_a)
842
def assertChangeDescription(self, expected_change, old_ie, new_ie):
843
change = InventoryEntry.describe_change(old_ie, new_ie)
844
self.assertEqual(expected_change, change)
847
class TestCHKInventory(tests.TestCaseWithMemoryTransport):
849
def get_chk_bytes(self):
850
factory = groupcompress.make_pack_factory(True, True, 1)
851
trans = self.get_transport('')
852
return factory(trans)
854
def read_bytes(self, chk_bytes, key):
855
stream = chk_bytes.get_record_stream([key], 'unordered', True)
856
return next(stream).get_bytes_as("fulltext")
858
def test_deserialise_gives_CHKInventory(self):
860
inv.revision_id = b"revid"
861
inv.root.revision = b"rootrev"
862
chk_bytes = self.get_chk_bytes()
863
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
864
lines = chk_inv.to_lines()
865
new_inv = CHKInventory.deserialise(chk_bytes, lines, (b"revid",))
866
self.assertEqual(b"revid", new_inv.revision_id)
867
self.assertEqual("directory", new_inv.root.kind)
868
self.assertEqual(inv.root.file_id, new_inv.root.file_id)
869
self.assertEqual(inv.root.parent_id, new_inv.root.parent_id)
870
self.assertEqual(inv.root.name, new_inv.root.name)
871
self.assertEqual(b"rootrev", new_inv.root.revision)
872
self.assertEqual(b'plain', new_inv._search_key_name)
874
def test_deserialise_wrong_revid(self):
876
inv.revision_id = b"revid"
877
inv.root.revision = b"rootrev"
878
chk_bytes = self.get_chk_bytes()
879
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
880
lines = chk_inv.to_lines()
881
self.assertRaises(ValueError, CHKInventory.deserialise, chk_bytes,
884
def test_captures_rev_root_byid(self):
886
inv.revision_id = b"foo"
887
inv.root.revision = b"bar"
888
chk_bytes = self.get_chk_bytes()
889
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
890
lines = chk_inv.to_lines()
893
b'revision_id: foo\n',
894
b'root_id: TREE_ROOT\n',
895
b'parent_id_basename_to_file_id: sha1:eb23f0ad4b07f48e88c76d4c94292be57fb2785f\n',
896
b'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
898
chk_inv = CHKInventory.deserialise(
899
chk_bytes, lines, (b'foo',))
900
self.assertEqual(b'plain', chk_inv._search_key_name)
902
def test_captures_parent_id_basename_index(self):
904
inv.revision_id = b"foo"
905
inv.root.revision = b"bar"
906
chk_bytes = self.get_chk_bytes()
907
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
908
lines = chk_inv.to_lines()
911
b'revision_id: foo\n',
912
b'root_id: TREE_ROOT\n',
913
b'parent_id_basename_to_file_id: sha1:eb23f0ad4b07f48e88c76d4c94292be57fb2785f\n',
914
b'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
916
chk_inv = CHKInventory.deserialise(
917
chk_bytes, lines, (b'foo',))
918
self.assertEqual(b'plain', chk_inv._search_key_name)
920
def test_captures_search_key_name(self):
922
inv.revision_id = b"foo"
923
inv.root.revision = b"bar"
924
chk_bytes = self.get_chk_bytes()
925
chk_inv = CHKInventory.from_inventory(chk_bytes, inv,
926
search_key_name=b'hash-16-way')
927
lines = chk_inv.to_lines()
930
b'search_key_name: hash-16-way\n',
931
b'root_id: TREE_ROOT\n',
932
b'parent_id_basename_to_file_id: sha1:eb23f0ad4b07f48e88c76d4c94292be57fb2785f\n',
933
b'revision_id: foo\n',
934
b'id_to_entry: sha1:debfe920f1f10e7929260f0534ac9a24d7aabbb4\n',
936
chk_inv = CHKInventory.deserialise(
937
chk_bytes, lines, (b'foo',))
938
self.assertEqual(b'hash-16-way', chk_inv._search_key_name)
940
def test_directory_children_on_demand(self):
942
inv.revision_id = b"revid"
943
inv.root.revision = b"rootrev"
944
inv.add(InventoryFile(b"fileid", "file", inv.root.file_id))
945
inv.get_entry(b"fileid").revision = b"filerev"
946
inv.get_entry(b"fileid").executable = True
947
inv.get_entry(b"fileid").text_sha1 = b"ffff"
948
inv.get_entry(b"fileid").text_size = 1
949
chk_bytes = self.get_chk_bytes()
950
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
951
lines = chk_inv.to_lines()
952
new_inv = CHKInventory.deserialise(chk_bytes, lines, (b"revid",))
953
root_entry = new_inv.get_entry(inv.root.file_id)
954
self.assertEqual(None, root_entry._children)
955
self.assertEqual({'file'}, set(root_entry.children))
956
file_direct = new_inv.get_entry(b"fileid")
957
file_found = root_entry.children['file']
958
self.assertEqual(file_direct.kind, file_found.kind)
959
self.assertEqual(file_direct.file_id, file_found.file_id)
960
self.assertEqual(file_direct.parent_id, file_found.parent_id)
961
self.assertEqual(file_direct.name, file_found.name)
962
self.assertEqual(file_direct.revision, file_found.revision)
963
self.assertEqual(file_direct.text_sha1, file_found.text_sha1)
964
self.assertEqual(file_direct.text_size, file_found.text_size)
965
self.assertEqual(file_direct.executable, file_found.executable)
967
def test_from_inventory_maximum_size(self):
968
# from_inventory supports the maximum_size parameter.
970
inv.revision_id = b"revid"
971
inv.root.revision = b"rootrev"
972
chk_bytes = self.get_chk_bytes()
973
chk_inv = CHKInventory.from_inventory(chk_bytes, inv, 120)
974
chk_inv.id_to_entry._ensure_root()
975
self.assertEqual(120, chk_inv.id_to_entry._root_node.maximum_size)
976
self.assertEqual(1, chk_inv.id_to_entry._root_node._key_width)
977
p_id_basename = chk_inv.parent_id_basename_to_file_id
978
p_id_basename._ensure_root()
979
self.assertEqual(120, p_id_basename._root_node.maximum_size)
980
self.assertEqual(2, p_id_basename._root_node._key_width)
982
def test_iter_all_ids(self):
984
inv.revision_id = b"revid"
985
inv.root.revision = b"rootrev"
986
inv.add(InventoryFile(b"fileid", "file", inv.root.file_id))
987
inv.get_entry(b"fileid").revision = b"filerev"
988
inv.get_entry(b"fileid").executable = True
989
inv.get_entry(b"fileid").text_sha1 = b"ffff"
990
inv.get_entry(b"fileid").text_size = 1
991
chk_bytes = self.get_chk_bytes()
992
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
993
lines = chk_inv.to_lines()
994
new_inv = CHKInventory.deserialise(chk_bytes, lines, (b"revid",))
995
fileids = sorted(new_inv.iter_all_ids())
996
self.assertEqual([inv.root.file_id, b"fileid"], fileids)
998
def test__len__(self):
1000
inv.revision_id = b"revid"
1001
inv.root.revision = b"rootrev"
1002
inv.add(InventoryFile(b"fileid", "file", inv.root.file_id))
1003
inv.get_entry(b"fileid").revision = b"filerev"
1004
inv.get_entry(b"fileid").executable = True
1005
inv.get_entry(b"fileid").text_sha1 = b"ffff"
1006
inv.get_entry(b"fileid").text_size = 1
1007
chk_bytes = self.get_chk_bytes()
1008
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1009
self.assertEqual(2, len(chk_inv))
1011
def test_get_entry(self):
1013
inv.revision_id = b"revid"
1014
inv.root.revision = b"rootrev"
1015
inv.add(InventoryFile(b"fileid", u"file", inv.root.file_id))
1016
inv.get_entry(b"fileid").revision = b"filerev"
1017
inv.get_entry(b"fileid").executable = True
1018
inv.get_entry(b"fileid").text_sha1 = b"ffff"
1019
inv.get_entry(b"fileid").text_size = 1
1020
chk_bytes = self.get_chk_bytes()
1021
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1022
lines = chk_inv.to_lines()
1023
new_inv = CHKInventory.deserialise(chk_bytes, lines, (b"revid",))
1024
root_entry = new_inv.get_entry(inv.root.file_id)
1025
file_entry = new_inv.get_entry(b"fileid")
1026
self.assertEqual("directory", root_entry.kind)
1027
self.assertEqual(inv.root.file_id, root_entry.file_id)
1028
self.assertEqual(inv.root.parent_id, root_entry.parent_id)
1029
self.assertEqual(inv.root.name, root_entry.name)
1030
self.assertEqual(b"rootrev", root_entry.revision)
1031
self.assertEqual("file", file_entry.kind)
1032
self.assertEqual(b"fileid", file_entry.file_id)
1033
self.assertEqual(inv.root.file_id, file_entry.parent_id)
1034
self.assertEqual(u"file", file_entry.name)
1035
self.assertEqual(b"filerev", file_entry.revision)
1036
self.assertEqual(b"ffff", file_entry.text_sha1)
1037
self.assertEqual(1, file_entry.text_size)
1038
self.assertEqual(True, file_entry.executable)
1039
self.assertRaises(errors.NoSuchId, new_inv.get_entry, 'missing')
1041
def test_has_id_true(self):
1043
inv.revision_id = b"revid"
1044
inv.root.revision = b"rootrev"
1045
inv.add(InventoryFile(b"fileid", "file", inv.root.file_id))
1046
inv.get_entry(b"fileid").revision = b"filerev"
1047
inv.get_entry(b"fileid").executable = True
1048
inv.get_entry(b"fileid").text_sha1 = b"ffff"
1049
inv.get_entry(b"fileid").text_size = 1
1050
chk_bytes = self.get_chk_bytes()
1051
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1052
self.assertTrue(chk_inv.has_id(b'fileid'))
1053
self.assertTrue(chk_inv.has_id(inv.root.file_id))
1055
def test_has_id_not(self):
1057
inv.revision_id = b"revid"
1058
inv.root.revision = b"rootrev"
1059
chk_bytes = self.get_chk_bytes()
1060
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1061
self.assertFalse(chk_inv.has_id(b'fileid'))
1063
def test_id2path(self):
1065
inv.revision_id = b"revid"
1066
inv.root.revision = b"rootrev"
1067
direntry = InventoryDirectory(b"dirid", "dir", inv.root.file_id)
1068
fileentry = InventoryFile(b"fileid", "file", b"dirid")
1071
inv.get_entry(b"fileid").revision = b"filerev"
1072
inv.get_entry(b"fileid").executable = True
1073
inv.get_entry(b"fileid").text_sha1 = b"ffff"
1074
inv.get_entry(b"fileid").text_size = 1
1075
inv.get_entry(b"dirid").revision = b"filerev"
1076
chk_bytes = self.get_chk_bytes()
1077
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1078
lines = chk_inv.to_lines()
1079
new_inv = CHKInventory.deserialise(chk_bytes, lines, (b"revid",))
1080
self.assertEqual('', new_inv.id2path(inv.root.file_id))
1081
self.assertEqual('dir', new_inv.id2path(b'dirid'))
1082
self.assertEqual('dir/file', new_inv.id2path(b'fileid'))
1084
def test_path2id(self):
1086
inv.revision_id = b"revid"
1087
inv.root.revision = b"rootrev"
1088
direntry = InventoryDirectory(b"dirid", "dir", inv.root.file_id)
1089
fileentry = InventoryFile(b"fileid", "file", b"dirid")
1092
inv.get_entry(b"fileid").revision = b"filerev"
1093
inv.get_entry(b"fileid").executable = True
1094
inv.get_entry(b"fileid").text_sha1 = b"ffff"
1095
inv.get_entry(b"fileid").text_size = 1
1096
inv.get_entry(b"dirid").revision = b"filerev"
1097
chk_bytes = self.get_chk_bytes()
1098
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1099
lines = chk_inv.to_lines()
1100
new_inv = CHKInventory.deserialise(chk_bytes, lines, (b"revid",))
1101
self.assertEqual(inv.root.file_id, new_inv.path2id(''))
1102
self.assertEqual(b'dirid', new_inv.path2id('dir'))
1103
self.assertEqual(b'fileid', new_inv.path2id('dir/file'))
1105
def test_create_by_apply_delta_sets_root(self):
1107
inv.root.revision = b"myrootrev"
1108
inv.revision_id = b"revid"
1109
chk_bytes = self.get_chk_bytes()
1110
base_inv = CHKInventory.from_inventory(chk_bytes, inv)
1111
inv.add_path("", "directory", b"myrootid", None)
1112
inv.revision_id = b"expectedid"
1113
inv.root.revision = b"myrootrev"
1114
reference_inv = CHKInventory.from_inventory(chk_bytes, inv)
1115
delta = [("", None, base_inv.root.file_id, None),
1116
(None, "", b"myrootid", inv.root)]
1117
new_inv = base_inv.create_by_apply_delta(delta, b"expectedid")
1118
self.assertEqual(reference_inv.root, new_inv.root)
1120
def test_create_by_apply_delta_empty_add_child(self):
1122
inv.revision_id = b"revid"
1123
inv.root.revision = b"rootrev"
1124
chk_bytes = self.get_chk_bytes()
1125
base_inv = CHKInventory.from_inventory(chk_bytes, inv)
1126
a_entry = InventoryFile(b"A-id", "A", inv.root.file_id)
1127
a_entry.revision = b"filerev"
1128
a_entry.executable = True
1129
a_entry.text_sha1 = b"ffff"
1130
a_entry.text_size = 1
1132
inv.revision_id = b"expectedid"
1133
reference_inv = CHKInventory.from_inventory(chk_bytes, inv)
1134
delta = [(None, "A", b"A-id", a_entry)]
1135
new_inv = base_inv.create_by_apply_delta(delta, b"expectedid")
1136
# new_inv should be the same as reference_inv.
1137
self.assertEqual(reference_inv.revision_id, new_inv.revision_id)
1138
self.assertEqual(reference_inv.root_id, new_inv.root_id)
1139
reference_inv.id_to_entry._ensure_root()
1140
new_inv.id_to_entry._ensure_root()
1141
self.assertEqual(reference_inv.id_to_entry._root_node._key,
1142
new_inv.id_to_entry._root_node._key)
1144
def test_create_by_apply_delta_empty_add_child_updates_parent_id(self):
1146
inv.revision_id = b"revid"
1147
inv.root.revision = b"rootrev"
1148
chk_bytes = self.get_chk_bytes()
1149
base_inv = CHKInventory.from_inventory(chk_bytes, inv)
1150
a_entry = InventoryFile(b"A-id", "A", inv.root.file_id)
1151
a_entry.revision = b"filerev"
1152
a_entry.executable = True
1153
a_entry.text_sha1 = b"ffff"
1154
a_entry.text_size = 1
1156
inv.revision_id = b"expectedid"
1157
reference_inv = CHKInventory.from_inventory(chk_bytes, inv)
1158
delta = [(None, "A", b"A-id", a_entry)]
1159
new_inv = base_inv.create_by_apply_delta(delta, b"expectedid")
1160
reference_inv.id_to_entry._ensure_root()
1161
reference_inv.parent_id_basename_to_file_id._ensure_root()
1162
new_inv.id_to_entry._ensure_root()
1163
new_inv.parent_id_basename_to_file_id._ensure_root()
1164
# new_inv should be the same as reference_inv.
1165
self.assertEqual(reference_inv.revision_id, new_inv.revision_id)
1166
self.assertEqual(reference_inv.root_id, new_inv.root_id)
1167
self.assertEqual(reference_inv.id_to_entry._root_node._key,
1168
new_inv.id_to_entry._root_node._key)
1169
self.assertEqual(reference_inv.parent_id_basename_to_file_id._root_node._key,
1170
new_inv.parent_id_basename_to_file_id._root_node._key)
1172
def test_iter_changes(self):
1173
# Low level bootstrapping smoke test; comprehensive generic tests via
1174
# InterTree are coming.
1176
inv.revision_id = b"revid"
1177
inv.root.revision = b"rootrev"
1178
inv.add(InventoryFile(b"fileid", "file", inv.root.file_id))
1179
inv.get_entry(b"fileid").revision = b"filerev"
1180
inv.get_entry(b"fileid").executable = True
1181
inv.get_entry(b"fileid").text_sha1 = b"ffff"
1182
inv.get_entry(b"fileid").text_size = 1
1184
inv2.revision_id = b"revid2"
1185
inv2.root.revision = b"rootrev"
1186
inv2.add(InventoryFile(b"fileid", "file", inv.root.file_id))
1187
inv2.get_entry(b"fileid").revision = b"filerev2"
1188
inv2.get_entry(b"fileid").executable = False
1189
inv2.get_entry(b"fileid").text_sha1 = b"bbbb"
1190
inv2.get_entry(b"fileid").text_size = 2
1191
# get fresh objects.
1192
chk_bytes = self.get_chk_bytes()
1193
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1194
lines = chk_inv.to_lines()
1195
inv_1 = CHKInventory.deserialise(chk_bytes, lines, (b"revid",))
1196
chk_inv2 = CHKInventory.from_inventory(chk_bytes, inv2)
1197
lines = chk_inv2.to_lines()
1198
inv_2 = CHKInventory.deserialise(chk_bytes, lines, (b"revid2",))
1199
self.assertEqual([(b'fileid', (u'file', u'file'), True, (True, True),
1200
(b'TREE_ROOT', b'TREE_ROOT'), (u'file',
1201
u'file'), ('file', 'file'),
1203
list(inv_1.iter_changes(inv_2)))
1205
def test_parent_id_basename_to_file_id_index_enabled(self):
1207
inv.revision_id = b"revid"
1208
inv.root.revision = b"rootrev"
1209
inv.add(InventoryFile(b"fileid", "file", inv.root.file_id))
1210
inv.get_entry(b"fileid").revision = b"filerev"
1211
inv.get_entry(b"fileid").executable = True
1212
inv.get_entry(b"fileid").text_sha1 = b"ffff"
1213
inv.get_entry(b"fileid").text_size = 1
1214
# get fresh objects.
1215
chk_bytes = self.get_chk_bytes()
1216
tmp_inv = CHKInventory.from_inventory(chk_bytes, inv)
1217
lines = tmp_inv.to_lines()
1218
chk_inv = CHKInventory.deserialise(chk_bytes, lines, (b"revid",))
1219
self.assertIsInstance(
1220
chk_inv.parent_id_basename_to_file_id, chk_map.CHKMap)
1222
{(b'', b''): b'TREE_ROOT', (b'TREE_ROOT', b'file'): b'fileid'},
1223
dict(chk_inv.parent_id_basename_to_file_id.iteritems()))
1225
def test_file_entry_to_bytes(self):
1226
inv = CHKInventory(None)
1227
ie = inventory.InventoryFile(b'file-id', 'filename', b'parent-id')
1228
ie.executable = True
1229
ie.revision = b'file-rev-id'
1230
ie.text_sha1 = b'abcdefgh'
1232
bytes = inv._entry_to_bytes(ie)
1233
self.assertEqual(b'file: file-id\nparent-id\nfilename\n'
1234
b'file-rev-id\nabcdefgh\n100\nY', bytes)
1235
ie2 = inv._bytes_to_entry(bytes)
1236
self.assertEqual(ie, ie2)
1237
self.assertIsInstance(ie2.name, text_type)
1238
self.assertEqual((b'filename', b'file-id', b'file-rev-id'),
1239
inv._bytes_to_utf8name_key(bytes))
1241
def test_file2_entry_to_bytes(self):
1242
inv = CHKInventory(None)
1244
ie = inventory.InventoryFile(b'file-id', u'\u03a9name', b'parent-id')
1245
ie.executable = False
1246
ie.revision = b'file-rev-id'
1247
ie.text_sha1 = b'123456'
1249
bytes = inv._entry_to_bytes(ie)
1250
self.assertEqual(b'file: file-id\nparent-id\n\xce\xa9name\n'
1251
b'file-rev-id\n123456\n25\nN', bytes)
1252
ie2 = inv._bytes_to_entry(bytes)
1253
self.assertEqual(ie, ie2)
1254
self.assertIsInstance(ie2.name, text_type)
1255
self.assertEqual((b'\xce\xa9name', b'file-id', b'file-rev-id'),
1256
inv._bytes_to_utf8name_key(bytes))
1258
def test_dir_entry_to_bytes(self):
1259
inv = CHKInventory(None)
1260
ie = inventory.InventoryDirectory(b'dir-id', 'dirname', b'parent-id')
1261
ie.revision = b'dir-rev-id'
1262
bytes = inv._entry_to_bytes(ie)
1263
self.assertEqual(b'dir: dir-id\nparent-id\ndirname\ndir-rev-id', bytes)
1264
ie2 = inv._bytes_to_entry(bytes)
1265
self.assertEqual(ie, ie2)
1266
self.assertIsInstance(ie2.name, text_type)
1267
self.assertEqual((b'dirname', b'dir-id', b'dir-rev-id'),
1268
inv._bytes_to_utf8name_key(bytes))
1270
def test_dir2_entry_to_bytes(self):
1271
inv = CHKInventory(None)
1272
ie = inventory.InventoryDirectory(b'dir-id', u'dir\u03a9name',
1274
ie.revision = b'dir-rev-id'
1275
bytes = inv._entry_to_bytes(ie)
1276
self.assertEqual(b'dir: dir-id\n\ndir\xce\xa9name\n'
1277
b'dir-rev-id', bytes)
1278
ie2 = inv._bytes_to_entry(bytes)
1279
self.assertEqual(ie, ie2)
1280
self.assertIsInstance(ie2.name, text_type)
1281
self.assertIs(ie2.parent_id, None)
1282
self.assertEqual((b'dir\xce\xa9name', b'dir-id', b'dir-rev-id'),
1283
inv._bytes_to_utf8name_key(bytes))
1285
def test_symlink_entry_to_bytes(self):
1286
inv = CHKInventory(None)
1287
ie = inventory.InventoryLink(b'link-id', 'linkname', b'parent-id')
1288
ie.revision = b'link-rev-id'
1289
ie.symlink_target = u'target/path'
1290
bytes = inv._entry_to_bytes(ie)
1291
self.assertEqual(b'symlink: link-id\nparent-id\nlinkname\n'
1292
b'link-rev-id\ntarget/path', bytes)
1293
ie2 = inv._bytes_to_entry(bytes)
1294
self.assertEqual(ie, ie2)
1295
self.assertIsInstance(ie2.name, text_type)
1296
self.assertIsInstance(ie2.symlink_target, text_type)
1297
self.assertEqual((b'linkname', b'link-id', b'link-rev-id'),
1298
inv._bytes_to_utf8name_key(bytes))
1300
def test_symlink2_entry_to_bytes(self):
1301
inv = CHKInventory(None)
1302
ie = inventory.InventoryLink(
1303
b'link-id', u'link\u03a9name', b'parent-id')
1304
ie.revision = b'link-rev-id'
1305
ie.symlink_target = u'target/\u03a9path'
1306
bytes = inv._entry_to_bytes(ie)
1307
self.assertEqual(b'symlink: link-id\nparent-id\nlink\xce\xa9name\n'
1308
b'link-rev-id\ntarget/\xce\xa9path', bytes)
1309
ie2 = inv._bytes_to_entry(bytes)
1310
self.assertEqual(ie, ie2)
1311
self.assertIsInstance(ie2.name, text_type)
1312
self.assertIsInstance(ie2.symlink_target, text_type)
1313
self.assertEqual((b'link\xce\xa9name', b'link-id', b'link-rev-id'),
1314
inv._bytes_to_utf8name_key(bytes))
1316
def test_tree_reference_entry_to_bytes(self):
1317
inv = CHKInventory(None)
1318
ie = inventory.TreeReference(b'tree-root-id', u'tree\u03a9name',
1320
ie.revision = b'tree-rev-id'
1321
ie.reference_revision = b'ref-rev-id'
1322
bytes = inv._entry_to_bytes(ie)
1323
self.assertEqual(b'tree: tree-root-id\nparent-id\ntree\xce\xa9name\n'
1324
b'tree-rev-id\nref-rev-id', bytes)
1325
ie2 = inv._bytes_to_entry(bytes)
1326
self.assertEqual(ie, ie2)
1327
self.assertIsInstance(ie2.name, text_type)
1328
self.assertEqual((b'tree\xce\xa9name', b'tree-root-id', b'tree-rev-id'),
1329
inv._bytes_to_utf8name_key(bytes))
1331
def make_basic_utf8_inventory(self):
1333
inv.revision_id = b"revid"
1334
inv.root.revision = b"rootrev"
1335
root_id = inv.root.file_id
1336
inv.add(InventoryFile(b"fileid", u'f\xefle', root_id))
1337
inv.get_entry(b"fileid").revision = b"filerev"
1338
inv.get_entry(b"fileid").text_sha1 = b"ffff"
1339
inv.get_entry(b"fileid").text_size = 0
1340
inv.add(InventoryDirectory(b"dirid", u'dir-\N{EURO SIGN}', root_id))
1341
inv.get_entry(b"dirid").revision = b"dirrev"
1342
inv.add(InventoryFile(b"childid", u'ch\xefld', b"dirid"))
1343
inv.get_entry(b"childid").revision = b"filerev"
1344
inv.get_entry(b"childid").text_sha1 = b"ffff"
1345
inv.get_entry(b"childid").text_size = 0
1346
chk_bytes = self.get_chk_bytes()
1347
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1348
lines = chk_inv.to_lines()
1349
return CHKInventory.deserialise(chk_bytes, lines, (b"revid",))
1351
def test__preload_handles_utf8(self):
1352
new_inv = self.make_basic_utf8_inventory()
1353
self.assertEqual({}, new_inv._fileid_to_entry_cache)
1354
self.assertFalse(new_inv._fully_cached)
1355
new_inv._preload_cache()
1357
sorted([new_inv.root_id, b"fileid", b"dirid", b"childid"]),
1358
sorted(new_inv._fileid_to_entry_cache.keys()))
1359
ie_root = new_inv._fileid_to_entry_cache[new_inv.root_id]
1360
self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
1361
sorted(ie_root._children.keys()))
1362
ie_dir = new_inv._fileid_to_entry_cache[b'dirid']
1363
self.assertEqual([u'ch\xefld'], sorted(ie_dir._children.keys()))
1365
def test__preload_populates_cache(self):
1367
inv.revision_id = b"revid"
1368
inv.root.revision = b"rootrev"
1369
root_id = inv.root.file_id
1370
inv.add(InventoryFile(b"fileid", "file", root_id))
1371
inv.get_entry(b"fileid").revision = b"filerev"
1372
inv.get_entry(b"fileid").executable = True
1373
inv.get_entry(b"fileid").text_sha1 = b"ffff"
1374
inv.get_entry(b"fileid").text_size = 1
1375
inv.add(InventoryDirectory(b"dirid", "dir", root_id))
1376
inv.get_entry(b"dirid").revision = b"dirrev"
1377
inv.add(InventoryFile(b"childid", "child", b"dirid"))
1378
inv.get_entry(b"childid").revision = b"filerev"
1379
inv.get_entry(b"childid").executable = False
1380
inv.get_entry(b"childid").text_sha1 = b"dddd"
1381
inv.get_entry(b"childid").text_size = 1
1382
chk_bytes = self.get_chk_bytes()
1383
chk_inv = CHKInventory.from_inventory(chk_bytes, inv)
1384
lines = chk_inv.to_lines()
1385
new_inv = CHKInventory.deserialise(chk_bytes, lines, (b"revid",))
1386
self.assertEqual({}, new_inv._fileid_to_entry_cache)
1387
self.assertFalse(new_inv._fully_cached)
1388
new_inv._preload_cache()
1390
sorted([root_id, b"fileid", b"dirid", b"childid"]),
1391
sorted(new_inv._fileid_to_entry_cache.keys()))
1392
self.assertTrue(new_inv._fully_cached)
1393
ie_root = new_inv._fileid_to_entry_cache[root_id]
1394
self.assertEqual(['dir', 'file'], sorted(ie_root._children.keys()))
1395
ie_dir = new_inv._fileid_to_entry_cache[b'dirid']
1396
self.assertEqual(['child'], sorted(ie_dir._children.keys()))
1398
def test__preload_handles_partially_evaluated_inventory(self):
1399
new_inv = self.make_basic_utf8_inventory()
1400
ie = new_inv.get_entry(new_inv.root_id)
1401
self.assertIs(None, ie._children)
1402
self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
1403
sorted(ie.children.keys()))
1404
# Accessing .children loads _children
1405
self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
1406
sorted(ie._children.keys()))
1407
new_inv._preload_cache()
1409
self.assertEqual([u'dir-\N{EURO SIGN}', u'f\xefle'],
1410
sorted(ie._children.keys()))
1411
ie_dir = new_inv.get_entry(b"dirid")
1412
self.assertEqual([u'ch\xefld'],
1413
sorted(ie_dir._children.keys()))
1415
def test_filter_change_in_renamed_subfolder(self):
1416
inv = Inventory(b'tree-root')
1417
inv.root.revision = b'rootrev'
1418
src_ie = inv.add_path('src', 'directory', b'src-id')
1419
src_ie.revision = b'srcrev'
1420
sub_ie = inv.add_path('src/sub/', 'directory', b'sub-id')
1421
sub_ie.revision = b'subrev'
1422
a_ie = inv.add_path('src/sub/a', 'file', b'a-id')
1423
a_ie.revision = b'filerev'
1424
a_ie.text_sha1 = osutils.sha_string(b'content\n')
1425
a_ie.text_size = len(b'content\n')
1426
chk_bytes = self.get_chk_bytes()
1427
inv = CHKInventory.from_inventory(chk_bytes, inv)
1428
inv = inv.create_by_apply_delta([
1429
("src/sub/a", "src/sub/a", b"a-id", a_ie),
1430
("src", "src2", b"src-id", src_ie),
1432
new_inv = inv.filter([b'a-id', b'src-id'])
1436
('src/sub', b'sub-id'),
1437
('src/sub/a', b'a-id'),
1438
], [(path, ie.file_id) for path, ie in new_inv.iter_entries()])
1441
class TestCHKInventoryExpand(tests.TestCaseWithMemoryTransport):
1443
def get_chk_bytes(self):
1444
factory = groupcompress.make_pack_factory(True, True, 1)
1445
trans = self.get_transport('')
1446
return factory(trans)
1448
def make_dir(self, inv, name, parent_id, revision):
1449
ie = inv.make_entry('directory', name, parent_id,
1450
name.encode('utf-8') + b'-id')
1451
ie.revision = revision
1454
def make_file(self, inv, name, parent_id, revision, content=b'content\n'):
1455
ie = inv.make_entry('file', name, parent_id,
1456
name.encode('utf-8') + b'-id')
1457
ie.text_sha1 = osutils.sha_string(content)
1458
ie.text_size = len(content)
1459
ie.revision = revision
1462
def make_simple_inventory(self):
1463
inv = Inventory(b'TREE_ROOT')
1464
inv.revision_id = b"revid"
1465
inv.root.revision = b"rootrev"
1468
# sub-file1 sub-file1-id
1469
# sub-file2 sub-file2-id
1470
# sub-dir1/ sub-dir1-id
1471
# subsub-file1 subsub-file1-id
1473
# sub2-file1 sub2-file1-id
1475
self.make_dir(inv, 'dir1', b'TREE_ROOT', b'dirrev')
1476
self.make_dir(inv, 'dir2', b'TREE_ROOT', b'dirrev')
1477
self.make_dir(inv, 'sub-dir1', b'dir1-id', b'dirrev')
1478
self.make_file(inv, 'top', b'TREE_ROOT', b'filerev')
1479
self.make_file(inv, 'sub-file1', b'dir1-id', b'filerev')
1480
self.make_file(inv, 'sub-file2', b'dir1-id', b'filerev')
1481
self.make_file(inv, 'subsub-file1', b'sub-dir1-id', b'filerev')
1482
self.make_file(inv, 'sub2-file1', b'dir2-id', b'filerev')
1483
chk_bytes = self.get_chk_bytes()
1484
# use a small maximum_size to force internal paging structures
1485
chk_inv = CHKInventory.from_inventory(chk_bytes, inv,
1487
search_key_name=b'hash-255-way')
1488
lines = chk_inv.to_lines()
1489
return CHKInventory.deserialise(chk_bytes, lines, (b"revid",))
1491
def assert_Getitems(self, expected_fileids, inv, file_ids):
1492
self.assertEqual(sorted(expected_fileids),
1493
sorted([ie.file_id for ie in inv._getitems(file_ids)]))
1495
def assertExpand(self, all_ids, inv, file_ids):
1497
val_children) = inv._expand_fileids_to_parents_and_children(file_ids)
1498
self.assertEqual(set(all_ids), val_all_ids)
1499
entries = inv._getitems(val_all_ids)
1500
expected_children = {}
1501
for entry in entries:
1502
s = expected_children.setdefault(entry.parent_id, [])
1503
s.append(entry.file_id)
1505
k: sorted(v) for k, v in val_children.items()}
1506
expected_children = {
1507
k: sorted(v) for k, v in expected_children.items()}
1508
self.assertEqual(expected_children, val_children)
1510
def test_make_simple_inventory(self):
1511
inv = self.make_simple_inventory()
1513
for path, entry in inv.iter_entries_by_dir():
1514
layout.append((path, entry.file_id))
1517
('dir1', b'dir1-id'),
1518
('dir2', b'dir2-id'),
1520
('dir1/sub-dir1', b'sub-dir1-id'),
1521
('dir1/sub-file1', b'sub-file1-id'),
1522
('dir1/sub-file2', b'sub-file2-id'),
1523
('dir1/sub-dir1/subsub-file1', b'subsub-file1-id'),
1524
('dir2/sub2-file1', b'sub2-file1-id'),
1527
def test__getitems(self):
1528
inv = self.make_simple_inventory()
1530
self.assert_Getitems([b'dir1-id'], inv, [b'dir1-id'])
1531
self.assertTrue(b'dir1-id' in inv._fileid_to_entry_cache)
1532
self.assertFalse(b'sub-file2-id' in inv._fileid_to_entry_cache)
1534
self.assert_Getitems([b'dir1-id'], inv, [b'dir1-id'])
1536
self.assert_Getitems([b'dir1-id', b'sub-file2-id'], inv,
1537
[b'dir1-id', b'sub-file2-id'])
1538
self.assertTrue(b'dir1-id' in inv._fileid_to_entry_cache)
1539
self.assertTrue(b'sub-file2-id' in inv._fileid_to_entry_cache)
1541
def test_single_file(self):
1542
inv = self.make_simple_inventory()
1543
self.assertExpand([b'TREE_ROOT', b'top-id'], inv, [b'top-id'])
1545
def test_get_all_parents(self):
1546
inv = self.make_simple_inventory()
1547
self.assertExpand([b'TREE_ROOT', b'dir1-id', b'sub-dir1-id',
1549
], inv, [b'subsub-file1-id'])
1551
def test_get_children(self):
1552
inv = self.make_simple_inventory()
1553
self.assertExpand([b'TREE_ROOT', b'dir1-id', b'sub-dir1-id',
1554
b'sub-file1-id', b'sub-file2-id', b'subsub-file1-id',
1555
], inv, [b'dir1-id'])
1557
def test_from_root(self):
1558
inv = self.make_simple_inventory()
1559
self.assertExpand([b'TREE_ROOT', b'dir1-id', b'dir2-id', b'sub-dir1-id',
1560
b'sub-file1-id', b'sub-file2-id', b'sub2-file1-id',
1561
b'subsub-file1-id', b'top-id'], inv, [b'TREE_ROOT'])
1563
def test_top_level_file(self):
1564
inv = self.make_simple_inventory()
1565
self.assertExpand([b'TREE_ROOT', b'top-id'], inv, [b'top-id'])
1567
def test_subsub_file(self):
1568
inv = self.make_simple_inventory()
1569
self.assertExpand([b'TREE_ROOT', b'dir1-id', b'sub-dir1-id',
1570
b'subsub-file1-id'], inv, [b'subsub-file1-id'])
1572
def test_sub_and_root(self):
1573
inv = self.make_simple_inventory()
1574
self.assertExpand([b'TREE_ROOT', b'dir1-id', b'sub-dir1-id', b'top-id',
1575
b'subsub-file1-id'], inv, [b'top-id', b'subsub-file1-id'])
1578
class TestMutableInventoryFromTree(TestCaseWithTransport):
1580
def test_empty(self):
1581
repository = self.make_repository('.')
1582
tree = repository.revision_tree(revision.NULL_REVISION)
1583
inv = mutable_inventory_from_tree(tree)
1584
self.assertEqual(revision.NULL_REVISION, inv.revision_id)
1585
self.assertEqual(0, len(inv))
1587
def test_some_files(self):
1588
wt = self.make_branch_and_tree('.')
1589
self.build_tree(['a'])
1590
wt.add(['a'], [b'thefileid'])
1591
revid = wt.commit("commit")
1592
tree = wt.branch.repository.revision_tree(revid)
1593
inv = mutable_inventory_from_tree(tree)
1594
self.assertEqual(revid, inv.revision_id)
1595
self.assertEqual(2, len(inv))
1596
self.assertEqual("a", inv.get_entry(b'thefileid').name)
1597
# The inventory should be mutable and independent of
1599
self.assertFalse(tree.root_inventory.get_entry(
1600
b'thefileid').executable)
1601
inv.get_entry(b'thefileid').executable = True
1602
self.assertFalse(tree.root_inventory.get_entry(
1603
b'thefileid').executable)
132
link = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
133
self.failIf(link.has_text())
136
class TestEntryDiffing(TestCaseInTempDir):
139
super(TestEntryDiffing, self).setUp()
140
self.branch = Branch.initialize('.')
141
self.wt = self.branch.working_tree()
142
print >> open('file', 'wb'), 'foo'
143
self.branch.add(['file'], ['fileid'])
145
os.symlink('target1', 'symlink')
146
self.branch.add(['symlink'], ['linkid'])
147
self.wt.commit('message_1', rev_id = '1')
148
print >> open('file', 'wb'), 'bar'
151
os.symlink('target2', 'symlink')
152
self.tree_1 = self.branch.revision_tree('1')
153
self.inv_1 = self.branch.get_inventory('1')
154
self.file_1 = self.inv_1['fileid']
155
self.tree_2 = self.branch.working_tree()
156
self.inv_2 = self.tree_2.read_working_inventory()
157
self.file_2 = self.inv_2['fileid']
159
self.link_1 = self.inv_1['linkid']
160
self.link_2 = self.inv_2['linkid']
162
def test_file_diff_deleted(self):
164
self.file_1.diff(internal_diff,
165
"old_label", self.tree_1,
166
"/dev/null", None, None,
168
self.assertEqual(output.getvalue(), "--- old_label\t\n"
174
def test_file_diff_added(self):
176
self.file_1.diff(internal_diff,
177
"new_label", self.tree_1,
178
"/dev/null", None, None,
179
output, reverse=True)
180
self.assertEqual(output.getvalue(), "--- /dev/null\t\n"
186
def test_file_diff_changed(self):
188
self.file_1.diff(internal_diff,
189
"/dev/null", self.tree_1,
190
"new_label", self.file_2, self.tree_2,
192
self.assertEqual(output.getvalue(), "--- /dev/null\t\n"
199
def test_link_diff_deleted(self):
200
if not has_symlinks():
203
self.link_1.diff(internal_diff,
204
"old_label", self.tree_1,
205
"/dev/null", None, None,
207
self.assertEqual(output.getvalue(),
208
"=== target was 'target1'\n")
210
def test_link_diff_added(self):
211
if not has_symlinks():
214
self.link_1.diff(internal_diff,
215
"new_label", self.tree_1,
216
"/dev/null", None, None,
217
output, reverse=True)
218
self.assertEqual(output.getvalue(),
219
"=== target is 'target1'\n")
221
def test_link_diff_changed(self):
222
if not has_symlinks():
225
self.link_1.diff(internal_diff,
226
"/dev/null", self.tree_1,
227
"new_label", self.link_2, self.tree_2,
229
self.assertEqual(output.getvalue(),
230
"=== target changed 'target1' => 'target2'\n")
233
class TestSnapshot(TestCaseInTempDir):
236
# for full testing we'll need a branch
237
# with a subdir to test parent changes.
238
# and a file, link and dir under that.
239
# but right now I only need one attribute
240
# to change, and then test merge patterns
241
# with fake parent entries.
242
super(TestSnapshot, self).setUp()
243
self.branch = Branch.initialize('.')
244
self.build_tree(['subdir/', 'subdir/file'])
245
self.branch.add(['subdir', 'subdir/file'], ['dirid', 'fileid'])
248
self.wt = self.branch.working_tree()
249
self.wt.commit('message_1', rev_id = '1')
250
self.tree_1 = self.branch.revision_tree('1')
251
self.inv_1 = self.branch.get_inventory('1')
252
self.file_1 = self.inv_1['fileid']
253
self.work_tree = self.branch.working_tree()
254
self.file_active = self.work_tree.inventory['fileid']
256
def test_snapshot_new_revision(self):
257
# This tests that a simple commit with no parents makes a new
258
# revision value in the inventory entry
259
self.file_active.snapshot('2', 'subdir/file', {}, self.work_tree,
260
self.branch.weave_store,
261
self.branch.get_transaction())
262
# expected outcome - file_1 has a revision id of '2', and we can get
263
# its text of 'file contents' out of the weave.
264
self.assertEqual(self.file_1.revision, '1')
265
self.assertEqual(self.file_active.revision, '2')
266
# this should be a separate test probably, but lets check it once..
267
lines = self.branch.weave_store.get_lines('fileid','2',
268
self.branch.get_transaction())
269
self.assertEqual(lines, ['contents of subdir/file\n'])
271
def test_snapshot_unchanged(self):
272
#This tests that a simple commit does not make a new entry for
273
# an unchanged inventory entry
274
self.file_active.snapshot('2', 'subdir/file', {'1':self.file_1},
275
self.work_tree, self.branch.weave_store,
276
self.branch.get_transaction())
277
self.assertEqual(self.file_1.revision, '1')
278
self.assertEqual(self.file_active.revision, '1')
279
self.assertRaises(errors.WeaveError,
280
self.branch.weave_store.get_lines, 'fileid', '2',
281
self.branch.get_transaction())
283
def test_snapshot_merge_identical_different_revid(self):
284
# This tests that a commit with two identical parents, one of which has
285
# a different revision id, results in a new revision id in the entry.
286
# 1->other, commit a merge of other against 1, results in 2.
287
other_ie = inventory.InventoryFile('fileid', 'newname', self.file_1.parent_id)
288
other_ie = inventory.InventoryFile('fileid', 'file', self.file_1.parent_id)
289
other_ie.revision = '1'
290
other_ie.text_sha1 = self.file_1.text_sha1
291
other_ie.text_size = self.file_1.text_size
292
self.assertEqual(self.file_1, other_ie)
293
other_ie.revision = 'other'
294
self.assertNotEqual(self.file_1, other_ie)
295
self.branch.weave_store.add_identical_text('fileid', '1', 'other', ['1'],
296
self.branch.get_transaction())
297
self.file_active.snapshot('2', 'subdir/file',
298
{'1':self.file_1, 'other':other_ie},
299
self.work_tree, self.branch.weave_store,
300
self.branch.get_transaction())
301
self.assertEqual(self.file_active.revision, '2')
303
def test_snapshot_changed(self):
304
# This tests that a commit with one different parent results in a new
305
# revision id in the entry.
306
self.file_active.name='newname'
307
rename('subdir/file', 'subdir/newname')
308
self.file_active.snapshot('2', 'subdir/newname', {'1':self.file_1},
310
self.branch.weave_store,
311
self.branch.get_transaction())
312
# expected outcome - file_1 has a revision id of '2'
313
self.assertEqual(self.file_active.revision, '2')
316
class TestPreviousHeads(TestCaseInTempDir):
319
# we want several inventories, that respectively
320
# give use the following scenarios:
321
# A) fileid not in any inventory (A),
322
# B) fileid present in one inventory (B) and (A,B)
323
# C) fileid present in two inventories, and they
324
# are not mutual descendents (B, C)
325
# D) fileid present in two inventories and one is
326
# a descendent of the other. (B, D)
327
super(TestPreviousHeads, self).setUp()
328
self.build_tree(['file'])
329
self.branch = Branch.initialize('.')
330
self.wt = self.branch.working_tree()
331
self.wt.commit('new branch', allow_pointless=True, rev_id='A')
332
self.inv_A = self.branch.get_inventory('A')
333
self.branch.add(['file'], ['fileid'])
334
self.wt.commit('add file', rev_id='B')
335
self.inv_B = self.branch.get_inventory('B')
336
self.branch.put_controlfile('revision-history', 'A\n')
337
self.assertEqual(self.branch.revision_history(), ['A'])
338
self.wt.commit('another add of file', rev_id='C')
339
self.inv_C = self.branch.get_inventory('C')
340
self.wt.add_pending_merge('B')
341
self.wt.commit('merge in B', rev_id='D')
342
self.inv_D = self.branch.get_inventory('D')
343
self.file_active = self.wt.inventory['fileid']
344
self.weave = self.branch.weave_store.get_weave('fileid',
345
self.branch.get_transaction())
347
def get_previous_heads(self, inventories):
348
return self.file_active.find_previous_heads(inventories, self.weave)
350
def test_fileid_in_no_inventory(self):
351
self.assertEqual({}, self.get_previous_heads([self.inv_A]))
353
def test_fileid_in_one_inventory(self):
354
self.assertEqual({'B':self.inv_B['fileid']},
355
self.get_previous_heads([self.inv_B]))
356
self.assertEqual({'B':self.inv_B['fileid']},
357
self.get_previous_heads([self.inv_A, self.inv_B]))
358
self.assertEqual({'B':self.inv_B['fileid']},
359
self.get_previous_heads([self.inv_B, self.inv_A]))
361
def test_fileid_in_two_inventories_gives_both_entries(self):
362
self.assertEqual({'B':self.inv_B['fileid'],
363
'C':self.inv_C['fileid']},
364
self.get_previous_heads([self.inv_B, self.inv_C]))
365
self.assertEqual({'B':self.inv_B['fileid'],
366
'C':self.inv_C['fileid']},
367
self.get_previous_heads([self.inv_C, self.inv_B]))
369
def test_fileid_in_two_inventories_already_merged_gives_head(self):
370
self.assertEqual({'D':self.inv_D['fileid']},
371
self.get_previous_heads([self.inv_B, self.inv_D]))
372
self.assertEqual({'D':self.inv_D['fileid']},
373
self.get_previous_heads([self.inv_D, self.inv_B]))
375
# TODO: test two inventories with the same file revision