3 bup_python="$(dirname "$0")/../cmd/bup-python" || exit $?
4 exec "$bup_python" "$0" ${1+"$@"}
8 from __future__ import print_function
9 from errno import ENOENT
10 from os import chdir, mkdir, rename
11 from os.path import abspath, dirname
12 from shutil import rmtree
13 from subprocess import PIPE
16 # For buptest, wvtest, ...
17 sys.path[:0] = (abspath(os.path.dirname(__file__) + '/..'),)
19 from bup import compat, path
20 from bup.compat import environ, getcwd, items
21 from bup.helpers import bquote, merge_dict, unlink
22 from bup.io import byte_stream
23 from buptest import ex, exo, test_tempdir
24 from wvtest import wvcheck, wvfail, wvmsg, wvpass, wvpasseq, wvpassne, wvstart
28 stdout = byte_stream(sys.stdout)
30 # FIXME: per-test function
31 environ[b'GIT_AUTHOR_NAME'] = b'bup test-get'
32 environ[b'GIT_COMMITTER_NAME'] = b'bup test-get'
33 environ[b'GIT_AUTHOR_EMAIL'] = b'bup@85430dcca2b611e4b2c3-8f5691723476'
34 environ[b'GIT_COMMITTER_EMAIL'] = b'bup@85430dcca2b611e4b2c3-8f5691723476'
36 # The clean-repo test can probably be applied more broadly. It was
37 # initially just applied to test-pick to catch a bug.
40 bup_cmd = bup.path.exe()
43 err = [] # because python's scoping mess...
44 def onerror(function, path, excinfo):
45 err.append((function, path, excinfo))
46 rmtree(path, onerror=onerror)
48 function, path, excinfo = err[0]
49 ex_type, ex, traceback = excinfo
50 if (not isinstance(ex, OSError)) or ex.errno != ENOENT:
53 def verify_trees_match(path1, path2):
55 exr = exo((top + b'/t/compare-trees', b'-c', path1, path2), check=False)
58 wvcheck(exr.rc == 0, 'process exit %d == 0' % exr.rc)
60 def verify_rcz(cmd, **kwargs):
61 assert not kwargs.get('check')
62 kwargs['check'] = False
63 result = exo(cmd, **kwargs)
64 stdout.write(result.out)
65 rc = result.proc.returncode
66 wvcheck(rc == 0, 'process exit %d == 0' % rc)
69 # FIXME: multline, or allow opts generally?
71 def verify_rx(rx, string):
72 wvcheck(re.search(rx, string), 'rx %r matches %r' % (rx, string))
74 def verify_nrx(rx, string):
75 wvcheck(not re.search(rx, string), "rx %r doesn't match %r" % (rx, string))
77 def validate_clean_repo():
78 out = verify_rcz((b'git', b'--git-dir', b'get-dest', b'fsck')).out
79 verify_nrx(br'dangling|mismatch|missing|unreachable', out)
81 def validate_blob(src_id, dest_id):
85 cat_tree = top + b'/t/git-cat-tree'
86 src_blob = verify_rcz((cat_tree, b'--git-dir', b'get-src', src_id)).out
87 dest_blob = verify_rcz((cat_tree, b'--git-dir', b'get-src', src_id)).out
88 wvpasseq(src_blob, dest_blob)
90 def validate_tree(src_id, dest_id):
95 mkdir(b'restore-dest')
97 commit_env = merge_dict(environ, {b'GIT_COMMITTER_DATE': b'2014-01-01 01:01'})
99 # Create a commit so the archive contents will have matching timestamps.
100 src_c = exo((b'git', b'--git-dir', b'get-src',
101 b'commit-tree', b'-m', b'foo', src_id),
102 env=commit_env).out.strip()
103 dest_c = exo((b'git', b'--git-dir', b'get-dest',
104 b'commit-tree', b'-m', b'foo', dest_id),
105 env=commit_env).out.strip()
106 exr = verify_rcz(b'git --git-dir get-src archive %s | tar xvf - -C restore-src'
109 if exr.rc != 0: return False
110 exr = verify_rcz(b'git --git-dir get-dest archive %s | tar xvf - -C restore-dest'
113 if exr.rc != 0: return False
115 # git archive doesn't include an entry for ./.
116 unlink(b'restore-src/pax_global_header')
117 unlink(b'restore-dest/pax_global_header')
118 ex((b'touch', b'-r', b'restore-src', b'restore-dest'))
119 verify_trees_match(b'restore-src/', b'restore-dest/')
121 rmrf(b'restore-dest')
123 def validate_commit(src_id, dest_id):
124 exr = verify_rcz((b'git', b'--git-dir', b'get-src', b'cat-file', b'commit', src_id))
125 if exr.rc != 0: return False
127 exr = verify_rcz((b'git', b'--git-dir', b'get-dest', b'cat-file', b'commit', dest_id))
128 if exr.rc != 0: return False
130 wvpasseq(src_cat, dest_cat)
131 if src_cat != dest_cat: return False
134 rmrf(b'restore-dest')
135 mkdir(b'restore-src')
136 mkdir(b'restore-dest')
137 qsrc = bquote(src_id)
138 qdest = bquote(dest_id)
139 exr = verify_rcz((b'git --git-dir get-src archive ' + qsrc
140 + b' | tar xf - -C restore-src'),
142 if exr.rc != 0: return False
143 exr = verify_rcz((b'git --git-dir get-dest archive ' + qdest +
144 b' | tar xf - -C restore-dest'),
146 if exr.rc != 0: return False
148 # git archive doesn't include an entry for ./.
149 ex((b'touch', b'-r', b'restore-src', b'restore-dest'))
150 verify_trees_match(b'restore-src/', b'restore-dest/')
152 rmrf(b'restore-dest')
154 def _validate_save(orig_dir, save_path, commit_id, tree_id):
157 exr = verify_rcz((bup_cmd, b'-d', b'get-dest',
158 b'restore', b'-C', b'restore', save_path + b'/.'))
159 if exr.rc: return False
160 verify_trees_match(orig_dir + b'/', b'restore/')
162 # FIXME: double check that get-dest is correct
163 exr = verify_rcz((b'git', b'--git-dir', b'get-dest', b'ls-tree', tree_id))
164 if exr.rc: return False
165 cat = verify_rcz((b'git', b'--git-dir', b'get-dest',
166 b'cat-file', b'commit', commit_id))
167 if cat.rc: return False
168 wvpasseq(b'tree ' + tree_id, cat.out.splitlines()[0])
170 # FIXME: re-merge save and new_save?
172 def validate_save(dest_name, restore_subpath, commit_id, tree_id, orig_value,
174 out = get_out.splitlines()
175 print('blarg: out', repr(out), file=sys.stderr)
176 wvpasseq(2, len(out))
178 get_commit_id = out[1]
179 wvpasseq(tree_id, get_tree_id)
180 wvpasseq(commit_id, get_commit_id)
181 _validate_save(orig_value, dest_name + restore_subpath, commit_id, tree_id)
183 def validate_new_save(dest_name, restore_subpath, commit_id, tree_id, orig_value,
185 out = get_out.splitlines()
186 wvpasseq(2, len(out))
188 get_commit_id = out[1]
189 wvpasseq(tree_id, get_tree_id)
190 wvpassne(commit_id, get_commit_id)
191 _validate_save(orig_value, dest_name + restore_subpath, get_commit_id, tree_id)
193 def validate_tagged_save(tag_name, restore_subpath,
194 commit_id, tree_id, orig_value, get_out):
195 out = get_out.splitlines()
196 wvpasseq(1, len(out))
198 wvpasseq(commit_id, get_tag_id)
199 # Make sure tmp doesn't already exist.
200 exr = exo((b'git', b'--git-dir', b'get-dest', b'show-ref', b'tmp-branch-for-tag'),
204 ex((b'git', b'--git-dir', b'get-dest', b'branch', b'tmp-branch-for-tag',
205 b'refs/tags/' + tag_name))
206 _validate_save(orig_value, b'tmp-branch-for-tag/latest' + restore_subpath,
208 ex((b'git', b'--git-dir', b'get-dest', b'branch', b'-D', b'tmp-branch-for-tag'))
210 def validate_new_tagged_commit(tag_name, commit_id, tree_id, get_out):
211 out = get_out.splitlines()
212 wvpasseq(1, len(out))
214 wvpassne(commit_id, get_tag_id)
215 validate_tree(tree_id, tag_name + b':')
220 def _run_get(disposition, method, what):
221 print('run_get:', repr((disposition, method, what)), file=sys.stderr)
224 if disposition == 'get':
225 get_cmd = (bup_cmd, b'-d', b'get-dest',
226 b'get', b'-vvct', b'--print-tags', b'-s', b'get-src')
227 elif disposition == 'get-on':
228 get_cmd = (bup_cmd, b'-d', b'get-dest',
229 b'on', b'-', b'get', b'-vvct', b'--print-tags', b'-s', b'get-src')
230 elif disposition == 'get-to':
231 get_cmd = (bup_cmd, b'-d', b'get-dest',
232 b'get', b'-vvct', b'--print-tags', b'-s', b'get-src',
233 b'-r', b'-:' + getcwd() + b'/get-dest')
235 raise Exception('error: unexpected get disposition ' + repr(disposition))
237 global get_cases_tested
238 if isinstance(what, bytes):
239 cmd = get_cmd + (method, what)
241 assert not isinstance(what, str) # python 3 sanity check
242 if method in (b'--ff', b'--append', b'--pick', b'--force-pick', b'--new-tag',
246 cmd = get_cmd + (method, src, dest)
247 result = exo(cmd, check=False, stderr=PIPE)
248 get_cases_tested += 1
249 fsck = ex((bup_cmd, b'-d', b'get-dest', b'fsck'), check=False)
253 def run_get(disposition, method, what=None, given=None):
256 ex((bup_cmd, b'-d', b'get-dest', b'init'))
259 # FIXME: replace bup-get with independent commands as is feasible
260 exr = _run_get(disposition, b'--replace', given)
262 return _run_get(disposition, method, what)
264 def test_universal_behaviors(get_disposition):
265 methods = (b'--ff', b'--append', b'--pick', b'--force-pick', b'--new-tag',
266 b'--replace', b'--unnamed')
267 for method in methods:
268 mmsg = method.decode('ascii')
269 wvstart(get_disposition + ' ' + mmsg + ', missing source, fails')
270 exr = run_get(get_disposition, method, b'not-there')
272 verify_rx(br'cannot find source', exr.err)
273 for method in methods:
274 mmsg = method.decode('ascii')
275 wvstart(get_disposition + ' ' + mmsg + ' / fails')
276 exr = run_get(get_disposition, method, b'/')
278 verify_rx(b'cannot fetch entire repository', exr.err)
280 def verify_only_refs(**kwargs):
281 for kind, refs in items(kwargs):
283 abs_refs = [b'refs/heads/' + ref for ref in refs]
286 abs_refs = [b'refs/tags/' + ref for ref in refs]
289 raise TypeError('unexpected keyword argument %r' % kind)
291 verify_rcz([b'git', b'--git-dir', b'get-dest',
292 b'show-ref', b'--verify', karg] + abs_refs)
293 exr = exo((b'git', b'--git-dir', b'get-dest', b'show-ref', karg),
296 expected_refs = sorted(abs_refs)
297 repo_refs = sorted([x.split()[1] for x in exr.out.splitlines()])
298 wvpasseq(expected_refs, repo_refs)
300 # FIXME: can we just check "git show-ref --heads == ''"?
301 exr = exo((b'git', b'--git-dir', b'get-dest', b'show-ref', karg),
304 wvpasseq(b'', exr.out.strip())
306 def test_replace(get_disposition, src_info):
307 print('blarg:', repr(src_info), file=sys.stderr)
309 wvstart(get_disposition + ' --replace to root fails')
310 for item in (b'.tag/tinyfile',
311 b'src/latest' + src_info['tinyfile-path'],
313 b'src/latest' + src_info['subtree-vfs-path'],
317 exr = run_get(get_disposition, b'--replace', (item, b'/'))
319 verify_rx(br'impossible; can only overwrite branch or tag', exr.err)
321 tinyfile_id = src_info['tinyfile-id']
322 tinyfile_path = src_info['tinyfile-path']
323 subtree_vfs_path = src_info['subtree-vfs-path']
324 subtree_id = src_info['subtree-id']
325 commit_2_id = src_info['commit-2-id']
326 tree_2_id = src_info['tree-2-id']
329 existing_items = {'nothing' : None,
330 'blob' : (b'.tag/tinyfile', b'.tag/obj'),
331 'tree' : (b'.tag/tree-1', b'.tag/obj'),
332 'commit': (b'.tag/commit-1', b'.tag/obj')}
333 for ex_type, ex_ref in items(existing_items):
334 wvstart(get_disposition + ' --replace ' + ex_type + ' with blob tag')
335 for item in (b'.tag/tinyfile', b'src/latest' + tinyfile_path):
336 exr = run_get(get_disposition, b'--replace', (item ,b'.tag/obj'),
339 validate_blob(tinyfile_id, tinyfile_id)
340 verify_only_refs(heads=[], tags=(b'obj',))
341 wvstart(get_disposition + ' --replace ' + ex_type + ' with tree tag')
342 for item in (b'.tag/subtree', b'src/latest' + subtree_vfs_path):
343 exr = run_get(get_disposition, b'--replace', (item, b'.tag/obj'),
345 validate_tree(subtree_id, subtree_id)
346 verify_only_refs(heads=[], tags=(b'obj',))
347 wvstart(get_disposition + ' --replace ' + ex_type + ' with commitish tag')
348 for item in (b'.tag/commit-2', b'src/latest', b'src'):
349 exr = run_get(get_disposition, b'--replace', (item, b'.tag/obj'),
351 validate_tagged_save(b'obj', getcwd() + b'/src',
352 commit_2_id, tree_2_id, b'src-2', exr.out)
353 verify_only_refs(heads=[], tags=(b'obj',))
355 # Committish to branch.
356 existing_items = (('nothing', None),
357 ('branch', (b'.tag/commit-1', b'obj')))
358 for ex_type, ex_ref in existing_items:
359 for item_type, item in (('commit', b'.tag/commit-2'),
360 ('save', b'src/latest'),
362 wvstart(get_disposition + ' --replace '
363 + ex_type + ' with ' + item_type)
364 exr = run_get(get_disposition, b'--replace', (item, b'obj'),
366 validate_save(b'obj/latest', getcwd() + b'/src',
367 commit_2_id, tree_2_id, b'src-2', exr.out)
368 verify_only_refs(heads=(b'obj',), tags=[])
370 # Not committish to branch
371 existing_items = (('nothing', None),
372 ('branch', (b'.tag/commit-1', b'obj')))
373 for ex_type, ex_ref in existing_items:
374 for item_type, item in (('blob', b'.tag/tinyfile'),
375 ('blob', b'src/latest' + tinyfile_path),
376 ('tree', b'.tag/subtree'),
377 ('tree', b'src/latest' + subtree_vfs_path)):
378 wvstart(get_disposition + ' --replace branch with '
379 + item_type + ' given ' + ex_type + ' fails')
381 exr = run_get(get_disposition, b'--replace', (item, b'obj'),
384 verify_rx(br'cannot overwrite branch with .+ for', exr.err)
386 wvstart(get_disposition + ' --replace, implicit destinations')
388 exr = run_get(get_disposition, b'--replace', b'src')
389 validate_save(b'src/latest', getcwd() + b'/src',
390 commit_2_id, tree_2_id, b'src-2', exr.out)
391 verify_only_refs(heads=(b'src',), tags=[])
393 exr = run_get(get_disposition, b'--replace', b'.tag/commit-2')
394 validate_tagged_save(b'commit-2', getcwd() + b'/src',
395 commit_2_id, tree_2_id, b'src-2', exr.out)
396 verify_only_refs(heads=[], tags=(b'commit-2',))
398 def test_ff(get_disposition, src_info):
400 wvstart(get_disposition + ' --ff to root fails')
401 tinyfile_path = src_info['tinyfile-path']
402 for item in (b'.tag/tinyfile', b'src/latest' + tinyfile_path):
403 exr = run_get(get_disposition, b'--ff', (item, b'/'))
405 verify_rx(br'source for .+ must be a branch, save, or commit', exr.err)
406 subtree_vfs_path = src_info['subtree-vfs-path']
407 for item in (b'.tag/subtree', b'src/latest' + subtree_vfs_path):
408 exr = run_get(get_disposition, b'--ff', (item, b'/'))
410 verify_rx(br'is impossible; can only --append a tree to a branch',
412 for item in (b'.tag/commit-1', b'src/latest', b'src'):
413 exr = run_get(get_disposition, b'--ff', (item, b'/'))
415 verify_rx(br'destination for .+ is a root, not a branch', exr.err)
417 wvstart(get_disposition + ' --ff of not-committish fails')
418 for src in (b'.tag/tinyfile', b'src/latest' + tinyfile_path):
419 # FIXME: use get_item elsewhere?
420 for given, get_item in ((None, (src, b'obj')),
421 (None, (src, b'.tag/obj')),
422 ((b'.tag/tinyfile', b'.tag/obj'), (src, b'.tag/obj')),
423 ((b'.tag/tree-1', b'.tag/obj'), (src, b'.tag/obj')),
424 ((b'.tag/commit-1', b'.tag/obj'), (src, b'.tag/obj')),
425 ((b'.tag/commit-1', b'obj'), (src, b'obj'))):
426 exr = run_get(get_disposition, b'--ff', get_item, given=given)
428 verify_rx(br'must be a branch, save, or commit', exr.err)
429 for src in (b'.tag/subtree', b'src/latest' + subtree_vfs_path):
430 for given, get_item in ((None, (src, b'obj')),
431 (None, (src, b'.tag/obj')),
432 ((b'.tag/tinyfile', b'.tag/obj'), (src, b'.tag/obj')),
433 ((b'.tag/tree-1', b'.tag/obj'), (src, b'.tag/obj')),
434 ((b'.tag/commit-1', b'.tag/obj'), (src, b'.tag/obj')),
435 ((b'.tag/commit-1', b'obj'), (src, b'obj'))):
436 exr = run_get(get_disposition, b'--ff', get_item, given=given)
438 verify_rx(br'can only --append a tree to a branch', exr.err)
440 wvstart(get_disposition + ' --ff committish, ff possible')
441 save_2 = src_info['save-2']
442 for src in (b'.tag/commit-2', b'src/' + save_2, b'src'):
443 for given, get_item, complaint in \
444 ((None, (src, b'.tag/obj'),
445 br'destination .+ must be a valid branch name'),
446 ((b'.tag/tinyfile', b'.tag/obj'), (src, b'.tag/obj'),
447 br'destination .+ is a blob, not a branch'),
448 ((b'.tag/tree-1', b'.tag/obj'), (src, b'.tag/obj'),
449 br'destination .+ is a tree, not a branch'),
450 ((b'.tag/commit-1', b'.tag/obj'), (src, b'.tag/obj'),
451 br'destination .+ is a tagged commit, not a branch'),
452 ((b'.tag/commit-2', b'.tag/obj'), (src, b'.tag/obj'),
453 br'destination .+ is a tagged commit, not a branch')):
454 exr = run_get(get_disposition, b'--ff', get_item, given=given)
456 verify_rx(complaint, exr.err)
457 # FIXME: use src or item and given or existing consistently in loops...
458 commit_2_id = src_info['commit-2-id']
459 tree_2_id = src_info['tree-2-id']
460 for src in (b'.tag/commit-2', b'src/' + save_2, b'src'):
461 for given in (None, (b'.tag/commit-1', b'obj'), (b'.tag/commit-2', b'obj')):
462 exr = run_get(get_disposition, b'--ff', (src, b'obj'), given=given)
464 validate_save(b'obj/latest', getcwd() + b'/src',
465 commit_2_id, tree_2_id, b'src-2', exr.out)
466 verify_only_refs(heads=(b'obj',), tags=[])
468 wvstart(get_disposition + ' --ff, implicit destinations')
469 for item in (b'src', b'src/latest'):
470 exr = run_get(get_disposition, b'--ff', item)
473 ex((b'find', b'get-dest/refs'))
474 ex((bup_cmd, b'-d', b'get-dest', b'ls'))
476 validate_save(b'src/latest', getcwd() + b'/src',
477 commit_2_id, tree_2_id, b'src-2', exr.out)
478 #verify_only_refs(heads=('src',), tags=[])
480 wvstart(get_disposition + ' --ff, ff impossible')
481 for given, get_item in (((b'unrelated-branch', b'src'), b'src'),
482 ((b'.tag/commit-2', b'src'), (b'.tag/commit-1', b'src'))):
483 exr = run_get(get_disposition, b'--ff', get_item, given=given)
485 verify_rx(br'destination is not an ancestor of source', exr.err)
487 def test_append(get_disposition, src_info):
488 tinyfile_path = src_info['tinyfile-path']
489 subtree_vfs_path = src_info['subtree-vfs-path']
491 wvstart(get_disposition + ' --append to root fails')
492 for item in (b'.tag/tinyfile', b'src/latest' + tinyfile_path):
493 exr = run_get(get_disposition, b'--append', (item, b'/'))
495 verify_rx(br'source for .+ must be a branch, save, commit, or tree',
497 for item in (b'.tag/subtree', b'src/latest' + subtree_vfs_path,
498 b'.tag/commit-1', b'src/latest', b'src'):
499 exr = run_get(get_disposition, b'--append', (item, b'/'))
501 verify_rx(br'destination for .+ is a root, not a branch', exr.err)
503 wvstart(get_disposition + ' --append of not-treeish fails')
504 for src in (b'.tag/tinyfile', b'src/latest' + tinyfile_path):
505 for given, item in ((None, (src, b'obj')),
506 (None, (src, b'.tag/obj')),
507 ((b'.tag/tinyfile', b'.tag/obj'), (src, b'.tag/obj')),
508 ((b'.tag/tree-1', b'.tag/obj'), (src, b'.tag/obj')),
509 ((b'.tag/commit-1', b'.tag/obj'), (src, b'.tag/obj')),
510 ((b'.tag/commit-1', b'obj'), (src, b'obj'))):
511 exr = run_get(get_disposition, b'--append', item, given=given)
513 verify_rx(br'must be a branch, save, commit, or tree', exr.err)
515 wvstart(get_disposition + ' --append committish failure cases')
516 save_2 = src_info['save-2']
517 for src in (b'.tag/subtree', b'src/latest' + subtree_vfs_path,
518 b'.tag/commit-2', b'src/' + save_2, b'src'):
519 for given, item, complaint in \
520 ((None, (src, b'.tag/obj'),
521 br'destination .+ must be a valid branch name'),
522 ((b'.tag/tinyfile', b'.tag/obj'), (src, b'.tag/obj'),
523 br'destination .+ is a blob, not a branch'),
524 ((b'.tag/tree-1', b'.tag/obj'), (src, b'.tag/obj'),
525 br'destination .+ is a tree, not a branch'),
526 ((b'.tag/commit-1', b'.tag/obj'), (src, b'.tag/obj'),
527 br'destination .+ is a tagged commit, not a branch'),
528 ((b'.tag/commit-2', b'.tag/obj'), (src, b'.tag/obj'),
529 br'destination .+ is a tagged commit, not a branch')):
530 exr = run_get(get_disposition, b'--append', item, given=given)
532 verify_rx(complaint, exr.err)
534 wvstart(get_disposition + ' --append committish')
535 commit_2_id = src_info['commit-2-id']
536 tree_2_id = src_info['tree-2-id']
537 for item in (b'.tag/commit-2', b'src/' + save_2, b'src'):
538 for existing in (None, (b'.tag/commit-1', b'obj'),
539 (b'.tag/commit-2', b'obj'),
540 (b'unrelated-branch', b'obj')):
541 exr = run_get(get_disposition, b'--append', (item, b'obj'),
544 validate_new_save(b'obj/latest', getcwd() + b'/src',
545 commit_2_id, tree_2_id, b'src-2', exr.out)
546 verify_only_refs(heads=(b'obj',), tags=[])
548 save_1 = src_info['save-1']
549 commit_1_id = src_info['commit-1-id']
550 tree_1_id = src_info['tree-1-id']
551 for item in (b'.tag/commit-1', b'src/' + save_1, b'src-1'):
552 exr = run_get(get_disposition, b'--append', (item, b'obj'),
553 given=(b'.tag/commit-2', b'obj'))
555 validate_new_save(b'obj/latest', getcwd() + b'/src',
556 commit_1_id, tree_1_id, b'src-1', exr.out)
557 verify_only_refs(heads=(b'obj',), tags=[])
559 wvstart(get_disposition + ' --append tree')
560 subtree_path = src_info['subtree-path']
561 subtree_id = src_info['subtree-id']
562 for item in (b'.tag/subtree', b'src/latest' + subtree_vfs_path):
563 for existing in (None,
564 (b'.tag/commit-1', b'obj'),
565 (b'.tag/commit-2', b'obj')):
566 exr = run_get(get_disposition, b'--append', (item, b'obj'),
569 validate_new_save(b'obj/latest', b'/', None, subtree_id, subtree_path,
571 verify_only_refs(heads=(b'obj',), tags=[])
573 wvstart(get_disposition + ' --append, implicit destinations')
575 for item in (b'src', b'src/latest'):
576 exr = run_get(get_disposition, b'--append', item)
578 validate_new_save(b'src/latest', getcwd() + b'/src', commit_2_id, tree_2_id,
580 verify_only_refs(heads=(b'src',), tags=[])
582 def test_pick(get_disposition, src_info, force=False):
583 flavor = b'--force-pick' if force else b'--pick'
584 flavormsg = flavor.decode('ascii')
585 tinyfile_path = src_info['tinyfile-path']
586 subtree_vfs_path = src_info['subtree-vfs-path']
588 wvstart(get_disposition + ' ' + flavormsg + ' to root fails')
589 for item in (b'.tag/tinyfile', b'src/latest' + tinyfile_path, b'src'):
590 exr = run_get(get_disposition, flavor, (item, b'/'))
592 verify_rx(br'can only pick a commit or save', exr.err)
593 for item in (b'.tag/commit-1', b'src/latest'):
594 exr = run_get(get_disposition, flavor, (item, b'/'))
596 verify_rx(br'destination is not a tag or branch', exr.err)
597 for item in (b'.tag/subtree', b'src/latest' + subtree_vfs_path):
598 exr = run_get(get_disposition, flavor, (item, b'/'))
600 verify_rx(br'is impossible; can only --append a tree', exr.err)
602 wvstart(get_disposition + ' ' + flavormsg + ' of blob or branch fails')
603 for item in (b'.tag/tinyfile', b'src/latest' + tinyfile_path, b'src'):
604 for given, get_item in ((None, (item, b'obj')),
605 (None, (item, b'.tag/obj')),
606 ((b'.tag/tinyfile', b'.tag/obj'), (item, b'.tag/obj')),
607 ((b'.tag/tree-1', b'.tag/obj'), (item, b'.tag/obj')),
608 ((b'.tag/commit-1', b'.tag/obj'), (item, b'.tag/obj')),
609 ((b'.tag/commit-1', b'obj'), (item, b'obj'))):
610 exr = run_get(get_disposition, flavor, get_item, given=given)
612 verify_rx(br'impossible; can only pick a commit or save', exr.err)
614 wvstart(get_disposition + ' ' + flavormsg + ' of tree fails')
615 for item in (b'.tag/subtree', b'src/latest' + subtree_vfs_path):
616 for given, get_item in ((None, (item, b'obj')),
617 (None, (item, b'.tag/obj')),
618 ((b'.tag/tinyfile', b'.tag/obj'), (item, b'.tag/obj')),
619 ((b'.tag/tree-1', b'.tag/obj'), (item, b'.tag/obj')),
620 ((b'.tag/commit-1', b'.tag/obj'), (item, b'.tag/obj')),
621 ((b'.tag/commit-1', b'obj'), (item, b'obj'))):
622 exr = run_get(get_disposition, flavor, get_item, given=given)
624 verify_rx(br'impossible; can only --append a tree', exr.err)
626 save_2 = src_info['save-2']
627 commit_2_id = src_info['commit-2-id']
628 tree_2_id = src_info['tree-2-id']
629 # FIXME: these two wvstart texts?
631 wvstart(get_disposition + ' ' + flavormsg + ' commit/save to existing tag')
632 for item in (b'.tag/commit-2', b'src/' + save_2):
633 for given in ((b'.tag/tinyfile', b'.tag/obj'),
634 (b'.tag/tree-1', b'.tag/obj'),
635 (b'.tag/commit-1', b'.tag/obj')):
636 exr = run_get(get_disposition, flavor, (item, b'.tag/obj'),
639 validate_new_tagged_commit(b'obj', commit_2_id, tree_2_id,
641 verify_only_refs(heads=[], tags=(b'obj',))
643 wvstart(get_disposition + ' ' + flavormsg
644 + ' commit/save to existing tag fails')
645 for item in (b'.tag/commit-2', b'src/' + save_2):
646 for given in ((b'.tag/tinyfile', b'.tag/obj'),
647 (b'.tag/tree-1', b'.tag/obj'),
648 (b'.tag/commit-1', b'.tag/obj')):
649 exr = run_get(get_disposition, flavor, (item, b'.tag/obj'), given=given)
651 verify_rx(br'cannot overwrite existing tag', exr.err)
653 wvstart(get_disposition + ' ' + flavormsg + ' commit/save to tag')
654 for item in (b'.tag/commit-2', b'src/' + save_2):
655 exr = run_get(get_disposition, flavor, (item, b'.tag/obj'))
657 validate_clean_repo()
658 validate_new_tagged_commit(b'obj', commit_2_id, tree_2_id, exr.out)
659 verify_only_refs(heads=[], tags=(b'obj',))
661 wvstart(get_disposition + ' ' + flavormsg + ' commit/save to branch')
662 for item in (b'.tag/commit-2', b'src/' + save_2):
663 for given in (None, (b'.tag/commit-1', b'obj'), (b'.tag/commit-2', b'obj')):
664 exr = run_get(get_disposition, flavor, (item, b'obj'), given=given)
666 validate_clean_repo()
667 validate_new_save(b'obj/latest', getcwd() + b'/src',
668 commit_2_id, tree_2_id, b'src-2', exr.out)
669 verify_only_refs(heads=(b'obj',), tags=[])
671 wvstart(get_disposition + ' ' + flavormsg
672 + ' commit/save unrelated commit to branch')
673 for item in(b'.tag/commit-2', b'src/' + save_2):
674 exr = run_get(get_disposition, flavor, (item, b'obj'),
675 given=(b'unrelated-branch', b'obj'))
677 validate_clean_repo()
678 validate_new_save(b'obj/latest', getcwd() + b'/src',
679 commit_2_id, tree_2_id, b'src-2', exr.out)
680 verify_only_refs(heads=(b'obj',), tags=[])
682 wvstart(get_disposition + ' ' + flavormsg + ' commit/save ancestor to branch')
683 save_1 = src_info['save-1']
684 commit_1_id = src_info['commit-1-id']
685 tree_1_id = src_info['tree-1-id']
686 for item in (b'.tag/commit-1', b'src/' + save_1):
687 exr = run_get(get_disposition, flavor, (item, b'obj'),
688 given=(b'.tag/commit-2', b'obj'))
690 validate_clean_repo()
691 validate_new_save(b'obj/latest', getcwd() + b'/src',
692 commit_1_id, tree_1_id, b'src-1', exr.out)
693 verify_only_refs(heads=(b'obj',), tags=[])
696 wvstart(get_disposition + ' ' + flavormsg + ', implicit destinations')
697 exr = run_get(get_disposition, flavor, b'.tag/commit-2')
699 validate_clean_repo()
700 validate_new_tagged_commit(b'commit-2', commit_2_id, tree_2_id, exr.out)
701 verify_only_refs(heads=[], tags=(b'commit-2',))
703 exr = run_get(get_disposition, flavor, b'src/latest')
705 validate_clean_repo()
706 validate_new_save(b'src/latest', getcwd() + b'/src',
707 commit_2_id, tree_2_id, b'src-2', exr.out)
708 verify_only_refs(heads=(b'src',), tags=[])
710 def test_new_tag(get_disposition, src_info):
711 tinyfile_id = src_info['tinyfile-id']
712 tinyfile_path = src_info['tinyfile-path']
713 commit_2_id = src_info['commit-2-id']
714 tree_2_id = src_info['tree-2-id']
715 subtree_id = src_info['subtree-id']
716 subtree_vfs_path = src_info['subtree-vfs-path']
718 wvstart(get_disposition + ' --new-tag to root fails')
719 for item in (b'.tag/tinyfile',
720 b'src/latest' + tinyfile_path,
722 b'src/latest' + subtree_vfs_path,
726 exr = run_get(get_disposition, b'--new-tag', (item, b'/'))
728 verify_rx(br'destination for .+ must be a VFS tag', exr.err)
730 # Anything to new tag.
731 wvstart(get_disposition + ' --new-tag, blob tag')
732 for item in (b'.tag/tinyfile', b'src/latest' + tinyfile_path):
733 exr = run_get(get_disposition, b'--new-tag', (item, b'.tag/obj'))
735 validate_blob(tinyfile_id, tinyfile_id)
736 verify_only_refs(heads=[], tags=(b'obj',))
738 wvstart(get_disposition + ' --new-tag, tree tag')
739 for item in (b'.tag/subtree', b'src/latest' + subtree_vfs_path):
740 exr = run_get(get_disposition, b'--new-tag', (item, b'.tag/obj'))
742 validate_tree(subtree_id, subtree_id)
743 verify_only_refs(heads=[], tags=(b'obj',))
745 wvstart(get_disposition + ' --new-tag, committish tag')
746 for item in (b'.tag/commit-2', b'src/latest', b'src'):
747 exr = run_get(get_disposition, b'--new-tag', (item, b'.tag/obj'))
749 validate_tagged_save(b'obj', getcwd() + b'/src/', commit_2_id, tree_2_id,
751 verify_only_refs(heads=[], tags=(b'obj',))
753 # Anything to existing tag (fails).
754 for ex_type, ex_tag in (('blob', (b'.tag/tinyfile', b'.tag/obj')),
755 ('tree', (b'.tag/tree-1', b'.tag/obj')),
756 ('commit', (b'.tag/commit-1', b'.tag/obj'))):
757 for item_type, item in (('blob tag', b'.tag/tinyfile'),
758 ('blob path', b'src/latest' + tinyfile_path),
759 ('tree tag', b'.tag/subtree'),
760 ('tree path', b'src/latest' + subtree_vfs_path),
761 ('commit tag', b'.tag/commit-2'),
762 ('save', b'src/latest'),
764 wvstart(get_disposition + ' --new-tag of ' + item_type
765 + ', given existing ' + ex_type + ' tag, fails')
766 exr = run_get(get_disposition, b'--new-tag', (item, b'.tag/obj'),
769 verify_rx(br'cannot overwrite existing tag .* \(requires --replace\)',
772 # Anything to branch (fails).
773 for ex_type, ex_tag in (('nothing', None),
774 ('blob', (b'.tag/tinyfile', b'.tag/obj')),
775 ('tree', (b'.tag/tree-1', b'.tag/obj')),
776 ('commit', (b'.tag/commit-1', b'.tag/obj'))):
777 for item_type, item in (('blob tag', b'.tag/tinyfile'),
778 ('blob path', b'src/latest' + tinyfile_path),
779 ('tree tag', b'.tag/subtree'),
780 ('tree path', b'src/latest' + subtree_vfs_path),
781 ('commit tag', b'.tag/commit-2'),
782 ('save', b'src/latest'),
784 wvstart(get_disposition + ' --new-tag to branch of ' + item_type
785 + ', given existing ' + ex_type + ' tag, fails')
786 exr = run_get(get_disposition, b'--new-tag', (item, b'obj'),
789 verify_rx(br'destination for .+ must be a VFS tag', exr.err)
791 wvstart(get_disposition + ' --new-tag, implicit destinations')
792 exr = run_get(get_disposition, b'--new-tag', b'.tag/commit-2')
794 validate_tagged_save(b'commit-2', getcwd() + b'/src/', commit_2_id, tree_2_id,
796 verify_only_refs(heads=[], tags=(b'commit-2',))
798 def test_unnamed(get_disposition, src_info):
799 tinyfile_id = src_info['tinyfile-id']
800 tinyfile_path = src_info['tinyfile-path']
801 subtree_vfs_path = src_info['subtree-vfs-path']
802 wvstart(get_disposition + ' --unnamed to root fails')
803 for item in (b'.tag/tinyfile',
804 b'src/latest' + tinyfile_path,
806 b'src/latest' + subtree_vfs_path,
810 for ex_ref in (None, (item, b'.tag/obj')):
811 exr = run_get(get_disposition, b'--unnamed', (item, b'/'),
814 verify_rx(br'usage: bup get ', exr.err)
816 wvstart(get_disposition + ' --unnamed file')
817 for item in (b'.tag/tinyfile', b'src/latest' + tinyfile_path):
818 exr = run_get(get_disposition, b'--unnamed', item)
820 validate_blob(tinyfile_id, tinyfile_id)
821 verify_only_refs(heads=[], tags=[])
823 exr = run_get(get_disposition, b'--unnamed', item,
824 given=(item, b'.tag/obj'))
826 validate_blob(tinyfile_id, tinyfile_id)
827 verify_only_refs(heads=[], tags=(b'obj',))
829 wvstart(get_disposition + ' --unnamed tree')
830 subtree_id = src_info['subtree-id']
831 for item in (b'.tag/subtree', b'src/latest' + subtree_vfs_path):
832 exr = run_get(get_disposition, b'--unnamed', item)
834 validate_tree(subtree_id, subtree_id)
835 verify_only_refs(heads=[], tags=[])
837 exr = run_get(get_disposition, b'--unnamed', item,
838 given=(item, b'.tag/obj'))
840 validate_tree(subtree_id, subtree_id)
841 verify_only_refs(heads=[], tags=(b'obj',))
843 wvstart(get_disposition + ' --unnamed committish')
844 save_2 = src_info['save-2']
845 commit_2_id = src_info['commit-2-id']
846 for item in (b'.tag/commit-2', b'src/' + save_2, b'src'):
847 exr = run_get(get_disposition, b'--unnamed', item)
849 validate_commit(commit_2_id, commit_2_id)
850 verify_only_refs(heads=[], tags=[])
852 exr = run_get(get_disposition, b'--unnamed', item,
853 given=(item, b'.tag/obj'))
855 validate_commit(commit_2_id, commit_2_id)
856 verify_only_refs(heads=[], tags=(b'obj',))
858 def create_get_src():
859 global bup_cmd, src_info
861 ex((bup_cmd, b'-d', b'get-src', b'init'))
864 open(b'src/unrelated', 'a').close()
865 ex((bup_cmd, b'-d', b'get-src', b'index', b'src'))
866 ex((bup_cmd, b'-d', b'get-src', b'save', b'-tcn', b'unrelated-branch', b'src'))
868 ex((bup_cmd, b'-d', b'get-src', b'index', b'--clear'))
871 open(b'src/zero', 'a').close()
872 ex((bup_cmd, b'-d', b'get-src', b'index', b'src'))
873 exr = exo((bup_cmd, b'-d', b'get-src', b'save', b'-tcn', b'src', b'src'))
874 out = exr.out.splitlines()
876 commit_0_id = out[-1]
877 exr = exo((bup_cmd, b'-d', b'get-src', b'ls', b'src'))
878 save_0 = exr.out.splitlines()[0]
879 ex((b'git', b'--git-dir', b'get-src', b'branch', b'src-0', b'src'))
880 ex((b'cp', b'-RPp', b'src', b'src-0'))
886 ex((bup_cmd + b' -d get-src random 1k > src/1'), shell=True)
887 ex((bup_cmd + b' -d get-src random 1k > src/x/2'), shell=True)
888 ex((bup_cmd, b'-d', b'get-src', b'index', b'src'))
889 exr = exo((bup_cmd, b'-d', b'get-src', b'save', b'-tcn', b'src', b'src'))
890 out = exr.out.splitlines()
892 commit_1_id = out[-1]
893 exr = exo((bup_cmd, b'-d', b'get-src', b'ls', b'src'))
894 save_1 = exr.out.splitlines()[1]
895 ex((b'git', b'--git-dir', b'get-src', b'branch', b'src-1', b'src'))
896 ex((b'cp', b'-RPp', b'src', b'src-1'))
898 # Make a copy the current state of src so we'll have an ancestor.
900 b'get-src/refs/heads/src', b'get-src/refs/heads/src-ancestor'))
902 with open(b'src/tiny-file', 'ab') as f: f.write(b'xyzzy')
903 ex((bup_cmd, b'-d', b'get-src', b'index', b'src'))
904 ex((bup_cmd, b'-d', b'get-src', b'tick')) # Ensure the save names differ
905 exr = exo((bup_cmd, b'-d', b'get-src', b'save', b'-tcn', b'src', b'src'))
906 out = exr.out.splitlines()
908 commit_2_id = out[-1]
909 exr = exo((bup_cmd, b'-d', b'get-src', b'ls', b'src'))
910 save_2 = exr.out.splitlines()[2]
911 rename(b'src', b'src-2')
913 src_root = getcwd() + b'/src'
915 subtree_path = b'src-2/x'
916 subtree_vfs_path = src_root + b'/x'
918 # No support for "ls -d", so grep...
919 exr = exo((bup_cmd, b'-d', b'get-src', b'ls', b'-s', b'src/latest' + src_root))
920 out = exr.out.splitlines()
924 subtree_id = line.split()[0]
927 # With a tiny file, we'll get a single blob, not a chunked tree
928 tinyfile_path = src_root + b'/tiny-file'
929 exr = exo((bup_cmd, b'-d', b'get-src', b'ls', b'-s', b'src/latest' + tinyfile_path))
930 tinyfile_id = exr.out.splitlines()[0].split()[0]
932 ex((bup_cmd, b'-d', b'get-src', b'tag', b'tinyfile', tinyfile_id))
933 ex((bup_cmd, b'-d', b'get-src', b'tag', b'subtree', subtree_id))
934 ex((bup_cmd, b'-d', b'get-src', b'tag', b'tree-0', tree_0_id))
935 ex((bup_cmd, b'-d', b'get-src', b'tag', b'tree-1', tree_1_id))
936 ex((bup_cmd, b'-d', b'get-src', b'tag', b'tree-2', tree_2_id))
937 ex((bup_cmd, b'-d', b'get-src', b'tag', b'commit-0', commit_0_id))
938 ex((bup_cmd, b'-d', b'get-src', b'tag', b'commit-1', commit_1_id))
939 ex((bup_cmd, b'-d', b'get-src', b'tag', b'commit-2', commit_2_id))
940 ex((b'git', b'--git-dir', b'get-src', b'branch', b'commit-1', commit_1_id))
941 ex((b'git', b'--git-dir', b'get-src', b'branch', b'commit-2', commit_2_id))
943 return {'tinyfile-path' : tinyfile_path,
944 'tinyfile-id' : tinyfile_id,
945 'subtree-id' : subtree_id,
946 'tree-0-id' : tree_0_id,
947 'tree-1-id' : tree_1_id,
948 'tree-2-id' : tree_2_id,
949 'commit-0-id' : commit_0_id,
950 'commit-1-id' : commit_1_id,
951 'commit-2-id' : commit_2_id,
954 'subtree-path' : subtree_path,
955 'subtree-vfs-path' : subtree_vfs_path}
957 # FIXME: this fails in a strange way:
958 # WVPASS given nothing get --ff not-there
960 dispositions_to_test = ('get',)
962 if int(environ.get(b'BUP_TEST_LEVEL', b'0')) >= 11:
963 dispositions_to_test += ('get-on', 'get-to')
965 if len(sys.argv) == 1:
966 categories = ('replace', 'universal', 'ff', 'append', 'pick', 'new-tag',
969 categories = sys.argv[1:]
971 with test_tempdir(b'get-') as tmpdir:
974 src_info = create_get_src()
975 for category in categories:
976 for disposition in dispositions_to_test:
977 # given=FOO depends on --replace, so test it early
978 if category == 'replace':
979 test_replace(disposition, src_info)
980 elif category == 'universal':
981 test_universal_behaviors(disposition)
982 elif category == 'ff':
983 test_ff(disposition, src_info)
984 elif category == 'append':
985 test_append(disposition, src_info)
986 elif category == 'pick':
987 test_pick(disposition, src_info, force=False)
988 test_pick(disposition, src_info, force=True)
989 elif category == 'new-tag':
990 test_new_tag(disposition, src_info)
991 elif category == 'unnamed':
992 test_unnamed(disposition, src_info)
994 raise Exception('unrecognized get test category')
995 except Exception as ex:
1000 wvmsg('checked %d cases' % get_cases_tested)