aboutsummaryrefslogtreecommitdiff
path: root/tests/drivers
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-15 17:03:03 +0330
committerGravatar zinc <zinc@downsta.ir> 2026-10-15 17:03:03 +0330
commit7cb983bbea98d2038b2a8015a6d1f1afd40991c9 (patch)
treeec3d88a15801207fc6d8d3c6d142617ffec85460 /tests/drivers
parentmerge work/borrow: borrow returns before definition, borrows clause, borrow m... (diff)
parentbench: rss probe path fix (diff)
merge work/actors: wasm seed bootstrap (seed/MANIFEST), shared copy-on-write actor memory, top-level hold blocks on all backends and lsp
Diffstat (limited to 'tests/drivers')
-rw-r--r--tests/drivers/hold_lsp_test.js54
-rw-r--r--tests/drivers/hold_test.py28
-rw-r--r--tests/drivers/imagekit.py2
3 files changed, 83 insertions, 1 deletions
diff --git a/tests/drivers/hold_lsp_test.js b/tests/drivers/hold_lsp_test.js
new file mode 100644
index 0000000..65d8154
--- /dev/null
+++ b/tests/drivers/hold_lsp_test.js
@@ -0,0 +1,54 @@
+// hold blocks in the language server: hover on hold, shared and a shared variable,
+// completion offers the keywords and marks shared variables, and a use outside the
+// block is a diagnostic. usage: node tests/drivers/hold_lsp_test.js path/to/zclsp
+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];
+const uri = 'file:///hold.zc';
+const frame = m => { const body = Buffer.from(JSON.stringify(m)); return Buffer.concat([Buffer.from('Content-Length: ' + body.length + '\r\n\r\n'), body]); };
+function session(text, request) {
+ const messages = [
+ { jsonrpc: '2.0', id: 1, method: 'initialize', params: {} },
+ { jsonrpc: '2.0', method: 'textDocument/didOpen', params: { textDocument: { uri, version: 1, languageId: 'zc', text } } },
+ ];
+ if (request) messages.push(Object.assign({ jsonrpc: '2.0', id: 2 }, request));
+ messages.push({ jsonrpc: '2.0', method: 'exit' });
+ const result = cp.spawnSync(server, [], { input: Buffer.concat(messages.map(frame)), timeout: 15000 * TIMEOUT_SCALE, maxBuffer: 8 << 20 });
+ assert.strictEqual(result.status, 0, String(result.stderr));
+ const out = []; let data = result.stdout;
+ while (data.length) {
+ const end = data.indexOf('\r\n\r\n');
+ const count = Number(/Content-Length:\s*(\d+)/i.exec(data.subarray(0, end).toString())[1]);
+ out.push(JSON.parse(data.subarray(end + 4, end + 4 + count).toString()));
+ data = data.subarray(end + 4 + count);
+ }
+ return out;
+}
+function at(text, needle, nth = 0, delta = 0) {
+ let o = -1; for (let i = 0; i <= nth; i++) o = text.indexOf(needle, o + 1);
+ assert.ok(o >= 0, needle); o += delta;
+ const before = text.slice(0, o);
+ return { line: before.split('\n').length - 1, character: before.length - before.lastIndexOf('\n') - 1 };
+}
+const hover = (text, needle, nth) => { const r = session(text, { method: 'textDocument/hover', params: { textDocument: { uri }, position: at(text, needle, nth) } }).find(m => m.id === 2); return r && r.result && r.result.contents.value || ''; };
+const good = 'namespace;\ninclude rt;\nhold {\n shared let count = 0;\n fn inc() { count = count + 1; return count; }\n}\nfn main() { return inc(); }\n';
+let h = hover(good, 'hold');
+assert.ok(h.includes('monitor') && h.includes('lock'), 'hover on hold explains the block: ' + h);
+h = hover(good, 'shared');
+assert.ok(h.includes('every actor'), 'hover on shared: ' + h);
+h = hover(good, 'count', 1);
+assert.ok(h.includes('shared in hold block 1'), 'hover on a shared variable names its block: ' + h);
+const bad = 'namespace;\ninclude rt;\nhold { shared let count; fn inc() { count++; return count; } }\nfn main() { count = 2; return inc(); }\n';
+const diags = session(bad).filter(m => m.method === 'textDocument/publishDiagnostics').flatMap(m => m.params.diagnostics);
+assert.ok(diags.some(d => d.message.includes('count: is shared in a hold block') && d.range.start.line === 3), 'use outside the block is a diagnostic on its line: ' + JSON.stringify(diags));
+const partial = 'namespace;\ninclude rt;\nhold { shared let count; fn inc() { count++; return count; } }\nho\nfn main() { return inc(); }\n';
+let r = session(partial, { method: 'textDocument/completion', params: { textDocument: { uri }, position: at(partial, '\nho', 0, 3) } }).find(m => m.id === 2);
+let items = (r.result.items || r.result);
+assert.ok(items.some(i => i.label === 'hold'), 'completion offers hold');
+const partial2 = 'namespace;\ninclude rt;\nhold { shared let count; fn inc() { cou; return count; } }\nfn main() { return inc(); }\n';
+r = session(partial2, { method: 'textDocument/completion', params: { textDocument: { uri }, position: at(partial2, 'cou;', 0, 3) } }).find(m => m.id === 2);
+items = (r.result.items || r.result);
+const it = items.find(i => i.label === 'count');
+assert.ok(it && String(it.detail).startsWith('shared'), 'completion marks a shared variable: ' + JSON.stringify(it));
+console.log('hold lsp: 6 checks passed');
diff --git a/tests/drivers/hold_test.py b/tests/drivers/hold_test.py
new file mode 100644
index 0000000..1f6ea39
--- /dev/null
+++ b/tests/drivers/hold_test.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+# hold blocks (spec 4.16): accepted programs print what their first line expects, rejected
+# ones report the error their first line names (exit 1), and trapping ones stop with
+# exit 2 and the message named. usage: hold_test.py [VM]
+from pathlib import Path
+import subprocess, sys
+root = Path(__file__).resolve().parents[2]
+vm = sys.argv[1] if len(sys.argv) > 1 else 'build/bin/zy'
+count = bad = 0
+for case in sorted((root / 'tests/hold').glob('*/*.zc')):
+ src = case.read_text(); first = src.splitlines()[0]
+ p = subprocess.run([str(root / vm) if not Path(vm).is_absolute() else vm], input=src, text=True, capture_output=True, cwd=root, timeout=60)
+ out = p.stdout + p.stderr
+ if first.startswith('// expect: '):
+ ok = p.returncode == 0 and p.stdout.strip() == first[11:].strip()
+ elif first.startswith('// error: '):
+ ok = p.returncode == 1 and first[10:].strip() in out
+ elif first.startswith('// trap: '):
+ ok = p.returncode == 2 and first[9:].strip() in out
+ else:
+ ok = False
+ count += 1
+ if not ok:
+ bad += 1; print(f'FAIL {case.relative_to(root)}: rc={p.returncode} out={out[:300]!r}')
+ else:
+ print(f'ok {case.relative_to(root)}')
+print(f'{count - bad}/{count} hold fixtures passed')
+sys.exit(1 if bad or not count else 0)
diff --git a/tests/drivers/imagekit.py b/tests/drivers/imagekit.py
index 6cc083f..5df4810 100644
--- a/tests/drivers/imagekit.py
+++ b/tests/drivers/imagekit.py
@@ -9,7 +9,7 @@ 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']
+ 'src/core/fops.h', 'src/core/bops.h', 'src/core/stack.h', 'src/core/hold.h', 'src/core/genome.h']
def compiler():