~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_dirstate.py

  • Committer: Martin Pool
  • Date: 2007-10-12 08:00:07 UTC
  • mto: This revision was merged to the branch mainline in revision 2913.
  • Revision ID: mbp@sourcefrog.net-20071012080007-vf80woayyom8s8e1
Rename update_to_one_parent_via_delta to more wieldy update_basis_by_delta

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
from bzrlib import (
24
24
    dirstate,
25
25
    errors,
26
 
    inventory,
27
26
    osutils,
28
27
    )
29
28
from bzrlib.memorytree import MemoryTree
 
29
from bzrlib.osutils import has_symlinks
30
30
from bzrlib.tests import (
31
 
        SymlinkFeature,
32
31
        TestCase,
33
32
        TestCaseWithTransport,
 
33
        TestSkipped,
34
34
        )
35
35
 
36
36
 
163
163
        """
164
164
        # The state should already be write locked, since we just had to do
165
165
        # some operation to get here.
166
 
        self.assertTrue(state._lock_token is not None)
 
166
        assert state._lock_token is not None
167
167
        try:
168
168
            self.assertEqual(expected_result[0],  state.get_parent_ids())
169
169
            # there should be no ghosts in this tree.
360
360
        # There are no files on disk and no parents
361
361
        tree = self.make_branch_and_tree('tree')
362
362
        expected_result = ([], [
363
 
            (('', '', tree.get_root_id()), # common details
 
363
            (('', '', tree.path2id('')), # common details
364
364
             [('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
365
365
             ])])
366
366
        state = dirstate.DirState.from_tree(tree, 'dirstate')
373
373
        rev_id = tree.commit('first post').encode('utf8')
374
374
        root_stat_pack = dirstate.pack_stat(os.stat(tree.basedir))
375
375
        expected_result = ([rev_id], [
376
 
            (('', '', tree.get_root_id()), # common details
 
376
            (('', '', tree.path2id('')), # common details
377
377
             [('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
378
378
              ('d', '', 0, False, rev_id), # first parent details
379
379
             ])])
393
393
        rev_id2 = tree2.commit('second post', allow_pointless=True)
394
394
        tree.merge_from_branch(tree2.branch)
395
395
        expected_result = ([rev_id, rev_id2], [
396
 
            (('', '', tree.get_root_id()), # common details
 
396
            (('', '', tree.path2id('')), # common details
397
397
             [('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
398
398
              ('d', '', 0, False, rev_id), # first parent details
399
399
              ('d', '', 0, False, rev_id2), # second parent details
412
412
        tree = self.make_branch_and_tree('tree')
413
413
        self.build_tree(['tree/unknown'])
414
414
        expected_result = ([], [
415
 
            (('', '', tree.get_root_id()), # common details
 
415
            (('', '', tree.path2id('')), # common details
416
416
             [('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
417
417
             ])])
418
418
        state = dirstate.DirState.from_tree(tree, 'dirstate')
421
421
    def get_tree_with_a_file(self):
422
422
        tree = self.make_branch_and_tree('tree')
423
423
        self.build_tree(['tree/a file'])
424
 
        tree.add('a file', 'a-file-id')
 
424
        tree.add('a file', 'a file id')
425
425
        return tree
426
426
 
427
427
    def test_non_empty_no_parents_to_dirstate(self):
429
429
        # There are files on disk and no parents
430
430
        tree = self.get_tree_with_a_file()
431
431
        expected_result = ([], [
432
 
            (('', '', tree.get_root_id()), # common details
 
432
            (('', '', tree.path2id('')), # common details
433
433
             [('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
434
434
             ]),
435
 
            (('', 'a file', 'a-file-id'), # common
 
435
            (('', 'a file', 'a file id'), # common
436
436
             [('f', '', 0, False, dirstate.DirState.NULLSTAT), # current
437
437
             ]),
438
438
            ])
447
447
        # and length:
448
448
        self.build_tree_contents([('tree/a file', 'new content\n')])
449
449
        expected_result = ([rev_id], [
450
 
            (('', '', tree.get_root_id()), # common details
 
450
            (('', '', tree.path2id('')), # common details
451
451
             [('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
452
452
              ('d', '', 0, False, rev_id), # first parent details
453
453
             ]),
454
 
            (('', 'a file', 'a-file-id'), # common
 
454
            (('', 'a file', 'a file id'), # common
455
455
             [('f', '', 0, False, dirstate.DirState.NULLSTAT), # current
456
456
              ('f', 'c3ed76e4bfd45ff1763ca206055bca8e9fc28aa8', 24, False,
457
457
               rev_id), # first parent
474
474
        # and length again, giving us three distinct values:
475
475
        self.build_tree_contents([('tree/a file', 'new content\n')])
476
476
        expected_result = ([rev_id, rev_id2], [
477
 
            (('', '', tree.get_root_id()), # common details
 
477
            (('', '', tree.path2id('')), # common details
478
478
             [('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
479
479
              ('d', '', 0, False, rev_id), # first parent details
480
480
              ('d', '', 0, False, rev_id2), # second parent details
481
481
             ]),
482
 
            (('', 'a file', 'a-file-id'), # common
 
482
            (('', 'a file', 'a file id'), # common
483
483
             [('f', '', 0, False, dirstate.DirState.NULLSTAT), # current
484
484
              ('f', 'c3ed76e4bfd45ff1763ca206055bca8e9fc28aa8', 24, False,
485
485
               rev_id), # first parent
526
526
        # get a state object
527
527
        # no parents, default tree content
528
528
        expected_result = ([], [
529
 
            (('', '', tree.get_root_id()), # common details
 
529
            (('', '', tree.path2id('')), # common details
530
530
             # current tree details, but new from_tree skips statting, it
531
531
             # uses set_state_from_inventory, and thus depends on the
532
532
             # inventory state.
647
647
        finally:
648
648
            state.unlock()
649
649
 
650
 
    def test_save_refuses_if_changes_aborted(self):
651
 
        self.build_tree(['a-file', 'a-dir/'])
652
 
        state = dirstate.DirState.initialize('dirstate')
653
 
        try:
654
 
            # No stat and no sha1 sum.
655
 
            state.add('a-file', 'a-file-id', 'file', None, '')
656
 
            state.save()
657
 
        finally:
658
 
            state.unlock()
659
 
 
660
 
        # The dirstate should include TREE_ROOT and 'a-file' and nothing else
661
 
        expected_blocks = [
662
 
            ('', [(('', '', 'TREE_ROOT'),
663
 
                   [('d', '', 0, False, dirstate.DirState.NULLSTAT)])]),
664
 
            ('', [(('', 'a-file', 'a-file-id'),
665
 
                   [('f', '', 0, False, dirstate.DirState.NULLSTAT)])]),
666
 
        ]
667
 
 
668
 
        state = dirstate.DirState.on_file('dirstate')
669
 
        state.lock_write()
670
 
        try:
671
 
            state._read_dirblocks_if_needed()
672
 
            self.assertEqual(expected_blocks, state._dirblocks)
673
 
 
674
 
            # Now modify the state, but mark it as inconsistent
675
 
            state.add('a-dir', 'a-dir-id', 'directory', None, '')
676
 
            state._changes_aborted = True
677
 
            state.save()
678
 
        finally:
679
 
            state.unlock()
680
 
 
681
 
        state = dirstate.DirState.on_file('dirstate')
682
 
        state.lock_read()
683
 
        try:
684
 
            state._read_dirblocks_if_needed()
685
 
            self.assertEqual(expected_blocks, state._dirblocks)
686
 
        finally:
687
 
            state.unlock()
688
 
 
689
650
 
690
651
class TestDirStateInitialize(TestCaseWithDirState):
691
652
 
718
679
        try:
719
680
            tree1.add('')
720
681
            revid1 = tree1.commit('foo').encode('utf8')
721
 
            root_id = tree1.get_root_id()
 
682
            root_id = tree1.inventory.root.file_id
722
683
            inv = tree1.inventory
723
684
        finally:
724
685
            tree1.unlock()
932
893
        tree2.lock_write()
933
894
        try:
934
895
            revid2 = tree2.commit('foo')
935
 
            root_id = tree2.get_root_id()
 
896
            root_id = tree2.inventory.root.file_id
936
897
        finally:
937
898
            tree2.unlock()
938
899
        state = dirstate.DirState.initialize('dirstate')
1002
963
        try:
1003
964
            tree2.put_file_bytes_non_atomic('file-id', 'new file-content')
1004
965
            revid2 = tree2.commit('foo')
1005
 
            root_id = tree2.get_root_id()
 
966
            root_id = tree2.inventory.root.file_id
1006
967
        finally:
1007
968
            tree2.unlock()
1008
969
        # check the layout in memory
1048
1009
            (('', '', 'TREE_ROOT'), [
1049
1010
             ('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
1050
1011
             ]),
1051
 
            (('', 'a file', 'a-file-id'), [
 
1012
            (('', 'a file', 'a file id'), [
1052
1013
             ('f', '1'*20, 19, False, dirstate.pack_stat(stat)), # current tree
1053
1014
             ]),
1054
1015
            ]
1055
1016
        try:
1056
 
            state.add('a file', 'a-file-id', 'file', stat, '1'*20)
 
1017
            state.add('a file', 'a file id', 'file', stat, '1'*20)
1057
1018
            # having added it, it should be in the output of iter_entries.
1058
1019
            self.assertEqual(expected_entries, list(state._iter_entries()))
1059
1020
            # saving and reloading should not affect this.
1078
1039
        state = dirstate.DirState.initialize('dirstate')
1079
1040
        try:
1080
1041
            self.assertRaises(errors.NotVersionedError, state.add,
1081
 
                'unversioned/a file', 'a-file-id', 'file', None, None)
 
1042
                'unversioned/a file', 'a file id', 'file', None, None)
1082
1043
        finally:
1083
1044
            state.unlock()
1084
1045
 
1116
1077
        # The most trivial addition of a symlink when there are no parents and
1117
1078
        # its in the root and all data about the file is supplied
1118
1079
        # bzr doesn't support fake symlinks on windows, yet.
1119
 
        self.requireFeature(SymlinkFeature)
 
1080
        if not has_symlinks():
 
1081
            raise TestSkipped("No symlink support")
1120
1082
        os.symlink('target', 'a link')
1121
1083
        stat = os.lstat('a link')
1122
1084
        expected_entries = [
1155
1117
            (('', 'a dir', 'a dir id'), [
1156
1118
             ('d', '', 0, False, dirstate.pack_stat(dirstat)), # current tree
1157
1119
             ]),
1158
 
            (('a dir', 'a file', 'a-file-id'), [
 
1120
            (('a dir', 'a file', 'a file id'), [
1159
1121
             ('f', '1'*20, 25, False,
1160
1122
              dirstate.pack_stat(filestat)), # current tree details
1161
1123
             ]),
1163
1125
        state = dirstate.DirState.initialize('dirstate')
1164
1126
        try:
1165
1127
            state.add('a dir', 'a dir id', 'directory', dirstat, None)
1166
 
            state.add('a dir/a file', 'a-file-id', 'file', filestat, '1'*20)
 
1128
            state.add('a dir/a file', 'a file id', 'file', filestat, '1'*20)
1167
1129
            # added it, it should be in the output of iter_entries.
1168
1130
            self.assertEqual(expected_entries, list(state._iter_entries()))
1169
1131
            # saving and reloading should not affect this.
1432
1394
            state.unlock()
1433
1395
 
1434
1396
 
1435
 
class TestIterChildEntries(TestCaseWithDirState):
1436
 
 
1437
 
    def create_dirstate_with_two_trees(self):
1438
 
        """This dirstate contains multiple files and directories.
