tricu

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

container_bindings.py (3794B)


      1 """Distro Python/PHP and upstream Node conformance against one relocated host."""
      2 import json
      3 import os
      4 from pathlib import Path
      5 import shutil
      6 import subprocess
      7 import sys
      8 
      9 
     10 def checked(command, env=None):
     11     result = subprocess.run(command, text=True, capture_output=True, timeout=90, env=env)
     12     if result.returncode:
     13         raise RuntimeError(f'{command[0]} failed ({result.returncode}): {result.stdout}\n{result.stderr}')
     14     return result.stdout.strip()
     15 
     16 
     17 def run(release, node_version):
     18     source = Path('/work/bindings')
     19     if not source.exists():
     20         shutil.copytree('/kit/bindings', source)
     21         (source/'ext/js/node_modules').symlink_to('/opt/tricu-js/node_modules')
     22     # PHP consumes the transferred header.
     23     shutil.copyfile(release/'include/tricu.h', source/'ext/php/tricu.h')
     24     node = '/opt/node/bin/node'
     25     actual_node = checked([node, '--version'])
     26     if actual_node != 'v'+node_version:
     27         raise ValueError('upstream Node version differs from image lock')
     28     php = ['/usr/bin/php', '-d', 'ffi.enable=true']
     29     php_info = json.loads(checked(php+['-r', 'echo json_encode(["version"=>PHP_VERSION,"ffi"=>extension_loaded("FFI"),"ffi_enable"=>ini_get("ffi.enable"),"integer_bytes"=>PHP_INT_SIZE]);']))
     30     if not php_info['ffi'] or php_info['integer_bytes'] != 8:
     31         raise ValueError('64-bit distro PHP with FFI required')
     32     koffi = json.loads(Path('/opt/tricu-js/node_modules/koffi/package.json').read_text())['version']
     33     locked = json.loads((source/'ext/js/package-lock.json').read_text())['packages']['node_modules/koffi']['version']
     34     if koffi != locked:
     35         raise ValueError('installed Koffi differs from package lock')
     36     specification = source/'ext/haskell/bindings/fixtures.json'
     37     expected = json.loads(specification.read_text())['expected']
     38     arguments = [str(release/'lib/libtricu-haskell.so'), '/tests/fixtures', str(specification), '/tests/copy.arboricx']
     39     js = '''import {readFileSync} from 'node:fs';
     40 import {pathToFileURL} from 'node:url';
     41 import {strict as assert} from 'node:assert';
     42 const {conformance} = await import(pathToFileURL(process.argv[1]));
     43 const report = conformance(...process.argv.slice(2));
     44 const maps = readFileSync('/proc/self/maps','utf8');
     45 assert(!maps.includes('/nix/store/'));
     46 assert(maps.includes(process.argv[2]));
     47 assert(maps.includes('/opt/tricu-js/node_modules/koffi/'));
     48 console.log(JSON.stringify(report));'''
     49     php_script = '''array_shift($argv); require $argv[0];
     50 $maps = file_get_contents('/proc/self/maps');
     51 if (str_contains($maps, '/nix/store/') || !str_contains($maps, $argv[1])) {
     52     throw new RuntimeException('unexpected native mappings');
     53 }'''
     54     commands = {
     55         'python': [sys.executable, str(source/'ext/haskell/bindings/python.py')],
     56         'js': [node, '--input-type=module', '-e', js, str(source/'ext/js/test/conformance.mjs')],
     57         'php': php+['-r', php_script, str(source/'ext/php/tests/conformance.php')],
     58     }
     59     env = dict(os.environ, PYTHONPATH=str(release/'share/tricu'), PYTHONDONTWRITEBYTECODE='1')
     60     results = {}
     61     for language, command in commands.items():
     62         actual = json.loads(checked(command+arguments, env))
     63         if actual != expected:
     64             raise ValueError(f'{language}: independent conformance expectations differ')
     65         results[language] = actual
     66     return dict(passed=list(results), results=results, node=actual_node, koffi=koffi, php=php_info,
     67                 provenance=dict(node='upstream official Node image, exact version and registry digest',
     68                                 php='distro package with process-local ffi.enable=true',
     69                                 koffi='npm ci from committed package lock'),
     70                 scope='CLI bindings, codecs, ownership, error controls and compiled copier IO')