]> arthur.barton.de Git - bup.git/blob - lib/bup/t/tresolve.py
Simplify bup startup process
[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, 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
16
17 bup_path = path.exe()
18
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.
24
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'))
32             git.repodir = bup_dir
33             with create_repo(bup_dir) as repo:
34                 test_repo(repo, tmpdir)
35
36 # Currently, we just test through the repos since LocalRepo resolve is
37 # just a straight redirection to vfs.resolve.
38
39 def test_resolve(repo, tmpdir):
40         data_path = tmpdir + '/src'
41         resolve = repo.resolve
42         save_time = 100000
43         save_time_str = strftime('%Y-%m-%d-%H%M%S', localtime(save_time))
44         os.mkdir(data_path)
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'))
55
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',
60                              tip_oidx))[0].strip()
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,
64                                           oid=tip_oid)
65         expected_latest_item = vfs.Commit(meta=S_IFDIR | 0o755,
66                                           oid=tip_tree_oid,
67                                           coid=tip_oid)
68         expected_latest_item_w_meta = vfs.Commit(meta=tip_tree['.'].meta,
69                                                  oid=tip_tree_oid,
70                                                  coid=tip_oid)
71         expected_latest_link = vfs.FakeLink(meta=vfs.default_symlink_mode,
72                                             target=save_time_str)
73         expected_test_tag_item = expected_latest_item
74
75         wvstart('resolve: /')
76         vfs.clear_cache()
77         res = resolve('/')
78         wvpasseq(1, len(res))
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),
83                             ('.tag', vfs._tags),
84                             ('test', test_revlist_w_meta)]),
85                  root_content)
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)
98             vfs.clear_cache()
99             res = resolve(path)
100             wvpasseq((('', vfs._root),), res)
101
102         wvstart('resolve: /.tag')
103         vfs.clear_cache()
104         res = resolve('/.tag')
105         wvpasseq(2, len(res))
106         wvpasseq((('', vfs._root), ('.tag', vfs._tags)),
107                  res)
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)]),
112                  tag_content)
113
114         wvstart('resolve: /test')
115         vfs.clear_cache()
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)]),
125                  test_content)
126
127         wvstart('resolve: /test/latest')
128         vfs.clear_cache()
129         res = resolve('/test/latest')
130         wvpasseq(3, len(res))
131         expected_latest_item_w_meta = vfs.Commit(meta=tip_tree['.'].meta,
132                                                  oid=tip_tree_oid,
133                                                  coid=tip_oid)
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]
142                                        for name in ('.',
143                                                     'bad-symlink',
144                                                     'dir',
145                                                     'dir-symlink',
146                                                     'file',
147                                                     'file-symlink')))
148         wvpasseq(expected, latest_content)
149
150         wvstart('resolve: /test/latest/file')
151         vfs.clear_cache()
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)
161
162         wvstart('resolve: /test/latest/bad-symlink')
163         vfs.clear_cache()
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),
169                     ('not-there', None))
170         wvpasseq(expected, res)
171
172         wvstart('resolve nofollow: /test/latest/bad-symlink')
173         vfs.clear_cache()
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)
184
185         wvstart('resolve: /test/latest/file-symlink')
186         vfs.clear_cache()
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)
194
195         wvstart('resolve nofollow: /test/latest/file-symlink')
196         vfs.clear_cache()
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)
207
208         wvstart('resolve: /test/latest/missing')
209         vfs.clear_cache()
210         res = resolve('/test/latest/missing')
211         wvpasseq(4, len(res))
212         name, item = res[-1]
213         wvpasseq('missing', name)
214         wvpass(item is None)
215
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)
224             vfs.clear_cache()
225             try:
226                 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])
231
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)
239             vfs.clear_cache()
240             try:
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])
246
247         wvstart('resolve: non-directory parent')
248         vfs.clear_cache()
249         file_res = resolve('/test/latest/file')
250         try:
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)
255
256         wvstart('resolve nofollow: /test/latest/dir-symlink')
257         vfs.clear_cache()
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)
268
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)
283                 vfs.clear_cache()
284                 res = resolver(path)
285                 wvpasseq(4, len(res))
286                 wvpasseq(expected, res)
287         wvstart('resolve: /test/latest/dir-symlink')
288         vfs.clear_cache()
289         res = resolve(path)
290         wvpasseq(4, len(res))
291         wvpasseq(expected, res)
292
293 @wvtest
294 def test_local_resolve():
295     prep_and_test_repo('local-vfs-resolve',
296                        lambda x: LocalRepo(repo_dir=x), test_resolve)
297
298 @wvtest
299 def test_remote_resolve():
300     prep_and_test_repo('remote-vfs-resolve',
301                        lambda x: RemoteRepo(x), test_resolve)
302
303 def test_resolve_loop(repo, tmpdir):
304             data_path = tmpdir + '/src'
305             os.mkdir(data_path)
306             symlink('loop', data_path + '/loop')
307             ex((bup_path, 'init'))
308             ex((bup_path, 'index', '-v', data_path))
309             save_utc = 100000
310             ex((bup_path, 'save', '-d', str(save_utc), '-tvvn', 'test', '--strip',
311                 data_path))
312             save_name = strftime('%Y-%m-%d-%H%M%S', localtime(save_utc))
313             try:
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])
320
321 @wvtest
322 def test_local_resolve_loop():
323     prep_and_test_repo('local-vfs-resolve-loop',
324                        lambda x: LocalRepo(x), test_resolve_loop)
325
326 @wvtest
327 def test_remote_resolve_loop():
328     prep_and_test_repo('remote-vfs-resolve-loop',
329                        lambda x: RemoteRepo(x), test_resolve_loop)
330
331 # FIXME: add tests for the want_meta=False cases.