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, RemoteRepo
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.
28 def prep_and_test_repo(name, create_repo, test_repo):
29 with no_lingering_errors():
30 with test_tempdir('bup-t' + name) as tmpdir:
31 bup_dir = tmpdir + '/bup'
32 environ['GIT_DIR'] = bup_dir
33 environ['BUP_DIR'] = bup_dir
34 environ['BUP_MAIN_EXE'] = bup_path
35 ex((bup_path, 'init'))
37 with create_repo(bup_dir) as repo:
38 test_repo(repo, tmpdir)
40 # Currently, we just test through the repos since LocalRepo resolve is
41 # just a straight redirection to vfs.resolve.
43 def test_resolve(repo, tmpdir):
44 data_path = tmpdir + '/src'
45 resolve = repo.resolve
47 save_time_str = strftime('%Y-%m-%d-%H%M%S', localtime(save_time))
49 os.mkdir(data_path + '/dir')
50 with open(data_path + '/file', 'w+') as tmpfile:
51 print('canary', file=tmpfile)
52 symlink('file', data_path + '/file-symlink')
53 symlink('dir', data_path + '/dir-symlink')
54 symlink('not-there', data_path + '/bad-symlink')
55 ex((bup_path, 'index', '-v', data_path))
56 ex((bup_path, 'save', '-d', str(save_time), '-tvvn', 'test',
57 '--strip', data_path))
58 ex((bup_path, 'tag', 'test-tag', 'test'))
60 tip_hash = exo(('git', 'show-ref', 'refs/heads/test'))[0]
61 tip_oidx = tip_hash.strip().split()[0]
62 tip_oid = tip_oidx.decode('hex')
63 tip_tree_oidx = exo(('git', 'log', '--pretty=%T', '-n1',
65 tip_tree_oid = tip_tree_oidx.decode('hex')
66 tip_tree = tree_dict(repo, tip_tree_oid)
67 test_revlist_w_meta = vfs.RevList(meta=tip_tree['.'].meta,
69 expected_latest_item = vfs.Commit(meta=S_IFDIR | 0o755,
72 expected_latest_item_w_meta = vfs.Commit(meta=tip_tree['.'].meta,
75 expected_latest_link = vfs.FakeLink(meta=vfs.default_symlink_mode,
77 expected_test_tag_item = expected_latest_item
83 wvpasseq((('', vfs._root),), res)
84 ignore, root_item = res[0]
85 root_content = frozenset(vfs.contents(repo, root_item))
86 wvpasseq(frozenset([('.', root_item),
88 ('test', test_revlist_w_meta)]),
90 for path in ('//', '/.', '/./', '/..', '/../',
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 '/test/././latest/dir/../../..',
98 '/test/.//./latest/dir/../../..',
99 '/test//.//.//latest/dir/../../..'
100 '/test//./latest/dir/../../..'):
101 wvstart('resolve: ' + path)
104 wvpasseq((('', vfs._root),), res)
106 wvstart('resolve: /.tag')
108 res = resolve('/.tag')
109 wvpasseq(2, len(res))
110 wvpasseq((('', vfs._root), ('.tag', vfs._tags)),
112 ignore, tag_item = res[1]
113 tag_content = frozenset(vfs.contents(repo, tag_item))
114 wvpasseq(frozenset([('.', tag_item),
115 ('test-tag', expected_test_tag_item)]),
118 wvstart('resolve: /test')
120 res = resolve('/test')
121 wvpasseq(2, len(res))
122 wvpasseq((('', vfs._root), ('test', test_revlist_w_meta)), res)
123 ignore, test_item = res[1]
124 test_content = frozenset(vfs.contents(repo, test_item))
125 # latest has metadata here due to caching
126 wvpasseq(frozenset([('.', test_revlist_w_meta),
127 (save_time_str, expected_latest_item_w_meta),
128 ('latest', expected_latest_link)]),
131 wvstart('resolve: /test/latest')
133 res = resolve('/test/latest')
134 wvpasseq(3, len(res))
135 expected_latest_item_w_meta = vfs.Commit(meta=tip_tree['.'].meta,
138 expected = (('', vfs._root),
139 ('test', test_revlist_w_meta),
140 (save_time_str, expected_latest_item_w_meta))
141 wvpasseq(expected, res)
142 ignore, latest_item = res[2]
143 latest_content = frozenset(vfs.contents(repo, latest_item))
144 expected = frozenset((x.name, vfs.Item(oid=x.oid, meta=x.meta))
145 for x in (tip_tree[name]
152 wvpasseq(expected, latest_content)
154 wvstart('resolve: /test/latest/file')
156 res = resolve('/test/latest/file')
157 wvpasseq(4, len(res))
158 expected_file_item_w_meta = vfs.Item(meta=tip_tree['file'].meta,
159 oid=tip_tree['file'].oid)
160 expected = (('', vfs._root),
161 ('test', test_revlist_w_meta),
162 (save_time_str, expected_latest_item_w_meta),
163 ('file', expected_file_item_w_meta))
164 wvpasseq(expected, res)
166 wvstart('resolve: /test/latest/bad-symlink')
168 res = resolve('/test/latest/bad-symlink')
169 wvpasseq(4, len(res))
170 expected = (('', vfs._root),
171 ('test', test_revlist_w_meta),
172 (save_time_str, expected_latest_item_w_meta),
174 wvpasseq(expected, res)
176 wvstart('resolve nofollow: /test/latest/bad-symlink')
178 res = resolve('/test/latest/bad-symlink', follow=False)
179 wvpasseq(4, len(res))
180 bad_symlink_value = tip_tree['bad-symlink']
181 expected_bad_symlink_item_w_meta = vfs.Item(meta=bad_symlink_value.meta,
182 oid=bad_symlink_value.oid)
183 expected = (('', vfs._root),
184 ('test', test_revlist_w_meta),
185 (save_time_str, expected_latest_item_w_meta),
186 ('bad-symlink', expected_bad_symlink_item_w_meta))
187 wvpasseq(expected, res)
189 wvstart('resolve: /test/latest/file-symlink')
191 res = resolve('/test/latest/file-symlink')
192 wvpasseq(4, len(res))
193 expected = (('', vfs._root),
194 ('test', test_revlist_w_meta),
195 (save_time_str, expected_latest_item_w_meta),
196 ('file', expected_file_item_w_meta))
197 wvpasseq(expected, res)
199 wvstart('resolve nofollow: /test/latest/file-symlink')
201 res = resolve('/test/latest/file-symlink', follow=False)
202 wvpasseq(4, len(res))
203 file_symlink_value = tip_tree['file-symlink']
204 expected_file_symlink_item_w_meta = vfs.Item(meta=file_symlink_value.meta,
205 oid=file_symlink_value.oid)
206 expected = (('', vfs._root),
207 ('test', test_revlist_w_meta),
208 (save_time_str, expected_latest_item_w_meta),
209 ('file-symlink', expected_file_symlink_item_w_meta))
210 wvpasseq(expected, res)
212 wvstart('resolve: /test/latest/missing')
214 res = resolve('/test/latest/missing')
215 wvpasseq(4, len(res))
217 wvpasseq('missing', name)
220 for path in ('/test/latest/file/',
221 '/test/latest/file/.',
222 '/test/latest/file/..',
223 '/test/latest/file/../',
224 '/test/latest/file/../.',
225 '/test/latest/file/../..',
226 '/test/latest/file/foo'):
227 wvstart('resolve: ' + path)
231 except vfs.IOError as res_ex:
232 wvpasseq(ENOTDIR, res_ex.errno)
233 wvpasseq(['', 'test', save_time_str, 'file'],
234 [name for name, item in res_ex.terminus])
236 for path in ('/test/latest/file-symlink/',
237 '/test/latest/file-symlink/.',
238 '/test/latest/file-symlink/..',
239 '/test/latest/file-symlink/../',
240 '/test/latest/file-symlink/../.',
241 '/test/latest/file-symlink/../..'):
242 wvstart('resolve nofollow: ' + path)
245 resolve(path, follow=False)
246 except vfs.IOError as res_ex:
247 wvpasseq(ENOTDIR, res_ex.errno)
248 wvpasseq(['', 'test', save_time_str, 'file'],
249 [name for name, item in res_ex.terminus])
251 wvstart('resolve: non-directory parent')
253 file_res = resolve('/test/latest/file')
255 resolve('foo', parent=file_res)
256 except vfs.IOError as res_ex:
257 wvpasseq(ENOTDIR, res_ex.errno)
258 wvpasseq(None, res_ex.terminus)
260 wvstart('resolve nofollow: /test/latest/dir-symlink')
262 res = resolve('/test/latest/dir-symlink', follow=False)
263 wvpasseq(4, len(res))
264 dir_symlink_value = tip_tree['dir-symlink']
265 expected_dir_symlink_item_w_meta = vfs.Item(meta=dir_symlink_value.meta,
266 oid=dir_symlink_value.oid)
267 expected = (('', vfs._root),
268 ('test', test_revlist_w_meta),
269 (save_time_str, expected_latest_item_w_meta),
270 ('dir-symlink', expected_dir_symlink_item_w_meta))
271 wvpasseq(expected, res)
273 dir_value = tip_tree['dir']
274 expected_dir_item = vfs.Item(oid=dir_value.oid,
275 meta=tree_dict(repo, dir_value.oid)['.'].meta)
276 expected = (('', vfs._root),
277 ('test', test_revlist_w_meta),
278 (save_time_str, expected_latest_item_w_meta),
279 ('dir', expected_dir_item))
280 def lresolve(*args, **keys):
281 return resolve(*args, **dict(keys, follow=False))
282 for resname, resolver in (('resolve', resolve),
283 ('resolve nofollow', lresolve)):
284 for path in ('/test/latest/dir-symlink/',
285 '/test/latest/dir-symlink/.'):
286 wvstart(resname + ': ' + path)
289 wvpasseq(4, len(res))
290 wvpasseq(expected, res)
291 wvstart('resolve: /test/latest/dir-symlink')
294 wvpasseq(4, len(res))
295 wvpasseq(expected, res)
298 def test_local_resolve():
299 prep_and_test_repo('local-vfs-resolve',
300 lambda x: LocalRepo(repo_dir=x), test_resolve)
303 def test_remote_resolve():
304 prep_and_test_repo('remote-vfs-resolve',
305 lambda x: RemoteRepo(x), test_resolve)
307 def test_resolve_loop(repo, tmpdir):
308 data_path = tmpdir + '/src'
310 symlink('loop', data_path + '/loop')
311 ex((bup_path, 'init'))
312 ex((bup_path, 'index', '-v', data_path))
314 ex((bup_path, 'save', '-d', str(save_utc), '-tvvn', 'test', '--strip',
316 save_name = strftime('%Y-%m-%d-%H%M%S', localtime(save_utc))
318 wvpasseq('this call should never return',
319 repo.resolve('/test/%s/loop' % save_name))
320 except vfs.IOError as res_ex:
321 wvpasseq(ELOOP, res_ex.errno)
322 wvpasseq(['', 'test', save_name, 'loop'],
323 [name for name, item in res_ex.terminus])
326 def test_local_resolve_loop():
327 prep_and_test_repo('local-vfs-resolve-loop',
328 lambda x: LocalRepo(x), test_resolve_loop)
331 def test_remote_resolve_loop():
332 prep_and_test_repo('remote-vfs-resolve-loop',
333 lambda x: RemoteRepo(x), test_resolve_loop)
335 # FIXME: add tests for the want_meta=False cases.