~james/remy

4
175b8b22df89336d3a03eb1209d760f6f426315b
main
remy/tests/cli.py -rw-r--r--5.0 KiB ViewLogBlameView rawPermalink 14346e1128 feat(cli): accept source input from standard input 19 days ago
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
from pathlib import Path
import os
import resource
import signal
import subprocess
import tempfile


ROOT = Path(__file__).resolve().parents[1]


def invoke(*args, ok=True, data=None, preexec_fn=None):
    result = subprocess.run([str(ROOT / '.build/remy'), *map(str, args)], input=data,
        capture_output=True, text=True, timeout=30, preexec_fn=preexec_fn)
    assert (result.returncode == 0) == ok, (args, result.stdout, result.stderr)
    return result.stdout if ok else result.stderr


def main():
    with tempfile.TemporaryDirectory() as directory:
        source, binary = Path(directory) / 'source', Path(directory) / 'binary'
        for text, expected in [('0x0',0),('0xff',255),('0X7F',127),('0xffffffffffffffff',(1 << 64)-1),
            ('(+ 0xffffffffffffffff 1)',0),('(< 0x8000000000000000 0xffffffffffffffff)',1)]:
            source.write_text(text)
            assert int(invoke('eval',source,'0','0','0')) == expected, text
        source.write_text('(+ x y)')
        assert int(invoke('eval',source,'0xFF','0x1','0x0')) == 256
        source.write_text('(let x1 7 (let x1 (+ x1 2) (* x1 x1)))')
        assert int(invoke('eval',source,'0','0','0')) == 81
        source.write_text('(let 1x 3 1x)')
        invoke(source,binary,ok=False)
        for text in ['(+ x; ignore ) tokens\ny) ; end', '(+ x; comment\r\ny)', '(+ x y);end']:
            source.write_text(text)
            assert int(invoke('eval',source,'3','4','0')) == 7, text
        for text, position in [('(+ x)',3),('(div x y)',1),('(let a 3 b)',4),('(+ x y',4),('(+ x y) 1',5)]:
            source.write_text(text)
            assert f'token {position}\n' in invoke(source,binary,ok=False), text
        source.write_text('(+ 1 ' * 129 + '0' + ')' * 129)
        assert 'parse depth limit' in invoke(source,binary,ok=False)
        large = '0'
        for _ in range(12):
            large = f'(+ {large} {large})'
        source.write_text(large)
        assert '4096 nodes' in invoke(source,binary,ok=False)
        source.write_text('(+ x y)')
        invoke('limits',4,3,2,source,binary)
        for depth, nodes, stack, message in [(4,2,2,'2 nodes'),(4,3,1,'1 words'),
            (0,3,2,'parse depth'),(129,3,2,'compiler bounds'),('bad',3,2,'invalid resource')]:
            binary.write_bytes(b'preserve')
            assert message in invoke('limits',depth,nodes,stack,source,binary,ok=False)
            assert binary.read_bytes() == b'preserve'
        source.write_text('(+ x y)')
        invoke(source,binary)
        listing = invoke('disassemble',binary)
        assert 'mov rax r9' in listing and 'arithmetic add' in listing and 'ret' in listing
        for malformed in [b'',bytes([195,0]),bytes([72,199,192,0]),bytes([80])]:
            binary.write_bytes(malformed)
            assert 'invalid native' in invoke('disassemble',binary,ok=False)
        binary.write_bytes(bytes([72,184,42,0,0,0,0,0,0,0,195]))
        assert '10  ret' in invoke('disassemble',binary)
        source.write_text('(+ x y)')
        invoke(source,binary)
        assert 'verified' in invoke('check',binary)
        for code in [[88,195],[80,195],[65,88,195],[73,137,192,195],[72,139,68,36,0,195]]:
            binary.write_bytes(bytes(code))
            invoke('check',binary,ok=False)
        binary.write_bytes(bytes([72,199,192,42,0,0,0,195]))
        invoke('check',binary)
        source.write_text('(+ x y)')
        binary.write_bytes(b'preserve')
        binary.chmod(0o600)
        invoke(source,binary,preexec_fn=lambda: os.umask(0))
        assert binary.stat().st_mode & 0o777 == 0o600
        def restricted():
            signal.signal(signal.SIGXFSZ,signal.SIG_IGN)
            resource.setrlimit(resource.RLIMIT_FSIZE,(1,1))
        for args in [(source,binary),('vectors',source,binary)]:
            binary.write_bytes(b'preserve')
            invoke(*args,ok=False,preexec_fn=restricted)
            assert binary.read_bytes() == b'preserve'
            assert not list(binary.parent.glob('.binary.*'))
        target = Path(directory) / 'folder'
        target.mkdir()
        invoke(source,target,ok=False)
        assert not list(target.parent.glob('.folder.*'))
        source.write_text('(+ x y)')
        invoke(source,source)
        assert source.read_bytes().endswith(bytes([195]))
        invoke('-',binary,data='(+ x y)')
        assert int(invoke('eval','-',3,4,0,data='(+ x y)')) == 7
        invoke('vectors','-',binary,data='(+ x y)')
        assert binary.stat().st_size == 16384
        for data in ['(+ x)', ' ' * 65537]:
            binary.write_bytes(b'preserve')
            invoke('-',binary,data=data,ok=False)
            assert binary.read_bytes() == b'preserve'
        source.write_text('é')
        assert 'ASCII' in invoke(source,binary,ok=False)
        for text in ['0x','0x10000000000000000','0xfg','0xffextra','-0x1']:
            source.write_text(text)
            binary.write_bytes(b'preserve')
            invoke(source,binary,ok=False)
            assert binary.read_bytes() == b'preserve', text
    print('CLI literal and rejection checks passed')


if __name__ == '__main__':
    main()