Skip to content

Commit 91a5cb4

Browse files
test: 18 tests for list_fns/fn_arity/fn_source/get_scope_vars introspection builtins
1 parent 2bd9abc commit 91a5cb4

1 file changed

Lines changed: 165 additions & 0 deletions

File tree

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# Tests for user-defined-function introspection builtins.
2+
#
3+
# list_fns() / list_defined_fns() → sorted array of user fn names
4+
# fn_arity(name) → param count or null
5+
# fn_source(name) → formatted source or null
6+
# get_scope_vars() → dict of global variables
7+
8+
fn assert_eq(actual, expected, msg) {
9+
if actual != expected {
10+
test_record_failure(msg + ": expected " + to_string(expected) + " got " + to_string(actual));
11+
}
12+
}
13+
14+
fn assert_true(cond, msg) { if !cond { test_record_failure(msg); } }
15+
16+
fn assert_null(v, msg) { if v != null { test_record_failure(msg + ": expected null, got " + to_string(v)); } }
17+
18+
# Helpers used as subjects for introspection tests below.
19+
20+
fn add(a, b) { a + b }
21+
22+
fn greet(name) {
23+
str_concat("Hello, ", name)
24+
}
25+
26+
fn no_args_fn() { 42 }
27+
28+
fn five_args(a, b, c, d, e) { a + b + c + d + e }
29+
30+
# ── list_fns ──────────────────────────────────────────────────────────────────
31+
32+
fn test_list_fns_is_array() {
33+
h fns = list_fns()
34+
assert_true(type_of(fns) == "array", "list_fns returns array")
35+
}
36+
37+
fn test_list_fns_contains_defined() {
38+
h fns = list_fns()
39+
h has_add = 0
40+
h has_greet = 0
41+
h i = 0
42+
while i < arr_len(fns) {
43+
if arr_get(fns, i) == "add" { has_add = 1 }
44+
if arr_get(fns, i) == "greet" { has_greet = 1 }
45+
i = i + 1
46+
}
47+
assert_eq(has_add, 1, "list_fns contains add")
48+
assert_eq(has_greet, 1, "list_fns contains greet")
49+
}
50+
51+
fn test_list_fns_sorted() {
52+
h fns = list_fns()
53+
h sorted_ok = 1
54+
h i = 1
55+
while i < arr_len(fns) {
56+
if arr_get(fns, i - 1) > arr_get(fns, i) { sorted_ok = 0 }
57+
i = i + 1
58+
}
59+
assert_eq(sorted_ok, 1, "list_fns output is sorted")
60+
}
61+
62+
fn test_list_fns_excludes_lambdas() {
63+
# Lambdas should be hidden from list_fns.
64+
h lam = fn(x) (x * 2)
65+
h fns = list_fns()
66+
h found_lambda = 0
67+
h i = 0
68+
while i < arr_len(fns) {
69+
h n = arr_get(fns, i)
70+
if str_starts_with(n, "__lambda") { found_lambda = 1 }
71+
if str_starts_with(n, "__rt_lambda") { found_lambda = 1 }
72+
i = i + 1
73+
}
74+
assert_eq(found_lambda, 0, "lambdas not in list_fns")
75+
}
76+
77+
fn test_list_defined_fns_alias() {
78+
# list_defined_fns and list_fns must return identical results.
79+
h a = list_fns()
80+
h b = list_defined_fns()
81+
assert_eq(arr_len(a), arr_len(b), "same length")
82+
h i = 0
83+
while i < arr_len(a) {
84+
assert_eq(arr_get(a, i), arr_get(b, i), str_concat("element ", to_string(i)))
85+
i = i + 1
86+
}
87+
}
88+
89+
# ── fn_arity ──────────────────────────────────────────────────────────────────
90+
91+
fn test_fn_arity_zero() {
92+
assert_eq(fn_arity("no_args_fn"), 0, "no_args_fn arity 0")
93+
}
94+
95+
fn test_fn_arity_one() {
96+
assert_eq(fn_arity("greet"), 1, "greet arity 1")
97+
}
98+
99+
fn test_fn_arity_two() {
100+
assert_eq(fn_arity("add"), 2, "add arity 2")
101+
}
102+
103+
fn test_fn_arity_five() {
104+
assert_eq(fn_arity("five_args"), 5, "five_args arity 5")
105+
}
106+
107+
fn test_fn_arity_unknown_returns_null() {
108+
assert_null(fn_arity("this_fn_does_not_exist_xyzzy"), "unknown → null")
109+
}
110+
111+
# ── fn_source ─────────────────────────────────────────────────────────────────
112+
113+
fn test_fn_source_returns_string() {
114+
h src = fn_source("add")
115+
assert_true(type_of(src) == "string", "fn_source returns string")
116+
assert_true(str_len(src) > 0, "non-empty source")
117+
}
118+
119+
fn test_fn_source_contains_name() {
120+
h src = fn_source("add")
121+
assert_true(re_match("add", src) == 1, "source contains function name")
122+
}
123+
124+
fn test_fn_source_contains_params() {
125+
h src = fn_source("add")
126+
assert_true(re_match("a", src) == 1 and re_match("b", src) == 1, "source contains params a and b")
127+
}
128+
129+
fn test_fn_source_contains_fn_keyword() {
130+
h src = fn_source("greet")
131+
assert_true(re_match("fn", src) == 1, "source starts with fn keyword")
132+
}
133+
134+
fn test_fn_source_unknown_returns_null() {
135+
assert_null(fn_source("totally_undefined_fn_abc"), "unknown → null")
136+
}
137+
138+
# ── get_scope_vars ────────────────────────────────────────────────────────────
139+
140+
h GLOBAL_CONST = 123
141+
h GLOBAL_STR = "hello"
142+
143+
fn test_get_scope_vars_returns_dict() {
144+
h sv = get_scope_vars()
145+
assert_true(type_of(sv) == "dict", "get_scope_vars returns dict")
146+
}
147+
148+
fn test_get_scope_vars_contains_globals() {
149+
h sv = get_scope_vars()
150+
assert_eq(dict_get(sv, "GLOBAL_CONST"), 123, "GLOBAL_CONST visible")
151+
assert_eq(dict_get(sv, "GLOBAL_STR"), "hello", "GLOBAL_STR visible")
152+
}
153+
154+
fn test_get_scope_vars_excludes_double_underscore() {
155+
h sv = get_scope_vars()
156+
# Keys starting with __ must not appear.
157+
h bad = 0
158+
h keys = dict_keys(sv)
159+
h i = 0
160+
while i < arr_len(keys) {
161+
if str_starts_with(arr_get(keys, i), "__") { bad = 1 }
162+
i = i + 1
163+
}
164+
assert_eq(bad, 0, "no __ prefixed keys in get_scope_vars")
165+
}

0 commit comments

Comments
 (0)