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
# Save platform specific info and reset it
68
cur_dir_reader = osutils._selected_dir_reader
71
osutils._selected_dir_reader = cur_dir_reader
72
self.addCleanup(restore)
74
osutils._selected_dir_reader = self._dir_reader_class()
53
76
def create_empty_dirstate(self):
54
77
"""Return a locked but empty dirstate"""
55
78
state = dirstate.DirState.initialize('dirstate')
396
419
(('', '', tree.get_root_id()), # common details
397
420
[('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
398
421
('d', '', 0, False, rev_id), # first parent details
399
('d', '', 0, False, rev_id2), # second parent details
422
('d', '', 0, False, rev_id), # second parent details
401
424
state = dirstate.DirState.from_tree(tree, 'dirstate')
402
425
self.check_state_with_reopen(expected_result, state)
477
500
(('', '', tree.get_root_id()), # common details
478
501
[('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
479
502
('d', '', 0, False, rev_id), # first parent details
480
('d', '', 0, False, rev_id2), # second parent details
503
('d', '', 0, False, rev_id), # second parent details
482
505
(('', 'a file', 'a-file-id'), # common
483
506
[('f', '', 0, False, dirstate.DirState.NULLSTAT), # current
745
768
# https://bugs.launchpad.net/bzr/+bug/146176
746
769
# set_state_from_inventory should preserve the stat and hash value for
747
770
# workingtree files that are not changed by the inventory.
749
772
tree = self.make_branch_and_tree('.')
750
773
# depends on the default format using dirstate...
751
774
tree.lock_write()
753
# make a dirstate with some valid hashcache data
776
# make a dirstate with some valid hashcache data
754
777
# file on disk, but that's not needed for this test
755
778
foo_contents = 'contents of foo'
756
779
self.build_tree_contents([('foo', foo_contents)])
776
799
(('', 'foo', 'foo-id',),
777
800
[('f', foo_sha, foo_size, False, foo_packed)]),
778
801
tree._dirstate._get_entry(0, 'foo-id'))
780
803
# extract the inventory, and add something to it
781
804
inv = tree._get_inventory()
782
805
# should see the file we poked in...
979
1000
[(('', '', root_id), [
980
1001
('d', '', 0, False, dirstate.DirState.NULLSTAT),
981
1002
('d', '', 0, False, revid1),
982
('d', '', 0, False, revid2)
1003
('d', '', 0, False, revid1)
984
1005
list(state._iter_entries()))
1013
1034
(('', '', root_id), [
1014
1035
('d', '', 0, False, dirstate.DirState.NULLSTAT),
1015
1036
('d', '', 0, False, revid1.encode('utf8')),
1016
('d', '', 0, False, revid2.encode('utf8'))
1037
('d', '', 0, False, revid1.encode('utf8'))
1018
1039
(('', 'a file', 'file-id'), [
1019
1040
('a', '', 0, False, ''),
1066
1087
state = dirstate.DirState.on_file('dirstate')
1067
1088
state.lock_read()
1069
self.assertEqual(expected_entries, list(state._iter_entries()))
1089
self.addCleanup(state.unlock)
1090
self.assertEqual(expected_entries, list(state._iter_entries()))
1073
1092
def test_add_path_to_unversioned_directory(self):
1074
1093
"""Adding a path to an unversioned directory should error.
1080
1099
self.build_tree(['unversioned/', 'unversioned/a file'])
1081
1100
state = dirstate.DirState.initialize('dirstate')
1083
self.assertRaises(errors.NotVersionedError, state.add,
1084
'unversioned/a file', 'a-file-id', 'file', None, None)
1101
self.addCleanup(state.unlock)
1102
self.assertRaises(errors.NotVersionedError, state.add,
1103
'unversioned/a file', 'a-file-id', 'file', None, None)
1088
1105
def test_add_directory_to_root_no_parents_all_data(self):
1089
1106
# The most trivial addition of a dir is when there are no parents and
1110
1127
state = dirstate.DirState.on_file('dirstate')
1111
1128
state.lock_read()
1129
self.addCleanup(state.unlock)
1112
1130
state._validate()
1114
self.assertEqual(expected_entries, list(state._iter_entries()))
1131
self.assertEqual(expected_entries, list(state._iter_entries()))
1118
def test_add_symlink_to_root_no_parents_all_data(self):
1133
def _test_add_symlink_to_root_no_parents_all_data(self, link_name, target):
1119
1134
# The most trivial addition of a symlink when there are no parents and
1120
1135
# its in the root and all data about the file is supplied
1121
1136
# 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')
1137
self.requireFeature(tests.SymlinkFeature)
1138
os.symlink(target, link_name)
1139
stat = os.lstat(link_name)
1125
1140
expected_entries = [
1126
1141
(('', '', 'TREE_ROOT'), [
1127
1142
('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
1144
(('', link_name.encode('UTF-8'), 'a link id'), [
1145
('l', target.encode('UTF-8'), stat[6],
1146
False, dirstate.pack_stat(stat)), # current tree
1133
1149
state = dirstate.DirState.initialize('dirstate')
1135
state.add('a link', 'a link id', 'symlink', stat, 'target')
1151
state.add(link_name, 'a link id', 'symlink', stat,
1152
target.encode('UTF-8'))
1136
1153
# having added it, it should be in the output of iter_entries.
1137
1154
self.assertEqual(expected_entries, list(state._iter_entries()))
1138
1155
# saving and reloading should not affect this.
1142
1159
state = dirstate.DirState.on_file('dirstate')
1143
1160
state.lock_read()
1145
self.assertEqual(expected_entries, list(state._iter_entries()))
1161
self.addCleanup(state.unlock)
1162
self.assertEqual(expected_entries, list(state._iter_entries()))
1164
def test_add_symlink_to_root_no_parents_all_data(self):
1165
self._test_add_symlink_to_root_no_parents_all_data('a link', 'target')
1167
def test_add_symlink_unicode_to_root_no_parents_all_data(self):
1168
self.requireFeature(tests.UnicodeFilenameFeature)
1169
self._test_add_symlink_to_root_no_parents_all_data(
1170
u'\N{Euro Sign}link', u'targ\N{Euro Sign}et')
1149
1172
def test_add_directory_and_child_no_parents_all_data(self):
1150
1173
# after adding a directory, we should be able to add children to it.
1176
1199
state = dirstate.DirState.on_file('dirstate')
1177
1200
state.lock_read()
1179
self.assertEqual(expected_entries, list(state._iter_entries()))
1201
self.addCleanup(state.unlock)
1202
self.assertEqual(expected_entries, list(state._iter_entries()))
1183
1204
def test_add_tree_reference(self):
1184
1205
# make a dirstate and add a tree reference
1199
1220
# now check we can read it back
1200
1221
state.lock_read()
1222
self.addCleanup(state.unlock)
1201
1223
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)
1224
entry2 = state._get_entry(0, 'subdir-id', 'subdir')
1225
self.assertEqual(entry, entry2)
1226
self.assertEqual(entry, expected_entry)
1227
# and lookup by id should work too
1228
entry2 = state._get_entry(0, fileid_utf8='subdir-id')
1229
self.assertEqual(entry, expected_entry)
1212
1231
def test_add_forbidden_names(self):
1213
1232
state = dirstate.DirState.initialize('dirstate')
1217
1236
self.assertRaises(errors.BzrError,
1218
1237
state.add, '..', 'ass-id', 'directory', None, None)
1239
def test_set_state_with_rename_b_a_bug_395556(self):
1240
# bug 395556 uncovered a bug where the dirstate ends up with a false
1241
# relocation record - in a tree with no parents there should be no
1242
# absent or relocated records. This then leads to further corruption
1243
# when a commit occurs, as the incorrect relocation gathers an
1244
# incorrect absent in tree 1, and future changes go to pot.
1245
tree1 = self.make_branch_and_tree('tree1')
1246
self.build_tree(['tree1/b'])
1249
tree1.add(['b'], ['b-id'])
1250
root_id = tree1.get_root_id()
1251
inv = tree1.inventory
1252
state = dirstate.DirState.initialize('dirstate')
1254
# Set the initial state with 'b'
1255
state.set_state_from_inventory(inv)
1256
inv.rename('b-id', root_id, 'a')
1257
# Set the new state with 'a', which currently corrupts.
1258
state.set_state_from_inventory(inv)
1259
expected_result1 = [('', '', root_id, 'd'),
1260
('', 'a', 'b-id', 'f'),
1263
for entry in state._iter_entries():
1264
values.append(entry[0] + entry[1][0][:1])
1265
self.assertEqual(expected_result1, values)
1221
1272
class TestGetLines(TestCaseWithDirState):
1455
1506
There is one parent tree, which has the same shape with the following variations:
1456
1507
b/g in the parent is gone.
1457
1508
b/h in the parent has a different id
1458
b/i is new in the parent
1509
b/i is new in the parent
1459
1510
c is renamed to b/j in the parent
1461
1512
:return: The dirstate, still write-locked.
1616
1667
class InstrumentedDirState(dirstate.DirState):
1617
1668
"""An DirState with instrumented sha1 functionality."""
1619
def __init__(self, path):
1620
super(InstrumentedDirState, self).__init__(path)
1670
def __init__(self, path, sha1_provider):
1671
super(InstrumentedDirState, self).__init__(path, sha1_provider)
1621
1672
self._time_offset = 0
1623
1674
# member is dynamically set in DirState.__init__ to turn on trace
1675
self._sha1_provider = sha1_provider
1624
1676
self._sha1_file = self._sha1_file_and_log
1626
1678
def _sha_cutoff_time(self):
1630
1682
def _sha1_file_and_log(self, abspath):
1631
1683
self._log.append(('sha1', abspath))
1632
return osutils.sha_file_by_name(abspath)
1684
return self._sha1_provider.sha1(abspath)
1634
1686
def _read_link(self, abspath, old_link):
1635
1687
self._log.append(('read_link', abspath, old_link))
1666
1718
self.st_ino = ino
1667
1719
self.st_mode = mode
1670
class TestPackStat(TestCaseWithTransport):
1723
return _FakeStat(st.st_size, st.st_mtime, st.st_ctime, st.st_dev,
1724
st.st_ino, st.st_mode)
1727
class TestPackStat(tests.TestCaseWithTransport):
1672
1729
def assertPackStat(self, expected, stat_value):
1673
1730
"""Check the packed and serialized form of a stat value."""
2200
2257
self.assertEqual(exp_dirblocks, state._dirblocks)
2203
class Test_InvEntryToDetails(TestCaseWithDirState):
2260
class Test_InvEntryToDetails(tests.TestCase):
2205
2262
def assertDetails(self, expected, inv_entry):
2206
2263
details = dirstate.DirState._inv_entry_to_details(inv_entry)
2213
2270
self.assertIsInstance(tree_data, str)
2215
2272
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',
2273
inv_entry = inventory.InventoryLink('link-file-id',
2274
u'nam\N{Euro Sign}e',
2219
2275
'link-parent-id')
2220
2276
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)
2277
target = u'link-targ\N{Euro Sign}t'
2278
inv_entry.symlink_target = target
2279
self.assertDetails(('l', target.encode('UTF-8'), 0, False,
2280
'link-revision-id'), inv_entry)
2283
class TestSHA1Provider(tests.TestCaseInTempDir):
2285
def test_sha1provider_is_an_interface(self):
2286
p = dirstate.SHA1Provider()
2287
self.assertRaises(NotImplementedError, p.sha1, "foo")
2288
self.assertRaises(NotImplementedError, p.stat_and_sha1, "foo")
2290
def test_defaultsha1provider_sha1(self):
2291
text = 'test\r\nwith\nall\rpossible line endings\r\n'
2292
self.build_tree_contents([('foo', text)])
2293
expected_sha = osutils.sha_string(text)
2294
p = dirstate.DefaultSHA1Provider()
2295
self.assertEqual(expected_sha, p.sha1('foo'))
2297
def test_defaultsha1provider_stat_and_sha1(self):
2298
text = 'test\r\nwith\nall\rpossible line endings\r\n'
2299
self.build_tree_contents([('foo', text)])
2300
expected_sha = osutils.sha_string(text)
2301
p = dirstate.DefaultSHA1Provider()
2302
statvalue, sha1 = p.stat_and_sha1('foo')
2303
self.assertTrue(len(statvalue) >= 10)
2304
self.assertEqual(len(text), statvalue.st_size)
2305
self.assertEqual(expected_sha, sha1)