tricu

A lazy functional language based on Tree Calculus
Log | Files | Refs | README | LICENSE

test_portable.py (4703B)


      1 #!/usr/bin/env python3
      2 """Exercise a relocated release under an empty mount root with no Nix store."""
      3 import argparse
      4 import json
      5 from pathlib import Path
      6 import shutil
      7 import subprocess
      8 import tempfile
      9 import tarfile
     10 
     11 
     12 def main():
     13     p = argparse.ArgumentParser(description=__doc__)
     14     p.add_argument('--release', type=Path, required=True)
     15     p.add_argument('--system', type=Path, required=True)
     16     p.add_argument('--source', type=Path, required=True)
     17     p.add_argument('--worker', type=Path, required=True)
     18     p.add_argument('--copier', type=Path, required=True)
     19     p.add_argument('--output', type=Path, required=True)
     20     a = p.parse_args()
     21     a.output.mkdir(parents=True, exist_ok=False)
     22     with tempfile.TemporaryDirectory() as tmp:
     23         tmp = Path(tmp)
     24         tests = tmp/'tests'
     25         tests.mkdir()
     26         for name in ('test_embedding.py', 'test_scopes.py'):
     27             shutil.copyfile(a.source/'ext/haskell'/name, tests/name)
     28         for name in ('python_filecheck.py', 'process_rss.py'):
     29             shutil.copyfile(a.source/'test'/name, tests/name)
     30         (tests/'fixtures').mkdir()
     31         for name in ('id.arboricx', 'append.arboricx'):
     32             shutil.copyfile(a.source/'test/fixtures'/name, tests/'fixtures'/name)
     33         shutil.copyfile(a.copier, tests/'copy.arboricx')
     34         shutil.copytree(a.source/'demos/python-filecheck/tricu_filecheck', tests/'tricu_filecheck')
     35         for path in [tests/'tricu_filecheck', *(tests/'tricu_filecheck').rglob('*')]:
     36             path.chmod(0o755 if path.is_dir() else 0o644)
     37         shutil.copyfile(a.worker/'worker.arboricx', tests/'tricu_filecheck/worker.arboricx')
     38         pin = (a.worker/'worker.sha256').read_text().strip()
     39         (tests/'tricu_filecheck/_bundle_pin.py').write_text(f'BUNDLE_SHA256 = "{pin}"\n')
     40         reports = []
     41         for location in ('first', 'relocated path with spaces'):
     42             release = tmp/location
     43             with tarfile.open(a.release, 'r:gz') as archive:
     44                 archive.extractall(tmp/'unpacked', filter='data')
     45             (tmp/'unpacked/tricu').rename(release)
     46             mount = '/'+location
     47             shutil.copyfile(a.source/'ext/haskell/portable_workload.py', tests/'run.py')
     48             (release/'lib').chmod(0o755)
     49             import hashlib
     50             inventory = json.loads((release/'share/tricu/portable.json').read_text())
     51             for name, row in inventory['libraries'].items():
     52                 assert hashlib.sha256((release/'lib'/name).read_bytes()).hexdigest() == row['sha256']
     53             work = tmp/(location+'-work')
     54             work.mkdir()
     55             command = ['bwrap', '--unshare-all', '--die-with-parent', '--new-session',
     56                        '--ro-bind', str(a.system/'python'), '/python',
     57                        '--ro-bind', str(a.system/'system'), '/system',
     58                        '--ro-bind', str(release), mount,
     59                        '--ro-bind', str(tests), '/tests',
     60                        '--bind', str(work), '/work', '--tmpfs', '/tmp',
     61                        '--proc', '/proc', '--dev', '/dev', '--chdir', '/work',
     62                        '--clearenv', '--setenv', 'HOME', '/tmp',
     63                        '--setenv', 'PATH', '/no-runtime-tools',
     64                        '--setenv', 'PYTHONHOME', '/python',
     65                        '--setenv', 'LD_LIBRARY_PATH', '/system/lib',
     66                        '--setenv', 'PYTHONDONTWRITEBYTECODE', '1',
     67                        '--setenv', 'PYTHONPATH', mount+'/share/tricu:/tests',
     68                        '/python/bin/python3', '/tests/run.py', mount]
     69             result = subprocess.run(command, text=True, capture_output=True, timeout=240)
     70             (a.output/(location+'.log')).write_text(result.stdout+result.stderr)
     71             if result.returncode:
     72                 raise RuntimeError(f'exit {result.returncode}: {result.stderr or result.stdout}')
     73             reports.append(json.loads((work/'report.json').read_text()))
     74             # Removing a required bundled dependency must fail before initialization.
     75             (release/'lib/libgmp.so.10').unlink()
     76             negative = subprocess.run(command[:-2]+['-c', 'from tricu import Library; Library()'],
     77                                       text=True, capture_output=True, timeout=30)
     78             assert negative.returncode and 'libgmp.so.10' in negative.stderr, negative
     79         (a.output/'report.json').write_text(json.dumps(dict(
     80             schema='tricu.portable-acceptance.v1', runs=reports,
     81             missing_dependency_rejected=True,
     82             scope='Nix-free mount namespace; pinned relocated Python/glibc test OS; not cross-distro acceptance'),
     83             indent=2)+'\n')
     84 
     85 
     86 if __name__ == '__main__':
     87     main()