From 0fa623093366351ad47583f47add6e51f56a56d8 Mon Sep 17 00:00:00 2001 From: Lexi Winter Date: Mon, 23 Jun 2025 00:32:38 +0100 Subject: nihil.ucl: improve tests --- nihil.ucl/boolean.ccm | 12 +++--- nihil.ucl/integer.ccm | 13 +++---- nihil.ucl/map.ccm | 2 +- nihil.ucl/object.ccm | 5 +++ nihil.ucl/real.ccm | 12 +++--- nihil.ucl/string.ccm | 48 ++++++++++++++++++++++-- nihil.ucl/tests/array.cc | 78 ++++++++++++++++++++++++++++++++------ nihil.ucl/tests/boolean.cc | 62 +++++++++++++++++++++++++++++-- nihil.ucl/tests/integer.cc | 65 ++++++++++++++++++++++++++++++-- nihil.ucl/tests/map.cc | 58 +++++++++++++++++++++++++++++ nihil.ucl/tests/real.cc | 32 ++++++++++++++++ nihil.ucl/tests/string.cc | 93 ++++++++++++++++++++++++++++++++++++++++++++-- 12 files changed, 436 insertions(+), 44 deletions(-) (limited to 'nihil.ucl') diff --git a/nihil.ucl/boolean.ccm b/nihil.ucl/boolean.ccm index 8510152..6a93867 100644 --- a/nihil.ucl/boolean.ccm +++ b/nihil.ucl/boolean.ccm @@ -18,7 +18,7 @@ import :object; namespace nihil::ucl { export struct boolean final : object { - using value_type = bool; + using contained_type = bool; inline static constexpr object_type ucl_type = object_type::boolean; @@ -41,7 +41,7 @@ export struct boolean final : object { boolean() : boolean(false) {} // Create a new boolean from a value. - explicit boolean(value_type value) + explicit boolean(contained_type value) : object(noref, ::ucl_object_frombool(value)) { if (_object == nullptr) @@ -49,9 +49,9 @@ export struct boolean final : object { } // Return this object's value. - auto value(this boolean const &self) -> value_type + auto value(this boolean const &self) -> contained_type { - auto v = value_type{}; + auto v = contained_type{}; auto const *uobj = self.get_ucl_object(); if (::ucl_object_toboolean_safe(uobj, &v)) @@ -77,13 +77,13 @@ export auto operator<=> (boolean const &a, boolean const &b) return a.value() <=> b.value(); } -export auto operator== (boolean const &a, boolean::value_type b) +export auto operator== (boolean const &a, boolean::contained_type b) -> bool { return a.value() == b; } -export auto operator<=> (boolean const &a, boolean::value_type b) +export auto operator<=> (boolean const &a, boolean::contained_type b) -> std::strong_ordering { return a.value() <=> b; diff --git a/nihil.ucl/integer.ccm b/nihil.ucl/integer.ccm index b58c04d..482a716 100644 --- a/nihil.ucl/integer.ccm +++ b/nihil.ucl/integer.ccm @@ -18,8 +18,7 @@ import :object; namespace nihil::ucl { export struct integer final : object { - using value_type = std::int64_t; - + using contained_type = std::int64_t; inline static constexpr object_type ucl_type = object_type::integer; // Create a new integer from a UCL object. @@ -43,7 +42,7 @@ export struct integer final : object { {} // Create a new integer from a value. - explicit integer(value_type value) + explicit integer(contained_type value) : object(noref, ::ucl_object_fromint(value)) { if (_object == nullptr) @@ -51,9 +50,9 @@ export struct integer final : object { } // Return the value of this object. - auto value(this integer const &self) -> value_type + auto value(this integer const &self) -> contained_type { - auto v = value_type{}; + auto v = contained_type{}; auto const *uobj = self.get_ucl_object(); if (::ucl_object_toint_safe(uobj, &v)) @@ -79,13 +78,13 @@ export auto operator<=> (integer const &a, integer const &b) return a.value() <=> b.value(); } -export auto operator== (integer const &a, integer::value_type b) +export auto operator== (integer const &a, integer::contained_type b) -> bool { return a.value() == b; } -export auto operator<=> (integer const &a, integer::value_type b) +export auto operator<=> (integer const &a, integer::contained_type b) -> std::strong_ordering { return a.value() <=> b; diff --git a/nihil.ucl/map.ccm b/nihil.ucl/map.ccm index b486787..c140885 100644 --- a/nihil.ucl/map.ccm +++ b/nihil.ucl/map.ccm @@ -164,7 +164,7 @@ struct map final : object { value_type>) map(std::from_range_t, Range &&range) : map(std::ranges::begin(range), - std::ranges::end(range)) + std::ranges::end(range)) { } diff --git a/nihil.ucl/object.ccm b/nihil.ucl/object.ccm index 0814125..b220335 100644 --- a/nihil.ucl/object.ccm +++ b/nihil.ucl/object.ccm @@ -137,6 +137,11 @@ protected: // The object we're wrapping. ::ucl_object_t *_object = nullptr; + friend auto swap(object &a, object &b) -> void + { + std::swap(a._object, b._object); + } + private: friend struct parser; diff --git a/nihil.ucl/real.ccm b/nihil.ucl/real.ccm index 51607f2..260e993 100644 --- a/nihil.ucl/real.ccm +++ b/nihil.ucl/real.ccm @@ -17,7 +17,7 @@ import :object; namespace nihil::ucl { export struct real final : object { - using value_type = double; + using contained_type = double; inline static constexpr object_type ucl_type = object_type::real; @@ -42,7 +42,7 @@ export struct real final : object { {} // Create a new real from a value. - explicit real(value_type value) + explicit real(contained_type value) : object(noref, ::ucl_object_fromdouble(value)) { if (_object == nullptr) @@ -50,9 +50,9 @@ export struct real final : object { } // Return the value of this real. - auto value(this real const &self) -> value_type + auto value(this real const &self) -> contained_type { - auto v = value_type{}; + auto v = contained_type{}; auto const *uobj = self.get_ucl_object(); if (::ucl_object_todouble_safe(uobj, &v)) @@ -78,13 +78,13 @@ export auto operator<=> (real const &a, real const &b) return a.value() <=> b.value(); } -export auto operator== (real const &a, real::value_type b) +export auto operator== (real const &a, real::contained_type b) -> bool { return a.value() == b; } -export auto operator<=> (real const &a, real::value_type b) +export auto operator<=> (real const &a, real::contained_type b) -> std::partial_ordering { return a.value() <=> b; diff --git a/nihil.ucl/string.ccm b/nihil.ucl/string.ccm index 64f88dc..f92c82c 100644 --- a/nihil.ucl/string.ccm +++ b/nihil.ucl/string.ccm @@ -17,10 +17,16 @@ import :object; namespace nihil::ucl { export struct string final : object { - using value_type = std::string_view; - + using contained_type = std::string_view; inline static constexpr object_type ucl_type = object_type::string; + using value_type = char const; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = value_type &; + using pointer = value_type *; + using iterator = pointer; + // Create a new string from a UCL object. string(ref_t, ::ucl_object_t const *uobj) : object(nihil::ucl::ref, uobj) @@ -71,7 +77,7 @@ export struct string final : object { {} // Return the value of this string. - auto value(this string const &self) -> value_type + auto value(this string const &self) -> contained_type { char const *dptr{}; std::size_t dlen; @@ -83,6 +89,42 @@ export struct string final : object { // This should never fail. std::abort(); } + + // Return the size of this string. + auto size(this string const &self) -> size_type + { + return self.value().size(); + } + + // Test if this string is empty. + auto empty(this string const &self) -> bool + { + return self.size() == 0; + } + + // Access this string's data + auto data(this string const &self) -> pointer + { + char const *dptr{}; + + auto const *uobj = self.get_ucl_object(); + if (::ucl_object_tostring_safe(uobj, &dptr)) + return dptr; + + // This should never fail. + std::abort(); + } + + // Iterator access + auto begin(this string const &self) -> iterator + { + return self.data(); + } + + auto end(this string const &self) -> iterator + { + return self.data() + self.size(); + } }; /* diff --git a/nihil.ucl/tests/array.cc b/nihil.ucl/tests/array.cc index 023b3bf..ce0976f 100644 --- a/nihil.ucl/tests/array.cc +++ b/nihil.ucl/tests/array.cc @@ -3,14 +3,36 @@ */ #include +#include #include #include #include +#include import nihil.ucl; -TEST_CASE("ucl: array: construct", "[ucl]") +TEST_CASE("ucl: array: invariants", "[ucl]") +{ + using namespace nihil::ucl; + + REQUIRE(array<>::ucl_type == object_type::array); + REQUIRE(static_cast<::ucl_type>(array<>::ucl_type) == UCL_ARRAY); + + static_assert(std::destructible>); + static_assert(std::default_initializable>); + static_assert(std::move_constructible>); + static_assert(std::copy_constructible>); + static_assert(std::equality_comparable>); + static_assert(std::totally_ordered>); + static_assert(std::swappable>); + + static_assert(std::ranges::sized_range>); + static_assert(std::same_as>, + integer>); +} + +TEST_CASE("ucl: array: default construct", "[ucl]") { using namespace nihil::ucl; @@ -19,6 +41,42 @@ TEST_CASE("ucl: array: construct", "[ucl]") REQUIRE(str(arr.type()) == "array"); } +TEST_CASE("ucl: array: construct from range", "[ucl]") +{ + using namespace nihil::ucl; + + auto vec = std::vector{integer(1), integer(42)}; + auto arr = array(std::from_range, vec); + + REQUIRE(arr.size() == 2); + REQUIRE(arr[0] == 1); + REQUIRE(arr[1] == 42); +} + +TEST_CASE("ucl: array: construct from iterator pair", "[ucl]") +{ + using namespace nihil::ucl; + + auto vec = std::vector{integer(1), integer(42)}; + auto arr = array(std::ranges::begin(vec), + std::ranges::end(vec)); + + REQUIRE(arr.size() == 2); + REQUIRE(arr[0] == 1); + REQUIRE(arr[1] == 42); +} + +TEST_CASE("ucl: array: construct from initializer_list", "[ucl]") +{ + using namespace nihil::ucl; + + auto arr = array{integer(1), integer(42)}; + + REQUIRE(arr.size() == 2); + REQUIRE(arr[0] == 1); + REQUIRE(arr[1] == 42); +} + TEST_CASE("ucl: array: push_back", "[ucl]") { using namespace nihil::ucl; @@ -45,10 +103,9 @@ TEST_CASE("ucl: array: compare", "[ucl]") { using namespace nihil::ucl; - auto arr = array(); - arr.push_back(integer(1)); - arr.push_back(integer(42)); - arr.push_back(integer(666)); + auto arr = array{ + integer(1), integer(42), integer(666) + }; auto arr2 = array(); REQUIRE(arr != arr2); @@ -58,10 +115,10 @@ TEST_CASE("ucl: array: compare", "[ucl]") arr2.push_back(integer(666)); REQUIRE(arr == arr2); - auto arr3 = array(); - arr3.push_back(integer(1)); - arr3.push_back(integer(1)); - arr3.push_back(integer(1)); + auto arr3 = array{ + integer(1), integer(1), integer(1) + }; + REQUIRE(arr != arr3); } @@ -124,7 +181,6 @@ TEST_CASE("ucl: array is a sized_range", "[ucl]") using namespace nihil::ucl; auto arr = array{integer(1), integer(42), integer(666)}; - static_assert(std::ranges::sized_range); auto size = std::ranges::size(arr); REQUIRE(size == 3); @@ -143,7 +199,7 @@ TEST_CASE("ucl: array is a sized_range", "[ucl]") auto arr_as_ints = arr | std::views::transform(&integer::value); - auto int_vec = std::vector(); + auto int_vec = std::vector(); std::ranges::copy(arr_as_ints, std::back_inserter(int_vec)); REQUIRE(int_vec == std::vector{1, 42, 666}); diff --git a/nihil.ucl/tests/boolean.cc b/nihil.ucl/tests/boolean.cc index ed5e1d7..49dc408 100644 --- a/nihil.ucl/tests/boolean.cc +++ b/nihil.ucl/tests/boolean.cc @@ -2,16 +2,29 @@ * This source code is released into the public domain. */ +#include #include #include +#include import nihil.ucl; -TEST_CASE("ucl: boolean: construct", "[ucl]") +TEST_CASE("ucl: boolean: invariants", "[ucl]") { - auto b = nihil::ucl::boolean(true); - REQUIRE(b == true); + using namespace nihil::ucl; + + static_assert(std::same_as); + REQUIRE(boolean::ucl_type == object_type::boolean); + REQUIRE(static_cast<::ucl_type>(boolean::ucl_type) == UCL_BOOLEAN); + + static_assert(std::destructible); + static_assert(std::default_initializable); + static_assert(std::move_constructible); + static_assert(std::copy_constructible); + static_assert(std::equality_comparable); + static_assert(std::totally_ordered); + static_assert(std::swappable); } TEST_CASE("ucl: boolean: default construct", "[ucl]") @@ -20,6 +33,42 @@ TEST_CASE("ucl: boolean: default construct", "[ucl]") REQUIRE(b == false); } +TEST_CASE("ucl: boolean: construct from value", "[ucl]") +{ + auto b = nihil::ucl::boolean(true); + REQUIRE(b == true); +} + +TEST_CASE("ucl: boolean: swap", "[ucl]") +{ + // do not add using namespace nihil::ucl + + auto b1 = nihil::ucl::boolean(true); + auto b2 = nihil::ucl::boolean(false); + + swap(b1, b2); + + REQUIRE(b1 == false); + REQUIRE(b2 == true); +} + +TEST_CASE("ucl: boolean: value()", "[ucl]") +{ + auto b = nihil::ucl::boolean(true); + REQUIRE(b.value() == true); +} + +TEST_CASE("ucl: boolean: key()", "[ucl]") +{ + using namespace nihil::ucl; + + auto obj = parse("a_bool = true"); + REQUIRE(object_cast(obj["a_bool"]).key() == "a_bool"); + + auto b = nihil::ucl::boolean(true); + REQUIRE(b.key() == ""); +} + TEST_CASE("ucl: boolean: operator==", "[ucl]") { auto b = nihil::ucl::boolean(true); @@ -53,6 +102,13 @@ TEST_CASE("ucl: boolean: parse", "[ucl]") } TEST_CASE("ucl: boolean: emit", "[ucl]") +{ + auto b = nihil::ucl::boolean(true); + auto str = std::format("{}", b); + REQUIRE(str == "true"); +} + +TEST_CASE("ucl: boolean: parse and emit", "[ucl]") { auto ucl = nihil::ucl::parse("bool = true;"); diff --git a/nihil.ucl/tests/integer.cc b/nihil.ucl/tests/integer.cc index ad513ca..811a864 100644 --- a/nihil.ucl/tests/integer.cc +++ b/nihil.ucl/tests/integer.cc @@ -2,16 +2,30 @@ * This source code is released into the public domain. */ +#include +#include #include #include +#include import nihil.ucl; -TEST_CASE("ucl: integer: construct", "[ucl]") +TEST_CASE("ucl: integer: invariants", "[ucl]") { - auto i = nihil::ucl::integer(42); - REQUIRE(i == 42); + using namespace nihil::ucl; + + static_assert(std::same_as); + REQUIRE(integer::ucl_type == object_type::integer); + REQUIRE(static_cast<::ucl_type>(integer::ucl_type) == UCL_INT); + + static_assert(std::destructible); + static_assert(std::default_initializable); + static_assert(std::move_constructible); + static_assert(std::copy_constructible); + static_assert(std::equality_comparable); + static_assert(std::totally_ordered); + static_assert(std::swappable); } TEST_CASE("ucl: integer: default construct", "[ucl]") @@ -20,6 +34,42 @@ TEST_CASE("ucl: integer: default construct", "[ucl]") REQUIRE(i == 0); } +TEST_CASE("ucl: integer: construct", "[ucl]") +{ + auto i = nihil::ucl::integer(42); + REQUIRE(i == 42); +} + +TEST_CASE("ucl: integer: swap", "[ucl]") +{ + // do not add using namespace nihil::ucl + + auto i1 = nihil::ucl::integer(1); + auto i2 = nihil::ucl::integer(2); + + swap(i1, i2); + + REQUIRE(i1 == 2); + REQUIRE(i2 == 1); +} + +TEST_CASE("ucl: integer: value()", "[ucl]") +{ + auto i = nihil::ucl::integer(42); + REQUIRE(i.value() == 42); +} + +TEST_CASE("ucl: integer: key()", "[ucl]") +{ + using namespace nihil::ucl; + + auto obj = parse("an_int = 42"); + REQUIRE(object_cast(obj["an_int"]).key() == "an_int"); + + auto i = nihil::ucl::integer(42); + REQUIRE(i.key() == ""); +} + TEST_CASE("ucl: integer: operator==", "[ucl]") { auto i = nihil::ucl::integer(42); @@ -42,7 +92,7 @@ TEST_CASE("ucl: integer: operator<=>", "[ucl]") REQUIRE(i > nihil::ucl::integer(1)); } -TEST_CASE("ucl: parse: integer", "[ucl]") +TEST_CASE("ucl: integer: parse", "[ucl]") { using namespace std::literals; @@ -53,6 +103,13 @@ TEST_CASE("ucl: parse: integer", "[ucl]") } TEST_CASE("ucl: integer: emit", "[ucl]") +{ + auto i = nihil::ucl::integer(42); + auto str = std::format("{}", i); + REQUIRE(str == "42"); +} + +TEST_CASE("ucl: integer: parse and emit", "[ucl]") { auto ucl = nihil::ucl::parse("int = 42;"); diff --git a/nihil.ucl/tests/map.cc b/nihil.ucl/tests/map.cc index d106c79..5d2fbe1 100644 --- a/nihil.ucl/tests/map.cc +++ b/nihil.ucl/tests/map.cc @@ -2,10 +2,33 @@ * This source code is released into the public domain. */ +#include + #include +#include import nihil.ucl; +TEST_CASE("ucl: map: invariants", "[ucl]") +{ + using namespace nihil::ucl; + + REQUIRE(map<>::ucl_type == object_type::object); + REQUIRE(static_cast<::ucl_type>(map<>::ucl_type) == UCL_OBJECT); + + static_assert(std::destructible>); + static_assert(std::default_initializable>); + static_assert(std::move_constructible>); + static_assert(std::copy_constructible>); + static_assert(std::equality_comparable>); + static_assert(std::totally_ordered>); + static_assert(std::swappable>); + + static_assert(std::ranges::range>); + static_assert(std::same_as, + std::ranges::range_value_t>>); +} + TEST_CASE("ucl: map: default construct", "[ucl]") { auto map = nihil::ucl::map<>(); @@ -27,6 +50,41 @@ TEST_CASE("ucl: map: construct from initializer_list", "[ucl]") REQUIRE(map["42"] == 42); } +TEST_CASE("ucl: map: construct from range", "[ucl]") +{ + using namespace nihil::ucl; + using namespace std::literals; + + auto vec = std::vector>{ + {"1"sv, integer(1)}, + {"42"sv, integer(42)}, + }; + + auto map = nihil::ucl::map(std::from_range, vec); + + REQUIRE(str(map.type()) == "object"); + REQUIRE(map["1"] == 1); + REQUIRE(map["42"] == 42); +} + +TEST_CASE("ucl: map: construct from iterator pair", "[ucl]") +{ + using namespace nihil::ucl; + using namespace std::literals; + + auto vec = std::vector>{ + {"1"sv, integer(1)}, + {"42"sv, integer(42)}, + }; + + auto map = nihil::ucl::map(std::ranges::begin(vec), + std::ranges::end(vec)); + + REQUIRE(str(map.type()) == "object"); + REQUIRE(map["1"] == 1); + REQUIRE(map["42"] == 42); +} + TEST_CASE("ucl: map: insert", "[ucl]") { using namespace nihil::ucl; diff --git a/nihil.ucl/tests/real.cc b/nihil.ucl/tests/real.cc index 4bd9b3f..b11c113 100644 --- a/nihil.ucl/tests/real.cc +++ b/nihil.ucl/tests/real.cc @@ -2,13 +2,32 @@ * This source code is released into the public domain. */ +#include #include #include #include +#include import nihil.ucl; +TEST_CASE("ucl: real: invariants", "[ucl]") +{ + using namespace nihil::ucl; + + static_assert(std::same_as); + REQUIRE(real::ucl_type == object_type::real); + REQUIRE(static_cast<::ucl_type>(real::ucl_type) == UCL_FLOAT); + + static_assert(std::destructible); + static_assert(std::default_initializable); + static_assert(std::move_constructible); + static_assert(std::copy_constructible); + static_assert(std::equality_comparable); + static_assert(std::totally_ordered); + static_assert(std::swappable); +} + TEST_CASE("ucl: real: construct", "[ucl]") { auto obj = nihil::ucl::real(42.1); @@ -22,6 +41,19 @@ TEST_CASE("ucl: real: default construct", "[ucl]") REQUIRE(i == 0); } +TEST_CASE("ucl: real: swap", "[ucl]") +{ + // do not add using namespace nihil::ucl + + auto r1 = nihil::ucl::real(1); + auto r2 = nihil::ucl::real(2); + + swap(r1, r2); + + REQUIRE(r1 == 2.); + REQUIRE(r2 == 1.); +} + TEST_CASE("ucl: real: operator==", "[ucl]") { auto i = nihil::ucl::real(42.5); diff --git a/nihil.ucl/tests/string.cc b/nihil.ucl/tests/string.cc index 19052cd..e7eb0ad 100644 --- a/nihil.ucl/tests/string.cc +++ b/nihil.ucl/tests/string.cc @@ -2,6 +2,7 @@ * This source code is released into the public domain. */ +#include #include #include #include @@ -15,9 +16,20 @@ TEST_CASE("ucl: string: invariants", "[ucl]") { using namespace nihil::ucl; - static_assert(std::same_as); + static_assert(std::same_as); REQUIRE(string::ucl_type == object_type::string); REQUIRE(static_cast<::ucl_type>(string::ucl_type) == UCL_STRING); + + static_assert(std::destructible); + static_assert(std::default_initializable); + static_assert(std::move_constructible); + static_assert(std::copy_constructible); + static_assert(std::equality_comparable); + static_assert(std::totally_ordered); + static_assert(std::swappable); + + static_assert(std::ranges::contiguous_range); + static_assert(std::same_as>); } TEST_CASE("ucl: string: default construct", "[ucl]") @@ -74,6 +86,74 @@ TEST_CASE("ucl: string: construct from non-contiguous iterator", "[ucl]") REQUIRE(str == "testing"); } +TEST_CASE("ucl: string: swap", "[ucl]") +{ + // do not add using namespace nihil::ucl + + auto s1 = nihil::ucl::string("one"); + auto s2 = nihil::ucl::string("two"); + + swap(s1, s2); + + REQUIRE(s1 == "two"); + REQUIRE(s2 == "one"); +} + +TEST_CASE("ucl: string: value()", "[ucl]") +{ + auto s = nihil::ucl::string("te\"st"); + REQUIRE(s.value() == "te\"st"); +} + +TEST_CASE("ucl: string: key()", "[ucl]") +{ + using namespace nihil::ucl; + + auto obj = parse("a_string = \"test\""); + REQUIRE(object_cast(obj["a_string"]).key() == "a_string"); + + auto s = nihil::ucl::string("test"); + REQUIRE(s.key() == ""); +} + +TEST_CASE("ucl: string: size", "[ucl]") +{ + using namespace nihil::ucl; + + REQUIRE(string().size() == 0); + REQUIRE(string("test").size() == 4); +} + +TEST_CASE("ucl: string: empty", "[ucl]") +{ + using namespace nihil::ucl; + + REQUIRE(string().empty() == true); + REQUIRE(string("test").empty() == false); +} + +TEST_CASE("ucl: string: iterator", "[ucl]") +{ + auto str = nihil::ucl::string("test"); + + auto begin = std::ranges::begin(str); + static_assert(std::contiguous_iterator); + + auto end = std::ranges::end(str); + static_assert(std::sentinel_for); + + REQUIRE(*begin == 't'); + ++begin; + REQUIRE(*begin == 'e'); + ++begin; + REQUIRE(*begin == 's'); + ++begin; + REQUIRE(*begin == 't'); + ++begin; + + REQUIRE(begin == end); +} + TEST_CASE("ucl: string: operator==", "[ucl]") { auto str = nihil::ucl::string("testing"); @@ -108,13 +188,20 @@ TEST_CASE("ucl: string: parse", "[ucl]") { using namespace std::literals; - auto obj = nihil::ucl::parse("value = \"str\""sv); + auto obj = nihil::ucl::parse("value = \"te\\\"st\""sv); auto v = obj["value"]; REQUIRE(v.key() == "value"); - REQUIRE(object_cast(v) == "str"); + REQUIRE(object_cast(v) == "te\"st"); } TEST_CASE("ucl: string: emit", "[ucl]") +{ + auto s = nihil::ucl::string("te\"st"); + auto str = std::format("{}", s); + REQUIRE(str == "\"te\\\"st\""); +} + +TEST_CASE("ucl: string: parse and emit", "[ucl]") { auto ucl = nihil::ucl::parse("str = \"te\\\"st\";"); -- cgit v1.2.3