1439
 
 
1440
 
         /        a-root-value
1441
 
         a/       a-dir
1442
 
         b/       b-dir
1443
 
         c        c-file
1444
 
         d        d-file
1445
 
         a/e/     e-dir
1446
 
         a/f      f-file
1447
 
         b/g      g-file
1448
 
         b/h\xc3\xa5  h-\xc3\xa5-file  #This is u'\xe5' encoded into utf-8
1449
 
 
1450
 
        Notice that a/e is an empty directory.
1451
 
 
1452
 
        There is one parent tree, which has the same shape with the following variations:
1453
 
        b/g in the parent is gone.
1454
 
        b/h in the parent has a different id
1455
 
        b/i is new in the parent 
1456
 
        c is renamed to b/j in the parent
1457
 
 
1458
 
        :return: The dirstate, still write-locked.
1459
 
        """
1460
 
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
1461
 
        null_sha = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
1462
 
        NULL_PARENT_DETAILS = dirstate.DirState.NULL_PARENT_DETAILS
1463
 
        root_entry = ('', '', 'a-root-value'), [
1464
 
            ('d', '', 0, False, packed_stat),
1465
 
            ('d', '', 0, False, 'parent-revid'),
1466
 
            ]
1467
 
        a_entry = ('', 'a', 'a-dir'), [
1468
 
            ('d', '', 0, False, packed_stat),
1469
 
            ('d', '', 0, False, 'parent-revid'),
1470
 
            ]
1471
 
        b_entry = ('', 'b', 'b-dir'), [
1472
 
            ('d', '', 0, False, packed_stat),
1473
 
            ('d', '', 0, False, 'parent-revid'),
1474
 
            ]
1475
 
        c_entry = ('', 'c', 'c-file'), [
1476
 
            ('f', null_sha, 10, False, packed_stat),
1477
 
            ('r', 'b/j', 0, False, ''),
1478
 
            ]
1479
 
        d_entry = ('', 'd', 'd-file'), [
1480
 
            ('f', null_sha, 20, False, packed_stat),
1481
 
            ('f', 'd', 20, False, 'parent-revid'),
1482
 
            ]
1483
 
        e_entry = ('a', 'e', 'e-dir'), [
1484
 
            ('d', '', 0, False, packed_stat),
1485
 
            ('d', '', 0, False, 'parent-revid'),
1486
 
            ]
1487
 
        f_entry = ('a', 'f', 'f-file'), [
1488
 
            ('f', null_sha, 30, False, packed_stat),
1489
 
            ('f', 'f', 20, False, 'parent-revid'),
1490
 
            ]
1491
 
        g_entry = ('b', 'g', 'g-file'), [
1492
 
            ('f', null_sha, 30, False, packed_stat),
1493
 
            NULL_PARENT_DETAILS,
1494
 
            ]
1495
 
        h_entry1 = ('b', 'h\xc3\xa5', 'h-\xc3\xa5-file1'), [
1496
 
            ('f', null_sha, 40, False, packed_stat),
1497
 
            NULL_PARENT_DETAILS,
1498
 
            ]
1499
 
        h_entry2 = ('b', 'h\xc3\xa5', 'h-\xc3\xa5-file2'), [
1500
 
            NULL_PARENT_DETAILS,
1501
 
            ('f', 'h', 20, False, 'parent-revid'),
1502
 
            ]
1503
 
        i_entry = ('b', 'i', 'i-file'), [
1504
 
            NULL_PARENT_DETAILS,
1505
 
            ('f', 'h', 20, False, 'parent-revid'),
1506
 
            ]
1507
 
        j_entry = ('b', 'j', 'c-file'), [
1508
 
            ('r', 'c', 0, False, ''),
1509
 
            ('f', 'j', 20, False, 'parent-revid'),
1510
 
            ]
1511
 
        dirblocks = []
1512
 
        dirblocks.append(('', [root_entry]))
1513
 
        dirblocks.append(('', [a_entry, b_entry, c_entry, d_entry]))
1514
 
        dirblocks.append(('a', [e_entry, f_entry]))
1515
 
        dirblocks.append(('b', [g_entry, h_entry1, h_entry2, i_entry, j_entry]))
1516
 
        state = dirstate.DirState.initialize('dirstate')
1517
 
        state._validate()
1518
 
        try:
1519
 
            state._set_data(['parent'], dirblocks)
1520
 
        except:
1521
 
            state.unlock()
1522
 
            raise
1523
 
        return state, dirblocks
1524
 
 
1525
 
    def test_iter_children_b(self):
1526
 
        state, dirblocks = self.create_dirstate_with_two_trees()
1527
 
        self.addCleanup(state.unlock)
1528
 
        expected_result = []
1529
 
        expected_result.append(dirblocks[3][1][2]) # h2
1530
 
        expected_result.append(dirblocks[3][1][3]) # i
1531
 
        expected_result.append(dirblocks[3][1][4]) # j
1532
 
        self.assertEqual(expected_result,
1533
 
            list(state._iter_child_entries(1, 'b')))
1534
 
 
1535
 
    def test_iter_child_root(self):
1536
 
        state, dirblocks = self.create_dirstate_with_two_trees()
1537
 
        self.addCleanup(state.unlock)
1538
 
        expected_result = []
1539
 
        expected_result.append(dirblocks[1][1][0]) # a
1540
 
        expected_result.append(dirblocks[1][1][1]) # b
1541
 
        expected_result.append(dirblocks[1][1][3]) # d
1542
 
        expected_result.append(dirblocks[2][1][0]) # e
1543
 
        expected_result.append(dirblocks[2][1][1]) # f
1544
 
        expected_result.append(dirblocks[3][1][2]) # h2
1545
 
        expected_result.append(dirblocks[3][1][3]) # i
1546
 
        expected_result.append(dirblocks[3][1][4]) # j
1547
 
        self.assertEqual(expected_result,
1548
 
            list(state._iter_child_entries(1, '')))
1549
 
 
1550
 
 
1551
1397
class TestDirstateSortOrder(TestCaseWithTransport):
1552
1398
    """Test that DirState adds entries in the right order."""
1553
1399
 
1752
1598
 
1753
1599
    def test_update_entry_symlink(self):
1754
1600
        """Update entry should read symlinks."""
1755
 
        self.requireFeature(SymlinkFeature)
 
1601
        if not osutils.has_symlinks():
 
1602
            # PlatformDeficiency / TestSkipped
 
1603
            raise TestSkipped("No symlink support")
1756
1604
        state, entry = self.get_state_with_a()
1757
1605
        state.save()
1758
1606
        self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
1909
1757
 
1910
1758
    def test_update_file_to_symlink(self):
1911
1759
        """File becomes a symlink"""
1912
 
        self.requireFeature(SymlinkFeature)
 
1760
        if not osutils.has_symlinks():
 
1761
            # PlatformDeficiency / TestSkipped
 
1762
            raise TestSkipped("No symlink support")
1913
1763
        state, entry = self.get_state_with_a()
1914
1764
        # The file sha1 won't be cached unless the file is old
1915
1765
        state.adjust_time(+10)
1928
1778
 
1929
1779
    def test_update_dir_to_symlink(self):
1930
1780
        """Directory becomes a symlink"""
1931
 
        self.requireFeature(SymlinkFeature)
 
1781
        if not osutils.has_symlinks():
 
1782
            # PlatformDeficiency / TestSkipped
 
1783
            raise TestSkipped("No symlink support")
1932
1784
        state, entry = self.get_state_with_a()
1933
1785
        # The symlink target won't be cached if it isn't old
1934
1786
        state.adjust_time(+10)
1938
1790
 
1939
1791
    def test_update_symlink_to_file(self):
1940
1792
        """Symlink becomes a file"""
1941
 
        self.requireFeature(SymlinkFeature)
 
1793
        if not has_symlinks():
 
1794
            raise TestSkipped("No symlink support")
1942
1795
        state, entry = self.get_state_with_a()
1943
1796
        # The symlink and file info won't be cached unless old
1944
1797
        state.adjust_time(+10)
1948
1801
 
1949
1802
    def test_update_symlink_to_dir(self):
1950
1803
        """Symlink becomes a directory"""
1951
 
        self.requireFeature(SymlinkFeature)
 
1804
        if not has_symlinks():
 
1805
            raise TestSkipped("No symlink support")
1952
1806
        state, entry = self.get_state_with_a()
1953
1807
        # The symlink target won't be cached if it isn't old
1954
1808
        state.adjust_time(+10)
2055
1909
        # the end it would still be fairly arbitrary, and we don't want the
2056
1910
        # extra overhead if we can avoid it. So sort everything to make sure
2057
1911
        # equality is true
2058
 
        self.assertEqual(len(map_keys), len(paths))
 
1912
        assert len(map_keys) == len(paths)
2059
1913
        expected = {}
2060
1914
        for path, keys in zip(paths, map_keys):
2061
1915
            if keys is None:
2080
1934
        :param paths: A list of directories
2081
1935
        """
