#include "runner.h" #include "../colla.h" #include // INI Parser Tests UNIT_TEST(ini_parse_basic) { arena_t arena = arena_make(ARENA_MALLOC, KB(4)); strview_t ini_content = strv_init( "[section1]\n" "key1=value1\n" "key2=value2\n" "\n" "[section2]\n" "key3=value3\n" "key4=value4\n" ); ini_t ini = ini_parse_str(&arena, ini_content, NULL); ASSERT(ini_is_valid(&ini)); // Test section1 initable_t *section1 = ini_get_table(&ini, strv_init("section1")); ASSERT(section1 != NULL); ASSERT(strv_equals(section1->name, strv_init("section1"))); // Test section1 values inivalue_t *key1 = ini_get(section1, strv_init("key1")); ASSERT(key1 != NULL); ASSERT(strv_equals(key1->key, strv_init("key1"))); ASSERT(strv_equals(key1->value, strv_init("value1"))); inivalue_t *key2 = ini_get(section1, strv_init("key2")); ASSERT(key2 != NULL); ASSERT(strv_equals(key2->key, strv_init("key2"))); ASSERT(strv_equals(key2->value, strv_init("value2"))); // Test section2 initable_t *section2 = ini_get_table(&ini, strv_init("section2")); ASSERT(section2 != NULL); ASSERT(strv_equals(section2->name, strv_init("section2"))); // Test section2 values inivalue_t *key3 = ini_get(section2, strv_init("key3")); ASSERT(key3 != NULL); ASSERT(strv_equals(key3->key, strv_init("key3"))); ASSERT(strv_equals(key3->value, strv_init("value3"))); inivalue_t *key4 = ini_get(section2, strv_init("key4")); ASSERT(key4 != NULL); ASSERT(strv_equals(key4->key, strv_init("key4"))); ASSERT(strv_equals(key4->value, strv_init("value4"))); arena_cleanup(&arena); } UNIT_TEST(ini_parse_with_options) { arena_t arena = arena_make(ARENA_MALLOC, KB(4)); strview_t ini_content = strv_init( "[section1]\n" "key1:value1\n" "# This is a comment\n" "key2:value2\n" "\n" "[section1]\n" // Duplicate section "key3:value3\n" ); iniopt_t options = { .merge_duplicate_tables = true, .merge_duplicate_keys = false, .key_value_divider = ':', .comment_vals = strv_init("#") }; ini_t ini = ini_parse_str(&arena, ini_content, &options); ASSERT(ini_is_valid(&ini)); // Test section1 (should be merged) initable_t *section1 = ini_get_table(&ini, strv_init("section1")); ASSERT(section1 != NULL); // Check all keys exist in merged section inivalue_t *key1 = ini_get(section1, strv_init("key1")); ASSERT(key1 != NULL); ASSERT(strv_equals(key1->value, strv_init("value1"))); inivalue_t *key2 = ini_get(section1, strv_init("key2")); ASSERT(key2 != NULL); ASSERT(strv_equals(key2->value, strv_init("value2"))); inivalue_t *key3 = ini_get(section1, strv_init("key3")); ASSERT(key3 != NULL); ASSERT(strv_equals(key3->value, strv_init("value3"))); arena_cleanup(&arena); } UNIT_TEST(ini_value_conversion) { arena_t arena = arena_make(ARENA_MALLOC, KB(4)); strview_t ini_content = strv_init( "[values]\n" "uint=42\n" "int=-42\n" "float=3.14\n" "bool_true=true\n" "bool_false=false\n" "array=item1,item2,item3\n" ); ini_t ini = ini_parse_str(&arena, ini_content, NULL); initable_t *values = ini_get_table(&ini, strv_init("values")); ASSERT(values != NULL); // Test uint conversion inivalue_t *uint_val = ini_get(values, strv_init("uint")); ASSERT(uint_val != NULL); ASSERT(ini_as_uint(uint_val) == 42); // Test int conversion inivalue_t *int_val = ini_get(values, strv_init("int")); ASSERT(int_val != NULL); ASSERT(ini_as_int(int_val) == -42); // Test float conversion inivalue_t *float_val = ini_get(values, strv_init("float")); ASSERT(float_val != NULL); ASSERT(ini_as_num(float_val) > 3.13 && ini_as_num(float_val) < 3.15); // Test bool conversion inivalue_t *bool_true = ini_get(values, strv_init("bool_true")); ASSERT(bool_true != NULL); ASSERT(ini_as_bool(bool_true) == true); inivalue_t *bool_false = ini_get(values, strv_init("bool_false")); ASSERT(bool_false != NULL); ASSERT(ini_as_bool(bool_false) == false); // Test array conversion inivalue_t *array_val = ini_get(values, strv_init("array")); ASSERT(array_val != NULL); iniarray_t array = ini_as_arr(&arena, array_val, ','); ASSERT(array.count == 3); ASSERT(strv_equals(array.values[0], strv_init("item1"))); ASSERT(strv_equals(array.values[1], strv_init("item2"))); ASSERT(strv_equals(array.values[2], strv_init("item3"))); arena_cleanup(&arena); } // JSON Parser Tests UNIT_TEST(json_parse_basic) { arena_t arena = arena_make(ARENA_MALLOC, KB(4)); strview_t json_content = strv_init( "{\n" " \"string\": \"value\",\n" " \"number\": 42,\n" " \"bool\": true,\n" " \"null\": null,\n" " \"array\": [1, 2, 3],\n" " \"object\": {\n" " \"nested\": \"nested_value\"\n" " }\n" "}" ); json_t *root = json_parse_str(&arena, json_content, JSON_DEFAULT); ASSERT(root != NULL); ASSERT(root->type == JSON_OBJECT); // Test string json_t *string_node = json_get(root, strv_init("string")); ASSERT(json_check(string_node, JSON_STRING)); ASSERT(strv_equals(string_node->string, strv_init("value"))); // Test number json_t *number_node = json_get(root, strv_init("number")); ASSERT(json_check(number_node, JSON_NUMBER)); ASSERT(number_node->number == 42); // Test bool json_t *bool_node = json_get(root, strv_init("bool")); ASSERT(json_check(bool_node, JSON_BOOL)); ASSERT(bool_node->boolean == true); // Test null json_t *null_node = json_get(root, strv_init("null")); ASSERT(json_check(null_node, JSON_NULL)); // Test array json_t *array_node = json_get(root, strv_init("array")); ASSERT(json_check(array_node, JSON_ARRAY)); // Test array contents int count = 0; int sum = 0; json_for(item, array_node) { ASSERT(json_check(item, JSON_NUMBER)); sum += (int)item->number; count++; } ASSERT(count == 3); ASSERT(sum == 6); // 1 + 2 + 3 // Test nested object json_t *object_node = json_get(root, strv_init("object")); ASSERT(json_check(object_node, JSON_OBJECT)); json_t *nested_node = json_get(object_node, strv_init("nested")); ASSERT(json_check(nested_node, JSON_STRING)); ASSERT(strv_equals(nested_node->string, strv_init("nested_value"))); arena_cleanup(&arena); } UNIT_TEST(json_parse_with_options) { arena_t arena = arena_make(ARENA_MALLOC, KB(4)); // JSON with comments and trailing commas strview_t json_content = strv_init( "{\n" " \"key1\": \"value1\",\n" " // This is a comment\n" " \"key2\": \"value2\",\n" " \"array\": [\n" " 1,\n" " 2,\n" " 3,\n" // Trailing comma " ],\n" // Trailing comma "}" ); // Test with default flags (should allow comments and trailing commas) json_t *root1 = json_parse_str(&arena, json_content, JSON_DEFAULT); ASSERT(root1 != NULL); ASSERT(json_get(root1, strv_init("key1")) != NULL); ASSERT(json_get(root1, strv_init("key2")) != NULL); // Test with NO_COMMENTS and NO_TRAILING_COMMAS flags json_t *root2 = json_parse_str(&arena, json_content, JSON_NO_COMMENTS | JSON_NO_TRAILING_COMMAS); // This should fail parsing due to the strict flags - but the behavior depends on implementation // Some parsers might ignore the errors, others might return NULL // We'll check both possibilities if (root2 != NULL) { // If parsing succeeded despite strict flags, ensure the content is correct ASSERT(json_get(root2, strv_init("key1")) != NULL); // key2 might be missing if comment handling failed } arena_cleanup(&arena); } // XML Parser Tests UNIT_TEST(xml_parse_basic) { arena_t arena = arena_make(ARENA_MALLOC, KB(4)); strview_t xml_content = strv_init( "\n" "\n" " \n" " Test Title\n" " Test Author\n" " \n" " \n" " Another Title\n" " \n" "" ); xml_t xml = xml_parse_str(&arena, xml_content); ASSERT(xml.root != NULL); ASSERT(strv_equals(xml.root->key, strv_init("root"))); // Find item tags xmltag_t *item = xml_get_tag(xml.root, strv_init("item"), false); ASSERT(item != NULL); // Check attributes strview_t id = xml_get_attribute(item, strv_init("id")); ASSERT(strv_equals(id, strv_init("1"))); strview_t type = xml_get_attribute(item, strv_init("type")); ASSERT(strv_equals(type, strv_init("book"))); // Check nested tags xmltag_t *title = xml_get_tag(item, strv_init("title"), false); ASSERT(title != NULL); ASSERT(strv_equals(title->content, strv_init("Test Title"))); xmltag_t *author = xml_get_tag(item, strv_init("author"), false); ASSERT(author != NULL); ASSERT(strv_equals(author->content, strv_init("Test Author"))); // Check recursive tag finding xmltag_t *title_recursive = xml_get_tag(xml.root, strv_init("title"), true); ASSERT(title_recursive != NULL); ASSERT(strv_equals(title_recursive->content, strv_init("Test Title"))); arena_cleanup(&arena); } // HTML Parser Tests UNIT_TEST(html_parse_basic) { arena_t arena = arena_make(ARENA_MALLOC, KB(4)); strview_t html_content = strv_init( "\n" "\n" "\n" " Test Page\n" "\n" "\n" "

