diff options
| author | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-02-02 00:48:12 +0000 |
|---|---|---|
| committer | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-02-02 00:48:12 +0000 |
| commit | 6bbcd9536522386a53cd2f87ece7aa6cf423829f (patch) | |
| tree | 55d6dc2b4e2a9d84c09a67acb36f1ab4131e290c /tests | |
| parent | ec140e5e1823ed72e1ac8ab4af121dbe1b3f85d7 (diff) | |
| parent | dadabf8b9d11d099777acc261068a3ed8ca06f24 (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.zc | 4 | ||||
| -rw-r--r-- | tests/features/test_asm_clobber.zc | 20 | ||||
| -rw-r--r-- | tests/features/test_portable_types.zc | 46 | ||||
| -rw-r--r-- | tests/memory/test_memory_safety.zc | 9 | ||||
| -rw-r--r-- | tests/std/test_direct_array_iteration.zc | 37 | ||||
| -rw-r--r-- | tests/std/test_json_serialization.zc | 149 | ||||
| -rw-r--r-- | tests/std/test_regex.zc | 187 | ||||
| -rw-r--r-- | tests/std/test_slice_iteration.zc | 29 | ||||
| -rw-r--r-- | tests/std/test_thread.zc | 42 |
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"); +} |