2082
1936
        result = state._bisect_dirblocks(paths)
2083
 
        self.assertEqual(len(map_keys), len(paths))
 
1937
        assert len(map_keys) == len(paths)
 
1938
 
2084
1939
        expected = {}
2085
1940
        for path, keys in zip(paths, map_keys):
2086
1941
            if keys is None:
2350
2205
            self.assertEqual(expected, state._find_block(key))
2351
2206
        finally:
2352
2207
            state.unlock()
2353
 
 
2354
 
 
2355
 
class TestDiscardMergeParents(TestCaseWithDirState):
2356
 
 
2357
 
    def test_discard_no_parents(self):
2358
 
        # This should be a no-op
2359
 
        state = self.create_empty_dirstate()
2360
 
        self.addCleanup(state.unlock)
2361
 
        state._discard_merge_parents()
2362
 
        state._validate()
2363
 
 
2364
 
    def test_discard_one_parent(self):
2365
 
        # No-op
2366
 
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
2367
 
        root_entry_direntry = ('', '', 'a-root-value'), [
2368
 
            ('d', '', 0, False, packed_stat),
2369
 
            ('d', '', 0, False, packed_stat),
2370
 
            ]
2371
 
        dirblocks = []
2372
 
        dirblocks.append(('', [root_entry_direntry]))
