#include #include #include "secsgem/secs2/codec.hpp" #include "secsgem/secs2/item.hpp" #include "secsgem/secs2/sml.hpp" using namespace secsgem::secs2; // Round-trip an item through to_sml() / parse_sml() and assert equality. // Equality on Item is structural so this catches any drift. static void roundtrip(const Item& original) { const auto text = to_sml(original); const auto parsed = parse_sml(text); CHECK(parsed == original); } TEST_CASE("SML: ASCII round-trip") { roundtrip(Item::ascii("hello world")); roundtrip(Item::ascii("")); } TEST_CASE("SML: U2 / U4 vector round-trip") { roundtrip(Item::u2(std::vector{1, 2, 3})); roundtrip(Item::u4(uint32_t{42})); } TEST_CASE("SML: signed integers + floats") { roundtrip(Item::i4(int32_t{-1})); roundtrip(Item::i8(std::vector{-9999, 0, 9999})); // Float comparison via SML drops trailing zeros; check the structure // rather than the exact value. auto parsed = parse_sml(to_sml(Item::f4(3.5f))); REQUIRE(parsed.format() == Format::F4); } TEST_CASE("SML: Binary scalar + multi-byte") { roundtrip(Item::binary(std::vector{0xDE, 0xAD, 0xBE, 0xEF})); } TEST_CASE("SML: Boolean scalar and multi-element") { roundtrip(Item::boolean(true)); roundtrip(Item::boolean(std::vector{1, 0, 1, 0})); } TEST_CASE("SML: nested list round-trip") { auto item = Item::list({ Item::ascii("LOT-A"), Item::list({ Item::u4(uint32_t{1}), Item::ascii("step-1"), }), Item::binary(std::vector{0x01, 0x02}), }); roundtrip(item); } TEST_CASE("SML: parser is whitespace-insensitive") { auto compact = parse_sml(R"(>)"); auto spaced = parse_sml(R"(< L < A "x" > < U4 42 > >)"); CHECK(compact == spaced); } TEST_CASE("SML: parse rejects unknown tag") { CHECK_THROWS_AS(parse_sml(""), SmlError); } TEST_CASE("SML: try_parse_sml swallows errors") { CHECK_FALSE(try_parse_sml("").has_value()); } TEST_CASE("SML: optional [n] count is accepted but not enforced") { // Emitter writes ""; parser must accept it back. auto m = parse_sml(R"( >)"); REQUIRE(m.format() == Format::List); CHECK(m.size() == 3); }