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, path, vfs
12 from bup.metadata import Metadata
13 from bup.repo import LocalRepo, RemoteRepo
14 from bup.test.vfs import tree_dict
15 from buptest import ex, exo, no_lingering_errors, test_tempdir
19 ## The clear_cache() calls below are to make sure that the test starts
20 ## from a known state since at the moment the cache entry for a given
21 ## item (like a commit) can change. For example, its meta value might
22 ## be promoted from a mode to a Metadata instance once the tree it
23 ## refers to is traversed.
25 def prep_and_test_repo(name, create_repo, test_repo):
26 with no_lingering_errors():
27 with test_tempdir('bup-t' + name) as tmpdir:
28 bup_dir = tmpdir + '/bup'
29 environ['GIT_DIR'] = bup_dir
30 environ['BUP_DIR'] = bup_dir
31 ex((bup_path, 'init'))
33 with create_repo(bup_dir) as repo:
34 test_repo(repo, tmpdir)
36 # Currently, we just test through the repos since LocalRepo resolve is
37 # just a straight redirection to vfs.resolve.
39 def test_resolve(repo, tmpdir):
40 data_path = tmpdir + '/src'
41 resolve = repo.resolve
43 save_time_str = strftime('%Y-%m-%d-%H%M%S', localtime(save_time))
45 os.mkdir(data_path + '/dir')
46 with open(data_path + '/file', 'w+') as tmpfile:
47 print('canary', file=tmpfile)
48 symlink('file', data_path + '/file-symlink')
49 symlink('dir', data_path + '/dir-symlink')
50 symlink('not-there', data_path + '/bad-symlink')
51 ex((bup_path, 'index', '-v', data_path))
52 ex((bup_path, 'save', '-d', str(save_time), '-tvvn', 'test',
53 '--strip', data_path))
54 ex((bup_path, 'tag', 'test-tag', 'test'))
56 tip_hash = exo(('git', 'show-ref', 'refs/heads/test'))[0]
57 tip_oidx = tip_hash.strip().split()[0]
58 tip_oid = tip_oidx.decode('hex')
59 tip_tree_oidx = exo(('git', 'log', '--pretty=%T', '-n1',
61 tip_tree_oid = tip_tree_oidx.decode('hex')
62 tip_tree = tree_dict(repo, tip_tree_oid)
63 test_revlist_w_meta = vfs.RevList(meta=tip_tree['.'].meta,
65 expected_latest_item = vfs.Commit(meta=S_IFDIR | 0o755,
68 expected_latest_item_w_meta = vfs.Commit(meta=tip_tree['.'].meta,
71 expected_latest_link = vfs.FakeLink(meta=vfs.default_symlink_mode,
73 expected_test_tag_item = expected_latest_item
79 wvpasseq((('', vfs._root),), res)
80 ignore, root_item = res[0]
81 root_content = frozenset(vfs.contents(repo, root_item))
82 wvpasseq(frozenset([('.', root_item),
84 ('test', test_revlist_w_meta)]),
86 for path in ('//', '/.', '/./', '/..', '/../',
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 '/test//.//.//latest/dir/../../..'
96 '/test//./latest/dir/../../..'):
97 wvstart('resolve: ' + path)
100 wvpasseq((('', vfs._root),), res)
102 wvstart('resolve: /.tag')
104 res = resolve('/.tag')
105 wvpasseq(2, len(res))
106 wvpasseq((('', vfs._root), ('.tag', vfs._tags)),
108 ignore, tag_item = res[1]
109 tag_content = frozenset(vfs.contents(repo, tag_item))
110 wvpasseq(frozenset([('.', tag_item),
111 ('test-tag', expected_test_tag_item)]),
114 wvstart('resolve: /test')
116 res = resolve('/test')
117 wvpasseq(2, len(res))
118 wvpasseq((('', vfs._root), ('test', test_revlist_w_meta)), res)
119 ignore, test_item = res[1]
120 test_content = frozenset(vfs.contents(repo, test_item))
121 # latest has metadata here due to caching
122 wvpasseq(frozenset([('.', test_revlist_w_meta),
123 (save_time_str, expected_latest_item_w_meta),
124 ('latest', expected_latest_link)]),
127 wvstart('resolve: /test/latest')
129 res = resolve('/test/latest')
130 wvpasseq(3, len(res))
131 expected_latest_item_w_meta = vfs.Commit(meta=tip_tree['.'].meta,
134 expected = (('', vfs._root),
135 ('test', test_revlist_w_meta),
136 (save_time_str, expected_latest_item_w_meta))
137 wvpasseq(expected, res)
138 ignore, latest_item = res[2]
139 latest_content = frozenset(vfs.contents(repo, latest_item))
140 expected = frozenset((x.name, vfs.Item(oid=x.oid, meta=x.meta))
141 for x in (tip_tree[name]
148 wvpasseq(expected, latest_content)
150 wvstart('resolve: /test/latest/file')
152 res = resolve('/test/latest/file')
153 wvpasseq(4, len(res))
154 expected_file_item_w_meta = vfs.Item(meta=tip_tree['file'].meta,
155 oid=tip_tree['file'].oid)
156 expected = (('', vfs._root),
157 ('test', test_revlist_w_meta),
158 (save_time_str, expected_latest_item_w_meta),
159 ('file', expected_file_item_w_meta))
160 wvpasseq(expected, res)
162 wvstart('resolve: /test/latest/bad-symlink')
164 res = resolve('/test/latest/bad-symlink')
165 wvpasseq(4, len(res))
166 expected = (('', vfs._root),
167 ('test', test_revlist_w_meta),
168 (save_time_str, expected_latest_item_w_meta),
170 wvpasseq(expected, res)
172 wvstart('resolve nofollow: /test/latest/bad-symlink')
174 res = resolve('/test/latest/bad-symlink', follow=False)
175 wvpasseq(4, len(res))
176 bad_symlink_value = tip_tree['bad-symlink']
177 expected_bad_symlink_item_w_meta = vfs.Item(meta=bad_symlink_value.meta,
178 oid=bad_symlink_value.oid)
179 expected = (('', vfs._root),
180 ('test', test_revlist_w_meta),
181 (save_time_str, expected_latest_item_w_meta),
182 ('bad-symlink', expected_bad_symlink_item_w_meta))
183 wvpasseq(expected, res)
185 wvstart('resolve: /test/latest/file-symlink')
187 res = resolve('/test/latest/file-symlink')
188 wvpasseq(4, len(res))
189 expected = (('', vfs._root),
190 ('test', test_revlist_w_meta),
191 (save_time_str, expected_latest_item_w_meta),
192 ('file', expected_file_item_w_meta))
193 wvpasseq(expected, res)
195 wvstart('resolve nofollow: /test/latest/file-symlink')
197 res = resolve('/test/latest/file-symlink', follow=False)
198 wvpasseq(4, len(res))
199 file_symlink_value = tip_tree['file-symlink']
200 expected_file_symlink_item_w_meta = vfs.Item(meta=file_symlink_value.meta,
201 oid=file_symlink_value.oid)
202 expected = (('', vfs._root),
203 ('test', test_revlist_w_meta),
204 (save_time_str, expected_latest_item_w_meta),
205 ('file-symlink', expected_file_symlink_item_w_meta))
206 wvpasseq(expected, res)
208 wvstart('resolve: /test/latest/missing')
210 res = resolve('/test/latest/missing')
211 wvpasseq(4, len(res))
213 wvpasseq('missing', name)
216 for path in ('/test/latest/file/',
217 '/test/latest/file/.',
218 '/test/latest/file/..',
219 '/test/latest/file/../',
220 '/test/latest/file/../.',
221 '/test/latest/file/../..',
222 '/test/latest/file/foo'):
223 wvstart('resolve: ' + path)
227 except vfs.IOError as res_ex:
228 wvpasseq(ENOTDIR, res_ex.errno)
229 wvpasseq(['', 'test', save_time_str, 'file'],
230 [name for name, item in res_ex.terminus])
232 for path in ('/test/latest/file-symlink/',
233 '/test/latest/file-symlink/.',
234 '/test/latest/file-symlink/..',
235 '/test/latest/file-symlink/../',
236 '/test/latest/file-symlink/../.',
237 '/test/latest/file-symlink/../..'):
238 wvstart('resolve nofollow: ' + path)
241 resolve(path, follow=False)
242 except vfs.IOError as res_ex:
243 wvpasseq(ENOTDIR, res_ex.errno)
244 wvpasseq(['', 'test', save_time_str, 'file'],
245 [name for name, item in res_ex.terminus])
247 wvstart('resolve: non-directory parent')
249 file_res = resolve('/test/latest/file')
251 resolve('foo', parent=file_res)
252 except vfs.IOError as res_ex:
253 wvpasseq(ENOTDIR, res_ex.errno)
254 wvpasseq(None, res_ex.terminus)
256 wvstart('resolve nofollow: /test/latest/dir-symlink')
258 res = resolve('/test/latest/dir-symlink', follow=False)
259 wvpasseq(4, len(res))
260 dir_symlink_value = tip_tree['dir-symlink']
261 expected_dir_symlink_item_w_meta = vfs.Item(meta=dir_symlink_value.meta,
262 oid=dir_symlink_value.oid)
263 expected = (('', vfs._root),
264 ('test', test_revlist_w_meta),
265 (save_time_str, expected_latest_item_w_meta),
266 ('dir-symlink', expected_dir_symlink_item_w_meta))
267 wvpasseq(expected, res)
269 dir_value = tip_tree['dir']
270 expected_dir_item = vfs.Item(oid=dir_value.oid,
271 meta=tree_dict(repo, dir_value.oid)['.'].meta)
272 expected = (('', vfs._root),
273 ('test', test_revlist_w_meta),
274 (save_time_str, expected_latest_item_w_meta),
275 ('dir', expected_dir_item))
276 def lresolve(*args, **keys):
277 return resolve(*args, **dict(keys, follow=False))
278 for resname, resolver in (('resolve', resolve),
279 ('resolve nofollow', lresolve)):
280 for path in ('/test/latest/dir-symlink/',
281 '/test/latest/dir-symlink/.'):
282 wvstart(resname + ': ' + path)
285 wvpasseq(4, len(res))
286 wvpasseq(expected, res)
287 wvstart('resolve: /test/latest/dir-symlink')
290 wvpasseq(4, len(res))
291 wvpasseq(expected, res)
294 def test_local_resolve():
295 prep_and_test_repo('local-vfs-resolve',
296 lambda x: LocalRepo(repo_dir=x), test_resolve)
299 def test_remote_resolve():
300 prep_and_test_repo('remote-vfs-resolve',
301 lambda x: RemoteRepo(x), test_resolve)
303 def test_resolve_loop(repo, tmpdir):
304 data_path = tmpdir + '/src'
306 symlink('loop', data_path + '/loop')
307 ex((bup_path, 'init'))
308 ex((bup_path, 'index', '-v', data_path))
310 ex((bup_path, 'save', '-d', str(save_utc), '-tvvn', 'test', '--strip',
312 save_name = strftime('%Y-%m-%d-%H%M%S', localtime(save_utc))
314 wvpasseq('this call should never return',
315 repo.resolve('/test/%s/loop' % save_name))
316 except vfs.IOError as res_ex:
317 wvpasseq(ELOOP, res_ex.errno)
318 wvpasseq(['', 'test', save_name, 'loop'],
319 [name for name, item in res_ex.terminus])
322 def test_local_resolve_loop():
323 prep_and_test_repo('local-vfs-resolve-loop',
324 lambda x: LocalRepo(x), test_resolve_loop)
327 def test_remote_resolve_loop():
328 prep_and_test_repo('remote-vfs-resolve-loop',
329 lambda x: RemoteRepo(x), test_resolve_loop)
331 # FIXME: add tests for the want_meta=False cases.