#include <iostream>
#include <string>
#include <cstdlib>
#include "hsv.hpp"
using namespace hsv;
int passed = 0;
int failed = 0;
void assert_true(bool condition, const std::string& name) {
if (condition) {
std::cout << "✓ " << name << "\n";
passed++;
} else {
std::cout << "✗ " << name << "\n";
failed++;
}
}
void assert_eq(const std::string& got, const std::string& expected, const std::string& name) {
assert_true(got == expected, name + " (got: \"" + got + "\")");
}
void assert_eq(size_t got, size_t expected, const std::string& name) {
assert_true(got == expected, name);
}
void test_basic() {
std::string input = std::string() + STX + "name" + US + "Alice" + RS + "age" + US + "30" + ETX;
auto doc = parse(input);
assert_eq(doc.records.size(), 1u, "Basic: 1 record");
assert_eq(get_string(doc.records[0], "name"), "Alice", "Basic: name=Alice");
assert_eq(get_string(doc.records[0], "age"), "30", "Basic: age=30");
}
void test_multiple_records() {
std::string input = std::string() + STX + "name" + US + "Alice" + FS + "name" + US + "Bob" + ETX;
auto doc = parse(input);
assert_eq(doc.records.size(), 2u, "Multiple records: 2 records");
}
void test_array_values() {
std::string input = std::string() + STX + "tags" + US + "a" + GS + "b" + GS + "c" + ETX;
auto doc = parse(input);
assert_eq(doc.records.size(), 1u, "Array: 1 record");
assert_true(doc.records[0].at("tags").is_array(), "Array: tags is array");
assert_eq(doc.records[0].at("tags").as_array().size(), 3u, "Array: 3 elements");
}
void test_header() {
std::string input = std::string() + SOH + "hsv" + US + "1.0" + RS + "type" + US + "users" +
STX + "name" + US + "Alice" + ETX;
auto doc = parse(input);
assert_true(doc.header.has_value(), "Header: has header");
assert_eq(get_string(*doc.header, "hsv"), "1.0", "Header: hsv=1.0");
assert_eq(get_string(*doc.header, "type"), "users", "Header: type=users");
assert_eq(doc.records.size(), 1u, "Header: 1 record");
}
void test_nesting() {
std::string input = std::string() + STX + "user" + US + SO +
"name" + US + "Alice" + RS + "email" + US + "a@b.com" + SI + ETX;
auto doc = parse(input);
assert_eq(doc.records.size(), 1u, "Nesting: 1 record");
assert_true(doc.records[0].at("user").is_object(), "Nesting: user is object");
auto& user = doc.records[0].at("user").as_object();
assert_eq(get_string(user, "name"), "Alice", "Nesting: name=Alice");
assert_eq(get_string(user, "email"), "a@b.com", "Nesting: email=a@b.com");
}
void test_deep_nesting() {
std::string input = std::string() + STX + "data" + US + SO +
"level1" + US + SO + "level2" + US + "deep" + SI + SI + ETX;
auto doc = parse(input);
assert_eq(doc.records.size(), 1u, "Deep nesting: 1 record");
auto& data = doc.records[0].at("data").as_object();
auto& level1 = data.at("level1").as_object();
assert_eq(get_string(level1, "level2"), "deep", "Deep nesting: level2=deep");
}
void test_binary_mode() {
std::string binary_data = std::string("raw") + STX + "data" + ETX + "here";
std::string input = std::string() + STX + "type" + US + "image" + RS +
"data" + US + DLE + STX + binary_data + DLE + ETX + ETX;
auto doc = parse(input);
assert_eq(doc.records.size(), 1u, "Binary: 1 record");
assert_eq(get_string(doc.records[0], "type"), "image", "Binary: type=image");
assert_eq(get_string(doc.records[0], "data"), binary_data, "Binary: data preserved");
}
void test_newlines() {
std::string input = std::string() + STX + "text" + US + "line1\nline2\nline3" + ETX;
auto doc = parse(input);
assert_eq(doc.records.size(), 1u, "Newlines: 1 record");
assert_eq(get_string(doc.records[0], "text"), "line1\nline2\nline3", "Newlines: preserved");
}
void test_quotes() {
std::string input = std::string() + STX + "msg" + US + "He said \"hello\"" + ETX;
auto doc = parse(input);
assert_eq(doc.records.size(), 1u, "Quotes: 1 record");
assert_eq(get_string(doc.records[0], "msg"), "He said \"hello\"", "Quotes: no escaping");
}
void test_mixed_content() {
std::string input = std::string("ignored") + STX + "name" + US + "Alice" + ETX + "also ignored";
auto doc = parse(input);
assert_eq(doc.records.size(), 1u, "Mixed: 1 record");
assert_eq(get_string(doc.records[0], "name"), "Alice", "Mixed: name=Alice");
}
void test_multiple_blocks() {
std::string input = std::string() + STX + "a" + US + "1" + ETX +
"junk" + STX + "b" + US + "2" + ETX;
auto doc = parse(input);
assert_eq(doc.records.size(), 2u, "Multiple blocks: 2 records");
}
void test_nested_array() {
std::string input = std::string() + STX + "user" + US + SO +
"name" + US + "Alice" + RS + "tags" + US + "admin" + GS + "user" + SI + ETX;
auto doc = parse(input);
assert_eq(doc.records.size(), 1u, "Nested array: 1 record");
auto& user = doc.records[0].at("user").as_object();
assert_eq(get_string(user, "name"), "Alice", "Nested array: name=Alice");
assert_true(user.at("tags").is_array(), "Nested array: tags is array");
assert_eq(user.at("tags").as_array().size(), 2u, "Nested array: 2 tags");
}
int main() {
std::cout << "==================================================\n";
std::cout << "HSV Parser Tests (C++)\n";
std::cout << "==================================================\n";
test_basic();
test_multiple_records();
test_array_values();
test_header();
test_nesting();
test_deep_nesting();
test_binary_mode();
test_newlines();
test_quotes();
test_mixed_content();
test_multiple_blocks();
test_nested_array();
std::cout << "==================================================\n";
std::cout << passed << " passed, " << failed << " failed\n";
std::cout << "==================================================\n";
return failed > 0 ? 1 : 0;
}