2373
 
        dirblocks.append(('', []))
2374
 
 
2375
 
        state = self.create_empty_dirstate()
2376
 
        self.addCleanup(state.unlock)
2377
 
        state._set_data(['parent-id'], dirblocks[:])
2378
 
        state._validate()
2379
 
 
2380
 
        state._discard_merge_parents()
2381
 
        state._validate()
2382
 
        self.assertEqual(dirblocks, state._dirblocks)
2383
 
 
2384
 
    def test_discard_simple(self):
2385
 
        # No-op
2386
 
        packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
2387
 
        root_entry_direntry = ('', '', 'a-root-value'), [
2388
 
            ('d', '', 0, False, packed_stat),
2389
 
            ('d', '', 0, False, packed_stat),
2390
 
            ('d', '', 0, False, packed_stat),
2391
 
            ]
2392
 
        expected_root_entry_direntry = ('', '', 'a-root-value'), [
2393
 
            ('d', '', 0, False, packed_stat),
2394
 
            ('d', '', 0, False, packed_stat),
2395
 
            ]
2396
 
        dirblocks = []
2397
 
        dirblocks.append(('', [root_entry_direntry]))
2398
 
        dirblocks.append(('', []))
2399
 
 
2400
 
        state = self.create_empty_dirstate()
