merge integrate/native-testing: add native El test suite

This commit is contained in:
Will Anderson
2026-05-05 00:10:00 -05:00
10 changed files with 1243 additions and 0 deletions
+91
View File
@@ -73,6 +73,97 @@ jobs:
EL_HOME="$(pwd)" \
bash tests/html_sanitizer/run.sh
# Native El test suites (elc --test, compile-link-run)
- name: Run tests — native (core)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_core.el > /tmp/el_native_core.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_core.c "$RUNTIME/el_runtime.c" \
-lcurl -lpthread -lm -o /tmp/el_native_core
/tmp/el_native_core
- name: Run tests — native (text)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_text.el > /tmp/el_native_text.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_text.c "$RUNTIME/el_runtime.c" \
-lcurl -lpthread -lm -o /tmp/el_native_text
/tmp/el_native_text
- name: Run tests — native (string)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_string.el > /tmp/el_native_string.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_string.c "$RUNTIME/el_runtime.c" \
-lcurl -lpthread -lm -o /tmp/el_native_string
/tmp/el_native_string
- name: Run tests — native (math)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_math.el > /tmp/el_native_math.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_math.c "$RUNTIME/el_runtime.c" \
-lcurl -lpthread -lm -o /tmp/el_native_math
/tmp/el_native_math
- name: Run tests — native (state)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_state.el > /tmp/el_native_state.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_state.c "$RUNTIME/el_runtime.c" \
-lcurl -lpthread -lm -o /tmp/el_native_state
/tmp/el_native_state
- name: Run tests — native (time)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_time.el > /tmp/el_native_time.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_time.c "$RUNTIME/el_runtime.c" \
-lcurl -lpthread -lm -o /tmp/el_native_time
/tmp/el_native_time
- name: Run tests — native (json)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_json.el > /tmp/el_native_json.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_json.c "$RUNTIME/el_runtime.c" \
-lcurl -lpthread -lm -o /tmp/el_native_json
/tmp/el_native_json
- name: Run tests — native (env)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_env.el > /tmp/el_native_env.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_env.c "$RUNTIME/el_runtime.c" \
-lcurl -lpthread -lm -o /tmp/el_native_env
/tmp/el_native_env
- name: Run tests — native (fs)
run: |
set -euo pipefail
ELC="$(pwd)/dist/platform/elc"
RUNTIME="$(pwd)/el-compiler/runtime"
"$ELC" --test tests/native/test_fs.el > /tmp/el_native_fs.c
gcc -O2 -I "$RUNTIME" /tmp/el_native_fs.c "$RUNTIME/el_runtime.c" \
-lcurl -lpthread -lm -o /tmp/el_native_fs
/tmp/el_native_fs
- name: Publish El SDK to Artifact Registry (dev)
env:
GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
+80
View File
@@ -0,0 +1,80 @@
// test_codegen_js.el - basic tests for JS codegen features.
//
// These tests verify that core El language features produce correct values
// when compiled and executed via the C backend. They serve as a
// regression baseline for the codegen pipeline.
test "arithmetic" {
let x: Int = 2 + 3
assert x == 5, "addition"
let y: Int = 10 - 4
assert y == 6, "subtraction"
let z: Int = 3 * 4
assert z == 12, "multiplication"
let w: Int = 15 / 3
assert w == 5, "division"
}
test "string-concat" {
let a: String = "hello"
let b: String = " world"
let c: String = a + b
assert c == "hello world", "string concatenation"
}
test "str-len" {
let n: Int = str_len("hello")
assert n == 5, "str_len hello"
let m: Int = str_len("")
assert m == 0, "str_len empty"
}
test "bool-logic" {
let t = true
let f = false
assert t, "true is truthy"
assert !f, "false negated is truthy"
assert t && !f, "true and not false"
assert t || f, "true or false"
}
test "list-operations" {
let lst: [Int] = native_list_empty()
let lst = native_list_append(lst, 10)
let lst = native_list_append(lst, 20)
let lst = native_list_append(lst, 30)
let n: Int = native_list_len(lst)
assert n == 3, "list length 3"
let v0: Int = native_list_get(lst, 0)
let v1: Int = native_list_get(lst, 1)
let v2: Int = native_list_get(lst, 2)
assert v0 == 10, "first element"
assert v1 == 20, "second element"
assert v2 == 30, "third element"
}
test "str-slice" {
let s: String = str_slice("hello world", 6, 11)
assert s == "world", "slice from 6 to 11"
}
test "str-contains" {
assert str_contains("hello world", "world"), "contains world"
assert !str_contains("hello world", "xyz"), "does not contain xyz"
}
test "int-to-str" {
let s: String = int_to_str(42)
assert s == "42", "int to string"
}
test "str-to-int" {
let n: Int = str_to_int("123")
assert n == 123, "string to int"
}
test "str-starts-ends" {
assert str_starts_with("hello world", "hello"), "starts with hello"
assert str_ends_with("hello world", "world"), "ends with world"
assert !str_starts_with("hello world", "world"), "does not start with world"
}
+82
View File
@@ -0,0 +1,82 @@
// test_env.el - native test suite for runtime/env.el
//
// Covers: env() for reading environment variables, args() returning a list,
// state_set/get/del/keys via the env module re-exports, uuid_new/uuid_v4
// format validation.
test "env-missing-returns-empty" {
let v: String = env("__EL_NO_SUCH_VAR_XYZ__")
assert v == "", "missing env var returns empty string"
}
test "env-path-is-set" {
// PATH is expected to be set in virtually any UNIX environment.
let v: String = env("PATH")
assert str_len(v) > 0, "PATH env var is non-empty"
}
test "env-home-is-set" {
// HOME is present on macOS/Linux test environments.
let v: String = env("HOME")
assert str_len(v) > 0, "HOME env var is non-empty"
}
test "args-returns-list" {
let a: [String] = args()
// Even with no arguments, the list should be non-null (at minimum the
// program name is argv[0]).
let n: Int = native_list_len(a)
assert n >= 0, "args returns a list (may be empty if runtime strips argv)"
}
test "uuid-new-format" {
let id: String = uuid_new()
// UUID v4: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx (36 chars)
let n: Int = str_len(id)
assert n == 36, "uuid_new returns 36-character string"
// Check the dashes at correct positions
let d1: String = str_char_at(id, 8)
let d2: String = str_char_at(id, 13)
let d3: String = str_char_at(id, 18)
let d4: String = str_char_at(id, 23)
assert d1 == "-", "dash at position 8"
assert d2 == "-", "dash at position 13"
assert d3 == "-", "dash at position 18"
assert d4 == "-", "dash at position 23"
}
test "uuid-v4-format" {
let id: String = uuid_v4()
let n: Int = str_len(id)
assert n == 36, "uuid_v4 returns 36-character string"
// The version nibble must be '4'
let version_char: String = str_char_at(id, 14)
assert version_char == "4", "uuid_v4 version nibble is '4'"
}
test "uuid-uniqueness" {
let id1: String = uuid_new()
let id2: String = uuid_new()
assert !str_eq(id1, id2), "two uuid_new calls produce different UUIDs"
}
test "env-state-set-get-via-env-module" {
// runtime/env.el re-exports state_set / state_get.
state_set("env_test_key", "env_test_val")
let v: String = state_get("env_test_key")
assert v == "env_test_val", "state_set/get work via env module"
state_del("env_test_key")
let after: String = state_get("env_test_key")
assert after == "", "state_del removes key"
}
test "env-state-keys-json-via-env-module" {
state_set("esk_a", "1")
state_set("esk_b", "2")
let ks: String = state_keys()
assert str_starts_with(ks, "["), "state_keys returns JSON array"
assert str_contains(ks, "esk_a"), "esk_a in keys"
assert str_contains(ks, "esk_b"), "esk_b in keys"
state_del("esk_a")
state_del("esk_b")
}
+96
View File
@@ -0,0 +1,96 @@
// test_fs.el - native test suite for runtime/fs.el
//
// Covers: fs_write/read round-trip, fs_exists, fs_mkdir, fs_list,
// fs_list_json, and edge cases (empty file, overwrite, non-existent path).
test "fs-write-and-read" {
let path: String = "/tmp/el_test_fs_basic.txt"
let content: String = "hello from El"
let ok: Bool = fs_write(path, content)
assert ok, "fs_write returns true on success"
let got: String = fs_read(path)
assert got == content, "fs_read returns what was written"
}
test "fs-exists" {
let path: String = "/tmp/el_test_fs_exists.txt"
fs_write(path, "exists check")
let exists: Bool = fs_exists(path)
assert exists, "fs_exists returns true for created file"
let missing: Bool = fs_exists("/tmp/__el_no_such_file_xyz__.txt")
assert !missing, "fs_exists returns false for non-existent file"
}
test "fs-read-nonexistent" {
let got: String = fs_read("/tmp/__el_no_such_file_abc__.txt")
assert got == "", "reading nonexistent file returns empty string"
}
test "fs-write-overwrite" {
let path: String = "/tmp/el_test_fs_overwrite.txt"
fs_write(path, "original content")
fs_write(path, "new content")
let got: String = fs_read(path)
assert got == "new content", "second write overwrites first"
}
test "fs-write-empty-file" {
let path: String = "/tmp/el_test_fs_empty.txt"
let ok: Bool = fs_write(path, "")
assert ok, "writing empty file succeeds"
let got: String = fs_read(path)
assert got == "", "reading empty file returns empty string"
let exists: Bool = fs_exists(path)
assert exists, "empty file still exists"
}
test "fs-write-multiline" {
let path: String = "/tmp/el_test_fs_multiline.txt"
let content: String = "line one\nline two\nline three"
fs_write(path, content)
let got: String = fs_read(path)
assert got == content, "multiline content round-trips"
let lines: [String] = str_split_lines(got)
let n: Int = native_list_len(lines)
assert n == 3, "three lines after read"
}
test "fs-mkdir" {
let dir: String = "/tmp/el_test_fs_mkdir_dir"
let ok: Bool = fs_mkdir(dir)
assert ok, "fs_mkdir returns true"
let exists: Bool = fs_exists(dir)
assert exists, "created directory exists"
}
test "fs-list" {
let dir: String = "/tmp/el_test_fs_list_dir"
fs_mkdir(dir)
fs_write(dir + "/a.txt", "a")
fs_write(dir + "/b.txt", "b")
let files: [String] = fs_list(dir)
// Filter out empty strings from trailing newline
let n: Int = native_list_len(files)
// We expect at least 2 entries (a.txt and b.txt)
assert n >= 2, "fs_list returns at least 2 entries"
}
test "fs-list-json" {
let dir: String = "/tmp/el_test_fs_listjson_dir"
fs_mkdir(dir)
fs_write(dir + "/x.txt", "x")
fs_write(dir + "/y.txt", "y")
let result: String = fs_list_json(dir)
assert str_starts_with(result, "["), "fs_list_json returns JSON array"
assert str_ends_with(result, "]"), "fs_list_json JSON array is closed"
// Should contain at least one filename
assert str_len(result) > 2, "fs_list_json result is non-empty array"
}
test "fs-write-and-read-special-chars" {
let path: String = "/tmp/el_test_fs_special.txt"
let content: String = "tabs:\there\nnewlines: ok\n\"quoted\""
fs_write(path, content)
let got: String = fs_read(path)
assert got == content, "special chars in content round-trip"
}
+132
View File
@@ -0,0 +1,132 @@
// test_json.el - native test suite for runtime/json.el
//
// Covers: json_get (dot-path), typed extractors (int, bool, float),
// json_array_len/get, json_set, json_build_object, json_build_array,
// and json_escape_string.
test "json-get-string" {
let obj: String = "{\"name\":\"alice\",\"role\":\"admin\"}"
let name: String = json_get(obj, "name")
assert name == "alice", "get string field name"
let role: String = json_get(obj, "role")
assert role == "admin", "get string field role"
let missing: String = json_get(obj, "xyz")
assert missing == "", "missing key returns empty string"
}
test "json-get-int" {
let obj: String = "{\"count\":42,\"offset\":0}"
let count: Int = json_get_int(obj, "count")
assert count == 42, "get int field count"
let offset: Int = json_get_int(obj, "offset")
assert offset == 0, "get int field offset = 0"
}
test "json-get-bool" {
let obj: String = "{\"active\":true,\"deleted\":false}"
let active: Bool = json_get_bool(obj, "active")
assert active, "get bool true"
let deleted: Bool = json_get_bool(obj, "deleted")
assert !deleted, "get bool false"
}
test "json-dot-path" {
let obj: String = "{\"user\":{\"name\":\"bob\",\"age\":30}}"
let name: String = json_get(obj, "user.name")
assert name == "bob", "dot-path traversal user.name"
let age: String = json_get(obj, "user.age")
assert age == "30", "dot-path traversal user.age"
}
test "json-array-len" {
let arr: String = "[\"a\",\"b\",\"c\"]"
let n: Int = json_array_len(arr)
assert n == 3, "array length 3"
let empty_arr: String = "[]"
let m: Int = json_array_len(empty_arr)
assert m == 0, "empty array length 0"
}
test "json-array-get" {
let arr: String = "[\"foo\",\"bar\",\"baz\"]"
let first: String = json_array_get_string(arr, 0)
assert first == "foo", "first element"
let second: String = json_array_get_string(arr, 1)
assert second == "bar", "second element"
let third: String = json_array_get_string(arr, 2)
assert third == "baz", "third element"
}
test "json-get-raw" {
let obj: String = "{\"items\":[1,2,3],\"meta\":{\"page\":1}}"
let raw_arr: String = json_get_raw(obj, "items")
let arr_len: Int = json_array_len(raw_arr)
assert arr_len == 3, "raw array has 3 elements"
let raw_meta: String = json_get_raw(obj, "meta")
let page: String = json_get(raw_meta, "page")
assert page == "1", "page from nested raw object"
}
test "json-set" {
let obj: String = "{\"name\":\"alice\"}"
let updated: String = json_set(obj, "name", "\"bob\"")
let name: String = json_get(updated, "name")
assert name == "bob", "set replaces existing key"
}
test "json-build-object" {
let kvs: [String] = native_list_empty()
let kvs = native_list_append(kvs, "name")
let kvs = native_list_append(kvs, "alice")
let kvs = native_list_append(kvs, "role")
let kvs = native_list_append(kvs, "admin")
let obj: String = json_build_object(kvs)
let name: String = json_get(obj, "name")
let role: String = json_get(obj, "role")
assert name == "alice", "built object name field"
assert role == "admin", "built object role field"
}
test "json-build-array" {
let items: [String] = native_list_empty()
let items = native_list_append(items, "\"alpha\"")
let items = native_list_append(items, "\"beta\"")
let items = native_list_append(items, "\"gamma\"")
let arr: String = json_build_array(items)
let n: Int = json_array_len(arr)
assert n == 3, "built array has 3 elements"
let first: String = json_array_get_string(arr, 0)
assert first == "alpha", "first built element"
let third: String = json_array_get_string(arr, 2)
assert third == "gamma", "third built element"
}
test "json-escape-string" {
let escaped: String = json_escape_string("say \"hello\"")
assert str_contains(escaped, "\\\""), "double quotes are escaped"
let tab_esc: String = json_escape_string("a\tb")
assert str_contains(tab_esc, "\\t"), "tabs are escaped"
let plain: String = json_escape_string("no special chars")
assert plain == "no special chars", "plain string unchanged"
}
test "json-get-float" {
let obj: String = "{\"price\":9.99,\"tax\":0.0}"
let price: Float = json_get_float(obj, "price")
let diff: Float = price - 9.99
assert diff > -0.001, "price close to 9.99 low"
assert diff < 0.001, "price close to 9.99 high"
let tax: Float = json_get_float(obj, "tax")
assert tax == 0.0, "tax is zero"
}
test "json-nested-array-index" {
let obj: String = "{\"tags\":[\"go\",\"el\",\"rust\"]}"
let tags_raw: String = json_get_raw(obj, "tags")
let count: Int = json_array_len(tags_raw)
assert count == 3, "tags array has 3 elements"
let first_tag: String = json_array_get_string(tags_raw, 0)
assert first_tag == "go", "first tag is go"
let last_tag: String = json_array_get_string(tags_raw, 2)
assert last_tag == "rust", "last tag is rust"
}
+153
View File
@@ -0,0 +1,153 @@
// test_math.el - native test suite for runtime/math.el
//
// Covers: integer math (abs, max, min), float math (sqrt, log, sin, cos, pi),
// float conversions, format_float, and decimal_round.
test "el-abs" {
let pos: Int = el_abs(5)
assert pos == 5, "abs of positive"
let neg: Int = el_abs(-5)
assert neg == 5, "abs of negative"
let zero: Int = el_abs(0)
assert zero == 0, "abs of zero"
let large: Int = el_abs(-1000000)
assert large == 1000000, "abs of large negative"
}
test "el-max" {
let m: Int = el_max(3, 7)
assert m == 7, "max of 3 and 7"
let m2: Int = el_max(7, 3)
assert m2 == 7, "max is commutative"
let same: Int = el_max(5, 5)
assert same == 5, "max of equal values"
let neg: Int = el_max(-3, -7)
assert neg == -3, "max of two negatives"
}
test "el-min" {
let m: Int = el_min(3, 7)
assert m == 3, "min of 3 and 7"
let m2: Int = el_min(7, 3)
assert m2 == 3, "min is commutative"
let same: Int = el_min(5, 5)
assert same == 5, "min of equal values"
let neg: Int = el_min(-3, -7)
assert neg == -7, "min of two negatives"
}
test "math-pi" {
let pi: Float = math_pi()
// pi ~ 3.14159265358979
// Check it's between 3.141 and 3.142
let too_low: Float = pi - 3.141
let too_high: Float = 3.142 - pi
assert too_low > 0.0, "pi > 3.141"
assert too_high > 0.0, "pi < 3.142"
}
test "math-sqrt" {
let r4: Float = math_sqrt(4.0)
let diff4: Float = r4 - 2.0
assert diff4 == 0.0, "sqrt(4) == 2.0"
let r9: Float = math_sqrt(9.0)
let diff9: Float = r9 - 3.0
assert diff9 == 0.0, "sqrt(9) == 3.0"
let r1: Float = math_sqrt(1.0)
let diff1: Float = r1 - 1.0
assert diff1 == 0.0, "sqrt(1) == 1.0"
let r0: Float = math_sqrt(0.0)
assert r0 == 0.0, "sqrt(0) == 0.0"
}
test "math-sin-cos" {
// sin(0) == 0, cos(0) == 1
let s0: Float = math_sin(0.0)
assert s0 == 0.0, "sin(0) == 0.0"
let c0: Float = math_cos(0.0)
assert c0 == 1.0, "cos(0) == 1.0"
// sin(pi/2) ~ 1.0, cos(pi/2) ~ 0.0
let half_pi: Float = math_pi() / 2.0
let s_half: Float = math_sin(half_pi)
// Check within 0.000001 of 1.0
let diff_s: Float = s_half - 1.0
assert diff_s > -0.000001, "sin(pi/2) close to 1.0 low"
assert diff_s < 0.000001, "sin(pi/2) close to 1.0 high"
}
test "int-to-float-and-back" {
let f: Float = int_to_float(42)
let back: Int = float_to_int(f)
assert back == 42, "42 round-trips int->float->int"
let neg: Float = int_to_float(-7)
let neg_back: Int = float_to_int(neg)
assert neg_back == -7, "-7 round-trips"
let zero: Float = int_to_float(0)
let zero_back: Int = float_to_int(zero)
assert zero_back == 0, "0 round-trips"
}
test "float-to-int-truncates" {
let t1: Int = float_to_int(3.9)
assert t1 == 3, "3.9 truncates to 3"
let t2: Int = float_to_int(3.1)
assert t2 == 3, "3.1 truncates to 3"
let t3: Int = float_to_int(-3.7)
assert t3 == -3, "-3.7 truncates toward zero to -3"
}
test "str-to-float-and-back" {
let f: Float = str_to_float("3.14")
// Check it's between 3.13 and 3.15
let lo: Float = f - 3.13
let hi: Float = 3.15 - f
assert lo > 0.0, "3.14 parsed > 3.13"
assert hi > 0.0, "3.14 parsed < 3.15"
let zero: Float = str_to_float("0.0")
assert zero == 0.0, "parse 0.0"
}
test "format-float" {
let s0: String = format_float(3.14159, 2)
assert s0 == "3.14", "format to 2 decimals"
let s1: String = format_float(1.0, 0)
assert s1 == "1", "format to 0 decimals"
let s2: String = format_float(0.0, 3)
assert s2 == "0.000", "format zero to 3 decimals"
let s3: String = format_float(-2.5, 1)
assert s3 == "-2.5", "format negative to 1 decimal"
}
test "decimal-round" {
let r0: Float = decimal_round(2.5, 0)
assert r0 == 3.0, "round 2.5 to 0 places"
let r1: Float = decimal_round(2.45, 1)
assert r1 == 2.5, "round 2.45 to 1 place"
let neg: Float = decimal_round(-2.5, 0)
assert neg == -3.0, "round -2.5 to 0 places (half-away-from-zero)"
let exact: Float = decimal_round(1.0, 2)
assert exact == 1.0, "rounding exact value unchanged"
}
test "math-log" {
// log10(100) == 2
let l100: Float = math_log(100.0)
let diff: Float = l100 - 2.0
assert diff > -0.000001, "log10(100) close to 2 low"
assert diff < 0.000001, "log10(100) close to 2 high"
// log10(1) == 0
let l1: Float = math_log(1.0)
assert l1 == 0.0, "log10(1) == 0"
}
test "math-ln" {
// ln(1) == 0
let l1: Float = math_ln(1.0)
assert l1 == 0.0, "ln(1) == 0"
// ln(e) ~ 1.0 e ~ 2.71828
let e: Float = 2.718281828
let le: Float = math_ln(e)
let diff: Float = le - 1.0
assert diff > -0.000001, "ln(e) close to 1 low"
assert diff < 0.000001, "ln(e) close to 1 high"
}
+98
View File
@@ -0,0 +1,98 @@
// test_state.el - native test suite for runtime/state.el
//
// Covers: state_set/get/del, state_has, state_get_or, state_keys,
// and edge cases such as empty values, overwrite, and multiple keys.
test "state-set-get-basic" {
state_set("test_key", "hello")
let v: String = state_get("test_key")
assert v == "hello", "get returns set value"
}
test "state-get-missing" {
let v: String = state_get("__nonexistent_key_xyz__")
assert v == "", "missing key returns empty string"
}
test "state-overwrite" {
state_set("ow_key", "first")
state_set("ow_key", "second")
let v: String = state_get("ow_key")
assert v == "second", "second write overwrites first"
}
test "state-del" {
state_set("del_key", "to be deleted")
state_del("del_key")
let v: String = state_get("del_key")
assert v == "", "deleted key returns empty string"
}
test "state-del-nonexistent" {
// Should not panic or error on deleting a non-existent key.
state_del("__never_set_key__")
let v: String = state_get("__never_set_key__")
assert v == "", "del of nonexistent key is a no-op"
}
test "state-has" {
state_set("has_key", "value")
assert state_has("has_key"), "has returns true for set key"
assert !state_has("__no_has_key__"), "has returns false for absent key"
state_del("has_key")
assert !state_has("has_key"), "has returns false after del"
}
test "state-get-or" {
state_set("gor_key", "actual")
let v1: String = state_get_or("gor_key", "default")
assert v1 == "actual", "get_or returns value when key set"
let v2: String = state_get_or("__absent_gor_key__", "fallback")
assert v2 == "fallback", "get_or returns default when key absent"
}
test "state-multiple-keys" {
state_set("mk_a", "alpha")
state_set("mk_b", "beta")
state_set("mk_c", "gamma")
let a: String = state_get("mk_a")
let b: String = state_get("mk_b")
let c: String = state_get("mk_c")
assert a == "alpha", "key a correct"
assert b == "beta", "key b correct"
assert c == "gamma", "key c correct"
state_del("mk_a")
state_del("mk_b")
state_del("mk_c")
}
test "state-keys-returns-json-array" {
state_set("keys_test_1", "v1")
state_set("keys_test_2", "v2")
let ks: String = state_keys()
// The result is a JSON array string like ["keys_test_1","keys_test_2",...]
assert str_starts_with(ks, "["), "state_keys returns JSON array"
assert str_ends_with(ks, "]"), "state_keys JSON array is closed"
assert str_contains(ks, "keys_test_1"), "keys array contains keys_test_1"
assert str_contains(ks, "keys_test_2"), "keys array contains keys_test_2"
state_del("keys_test_1")
state_del("keys_test_2")
}
test "state-numeric-value-as-string" {
state_set("num_key", "42")
let v: String = state_get("num_key")
let n: Int = str_to_int(v)
assert n == 42, "stored numeric string round-trips to int"
state_del("num_key")
}
test "state-long-value" {
let long_val: String = str_repeat("abcdefghij", 100)
state_set("long_val_key", long_val)
let got: String = state_get("long_val_key")
assert got == long_val, "long value round-trips correctly"
let got_len: Int = str_len(got)
assert got_len == 1000, "long value is 1000 bytes"
state_del("long_val_key")
}
+272
View File
@@ -0,0 +1,272 @@
// test_string.el - native test suite for runtime/string.el
//
// Covers: type conversions, core primitives, comparison and search,
// case conversion, whitespace trimming, replacement, repetition,
// reversal, prefix/suffix stripping, padding, counting, character
// classification, splitting, and joining.
test "int-to-str" {
let s: String = int_to_str(42)
assert s == "42", "int 42 to string"
let neg: String = int_to_str(-7)
assert neg == "-7", "negative int to string"
let zero: String = int_to_str(0)
assert zero == "0", "zero to string"
}
test "str-to-int" {
let n: Int = str_to_int("123")
assert n == 123, "parse 123"
let neg: Int = str_to_int("-5")
assert neg == -5, "parse negative"
let zero: Int = str_to_int("0")
assert zero == 0, "parse zero"
}
test "bool-to-str" {
let t: String = bool_to_str(true)
assert t == "true", "true to string"
let f: String = bool_to_str(false)
assert f == "false", "false to string"
}
test "str-len" {
let n: Int = str_len("hello")
assert n == 5, "length of hello"
let e: Int = str_len("")
assert e == 0, "length of empty string"
let space: Int = str_len("a b")
assert space == 3, "length with spaces"
}
test "str-eq" {
assert str_eq("abc", "abc"), "identical strings are equal"
assert !str_eq("abc", "ABC"), "case-sensitive comparison"
assert str_eq("", ""), "empty strings are equal"
assert !str_eq("a", "b"), "different single chars"
}
test "str-slice" {
let s: String = str_slice("hello world", 6, 11)
assert s == "world", "slice end of string"
let start: String = str_slice("hello world", 0, 5)
assert start == "hello", "slice start of string"
let empty: String = str_slice("hello", 2, 2)
assert empty == "", "zero-length slice"
}
test "str-starts-ends-with" {
assert str_starts_with("hello world", "hello"), "starts with hello"
assert str_ends_with("hello world", "world"), "ends with world"
assert !str_starts_with("hello world", "world"), "does not start with world"
assert !str_ends_with("hello world", "hello"), "does not end with hello"
assert str_starts_with("abc", ""), "empty prefix always matches"
assert str_ends_with("abc", ""), "empty suffix always matches"
}
test "str-contains" {
assert str_contains("hello world", "world"), "contains world"
assert str_contains("hello world", "lo wo"), "contains interior substring"
assert !str_contains("hello world", "xyz"), "does not contain xyz"
assert str_contains("abc", ""), "empty sub is always contained"
assert !str_contains("", "x"), "empty string does not contain nonempty sub"
}
test "str-index-of" {
let i: Int = str_index_of("hello world", "world")
assert i == 6, "index of world"
let j: Int = str_index_of("hello world", "xyz")
assert j == -1, "not found returns -1"
let k: Int = str_index_of("aabbcc", "bb")
assert k == 2, "index of bb in aabbcc"
}
test "str-last-index-of" {
let i: Int = str_last_index_of("abcabc", "bc")
assert i == 4, "last occurrence of bc"
let j: Int = str_last_index_of("hello", "xyz")
assert j == -1, "not found returns -1"
let k: Int = str_last_index_of("aaa", "a")
assert k == 2, "last single-char match"
}
test "str-to-upper-lower" {
let up: String = str_to_upper("hello")
assert up == "HELLO", "to upper"
let lo: String = str_to_lower("WORLD")
assert lo == "world", "to lower"
let mixed: String = str_to_upper("Hello World")
assert mixed == "HELLO WORLD", "mixed to upper"
let empty: String = str_to_lower("")
assert empty == "", "empty stays empty"
}
test "str-trim" {
let s: String = str_trim(" hello ")
assert s == "hello", "trim both ends"
let lonly: String = str_trim(" hello")
assert lonly == "hello", "trim left only"
let ronly: String = str_trim("hello ")
assert ronly == "hello", "trim right only"
let tabs: String = str_trim("\thello\n")
assert tabs == "hello", "trim tabs and newlines"
let empty: String = str_trim(" ")
assert empty == "", "all whitespace trims to empty"
}
test "str-replace" {
let s: String = str_replace("hello world", "world", "there")
assert s == "hello there", "replace word"
let none: String = str_replace("hello", "xyz", "abc")
assert none == "hello", "no match leaves string unchanged"
let multi: String = str_replace("aaa", "a", "b")
assert multi == "bbb", "replace all occurrences"
let empty_from: String = str_replace("hello", "", "x")
assert empty_from == "hello", "empty from returns original"
}
test "str-repeat" {
let s: String = str_repeat("ab", 3)
assert s == "ababab", "repeat 3 times"
let once: String = str_repeat("x", 1)
assert once == "x", "repeat once"
let zero: String = str_repeat("abc", 0)
assert zero == "", "repeat zero times"
let neg: String = str_repeat("abc", -1)
assert neg == "", "negative repeat is empty"
}
test "str-reverse" {
let s: String = str_reverse("hello")
assert s == "olleh", "reverse hello"
let single: String = str_reverse("a")
assert single == "a", "reverse single char"
let empty: String = str_reverse("")
assert empty == "", "reverse empty string"
let palindrome: String = str_reverse("racecar")
assert palindrome == "racecar", "reverse palindrome"
}
test "str-strip-prefix-suffix" {
let p: String = str_strip_prefix("foobar", "foo")
assert p == "bar", "strip prefix foo"
let no_prefix: String = str_strip_prefix("foobar", "baz")
assert no_prefix == "foobar", "no match leaves string unchanged"
let s: String = str_strip_suffix("hello.md", ".md")
assert s == "hello", "strip suffix .md"
let no_suffix: String = str_strip_suffix("hello.md", ".txt")
assert no_suffix == "hello.md", "non-matching suffix unchanged"
}
test "str-strip-chars" {
let s: String = str_strip_chars(" \thello \n", " \t\n")
assert s == "hello", "strip whitespace chars"
let dots: String = str_strip_chars("...hello...", ".")
assert dots == "hello", "strip dot chars"
let all: String = str_strip_chars("aaa", "a")
assert all == "", "strip all chars leaves empty"
}
test "str-pad-left-right" {
let l: String = str_pad_left("42", 5, "0")
assert l == "00042", "zero-pad left to width 5"
let r: String = str_pad_right("hi", 5, "-")
assert r == "hi---", "dash-pad right to width 5"
let no_pad: String = str_pad_left("hello", 3, "x")
assert no_pad == "hello", "no pad when string already wide enough"
}
test "str-count" {
let n: Int = str_count("abc abc abc", "abc")
assert n == 3, "count three occurrences"
let overlap: Int = str_count("aaa", "aa")
assert overlap == 1, "non-overlapping count"
let zero: Int = str_count("hello", "xyz")
assert zero == 0, "not found gives 0"
let empty_sub: Int = str_count("hello", "")
assert empty_sub == 0, "empty sub gives 0"
}
test "str-count-lines-words-letters" {
let s: String = "hello world\nfoo bar"
let lines: Int = str_count_lines(s)
let words: Int = str_count_words(s)
let letters: Int = str_count_letters(s)
assert lines == 2, "line count"
assert words == 4, "word count"
assert letters == 16, "letter count"
}
test "str-count-chars-and-digits" {
let digits: Int = str_count_digits("abc123def456")
assert digits == 6, "six digits"
let none: Int = str_count_digits("hello")
assert none == 0, "no digits"
let chars: Int = str_count_chars("hello")
assert chars == 5, "five ASCII chars"
}
test "char-classes" {
assert is_letter("A"), "A is a letter"
assert is_digit("7"), "7 is a digit"
assert is_whitespace(" "), "space is whitespace"
assert !is_letter("3"), "3 is not a letter"
assert !is_digit("X"), "X is not a digit"
assert is_alphanumeric("abc123"), "abc123 is alphanumeric"
assert !is_alphanumeric("abc!"), "abc! is not alphanumeric"
assert is_uppercase("ABC"), "ABC is uppercase"
assert is_lowercase("abc"), "abc is lowercase"
assert !is_uppercase("Abc"), "mixed is not uppercase"
}
test "str-split" {
let parts: [String] = str_split("a,b,c", ",")
let n: Int = native_list_len(parts)
assert n == 3, "split into 3 parts"
let p0: String = native_list_get(parts, 0)
let p1: String = native_list_get(parts, 1)
let p2: String = native_list_get(parts, 2)
assert p0 == "a", "first part"
assert p1 == "b", "second part"
assert p2 == "c", "third part"
}
test "str-split-lines" {
let lines: [String] = str_split_lines("alpha\nbeta\r\ngamma\n")
let n: Int = native_list_len(lines)
assert n == 3, "split into 3 lines"
let l0: String = native_list_get(lines, 0)
let l1: String = native_list_get(lines, 1)
let l2: String = native_list_get(lines, 2)
assert l0 == "alpha", "first line"
assert l1 == "beta", "second line strips CR"
assert l2 == "gamma", "third line"
}
test "str-join" {
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, "alpha")
let parts = native_list_append(parts, "beta")
let parts = native_list_append(parts, "gamma")
let result: String = str_join(parts, ", ")
assert result == "alpha, beta, gamma", "join with separator"
let empty_parts: [String] = native_list_empty()
let empty_result: String = str_join(empty_parts, ",")
assert empty_result == "", "join empty list gives empty string"
}
test "str-char-at" {
let c: String = str_char_at("hello", 1)
assert c == "e", "char at index 1"
let first: String = str_char_at("abc", 0)
assert first == "a", "first char"
let oob: String = str_char_at("abc", 10)
assert oob == "", "out of bounds returns empty"
}
test "url-encode-decode" {
let encoded: String = url_encode("hello world")
assert !str_contains(encoded, " "), "space is encoded"
let decoded: String = url_decode(encoded)
assert decoded == "hello world", "round-trip decode"
}
+84
View File
@@ -0,0 +1,84 @@
// test_text.el - native test suite for text primitives.
//
// Mirrors the acceptance corpus in tests/text/examples/ using the
// native test/assert system instead of run.sh + expected output files.
test "count-substring" {
let x: Int = str_count("abc abc abc", "abc")
assert x == 3
}
test "count-overlap-skip" {
let x: Int = str_count("aaa", "aa")
assert x == 1, "non-overlapping count of aa in aaa"
}
test "count-lines-words-letters" {
let s: String = "hello world\nfoo bar"
let lines: Int = str_count_lines(s)
let words: Int = str_count_words(s)
let letters: Int = str_count_letters(s)
assert lines == 2, "line count"
assert words == 4, "word count"
assert letters == 16, "letter count"
}
test "index-of-all" {
let positions: [Int] = str_index_of_all("abXcdXefX", "X")
let n: Int = native_list_len(positions)
assert n == 3, "should find 3 occurrences"
let p0: Int = native_list_get(positions, 0)
let p1: Int = native_list_get(positions, 1)
let p2: Int = native_list_get(positions, 2)
assert p0 == 2, "first X at index 2"
assert p1 == 5, "second X at index 5"
assert p2 == 8, "third X at index 8"
}
test "str-repeat" {
let s: String = str_repeat("ab", 3)
assert s == "ababab", "repeat 3 times"
}
test "str-reverse" {
let s: String = str_reverse("hello")
assert s == "olleh", "reverse hello"
}
test "str-strip-prefix" {
let s: String = str_strip_prefix("foobar", "foo")
assert s == "bar", "strip prefix foo"
}
test "str-strip-suffix" {
let s: String = str_strip_suffix("hello.md", ".md")
assert s == "hello", "strip suffix .md"
}
test "str-strip-chars" {
let s: String = str_strip_chars(" \thello \n", " \t\n")
assert s == "hello", "strip whitespace chars"
}
test "split-lines" {
let lines: [String] = str_split_lines("alpha\nbeta\r\ngamma\n")
let n: Int = native_list_len(lines)
assert n == 3, "split into 3 lines"
}
test "str-join" {
let parts: [String] = native_list_empty()
let parts = native_list_append(parts, "alpha")
let parts = native_list_append(parts, "beta")
let parts = native_list_append(parts, "gamma")
let result: String = str_join(parts, ", ")
assert result == "alpha, beta, gamma", "join with separator"
}
test "char-classes" {
assert is_letter("A"), "A is a letter"
assert is_digit("7"), "7 is a digit"
assert is_whitespace(" "), "space is whitespace"
assert !is_letter("3"), "3 is not a letter"
assert !is_digit("X"), "X is not a digit"
}
+155
View File
@@ -0,0 +1,155 @@
// test_time.el - native test suite for runtime/time.el
//
// Covers: time_now (positive timestamp), time_to_parts (UTC decomposition),
// time_format (ISO 8601 and strftime tokens), time_add/diff, unix_timestamp,
// duration helpers, and instant conversions.
test "time-now-positive" {
let ts: Int = time_now()
// time_now() returns milliseconds since epoch.
// Any value greater than 1_700_000_000_000 (Nov 2023) is valid in 2025+.
assert ts > 1700000000000, "time_now returns a reasonable recent timestamp"
}
test "unix-timestamp-positive" {
let s: Int = unix_timestamp()
// Should be greater than 1_700_000_000 (seconds, Nov 2023)
assert s > 1700000000, "unix_timestamp returns seconds > 1700000000"
}
test "now-ns-positive" {
let ns: Int = now_ns()
// Should be a large nanosecond value
assert ns > 0, "now_ns returns positive value"
}
test "time-to-parts-epoch" {
// Unix epoch: 0 ms = 1970-01-01T00:00:00.000Z
let parts: String = time_to_parts(0)
let year: String = json_get(parts, "year")
let month: String = json_get(parts, "month")
let day: String = json_get(parts, "day")
let hour: String = json_get(parts, "hour")
assert year == "1970", "epoch year is 1970"
assert month == "1", "epoch month is 1"
assert day == "1", "epoch day is 1"
assert hour == "0", "epoch hour is 0"
}
test "time-to-parts-known-date" {
// 2024-03-15T12:30:45.000Z
// seconds = 2024-03-15 12:30:45 UTC
// epoch ms: use a known value
// 2024-03-15 00:00:00 UTC = 1710460800 seconds
// + 12*3600 + 30*60 + 45 = 43200 + 1800 + 45 = 45045 seconds
// total: 1710505845 seconds = 1710505845000 ms
let ts: Int = 1710505845000
let parts: String = time_to_parts(ts)
let year: String = json_get(parts, "year")
let month: String = json_get(parts, "month")
let day: String = json_get(parts, "day")
let hour: String = json_get(parts, "hour")
let minute: String = json_get(parts, "minute")
let second: String = json_get(parts, "second")
assert year == "2024", "year is 2024"
assert month == "3", "month is 3 (March)"
assert day == "15", "day is 15"
assert hour == "12", "hour is 12"
assert minute == "30", "minute is 30"
assert second == "45", "second is 45"
}
test "time-format-iso" {
// Epoch = 1970-01-01T00:00:00.000Z
let formatted: String = time_format(0, "ISO")
assert formatted == "1970-01-01T00:00:00.000Z", "epoch formats to ISO correctly"
}
test "time-format-iso-empty-fmt" {
// Empty format string should also produce ISO 8601
let formatted: String = time_format(0, "")
assert formatted == "1970-01-01T00:00:00.000Z", "empty fmt produces ISO"
}
test "time-format-strftime" {
// 2024-03-15T12:30:45.000Z
let ts: Int = 1710505845000
let fmt: String = time_format(ts, "%Y-%m-%d")
assert fmt == "2024-03-15", "strftime %Y-%m-%d"
let hms: String = time_format(ts, "%H:%M:%S")
assert hms == "12:30:45", "strftime %H:%M:%S"
}
test "time-add-milliseconds" {
let base: Int = 1000000
let result: Int = time_add(base, 500, "ms")
assert result == 1000500, "add 500 ms"
}
test "time-add-seconds" {
let base: Int = 0
let result: Int = time_add(base, 60, "sec")
assert result == 60000, "add 60 seconds = 60000 ms"
}
test "time-add-minutes" {
let base: Int = 0
let result: Int = time_add(base, 2, "min")
assert result == 120000, "add 2 minutes = 120000 ms"
}
test "time-add-hours" {
let base: Int = 0
let result: Int = time_add(base, 1, "hour")
assert result == 3600000, "add 1 hour = 3600000 ms"
}
test "time-add-days" {
let base: Int = 0
let result: Int = time_add(base, 1, "day")
assert result == 86400000, "add 1 day = 86400000 ms"
}
test "time-diff-seconds" {
let t1: Int = 0
let t2: Int = 90000
let d: Int = time_diff(t1, t2, "sec")
assert d == 90, "diff 90000 ms = 90 seconds"
}
test "time-diff-negative" {
let t1: Int = 5000
let t2: Int = 2000
let d: Int = time_diff(t1, t2, "sec")
assert d == -3, "negative diff when t2 < t1"
}
test "duration-helpers" {
let d_secs: Int = duration_seconds(5)
assert d_secs == 5000000000, "5 seconds in nanoseconds"
let d_ms: Int = duration_millis(100)
assert d_ms == 100000000, "100 ms in nanoseconds"
let d_ns: Int = duration_nanos(42)
assert d_ns == 42, "42 nanos is identity"
let back_secs: Int = duration_to_seconds(d_secs)
assert back_secs == 5, "convert back to seconds"
let back_ms: Int = duration_to_millis(d_ms)
assert back_ms == 100, "convert back to milliseconds"
}
test "instant-conversions" {
let inst: Int = unix_millis(1000)
assert inst == 1000000000, "unix_millis(1000) in nanoseconds"
let inst_secs: Int = unix_seconds(2)
assert inst_secs == 2000000000, "unix_seconds(2) in nanoseconds"
let back_ms: Int = instant_to_unix_millis(inst)
assert back_ms == 1000, "instant to unix millis"
let back_secs: Int = instant_to_unix_seconds(inst_secs)
assert back_secs == 2, "instant to unix seconds"
}
test "time-from-parts" {
// time_from_parts(secs, ns, tz) -> secs*1000 + ns/1_000_000
let ts: Int = time_from_parts(1000, 500000000, "UTC")
assert ts == 1000500, "time_from_parts: 1000 secs + 500ms"
}