aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-14 10:38:17 +0000
committerGravatar zinc <zinc@downsta.ir> 2026-10-14 10:38:17 +0000
commit93340f97dce404a008f489729df144ace53528ab (patch)
treecbada309f3a4683d083d84484f11952418c8dd2b /tests
parenttodo: handoff section with state, conventions, in-flight work and architectur... (diff)
fix std hover, full capture terminator, bare static calls, void main in run-only images
- hover: a symbol from an embedded library source (tinyvm:std.zc, tinyvm:icon.zc) was looked up only in the document; look it up by its qualified name in the library source (hover_embedded_sym), and render a method found that way by its line (bs_hover). - std.capture: allocate one word more than the 256 it holds, so end() terminates inside the buffer. - a bare call to a static function of the enclosing struct resolves like a bare struct constant (mstruct is set for every non-generic struct's methods); the struct's own wins over a global. - run.zc declares main untyped (fn main();), so a run-only image accepts int, void or untyped main with both compilers.
Diffstat (limited to 'tests')
-rw-r--r--tests/drivers/lsp_test.js12
-rw-r--r--tests/drivers/runtime_image_test.py2
-rw-r--r--tests/programs/capture_long.zc26
-rw-r--r--tests/programs/struct_static_call.zc31
-rw-r--r--tests/programs/void_main.zc8
-rwxr-xr-xtests/test.sh5
6 files changed, 84 insertions, 0 deletions
diff --git a/tests/drivers/lsp_test.js b/tests/drivers/lsp_test.js
index 0f4abe1..2ad9181 100644
--- a/tests/drivers/lsp_test.js
+++ b/tests/drivers/lsp_test.js
@@ -199,6 +199,18 @@ const tmsrc = 'struct point {\n\tlet x;\n\t// the sum of the parts\n\tint sum(in
hv = hover(tmsrc, 7, at(tmsrc, 7, 'sum'));
check(hv && hv.includes('int sum(int k)') && hv.includes('the sum of the parts'), 'a typed method shows its return type and parameters', hv);
+// members of the embedded standard library hover like the document's own: the group's static
+// function, the group, and a top-level function, each with the comment from std.zc
+const stdsrc = 'include rt, std;\nfn main() { char[24] b; rt.put_s(std.Text.itoa(5, b)); return std.array_contains; }\n';
+hv = hover(stdsrc, 1, at(stdsrc, 1, 'itoa'));
+check(hv && hv.includes('static int itoa(int n, char* dst)') && hv.includes('writes n in decimal into dst'), 'hover on a std group function shows its signature and doc from std.zc', hv);
+hv = hover(stdsrc, 1, at(stdsrc, 1, 'Text'));
+check(hv && hv.includes('struct Text') && hv.includes('helpers for char* strings'), 'hover on a std group shows the comment of the group', hv);
+hv = hover(stdsrc, 1, at(stdsrc, 1, 'array_contains'));
+check(hv && hv.includes('array_contains<T>') && hv.includes('true when an element equals value'), 'hover on a top-level std function shows its doc', hv);
+hv = hover(stdsrc, 1, at(stdsrc, 1, 'put_s'));
+check(hv && hv.includes('int put_s(char* s)'), 'hover on a runtime function still works beside the std ones', hv);
+
// externs are declarations, so they draw no "never reassigned" hint
const ext = 'extern double sqrt(double);\nextern double pow(double x, double y);\nfn main() {\n\treturn 0;\n}\n';
r = run([init(1), open(A, ext)]);
diff --git a/tests/drivers/runtime_image_test.py b/tests/drivers/runtime_image_test.py
index d214613..6c44349 100644
--- a/tests/drivers/runtime_image_test.py
+++ b/tests/drivers/runtime_image_test.py
@@ -18,6 +18,8 @@ cases = [
('enhancements', 'include rt; fn main() { rt.put_n(17 % 5); rt.put_c(32); rt.put_n(17 / 5); }', b'2 3'),
('array algorithms', Path('examples/learn/31-array-algorithms.zc').read_text(), Path('examples/learn/31-array-algorithms.out').read_bytes()),
('owned pop', 'include rt; int*.u take() { int*[?] a = rt.new(int*[?]); int* p = rt.new(int); *p = 8; a.push(p); return a.pop(); } fn main() { let p = take(); rt.put_n(rt.isobj(p)); rt.put_c(32); rt.put_n(*p); }', b'1 8'),
+ # main may be void: run.zc's prototype of main must not fix its type (docs/SPEC.md 5.2.5)
+ ('void main', Path('tests/programs/void_main.zc').read_text(), b'void main\n'),
]
prefix = b''.join(Path(f).read_bytes() + b'\n' for f in ['src/runtime/rt.zc', 'src/core/core.zc', 'src/runtime/run.zc'])
for name, source, expected in cases:
diff --git a/tests/programs/capture_long.zc b/tests/programs/capture_long.zc
new file mode 100644
index 0000000..52736a4
--- /dev/null
+++ b/tests/programs/capture_long.zc
@@ -0,0 +1,26 @@
+namespace;
+include rt, std;
+
+// a capture holds 256 characters and drops the rest. end() must still put its
+// 0 inside the buffer, even when the capture was full: the length is 256 and the
+// last kept character is the 256th one written (index 255, 'a' + 255 % 26 = 'v').
+// a block allocated right after the buffer is checked afterwards: a 0 written one
+// word past the buffer would land in its header and read as a bad or freed block.
+memdebug on; // header checks make a write into a neighbouring block visible
+fn capture_long() {
+ let buf = std.capture.start();
+ int* next = rt.new(8);
+ int i = 0;
+ while (i < 300) { rt.put_c('a' + i % 26); i++; }
+ std.capture.end(buf);
+ next[0] = 7; // stops with "bad heap pointer" if its header was overwritten
+ rt.put_n(next[0]); rt.put_c(10); // 7
+ return buf;
+}
+memdebug off; // the header check only searches 64 words back, so the long buffer is read unchecked
+fn main() {
+ char* buf = capture_long();
+ rt.put_n(rt.len_w(buf)); rt.put_c(10); // 256
+ rt.put_c(buf[255]); rt.put_c(10); // v
+ return 0;
+}
diff --git a/tests/programs/struct_static_call.zc b/tests/programs/struct_static_call.zc
new file mode 100644
index 0000000..73fc0fc
--- /dev/null
+++ b/tests/programs/struct_static_call.zc
@@ -0,0 +1,31 @@
+namespace;
+include rt;
+
+// a static function of a struct can be called by its bare name inside the struct's
+// methods and static functions, as a struct constant can. the struct's own function
+// wins over a top-level one of the same name; outside the struct only S.name reaches it.
+int lim() { return 100; }
+int base() { return 7; }
+
+struct S {
+ int v;
+ static int lim() { return 3; }
+ static int twice(int x) { return x * 2; }
+ static int both() { return lim() + twice(4); }
+ int get() { return lim() + this.v; }
+ int scaled() { return twice(this.v) + later(); }
+ int top() { return base(); }
+ static int later() { return 1000; }
+}
+
+int main() {
+ S s;
+ s.v = 5;
+ rt.put_n(S.lim()); rt.put_c(32); // 3
+ rt.put_n(lim()); rt.put_c(32); // 100, the global one
+ rt.put_n(S.both()); rt.put_c(32); // 3 + 8
+ rt.put_n(s.get()); rt.put_c(32); // 3 + 5
+ rt.put_n(s.scaled()); rt.put_c(32); // 10 + 1000, a static function declared later
+ rt.put_n(s.top()); rt.put_c(10); // 7, a global that the struct has no member of
+ return 0;
+}
diff --git a/tests/programs/void_main.zc b/tests/programs/void_main.zc
new file mode 100644
index 0000000..b4fde70
--- /dev/null
+++ b/tests/programs/void_main.zc
@@ -0,0 +1,8 @@
+namespace;
+include rt;
+
+// main may be declared void: the runtime ignores what main returns (docs/SPEC.md 5.2.5, 8.1.3),
+// so a run-only image, whose run.zc declares int main();, accepts it as zy does.
+void main() {
+ rt.put_s("void main"); rt.put_c(10);
+}
diff --git a/tests/test.sh b/tests/test.sh
index 298c356..c800e8f 100755
--- a/tests/test.sh
+++ b/tests/test.sh
@@ -128,6 +128,7 @@ run(){ # $1 = vm command, $2 = label
chk "$($vm < tests/programs/genmeth.zc | md5sum)" "$(md5sum < tests/programs/genmeth.expected)" "$l methods of generic structs are compiled per type arguments"
chk "$($vm < tests/programs/static.zc)" "14 5 9" "$l static functions of a struct are called as Struct.name(args)"
chk "$($vm < tests/programs/struct_const.zc)" "24 49 24 23 19 55 12 99" "$l constants declared in a struct are reached as Struct.NAME, and by bare name in its methods"
+ chk "$($vm < tests/programs/struct_static_call.zc)" "3 100 11 8 1010 7" "$l a static function of a struct is called by its bare name in its methods, and wins over a global of the same name"
chk "$($vm < tests/programs/methodown.zc)" "7 9" "$l struct methods and functions return owned objects (.u)"
chk "$($vm < tests/programs/typedmethods.zc)" "14 5 13" "$l struct methods declare a return type and typed parameters"
chk "$($vm < tests/programs/enumscope.zc)" "0 5 6 blue" "$l enum members are reached as Enum.MEMBER, also in a case"
@@ -158,6 +159,9 @@ hello world!42
1
1
0" "$l capture.start() collects output and end() hands back the string"
+ chk "$($vm < tests/programs/capture_long.zc)" "7
+256
+v" "$l a full capture is still terminated inside its buffer"
chk "$($vm < tests/programs/sys.zc)" "2 zy time" "$l syscalls: write, read, time"
chk "$($vm < tests/programs/memleak.zc)" $'leak: line 8 3 words\n1' "$l memdebug names a live allocation"
chk "$($vm < tests/programs/uaf.zc)" $'zc: line 10: warning: use after free: this storage\'s lifetime ended on line 9\nzy: use after free from line 7' "$l memdebug: use after free"
@@ -353,6 +357,7 @@ s_runonly() {
runonly wide tests/programs/wide.zc
runonly selfpatch examples/selfpatch.zc
runonly gc tests/programs/gc.zc
+ runonly voidmain tests/programs/void_main.zc
runonly float tests/programs/float.zc
runonly ftruth tests/programs/ftruth.zc
runonly ftext tests/programs/ftext.zc