2401
 
        self.addCleanup(state.unlock)
2402
 
        state._set_data(['parent-id', 'merged-id'], dirblocks[:])
2403
 
        state._validate()
2404
 
 
2405
 
        # This should strip of the extra column
2406
 
        state._discard_merge_parents()
2407
 
        state._validate()
2408
 
        expected_dirblocks = [('', [expected_root_entry_direntry]), ('', [])]
2409
 
        self.assertEqual(expected_dirblocks, state._dirblocks)
2410
 
 
2411
 
    def test_discard_absent(self):
2412
 
        """If entries are only in a merge, discard should remove the entries"""
2413
 
        null_stat = dirstate.DirState.NULLSTAT
2414
 
        present_dir = ('d', '', 0, False, null_stat)
2415
 
        present_file = ('f', '', 0, False, null_stat)
2416
 
        absent = dirstate.DirState.NULL_PARENT_DETAILS
2417
 
        root_key = ('', '', 'a-root-value')
2418
 
        file_in_root_key = ('', 'file-in-root', 'a-file-id')
2419
 
        file_in_merged_key = ('', 'file-in-merged', 'b-file-id')
2420
 
        dirblocks = [('', [(root_key, [present_dir, present_dir, present_dir])]),
2421
 
                     ('', [(file_in_merged_key,
2422
 
                            [absent, absent, present_file]),
2423
 
                           (file_in_root_key,
2424
 
                            [present_file, present_file, present_file]),
2425
 
                          ]),
2426
 
                    ]
