diff options
Diffstat (limited to 'tests/drivers')
38 files changed, 255 insertions, 159 deletions
diff --git a/tests/drivers/actor_image_test.py b/tests/drivers/actor_image_test.py index 9df8828..5b815f2 100644 --- a/tests/drivers/actor_image_test.py +++ b/tests/drivers/actor_image_test.py @@ -1,12 +1,16 @@ #!/usr/bin/env python3 # source syntax callbacks execute in the host while actor code is emitted separately. from pathlib import Path +import sys import argparse import subprocess import tempfile +sys.dont_write_bytecode = True # importing imagekit must not leave __pycache__ in the tree +from imagekit import micro_image parser = argparse.ArgumentParser() -parser.add_argument('--host', default='bin/zy') +parser.add_argument('--host', default='build/bin/zy') +parser.add_argument('--mkgenome', default='build/bin/mkgenome') parser.add_argument('--case', default='') args = parser.parse_args() prefix = b''.join(Path(f).read_bytes() + b'\n' for f in ('src/runtime/rt.zc', 'src/core/core.zc', 'src/runtime/run.zc')) @@ -18,11 +22,8 @@ for name, source, expected in cases: directory = Path(work) compiled = subprocess.run([args.host], input=Path('src/tools/build.zc').read_bytes() + b'`' + prefix + source.encode(), capture_output=True, timeout=30) assert compiled.returncode == 0 and len(compiled.stdout) >= 56 and not compiled.stdout.startswith(b'zc:'), (name, compiled.stdout[:2000], compiled.stderr) - header = subprocess.run(['bin/mkgenome'], input=compiled.stdout, capture_output=True, check=True) - (directory / 'genome.h').write_bytes(header.stdout) - for f in ('src/core/memmap.h', 'src/core/fops.h', 'src/core/bops.h', 'src/core/stack.h', 'src/micro/micro.c', 'src/micro/micro_port.h', 'src/micro/port_linux.c'): - (directory / Path(f).name).write_bytes(Path(f).read_bytes()) - subprocess.run(['cc', '-O2', '-w', '-fwrapv', '-pthread', '-DZY_THREADS', str(directory / 'micro.c'), str(directory / 'port_linux.c'), '-I' + work, '-o', str(directory / 'image')], check=True, timeout=30) - result = subprocess.run([str(directory / 'image')], capture_output=True, timeout=12) + image = subprocess.run([args.mkgenome], input=compiled.stdout, capture_output=True, check=True) + binary = micro_image(directory, image.stdout) + result = subprocess.run([str(binary)], capture_output=True, timeout=12) assert result.returncode == 0 and result.stdout == expected, (name, result.returncode, result.stdout, result.stderr) print('ok actor compiler-free image: ' + name) diff --git a/tests/drivers/actor_test.py b/tests/drivers/actor_test.py index 43a110f..0cfa51f 100644 --- a/tests/drivers/actor_test.py +++ b/tests/drivers/actor_test.py @@ -6,7 +6,7 @@ import subprocess import time parser = argparse.ArgumentParser() -parser.add_argument('vm', nargs='?', default='bin/zy') +parser.add_argument('vm', nargs='?', default='build/bin/zy') parser.add_argument('--case', default='') parser.add_argument('--library', default='') args = parser.parse_args() diff --git a/tests/drivers/analysis_cache_test.js b/tests/drivers/analysis_cache_test.js index 3ffde33..88bb4bd 100644 --- a/tests/drivers/analysis_cache_test.js +++ b/tests/drivers/analysis_cache_test.js @@ -1,4 +1,6 @@ // Repeated replies use cached results; edits, include updates and reopen invalidate them. +// interpreted hosts (no jit) are far slower; ZC_TEST_TIMEOUT_SCALE stretches every deadline +const TIMEOUT_SCALE = Number(process.env.ZC_TEST_TIMEOUT_SCALE) || 1; const assert = require('assert'), cp = require('child_process'), path = require('path'); const uri = 'file:///cache/main.zc', text = 'include "dep.zc";\nfn main() { int n = 7; return n + value(); }'; const msgs = []; let id = 1; @@ -26,7 +28,7 @@ if (process.argv[2] === '--wasm' || process.argv[2] === '--vm') { const p = (wasm ? host.wasmProcess : host.vmProcess)(path.resolve(process.argv[3]), { log: () => {} }); const chunks = []; p.stdout.on('data', d => chunks.push(d)); msgs.forEach(m => p.stdin.write(frame(m))); out = Buffer.concat(chunks); } else { - const r = cp.spawnSync(process.argv[2], process.argv.slice(3), { input: Buffer.concat(msgs.map(frame)), timeout: 60000, maxBuffer: 16 << 20 }); + const r = cp.spawnSync(process.argv[2], process.argv.slice(3), { input: Buffer.concat(msgs.map(frame)), timeout: 60000 * TIMEOUT_SCALE, maxBuffer: 16 << 20 }); assert.strictEqual(r.status, 0, String(r.stderr)); out = r.stdout; } const replies = new Map(); diff --git a/tests/drivers/array_methods_test.py b/tests/drivers/array_methods_test.py index 82fef67..bf6e670 100644 --- a/tests/drivers/array_methods_test.py +++ b/tests/drivers/array_methods_test.py @@ -5,7 +5,7 @@ import re import subprocess parser = argparse.ArgumentParser() -parser.add_argument('host', nargs='?', default='bin/zy') +parser.add_argument('host', nargs='?', default='build/bin/zy') parser.add_argument('--case', default='') args = parser.parse_args() diff --git a/tests/drivers/cli_help_test.py b/tests/drivers/cli_help_test.py index 1b32fa4..97eebea 100644 --- a/tests/drivers/cli_help_test.py +++ b/tests/drivers/cli_help_test.py @@ -7,15 +7,16 @@ from pathlib import Path parser = argparse.ArgumentParser() parser.add_argument('--group', choices=['binaries', 'scripts', 'js', 'all'], default='all') -parser.add_argument('--bin-dir', default='bin') +parser.add_argument('--bin-dir', default='build/bin') +parser.add_argument('--js-dir', default='build/js') args = parser.parse_args() commands = [] if args.group in ('binaries', 'all'): commands += [[str(Path(args.bin_dir) / name)] for name in ['zy', 'zym', 'zclsp', 'zc0', 'mkgenome', 'genmap']] if args.group in ('scripts', 'all'): - commands += [['bash', name] for name in ['tinyvm.sh', 'src/tools/zcc', 'src/tools/automap.sh', 'src/tools/embed.sh', 'src/tools/mklsp.sh', 'src/tools/mklspwasm.sh', 'src/tools/mkvsix.sh', 'src/web/build.sh', 'src/baremetal/run.sh']] + commands += [['bash', name] for name in ['tinyvm.sh', 'src/tools/zcc', 'src/tools/automap.sh', 'src/tools/findcc.sh', 'src/tools/bootstrap-check.sh', 'src/tools/mklsp.sh', 'src/tools/mklspwasm.sh', 'src/tools/mkvsix.sh', 'src/web/build.sh', 'src/baremetal/run.sh']] if args.group in ('js', 'all'): - commands += [['node', name] for name in ['src/js/tinyvm.js', 'src/js/extract_genome.js', 'src/js/extract_lsp.js']] + commands += [['node', name] for name in [str(Path(args.js_dir) / 'tinyvm.js'), 'src/js/mklayout.js', 'src/js/extract_lsp.js']] def invoke(command, option): # keep stdin open: help and rejected options must finish without reading it. diff --git a/tests/drivers/completion_test.js b/tests/drivers/completion_test.js index 22c5fa5..f1ee5e6 100644 --- a/tests/drivers/completion_test.js +++ b/tests/drivers/completion_test.js @@ -1,5 +1,7 @@ // compiler-backed completion, including incomplete source and position-aware lifetimes. // usage: node tests/drivers/completion_test.js path/to/zclsp [server arguments] +// interpreted hosts (no jit) are far slower; ZC_TEST_TIMEOUT_SCALE stretches every deadline +const TIMEOUT_SCALE = Number(process.env.ZC_TEST_TIMEOUT_SCALE) || 1; const assert = require('assert'); const cp = require('child_process'); const path = require('path'); @@ -25,7 +27,7 @@ function complete(source, extra = []) { for (const m of msgs) proc.stdin.write(frame(m)); output = Buffer.concat(chunks); } else { - const r = cp.spawnSync(process.argv[2], process.argv.slice(3), { input: Buffer.concat(msgs.map(frame)), timeout: 30000, maxBuffer: 32 << 20 }); + const r = cp.spawnSync(process.argv[2], process.argv.slice(3), { input: Buffer.concat(msgs.map(frame)), timeout: 30000 * TIMEOUT_SCALE, maxBuffer: 32 << 20 }); assert.strictEqual(r.status, 0, String(r.stderr)); output = r.stdout; } let b = output; const replies = []; diff --git a/tests/drivers/core_lsp_test.js b/tests/drivers/core_lsp_test.js index 983f9aa..c3c01aa 100644 --- a/tests/drivers/core_lsp_test.js +++ b/tests/drivers/core_lsp_test.js @@ -1,4 +1,6 @@ // check maintained zc sources through the editor document protocol, one file at a time. +// interpreted hosts (no jit) are far slower; ZC_TEST_TIMEOUT_SCALE stretches every deadline +const TIMEOUT_SCALE = Number(process.env.ZC_TEST_TIMEOUT_SCALE) || 1; const assert = require('assert'); const fs = require('fs'); const cp = require('child_process'); @@ -23,7 +25,7 @@ for (const file of files) { { jsonrpc: '2.0', id: 3, method: 'shutdown', params: null }, { jsonrpc: '2.0', method: 'exit' }, ]; - const result = cp.spawnSync(target, [], { input: Buffer.concat(messages.map(frame)), timeout: 30000, maxBuffer: 32 << 20 }); + const result = cp.spawnSync(target, [], { input: Buffer.concat(messages.map(frame)), timeout: 30000 * TIMEOUT_SCALE, maxBuffer: 32 << 20 }); assert.strictEqual(result.status, 0, file + ': ' + result.stderr); let data = result.stdout; const replies = []; diff --git a/tests/drivers/enhancement_backends_test.py b/tests/drivers/enhancement_backends_test.py index b7d553b..ddfee1b 100644 --- a/tests/drivers/enhancement_backends_test.py +++ b/tests/drivers/enhancement_backends_test.py @@ -9,7 +9,7 @@ source = (root / 'tests/programs/enhance.zc').read_text() source = source.replace('i < 5000', 'i < 200') source += '\n' reference = None -for command in [['bin/zy'], ['bin/zym'], ['node', 'src/js/tinyvm.js']]: +for command in [['build/bin/zy'], ['build/bin/zym'], ['node', 'src/js/tinyvm.js']]: result = subprocess.run(command, input=source, text=True, capture_output=True, timeout=25) assert result.returncode == 0 and result.stdout and 'zc:' not in result.stdout, (command, result.returncode, result.stdout, result.stderr) if reference is None: @@ -20,7 +20,7 @@ for command in [['bin/zy'], ['bin/zym'], ['node', 'src/js/tinyvm.js']]: source = '''o3 int increment(int value) { return value + 1; } fn main() { put_n(increment(6)); put_c(32); put_n(17 / 5); put_c(32); put_n(17 % 5); put_c(10); return 0; } ''' -for command in [['bin/zy'], ['bin/zym'], ['node', 'src/js/tinyvm.js']]: +for command in [['build/bin/zy'], ['build/bin/zym'], ['node', 'src/js/tinyvm.js']]: result = subprocess.run(command, input=source, text=True, capture_output=True, timeout=15) assert result.returncode == 0 and result.stdout == '7 3 2\n', (command, result.stdout, result.stderr) print('ok optimizer descriptors preserve portable division slots: ' + command[0]) @@ -39,7 +39,7 @@ fn main() { put_n(installed); put_c(32); put_n(first); put_c(32); put_n(rejected); put_c(32); put_n(second); put_c(10); return 0; } ''' -for command in [['bin/zy'], ['bin/zym'], ['node', 'src/js/tinyvm.js']]: +for command in [['build/bin/zy'], ['build/bin/zym'], ['node', 'src/js/tinyvm.js']]: result = subprocess.run(command, input=source, text=True, capture_output=True, timeout=15) assert result.returncode == 0 and result.stdout == '1 22 1 22\n', (command, result.stdout, result.stderr) print('ok replacing a built-in slot and rejecting a bad recipe: ' + command[0]) diff --git a/tests/drivers/ext_cache_test.js b/tests/drivers/ext_cache_test.js index f389fcc..93caf94 100644 --- a/tests/drivers/ext_cache_test.js +++ b/tests/drivers/ext_cache_test.js @@ -5,14 +5,25 @@ // starts; a change to an included file is checked again; putting it back is // answered from the cache. // -// node tests/drivers/ext_cache_test.js +// node tests/drivers/ext_cache_test.js [server-dir] +// +// the server directory holds what the packaged extension carries in server/: +// lsp.wasm, or tinyvm.js, layout.js, lsp.json and lsp.bin (the default is +// build/editors/vscode/server, which src/tools/mkvsix.sh fills). the extension is +// copied with that server into a scratch directory, since the tree has none. const fs = require('fs'); const os = require('os'); const path = require('path'); const Module = require('module'); -const client = path.join(__dirname, '..', '..', 'editors', 'vscode', 'client'); +const serverDir = path.resolve(process.argv[2] || path.join(__dirname, '..', '..', 'build', 'editors', 'vscode', 'server')); +if (!fs.existsSync(serverDir)) { console.log('FAIL no server directory at ' + serverDir); process.exit(1); } +const staged = fs.mkdtempSync(path.join(os.tmpdir(), 'zc-extension-')); +fs.cpSync(path.join(__dirname, '..', '..', 'editors', 'vscode'), staged, { recursive: true }); +fs.cpSync(serverDir, path.join(staged, 'server'), { recursive: true }); +process.on('exit', () => fs.rmSync(staged, { recursive: true, force: true })); +const client = path.join(staged, 'client'); // the vscode module, as far as the client uses it const world = { lines: [], sets: [], listeners: {}, watchers: [], textDocuments: [], saved: undefined, root: '' }; diff --git a/tests/drivers/float_backends_test.py b/tests/drivers/float_backends_test.py index cdcf48b..65901e2 100644 --- a/tests/drivers/float_backends_test.py +++ b/tests/drivers/float_backends_test.py @@ -4,7 +4,7 @@ import pathlib import subprocess root = pathlib.Path(__file__).resolve().parents[2] -commands = [['bin/zy'], ['bin/zym'], ['node', 'src/js/tinyvm.js']] +commands = [['build/bin/zy'], ['build/bin/zym'], ['node', 'src/js/tinyvm.js']] sources = [(root / 'tests/programs/float.zc').read_text(), '''include rt; fn render(bits, precision, mode) { asm { bits @ precision @ mode @ #1236 } return 0; } fn convert(bits) { return asm { bits @ #1218 }; } fn main() { diff --git a/tests/drivers/fuzz_compiler.py b/tests/drivers/fuzz_compiler.py index 512ec26..0fe0d27 100644 --- a/tests/drivers/fuzz_compiler.py +++ b/tests/drivers/fuzz_compiler.py @@ -1,17 +1,23 @@ #!/usr/bin/env python3 """Deterministic grammar fuzzing for the parser, runtime, and ownership paths. -Usage: python3 tests/drivers/fuzz_compiler.py [path/to/zy] [path/to/zym] [--js] +Usage: python3 tests/drivers/fuzz_compiler.py [path/to/zy] [path/to/zym] [--js [path/to/tinyvm.js]] +(--js alone runs build/js/tinyvm.js, from make js) """ import random import subprocess import sys -args = [arg for arg in sys.argv[1:] if arg != "--js"] -js = "--js" in sys.argv[1:] -zy = args[0] if len(args) > 0 else "bin/zy" -zym = args[1] if len(args) > 1 else "bin/zym" +argv = sys.argv[1:] +js = None +if "--js" in argv: + k = argv.index("--js") + del argv[k] + js = argv.pop(k) if k < len(argv) and argv[k].endswith(".js") else "build/js/tinyvm.js" +args = argv +zy = args[0] if len(args) > 0 else "build/bin/zy" +zym = args[1] if len(args) > 1 else "build/bin/zym" rng = random.Random(0x5A17) values = [] library_values = [] @@ -33,26 +39,26 @@ def expr(depth): return f"({left} {op} {right})", value -lines = ["include std;", "fn churn(n) {", " int i = 0;", " while (i < n) {", " { int* p = new(int); *p = i; }", " i++;", " }", " return 0;", "}", "fn stable() { return 73; }", "fn main() {", " let base = live_objects();"] +lines = ["namespace;", "include rt, run, std;", "fn churn(n) {", " int i = 0;", " while (i < n) {", " { int* p = rt.new(int); *p = i; }", " i++;", " }", " return 0;", "}", "fn stable() { return 73; }", "fn main() {", " let base = rt.live_objects();"] for _ in range(128): source, value = expr(4) - lines.append(f" put_n({source}); put_c(32);") + lines.append(f" rt.put_n({source}); rt.put_c(32);") values.append(value) for _ in range(32): a = "".join(rng.choice("abc") for _ in range(rng.randrange(11))) b = "".join(rng.choice("abc") for _ in range(rng.randrange(6))) - lines.append(f' put_n(Text.len("{a}")); put_c(32); put_n(Text.eq("{a}", "{b}")); put_c(32);') - lines.append(f' put_n(Text.starts("{a}", "{b}")); put_c(32); put_n(Text.ends("{a}", "{b}")); put_c(32); put_n(Text.find("{a}", "{b}")); put_c(32);') - lines.append(f' put_n(Bytes.len(b"{a}")); put_c(32); put_n(Bytes.eq(b"{a}", b"{b}")); put_c(32);') - lines.append(f' put_n(Bytes.starts(b"{a}", b"{b}")); put_c(32); put_n(Bytes.ends(b"{a}", b"{b}")); put_c(32); put_n(Bytes.find(b"{a}", b"{b}")); put_c(32);') + lines.append(f' rt.put_n(std.Text.len("{a}")); rt.put_c(32); rt.put_n(std.Text.eq("{a}", "{b}")); rt.put_c(32);') + lines.append(f' rt.put_n(std.Text.starts("{a}", "{b}")); rt.put_c(32); rt.put_n(std.Text.ends("{a}", "{b}")); rt.put_c(32); rt.put_n(std.Text.find("{a}", "{b}")); rt.put_c(32);') + lines.append(f' rt.put_n(std.Bytes.len(b"{a}")); rt.put_c(32); rt.put_n(std.Bytes.eq(b"{a}", b"{b}")); rt.put_c(32);') + lines.append(f' rt.put_n(std.Bytes.starts(b"{a}", b"{b}")); rt.put_c(32); rt.put_n(std.Bytes.ends(b"{a}", b"{b}")); rt.put_c(32); rt.put_n(std.Bytes.find(b"{a}", b"{b}")); rt.put_c(32);') library_values += [len(a), int(a == b), int(a.startswith(b)), int(a.endswith(b)), a.find(b), len(a.encode()), int(a == b), int(a.startswith(b)), int(a.endswith(b)), a.find(b)] lines += [ - " churn(300); gc(); put_n(live_objects() - base); put_c(32);", - ' char* source = "fn f() { return 7; }"; let parsed = tokens(source);', - ' put_n(parsed[0] > 0); put_c(32); put_n(eq_w(source, "fn f() { return 7; }")); put_c(32);', + " churn(300); rt.gc(); rt.put_n(rt.live_objects() - base); rt.put_c(32);", + ' char* source = "fn f() { return 7; }"; let parsed = run.tokens(source);', + ' rt.put_n(parsed[0] > 0); rt.put_c(32); rt.put_n(rt.eq_w(source, "fn f() { return 7; }")); rt.put_c(32);', ' char* eval_source = "return 42;"; int e = 0;', - ' while (e < 16) { let q = tokens(eval_source); put_n(eval(q)); put_c(32); e++; }', - ' put_n(stable()); put_c(32); put_n(eq_w(eval_source, "return 42;")); put_c(10);', + ' while (e < 16) { let q = run.tokens(eval_source); rt.put_n(run.eval(q)); rt.put_c(32); e++; }', + ' rt.put_n(stable()); rt.put_c(32); rt.put_n(rt.eq_w(eval_source, "return 42;")); rt.put_c(10);', " return 0;", "}", ] @@ -78,7 +84,7 @@ def run(binary): hosts = [(zy, [zy]), (zym, [zym])] if js: - hosts.append(("javascript", ["node", "src/js/tinyvm.js"])) + hosts.append(("javascript", ["node", js])) outputs = [run(command) for _, command in hosts] if any(output != outputs[0] for output in outputs[1:]): raise SystemExit("compiler hosts disagreed on deterministic fuzz output") diff --git a/tests/drivers/fuzz_lsp.js b/tests/drivers/fuzz_lsp.js index e22a44b..f8a1d3b 100644 --- a/tests/drivers/fuzz_lsp.js +++ b/tests/drivers/fuzz_lsp.js @@ -1,7 +1,9 @@ // Deterministic source/protocol fuzzing for diagnostics, caching, and recovery. // Usage: node tests/drivers/fuzz_lsp.js path/to/zclsp +// interpreted hosts (no jit) are far slower; ZC_TEST_TIMEOUT_SCALE stretches every deadline +const TIMEOUT_SCALE = Number(process.env.ZC_TEST_TIMEOUT_SCALE) || 1; const { spawnSync } = require('child_process'); -const server = process.argv[2] || 'bin/zclsp'; +const server = process.argv[2] || 'build/bin/zclsp'; let state = 0x5a17; function next() { state = (Math.imul(state, 1664525) + 1013904223) >>> 0; return state; } function frame(m) { @@ -35,7 +37,7 @@ for (let i = 0; i < 32; i++) { msgs.push({ jsonrpc: '2.0', method: 'textDocument/didChange', params: { textDocument: { uri, version: 2 }, contentChanges: [{ text: fixed }] } }); } const stdUri = 'file:///fuzz-std.zc'; -msgs.push({ jsonrpc: '2.0', method: 'textDocument/didOpen', params: { textDocument: { uri: stdUri, languageId: 'zc', version: 1, text: 'include std;\nfn main() { return Text.starts("alpha", "al"); }\n' } } }); +msgs.push({ jsonrpc: '2.0', method: 'textDocument/didOpen', params: { textDocument: { uri: stdUri, languageId: 'zc', version: 1, text: 'include std;\nfn main() { return std.Text.starts("alpha", "al"); }\n' } } }); // Keep one server alive while documents are repeatedly opened, made malformed, // repaired, closed, and reopened at a new version. This exercises cache @@ -57,7 +59,7 @@ for (const [uri, text] of churn) { msgs.push({ jsonrpc: '2.0', method: 'textDocument/didOpen', params: { textDocument: { uri, languageId: 'zc', version: churnVersion++, text } } }); } msgs.push({ jsonrpc: '2.0', method: 'exit' }); -const run = spawnSync(server, [], { input: Buffer.concat(msgs.map(frame)), timeout: 60000, maxBuffer: 1 << 28 }); +const run = spawnSync(server, [], { input: Buffer.concat(msgs.map(frame)), timeout: 60000 * TIMEOUT_SCALE, maxBuffer: 1 << 28 }); if (run.error || run.status !== 0) throw run.error || new Error(`server exit ${run.status}: ${run.stderr}`); const out = parse(run.stdout); const diagnostics = new Map(); diff --git a/tests/drivers/fuzz_malformed.py b/tests/drivers/fuzz_malformed.py index 5bf97cf..42b8a0e 100644 --- a/tests/drivers/fuzz_malformed.py +++ b/tests/drivers/fuzz_malformed.py @@ -8,7 +8,7 @@ import subprocess import sys -bins = sys.argv[1:] or ["bin/zy", "bin/zym"] +bins = sys.argv[1:] or ["build/bin/zy", "build/bin/zym"] if len(bins) == 1: bins.append(bins[0]) rng = random.Random(0xC0DE) diff --git a/tests/drivers/fuzz_wasm.js b/tests/drivers/fuzz_wasm.js index b0982d4..22fa7d5 100644 --- a/tests/drivers/fuzz_wasm.js +++ b/tests/drivers/fuzz_wasm.js @@ -31,7 +31,7 @@ function quote(s) { return '"' + s + '"'; } lines.push(' churn(300); gc(); put_n(live_objects() - base); put_c(32);', ' put_n(stable()); put_c(10);', ' return 0;', '}'); expected.push(0, 73); const source = lines.join('\n') + '\n', want = expected.join(' ') + '\n'; - const zc = await ZC.load(fs.readFileSync(process.argv[2] || 'src/web/zc.wasm')); + const zc = await ZC.load(fs.readFileSync(process.argv[2] || 'build/web/zc.wasm')); const result = await zc.run(source); if (result.output !== want) throw new Error(`wasm fuzz mismatch\nexpected: ${want.slice(0, 180)}\nactual: ${result.output.slice(0, 180)}\nmessages: ${result.messages}`); console.log('ok wasm compiler/runtime/stdlib fuzz: 128 expressions, 300 scoped allocations and GC, and 32 Text/Bytes cases'); diff --git a/tests/drivers/genome_blob_test.py b/tests/drivers/genome_blob_test.py index 81533a7..fea055b 100644 --- a/tests/drivers/genome_blob_test.py +++ b/tests/drivers/genome_blob_test.py @@ -1,13 +1,17 @@ #!/usr/bin/env python3 # documentation precedes the symbol table; the boot header must skip those bytes. +# usage: python3 tests/drivers/genome_blob_test.py [build/lsp] (the documented language server image) import pathlib import re +import struct +import sys -source = pathlib.Path('src/core/genome.h').read_text() -org = int(re.search(r'#define GORG (\d+)', source)[1]) -length = int(re.search(r'#define GLEN (\d+)', source)[1]) -words = [int(word.strip()) for word in re.search(r'genome\[\]\s*=\s*\{([\s\S]*?)\};', source)[1].split(',') if word.strip()] -assert len(words) == length and words[6] == org + length +folder = pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else 'build/lsp') +image = (folder / 'lsp_genome.bin').read_bytes() +org = int(re.search(r'#define GORG (?:0x)?([0-9A-Fa-f]+)', (folder / 'memmap.h').read_text())[1], 16) +words = list(struct.unpack('<%dq' % (len(image) // 8), image)) +length = len(words) +assert words[6] == org + length cursor = words[5] - org assert 7 <= cursor < length records = {} @@ -23,8 +27,8 @@ while words[cursor]: cursor += 1 records[name] = (kind, value, signature) assert cursor == length - 1 -assert len(records) > 1000 and records['start'][0] == 1 -assert records['reserve_code'][0] == 1 and records['build_image_grow'][0] == 1 +assert len(records) > 1000 and records['run.start'][0] == 1 +assert records['run.reserve_code'][0] == 1 and records['run.build_image'][0] == 1 assert any(name.startswith('doc ') for name in records), 'the fixture contains documentation bytes' for name, (kind, value, _) in records.items(): if kind == 8: diff --git a/tests/drivers/hints_test.js b/tests/drivers/hints_test.js index 8bb1934..b3598be 100644 --- a/tests/drivers/hints_test.js +++ b/tests/drivers/hints_test.js @@ -1,5 +1,7 @@ // hints are informational diagnostics (severity 3) and follow positional hints on / small / off directives. // usage: node tests/drivers/hints_test.js path/to/zclsp +// interpreted hosts (no jit) are far slower; ZC_TEST_TIMEOUT_SCALE stretches every deadline +const TIMEOUT_SCALE = Number(process.env.ZC_TEST_TIMEOUT_SCALE) || 1; const assert = require('assert'); const cp = require('child_process'); const server = process.argv[2]; @@ -10,7 +12,7 @@ function diagnostics(text) { { jsonrpc: '2.0', id: 1, method: 'initialize', params: {} }, { jsonrpc: '2.0', method: 'textDocument/didOpen', params: { textDocument: { uri, version: 1, languageId: 'zc', text } } }, { jsonrpc: '2.0', method: 'exit' }].map(frame)); - const r = cp.spawnSync(server, process.argv.slice(3), { input, timeout: 20000, maxBuffer: 4 << 20 }); + const r = cp.spawnSync(server, process.argv.slice(3), { input, timeout: 20000 * TIMEOUT_SCALE, maxBuffer: 4 << 20 }); assert.strictEqual(r.status, 0, String(r.stderr)); let d = r.stdout, out = []; while (d.length) { diff --git a/tests/drivers/hover_features_test.js b/tests/drivers/hover_features_test.js index d6e1226..25f5965 100644 --- a/tests/drivers/hover_features_test.js +++ b/tests/drivers/hover_features_test.js @@ -1,5 +1,7 @@ // focused type, inference, and block-documentation hover regressions. // usage: node tests/drivers/hover_features_test.js path/to/zclsp +// interpreted hosts (no jit) are far slower; ZC_TEST_TIMEOUT_SCALE stretches every deadline +const TIMEOUT_SCALE = Number(process.env.ZC_TEST_TIMEOUT_SCALE) || 1; const assert = require('assert'); const cp = require('child_process'); const server = process.argv[2]; @@ -21,7 +23,7 @@ function hover(text, needle, nth = 0) { { jsonrpc: '2.0', id: 2, method: 'textDocument/hover', params: { textDocument: { uri }, position: { line, character } } }, { jsonrpc: '2.0', method: 'exit' }, ]; - const result = cp.spawnSync(server, process.argv.slice(3), { input: Buffer.concat(messages.map(frame)), timeout: 10000, maxBuffer: 4 << 20 }); + const result = cp.spawnSync(server, process.argv.slice(3), { input: Buffer.concat(messages.map(frame)), timeout: 10000 * TIMEOUT_SCALE, maxBuffer: 4 << 20 }); assert.strictEqual(result.status, 0, String(result.stderr)); let data = result.stdout; while (data.length) { diff --git a/tests/drivers/hover_surface_test.js b/tests/drivers/hover_surface_test.js index 13a9a3f..688aca6 100644 --- a/tests/drivers/hover_surface_test.js +++ b/tests/drivers/hover_surface_test.js @@ -1,4 +1,6 @@ // hover declarations and instructions in the source-only documents opened by f12. +// interpreted hosts (no jit) are far slower; ZC_TEST_TIMEOUT_SCALE stretches every deadline +const TIMEOUT_SCALE = Number(process.env.ZC_TEST_TIMEOUT_SCALE) || 1; const assert = require('assert'); const fs = require('fs'); const cp = require('child_process'); @@ -48,7 +50,7 @@ if (process.argv[2] === '--wasm' || process.argv[2] === '--vm') { for (const m of messages) p.stdin.write(frame({ jsonrpc: '2.0', ...m })); out = Buffer.concat(chunks); } else { - const r = cp.spawnSync(process.argv[2], process.argv.slice(3), { input: Buffer.concat(messages.map(m => frame({ jsonrpc: '2.0', ...m }))), timeout: 60000, maxBuffer: 32 << 20 }); + const r = cp.spawnSync(process.argv[2], process.argv.slice(3), { input: Buffer.concat(messages.map(m => frame({ jsonrpc: '2.0', ...m }))), timeout: 60000 * TIMEOUT_SCALE, maxBuffer: 32 << 20 }); assert.strictEqual(r.status, 0, String(r.stderr)); out = r.stdout; } const replies = new Map(); diff --git a/tests/drivers/hover_values_test.js b/tests/drivers/hover_values_test.js index badc2ed..0f09cfc 100644 --- a/tests/drivers/hover_values_test.js +++ b/tests/drivers/hover_values_test.js @@ -1,4 +1,6 @@ // value previews follow the compiler's bindings and the position of each use. +// interpreted hosts (no jit) are far slower; ZC_TEST_TIMEOUT_SCALE stretches every deadline +const TIMEOUT_SCALE = Number(process.env.ZC_TEST_TIMEOUT_SCALE) || 1; const assert = require('assert'); const cp = require('child_process'); const server = process.argv[2]; @@ -17,7 +19,7 @@ function hover(text, needle, nth = 0) { const body = Buffer.from(JSON.stringify(m)); return Buffer.concat([Buffer.from('Content-Length: ' + body.length + '\r\n\r\n'), body]); })); - const result = cp.spawnSync(server, process.argv.slice(3), { input, timeout: 20000, maxBuffer: 8 << 20 }); + const result = cp.spawnSync(server, process.argv.slice(3), { input, timeout: 20000 * TIMEOUT_SCALE, maxBuffer: 8 << 20 }); assert.strictEqual(result.status, 0, String(result.stderr)); let data = result.stdout; while (data.length) { diff --git a/tests/drivers/icon_test.py b/tests/drivers/icon_test.py index 560c622..72cd69b 100644 --- a/tests/drivers/icon_test.py +++ b/tests/drivers/icon_test.py @@ -5,7 +5,7 @@ import argparse import subprocess parser = argparse.ArgumentParser() -parser.add_argument('vm', nargs='?', default='bin/zy') +parser.add_argument('vm', nargs='?', default='build/bin/zy') parser.add_argument('--case', default='') parser.add_argument('--js', action='store_true') parser.add_argument('--source', action='store_true', help='test the working library source instead of the embedded module') diff --git a/tests/drivers/image_run.js b/tests/drivers/image_run.js index f657916..ea05f9f 100644 --- a/tests/drivers/image_run.js +++ b/tests/drivers/image_run.js @@ -2,12 +2,11 @@ const fs = require('fs'); const { TinyVM } = require('../../build/js/tinyvm.js'); const { mapValues } = require('../../src/js/mapvalues.js'); -const header = fs.readFileSync(process.argv[2], 'utf8'); -const org = Number(header.match(/#define\s+GORG\s+(\d+)/)[1]); -const words = header.match(/genome\[\]\s*=\s*\{([\s\S]*?)\};/)[1].split(',').map(s => s.trim()).filter(Boolean); +// usage: node tests/drivers/image_run.js genome.bin memmap.h [--stdin] +const image = fs.readFileSync(process.argv[2]); const layout = mapValues(process.argv[3], ['GORG', 'DS0', 'DSLIM', 'RS0', 'RSLIM', 'SYSGATE', 'ZFP', 'HEAP_LO', 'HEAP_HI']); const machine = new TinyVM({ layout, onMessage: message => process.stderr.write(message) }); -machine.loadGenome(words, org); +machine.loadImage(image, layout.GORG); if (process.argv[4] === '--stdin') machine.setInput(fs.readFileSync(0)); const status = machine.run(); process.stdout.write(machine.getOutput()); diff --git a/tests/drivers/imagekit.py b/tests/drivers/imagekit.py new file mode 100644 index 0000000..6cc083f --- /dev/null +++ b/tests/drivers/imagekit.py @@ -0,0 +1,34 @@ +"""shared steps for the drivers that build their own images: pick a c compiler with +#embed, and link a microcore around a genome image (little-endian 64-bit words) +for a given memory map. everything is written into the caller's directory.""" +import os +import shutil +import subprocess +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +DEFAULT_MAP = ROOT / 'build' / 'core' / 'map' / 'memmap.h' +MICRO_SOURCES = ['src/micro/micro.c', 'src/micro/micro_port.h', 'src/micro/port_linux.c', + 'src/core/fops.h', 'src/core/bops.h', 'src/core/stack.h', 'src/core/genome.h'] + + +def compiler(): + """the c compiler: CC, or the one src/tools/findcc.sh picks.""" + if os.environ.get('CC'): + return os.environ['CC'] + return subprocess.run(['sh', str(ROOT / 'src/tools/findcc.sh')], check=True, capture_output=True, text=True).stdout.strip() + + +def micro_image(directory, image, memmap=DEFAULT_MAP, threads=True, name='image', timeout=60): + """link a microcore in directory that boots image (bytes). memmap is the map header the + image was built for. returns the executable's path.""" + directory = Path(directory) + (directory / 'genome.bin').write_bytes(image) + shutil.copy(memmap, directory / 'memmap.h') + for f in MICRO_SOURCES: + shutil.copy(ROOT / f, directory) + flags = ['-pthread', '-DZY_THREADS'] if threads else [] + out = directory / name + subprocess.run([compiler(), '-O2', '-w', '-fwrapv', *flags, str(directory / 'micro.c'), str(directory / 'port_linux.c'), + '-I' + str(directory), '-o', str(out)], check=True, timeout=timeout) + return out diff --git a/tests/drivers/layout_config_test.py b/tests/drivers/layout_config_test.py index 469e7dd..2f0fa07 100644 --- a/tests/drivers/layout_config_test.py +++ b/tests/drivers/layout_config_test.py @@ -5,7 +5,7 @@ import subprocess import tempfile root = pathlib.Path(__file__).resolve().parents[2] -tool = root / 'bin/genmap' +tool = root / 'build/bin/genmap' result = subprocess.run([tool, '--help'], capture_output=True, text=True) assert result.returncode == 0 and '--generated-text' in result.stderr invalid = [ diff --git a/tests/drivers/lsp_growth_test.js b/tests/drivers/lsp_growth_test.js index b06230a..6d221fd 100644 --- a/tests/drivers/lsp_growth_test.js +++ b/tests/drivers/lsp_growth_test.js @@ -1,5 +1,7 @@ // focused regressions for growing document, uri, hover, and name-set storage. // usage: node tests/drivers/lsp_growth_test.js path/to/zclsp-or-lsp.wasm +// interpreted hosts (no jit) are far slower; ZC_TEST_TIMEOUT_SCALE stretches every deadline +const TIMEOUT_SCALE = Number(process.env.ZC_TEST_TIMEOUT_SCALE) || 1; const assert = require('assert'); const cp = require('child_process'); const { wasmProcess } = require('../../editors/vscode/client/wasmproc'); @@ -36,7 +38,7 @@ if (target.endsWith('.wasm')) { for (const message of messages) assert.ok(server.stdin.write(frame(message))); output = Buffer.concat(chunks); } else { - const result = cp.spawnSync(target, [], { input: Buffer.concat(messages.map(frame)), timeout: 30000, maxBuffer: 8 << 20 }); + const result = cp.spawnSync(target, [], { input: Buffer.concat(messages.map(frame)), timeout: 30000 * TIMEOUT_SCALE, maxBuffer: 8 << 20 }); assert.strictEqual(result.status, 0, String(result.stderr)); output = result.stdout; } diff --git a/tests/drivers/lsp_sandbox.js b/tests/drivers/lsp_sandbox.js index 14bc1e3..41523cc 100644 --- a/tests/drivers/lsp_sandbox.js +++ b/tests/drivers/lsp_sandbox.js @@ -2,10 +2,12 @@ // (immediate functions). the server must not let that code write files, so a file that // tries to leaves nothing behind and the server still answers. // usage: node tests/drivers/lsp_sandbox.js path/to/zclsp +// interpreted hosts (no jit) are far slower; ZC_TEST_TIMEOUT_SCALE stretches every deadline +const TIMEOUT_SCALE = Number(process.env.ZC_TEST_TIMEOUT_SCALE) || 1; const cp = require('child_process'), fs = require('fs'), os = require('os'), path = require('path'); const target = path.join(os.tmpdir(), 'zc-sandbox-' + process.pid); try { fs.unlinkSync(target); } catch (e) {} -const src = 'immediate fn evil() { let fd = syscall(1, "' + target + '", 1, 0); syscall(4, fd, 65, 1); syscall(2, fd, 0, 0); return 0; }\nevil\nfn main() { return 0; }\n'; +const src = 'include rt, run;\nimmediate fn evil() { run.next(); let fd = rt.syscall(1, "' + target + '", 1, 0); rt.syscall(4, fd, 65, 1); rt.syscall(2, fd, 0, 0); return 0; }\nevil\nfn main() { return 0; }\n'; const frame = m => { const b = Buffer.from(JSON.stringify(m)); return Buffer.concat([Buffer.from('Content-Length: ' + b.length + '\r\n\r\n'), b]); }; const input = Buffer.concat([ frame({ jsonrpc: '2.0', id: 1, method: 'initialize', params: { processId: 1, capabilities: {} } }), @@ -13,7 +15,7 @@ const input = Buffer.concat([ frame({ jsonrpc: '2.0', id: 3, method: 'textDocument/semanticTokens/full', params: { textDocument: { uri: 'file:///evil.zc' } } }), frame({ jsonrpc: '2.0', method: 'exit' }), ]); -const r = cp.spawnSync(process.argv[2], [], { input, timeout: 20000 }); +const r = cp.spawnSync(process.argv[2], [], { input, timeout: 20000 * TIMEOUT_SCALE }); const wrote = fs.existsSync(target); if (wrote) fs.unlinkSync(target); const answered = r.stdout && r.stdout.includes('"id":3'); diff --git a/tests/drivers/lsp_test.js b/tests/drivers/lsp_test.js index ab43c41..18d8bf7 100644 --- a/tests/drivers/lsp_test.js +++ b/tests/drivers/lsp_test.js @@ -1,5 +1,7 @@ // protocol tests for the language server (node, no packages). // usage: node tests/drivers/lsp_test.js path/to/zclsp +// interpreted hosts (no jit) are far slower; ZC_TEST_TIMEOUT_SCALE stretches every deadline +const TIMEOUT_SCALE = Number(process.env.ZC_TEST_TIMEOUT_SCALE) || 1; const { spawnSync } = require('child_process'); const fs = require('fs'); const server = process.argv[2]; @@ -24,7 +26,7 @@ function parse(buf) { return out; } function run(msgs) { - const r = spawnSync(server, [], { input: Buffer.concat(msgs.map(frame)), timeout: 30000, maxBuffer: 1 << 28 }); + const r = spawnSync(server, [], { input: Buffer.concat(msgs.map(frame)), timeout: 30000 * TIMEOUT_SCALE, maxBuffer: 1 << 28 }); return { code: r.status, msgs: parse(r.stdout) }; } const init = id => ({ jsonrpc: '2.0', id, method: 'initialize', params: {} }); @@ -173,7 +175,7 @@ check(hv && hv.includes('leaves the function with a value'), 'a keyword shows wh const builtinSource = 'include rt; fn main() {\n\trt.put_c(32);\n\treturn 0;\n}\n'; hv = hover(builtinSource, 1, at(builtinSource, 1, 'put_c')); check(hv && hv.includes('put_c('), 'a built-in function shows its signature', hv); -const kwsrc = 'include rt; fn main() {\n\tint* p = new(int);\n\trt.free(p);\n\treturn 0;\n}\n'; +const kwsrc = 'include rt; fn main() {\n\tint* p = rt.new(int);\n\trt.free(p);\n\treturn 0;\n}\n'; hv = hover(kwsrc, 1, at(kwsrc, 1, 'new')); check(hv && hv.includes('new(T)') && hv.includes('fresh heap object') && hv.includes('owns it'), 'hover on new shows its form and ownership behavior', hv); hv = hover(kwsrc, 2, at(kwsrc, 2, 'free')); @@ -209,7 +211,7 @@ check(r.code === 0 && r.msgs.some(m => m.id === 9 && m.result === null), 'shutdo // the corpus: every valid program checks clean, every bad one is rejected with its message function cli(file) { - const r = spawnSync(server, [], { input: fs.readFileSync(file), timeout: 30000 }); + const r = spawnSync(server, [], { input: fs.readFileSync(file), timeout: 30000 * TIMEOUT_SCALE }); return r.stdout.toString(); } let clean = 0, dirty = []; diff --git a/tests/drivers/memory_limits_test.py b/tests/drivers/memory_limits_test.py index a973480..808a239 100644 --- a/tests/drivers/memory_limits_test.py +++ b/tests/drivers/memory_limits_test.py @@ -13,7 +13,7 @@ source = '''fn main() { return 0; } ''' -commands = [['bin/zy'], ['bin/zym'], ['node', 'src/js/tinyvm.js']] +commands = [['build/bin/zy'], ['build/bin/zym'], ['node', 'src/js/tinyvm.js']] environment = dict(os.environ, TINYVM_MAX_MEMORY_WORDS='16777216') for command in commands: result = subprocess.run(command, input=source, text=True, capture_output=True, env=environment, timeout=15) diff --git a/tests/drivers/nav_test.js b/tests/drivers/nav_test.js index 23afbf8..8f0d8fd 100644 --- a/tests/drivers/nav_test.js +++ b/tests/drivers/nav_test.js @@ -1,5 +1,7 @@ // go to definition and find references, in the document and across includes. // usage: node tests/drivers/nav_test.js path/to/zclsp (run from the repository root) +// interpreted hosts (no jit) are far slower; ZC_TEST_TIMEOUT_SCALE stretches every deadline +const TIMEOUT_SCALE = Number(process.env.ZC_TEST_TIMEOUT_SCALE) || 1; const assert = require('assert'); const cp = require('child_process'); const fs = require('fs'); @@ -18,7 +20,7 @@ function request(text, needle, method, extra = {}) { { jsonrpc: '2.0', method: 'textDocument/didOpen', params: { textDocument: { uri, version: 1, languageId: 'zc', text } } }, { jsonrpc: '2.0', id: 2, method, params: { textDocument: { uri }, position, ...extra } }, { jsonrpc: '2.0', method: 'exit' }].map(frame)); - const r = cp.spawnSync(server, process.argv.slice(3), { input, cwd: root, timeout: 30000, maxBuffer: 8 << 20 }); + const r = cp.spawnSync(server, process.argv.slice(3), { input, cwd: root, timeout: 30000 * TIMEOUT_SCALE, maxBuffer: 8 << 20 }); assert.strictEqual(r.status, 0, String(r.stderr)); let d = r.stdout, init; while (d.length) { diff --git a/tests/drivers/protocol_growth_test.js b/tests/drivers/protocol_growth_test.js index 64015d8..d8dff6c 100644 --- a/tests/drivers/protocol_growth_test.js +++ b/tests/drivers/protocol_growth_test.js @@ -1,4 +1,6 @@ // preserve long request identifiers and ignore deeply nested unknown json fields. +// interpreted hosts (no jit) are far slower; ZC_TEST_TIMEOUT_SCALE stretches every deadline +const TIMEOUT_SCALE = Number(process.env.ZC_TEST_TIMEOUT_SCALE) || 1; const assert = require('assert'); const cp = require('child_process'); const target = process.argv[2]; @@ -15,7 +17,7 @@ for (let i = 0; i < 24; i++) messages.push({ jsonrpc: '2.0', method: 'textDocume text: 'enum choice { item { int value; }, empty };\nfn main() { choice*.m p = choice.item(7); return p.item_value; }\n', } } }); messages.push({ jsonrpc: '2.0', id: 'finish', method: 'shutdown', params: null }, { jsonrpc: '2.0', method: 'exit' }); -const result = cp.spawnSync(target, [], { input: Buffer.concat(messages.map(frame)), timeout: 20000, maxBuffer: 8 << 20 }); +const result = cp.spawnSync(target, [], { input: Buffer.concat(messages.map(frame)), timeout: 20000 * TIMEOUT_SCALE, maxBuffer: 8 << 20 }); assert.strictEqual(result.status, 0, String(result.stderr)); const responses = []; let data = result.stdout; diff --git a/tests/drivers/runtime_image_test.py b/tests/drivers/runtime_image_test.py index c1f4b9c..d214613 100644 --- a/tests/drivers/runtime_image_test.py +++ b/tests/drivers/runtime_image_test.py @@ -1,14 +1,18 @@ #!/usr/bin/env python3 """self-hosted run-only images retain enhancements and generic method bodies.""" +import sys import argparse import subprocess import tempfile from pathlib import Path +sys.dont_write_bytecode = True # importing imagekit must not leave __pycache__ in the tree +from imagekit import micro_image parser = argparse.ArgumentParser() parser.add_argument('--case', default='') -parser.add_argument('--host', default='bin/zy') -parser.add_argument('--mkgenome', default='bin/mkgenome') +parser.add_argument('--host', default='build/bin/zy') +parser.add_argument('--mkgenome', default='build/bin/mkgenome') +parser.add_argument('--map', default='build/core/map/memmap.h') args = parser.parse_args() cases = [ ('enhancements', 'include rt; fn main() { rt.put_n(17 % 5); rt.put_c(32); rt.put_n(17 / 5); }', b'2 3'), @@ -23,12 +27,9 @@ for name, source, expected in cases: directory = Path(work) r = subprocess.run([args.host], input=Path('src/tools/build.zc').read_bytes() + b'`' + prefix + source.encode(), capture_output=True, timeout=30) assert r.returncode == 0 and len(r.stdout) >= 56 and not r.stdout.startswith(b'zc:'), (name, r.stdout[:1000], r.stderr) - header = subprocess.run([args.mkgenome], input=r.stdout, capture_output=True, timeout=5) - assert header.returncode == 0, (name, header.stderr) - (directory / 'genome.h').write_bytes(header.stdout) - for f in ['src/core/memmap.h', 'src/core/fops.h', 'src/core/bops.h', 'src/core/stack.h', 'src/micro/micro.c', 'src/micro/micro_port.h', 'src/micro/port_linux.c']: - (directory / Path(f).name).write_bytes(Path(f).read_bytes()) - subprocess.run(['cc', '-O2', '-w', '-fwrapv', '-pthread', '-DZY_THREADS', str(directory / 'micro.c'), str(directory / 'port_linux.c'), '-I' + work, '-o', str(directory / 'image')], check=True, timeout=30) - r = subprocess.run([str(directory / 'image')], capture_output=True, timeout=10) + image = subprocess.run([args.mkgenome], input=r.stdout, capture_output=True, timeout=5) + assert image.returncode == 0, (name, image.stderr) + binary = micro_image(directory, image.stdout, args.map) + r = subprocess.run([str(binary)], capture_output=True, timeout=10) assert r.returncode == 0 and r.stdout == expected, (name, r.returncode, r.stdout, r.stderr, expected) print('ok self-hosted run-only image: ' + name) diff --git a/tests/drivers/seed_growth_test.py b/tests/drivers/seed_growth_test.py index 426c970..b94445e 100644 --- a/tests/drivers/seed_growth_test.py +++ b/tests/drivers/seed_growth_test.py @@ -1,25 +1,24 @@ #!/usr/bin/env python3 # stage 0 accepts long source streams and grows a tiny initial image reservation. +import sys import pathlib import subprocess import tempfile +sys.dont_write_bytecode = True # importing imagekit must not leave __pycache__ in the tree +from imagekit import compiler, micro_image root = pathlib.Path(__file__).resolve().parents[2] +(root / 'build').mkdir(exist_ok=True) with tempfile.TemporaryDirectory(prefix='seed-growth-', dir=root / 'build') as directory: folder = pathlib.Path(directory) - subprocess.run(['bin/genmap', '--preset', 'run', '--grow', '--genome', '600000', '--heap', '1048576', '-o', directory], check=True, capture_output=True) - compiler = folder / 'zc0' - subprocess.run(['cc', '-O1', '-w', '-fwrapv', '-DMAXW=16', 'src/tools/zc0.c', '-I' + directory, '-Isrc/core', '-o', str(compiler)], check=True) + subprocess.run(['build/bin/genmap', '--preset', 'run', '--grow', '--genome', '600000', '--heap', '1048576', '-o', directory], check=True, capture_output=True) + zc0 = folder / 'zc0' + subprocess.run([compiler(), '-O1', '-w', '-fwrapv', '-DMAXW=16', 'src/tools/zc0.c', '-I' + directory, '-Isrc/core', '-o', str(zc0)], check=True) source = '/*' + 'x' * (5 << 20) + '*/\n' - source += (folder / 'rt.zc').read_text() + (root / 'src/runtime/run.zc').read_text() - source += 'let data[550000]; fn main() { data[549999] = 7; put_n(data[549999]); put_c(32); put_n(4611686018427387904); put_c(10); return 0; }\n' - with (folder / 'genome.h').open('w') as output: - subprocess.run([str(compiler)], input=source, text=True, stdout=output, check=True, timeout=30) - binary = folder / 'vm' - import shutil - for path in ['src/micro/micro.c', 'src/micro/micro_port.h', 'src/micro/port_linux.c', 'src/core/fops.h', 'src/core/bops.h', 'src/core/stack.h']: - shutil.copy(root / path, folder) - subprocess.run(['cc', '-O2', '-w', '-fwrapv', str(folder / 'micro.c'), str(folder / 'port_linux.c'), '-I' + directory, '-o', str(binary)], check=True) + source += (folder / 'rt.zc').read_text() + (root / 'src/core/core.zc').read_text() + (root / 'src/runtime/run.zc').read_text() + source += 'namespace; include rt; let data[550000]; fn main() { data[549999] = 7; rt.put_n(data[549999]); rt.put_c(32); rt.put_n(4611686018427387904); rt.put_c(10); return 0; }\n' + image = subprocess.run([str(zc0)], input=source.encode(), capture_output=True, check=True, timeout=30).stdout + binary = micro_image(folder / 'vm', image, folder / 'memmap.h', threads=False) if (folder / 'vm').mkdir() is None else None result = subprocess.run([str(binary)], text=True, capture_output=True, timeout=20) assert result.returncode == 0 and result.stdout == '7 4611686018427387904\n', (result.returncode, result.stdout, result.stderr) print('ok stage 0 reads beyond four megabytes and grows beyond its initial image capacity') diff --git a/tests/drivers/stack_growth_test.py b/tests/drivers/stack_growth_test.py index d4abe81..3c90305 100644 --- a/tests/drivers/stack_growth_test.py +++ b/tests/drivers/stack_growth_test.py @@ -4,6 +4,8 @@ import subprocess import sys import tempfile from pathlib import Path +sys.dont_write_bytecode = True # importing imagekit must not leave __pycache__ in the tree +from imagekit import compiler command = sys.argv[1:] @@ -12,47 +14,53 @@ def run(source, expected, compile_image=False): if compile_image and command[0] == 'node': # compile the execution stress cases once with stage 0, so a javascript # compiler's speed does not determine the execution-test timeout. + Path('build').mkdir(exist_ok=True) with tempfile.TemporaryDirectory(prefix='stack-image-', dir='build') as directory: target = Path(directory) - subprocess.run(['bin/genmap', '--preset', 'run', '--grow', '--heap', '1048576', '--genome', '131072', '--frame', '4096', '--return', '256', '--data', '3072', '-o', directory], check=True, capture_output=True) - compiler = target / 'zc0' - subprocess.run(['cc', '-O1', '-w', '-fwrapv', 'src/tools/zc0.c', '-I' + directory, '-Isrc/core', '-o', str(compiler)], check=True, capture_output=True) - full_source = (target / 'rt.zc').read_text() + Path('src/runtime/run.zc').read_text() + source - image = subprocess.run([str(compiler)], input=full_source, text=True, capture_output=True, check=True) - header = target / 'genome.h' - header.write_text(image.stdout) - result = subprocess.run(['node', 'tests/drivers/image_run.js', str(header), str(target / 'memmap.h')], text=True, capture_output=True, timeout=45) + subprocess.run(['build/bin/genmap', '--preset', 'run', '--grow', '--heap', '1048576', '--genome', '131072', '--frame', '4096', '--return', '256', '--data', '3072', '-o', directory], check=True, capture_output=True) + zc0 = target / 'zc0' + subprocess.run([compiler(), '-O1', '-w', '-fwrapv', 'src/tools/zc0.c', '-I' + directory, '-Isrc/core', '-o', str(zc0)], check=True, capture_output=True) + full_source = (target / 'rt.zc').read_text() + Path('src/core/core.zc').read_text() + Path('src/runtime/run.zc').read_text() + source + image = subprocess.run([str(zc0)], input=full_source.encode(), capture_output=True, check=True) + (target / 'genome.bin').write_bytes(image.stdout) + result = subprocess.run(['node', 'tests/drivers/image_run.js', str(target / 'genome.bin'), str(target / 'memmap.h')], text=True, capture_output=True, timeout=45) else: result = subprocess.run(command, input=source, text=True, capture_output=True, timeout=45) assert result.returncode == 0 and result.stdout == expected, (command, result.returncode, result.stdout, result.stderr) -run('''fn main() { - int old_data = data_top - data_low, old_return = return_top - return_low; - put_n(reserve_data(old_data + 1) > old_data); put_c(32); - put_n(reserve_returns(old_return + 1) > old_return); put_c(32); - gc(); put_n(data_node != 0 && return_node != 0); put_c(10); return 0; +run('''namespace; +include rt; +fn main() { + int old_data = rt.data_top - rt.data_low, old_return = rt.return_top - rt.return_low; + rt.put_n(rt.reserve_data(old_data + 1) > old_data); rt.put_c(32); + rt.put_n(rt.reserve_returns(old_return + 1) > old_return); rt.put_c(32); + rt.gc(); rt.put_n(rt.data_node != 0 && rt.return_node != 0); rt.put_c(10); return 0; } ''', '1 1 1\n') -run('''fn main() { - put_n(reserve_data(-1) == -1); put_c(32); put_n(reserve_returns(-1) == -1); put_c(32); - put_n(syscall(9, 6, 0, 0) == -1); put_c(32); - int current = syscall(9, 0, 0, 0); syscall(9, 5, current, 0); - put_n(reserve_data(0x10000000) == -1); put_c(32); gc(); put_n(7); put_c(10); return 0; +run('''namespace; +include rt; +fn main() { + rt.put_n(rt.reserve_data(-1) == -1); rt.put_c(32); rt.put_n(rt.reserve_returns(-1) == -1); rt.put_c(32); + rt.put_n(rt.syscall(9, 6, 0, 0) == -1); rt.put_c(32); + int current = rt.syscall(9, 0, 0, 0); rt.syscall(9, 5, current, 0); + rt.put_n(rt.reserve_data(0x10000000) == -1); rt.put_c(32); rt.gc(); rt.put_n(7); rt.put_c(10); return 0; } ''', '1 1 1 1 7\n') count = 40000 -source = 'fn main() { int total = asm { ' + ' '.join(str(i + 1) for i in range(count)) -source += ' + ' * (count - 1) + ' }; gc(); put_n(total); put_c(32); ' -source += 'put_n(data_node != 0); put_c(10); return 0; }\n' +source = 'namespace; include rt; fn main() { int total = asm { ' + ' '.join(str(i + 1) for i in range(count)) +source += ' + ' * (count - 1) + ' }; rt.gc(); rt.put_n(total); rt.put_c(32); ' +source += 'rt.put_n(rt.data_node != 0); rt.put_c(10); return 0; }\n' run(source, '800020000 1\n', compile_image=True) -run('''unchecked int descend(int n) { - if (!n) { gc(); return 7; } +run('''namespace; +include rt; +unchecked int descend(int n) { + if (!n) { rt.gc(); return 7; } return descend(n - 1) + 1; } -fn main() { put_n(descend(70000)); put_c(32); put_n(return_node != 0); put_c(10); return 0; } +fn main() { rt.put_n(descend(70000)); rt.put_c(32); rt.put_n(rt.return_node != 0); rt.put_c(10); return 0; } ''', '70007 1\n', compile_image=True) print('ok explicit reservation, 40000 operands, and 70000 return addresses: ' + command[0]) diff --git a/tests/drivers/storage_analysis_test.js b/tests/drivers/storage_analysis_test.js index 774fa89..704a844 100644 --- a/tests/drivers/storage_analysis_test.js +++ b/tests/drivers/storage_analysis_test.js @@ -1,8 +1,12 @@ // definite storage findings use the compiler's parser and the editor protocol. +// interpreted hosts (no jit) are far slower; ZC_TEST_TIMEOUT_SCALE stretches every deadline +const TIMEOUT_SCALE = Number(process.env.ZC_TEST_TIMEOUT_SCALE) || 1; const assert = require('assert'); const cp = require('child_process'); const server = process.argv[2]; function check(text, needle) { + // the runtime is an explicit import; it shares the first line so line numbers stay as written + text = 'include rt; ' + text; const uri = 'file:///storage-analysis.zc'; const messages = [{ jsonrpc: '2.0', id: 1, method: 'initialize', params: {} }, { jsonrpc: '2.0', method: 'textDocument/didOpen', params: { textDocument: { uri, version: 1, text } } }]; @@ -13,7 +17,7 @@ function check(text, needle) { } messages.push({ jsonrpc: '2.0', method: 'exit' }); const input = Buffer.concat(messages.map(m => { const b = Buffer.from(JSON.stringify(m)); return Buffer.concat([Buffer.from('Content-Length: ' + b.length + '\r\n\r\n'), b]); })); - const r = cp.spawnSync(server, process.argv.slice(3), { input, timeout: 20000, maxBuffer: 8 << 20 }); + const r = cp.spawnSync(server, process.argv.slice(3), { input, timeout: 20000 * TIMEOUT_SCALE, maxBuffer: 8 << 20 }); assert.strictEqual(r.status, 0, String(r.stderr)); const replies = []; let data = r.stdout; while (data.length) { @@ -27,50 +31,50 @@ function check(text, needle) { } const prefix = 'unchecked fn main() {\n'; for (const [type, length] of [['int', 3], ['u8', 3], ['fixed int[?]', 3], ['u8[?]', 3]]) { - const r = check(prefix + ` let p = new(${type}, ${length});\n let alias = p;\n return alias[${length}];\n}\n`, 'alias['); + const r = check(prefix + ` let p = rt.new(${type}, ${length});\n let alias = p;\n return alias[${length}];\n}\n`, 'alias['); assert.strictEqual(r.warnings.length, 1, JSON.stringify(r.diagnostics)); assert.match(r.warnings[0].message, /out-of-bounds.*index 3.*length 3/); assert.match(r.hover, /size: 3 elements.*lifetime: heap storage; live here/); assert.strictEqual(r.warnings[0].range.start.line, 3); } -const negative = check(prefix + ' let p = new(3); return p[-1];\n}\n'); +const negative = check(prefix + ' let p = rt.new(3); return p[-1];\n}\n'); assert.match(negative.warnings[0]?.message || '', /index -1.*length 3/); -const freed = check(prefix + ' let p = new(int, 3);\n let q = p;\n free(p);\n return q[0];\n}\n', 'q['); +const freed = check(prefix + ' let p = rt.new(int, 3);\n let q = p;\n rt.free(p);\n return q[0];\n}\n', 'q['); assert.strictEqual(freed.warnings.length, 1, JSON.stringify(freed.diagnostics)); assert.match(freed.warnings[0].message, /use after free.*line 4/); assert.match(freed.hover, /size: 3 elements.*lifetime: ended on line 4/); -const dereference = check(prefix + ' let p = new(int); free(p); return *p;\n}\n'); +const dereference = check(prefix + ' let p = rt.new(int); rt.free(p); return *p;\n}\n'); assert.match(dereference.warnings[0]?.message || '', /use after free/); -const field = check('struct item { int value; };\n' + prefix + ' let p = new(item); free(p); return p.value;\n}\n'); +const field = check('struct item { int value; };\n' + prefix + ' let p = rt.new(item); rt.free(p); return p.value;\n}\n'); assert.match(field.warnings[0]?.message || '', /use after free/); const scope = check(prefix + ' int* p;\n { let a[3]; p = a; }\n return p[0];\n}\n', 'p['); assert.match(scope.warnings[0]?.message || '', /use after free.*line 3/); assert.match(scope.hover, /lifetime: ended on line 3/); const safe = [ - prefix + ' let p = new(int, 3); let q = p as u8*; return q[23]; }', - prefix + ' let p = new(int, 3); let q = (u8*)p; return q[23]; }', + prefix + ' let p = rt.new(int, 3); let q = p as u8*; return q[23]; }', + prefix + ' let p = rt.new(int, 3); let q = (u8*)p; return q[23]; }', prefix + ' int n = 2; let p = &n; let q = n; return q[2]; }', - prefix + ' let p = new(int, 3); let ignored = 0 && free(p); return p[0]; }', - prefix + ' let p = new(int, 3); let ignored = 1 || free(p); return p[0]; }', - prefix + ' let p = new(int, 50); return p[(u8)300]; }', - prefix + ' let p = new(int, 50); return p[300 as u8]; }', - prefix + ' let p = new(int, 3); let q = p; q[2] = 7; free(p); return 0; }', - prefix + ' let p = new(3); free(p); p = new(3); return p[0]; }', - 'unchecked fn unknown(int* p) {}\n' + prefix + ' let p = new(int, 3); unknown(p); return p[9]; }', - 'fn maybe(int n) { return n; }\n' + prefix + ' let p = new(int, 3); return p[maybe(9)]; }', - prefix + ' let p = new(int, 3); if (1) free(p); return p[0]; }', - prefix + ' let p = new(int, 3); while (0) free(p); return p[0]; }', - prefix + ' let p = new(int, 3); let f = fn() { free(p); return 0; }; return p[0]; }', - prefix + ' let p = new(int[?], 0); push(p, 9); return p[0]; }', + prefix + ' let p = rt.new(int, 3); let ignored = 0 && rt.free(p); return p[0]; }', + prefix + ' let p = rt.new(int, 3); let ignored = 1 || rt.free(p); return p[0]; }', + prefix + ' let p = rt.new(int, 50); return p[(u8)300]; }', + prefix + ' let p = rt.new(int, 50); return p[300 as u8]; }', + prefix + ' let p = rt.new(int, 3); let q = p; q[2] = 7; rt.free(p); return 0; }', + prefix + ' let p = rt.new(3); rt.free(p); p = rt.new(3); return p[0]; }', + 'unchecked fn unknown(int* p) {}\n' + prefix + ' let p = rt.new(int, 3); unknown(p); return p[9]; }', + 'fn maybe(int n) { return n; }\n' + prefix + ' let p = rt.new(int, 3); return p[maybe(9)]; }', + prefix + ' let p = rt.new(int, 3); if (1) rt.free(p); return p[0]; }', + prefix + ' let p = rt.new(int, 3); while (0) rt.free(p); return p[0]; }', + prefix + ' let p = rt.new(int, 3); let f = fn() { rt.free(p); return 0; }; return p[0]; }', + prefix + ' let p = rt.new(int[?], 0); push(p, 9); return p[0]; }', ]; for (const source of safe) assert.deepStrictEqual(check(source).warnings, [], source); -const preserved = check('int read(int* p) { return p[0]; }\n' + prefix + ' let p = new(int, 3); read(p); return p[9]; }'); +const preserved = check('int read(int* p) { return p[0]; }\n' + prefix + ' let p = rt.new(int, 3); read(p); return p[9]; }'); assert.match(preserved.warnings[0]?.message || '', /index 9.*length 3/); const unknown = check('int read(int* p) { return p[0]; }\nfn main() { int n = 1; return read(&n); }\n', 'p['); assert.match(unknown.hover, /size: unknown.*lifetime: unknown/); const stack = check('fn main() { let a[3]; return a[2]; }\n', 'a['); assert.match(stack.hover, /size: 3 elements.*lifetime: stack storage/); -const replacement = check('fn main() {\n let owner = new(1), replacement = new(2);\n free(owner); owner = replacement;\n return owner[0];\n}\n', 'replacement;'); +const replacement = check('fn main() {\n let owner = rt.new(1), replacement = rt.new(2);\n rt.free(owner); owner = replacement;\n return owner[0];\n}\n', 'replacement;'); assert.ok(!replacement.diagnostics.some(d => d.severity === 1), JSON.stringify(replacement.diagnostics)); assert.match(replacement.hover, /moved: replaces a released owner in `owner` on line 3/); console.log('ok definite bounds, free and scope lifetimes, alias hovers, and conservative unknown cases'); diff --git a/tests/drivers/thread_context_test.py b/tests/drivers/thread_context_test.py index fa3f144..b237acd 100644 --- a/tests/drivers/thread_context_test.py +++ b/tests/drivers/thread_context_test.py @@ -5,7 +5,7 @@ import argparse import subprocess parser = argparse.ArgumentParser() -parser.add_argument('vm', nargs='?', default='bin/zy') +parser.add_argument('vm', nargs='?', default='build/bin/zy') parser.add_argument('--case', default='') parser.add_argument('--library', default='') parser.add_argument('--backend', choices=('native', 'micro'), default='native') diff --git a/tests/drivers/toolchain_lsp_contract_test.js b/tests/drivers/toolchain_lsp_contract_test.js index 0d1b515..2872282 100644 --- a/tests/drivers/toolchain_lsp_contract_test.js +++ b/tests/drivers/toolchain_lsp_contract_test.js @@ -1,4 +1,6 @@ // pending requirements have explicit protocol oracles, selectable in small groups. +// interpreted hosts (no jit) are far slower; ZC_TEST_TIMEOUT_SCALE stretches every deadline +const TIMEOUT_SCALE = Number(process.env.ZC_TEST_TIMEOUT_SCALE) || 1; const assert = require('assert'); const cp = require('child_process'); const fs = require('fs'); @@ -21,7 +23,7 @@ function run(text, requests = [], uri = 'file:///contracts.zc', files = []) { for (const message of messages) proc.stdin.write(frame(message)); output = Buffer.concat(chunks); } else { - const r = cp.spawnSync(host, process.argv.slice(3), { input: Buffer.concat(messages.map(frame)), timeout: 60000, maxBuffer: 32 << 20 }); + const r = cp.spawnSync(host, process.argv.slice(3), { input: Buffer.concat(messages.map(frame)), timeout: 60000 * TIMEOUT_SCALE, maxBuffer: 32 << 20 }); assert.strictEqual(r.status, 0, String(r.stderr)); output = r.stdout; } const replies = []; let data = output; diff --git a/tests/drivers/tools_contract_test.py b/tests/drivers/tools_contract_test.py index df02b2b..6a98678 100644 --- a/tests/drivers/tools_contract_test.py +++ b/tests/drivers/tools_contract_test.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 """bounded accept/reject tests for standalone build tools and generated images.""" import argparse -import re import struct import subprocess import tempfile @@ -17,32 +16,33 @@ def command(argv, data=None): return r if args.group == 'image': - source = Path('src/core/genome.h').read_text() - body = source.split('{', 1)[1].split('}', 1)[0] - words = [int(x) for x in re.findall(r'-?\d+', body)] + image = Path('build/core/genome.bin').read_bytes() + words = list(struct.unpack('<%dq' % (len(image) // 8), image)) pack = lambda xs: b''.join(struct.pack('<q', x) for x in xs) - good = command(['bin/mkgenome'], pack(words)) - assert good.returncode == 0 and f'#define GLEN {len(words)}'.encode() in good.stdout - for data in [b'', b'x', pack(words[:6]), pack(words) + b'x']: - r = command(['bin/mkgenome'], data) - assert r.returncode != 0 and b'incomplete' in r.stderr + good = command(['build/bin/mkgenome'], image) + assert good.returncode == 0 and good.stdout == image + for data in [b'', b'x', pack(words[:6]), image + b'x']: + r = command(['build/bin/mkgenome'], data) + assert r.returncode != 0 and b'incomplete' in r.stderr and not r.stdout for index in range(3): changed = words[:]; changed[index] ^= 2 - r = command(['bin/mkgenome'], pack(changed)) - assert r.returncode != 0 and b'not a genome' in r.stderr - print('ok mkgenome roundtrip, truncation, alignment and map-header mutation contracts') + r = command(['build/bin/mkgenome'], pack(changed)) + assert r.returncode != 0 and b'not a genome' in r.stderr and not r.stdout + r = command(['build/bin/mkgenome'], b'zc: line 1: something is wrong\nzc: 1 error\n') + assert r.returncode != 0 and b'something is wrong' in r.stderr and not r.stdout + print('ok mkgenome roundtrip, truncation, alignment, map-header mutation and diagnostic contracts') elif args.group == 'map': for preset in ['default', 'small', 'run', 'lsp', 'fixed']: with tempfile.TemporaryDirectory(prefix='tinyvm preset ') as directory: - r = command(['bin/genmap', '--preset', preset, '-o', directory]) + r = command(['build/bin/genmap', '--preset', preset, '-o', directory]) assert r.returncode == 0, (preset, r.stderr) assert (Path(directory) / 'memmap.h').is_file() - assert command(['bin/genmap', '--preset', 'default', '--check']).returncode == 0 + assert command(['build/bin/genmap', '--preset', 'default', '--check']).returncode == 0 for options in [['--words', '-1'], ['--heap', '0'], ['--data', '0'], ['--symbols', '-1'], ['--total', '999999999999999999999'], ['--preset', 'imaginary']]: - r = command(['bin/genmap', *options, '--check']) + r = command(['build/bin/genmap', *options, '--check']) assert r.returncode != 0, options with tempfile.TemporaryDirectory(prefix='tinyvm map ') as directory: - r = command(['bin/genmap', '--preset', 'small', '--locals', '32', '--symbols', '64', '-o', directory]) + r = command(['build/bin/genmap', '--preset', 'small', '--locals', '32', '--symbols', '64', '-o', directory]) assert r.returncode == 0, r.stderr for name in ['memmap.h', 'rt.zc', 'compiler.zc', 'map.txt']: assert (Path(directory) / name).is_file(), name @@ -50,7 +50,7 @@ elif args.group == 'map': else: r = command(['bash', '-n', 'tinyvm.sh']) assert r.returncode == 0, r.stderr - for script in ['src/tools/automap.sh', 'src/tools/mklsp.sh', 'src/tools/mklspwasm.sh', 'src/tools/mkvsix.sh', 'src/tools/embed.sh']: + for script in ['src/tools/automap.sh', 'src/tools/mklsp.sh', 'src/tools/mklspwasm.sh', 'src/tools/mkvsix.sh', 'src/tools/findcc.sh', 'src/tools/bootstrap-check.sh']: assert command(['bash', '-n', script]).returncode == 0 assert command(['bash', 'tinyvm.sh', 'imaginary-command']).returncode != 0 assert Path('tinyvm.bat').is_file() diff --git a/tests/drivers/wasm_test.js b/tests/drivers/wasm_test.js index e4f7ecb..56f32a2 100644 --- a/tests/drivers/wasm_test.js +++ b/tests/drivers/wasm_test.js @@ -1,9 +1,9 @@ -// runs programs on the webassembly build (src/web/zc.wasm) and compares with the expected output. +// runs programs on the webassembly build (build/web/zc.wasm, from make web) and compares with the expected output. // usage: node tests/drivers/wasm_test.js [path/to/zc.wasm] const fs = require('fs'); const ZC = require('../../src/web/zc.js'); (async () => { - const zc = await ZC.load(fs.readFileSync(process.argv[2] || __dirname + '/../../src/web/zc.wasm')); + const zc = await ZC.load(fs.readFileSync(process.argv[2] || __dirname + '/../../build/web/zc.wasm')); const cases = [ ['examples/hello.zc', 'hello, world!'], ['examples/fact.zc', '1101110101111100000000\n3628800\n'], diff --git a/tests/drivers/word_literals_test.py b/tests/drivers/word_literals_test.py index a2d230d..cfac811 100644 --- a/tests/drivers/word_literals_test.py +++ b/tests/drivers/word_literals_test.py @@ -5,7 +5,7 @@ import subprocess values = [0, 1, (1 << 62) - 1, 1 << 62, (1 << 63) - 1, -(1 << 62), -(1 << 62) - 1, -(1 << 63), -1] source = 'fn main() {\n' + ''.join(f'put_n({value}); put_c(10);\n' for value in values) + 'return 0; }\n' expected = ''.join(str(value) + '\n' for value in values) -for command in [['bin/zy'], ['bin/zym'], ['node', 'src/js/tinyvm.js']]: +for command in [['build/bin/zy'], ['build/bin/zym'], ['node', 'src/js/tinyvm.js']]: result = subprocess.run(command, input=source, text=True, capture_output=True, timeout=20) assert result.returncode == 0 and result.stdout == expected, (command, result.stdout, result.stderr) print('ok full-width positive and negative word literals: ' + command[0]) @@ -13,7 +13,7 @@ source = '''immediate fn add_word_operator() { next(); add_binop("**", 11, "4611 add_word_operator fn main() { put_n(0 ** 0); put_c(10); return 0; } ''' -for command in [['bin/zy'], ['bin/zym'], ['node', 'src/js/tinyvm.js']]: +for command in [['build/bin/zy'], ['build/bin/zym'], ['node', 'src/js/tinyvm.js']]: result = subprocess.run(command, input=source, text=True, capture_output=True, timeout=20) assert result.returncode == 0 and result.stdout == '4611686018427387904\n', (command, result.stdout, result.stderr) print('ok full-width word in a stored operator sequence: ' + command[0]) |
