~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_dirstate.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-09-01 08:02:42 UTC
  • mfrom: (5390.3.3 faster-revert-593560)
  • Revision ID: pqm@pqm.ubuntu.com-20100901080242-esg62ody4frwmy66
(spiv) Avoid repeatedly calling self.target.all_file_ids() in
 InterTree.iter_changes. (Andrew Bennetts)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
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
23
23
    dirstate,
24
24
    errors,
25
25
    inventory,
 
26
    memorytree,
26
27
    osutils,
27
28
    revision as _mod_revision,
 
29
    tests,
28
30
    )
29
 
from bzrlib.memorytree import MemoryTree
30
 
from bzrlib.tests import (
31
 
        SymlinkFeature,
32
 
        TestCase,
33
 
        TestCaseInTempDir,
34
 
        TestCaseWithTransport,
35
 
        )
 
31
from bzrlib.tests import test_osutils
36
32
 
37
33
 
38
34
# TODO:
48
44
# set_path_id  setting id when state is in memory modified
49
45
 
50
46
 
51
 
class TestCaseWithDirState(TestCaseWithTransport):
 
47
def load_tests(basic_tests, module, loader):
 
48
    suite = loader.suiteClass()
 
49
    dir_reader_tests, remaining_tests = tests.split_suite_by_condition(
 
50
        basic_tests, tests.condition_isinstance(TestCaseWithDirState))
 
51
    tests.multiply_tests(dir_reader_tests,
 
52
                         test_osutils.dir_reader_scenarios(), suite)
 
53
    suite.addTest(remaining_tests)
 
54
    return suite
 
55
 
 
56
 
 
57
class TestCaseWithDirState(tests.TestCaseWithTransport):
52
58
    """Helper functions for creating DirState objects with various content."""
53
59
 
 
60
    # Set by load_tests
 
61
    _dir_reader_class = None
 
62
    _native_to_unicode = None # Not used yet
 
63
 
 
64
    def setUp(self):
 
65
        tests.TestCaseWithTransport.setUp(self)
 
66
 
 
67
        self.overrideAttr(osutils,
 
68
                          '_selected_dir_reader', self._dir_reader_class())
 
69
 
54
70
    def create_empty_dirstate(self):
55
71
        """Return a locked but empty dirstate"""
56
72
        state = dirstate.DirState.initialize('dirstate')
397
413
            (('', '', tree.get_root_id()), # common details
398
414
             [('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
399
415
              ('d', '', 0, False, rev_id), # first parent details
400
 
              ('d', '', 0, False, rev_id2), # second parent details
 
416
              ('d', '', 0, False, rev_id), # second parent details
401
417
             ])])
402
418
        state = dirstate.DirState.from_tree(tree, 'dirstate')
403
419
        self.check_state_with_reopen(expected_result, state)
