369 lines
12 KiB
C
369 lines
12 KiB
C
#include "runner.h"
|
|
#include "../colla.h"
|
|
#include <stdio.h>
|
|
|
|
// 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(
|
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
|
"<root>\n"
|
|
" <item id=\"1\" type=\"book\">\n"
|
|
" <title>Test Title</title>\n"
|
|
" <author>Test Author</author>\n"
|
|
" </item>\n"
|
|
" <item id=\"2\" type=\"magazine\">\n"
|
|
" <title>Another Title</title>\n"
|
|
" </item>\n"
|
|
"</root>"
|
|
);
|
|
|
|
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(
|
|
"<!DOCTYPE html>\n"
|
|
"<html>\n"
|
|
"<head>\n"
|
|
" <title>Test Page</title>\n"
|
|
"</head>\n"
|
|
"<body>\n"
|
|
" <h1>Hello World</h1>\n"
|
|
" <p class=\"intro\">This is a test.</p>\n"
|
|
" <div id=\"content\">\n"
|
|
" <p>More content here.</p>\n"
|
|
" </div>\n"
|
|
"</body>\n"
|
|
"</html>"
|
|
);
|
|
|
|
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);
|
|
}
|