764
686
# This will unlock it
765
687
self.check_state_with_reopen(expected_result, state)
767
def test_set_state_from_inventory_preserves_hashcache(self):
768
# https://bugs.launchpad.net/bzr/+bug/146176
769
# set_state_from_inventory should preserve the stat and hash value for
770
# workingtree files that are not changed by the inventory.
772
tree = self.make_branch_and_tree('.')
773
# depends on the default format using dirstate...
776
# make a dirstate with some valid hashcache data
777
# file on disk, but that's not needed for this test
778
foo_contents = 'contents of foo'
779
self.build_tree_contents([('foo', foo_contents)])
780
tree.add('foo', 'foo-id')
782
foo_stat = os.stat('foo')
783
foo_packed = dirstate.pack_stat(foo_stat)
784
foo_sha = osutils.sha_string(foo_contents)
785
foo_size = len(foo_contents)
787
# should not be cached yet, because the file's too fresh
789
(('', 'foo', 'foo-id',),
790
[('f', '', 0, False, dirstate.DirState.NULLSTAT)]),
791
tree._dirstate._get_entry(0, 'foo-id'))
792
# poke in some hashcache information - it wouldn't normally be
793
# stored because it's too fresh
794
tree._dirstate.update_minimal(
795
('', 'foo', 'foo-id'),
796
'f', False, foo_sha, foo_packed, foo_size, 'foo')
797
# now should be cached
799
(('', 'foo', 'foo-id',),
800
[('f', foo_sha, foo_size, False, foo_packed)]),
801
tree._dirstate._get_entry(0, 'foo-id'))
803
# extract the inventory, and add something to it
804
inv = tree._get_inventory()
805
# should see the file we poked in...
806
self.assertTrue(inv.has_id('foo-id'))
807
self.assertTrue(inv.has_filename('foo'))
808
inv.add_path('bar', 'file', 'bar-id')
809
tree._dirstate._validate()
810
# this used to cause it to lose its hashcache
811
tree._dirstate.set_state_from_inventory(inv)
812
tree._dirstate._validate()
818
# now check that the state still has the original hashcache value
819
state = tree._dirstate
821
foo_tuple = state._get_entry(0, path_utf8='foo')
823
(('', 'foo', 'foo-id',),
824
[('f', foo_sha, len(foo_contents), False,
825
dirstate.pack_stat(foo_stat))]),
831
def test_set_state_from_inventory_mixed_paths(self):
832
tree1 = self.make_branch_and_tree('tree1')
833
self.build_tree(['tree1/a/', 'tree1/a/b/', 'tree1/a-b/',
834
'tree1/a/b/foo', 'tree1/a-b/bar'])
837
tree1.add(['a', 'a/b', 'a-b', 'a/b/foo', 'a-b/bar'],
838
['a-id', 'b-id', 'a-b-id', 'foo-id', 'bar-id'])
839
tree1.commit('rev1', rev_id='rev1')
840
root_id = tree1.get_root_id()
841
inv = tree1.inventory
844
expected_result1 = [('', '', root_id, 'd'),
845
('', 'a', 'a-id', 'd'),
846
('', 'a-b', 'a-b-id', 'd'),
847
('a', 'b', 'b-id', 'd'),
848
('a/b', 'foo', 'foo-id', 'f'),
849
('a-b', 'bar', 'bar-id', 'f'),
851
expected_result2 = [('', '', root_id, 'd'),
852
('', 'a', 'a-id', 'd'),
853
('', 'a-b', 'a-b-id', 'd'),
854
('a-b', 'bar', 'bar-id', 'f'),
856
state = dirstate.DirState.initialize('dirstate')
858
state.set_state_from_inventory(inv)
860
for entry in state._iter_entries():
861
values.append(entry[0] + entry[1][0][:1])
862
self.assertEqual(expected_result1, values)
864
state.set_state_from_inventory(inv)
866
for entry in state._iter_entries():
867
values.append(entry[0] + entry[1][0][:1])
868
self.assertEqual(expected_result2, values)
872
689
def test_set_path_id_no_parents(self):
873
690
"""The id of a path can be changed trivally with no parents."""
874
691
state = dirstate.DirState.initialize('dirstate')
1459
class TestIterChildEntries(TestCaseWithDirState):
1461
def create_dirstate_with_two_trees(self):
1462
"""This dirstate contains multiple files and directories.
1472
b/h\xc3\xa5 h-\xc3\xa5-file #This is u'\xe5' encoded into utf-8
1474
Notice that a/e is an empty directory.
1476
There is one parent tree, which has the same shape with the following variations:
1477
b/g in the parent is gone.
1478
b/h in the parent has a different id
1479
b/i is new in the parent
1480
c is renamed to b/j in the parent
1482
:return: The dirstate, still write-locked.
1484
packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
1485
null_sha = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
1486
NULL_PARENT_DETAILS = dirstate.DirState.NULL_PARENT_DETAILS
1487
root_entry = ('', '', 'a-root-value'), [
1488
('d', '', 0, False, packed_stat),
1489
('d', '', 0, False, 'parent-revid'),
1491
a_entry = ('', 'a', 'a-dir'), [
1492
('d', '', 0, False, packed_stat),
1493
('d', '', 0, False, 'parent-revid'),
1495
b_entry = ('', 'b', 'b-dir'), [
1496
('d', '', 0, False, packed_stat),
1497
('d', '', 0, False, 'parent-revid'),
1499
c_entry = ('', 'c', 'c-file'), [
1500
('f', null_sha, 10, False, packed_stat),
1501
('r', 'b/j', 0, False, ''),
1503
d_entry = ('', 'd', 'd-file'), [
1504
('f', null_sha, 20, False, packed_stat),
1505
('f', 'd', 20, False, 'parent-revid'),
1507
e_entry = ('a', 'e', 'e-dir'), [
1508
('d', '', 0, False, packed_stat),
1509
('d', '', 0, False, 'parent-revid'),
1511
f_entry = ('a', 'f', 'f-file'), [
1512
('f', null_sha, 30, False, packed_stat),
1513
('f', 'f', 20, False, 'parent-revid'),
1515
g_entry = ('b', 'g', 'g-file'), [
1516
('f', null_sha, 30, False, packed_stat),
1517
NULL_PARENT_DETAILS,
1519
h_entry1 = ('b', 'h\xc3\xa5', 'h-\xc3\xa5-file1'), [
1520
('f', null_sha, 40, False, packed_stat),
1521
NULL_PARENT_DETAILS,
1523
h_entry2 = ('b', 'h\xc3\xa5', 'h-\xc3\xa5-file2'), [
1524
NULL_PARENT_DETAILS,
1525
('f', 'h', 20, False, 'parent-revid'),
1527
i_entry = ('b', 'i', 'i-file'), [
1528
NULL_PARENT_DETAILS,
1529
('f', 'h', 20, False, 'parent-revid'),
1531
j_entry = ('b', 'j', 'c-file'), [
1532
('r', 'c', 0, False, ''),
1533
('f', 'j', 20, False, 'parent-revid'),
1536
dirblocks.append(('', [root_entry]))
1537
dirblocks.append(('', [a_entry, b_entry, c_entry, d_entry]))
1538
dirblocks.append(('a', [e_entry, f_entry]))
1539
dirblocks.append(('b', [g_entry, h_entry1, h_entry2, i_entry, j_entry]))
1540
state = dirstate.DirState.initialize('dirstate')
1543
state._set_data(['parent'], dirblocks)
1547
return state, dirblocks
1549
def test_iter_children_b(self):
1550
state, dirblocks = self.create_dirstate_with_two_trees()
1551
self.addCleanup(state.unlock)
1552
expected_result = []
1553
expected_result.append(dirblocks[3][1][2]) # h2
1554
expected_result.append(dirblocks[3][1][3]) # i
1555
expected_result.append(dirblocks[3][1][4]) # j
1556
self.assertEqual(expected_result,
1557
list(state._iter_child_entries(1, 'b')))
1559
def test_iter_child_root(self):
1560
state, dirblocks = self.create_dirstate_with_two_trees()
1561
self.addCleanup(state.unlock)
1562
expected_result = []
1563
expected_result.append(dirblocks[1][1][0]) # a
1564
expected_result.append(dirblocks[1][1][1]) # b
1565
expected_result.append(dirblocks[1][1][3]) # d
1566
expected_result.append(dirblocks[2][1][0]) # e
1567
expected_result.append(dirblocks[2][1][1]) # f
1568
expected_result.append(dirblocks[3][1][2]) # h2
1569
expected_result.append(dirblocks[3][1][3]) # i
1570
expected_result.append(dirblocks[3][1][4]) # j
1571
self.assertEqual(expected_result,
1572
list(state._iter_child_entries(1, '')))
1575
class TestDirstateSortOrder(tests.TestCaseWithTransport):
1278
class TestDirstateSortOrder(TestCaseWithTransport):
1576
1279
"""Test that DirState adds entries in the right order."""
1578
1281
def test_add_sorting(self):
1688
1388
self.st_ino = ino
1689
1389
self.st_mode = mode
1693
return _FakeStat(st.st_size, st.st_mtime, st.st_ctime, st.st_dev,
1694
st.st_ino, st.st_mode)
1697
class TestPackStat(tests.TestCaseWithTransport):
1392
class TestUpdateEntry(TestCaseWithDirState):
1393
"""Test the DirState.update_entry functions"""
1395
def get_state_with_a(self):
1396
"""Create a DirState tracking a single object named 'a'"""
1397
state = InstrumentedDirState.initialize('dirstate')
1398
self.addCleanup(state.unlock)
1399
state.add('a', 'a-id', 'file', None, '')
1400
entry = state._get_entry(0, path_utf8='a')
1403
def test_update_entry(self):
1404
state, entry = self.get_state_with_a()
1405
self.build_tree(['a'])
1406
# Add one where we don't provide the stat or sha already
1407
self.assertEqual(('', 'a', 'a-id'), entry[0])
1408
self.assertEqual([('f', '', 0, False, dirstate.DirState.NULLSTAT)],
1410
# Flush the buffers to disk
1412
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1413
state._dirblock_state)
1415
stat_value = os.lstat('a')
1416
packed_stat = dirstate.pack_stat(stat_value)
1417
link_or_sha1 = state.update_entry(entry, abspath='a',
1418
stat_value=stat_value)
1419
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
1422
# The dirblock entry should be updated with the new info
1423
self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
1425
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1426
state._dirblock_state)
1427
mode = stat_value.st_mode
1428
self.assertEqual([('sha1', 'a'), ('is_exec', mode, False)], state._log)
1431
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1432
state._dirblock_state)
1434
# If we do it again right away, we don't know if the file has changed
1435
# so we will re-read the file. Roll the clock back so the file is
1436
# guaranteed to look too new.
1437
state.adjust_time(-10)
1439
link_or_sha1 = state.update_entry(entry, abspath='a',
1440
stat_value=stat_value)
1441
self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
1442
('sha1', 'a'), ('is_exec', mode, False),
1444
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
1446
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1447
state._dirblock_state)
1450
# However, if we move the clock forward so the file is considered
1451
# "stable", it should just returned the cached value.
1452
state.adjust_time(20)
1453
link_or_sha1 = state.update_entry(entry, abspath='a',
1454
stat_value=stat_value)
1455
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
1457
self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
1458
('sha1', 'a'), ('is_exec', mode, False),
1461
def test_update_entry_no_stat_value(self):
1462
"""Passing the stat_value is optional."""
1463
state, entry = self.get_state_with_a()
1464
state.adjust_time(-10) # Make sure the file looks new
1465
self.build_tree(['a'])
1466
# Add one where we don't provide the stat or sha already
1467
link_or_sha1 = state.update_entry(entry, abspath='a')
1468
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
1470
stat_value = os.lstat('a')
1471
self.assertEqual([('lstat', 'a'), ('sha1', 'a'),
1472
('is_exec', stat_value.st_mode, False),
1475
def test_update_entry_symlink(self):
1476
"""Update entry should read symlinks."""
1477
if not osutils.has_symlinks():
1478
# PlatformDeficiency / TestSkipped
1479
raise TestSkipped("No symlink support")
1480
state, entry = self.get_state_with_a()
1482
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1483
state._dirblock_state)
1484
os.symlink('target', 'a')
1486
state.adjust_time(-10) # Make the symlink look new
1487
stat_value = os.lstat('a')
1488
packed_stat = dirstate.pack_stat(stat_value)
1489
link_or_sha1 = state.update_entry(entry, abspath='a',
1490
stat_value=stat_value)
1491
self.assertEqual('target', link_or_sha1)
1492
self.assertEqual([('read_link', 'a', '')], state._log)
1493
# Dirblock is updated
1494
self.assertEqual([('l', link_or_sha1, 6, False, packed_stat)],
1496
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
1497
state._dirblock_state)
1499
# Because the stat_value looks new, we should re-read the target
1500
link_or_sha1 = state.update_entry(entry, abspath='a',
1501
stat_value=stat_value)
1502
self.assertEqual('target', link_or_sha1)
1503
self.assertEqual([('read_link', 'a', ''),
1504
('read_link', 'a', 'target'),
1506
state.adjust_time(+20) # Skip into the future, all files look old
1507
link_or_sha1 = state.update_entry(entry, abspath='a',
1508
stat_value=stat_value)
1509
self.assertEqual('target', link_or_sha1)
1510
# There should not be a new read_link call.
1511
# (this is a weak assertion, because read_link is fairly inexpensive,
1512
# versus the number of symlinks that we would have)
1513
self.assertEqual([('read_link', 'a', ''),
1514
('read_link', 'a', 'target'),
1517
def test_update_entry_dir(self):
1518
state, entry = self.get_state_with_a()
1519
self.build_tree(['a/'])
1520
self.assertIs(None, state.update_entry(entry, 'a'))
1522
def create_and_test_file(self, state, entry):
1523
"""Create a file at 'a' and verify the state finds it.
1525
The state should already be versioning *something* at 'a'. This makes
1526
sure that state.update_entry recognizes it as a file.
1528
self.build_tree(['a'])
1529
stat_value = os.lstat('a')
1530
packed_stat = dirstate.pack_stat(stat_value)
1532
link_or_sha1 = state.update_entry(entry, abspath='a')
1533
self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
1535
self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
1539
def create_and_test_dir(self, state, entry):
1540
"""Create a directory at 'a' and verify the state finds it.
1542
The state should already be versioning *something* at 'a'. This makes
1543
sure that state.update_entry recognizes it as a directory.
1545
self.build_tree(['a/'])
1546
stat_value = os.lstat('a')
1547
packed_stat = dirstate.pack_stat(stat_value)
1549
link_or_sha1 = state.update_entry(entry, abspath='a')
1550
self.assertIs(None, link_or_sha1)
1551
self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
1555
def create_and_test_symlink(self, state, entry):
1556
"""Create a symlink at 'a' and verify the state finds it.
1558
The state should already be versioning *something* at 'a'. This makes
1559
sure that state.update_entry recognizes it as a symlink.
1561
This should not be called if this platform does not have symlink
1564
# caller should care about skipping test on platforms without symlinks
1565
os.symlink('path/to/foo', 'a')
1567
stat_value = os.lstat('a')
1568
packed_stat = dirstate.pack_stat(stat_value)
1570
link_or_sha1 = state.update_entry(entry, abspath='a')
1571
self.assertEqual('path/to/foo', link_or_sha1)
1572
self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
1576
def test_update_missing_file(self):
1577
state, entry = self.get_state_with_a()
1578
packed_stat = self.create_and_test_file(state, entry)
1579
# Now if we delete the file, update_entry should recover and
1582
self.assertIs(None, state.update_entry(entry, abspath='a'))
1583
# And the record shouldn't be changed.
1584
digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1585
self.assertEqual([('f', digest, 14, False, packed_stat)],
1588
def test_update_missing_dir(self):
1589
state, entry = self.get_state_with_a()
1590
packed_stat = self.create_and_test_dir(state, entry)
1591
# Now if we delete the directory, update_entry should recover and
1594
self.assertIs(None, state.update_entry(entry, abspath='a'))
1595
self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
1597
def test_update_missing_symlink(self):
1598
if not osutils.has_symlinks():
1599
# PlatformDeficiency / TestSkipped
1600
raise TestSkipped("No symlink support")
1601
state, entry = self.get_state_with_a()
1602
packed_stat = self.create_and_test_symlink(state, entry)
1604
self.assertIs(None, state.update_entry(entry, abspath='a'))
1605
# And the record shouldn't be changed.
1606
self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
1609
def test_update_file_to_dir(self):
1610
"""If a file changes to a directory we return None for the sha.
1611
We also update the inventory record.
1613
state, entry = self.get_state_with_a()
1614
self.create_and_test_file(state, entry)
1616
self.create_and_test_dir(state, entry)
1618
def test_update_file_to_symlink(self):
1619
"""File becomes a symlink"""
1620
if not osutils.has_symlinks():
1621
# PlatformDeficiency / TestSkipped
1622
raise TestSkipped("No symlink support")
1623
state, entry = self.get_state_with_a()
1624
self.create_and_test_file(state, entry)
1626
self.create_and_test_symlink(state, entry)
1628
def test_update_dir_to_file(self):
1629
"""Directory becoming a file updates the entry."""
1630
state, entry = self.get_state_with_a()
1631
self.create_and_test_dir(state, entry)
1633
self.create_and_test_file(state, entry)
1635
def test_update_dir_to_symlink(self):
1636
"""Directory becomes a symlink"""
1637
if not osutils.has_symlinks():
1638
# PlatformDeficiency / TestSkipped
1639
raise TestSkipped("No symlink support")
1640
state, entry = self.get_state_with_a()
1641
self.create_and_test_dir(state, entry)
1643
self.create_and_test_symlink(state, entry)
1645
def test_update_symlink_to_file(self):
1646
"""Symlink becomes a file"""
1647
if not has_symlinks():
1648
raise TestSkipped("No symlink support")
1649
state, entry = self.get_state_with_a()
1650
self.create_and_test_symlink(state, entry)
1652
self.create_and_test_file(state, entry)
1654
def test_update_symlink_to_dir(self):
1655
"""Symlink becomes a directory"""
1656
if not has_symlinks():
1657
raise TestSkipped("No symlink support")
1658
state, entry = self.get_state_with_a()
1659
self.create_and_test_symlink(state, entry)
1661
self.create_and_test_dir(state, entry)
1663
def test__is_executable_win32(self):
1664
state, entry = self.get_state_with_a()
1665
self.build_tree(['a'])
1667
# Make sure we are using the win32 implementation of _is_executable
1668
state._is_executable = state._is_executable_win32
1670
# The file on disk is not executable, but we are marking it as though
1671
# it is. With _is_executable_win32 we ignore what is on disk.
1672
entry[1][0] = ('f', '', 0, True, dirstate.DirState.NULLSTAT)
1674
stat_value = os.lstat('a')
1675
packed_stat = dirstate.pack_stat(stat_value)
1677
state.adjust_time(-10) # Make sure everything is new
1678
# Make sure it wants to kkkkkkkk
1679
state.update_entry(entry, abspath='a', stat_value=stat_value)
1681
# The row is updated, but the executable bit stays set.
1682
digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
1683
self.assertEqual([('f', digest, 14, True, packed_stat)], entry[1])
1686
class TestPackStat(TestCaseWithTransport):
1699
1688
def assertPackStat(self, expected, stat_value):
1700
1689
"""Check the packed and serialized form of a stat value."""
1970
class TestBisectDirblock(TestCase):
1971
"""Test that bisect_dirblock() returns the expected values.
1973
bisect_dirblock is intended to work like bisect.bisect_left() except it
1974
knows it is working on dirblocks and that dirblocks are sorted by ('path',
1975
'to', 'foo') chunks rather than by raw 'path/to/foo'.
1978
def assertBisect(self, dirblocks, split_dirblocks, path, *args, **kwargs):
1979
"""Assert that bisect_split works like bisect_left on the split paths.
1981
:param dirblocks: A list of (path, [info]) pairs.
1982
:param split_dirblocks: A list of ((split, path), [info]) pairs.
1983
:param path: The path we are indexing.
1985
All other arguments will be passed along.
1987
bisect_split_idx = dirstate.bisect_dirblock(dirblocks, path,
1989
split_dirblock = (path.split('/'), [])
1990
bisect_left_idx = bisect.bisect_left(split_dirblocks, split_dirblock,
1992
self.assertEqual(bisect_left_idx, bisect_split_idx,
1993
'bisect_split disagreed. %s != %s'
1995
% (bisect_left_idx, bisect_split_idx, path)
1998
def paths_to_dirblocks(self, paths):
1999
"""Convert a list of paths into dirblock form.
2001
Also, ensure that the paths are in proper sorted order.
2003
dirblocks = [(path, []) for path in paths]
2004
split_dirblocks = [(path.split('/'), []) for path in paths]
2005
self.assertEqual(sorted(split_dirblocks), split_dirblocks)
2006
return dirblocks, split_dirblocks
2008
def test_simple(self):
2009
"""In the simple case it works just like bisect_left"""
2010
paths = ['', 'a', 'b', 'c', 'd']
2011
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
2013
self.assertBisect(dirblocks, split_dirblocks, path)
2014
self.assertBisect(dirblocks, split_dirblocks, '_')
2015
self.assertBisect(dirblocks, split_dirblocks, 'aa')
2016
self.assertBisect(dirblocks, split_dirblocks, 'bb')
2017
self.assertBisect(dirblocks, split_dirblocks, 'cc')
2018
self.assertBisect(dirblocks, split_dirblocks, 'dd')
2019
self.assertBisect(dirblocks, split_dirblocks, 'a/a')
2020
self.assertBisect(dirblocks, split_dirblocks, 'b/b')
2021
self.assertBisect(dirblocks, split_dirblocks, 'c/c')
2022
self.assertBisect(dirblocks, split_dirblocks, 'd/d')
2024
def test_involved(self):
2025
"""This is where bisect_left diverges slightly."""
2027
'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
2028
'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
2030
'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
2031
'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
2034
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
2036
self.assertBisect(dirblocks, split_dirblocks, path)
2038
def test_involved_cached(self):
2039
"""This is where bisect_left diverges slightly."""
2041
'a/a', 'a/a/a', 'a/a/z', 'a/a-a', 'a/a-z',
2042
'a/z', 'a/z/a', 'a/z/z', 'a/z-a', 'a/z-z',
2044
'z', 'z/a/a', 'z/a/z', 'z/a-a', 'z/a-z',
2045
'z/z', 'z/z/a', 'z/z/z', 'z/z-a', 'z/z-z',
2049
dirblocks, split_dirblocks = self.paths_to_dirblocks(paths)
2051
self.assertBisect(dirblocks, split_dirblocks, path, cache=cache)
1988
2054
class TestDirstateValidation(TestCaseWithDirState):
1990
2056
def test_validate_correct_dirstate(self):
2040
2106
state._validate)
2041
2107
self.assertContainsRe(str(e),
2042
2108
'file a-id is absent in row')
2045
class TestDirstateTreeReference(TestCaseWithDirState):
2047
def test_reference_revision_is_none(self):
2048
tree = self.make_branch_and_tree('tree', format='dirstate-with-subtree')
2049
subtree = self.make_branch_and_tree('tree/subtree',
2050
format='dirstate-with-subtree')
2051
subtree.set_root_id('subtree')
2052
tree.add_reference(subtree)
2054
state = dirstate.DirState.from_tree(tree, 'dirstate')
2055
key = ('', 'subtree', 'subtree')
2056
expected = ('', [(key,
2057
[('t', '', 0, False, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')])])
2060
self.assertEqual(expected, state._find_block(key))
2065
class TestDiscardMergeParents(TestCaseWithDirState):
2067
def test_discard_no_parents(self):
2068
# This should be a no-op
2069
state = self.create_empty_dirstate()
2070
self.addCleanup(state.unlock)
2071
state._discard_merge_parents()
2074
def test_discard_one_parent(self):
2076
packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
2077
root_entry_direntry = ('', '', 'a-root-value'), [
2078
('d', '', 0, False, packed_stat),
2079
('d', '', 0, False, packed_stat),
2082
dirblocks.append(('', [root_entry_direntry]))
2083
dirblocks.append(('', []))
2085
state = self.create_empty_dirstate()
2086
self.addCleanup(state.unlock)
2087
state._set_data(['parent-id'], dirblocks[:])
2090
state._discard_merge_parents()
2092
self.assertEqual(dirblocks, state._dirblocks)
2094
def test_discard_simple(self):
2096
packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
2097
root_entry_direntry = ('', '', 'a-root-value'), [
2098
('d', '', 0, False, packed_stat),
2099
('d', '', 0, False, packed_stat),
2100
('d', '', 0, False, packed_stat),
2102
expected_root_entry_direntry = ('', '', 'a-root-value'), [
2103
('d', '', 0, False, packed_stat),
2104
('d', '', 0, False, packed_stat),
2107
dirblocks.append(('', [root_entry_direntry]))
2108
dirblocks.append(('', []))
2110
state = self.create_empty_dirstate()
2111
self.addCleanup(state.unlock)
2112
state._set_data(['parent-id', 'merged-id'], dirblocks[:])
2115
# This should strip of the extra column
2116
state._discard_merge_parents()
2118
expected_dirblocks = [('', [expected_root_entry_direntry]), ('', [])]
2119
self.assertEqual(expected_dirblocks, state._dirblocks)
2121
def test_discard_absent(self):
2122
"""If entries are only in a merge, discard should remove the entries"""
2123
null_stat = dirstate.DirState.NULLSTAT
2124
present_dir = ('d', '', 0, False, null_stat)
2125
present_file = ('f', '', 0, False, null_stat)
2126
absent = dirstate.DirState.NULL_PARENT_DETAILS
2127
root_key = ('', '', 'a-root-value')
2128
file_in_root_key = ('', 'file-in-root', 'a-file-id')
2129
file_in_merged_key = ('', 'file-in-merged', 'b-file-id')
2130
dirblocks = [('', [(root_key, [present_dir, present_dir, present_dir])]),
2131
('', [(file_in_merged_key,
2132
[absent, absent, present_file]),
2134
[present_file, present_file, present_file]),
2138
state = self.create_empty_dirstate()
2139
self.addCleanup(state.unlock)
2140
state._set_data(['parent-id', 'merged-id'], dirblocks[:])
2143
exp_dirblocks = [('', [(root_key, [present_dir, present_dir])]),
2144
('', [(file_in_root_key,
2145
[present_file, present_file]),
2148
state._discard_merge_parents()
2150
self.assertEqual(exp_dirblocks, state._dirblocks)
2152
def test_discard_renamed(self):
2153
null_stat = dirstate.DirState.NULLSTAT
2154
present_dir = ('d', '', 0, False, null_stat)
2155
present_file = ('f', '', 0, False, null_stat)
2156
absent = dirstate.DirState.NULL_PARENT_DETAILS
2157
root_key = ('', '', 'a-root-value')
2158
file_in_root_key = ('', 'file-in-root', 'a-file-id')
2159
# Renamed relative to parent
2160
file_rename_s_key = ('', 'file-s', 'b-file-id')
2161
file_rename_t_key = ('', 'file-t', 'b-file-id')
2162
# And one that is renamed between the parents, but absent in this
2163
key_in_1 = ('', 'file-in-1', 'c-file-id')
2164
key_in_2 = ('', 'file-in-2', 'c-file-id')
2167
('', [(root_key, [present_dir, present_dir, present_dir])]),
2169
[absent, present_file, ('r', 'file-in-2', 'c-file-id')]),
2171
[absent, ('r', 'file-in-1', 'c-file-id'), present_file]),
2173
[present_file, present_file, present_file]),
2175
[('r', 'file-t', 'b-file-id'), absent, present_file]),
2177
[present_file, absent, ('r', 'file-s', 'b-file-id')]),
2181
('', [(root_key, [present_dir, present_dir])]),
2182
('', [(key_in_1, [absent, present_file]),
2183
(file_in_root_key, [present_file, present_file]),
2184
(file_rename_t_key, [present_file, absent]),
2187
state = self.create_empty_dirstate()
2188
self.addCleanup(state.unlock)
2189
state._set_data(['parent-id', 'merged-id'], dirblocks[:])
2192
state._discard_merge_parents()
2194
self.assertEqual(exp_dirblocks, state._dirblocks)
2196
def test_discard_all_subdir(self):
2197
null_stat = dirstate.DirState.NULLSTAT
2198
present_dir = ('d', '', 0, False, null_stat)
2199
present_file = ('f', '', 0, False, null_stat)
2200
absent = dirstate.DirState.NULL_PARENT_DETAILS
2201
root_key = ('', '', 'a-root-value')
2202
subdir_key = ('', 'sub', 'dir-id')
2203
child1_key = ('sub', 'child1', 'child1-id')
2204
child2_key = ('sub', 'child2', 'child2-id')
2205
child3_key = ('sub', 'child3', 'child3-id')
2208
('', [(root_key, [present_dir, present_dir, present_dir])]),
2209
('', [(subdir_key, [present_dir, present_dir, present_dir])]),
2210
('sub', [(child1_key, [absent, absent, present_file]),
2211
(child2_key, [absent, absent, present_file]),
2212
(child3_key, [absent, absent, present_file]),
2216
('', [(root_key, [present_dir, present_dir])]),
2217
('', [(subdir_key, [present_dir, present_dir])]),
2220
state = self.create_empty_dirstate()
2221
self.addCleanup(state.unlock)
2222
state._set_data(['parent-id', 'merged-id'], dirblocks[:])
2225
state._discard_merge_parents()
2227
self.assertEqual(exp_dirblocks, state._dirblocks)
2230
class Test_InvEntryToDetails(tests.TestCase):
2232
def assertDetails(self, expected, inv_entry):
2233
details = dirstate.DirState._inv_entry_to_details(inv_entry)
2234
self.assertEqual(expected, details)
2235
# details should always allow join() and always be a plain str when
2237
(minikind, fingerprint, size, executable, tree_data) = details
2238
self.assertIsInstance(minikind, str)
2239
self.assertIsInstance(fingerprint, str)
2240
self.assertIsInstance(tree_data, str)
2242
def test_unicode_symlink(self):
2243
inv_entry = inventory.InventoryLink('link-file-id',
2244
u'nam\N{Euro Sign}e',
2246
inv_entry.revision = 'link-revision-id'
2247
target = u'link-targ\N{Euro Sign}t'
2248
inv_entry.symlink_target = target
2249
self.assertDetails(('l', target.encode('UTF-8'), 0, False,
2250
'link-revision-id'), inv_entry)
2253
class TestSHA1Provider(tests.TestCaseInTempDir):
2255
def test_sha1provider_is_an_interface(self):
2256
p = dirstate.SHA1Provider()
2257
self.assertRaises(NotImplementedError, p.sha1, "foo")
2258
self.assertRaises(NotImplementedError, p.stat_and_sha1, "foo")
2260
def test_defaultsha1provider_sha1(self):
2261
text = 'test\r\nwith\nall\rpossible line endings\r\n'
2262
self.build_tree_contents([('foo', text)])
2263
expected_sha = osutils.sha_string(text)
2264
p = dirstate.DefaultSHA1Provider()
2265
self.assertEqual(expected_sha, p.sha1('foo'))
2267
def test_defaultsha1provider_stat_and_sha1(self):
2268
text = 'test\r\nwith\nall\rpossible line endings\r\n'
2269
self.build_tree_contents([('foo', text)])
2270
expected_sha = osutils.sha_string(text)
2271
p = dirstate.DefaultSHA1Provider()
2272
statvalue, sha1 = p.stat_and_sha1('foo')
2273
self.assertTrue(len(statvalue) >= 10)
2274
self.assertEqual(len(text), statvalue.st_size)
2275
self.assertEqual(expected_sha, sha1)