2427
 
 
2428
 
        state = self.create_empty_dirstate()
2429
 
        self.addCleanup(state.unlock)
2430
 
        state._set_data(['parent-id', 'merged-id'], dirblocks[:])
2431
 
        state._validate()
2432
 
 
2433
 
        exp_dirblocks = [('', [(root_key, [present_dir, present_dir])]),
2434
 
                         ('', [(file_in_root_key,
2435
 
                                [present_file, present_file]),
2436
 
                              ]),
2437
 
                        ]
2438
 
        state._discard_merge_parents()
2439
 
        state._validate()
2440
 
        self.assertEqual(exp_dirblocks, state._dirblocks)
2441
 
 
2442
 
    def test_discard_renamed(self):
2443
 
        null_stat = dirstate.DirState.NULLSTAT
2444
 
        present_dir = ('d', '', 0, False, null_stat)
2445
 
        present_file = ('f', '', 0, False, null_stat)
2446
 
        absent = dirstate.DirState.NULL_PARENT_DETAILS
2447
 
        root_key = ('', '', 'a-root-value')
2448
 
        file_in_root_key = ('', 'file-in-root', 'a-file-id')
2449
 
        # Renamed relative to parent
2450
 
        file_rename_s_key = ('', 'file-s', 'b-file-id')