478
494
            (('', '', tree.get_root_id()), # common details
479
495
             [('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
480
496
              ('d', '', 0, False, rev_id), # first parent details
481
 
              ('d', '', 0, False, rev_id2), # second parent details
 
497
              ('d', '', 0, False, rev_id), # second parent details
482
498
             ]),
483
499
            (('', 'a file', 'a-file-id'), # common
484
500
             [('f', '', 0, False, dirstate.DirState.NULLSTAT), # current
714
730
 
715
731
class TestDirStateManipulations(TestCaseWithDirState):
716
732
 
 
733
    def test_update_minimal_updates_id_index(self):
 
734
        state = self.create_dirstate_with_root_and_subdir()
 
735
        self.addCleanup(state.unlock)
 
736
        id_index = state._get_id_index()
 
737
        self.assertEqual(['a-root-value', 'subdir-id'], sorted(id_index))
 
738
        state.add('file-name', 'file-id', 'file', None, '')
 
739
        self.assertEqual(['a-root-value', 'file-id', 'subdir-id'],
 
740
                         sorted(id_index))
 
741
        state.update_minimal(('', 'new-name', 'file-id'), 'f',
 
742
                             path_utf8='new-name')
 
743
        self.assertEqual(['a-root-value', 'file-id', 'subdir-id'],
 
744
                         sorted(id_index))
 
745
        self.assertEqual([('', 'new-name', 'file-id')],
 
746
                         sorted(id_index['file-id']))
 
747
        state._validate()
 
748
 
717
749
    def test_set_state_from_inventory_no_content_no_parents(self):
718
750
        # setting the current inventory is a slow but important api to support.
719
751
        tree1 = self.make_branch_and_memory_tree('tree1')
805
837
        finally:
806
838
            tree.unlock()
807
839
 
808
 
 
809
840
    def test_set_state_from_inventory_mixed_paths(self):
810
841
        tree1 = self.make_branch_and_tree('tree1')
811
842
        self.build_tree(['tree1/a/', 'tree1/a/b/', 'tree1/a-b/',
852
883
        state = dirstate.DirState.initialize('dirstate')
853
884
        try:
854
885
            # check precondition to be sure the state does change appropriately.
855
 
            self.assertEqual(
856
 
                [(('', '', 'TREE_ROOT'), [('d', '', 0, False,
857
 
                   'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')])],
858
 
                list(state._iter_entries()))
859
 
            state.set_path_id('', 'foobarbaz')
860
 
            expected_rows = [
861
 
                (('', '', 'foobarbaz'), [('d', '', 0, False,
862
 
                   'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')])]
 
886
            root_entry = (('', '', 'TREE_ROOT'), [('d', '', 0, False, 'x'*32)])
 
887
            self.assertEqual([root_entry], list(state._iter_entries()))
 
888
            self.assertEqual(root_entry, state._get_entry(0, path_utf8=''))
 
889
            self.assertEqual(root_entry,
 
890
                             state._get_entry(0, fileid_utf8='TREE_ROOT'))
 
891
            self.assertEqual((None, None),
 
892
                             state._get_entry(0, fileid_utf8='second-root-id'))
 
893
            state.set_path_id('', 'second-root-id')
 
894
            new_root_entry = (('', '', 'second-root-id'),
 
895
                              [('d', '', 0, False, 'x'*32)])
 
896
            expected_rows = [new_root_entry]
863
897
            self.assertEqual(expected_rows, list(state._iter_entries()))
 
898
            self.assertEqual(new_root_entry, state._get_entry(0, path_utf8=''))
 
899
            self.assertEqual(new_root_entry, 
 
900
                             state._get_entry(0, fileid_utf8='second-root-id'))
 
901
            self.assertEqual((None, None),
 
902
                             state._get_entry(0, fileid_utf8='TREE_ROOT'))
864
903
            # should work across save too
865
904
            state.save()
866
905
        finally:
884
923
        state._validate()
885
924
        try:
886
925
            state.set_parent_trees([('parent-revid', rt)], ghosts=[])
887
 
            state.set_path_id('', 'foobarbaz')
 
926
            root_entry = (('', '', 'TREE_ROOT'),
 
927
                          [('d', '', 0, False, 'x'*32),
 
928
                           ('d', '', 0, False, 'parent-revid')])
 
929
            self.assertEqual(root_entry, state._get_entry(0, path_utf8=''))
 
930
            self.assertEqual(root_entry,
 
931
                             state._get_entry(0, fileid_utf8='TREE_ROOT'))
 
932
            self.assertEqual((None, None),
 
933
                             state._get_entry(0, fileid_utf8='Asecond-root-id'))
 
934
            state.set_path_id('', 'Asecond-root-id')
888
935
            state._validate()
889
936
            # now see that it is what we expected
890
 
            expected_rows = [
891
 
                (('', '', 'TREE_ROOT'),
892
 
                    [('a', '', 0, False, ''),
893
 
                     ('d', '', 0, False, 'parent-revid'),
894
 
                     ]),
895
 
                (('', '', 'foobarbaz'),
896
 
                    [('d', '', 0, False, ''),
897
 
                     ('a', '', 0, False, ''),
898
 
                     ]),
899
 
                ]
 
937
            old_root_entry = (('', '', 'TREE_ROOT'),
 
938
                              [('a', '', 0, False, ''),
 
939
                               ('d', '', 0, False, 'parent-revid')])
 
940
            new_root_entry = (('', '', 'Asecond-root-id'),
 
941
                              [('d', '', 0, False, ''),
 
942
                               ('a', '', 0, False, '')])
 
943
            expected_rows = [new_root_entry, old_root_entry]
900
944
            state._validate()
901
945
            self.assertEqual(expected_rows, list(state._iter_entries()))
 
946
            self.assertEqual(new_root_entry, state._get_entry(0, path_utf8=''))
 
947
            self.assertEqual(old_root_entry, state._get_entry(1, path_utf8=''))
 
948
            self.assertEqual((None, None),
 
949
                             state._get_entry(0, fileid_utf8='TREE_ROOT'))
 
950
            self.assertEqual(old_root_entry,
 
951
                             state._get_entry(1, fileid_utf8='TREE_ROOT'))
 
952
            self.assertEqual(new_root_entry,
 
953
                             state._get_entry(0, fileid_utf8='Asecond-root-id'))
 
954
            self.assertEqual((None, None),
 
955
                             state._get_entry(1, fileid_utf8='Asecond-root-id'))
902
956
            # should work across save too
903
957
            state.save()
904
958
        finally:
920
974
        finally:
921
975
            state.unlock()
922
976
 
923
 
 
924
977
    def test_set_parent_trees_no_content(self):
925
978
        # set_parent_trees is a slow but important api to support.
926
979
        tree1 = self.make_branch_and_memory_tree('tree1')
931
984
        finally:
932
985
            tree1.unlock()
933
986
        branch2 = tree1.branch.bzrdir.clone('tree2').open_branch()
934
 
        tree2 = MemoryTree.create_on_branch(branch2)
 
987
        tree2 = memorytree.MemoryTree.create_on_branch(branch2)
935
988
        tree2.lock_write()
936
989
        try:
937
990
            revid2 = tree2.commit('foo')
980
1033
                [(('', '', root_id), [
981
1034
                  ('d', '', 0, False, dirstate.DirState.NULLSTAT),
982
1035
                  ('d', '', 0, False, revid1),
983
 
                  ('d', '', 0, False, revid2)
 
1036
                  ('d', '', 0, False, revid1)
984
1037
                  ])],
985
1038
                list(state._iter_entries()))
986
1039
        finally:
1001
1054
        finally:
1002
1055
            tree1.unlock()
1003
1056
        branch2 = tree1.branch.bzrdir.clone('tree2').open_branch()
1004
 
        tree2 = MemoryTree.create_on_branch(branch2)
 
1057
        tree2 = memorytree.MemoryTree.create_on_branch(branch2)
1005
1058
        tree2.lock_write()
1006
1059
        try:
1007
1060
            tree2.put_file_bytes_non_atomic('file-id', 'new file-content')
1014
1067
            (('', '', root_id), [
1015
1068
             ('d', '', 0, False, dirstate.DirState.NULLSTAT),
1016
1069
             ('d', '', 0, False, revid1.encode('utf8')),
1017
 
             ('d', '', 0, False, revid2.encode('utf8'))
 
1070
             ('d', '', 0, False, revid1.encode('utf8'))
1018
1071
             ]),
1019
1072
            (('', 'a file', 'file-id'), [
1020
1073
             ('a', '', 0, False, ''),
1066
1119
            state.unlock()
1067
1120
        state = dirstate.DirState.on_file('dirstate')
1068
1121
        state.lock_read()
1069
 
        try:
1070
 
            self.assertEqual(expected_entries, list(state._iter_entries()))
1071
 
        finally:
1072
 
            state.unlock()
 
1122
        self.addCleanup(state.unlock)
 
1123
        self.assertEqual(expected_entries, list(state._iter_entries()))
1073
1124
 
1074
1125
    def test_add_path_to_unversioned_directory(self):
1075
1126
        """Adding a path to an unversioned directory should error.
1080
1131
        """
1081
1132
        self.build_tree(['unversioned/', 'unversioned/a file'])
1082
1133
        state = dirstate.DirState.initialize('dirstate')
1083
 
        try:
1084
 
            self.assertRaises(errors.NotVersionedError, state.add,
1085
 
                'unversioned/a file', 'a-file-id', 'file', None, None)
1086
 
        finally:
1087
 
            state.unlock()
 
1134
        self.addCleanup(state.unlock)
 
1135
        self.assertRaises(errors.NotVersionedError, state.add,
 
1136
                          'unversioned/a file', 'a-file-id', 'file', None, None)
1088
1137
 
1089
1138
    def test_add_directory_to_root_no_parents_all_data(self):
1090
1139
        # The most trivial addition of a dir is when there are no parents and
1110
1159
            state.unlock()
1111
1160
        state = dirstate.DirState.on_file('dirstate')
1112
1161
        state.lock_read()
 
1162
        self.addCleanup(state.unlock)
1113
1163
        state._validate()
1114
 
        try:
1115
 
            self.assertEqual(expected_entries, list(state._iter_entries()))
1116
 
        finally:
1117
 
            state.unlock()
 
1164
        self.assertEqual(expected_entries, list(state._iter_entries()))
1118
1165
 
1119
 
    def test_add_symlink_to_root_no_parents_all_data(self):
 
1166
    def _test_add_symlink_to_root_no_parents_all_data(self, link_name, target):
1120
1167
        # The most trivial addition of a symlink when there are no parents and
1121
1168
        # its in the root and all data about the file is supplied
1122
1169
        # bzr doesn't support fake symlinks on windows, yet.
1123
 
        self.requireFeature(SymlinkFeature)
1124
 
        os.symlink('target', 'a link')
1125
 
        stat = os.lstat('a link')
 
1170
        self.requireFeature(tests.SymlinkFeature)
 
1171
        os.symlink(target, link_name)
 
1172
        stat = os.lstat(link_name)
1126
1173
        expected_entries = [
1127
1174
            (('', '', 'TREE_ROOT'), [
1128
1175
             ('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
1129
1176
             ]),
1130
 
            (('', 'a link', 'a link id'), [
1131
 
             ('l', 'target', 6, False, dirstate.pack_stat(stat)), # current tree
 
1177
            (('', link_name.encode('UTF-8'), 'a link id'), [
 
1178
             ('l', target.encode('UTF-8'), stat[6],
 
1179
              False, dirstate.pack_stat(stat)), # current tree
1132
1180
             ]),
1133
1181
            ]
1134
1182
        state = dirstate.DirState.initialize('dirstate')
1135
1183
        try:
1136
 
            state.add('a link', 'a link id', 'symlink', stat, 'target')
 
1184
            state.add(link_name, 'a link id', 'symlink', stat,
 
1185
                      target.encode('UTF-8'))
1137
1186
            # having added it, it should be in the output of iter_entries.
1138
1187
            self.assertEqual(expected_entries, list(state._iter_entries()))
1139
1188
            # saving and reloading should not affect this.
1142
1191
            state.unlock()
1143
1192
        state = dirstate.DirState.on_file('dirstate')
1144
1193
        state.lock_read()
1145
 
        try:
1146
 
            self.assertEqual(expected_entries, list(state._iter_entries()))
1147
 
        finally:
1148
 
            state.unlock()
 
1194
        self.addCleanup(state.unlock)
 
1195
        self.assertEqual(expected_entries, list(state._iter_entries()))
 
1196
 
 
1197
    def test_add_symlink_to_root_no_parents_all_data(self):
 
1198
        self._test_add_symlink_to_root_no_parents_all_data('a link', 'target')
 
1199
 
 
1200
    def test_add_symlink_unicode_to_root_no_parents_all_data(self):
 
1201
        self.requireFeature(tests.UnicodeFilenameFeature)
 
1202
        self._test_add_symlink_to_root_no_parents_all_data(
 
1203
            u'\N{Euro Sign}link', u'targ\N{Euro Sign}et')
1149
1204
 
1150
1205
    def test_add_directory_and_child_no_parents_all_data(self):
1151
1206
        # after adding a directory, we should be able to add children to it.
1176
1231
            state.unlock()
1177
1232
        state = dirstate.DirState.on_file('dirstate')
1178
1233
        state.lock_read()
1179
 
        try:
1180
 
            self.assertEqual(expected_entries, list(state._iter_entries()))
1181
 
        finally:
1182
 
            state.unlock()
 
1234
        self.addCleanup(state.unlock)
 
1235
        self.assertEqual(expected_entries, list(state._iter_entries()))
1183
1236
 
1184
1237
    def test_add_tree_reference(self):
1185
1238
        # make a dirstate and add a tree reference
1199
1252
            state.unlock()
1200
1253
        # now check we can read it back
1201
1254
        state.lock_read()
 
1255
        self.addCleanup(state.unlock)
1202
1256
        state._validate()
1203
 
        try:
1204
 
            entry2 = state._get_entry(0, 'subdir-id', 'subdir')
1205
 
            self.assertEqual(entry, entry2)
1206
 
            self.assertEqual(entry, expected_entry)
1207
 
            # and lookup by id should work too
1208
 
            entry2 = state._get_entry(0, fileid_utf8='subdir-id')
1209
 
            self.assertEqual(entry, expected_entry)
1210
 
        finally:
1211
 
            state.unlock()
 
1257
        entry2 = state._get_entry(0, 'subdir-id', 'subdir')
 
1258
        self.assertEqual(entry, entry2)
 
1259
        self.assertEqual(entry, expected_entry)
 
1260
        # and lookup by id should work too
 
1261
        entry2 = state._get_entry(0, fileid_utf8='subdir-id')
 
1262
        self.assertEqual(entry, expected_entry)
1212
1263
 
1213
1264
    def test_add_forbidden_names(self):
1214
1265
        state = dirstate.DirState.initialize('dirstate')
1218
1269
        self.assertRaises(errors.BzrError,
1219
1270
            state.add, '..', 'ass-id', 'directory', None, None)
1220
1271
 
 
1272
    def test_set_state_with_rename_b_a_bug_395556(self):
 
1273
        # bug 395556 uncovered a bug where the dirstate ends up with a false
 
1274
        # relocation record - in a tree with no parents there should be no
 
1275
        # absent or relocated records. This then leads to further corruption
 
1276
        # when a commit occurs, as the incorrect relocation gathers an
 
1277
        # incorrect absent in tree 1, and future changes go to pot.
 
1278
        tree1 = self.make_branch_and_tree('tree1')
 
1279
        self.build_tree(['tree1/b'])
 
1280
        tree1.lock_write()
 
1281
        try:
 
1282
            tree1.add(['b'], ['b-id'])
 
1283
            root_id = tree1.get_root_id()
 
1284
            inv = tree1.inventory
 
1285
            state = dirstate.DirState.initialize('dirstate')
 
1286
            try:
 
1287
                # Set the initial state with 'b'
 
1288
                state.set_state_from_inventory(inv)
 
1289
                inv.rename('b-id', root_id, 'a')
 
1290
                # Set the new state with 'a', which currently corrupts.
 
1291
                state.set_state_from_inventory(inv)
 
1292
                expected_result1 = [('', '', root_id, 'd'),
 
1293
                                    ('', 'a', 'b-id', 'f'),
 
1294
                                   ]
 
1295
                values = []
 
1296
                for entry in state._iter_entries():
 
1297
                    values.append(entry[0] + entry[1][0][:1])
 
1298
                self.assertEqual(expected_result1, values)
 
1299
            finally:
 
1300
                state.unlock()
 
1301
        finally:
 
1302
            tree1.unlock()
 
1303
 
1221
1304
 
1222
1305
class TestGetLines(TestCaseWithDirState):
1223
1306
 
1552
1635
            list(state._iter_child_entries(1, '')))
1553
1636
 
1554
1637
 
1555
 
class TestDirstateSortOrder(TestCaseWithTransport):
 
1638
class TestDirstateSortOrder(tests.TestCaseWithTransport):
1556
1639
    """Test that DirState adds entries in the right order."""
1557
1640
 
1558
1641
    def test_add_sorting(self):
1674
1757
            st.st_ino, st.st_mode)
1675
1758
 
1676
1759
 
1677
 
class TestPackStat(TestCaseWithTransport):
 
1760
class TestPackStat(tests.TestCaseWithTransport):
1678
1761
 
1679
1762
    def assertPackStat(self, expected, stat_value):
1680
1763
        """Check the packed and serialized form of a stat value."""
2207
2290
        self.assertEqual(exp_dirblocks, state._dirblocks)
2208
2291
 
2209
2292
 
2210
 
class Test_InvEntryToDetails(TestCaseWithDirState):
 
2293
class Test_InvEntryToDetails(tests.TestCase):
2211
2294
 
2212
2295
    def assertDetails(self, expected, inv_entry):
2213
2296
        details = dirstate.DirState._inv_entry_to_details(inv_entry)
2220
2303
        self.assertIsInstance(tree_data, str)
2221
2304
 
2222
2305
    def test_unicode_symlink(self):
2223
 
        # In general, the code base doesn't support a target that contains
2224
 
        # non-ascii characters. So we just assert tha
2225
 
        inv_entry = inventory.InventoryLink('link-file-id', 'name',
 
2306
        inv_entry = inventory.InventoryLink('link-file-id',
 
2307
                                            u'nam\N{Euro Sign}e',
2226
2308
                                            'link-parent-id')
2227
2309
        inv_entry.revision = 'link-revision-id'
2228
 
        inv_entry.symlink_target = u'link-target'
2229
 
        details = self.assertDetails(('l', 'link-target', 0, False,
2230
 
                                      'link-revision-id'), inv_entry)
2231
 
 
2232
 
 
2233
 
class TestSHA1Provider(TestCaseInTempDir):
 
2310
        target = u'link-targ\N{Euro Sign}t'
 
2311
        inv_entry.symlink_target = target
 
2312
        self.assertDetails(('l', target.encode('UTF-8'), 0, False,
 
2313
                            'link-revision-id'), inv_entry)
 
2314
 
 
2315
 
 
2316
class TestSHA1Provider(tests.TestCaseInTempDir):
2234
2317
 
2235
2318
    def test_sha1provider_is_an_interface(self):
2236
2319
        p = dirstate.SHA1Provider()