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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
"""Tests of the dirstate functionality being built for WorkingTreeFormat4."""
47
44
# set_path_id setting id when state is in memory modified
50
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)
57
class TestCaseWithDirState(tests.TestCaseWithTransport):
51
58
"""Helper functions for creating DirState objects with various content."""
61
_dir_reader_class = None
62
_native_to_unicode = None # Not used yet
65
tests.TestCaseWithTransport.setUp(self)
67
self.overrideAttr(osutils,
68
'_selected_dir_reader', self._dir_reader_class())
53
70
def create_empty_dirstate(self):
54
71
"""Return a locked but empty dirstate"""
55
72
state = dirstate.DirState.initialize('dirstate')
396
413
(('', '', tree.get_root_id()), # common details
397
414
[('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
398
415
('d', '', 0, False, rev_id), # first parent details
399
('d', '', 0, False, rev_id2), # second parent details
416
('d', '', 0, False, rev_id), # second parent details
401
418
state = dirstate.DirState.from_tree(tree, 'dirstate')
402
419
self.check_state_with_reopen(expected_result, state)
477
494
(('', '', tree.get_root_id()), # common details
478
495
[('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
479
496
('d', '', 0, False, rev_id), # first parent details
480
('d', '', 0, False, rev_id2), # second parent details
497
('d', '', 0, False, rev_id), # second parent details
482
499
(('', 'a file', 'a-file-id'), # common
483
500
[('f', '', 0, False, dirstate.DirState.NULLSTAT), # current
745
762
# https://bugs.launchpad.net/bzr/+bug/146176
746
763
# set_state_from_inventory should preserve the stat and hash value for
747
764
# workingtree files that are not changed by the inventory.
749
766
tree = self.make_branch_and_tree('.')
750
767
# depends on the default format using dirstate...
751
768
tree.lock_write()
753
# make a dirstate with some valid hashcache data
770
# make a dirstate with some valid hashcache data
754
771
# file on disk, but that's not needed for this test
755
772
foo_contents = 'contents of foo'
756
773
self.build_tree_contents([('foo', foo_contents)])
851
867
state = dirstate.DirState.initialize('dirstate')
853
869
# check precondition to be sure the state does change appropriately.
855
[(('', '', 'TREE_ROOT'), [('d', '', 0, False,
856
'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')])],
857
list(state._iter_entries()))
858
state.set_path_id('', 'foobarbaz')
860
(('', '', 'foobarbaz'), [('d', '', 0, False,
861
'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')])]
870
root_entry = (('', '', 'TREE_ROOT'), [('d', '', 0, False, 'x'*32)])
871
self.assertEqual([root_entry], list(state._iter_entries()))
872
self.assertEqual(root_entry, state._get_entry(0, path_utf8=''))
873
self.assertEqual(root_entry,
874
state._get_entry(0, fileid_utf8='TREE_ROOT'))
875
self.assertEqual((None, None),
876
state._get_entry(0, fileid_utf8='second-root-id'))
877
state.set_path_id('', 'second-root-id')
878
new_root_entry = (('', '', 'second-root-id'),
879
[('d', '', 0, False, 'x'*32)])
880
expected_rows = [new_root_entry]
862
881
self.assertEqual(expected_rows, list(state._iter_entries()))
882
self.assertEqual(new_root_entry, state._get_entry(0, path_utf8=''))
883
self.assertEqual(new_root_entry,
884
state._get_entry(0, fileid_utf8='second-root-id'))
885
self.assertEqual((None, None),
886
state._get_entry(0, fileid_utf8='TREE_ROOT'))
863
887
# should work across save too
883
907
state._validate()
885
909
state.set_parent_trees([('parent-revid', rt)], ghosts=[])
886
state.set_path_id('', 'foobarbaz')
910
root_entry = (('', '', 'TREE_ROOT'),
911
[('d', '', 0, False, 'x'*32),
912
('d', '', 0, False, 'parent-revid')])
913
self.assertEqual(root_entry, state._get_entry(0, path_utf8=''))
914
self.assertEqual(root_entry,
915
state._get_entry(0, fileid_utf8='TREE_ROOT'))
916
self.assertEqual((None, None),
917
state._get_entry(0, fileid_utf8='Asecond-root-id'))
918
state.set_path_id('', 'Asecond-root-id')
887
919
state._validate()
888
920
# now see that it is what we expected
890
(('', '', 'TREE_ROOT'),
891
[('a', '', 0, False, ''),
892
('d', '', 0, False, 'parent-revid'),
894
(('', '', 'foobarbaz'),
895
[('d', '', 0, False, ''),
896
('a', '', 0, False, ''),
921
old_root_entry = (('', '', 'TREE_ROOT'),
922
[('a', '', 0, False, ''),
923
('d', '', 0, False, 'parent-revid')])
924
new_root_entry = (('', '', 'Asecond-root-id'),
925
[('d', '', 0, False, ''),
926
('a', '', 0, False, '')])
927
expected_rows = [new_root_entry, old_root_entry]
899
928
state._validate()
900
929
self.assertEqual(expected_rows, list(state._iter_entries()))
930
self.assertEqual(new_root_entry, state._get_entry(0, path_utf8=''))
931
self.assertEqual(old_root_entry, state._get_entry(1, path_utf8=''))
932
self.assertEqual((None, None),
933
state._get_entry(0, fileid_utf8='TREE_ROOT'))
934
self.assertEqual(old_root_entry,
935
state._get_entry(1, fileid_utf8='TREE_ROOT'))
936
self.assertEqual(new_root_entry,
937
state._get_entry(0, fileid_utf8='Asecond-root-id'))
938
self.assertEqual((None, None),
939
state._get_entry(1, fileid_utf8='Asecond-root-id'))
901
940
# should work across save too
979
1017
[(('', '', root_id), [
980
1018
('d', '', 0, False, dirstate.DirState.NULLSTAT),
981
1019
('d', '', 0, False, revid1),
982
('d', '', 0, False, revid2)
1020
('d', '', 0, False, revid1)
984
1022
list(state._iter_entries()))
1013
1051
(('', '', root_id), [
1014
1052
('d', '', 0, False, dirstate.DirState.NULLSTAT),
1015
1053
('d', '', 0, False, revid1.encode('utf8')),
1016
('d', '', 0, False, revid2.encode('utf8'))
1054
('d', '', 0, False, revid1.encode('utf8'))
1018
1056
(('', 'a file', 'file-id'), [
1019
1057
('a', '', 0, False, ''),
1080
1116
self.build_tree(['unversioned/', 'unversioned/a file'])
1081
1117
state = dirstate.DirState.initialize('dirstate')
1083
self.assertRaises(errors.NotVersionedError, state.add,
1084
'unversioned/a file', 'a-file-id', 'file', None, None)
1118
self.addCleanup(state.unlock)
1119
self.assertRaises(errors.NotVersionedError, state.add,
1120
'unversioned/a file', 'a-file-id', 'file', None, None)
1088
1122
def test_add_directory_to_root_no_parents_all_data(self):
1089
1123
# The most trivial addition of a dir is when there are no parents and
1110
1144
state = dirstate.DirState.on_file('dirstate')
1111
1145
state.lock_read()
1146
self.addCleanup(state.unlock)
1112
1147
state._validate()
1114
self.assertEqual(expected_entries, list(state._iter_entries()))
1148
self.assertEqual(expected_entries, list(state._iter_entries()))
1118
def test_add_symlink_to_root_no_parents_all_data(self):
1150
def _test_add_symlink_to_root_no_parents_all_data(self, link_name, target):
1119
1151
# The most trivial addition of a symlink when there are no parents and
1120
1152
# its in the root and all data about the file is supplied
1121
1153
# bzr doesn't support fake symlinks on windows, yet.
1122
self.requireFeature(SymlinkFeature)
1123
os.symlink('target', 'a link')
1124
stat = os.lstat('a link')
1154
self.requireFeature(tests.SymlinkFeature)
1155
os.symlink(target, link_name)
1156
stat = os.lstat(link_name)
1125
1157
expected_entries = [
1126
1158
(('', '', 'TREE_ROOT'), [
1127
1159
('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
1129
(('', 'a link', 'a link id'), [
1130
('l', 'target', 6, False, dirstate.pack_stat(stat)), # current tree
1161
(('', link_name.encode('UTF-8'), 'a link id'), [
1162
('l', target.encode('UTF-8'), stat[6],
1163
False, dirstate.pack_stat(stat)), # current tree
1133
1166
state = dirstate.DirState.initialize('dirstate')
1135
state.add('a link', 'a link id', 'symlink', stat, 'target')
1168
state.add(link_name, 'a link id', 'symlink', stat,
1169
target.encode('UTF-8'))
1136
1170
# having added it, it should be in the output of iter_entries.
1137
1171
self.assertEqual(expected_entries, list(state._iter_entries()))
1138
1172
# saving and reloading should not affect this.
1142
1176
state = dirstate.DirState.on_file('dirstate')
1143
1177
state.lock_read()
1145
self.assertEqual(expected_entries, list(state._iter_entries()))
1178
self.addCleanup(state.unlock)
1179
self.assertEqual(expected_entries, list(state._iter_entries()))
1181
def test_add_symlink_to_root_no_parents_all_data(self):
1182
self._test_add_symlink_to_root_no_parents_all_data('a link', 'target')
1184
def test_add_symlink_unicode_to_root_no_parents_all_data(self):
1185
self.requireFeature(tests.UnicodeFilenameFeature)
1186
self._test_add_symlink_to_root_no_parents_all_data(
1187
u'\N{Euro Sign}link', u'targ\N{Euro Sign}et')
1149
1189
def test_add_directory_and_child_no_parents_all_data(self):
1150
1190
# after adding a directory, we should be able to add children to it.
1199
1237
# now check we can read it back
1200
1238
state.lock_read()
1239
self.addCleanup(state.unlock)
1201
1240
state._validate()
1203
entry2 = state._get_entry(0, 'subdir-id', 'subdir')
1204
self.assertEqual(entry, entry2)
1205
self.assertEqual(entry, expected_entry)
1206
# and lookup by id should work too
1207
entry2 = state._get_entry(0, fileid_utf8='subdir-id')
1208
self.assertEqual(entry, expected_entry)
1241
entry2 = state._get_entry(0, 'subdir-id', 'subdir')
1242
self.assertEqual(entry, entry2)
1243
self.assertEqual(entry, expected_entry)
1244
# and lookup by id should work too
1245
entry2 = state._get_entry(0, fileid_utf8='subdir-id')
1246
self.assertEqual(entry, expected_entry)
1212
1248
def test_add_forbidden_names(self):
1213
1249
state = dirstate.DirState.initialize('dirstate')
1217
1253
self.assertRaises(errors.BzrError,
1218
1254
state.add, '..', 'ass-id', 'directory', None, None)
1256
def test_set_state_with_rename_b_a_bug_395556(self):
1257
# bug 395556 uncovered a bug where the dirstate ends up with a false
1258
# relocation record - in a tree with no parents there should be no
1259
# absent or relocated records. This then leads to further corruption
1260
# when a commit occurs, as the incorrect relocation gathers an
1261
# incorrect absent in tree 1, and future changes go to pot.
1262
tree1 = self.make_branch_and_tree('tree1')
1263
self.build_tree(['tree1/b'])
1266
tree1.add(['b'], ['b-id'])
1267
root_id = tree1.get_root_id()
1268
inv = tree1.inventory
1269
state = dirstate.DirState.initialize('dirstate')
1271
# Set the initial state with 'b'
1272
state.set_state_from_inventory(inv)
1273
inv.rename('b-id', root_id, 'a')
1274
# Set the new state with 'a', which currently corrupts.
1275
state.set_state_from_inventory(inv)
1276
expected_result1 = [('', '', root_id, 'd'),
1277
('', 'a', 'b-id', 'f'),
1280
for entry in state._iter_entries():
1281
values.append(entry[0] + entry[1][0][:1])
1282
self.assertEqual(expected_result1, values)
1221
1289
class TestGetLines(TestCaseWithDirState):
1616
1684
class InstrumentedDirState(dirstate.DirState):
1617
1685
"""An DirState with instrumented sha1 functionality."""
1619
def __init__(self, path):
1620
super(InstrumentedDirState, self).__init__(path)
1687
def __init__(self, path, sha1_provider):
1688
super(InstrumentedDirState, self).__init__(path, sha1_provider)
1621
1689
self._time_offset = 0
1623
1691
# member is dynamically set in DirState.__init__ to turn on trace
1692
self._sha1_provider = sha1_provider
1624
1693
self._sha1_file = self._sha1_file_and_log
1626
1695
def _sha_cutoff_time(self):
2213
2287
self.assertIsInstance(tree_data, str)
2215
2289
def test_unicode_symlink(self):
2216
# In general, the code base doesn't support a target that contains
2217
# non-ascii characters. So we just assert tha
2218
inv_entry = inventory.InventoryLink('link-file-id', 'name',
2290
inv_entry = inventory.InventoryLink('link-file-id',
2291
u'nam\N{Euro Sign}e',
2219
2292
'link-parent-id')
2220
2293
inv_entry.revision = 'link-revision-id'
2221
inv_entry.symlink_target = u'link-target'
2222
details = self.assertDetails(('l', 'link-target', 0, False,
2223
'link-revision-id'), inv_entry)
2294
target = u'link-targ\N{Euro Sign}t'
2295
inv_entry.symlink_target = target
2296
self.assertDetails(('l', target.encode('UTF-8'), 0, False,
2297
'link-revision-id'), inv_entry)
2300
class TestSHA1Provider(tests.TestCaseInTempDir):
2302
def test_sha1provider_is_an_interface(self):
2303
p = dirstate.SHA1Provider()
2304
self.assertRaises(NotImplementedError, p.sha1, "foo")
2305
self.assertRaises(NotImplementedError, p.stat_and_sha1, "foo")
2307
def test_defaultsha1provider_sha1(self):
2308
text = 'test\r\nwith\nall\rpossible line endings\r\n'
2309
self.build_tree_contents([('foo', text)])
2310
expected_sha = osutils.sha_string(text)
2311
p = dirstate.DefaultSHA1Provider()
2312
self.assertEqual(expected_sha, p.sha1('foo'))
2314
def test_defaultsha1provider_stat_and_sha1(self):
2315
text = 'test\r\nwith\nall\rpossible line endings\r\n'
2316
self.build_tree_contents([('foo', text)])
2317
expected_sha = osutils.sha_string(text)
2318
p = dirstate.DefaultSHA1Provider()
2319
statvalue, sha1 = p.stat_and_sha1('foo')
2320
self.assertTrue(len(statvalue) >= 10)
2321
self.assertEqual(len(text), statvalue.st_size)
2322
self.assertEqual(expected_sha, sha1)