2451
 
        file_rename_t_key = ('', 'file-t', 'b-file-id')
2452
 
        # And one that is renamed between the parents, but absent in this
2453
 
        key_in_1 = ('', 'file-in-1', 'c-file-id')
2454
 
        key_in_2 = ('', 'file-in-2', 'c-file-id')
2455
 
 
2456
 
        dirblocks = [
2457
 
            ('', [(root_key, [present_dir, present_dir, present_dir])]),
2458
 
            ('', [(key_in_1,
2459
 
                   [absent, present_file, ('r', 'file-in-2', 'c-file-id')]),
2460
 
                  (key_in_2,
2461
 
                   [absent, ('r', 'file-in-1', 'c-file-id'), present_file]),
2462
 
                  (file_in_root_key,
2463
 
                   [present_file, present_file, present_file]),
2464
 
                  (file_rename_s_key,
2465
 
                   [('r', 'file-t', 'b-file-id'), absent, present_file]),
2466
 
                  (file_rename_t_key,
2467
 
                   [present_file, absent, ('r', 'file-s', 'b-file-id')]),
2468
 
                 ]),
2469
 
        ]
2470
 
        exp_dirblocks = [
2471
 
            ('', [(root_key, [present_dir, present_dir])]),
2472
 
            ('', [(key_in_1, [absent, present_file]),
2473
 
                  (file_in_root_key, [present_file, present_file]),
2474
 
                  (file_rename_t_key, [present_file, absent]),
2475
 
                 ]),
2476
 
        ]
