summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-02-02 00:48:12 +0000
committerZuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian>2026-02-02 00:48:12 +0000
commit6bbcd9536522386a53cd2f87ece7aa6cf423829f (patch)
tree55d6dc2b4e2a9d84c09a67acb36f1ab4131e290c /tests
parentec140e5e1823ed72e1ac8ab4af121dbe1b3f85d7 (diff)
parentdadabf8b9d11d099777acc261068a3ed8ca06f24 (diff)
Merge branch 'main' of https://github.com/z-libs/Zen-C into patch-1
Diffstat (limited to 'tests')
-rw-r--r--tests/collections/test_string_suite.zc4
-rw-r--r--tests/features/test_asm_clobber.zc20
-rw-r--r--tests/features/test_portable_types.zc46
-rw-r--r--tests/memory/test_memory_safety.zc9
-rw-r--r--tests/std/test_direct_array_iteration.zc37
-rw-r--r--tests/std/test_json_serialization.zc149
-rw-r--r--tests/std/test_regex.zc187
-rw-r--r--tests/std/test_slice_iteration.zc29
-rw-r--r--tests/std/test_thread.zc42
9 files changed, 519 insertions, 4 deletions
diff --git a/tests/collections/test_string_suite.zc b/tests/collections/test_string_suite.zc
index afe08af..64ed9d8 100644
--- a/tests/collections/test_string_suite.zc
+++ b/tests/collections/test_string_suite.zc
@@ -91,7 +91,9 @@ test "test_fstrings_return" {
let inner = f"Inner({x})";
let outer = f"Outer({inner})";
println "Composed: {outer}";
- assert(strcmp(outer, "Outer(Inner(100))") == 0, "Composed f-string failed");
+ let outer_res = strcmp(outer, "Outer(Inner(100))");
+ let zero: c_int = 0;
+ assert(outer_res == zero, "Composed f-string failed");
}
test "test_string_std_ops" {
diff --git a/tests/features/test_asm_clobber.zc b/tests/features/test_asm_clobber.zc
new file mode 100644
index 0000000..2ba74da
--- /dev/null
+++ b/tests/features/test_asm_clobber.zc
@@ -0,0 +1,20 @@
+fn add(a: int, b: int) -> int {
+ let result: int;
+ asm {
+ "mov {a}, {result}"
+ "add {b}, {result}"
+ : out(result)
+ : in(a), in(b)
+ : clobber("cc")
+ }
+ return result;
+}
+
+test "asm_clobber" {
+ let res = add(10, 20);
+ if (res != 30) {
+ println "Failed: Expected 30, got {res}";
+ exit(1);
+ }
+ println "Success: asm with clobber works properly";
+}
diff --git a/tests/features/test_portable_types.zc b/tests/features/test_portable_types.zc
new file mode 100644
index 0000000..8f54fcb
--- /dev/null
+++ b/tests/features/test_portable_types.zc
@@ -0,0 +1,46 @@
+
+import "std/io.zc";
+
+// This test verifies the new portable integer types and C interop types.
+
+extern fn abs(x: c_int) -> c_int;
+extern fn labs(x: c_long) -> c_long;
+
+fn main() -> int {
+ // Portable types
+ let a: i32 = -42;
+ let b: u32 = 42;
+ let c: i64 = -1000000;
+ let d: u64 = 1000000;
+
+ if (a != -42) return 1;
+ if (b != 42) return 2;
+ if (c != -1000000) return 3;
+ if (d != 1000000) return 4;
+
+ // C Types
+ let ca: c_int = -10;
+ let cb: c_long = -20;
+ let cc: c_short = -5;
+ let cd: c_char = 65; // 'A'
+
+ // Test C interaction
+ let abs_val = abs(ca);
+ let expected_abs: c_int = 10;
+ if (abs_val != expected_abs) return 5;
+
+ let labs_val = labs(cb);
+ let expected_labs: c_long = 20;
+ if (labs_val != expected_labs) return 6;
+
+ // Size checks (these are platform dependent but we can check relations)
+ // sizeof(c_char) is always 1
+ if (sizeof(c_char) != 1) return 7;
+
+ // sizeof(c_short) <= sizeof(c_int) <= sizeof(c_long)
+ if (sizeof(c_short) > sizeof(c_int)) return 8;
+ if (sizeof(c_int) > sizeof(c_long)) return 9;
+
+ printf("Portable types test passed.\n");
+ return 0;
+}
diff --git a/tests/memory/test_memory_safety.zc b/tests/memory/test_memory_safety.zc
index a5cc960..b672cc9 100644
--- a/tests/memory/test_memory_safety.zc
+++ b/tests/memory/test_memory_safety.zc
@@ -1,5 +1,6 @@
import "std/mem.zc"
+import "std/slice.zc"
// ** Globals **
let DROP_COUNT = 0;
@@ -127,11 +128,13 @@ test "test_slice" {
let data: int[5] = [1, 2, 3, 4, 5];
let s = Slice<int>::new(&data[0], 5);
f" Slice len: {(int)s.len}";
- let v2 = s.get(2);
+ let opt_v2 = s.get(2);
+ let v2 = opt_v2.unwrap();
f" Slice[2]: {v2}";
assert(v2 == 3, "Slice get failed");
- s.set(0, 99);
- let v0 = s.get(0);
+ s.data[0] = 99;
+ let opt_v0 = s.get(0);
+ let v0 = opt_v0.unwrap();
f" After set: Slice[0] = {v0}";
assert(v0 == 99, "Slice set failed");
" ✓ Slice works!";
diff --git a/tests/std/test_direct_array_iteration.zc b/tests/std/test_direct_array_iteration.zc
new file mode 100644
index 0000000..359951f
--- /dev/null
+++ b/tests/std/test_direct_array_iteration.zc
@@ -0,0 +1,37 @@
+import "std/slice.zc"
+
+test "direct array iteration" {
+ let arr: int[5] = [1, 2, 3, 4, 5];
+
+ let sum = 0;
+ for val in arr {
+ sum = sum + val;
+ }
+
+ assert(sum == 15, "Sum should be 1+2+3+4+5 = 15");
+}
+
+test "direct array iteration with different types" {
+ let floats: float[3] = [1.5, 2.5, 3.0];
+ let count = 0;
+
+ for f in floats {
+ count = count + 1;
+ }
+
+ assert(count == 3, "Should iterate over all 3 elements");
+}
+
+// TODO: Nested array iteration needs special handling
+// test "nested array iteration" {
+// let matrix: int[2][3] = [[1, 2, 3], [4, 5, 6]];
+// let total = 0;
+//
+// for row in matrix {
+// for val in row {
+// total = total + val;
+// }
+// }
+//
+// assert(total == 21, "Sum should be 1+2+3+4+5+6 = 21");
+// }
diff --git a/tests/std/test_json_serialization.zc b/tests/std/test_json_serialization.zc
new file mode 100644
index 0000000..9fd5b32
--- /dev/null
+++ b/tests/std/test_json_serialization.zc
@@ -0,0 +1,149 @@
+import "std/json.zc"
+import "std/io.zc"
+
+test "primitives" {
+ // Null
+ let v = JsonValue::null();
+ let s = v.to_string();
+ let expected = String::from("null");
+ if (!s.eq(&expected)) {
+ panic("Null serialization failed");
+ }
+ expected.free();
+ s.free();
+
+ // Bool True
+ v = JsonValue::bool(true);
+ s = v.to_string();
+ expected = String::from("true");
+ if (!s.eq(&expected)) {
+ panic("Bool true serialization failed");
+ }
+ expected.free();
+ s.free();
+
+ // Bool False
+ v = JsonValue::bool(false);
+ s = v.to_string();
+ expected = String::from("false");
+ if (!s.eq(&expected)) {
+ panic("Bool false serialization failed");
+ }
+ expected.free();
+ s.free();
+
+ // Number Int
+ v = JsonValue::number(123.0);
+ s = v.to_string();
+ expected = String::from("123");
+ if (!s.eq(&expected)) {
+ println "{s.c_str()}";
+ panic("Number 123 serialization failed");
+ }
+ expected.free();
+ s.free();
+
+ // Number Float
+ v = JsonValue::number(12.5);
+ s = v.to_string();
+ expected = String::from("12.5");
+ if (!s.eq(&expected)) {
+ panic("Number 12.5 serialization failed");
+ }
+ expected.free();
+ s.free();
+
+ // String Simple
+ v = JsonValue::string("hello");
+ s = v.to_string();
+ expected = String::from("\"hello\"");
+ if (!s.eq(&expected)) {
+ println "{s.c_str()}";
+ panic("String hello serialization failed");
+ }
+ expected.free();
+ s.free();
+
+ // String Escaped
+ v = JsonValue::string("hello \"world\"");
+ s = v.to_string();
+ expected = String::from("\"hello \\\"world\\\"\"");
+ if (!s.eq(&expected)) {
+ println "Got: {s.c_str()}";
+ panic("String escaped serialization failed");
+ }
+ expected.free();
+ s.free();
+}
+
+test "array" {
+ let v = JsonValue::array();
+ v.push(JsonValue::number(1.0));
+ v.push(JsonValue::bool(true));
+ v.push(JsonValue::string("a"));
+
+ let s = v.to_string();
+ let expected = String::from("[1,true,\"a\"]");
+ if (!s.eq(&expected)) {
+ println "Got: {s.c_str()}";
+ panic("Array serialization failed");
+ }
+ expected.free();
+ s.free();
+}
+
+test "object" {
+ let v = JsonValue::object();
+ v.set("key", JsonValue::string("value"));
+
+ let s = v.to_string();
+ // Round trip verification to avoid parser bug with literals
+ let parsed_res = JsonValue::parse(s.c_str());
+ if (parsed_res.is_err()) {
+ panic("Object round trip parse failed");
+ }
+ let parsed = parsed_res.unwrap();
+ if (!parsed.is_object()) panic("Round trip not object");
+
+ let val_opt = (*parsed).get_string("key");
+ if (val_opt.is_none()) panic("Round trip missing 'key'");
+
+ let val_str = val_opt.unwrap();
+ if (strcmp(val_str, "value") != 0) panic("Round trip wrong value");
+
+ // Cleanup
+ (*parsed).free();
+ free(parsed);
+ s.free();
+}
+
+test "nested" {
+ // {"arr":[1,2]}
+ let v = JsonValue::object();
+ let arr = JsonValue::array();
+ arr.push(JsonValue::number(1.0));
+ arr.push(JsonValue::number(2.0));
+ v.set("arr", arr);
+
+ let s = v.to_string();
+
+ // Round trip
+ let parsed_res = JsonValue::parse(s.c_str());
+ if (parsed_res.is_err()) {
+ panic("Round trip parse failed");
+ }
+ let parsed = parsed_res.unwrap();
+ if (!parsed.is_object()) panic("Round trip type mismatch");
+
+ let arr_opt = (*parsed).get_array("arr");
+ if (arr_opt.is_none()) panic("Round trip missing arr");
+
+ let arr_ptr = arr_opt.unwrap();
+ if (!(*arr_ptr).is_array()) panic("Inner not array");
+ if ((*arr_ptr).len() != 2) panic("Wrong array length");
+
+ // Cleanup
+ (*parsed).free();
+ free(parsed);
+ s.free();
+} \ No newline at end of file
diff --git a/tests/std/test_regex.zc b/tests/std/test_regex.zc
new file mode 100644
index 0000000..4fe176c
--- /dev/null
+++ b/tests/std/test_regex.zc
@@ -0,0 +1,187 @@
+import "std/regex.zc"
+
+fn test_basic_matching() {
+ "testing: basic matching";
+ let re = Regex::compile("abc");
+
+ if (re.match("abc")) { "literal match works"; } else { "FAILED: literal match"; }
+ if (re.match("abcdef")) { "substring match works"; } else { "FAILED: substring match"; }
+ if (!re.match("xyz")) { "not matching correctly returns false"; } else { "FAILED: mismatching"; }
+
+ re.destroy();
+ "";
+}
+
+fn test_anchors() {
+ "testing: anchors";
+ let re = Regex::compile("^start");
+
+ if (re.match("start here")) { " ^ anchor works for start"; } else { "FAILED: ^ anchor start"; }
+ if (!re.match("no start")) { " ^ anchor rejects non-start"; } else { "FAILED: ^ anchor reject"; }
+
+ re.destroy();
+
+ let re2 = Regex::compile("end$");
+ if (re2.match("the end")) { " $ anchor works for end"; } else { "FAILED: $ anchor end"; }
+ if (!re2.match("end here")) { " $ anchor rejects non-end"; } else { "FAILED: $ anchor reject"; }
+
+ re2.destroy();
+ "";
+}
+
+fn test_wildcards() {
+ "testing: wild cards";
+ let re = Regex::compile("a.c");
+
+ if (re.match("abc")) { " . matches single char"; } else { "FAILED: . match 1"; }
+ if (re.match("axc")) { " . matches different char"; } else { "FAILED: . match 2"; }
+ if (!re.match("ac")) { " . requires exactly one char"; } else { "FAILED: . match 3"; }
+
+ re.destroy();
+ "";
+}
+
+fn test_quantifiers() {
+ "testing: quantifiers";
+ let re1 = Regex::compile("a*b");
+ if (re1.match("b")) { " * matches zero occurrences"; } else { "FAILED: * 0"; }
+ if (re1.match("ab")) { " * matches one occurrence"; } else { "FAILED: * 1"; }
+ if (re1.match("aaab")) { " * matches multiple occurrences"; } else { "FAILED: * many"; }
+ re1.destroy();
+
+ let re2 = Regex::compile("a+b");
+ if (!re2.match("b")) { " + requires at least one"; } else { "FAILED: + 0"; }
+ if (re2.match("ab")) { " + matches one occurrence"; } else { "FAILED: + 1"; }
+ if (re2.match("aaab")) { " + matches multiple occurrences"; } else { "FAILED: + many"; }
+ re2.destroy();
+
+ let re3 = Regex::compile("colou?r");
+ if (re3.match("color")) { " ? matches with char"; } else { "FAILED: ? with"; }
+ if (re3.match("colour")) { " ? matches without char"; } else { "FAILED: ? without"; }
+ re3.destroy();
+ "";
+}
+
+fn test_character_classes() {
+ "testing: character class stuff"
+ let re = Regex::compile("[0-9]+");
+
+ if (re.match("123")) { " [0-9] matches digits"; } else { "FAILED: [0-9] match"; }
+ if (re.match("abc123")) { " [0-9] finds digits in string"; } else { "FAILED: [0-9] find"; }
+ if (!re.match("abc")) { " [0-9] rejects non-digits"; } else { "FAILED: [0-9] reject"; }
+
+ re.destroy();
+ "";
+}
+
+fn test_alternation() {
+ "test: alternation";
+ let re = Regex::compile("cat|dog");
+
+ if (re.match("cat")) { " | matches first alternative"; } else { "FAILED: | match 1"; }
+ if (re.match("dog")) { " | matches second alternative"; } else { "FAILED: | match 2"; }
+ if (!re.match("bird")) { " | rejects non-matching"; } else { "FAILED: | reject"; }
+
+ re.destroy();
+ "";
+}
+
+fn test_word_boundaries() {
+ "testing: word matching";
+ let re = Regex::compile("[a-zA-Z]+");
+
+ if (re.match("hello")) { " letter class matches words"; } else { "FAILED: letter match"; }
+ if (re.match("hello123")) { " letter class finds word part"; } else { "FAILED: letter part"; }
+ if (!re.match("123")) { " letter class rejects non-letters"; } else { "FAILED: letter reject"; }
+
+ re.destroy();
+ "";
+}
+
+fn test_is_valid() {
+ "testing: patern validation"
+
+ if (Regex::is_valid_pattern("^[a-z]+$")) { " valid pattern accepted"; } else { "FAILED: pattern validation 1"; }
+ if (Regex::is_valid_pattern("(hello|world)")) { " complex pattern accepted"; } else { "FAILED: pattern validation 2"; }
+
+ "";
+}
+
+fn test_find() {
+ "testing: find functionality";
+ let re = Regex::compile("[0-9]+");
+ let m = re.find("abc123def456");
+
+ if (m.is_some()) { " find locates match"; } else { "FAILED: find match"; }
+
+ re.destroy();
+ "";
+}
+
+fn test_count() {
+ "testing: count";
+ let re = Regex::compile("[0-9]+");
+ let count = re.count("123 456 789");
+
+ if (count >= 1) { " count finds matches"; } else { "FAILED: count matches"; }
+
+ re.destroy();
+ "";
+}
+
+fn test_convenience_functions() {
+ "testing: just some other functions and stuff";
+
+ if (regex_match("^test", "testing")) { " regex_match works"; } else { "FAILED: regex_match"; }
+ if (regex_count("a", "banana") >= 1) { " regex_count works"; } else { "FAILED: regex_count"; }
+
+ let m = regex_find("[0-9]+", "id: 42");
+ if (m.is_some()) { " regex_find works"; } else { "FAILED: regex_find"; }
+
+ "";
+}
+
+fn test_email_pattern() {
+ "test: email pattern stuff"
+ let email_re = Regex::compile("^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z][a-zA-Z]+$");
+
+ if (email_re.match("swag@swag.com")) { " valid email accepted"; } else { "FAILED: valid email"; }
+ if (email_re.match("swag.swag@swag.swag.swag")) { " complex email accepted"; } else { "FAILED: complex email"; }
+ if (!email_re.match("invalid.email")) { " invalid email rejected"; } else { "FAILED: invalid email reject"; }
+
+ email_re.destroy();
+ "";
+}
+
+fn test_url_pattern() {
+ "testing: url pattern stuff"
+ let url_re = Regex::compile("https?://[a-zA-Z0-9.-]+");
+
+ if (url_re.match("http://example.com")) { " http url matched matched"; } else { "FAILED: http url"; }
+ if (url_re.match("https://secure.example.com")) { " https url matched"; } else { "FAILED: https url"; }
+ if (!url_re.match("ftp://something.com")) { " ftp url rejected"; } else { "FAILED: ftp url reject"; }
+
+ url_re.destroy();
+ "";
+}
+
+fn main() {
+ "testing....";
+
+ test_basic_matching();
+ test_anchors();
+ test_wildcards();
+ test_quantifiers();
+ test_character_classes();
+ test_alternation();
+ test_word_boundaries();
+ test_is_valid();
+ test_find();
+ test_count();
+ test_convenience_functions();
+ test_email_pattern();
+ test_url_pattern();
+
+ "all tests worked... (hopefully.. look around for \"FAILED\" messages)";
+ "";
+}
diff --git a/tests/std/test_slice_iteration.zc b/tests/std/test_slice_iteration.zc
new file mode 100644
index 0000000..b7eddf4
--- /dev/null
+++ b/tests/std/test_slice_iteration.zc
@@ -0,0 +1,29 @@
+import "std/slice.zc"
+import "std/io.zc"
+
+test "slice from array iteration" {
+ let ints: int[5] = [1, 2, 3, 4, 5];
+ let slice = Slice<int>::from_array((int*)(&ints), 5);
+
+ // Test iteration
+ let sum = 0;
+ for val in slice {
+ sum = sum + val;
+ }
+
+ if (sum != 15) {
+ panic("Slice iteration failed: expected sum 15");
+ }
+}
+
+test "slice methods" {
+ let arr: int[3] = [10, 20, 30];
+ let slice = Slice<int>::from_array((int*)(&arr), 3);
+
+ if (slice.length() != 3) panic("Slice length wrong");
+ if (slice.is_empty()) panic("Slice should not be empty");
+
+ let opt = slice.get(1);
+ if (opt.is_none()) panic("Slice get failed");
+ if (opt.unwrap() != 20) panic("Slice get returned wrong value");
+}
diff --git a/tests/std/test_thread.zc b/tests/std/test_thread.zc
new file mode 100644
index 0000000..42fa82e
--- /dev/null
+++ b/tests/std/test_thread.zc
@@ -0,0 +1,42 @@
+import "std/thread.zc"
+
+test "Thread Spawn and Join" {
+ "Testing thread spawn and join";
+
+ let spawn_result = Thread::spawn(fn(){
+ "Running on a separate thread";
+ });
+ assert(spawn_result.is_ok(), "Thread spawn has failed");
+
+ let thr = spawn_result.unwrap();
+ let join_result = thr.join();
+ assert(join_result.is_ok(), "Thread join has failed");
+}
+
+test "Thread Spawn and Detach" {
+ "Testing thread spawn and detach";
+
+ let spawn_result = Thread::spawn(fn(){
+ "Detached thread, this line might print later.";
+ });
+ assert(spawn_result.is_ok(), "Thread spawn has failed");
+
+ let thr = spawn_result.unwrap();
+ let detach_result = thr.detach();
+ assert(detach_result.is_ok(), "Thread detach has failed");
+}
+
+test "Thread Spawn and Cancel" {
+ "Testing thread spawn and cancel";
+
+ let spawn_result = Thread::spawn(fn(){
+ "Thread running indefinitely...";
+ while true {
+ }
+ });
+ assert(spawn_result.is_ok(), "Thread spawn has failed");
+
+ let thr = spawn_result.unwrap();
+ let cancel_result = thr.cancel();
+ assert(cancel_result.is_ok(), "Thread cancel has failed");
+}