aboutsummaryrefslogtreecommitdiff
path: root/tests/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'tests/drivers')
-rw-r--r--tests/drivers/actor_image_test.py15
-rw-r--r--tests/drivers/actor_test.py2
-rw-r--r--tests/drivers/analysis_cache_test.js4
-rw-r--r--tests/drivers/array_methods_test.py2
-rw-r--r--tests/drivers/cli_help_test.py7
-rw-r--r--tests/drivers/completion_test.js4
-rw-r--r--tests/drivers/core_lsp_test.js4
-rw-r--r--tests/drivers/enhancement_backends_test.py6
-rw-r--r--tests/drivers/ext_cache_test.js15
-rw-r--r--tests/drivers/float_backends_test.py2
-rw-r--r--tests/drivers/fuzz_compiler.py40
-rw-r--r--tests/drivers/fuzz_lsp.js8
-rw-r--r--tests/drivers/fuzz_malformed.py2
-rw-r--r--tests/drivers/fuzz_wasm.js2
-rw-r--r--tests/drivers/genome_blob_test.py18
-rw-r--r--tests/drivers/hints_test.js4
-rw-r--r--tests/drivers/hover_features_test.js4
-rw-r--r--tests/drivers/hover_surface_test.js4
-rw-r--r--tests/drivers/hover_values_test.js4
-rw-r--r--tests/drivers/icon_test.py2
-rw-r--r--tests/drivers/image_run.js7
-rw-r--r--tests/drivers/imagekit.py34
-rw-r--r--tests/drivers/layout_config_test.py2
-rw-r--r--tests/drivers/lsp_growth_test.js4
-rw-r--r--tests/drivers/lsp_sandbox.js6
-rw-r--r--tests/drivers/lsp_test.js8
-rw-r--r--tests/drivers/memory_limits_test.py2
-rw-r--r--tests/drivers/nav_test.js4
-rw-r--r--tests/drivers/protocol_growth_test.js4
-rw-r--r--tests/drivers/runtime_image_test.py19
-rw-r--r--tests/drivers/seed_growth_test.py23
-rw-r--r--tests/drivers/stack_growth_test.py56
-rw-r--r--tests/drivers/storage_analysis_test.js48
-rw-r--r--tests/drivers/thread_context_test.py2
-rw-r--r--tests/drivers/toolchain_lsp_contract_test.js4
-rw-r--r--tests/drivers/tools_contract_test.py34
-rw-r--r--tests/drivers/wasm_test.js4
-rw-r--r--tests/drivers/word_literals_test.py4
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])