365 lines
14 KiB
C
365 lines
14 KiB
C
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#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;
|
||
|
}
|