]> arthur.barton.de Git - bup.git/blob - lib/bup/t/tresolve.py
Move vfs resolve() tests to tresolve.py
[bup.git] / lib / bup / t / tresolve.py
1
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
6 from sys import stderr
7 from time import localtime, strftime
8
9 from wvtest import *
10
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
16
17 top_dir = '../../..'
18 bup_tmp = os.path.realpath('../../../t/tmp')
19 bup_path = top_dir + '/bup'
20 start_dir = os.getcwd()
21
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.
27
28 @wvtest
29 def test_resolve():
30     with no_lingering_errors():
31         with test_tempdir('bup-tvfs-') as tmpdir:
32             resolve = vfs.resolve
33             bup_dir = tmpdir + '/bup'
34             environ['GIT_DIR'] = bup_dir
35             environ['BUP_DIR'] = bup_dir
36             git.repodir = bup_dir
37             data_path = tmpdir + '/src'
38             save_time = 100000
39             save_time_str = strftime('%Y-%m-%d-%H%M%S', localtime(save_time))
40             os.mkdir(data_path)
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'))
52             repo = LocalRepo()
53
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',
58                                  tip_oidx))[0].strip()
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,
62                                               oid=tip_oid)
63             expected_latest_item = vfs.Commit(meta=S_IFDIR | 0o755,
64                                               oid=tip_tree_oid,
65                                               coid=tip_oid)
66             expected_latest_item_w_meta = vfs.Commit(meta=tip_tree['.'].meta,
67                                                      oid=tip_tree_oid,
68                                                      coid=tip_oid)
69             expected_latest_link = vfs.FakeLink(meta=vfs.default_symlink_mode,
70                                                 target=save_time_str)
71             expected_test_tag_item = expected_latest_item
72
73             wvstart('resolve: /')
74             vfs.clear_cache()
75             res = resolve(repo, '/')
76             wvpasseq(1, len(res))
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),
81                                 ('.tag', vfs._tags),
82                                 ('test', test_revlist_w_meta)]),
83                      root_content)
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)
96                 vfs.clear_cache()
97                 res = resolve(repo, path)
98                 wvpasseq((('', vfs._root),), res)
99
100             wvstart('resolve: /.tag')
101             vfs.clear_cache()
102             res = resolve(repo, '/.tag')
103             wvpasseq(2, len(res))
104             wvpasseq((('', vfs._root), ('.tag', vfs._tags)),
105                      res)
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)]),
110                      tag_content)
111
112             wvstart('resolve: /test')
113             vfs.clear_cache()
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)]),
123                      test_content)
124
125             wvstart('resolve: /test/latest')
126             vfs.clear_cache()
127             res = resolve(repo, '/test/latest')
128             wvpasseq(3, len(res))
129             expected_latest_item_w_meta = vfs.Commit(meta=tip_tree['.'].meta,
130                                                      oid=tip_tree_oid,
131                                                      coid=tip_oid)
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]
140                                            for name in ('.',
141                                                         'bad-symlink',
142                                                         'dir',
143                                                         'dir-symlink',
144                                                         'file',
145                                                         'file-symlink')))
146             wvpasseq(expected, latest_content)
147
148             wvstart('resolve: /test/latest/file')
149             vfs.clear_cache()
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)
159
160             wvstart('resolve: /test/latest/bad-symlink')
161             vfs.clear_cache()
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),
167                         ('not-there', None))
168             wvpasseq(expected, res)
169
170             wvstart('resolve nofollow: /test/latest/bad-symlink')
171             vfs.clear_cache()
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)
182
183             wvstart('resolve: /test/latest/file-symlink')
184             vfs.clear_cache()
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)
192
193             wvstart('resolve nofollow: /test/latest/file-symlink')
194             vfs.clear_cache()
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)
205
206             wvstart('resolve: /test/latest/missing')
207             vfs.clear_cache()
208             res = resolve(repo, '/test/latest/missing')
209             wvpasseq(4, len(res))
210             name, item = res[-1]
211             wvpasseq('missing', name)
212             wvpass(item is None)
213
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)
222                 vfs.clear_cache()
223                 try:
224                     resolve(repo, 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])
229
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)
237                 vfs.clear_cache()
238                 try:
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])
244
245             wvstart('resolve: non-directory parent')
246             vfs.clear_cache()
247             file_res = resolve(repo, '/test/latest/file')
248             try:
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)
253
254             wvstart('resolve nofollow: /test/latest/dir-symlink')
255             vfs.clear_cache()
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)
266
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)
281                     vfs.clear_cache()
282                     res = resolver(repo, path)
283                     wvpasseq(4, len(res))
284                     wvpasseq(expected, res)
285             wvstart('resolve: /test/latest/dir-symlink')
286             vfs.clear_cache()
287             res = resolve(repo, path)
288             wvpasseq(4, len(res))
289             wvpasseq(expected, res)
290
291 @wvtest
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
300             repo = LocalRepo()
301             data_path = tmpdir + '/src'
302             os.mkdir(data_path)
303             symlink('loop', data_path + '/loop')
304             ex((bup_path, 'init'))
305             ex((bup_path, 'index', '-v', data_path))
306             save_utc = 100000
307             ex((bup_path, 'save', '-d', str(save_utc), '-tvvn', 'test', '--strip',
308                 data_path))
309             save_name = strftime('%Y-%m-%d-%H%M%S', localtime(save_utc))
310             try:
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])
317
318 # FIXME: add tests for the want_meta=False cases.