~james/remy

remy/tests/cli.py -rw-r--r-- 6.1 KiB
2d73ca56 — james chore(import): snapshot james.dad.town/remy at 76b7f307a15a 16 hours 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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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
        invoke(source,binary)
        assert int(invoke('simulate',binary,'0xFF','0x1','0x0')) == 256
        invoke('simulate',binary,'invalid','0','0',ok=False)
        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
        assert 'push' not in listing and 'pop' not in listing
        source.write_text('(- x (- y (- t x)))')
        invoke(source,binary)
        listing = invoke('disassemble',binary)
        assert 'push rax' in listing and 'pop rcx' in listing
        source.write_text('(if x (+ y 7) (- t 9))')
        invoke(source,binary)
        listing = invoke('disassemble',binary)
        assert 'jz ' in listing and 'jmp ' in listing and 'conditional ' not in listing
        invoke('check',binary)
        original = binary.read_bytes()
        branch = original.index(bytes.fromhex('48 85 c0 0f 84'))
        for offset in (0,4,0x7fffffff,0xffffffff):
            damaged = bytearray(original)
            damaged[branch+5:branch+9] = offset.to_bytes(4,'little')
            binary.write_bytes(damaged)
            invoke('check',binary,ok=False)
        for cut in range(branch+1,branch+9):
            binary.write_bytes(original[:cut]+bytes([195]))
            invoke('check',binary,ok=False)
        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()