2477
 
        state = self.create_empty_dirstate()
2478
 
        self.addCleanup(state.unlock)
2479
 
        state._set_data(['parent-id', 'merged-id'], dirblocks[:])
2480
 
        state._validate()
2481
 
 
2482
 
        state._discard_merge_parents()
2483
 
        state._validate()
2484
 
        self.assertEqual(exp_dirblocks, state._dirblocks)
2485
 
 
2486
 
    def test_discard_all_subdir(self):
2487
 
        null_stat = dirstate.DirState.NULLSTAT
2488
 
        present_dir = ('d', '', 0, False, null_stat)
2489
 
        present_file = ('f', '', 0, False, null_stat)
2490
 
        absent = dirstate.DirState.NULL_PARENT_DETAILS
2491
 
        root_key = ('', '', 'a-root-value')
2492
 
        subdir_key = ('', 'sub', 'dir-id')
2493
 
        child1_key = ('sub', 'child1', 'child1-id')
2494
 
        child2_key = ('sub', 'child2', 'child2-id')
2495
 
        child3_key = ('sub', 'child3', 'child3-id')
2496
 
 
2497
 
        dirblocks = [
2498
 
            ('', [(root_key, [present_dir, present_dir, present_dir])]),
2499
 
            ('', [(subdir_key, [present_dir, present_dir, present_dir])]),
2500
 
            ('sub', [(child1_key, [absent, absent, present_file]),
2501
 
                     (child2_key, [absent, absent, present_file]),
2502
 
                     (child3_key, [absent, absent, present_file]),
2503
 
                    ]),
2504
 
        ]
2505
 
        exp_dirblocks = [
2506
 
            ('', [(root_key, [present_dir, present_dir])]),
2507
 
            ('', [(subdir_key, [present_dir, present_dir])]),
2508
 
            ('sub', []),
2509
 
        ]
2510
 
        state = self.create_empty_dirstate()
2511
 
        self.addCleanup(state.unlock)
2512
 
        state._set_data(['parent-id', 'merged-id'], dirblocks[:])
2513
 
        state._validate()
2514
 
 
2515
 
        state._discard_merge_parents()
2516
 
        state._validate()
2517
 
        self.assertEqual(exp_dirblocks, state._dirblocks)
2518
 
 
2519
 
 
2520
 
class Test_InvEntryToDetails(TestCaseWithDirState):
2521
 
 
2522
 
    def assertDetails(self, expected, inv_entry):
2523
 
        details = dirstate.DirState._inv_entry_to_details(inv_entry)
2524
 
        self.assertEqual(expected, details)
2525
 
        # details should always allow join() and always be a plain str when
2526
 
        # finished
2527
 
        (minikind, fingerprint, size, executable, tree_data) = details
2528
 
        self.assertIsInstance(minikind, str)
2529
 
        self.assertIsInstance(fingerprint, str)
2530
 
        self.assertIsInstance(tree_data, str)
2531
 
 
2532
 
    def test_unicode_symlink(self):
2533
 
        # In general, the code base doesn't support a target that contains
2534
 
        # non-ascii characters. So we just assert tha 
2535
 
        inv_entry = inventory.InventoryLink('link-file-id', 'name',
2536
 
                                            'link-parent-id')
2537
 
        inv_entry.revision = 'link-revision-id'
2538
 
        inv_entry.symlink_target = u'link-target'
2539
 
        details = self.assertDetails(('l', 'link-target', 0, False,
2540
 
                                      'link-revision-id'), inv_entry)