2 from __future__ import absolute_import, print_function
3 from errno import ELOOP, ENOTDIR
4 from os import environ, symlink
5 from stat import S_IFDIR
7 from time import localtime, strftime
11 from bup import git, vfs
12 from bup.metadata import Metadata
13 from bup.repo import LocalRepo
14 from bup.test.vfs import tree_dict
15 from buptest import ex, exo, no_lingering_errors, test_tempdir
18 bup_tmp = os.path.realpath('../../../t/tmp')
19 bup_path = top_dir + '/bup'
20 start_dir = os.getcwd()
22 ## The clear_cache() calls below are to make sure that the test starts
23 ## from a known state since at the moment the cache entry for a given
24 ## item (like a commit) can change. For example, its meta value might
25 ## be promoted from a mode to a Metadata instance once the tree it
26 ## refers to is traversed.
30 with no_lingering_errors():
31 with test_tempdir('bup-tvfs-') as tmpdir:
33 bup_dir = tmpdir + '/bup'
34 environ['GIT_DIR'] = bup_dir
35 environ['BUP_DIR'] = bup_dir
37 data_path = tmpdir + '/src'
39 save_time_str = strftime('%Y-%m-%d-%H%M%S', localtime(save_time))
41 os.mkdir(data_path + '/dir')
42 with open(data_path + '/file', 'w+') as tmpfile:
43 print('canary', file=tmpfile)
44 symlink('file', data_path + '/file-symlink')
45 symlink('dir', data_path + '/dir-symlink')
46 symlink('not-there', data_path + '/bad-symlink')
47 ex((bup_path, 'init'))
48 ex((bup_path, 'index', '-v', data_path))
49 ex((bup_path, 'save', '-d', str(save_time), '-tvvn', 'test',
50 '--strip', data_path))
51 ex((bup_path, 'tag', 'test-tag', 'test'))
54 tip_hash = exo(('git', 'show-ref', 'refs/heads/test'))[0]
55 tip_oidx = tip_hash.strip().split()[0]
56 tip_oid = tip_oidx.decode('hex')
57 tip_tree_oidx = exo(('git', 'log', '--pretty=%T', '-n1',
59 tip_tree_oid = tip_tree_oidx.decode('hex')
60 tip_tree = tree_dict(repo, tip_tree_oid)
61 test_revlist_w_meta = vfs.RevList(meta=tip_tree['.'].meta,
63 expected_latest_item = vfs.Commit(meta=S_IFDIR | 0o755,
66 expected_latest_item_w_meta = vfs.Commit(meta=tip_tree['.'].meta,
69 expected_latest_link = vfs.FakeLink(meta=vfs.default_symlink_mode,
71 expected_test_tag_item = expected_latest_item
75 res = resolve(repo, '/')
77 wvpasseq((('', vfs._root),), res)
78 ignore, root_item = res[0]
79 root_content = frozenset(vfs.contents(repo, root_item))
80 wvpasseq(frozenset([('.', root_item),
82 ('test', test_revlist_w_meta)]),
84 for path in ('//', '/.', '/./', '/..', '/../',
85 '/test/latest/dir/../../..',
86 '/test/latest/dir/../../../',
87 '/test/latest/dir/../../../.',
88 '/test/latest/dir/../../..//',
89 '/test//latest/dir/../../..',
90 '/test/./latest/dir/../../..',
91 '/test/././latest/dir/../../..',
92 '/test/.//./latest/dir/../../..',
93 '/test//.//.//latest/dir/../../..'
94 '/test//./latest/dir/../../..'):
95 wvstart('resolve: ' + path)
97 res = resolve(repo, path)
98 wvpasseq((('', vfs._root),), res)
100 wvstart('resolve: /.tag')
102 res = resolve(repo, '/.tag')
103 wvpasseq(2, len(res))
104 wvpasseq((('', vfs._root), ('.tag', vfs._tags)),
106 ignore, tag_item = res[1]
107 tag_content = frozenset(vfs.contents(repo, tag_item))
108 wvpasseq(frozenset([('.', tag_item),
109 ('test-tag', expected_test_tag_item)]),
112 wvstart('resolve: /test')
114 res = resolve(repo, '/test')
115 wvpasseq(2, len(res))
116 wvpasseq((('', vfs._root), ('test', test_revlist_w_meta)), res)
117 ignore, test_item = res[1]
118 test_content = frozenset(vfs.contents(repo, test_item))
119 # latest has metadata here due to caching
120 wvpasseq(frozenset([('.', test_revlist_w_meta),
121 (save_time_str, expected_latest_item_w_meta),
122 ('latest', expected_latest_link)]),
125 wvstart('resolve: /test/latest')
127 res = resolve(repo, '/test/latest')
128 wvpasseq(3, len(res))
129 expected_latest_item_w_meta = vfs.Commit(meta=tip_tree['.'].meta,
132 expected = (('', vfs._root),
133 ('test', test_revlist_w_meta),
134 (save_time_str, expected_latest_item_w_meta))
135 wvpasseq(expected, res)
136 ignore, latest_item = res[2]
137 latest_content = frozenset(vfs.contents(repo, latest_item))
138 expected = frozenset((x.name, vfs.Item(oid=x.oid, meta=x.meta))
139 for x in (tip_tree[name]
146 wvpasseq(expected, latest_content)
148 wvstart('resolve: /test/latest/file')
150 res = resolve(repo, '/test/latest/file')
151 wvpasseq(4, len(res))
152 expected_file_item_w_meta = vfs.Item(meta=tip_tree['file'].meta,
153 oid=tip_tree['file'].oid)
154 expected = (('', vfs._root),
155 ('test', test_revlist_w_meta),
156 (save_time_str, expected_latest_item_w_meta),
157 ('file', expected_file_item_w_meta))
158 wvpasseq(expected, res)
160 wvstart('resolve: /test/latest/bad-symlink')
162 res = resolve(repo, '/test/latest/bad-symlink')
163 wvpasseq(4, len(res))
164 expected = (('', vfs._root),
165 ('test', test_revlist_w_meta),
166 (save_time_str, expected_latest_item_w_meta),
168 wvpasseq(expected, res)
170 wvstart('resolve nofollow: /test/latest/bad-symlink')
172 res = resolve(repo, '/test/latest/bad-symlink', follow=False)
173 wvpasseq(4, len(res))
174 bad_symlink_value = tip_tree['bad-symlink']
175 expected_bad_symlink_item_w_meta = vfs.Item(meta=bad_symlink_value.meta,
176 oid=bad_symlink_value.oid)
177 expected = (('', vfs._root),
178 ('test', test_revlist_w_meta),
179 (save_time_str, expected_latest_item_w_meta),
180 ('bad-symlink', expected_bad_symlink_item_w_meta))
181 wvpasseq(expected, res)
183 wvstart('resolve: /test/latest/file-symlink')
185 res = resolve(repo, '/test/latest/file-symlink')
186 wvpasseq(4, len(res))
187 expected = (('', vfs._root),
188 ('test', test_revlist_w_meta),
189 (save_time_str, expected_latest_item_w_meta),
190 ('file', expected_file_item_w_meta))
191 wvpasseq(expected, res)
193 wvstart('resolve nofollow: /test/latest/file-symlink')
195 res = resolve(repo, '/test/latest/file-symlink', follow=False)
196 wvpasseq(4, len(res))
197 file_symlink_value = tip_tree['file-symlink']
198 expected_file_symlink_item_w_meta = vfs.Item(meta=file_symlink_value.meta,
199 oid=file_symlink_value.oid)
200 expected = (('', vfs._root),
201 ('test', test_revlist_w_meta),
202 (save_time_str, expected_latest_item_w_meta),
203 ('file-symlink', expected_file_symlink_item_w_meta))
204 wvpasseq(expected, res)
206 wvstart('resolve: /test/latest/missing')
208 res = resolve(repo, '/test/latest/missing')
209 wvpasseq(4, len(res))
211 wvpasseq('missing', name)
214 for path in ('/test/latest/file/',
215 '/test/latest/file/.',
216 '/test/latest/file/..',
217 '/test/latest/file/../',
218 '/test/latest/file/../.',
219 '/test/latest/file/../..',
220 '/test/latest/file/foo'):
221 wvstart('resolve: ' + path)
225 except vfs.IOError as res_ex:
226 wvpasseq(ENOTDIR, res_ex.errno)
227 wvpasseq(['', 'test', save_time_str, 'file'],
228 [name for name, item in res_ex.terminus])
230 for path in ('/test/latest/file-symlink/',
231 '/test/latest/file-symlink/.',
232 '/test/latest/file-symlink/..',
233 '/test/latest/file-symlink/../',
234 '/test/latest/file-symlink/../.',
235 '/test/latest/file-symlink/../..'):
236 wvstart('resolve nofollow: ' + path)
239 resolve(repo, path, follow=False)
240 except vfs.IOError as res_ex:
241 wvpasseq(ENOTDIR, res_ex.errno)
242 wvpasseq(['', 'test', save_time_str, 'file'],
243 [name for name, item in res_ex.terminus])
245 wvstart('resolve: non-directory parent')
247 file_res = resolve(repo, '/test/latest/file')
249 resolve(repo, 'foo', parent=file_res)
250 except vfs.IOError as res_ex:
251 wvpasseq(ENOTDIR, res_ex.errno)
252 wvpasseq(None, res_ex.terminus)
254 wvstart('resolve nofollow: /test/latest/dir-symlink')
256 res = resolve(repo, '/test/latest/dir-symlink', follow=False)
257 wvpasseq(4, len(res))
258 dir_symlink_value = tip_tree['dir-symlink']
259 expected_dir_symlink_item_w_meta = vfs.Item(meta=dir_symlink_value.meta,
260 oid=dir_symlink_value.oid)
261 expected = (('', vfs._root),
262 ('test', test_revlist_w_meta),
263 (save_time_str, expected_latest_item_w_meta),
264 ('dir-symlink', expected_dir_symlink_item_w_meta))
265 wvpasseq(expected, res)
267 dir_value = tip_tree['dir']
268 expected_dir_item = vfs.Item(oid=dir_value.oid,
269 meta=tree_dict(repo, dir_value.oid)['.'].meta)
270 expected = (('', vfs._root),
271 ('test', test_revlist_w_meta),
272 (save_time_str, expected_latest_item_w_meta),
273 ('dir', expected_dir_item))
274 def lresolve(*args, **keys):
275 return resolve(*args, **dict(keys, follow=False))
276 for resname, resolver in (('resolve', resolve),
277 ('resolve nofollow', lresolve)):
278 for path in ('/test/latest/dir-symlink/',
279 '/test/latest/dir-symlink/.'):
280 wvstart(resname + ': ' + path)
282 res = resolver(repo, path)
283 wvpasseq(4, len(res))
284 wvpasseq(expected, res)
285 wvstart('resolve: /test/latest/dir-symlink')
287 res = resolve(repo, path)
288 wvpasseq(4, len(res))
289 wvpasseq(expected, res)
292 def test_resolve_loop():
293 with no_lingering_errors():
294 with test_tempdir('bup-tvfs-resloop-') as tmpdir:
295 resolve = vfs.resolve
296 bup_dir = tmpdir + '/bup'
297 environ['GIT_DIR'] = bup_dir
298 environ['BUP_DIR'] = bup_dir
299 git.repodir = bup_dir
301 data_path = tmpdir + '/src'
303 symlink('loop', data_path + '/loop')
304 ex((bup_path, 'init'))
305 ex((bup_path, 'index', '-v', data_path))
307 ex((bup_path, 'save', '-d', str(save_utc), '-tvvn', 'test', '--strip',
309 save_name = strftime('%Y-%m-%d-%H%M%S', localtime(save_utc))
311 wvpasseq('this call should never return',
312 resolve(repo, '/test/%s/loop' % save_name))
313 except vfs.IOError as res_ex:
314 wvpasseq(ELOOP, res_ex.errno)
315 wvpasseq(['', 'test', save_name, 'loop'],
316 [name for name, item in res_ex.terminus])
318 # FIXME: add tests for the want_meta=False cases.