Hello World

\n" "

This is a test.

\n" "
\n" "

More content here.

\n" "
\n" "\n" "" ); html_t html = html_parse_str(&arena, html_content); ASSERT(html.root != NULL); ASSERT(str_equals(html.root->key, str_init(&arena, "html"))); // Find head and body htmltag_t *head = html_get_tag(html.root, strv_init("head"), false); ASSERT(head != NULL); htmltag_t *body = html_get_tag(html.root, strv_init("body"), false); ASSERT(body != NULL); // Find title in head htmltag_t *title = html_get_tag(head, strv_init("title"), false); ASSERT(title != NULL); ASSERT(strv_equals(title->content, strv_init("Test Page"))); // Find elements in body htmltag_t *h1 = html_get_tag(body, strv_init("h1"), false); ASSERT(h1 != NULL); ASSERT(strv_equals(h1->content, strv_init("Hello World"))); // Find paragraph with class htmltag_t *p = html_get_tag(body, strv_init("p"), false); ASSERT(p != NULL); strview_t p_class = html_get_attribute(p, strv_init("class")); ASSERT(strv_equals(p_class, strv_init("intro"))); ASSERT(strv_equals(p->content, strv_init("This is a test."))); // Find div by id htmltag_t *div = html_get_tag(body, strv_init("div"), false); ASSERT(div != NULL); strview_t div_id = html_get_attribute(div, strv_init("id")); ASSERT(strv_equals(div_id, strv_init("content"))); // Find nested paragraph using recursive search htmltag_t *nested_p = html_get_tag(div, strv_init("p"), false); ASSERT(nested_p != NULL); ASSERT(strv_equals(nested_p->content, strv_init("More content here."))); arena_cleanup(&arena); }