#include #include #include #define CYAML_IMPLEMENTATION #include "../include/cyaml.h" // Adjust this path as needed /* ANSI color definitions for pretty terminal output */ #define COLOR_RED "\033[31m" #define COLOR_GREEN "\033[32m" #define COLOR_YELLOW "\033[33m" #define COLOR_RESET "\033[0m" /* Global counters for tests */ static int tests_passed = 0; static int tests_run = 0; /* Test assertion macro */ #define TEST_ASSERT(cond, msg) do { \ tests_run++; \ if (!(cond)) { \ printf(COLOR_RED "[FAIL]" COLOR_RESET " %s\n", msg); \ } else { \ tests_passed++; \ printf(COLOR_GREEN "[PASS]" COLOR_RESET " %s\n", msg); \ } \ } while(0) /*--------------------------------------------------------- * Utility Functions to Clean Up Manually Created Nodes * (These are needed because our cyaml library currently * only auto-frees scalar nodes from cyaml_load_* functions.) *---------------------------------------------------------*/ static void free_mapping_node(cyaml_node_t *node) { if (node->keys) { for (size_t i = 0; i < node->mapping_size; i++) { free(node->keys[i]); } free(node->keys); } if (node->values) { for (size_t i = 0; i < node->mapping_size; i++) { if (node->values[i]) { if (node->values[i]->scalar) free(node->values[i]->scalar); free(node->values[i]); } } free(node->values); } free(node); } static void free_sequence_node(cyaml_node_t *node) { if (node->sequence) { for (size_t i = 0; i < node->sequence_size; i++) { if (node->sequence[i]) { if (node->sequence[i]->scalar) free(node->sequence[i]->scalar); free(node->sequence[i]); } } free(node->sequence); } free(node); } /*--------------------------------------------------------- * Test 1: Load YAML String (Scalar) *---------------------------------------------------------*/ void test_load_string() { const char *yaml = "Hello, cyaml!"; cyaml_document_t *doc = cyaml_load_string(yaml); TEST_ASSERT(doc != NULL, "cyaml_load_string should not return NULL"); if (doc) { const cyaml_node_t *root = cyaml_document_get_root(doc); TEST_ASSERT(root != NULL, "Root node should not be NULL"); if (root) { const char *str = cyaml_node_as_string(root); TEST_ASSERT(str != NULL, "Root node should be a scalar string"); if (str) { TEST_ASSERT(strcmp(str, yaml) == 0, "Scalar value should match input string"); } } cyaml_document_destroy(doc); } } /*--------------------------------------------------------- * Test 2: Emitter for Scalar YAML *---------------------------------------------------------*/ void test_emitter_scalar() { const char *yaml = "Emitter Test!"; cyaml_document_t *doc = cyaml_load_string(yaml); TEST_ASSERT(doc != NULL, "cyaml_load_string for emitter test should not return NULL"); if (doc) { cyaml_emitter_t *emitter = cyaml_emitter_create(); TEST_ASSERT(emitter != NULL, "cyaml_emitter_create should not return NULL"); if (emitter) { int emit_result = cyaml_emitter_emit(emitter, doc); TEST_ASSERT(emit_result == 0, "cyaml_emitter_emit should succeed"); char *emitted_str = cyaml_emitter_to_string(emitter); TEST_ASSERT(emitted_str != NULL, "cyaml_emitter_to_string should not return NULL"); if (emitted_str) { TEST_ASSERT(strcmp(emitted_str, yaml) == 0, "Emitted YAML should match original scalar"); free(emitted_str); } cyaml_emitter_destroy(emitter); } cyaml_document_destroy(doc); } } /*--------------------------------------------------------- * Test 3: Load YAML from a File (Scalar) *---------------------------------------------------------*/ void test_load_file() { const char *filename = "test.yaml"; const char *yaml = "File Test!"; /* Create a temporary file with YAML content */ FILE *file = fopen(filename, "w"); if (!file) { printf(COLOR_YELLOW "Warning: Could not create temporary test file.\n" COLOR_RESET); return; } fputs(yaml, file); fclose(file); cyaml_document_t *doc = cyaml_load_file(filename); TEST_ASSERT(doc != NULL, "cyaml_load_file should not return NULL"); if (doc) { const cyaml_node_t *root = cyaml_document_get_root(doc); TEST_ASSERT(root != NULL, "Root node from file should not be NULL"); if (root) { const char *str = cyaml_node_as_string(root); TEST_ASSERT(str != NULL, "Root node from file should be a scalar string"); if (str) { TEST_ASSERT(strcmp(str, yaml) == 0, "File content should match expected YAML"); } } cyaml_document_destroy(doc); } /* Remove the temporary file */ remove(filename); } /*--------------------------------------------------------- * Test 4: Numeric Conversions *---------------------------------------------------------*/ void test_numeric_conversion() { // Integer conversion test const char *int_str = "12345"; cyaml_document_t *doc_int = cyaml_load_string(int_str); TEST_ASSERT(doc_int != NULL, "Load integer string for conversion"); if (doc_int) { const cyaml_node_t *node = cyaml_document_get_root(doc_int); int value = cyaml_node_as_int(node); TEST_ASSERT(value == 12345, "Integer conversion: value should be 12345"); cyaml_document_destroy(doc_int); } // Double conversion test const char *double_str = "3.14159"; cyaml_document_t *doc_double = cyaml_load_string(double_str); TEST_ASSERT(doc_double != NULL, "Load double string for conversion"); if (doc_double) { const cyaml_node_t *node = cyaml_document_get_root(doc_double); double dvalue = cyaml_node_as_double(node); TEST_ASSERT(dvalue > 3.14158 && dvalue < 3.14160, "Double conversion: value should be approx 3.14159"); cyaml_document_destroy(doc_double); } } /*--------------------------------------------------------- * Test 5: Mapping Node Functionality *---------------------------------------------------------*/ void test_mapping_node() { // Manually create a mapping node document cyaml_document_t *doc = (cyaml_document_t*)malloc(sizeof(cyaml_document_t)); doc->root = (cyaml_node_t*)malloc(sizeof(cyaml_node_t)); cyaml_node_t *map = doc->root; map->type = CYAML_NODE_MAPPING; map->mapping_size = 3; map->keys = (char**)malloc(sizeof(char*) * 3); map->values = (cyaml_node_t**)malloc(sizeof(cyaml_node_t*) * 3); // Key-value pair 1: "name": "cyaml" map->keys[0] = strdup("name"); map->values[0] = (cyaml_node_t*)malloc(sizeof(cyaml_node_t)); map->values[0]->type = CYAML_NODE_SCALAR; map->values[0]->scalar = strdup("cyaml"); // Key-value pair 2: "version": "1.0" map->keys[1] = strdup("version"); map->values[1] = (cyaml_node_t*)malloc(sizeof(cyaml_node_t)); map->values[1]->type = CYAML_NODE_SCALAR; map->values[1]->scalar = strdup("1.0"); // Key-value pair 3: "count": "42" map->keys[2] = strdup("count"); map->values[2] = (cyaml_node_t*)malloc(sizeof(cyaml_node_t)); map->values[2]->type = CYAML_NODE_SCALAR; map->values[2]->scalar = strdup("42"); TEST_ASSERT(cyaml_node_size(map) == 3, "Mapping node size should be 3"); const cyaml_node_t *node_name = cyaml_node_get(map, "name"); TEST_ASSERT(node_name != NULL, "Mapping node: key 'name' should exist"); if (node_name) { TEST_ASSERT(strcmp(cyaml_node_as_string(node_name), "cyaml") == 0, "Mapping node: 'name' value should be 'cyaml'"); } const cyaml_node_t *node_version = cyaml_node_get(map, "version"); TEST_ASSERT(node_version != NULL, "Mapping node: key 'version' should exist"); if (node_version) { TEST_ASSERT(strcmp(cyaml_node_as_string(node_version), "1.0") == 0, "Mapping node: 'version' value should be '1.0'"); } const cyaml_node_t *node_count = cyaml_node_get(map, "count"); TEST_ASSERT(node_count != NULL, "Mapping node: key 'count' should exist"); if (node_count) { int count = cyaml_node_as_int(node_count); TEST_ASSERT(count == 42, "Mapping node: 'count' value should be 42"); } // Clean up manually created mapping node free_mapping_node(map); free(doc); } /*--------------------------------------------------------- * Test 6: Sequence Node Functionality *---------------------------------------------------------*/ void test_sequence_node() { // Manually create a sequence node document cyaml_document_t *doc = (cyaml_document_t*)malloc(sizeof(cyaml_document_t)); doc->root = (cyaml_node_t*)malloc(sizeof(cyaml_node_t)); cyaml_node_t *seq = doc->root; seq->type = CYAML_NODE_SEQUENCE; seq->sequence_size = 4; seq->sequence = (cyaml_node_t**)malloc(sizeof(cyaml_node_t*) * 4); const char *items[] = { "one", "two", "three", "four" }; for (size_t i = 0; i < 4; i++) { seq->sequence[i] = (cyaml_node_t*)malloc(sizeof(cyaml_node_t)); seq->sequence[i]->type = CYAML_NODE_SCALAR; seq->sequence[i]->scalar = strdup(items[i]); } TEST_ASSERT(cyaml_node_size(seq) == 4, "Sequence node size should be 4"); for (size_t i = 0; i < 4; i++) { const cyaml_node_t *item = cyaml_node_index(seq, i); char msg[128]; snprintf(msg, sizeof(msg), "Sequence node: item %zu should not be NULL", i); TEST_ASSERT(item != NULL, msg); if (item) { snprintf(msg, sizeof(msg), "Sequence node: item %zu should equal '%s'", i, items[i]); TEST_ASSERT(strcmp(cyaml_node_as_string(item), items[i]) == 0, msg); } } // Clean up manually created sequence node free_sequence_node(seq); free(doc); } /*--------------------------------------------------------- * Test 7: Bulk YAML String Loading (100 iterations) *---------------------------------------------------------*/ void test_bulk_load_string() { for (int i = 0; i < 100; i++) { char buffer[64]; snprintf(buffer, sizeof(buffer), "Bulk test %d", i); cyaml_document_t *doc = cyaml_load_string(buffer); char test_msg[128]; snprintf(test_msg, sizeof(test_msg), "Bulk load string test %d: document should not be NULL", i); TEST_ASSERT(doc != NULL, test_msg); if (doc) { const cyaml_node_t *root = cyaml_document_get_root(doc); snprintf(test_msg, sizeof(test_msg), "Bulk load string test %d: root should not be NULL", i); TEST_ASSERT(root != NULL, test_msg); if (root) { snprintf(test_msg, sizeof(test_msg), "Bulk load string test %d: value should match", i); TEST_ASSERT(strcmp(cyaml_node_as_string(root), buffer) == 0, test_msg); } cyaml_document_destroy(doc); } } } /*--------------------------------------------------------- * Test 8: Bulk File Read/Write (20 iterations) *---------------------------------------------------------*/ void test_bulk_file_rw() { for (int i = 0; i < 20; i++) { char filename[64]; char content[64]; snprintf(filename, sizeof(filename), "temp_test_%d.yaml", i); snprintf(content, sizeof(content), "File bulk test %d", i); /* Write to file */ FILE *file = fopen(filename, "w"); if (!file) { printf(COLOR_YELLOW "Warning: Could not create temporary file %s.\n" COLOR_RESET, filename); continue; } fputs(content, file); fclose(file); /* Read file and validate */ cyaml_document_t *doc = cyaml_load_file(filename); char test_msg[128]; snprintf(test_msg, sizeof(test_msg), "Bulk file test %d: document should not be NULL", i); TEST_ASSERT(doc != NULL, test_msg); if (doc) { const cyaml_node_t *root = cyaml_document_get_root(doc); snprintf(test_msg, sizeof(test_msg), "Bulk file test %d: root should not be NULL", i); TEST_ASSERT(root != NULL, test_msg); if (root) { snprintf(test_msg, sizeof(test_msg), "Bulk file test %d: content should match", i); TEST_ASSERT(strcmp(cyaml_node_as_string(root), content) == 0, test_msg); } cyaml_document_destroy(doc); } /* Remove the temporary file */ remove(filename); } } /*--------------------------------------------------------- * Main Function: Run All Tests and Display Summary *---------------------------------------------------------*/ int main(void) { printf("========================================\n"); printf(" " COLOR_YELLOW "cyaml Unit Test Suite" COLOR_RESET "\n"); printf("========================================\n\n"); test_load_string(); test_emitter_scalar(); test_load_file(); test_numeric_conversion(); test_mapping_node(); test_sequence_node(); test_bulk_load_string(); test_bulk_file_rw(); printf("\n========================================\n"); printf("Test Summary:\n"); printf("Total tests run: %d\n", tests_run); printf(COLOR_GREEN "Passed: %d" COLOR_RESET "\n", tests_passed); printf(COLOR_RED "Failed: %d" COLOR_RESET "\n", tests_run - tests_passed); printf("========================================\n"); return (tests_run - tests_passed) == 0 ? EXIT_SUCCESS : EXIT_FAILURE; }