aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.ucl/tests
diff options
context:
space:
mode:
authorLexi Winter <lexi@le-fay.org>2025-06-27 12:08:58 +0100
committerLexi Winter <lexi@le-fay.org>2025-06-27 12:08:58 +0100
commit001c9917ace09f7b1c80d96eb067e1d37e86c546 (patch)
tree89e360961b9659a8c6b897c5412b7d6834b8eed9 /nihil.ucl/tests
parent90aa957ca9b7c217af7569009d1675e0f3ff8e9b (diff)
downloadnihil-001c9917ace09f7b1c80d96eb067e1d37e86c546.tar.gz
nihil-001c9917ace09f7b1c80d96eb067e1d37e86c546.tar.bz2
improve error handling
Diffstat (limited to 'nihil.ucl/tests')
-rw-r--r--nihil.ucl/tests/array.cc251
-rw-r--r--nihil.ucl/tests/boolean.cc168
-rw-r--r--nihil.ucl/tests/integer.cc208
-rw-r--r--nihil.ucl/tests/real.cc204
-rw-r--r--nihil.ucl/tests/string.cc383
5 files changed, 973 insertions, 241 deletions
diff --git a/nihil.ucl/tests/array.cc b/nihil.ucl/tests/array.cc
index fb23178..37928aa 100644
--- a/nihil.ucl/tests/array.cc
+++ b/nihil.ucl/tests/array.cc
@@ -34,49 +34,114 @@ TEST_CASE("ucl: array: invariants", "[ucl]")
integer>);
}
-TEST_CASE("ucl: array: default construct", "[ucl]")
+TEST_CASE("ucl: array: constructor", "[ucl]")
{
using namespace nihil::ucl;
- auto arr = array<integer>();
- REQUIRE(arr.size() == 0);
- REQUIRE(str(arr.type()) == "array");
+ SECTION("default") {
+ auto arr = array<integer>();
+ REQUIRE(arr.size() == 0);
+ REQUIRE(str(arr.type()) == "array");
+ }
+
+ SECTION("from range") {
+ auto vec = std::vector{integer(1), integer(42)};
+ auto arr = array<integer>(std::from_range, vec);
+
+ REQUIRE(arr.size() == 2);
+ REQUIRE(arr[0] == 1);
+ REQUIRE(arr[1] == 42);
+ }
+
+ SECTION("from iterator pair") {
+ auto vec = std::vector{integer(1), integer(42)};
+ auto arr = array<integer>(std::ranges::begin(vec),
+ std::ranges::end(vec));
+
+ REQUIRE(arr.size() == 2);
+ REQUIRE(arr[0] == 1);
+ REQUIRE(arr[1] == 42);
+ }
+
+ SECTION("from initializer_list") {
+ auto arr = array<integer>{integer(1), integer(42)};
+
+ REQUIRE(arr.size() == 2);
+ REQUIRE(arr[0] == 1);
+ REQUIRE(arr[1] == 42);
+ }
}
-TEST_CASE("ucl: array: construct from range", "[ucl]")
+TEST_CASE("ucl: array: construct from UCL object", "[ucl]")
{
using namespace nihil::ucl;
- auto vec = std::vector{integer(1), integer(42)};
- auto arr = array<integer>(std::from_range, vec);
+ SECTION("ref, correct type") {
+ auto uarr = ::ucl_object_typed_new(UCL_ARRAY);
+ auto uint = ::ucl_object_fromint(42);
+ ::ucl_array_append(uarr, uint);
- REQUIRE(arr.size() == 2);
- REQUIRE(arr[0] == 1);
- REQUIRE(arr[1] == 42);
-}
+ auto arr = array<integer>(ref, uarr);
+ REQUIRE(arr[0] == 42);
-TEST_CASE("ucl: array: construct from iterator pair", "[ucl]")
-{
- using namespace nihil::ucl;
+ ::ucl_object_unref(uarr);
+ }
- auto vec = std::vector{integer(1), integer(42)};
- auto arr = array<integer>(std::ranges::begin(vec),
- std::ranges::end(vec));
+ SECTION("noref, correct type") {
+ auto uarr = ::ucl_object_typed_new(UCL_ARRAY);
+ auto uint = ::ucl_object_fromint(42);
+ ::ucl_array_append(uarr, uint);
- REQUIRE(arr.size() == 2);
- REQUIRE(arr[0] == 1);
- REQUIRE(arr[1] == 42);
+ auto arr = array<integer>(noref, uarr);
+ REQUIRE(arr[0] == 42);
+ }
+
+ SECTION("ref, wrong element type") {
+ auto uarr = ::ucl_object_typed_new(UCL_ARRAY);
+ auto uint = ::ucl_object_frombool(true);
+ ::ucl_array_append(uarr, uint);
+
+ auto arr = array<integer>(noref, uarr);
+ REQUIRE_THROWS_AS(arr[0], type_mismatch);
+ }
+
+ SECTION("ref, wrong type") {
+ auto uobj = ::ucl_object_frombool(true);
+
+ REQUIRE_THROWS_AS(array(ref, uobj), type_mismatch);
+
+ ::ucl_object_unref(uobj);
+ }
+
+ SECTION("noref, wrong type") {
+ auto uobj = ::ucl_object_frombool(true);
+
+ REQUIRE_THROWS_AS(array(noref, uobj), type_mismatch);
+
+ ::ucl_object_unref(uobj);
+ }
}
-TEST_CASE("ucl: array: construct from initializer_list", "[ucl]")
+TEST_CASE("ucl: array: swap", "[ucl]")
{
- using namespace nihil::ucl;
+ // do not add using namespace nihil::ucl
- auto arr = array<integer>{integer(1), integer(42)};
+ auto arr1 = nihil::ucl::array<nihil::ucl::integer>{
+ nihil::ucl::integer(1),
+ nihil::ucl::integer(2)
+ };
- REQUIRE(arr.size() == 2);
- REQUIRE(arr[0] == 1);
- REQUIRE(arr[1] == 42);
+ auto arr2 = nihil::ucl::array<nihil::ucl::integer>{
+ nihil::ucl::integer(3),
+ };
+
+ swap(arr1, arr2);
+
+ REQUIRE(arr1.size() == 1);
+ REQUIRE(arr1[0] == 3);
+
+ REQUIRE(arr2.size() == 2);
+ REQUIRE(arr2[0] == 1);
}
TEST_CASE("ucl: array: push_back", "[ucl]")
@@ -156,14 +221,10 @@ TEST_CASE("ucl: array: parse", "[ucl]")
using namespace std::literals;
using namespace nihil::ucl;
- auto obj_err = parse("value = [1, 42, 666]"sv);
- REQUIRE(obj_err);
- auto obj = *obj_err;
+ auto obj = parse("value = [1, 42, 666]"sv).value();
- auto err = object_cast<array<integer>>(obj["value"]);
- REQUIRE(err);
+ auto arr = object_cast<array<integer>>(obj["value"]).value();
- auto arr = *err;
REQUIRE(arr.size() == 3);
REQUIRE(arr[0] == 1);
REQUIRE(arr[1] == 42);
@@ -174,10 +235,9 @@ TEST_CASE("ucl: array: emit", "[ucl]")
{
using namespace nihil::ucl;
- auto ucl = parse("array = [1, 42, 666];");
- REQUIRE(ucl);
+ auto ucl = parse("array = [1, 42, 666];").value();
- auto output = std::format("{:c}", *ucl);
+ auto output = std::format("{:c}", ucl);
REQUIRE(output ==
"array [\n"
" 1,\n"
@@ -186,6 +246,65 @@ TEST_CASE("ucl: array: emit", "[ucl]")
"]\n");
}
+TEST_CASE("ucl: array: format", "[ucl]")
+{
+ using namespace nihil::ucl;
+
+ SECTION("empty array") {
+ auto arr = array<integer>();
+ REQUIRE(std::format("{}", arr) == "[]");
+ }
+
+ SECTION("bare array") {
+ auto arr = array<integer>{
+ integer(1), integer(42), integer(666)
+ };
+
+ auto output = std::format("{}", arr);
+ REQUIRE(output == "[1, 42, 666]");
+ }
+
+ SECTION("parsed array") {
+ auto ucl = parse("array = [1, 42, 666];").value();
+ auto arr = object_cast<array<integer>>(ucl["array"]).value();
+
+ auto output = std::format("{}", arr);
+ REQUIRE(output == "[1, 42, 666]");
+ }
+}
+
+TEST_CASE("ucl: array: print to ostream", "[ucl]")
+{
+ using namespace nihil::ucl;
+
+ SECTION("empty array") {
+ auto arr = array<integer>();
+ auto strm = std::ostringstream();
+ strm << arr;
+
+ REQUIRE(strm.str() == "[]");
+ }
+
+ SECTION("bare array") {
+ auto arr = array<integer>{
+ integer(1), integer(42), integer(666)
+ };
+ auto strm = std::ostringstream();
+ strm << arr;
+
+ REQUIRE(strm.str() == "[1, 42, 666]");
+ }
+
+ SECTION("parsed array") {
+ auto ucl = parse("array = [1, 42, 666];").value();
+ auto arr = object_cast<array<integer>>(ucl["array"]).value();
+ auto strm = std::ostringstream();
+ strm << arr;
+
+ REQUIRE(strm.str() == "[1, 42, 666]");
+ }
+}
+
TEST_CASE("ucl: array is a sized_range", "[ucl]")
{
using namespace nihil::ucl;
@@ -296,3 +415,65 @@ TEST_CASE("ucl: array: homogeneous cast", "[ucl]")
REQUIRE(obj_arr[1] == 42);
}
+TEST_CASE("array iterator: empty iterator", "[ucl]")
+{
+ using namespace nihil::ucl;
+
+ auto it = array_iterator<integer>();
+
+ REQUIRE_THROWS_AS(*it, std::logic_error);
+ REQUIRE_THROWS_AS(it[0], std::logic_error);
+ REQUIRE_THROWS_AS(it++, std::logic_error);
+ REQUIRE_THROWS_AS(++it, std::logic_error);
+
+ auto it2 = array_iterator<integer>();
+ REQUIRE(it == it2);
+ REQUIRE((it < it2) == false);
+ REQUIRE((it > it2) == false);
+}
+
+TEST_CASE("array iterator: invalid operations", "[ucl]")
+{
+ using namespace nihil::ucl;
+
+ auto arr = array<integer>{ integer(42) };
+ auto it = arr.begin();
+
+ SECTION("decrement before start") {
+ REQUIRE_THROWS_AS(--it, std::logic_error);
+ REQUIRE_THROWS_AS(it--, std::logic_error);
+ REQUIRE_THROWS_AS(it - 1, std::logic_error);
+ }
+
+ SECTION("increment past end") {
+ ++it;
+ REQUIRE(it == arr.end());
+
+ REQUIRE_THROWS_AS(++it, std::logic_error);
+ REQUIRE_THROWS_AS(it++, std::logic_error);
+ REQUIRE_THROWS_AS(it + 1, std::logic_error);
+ }
+
+ SECTION("dereference iterator at end") {
+ REQUIRE_THROWS_AS(it[1], std::logic_error);
+
+ ++it;
+ REQUIRE(it == arr.end());
+
+ REQUIRE_THROWS_AS(*it, std::logic_error);
+ }
+
+ SECTION("compare with different array") {
+ auto arr2 = array<integer>{ integer(42) };
+ REQUIRE_THROWS_AS(it == arr2.begin(), std::logic_error);
+ REQUIRE_THROWS_AS(it > arr2.begin(), std::logic_error);
+ REQUIRE_THROWS_AS(it - arr2.begin(), std::logic_error);
+ }
+
+ SECTION("compare with empty iterator") {
+ auto it2 = array_iterator<integer>();
+ REQUIRE_THROWS_AS(it == it2, std::logic_error);
+ REQUIRE_THROWS_AS(it > it2, std::logic_error);
+ REQUIRE_THROWS_AS(it - it2, std::logic_error);
+ }
+}
diff --git a/nihil.ucl/tests/boolean.cc b/nihil.ucl/tests/boolean.cc
index 495071d..f7ef95e 100644
--- a/nihil.ucl/tests/boolean.cc
+++ b/nihil.ucl/tests/boolean.cc
@@ -27,16 +27,71 @@ TEST_CASE("ucl: boolean: invariants", "[ucl]")
static_assert(std::swappable<boolean>);
}
-TEST_CASE("ucl: boolean: default construct", "[ucl]")
+TEST_CASE("ucl: boolean: constructor", "[ucl]")
{
- auto b = nihil::ucl::boolean();
- REQUIRE(b == false);
+ using namespace nihil::ucl;
+
+ SECTION("default") {
+ auto b = boolean();
+ REQUIRE(b == false);
+ }
+
+ SECTION("with value") {
+ auto b = boolean(true);
+ REQUIRE(b == true);
+ }
}
-TEST_CASE("ucl: boolean: construct from value", "[ucl]")
+TEST_CASE("ucl: boolean: construct from UCL object", "[ucl]")
{
- auto b = nihil::ucl::boolean(true);
- REQUIRE(b == true);
+ using namespace nihil::ucl;
+
+ SECTION("ref, correct type") {
+ auto uobj = ::ucl_object_frombool(true);
+
+ auto i = boolean(ref, uobj);
+ REQUIRE(i == true);
+
+ ::ucl_object_unref(uobj);
+ }
+
+ SECTION("noref, correct type") {
+ auto uobj = ::ucl_object_frombool(true);
+
+ auto i = boolean(noref, uobj);
+ REQUIRE(i == true);
+ }
+
+ SECTION("ref, wrong type") {
+ auto uobj = ::ucl_object_fromint(1);
+
+ REQUIRE_THROWS_AS(boolean(ref, uobj), type_mismatch);
+
+ ::ucl_object_unref(uobj);
+ }
+
+ SECTION("noref, wrong type") {
+ auto uobj = ::ucl_object_fromint(1);
+
+ REQUIRE_THROWS_AS(boolean(noref, uobj), type_mismatch);
+
+ ::ucl_object_unref(uobj);
+ }
+}
+
+TEST_CASE("ucl: boolean: make_boolean", "[ucl]")
+{
+ using namespace nihil::ucl;
+
+ SECTION("default value") {
+ auto b = make_boolean().value();
+ REQUIRE(b == false);
+ }
+
+ SECTION("explicit value") {
+ auto b = make_boolean(true).value();
+ REQUIRE(b == true);
+ }
}
TEST_CASE("ucl: boolean: swap", "[ucl]")
@@ -72,57 +127,98 @@ TEST_CASE("ucl: boolean: key()", "[ucl]")
REQUIRE(b.key() == "");
}
-TEST_CASE("ucl: boolean: operator==", "[ucl]")
+TEST_CASE("ucl: boolean: comparison", "[ucl]")
{
- auto b = nihil::ucl::boolean(true);
+ using namespace nihil::ucl;
- REQUIRE(b == true);
- REQUIRE(b == nihil::ucl::boolean(true));
+ auto b = boolean(true);
- REQUIRE(b != false);
- REQUIRE(b != nihil::ucl::boolean(false));
-}
+ SECTION("operator==") {
+ REQUIRE(b == true);
+ REQUIRE(b == boolean(true));
+ }
-TEST_CASE("ucl: boolean: operator<=>", "[ucl]")
-{
- auto b = nihil::ucl::boolean(false);
+ SECTION("operator!=") {
+ REQUIRE(b != false);
+ REQUIRE(b != boolean(false));
+ }
- REQUIRE(b < true);
- REQUIRE(b < nihil::ucl::boolean(true));
+ SECTION("operator<") {
+ REQUIRE(b <= true);
+ REQUIRE(b <= nihil::ucl::boolean(true));
+ }
- REQUIRE(b >= false);
- REQUIRE(b >= nihil::ucl::boolean(false));
+ SECTION("operator>") {
+ REQUIRE(b > false);
+ REQUIRE(b > nihil::ucl::boolean(false));
+ }
}
TEST_CASE("ucl: boolean: parse", "[ucl]")
{
- using namespace std::literals;
-
- auto err = nihil::ucl::parse("value = true"sv);
- REQUIRE(err);
+ using namespace nihil::ucl;
- auto obj = *err;
+ auto obj = parse("value = true").value();
auto v = obj["value"];
REQUIRE(v.key() == "value");
- REQUIRE(*object_cast<nihil::ucl::boolean>(v) == true);
-}
-
-TEST_CASE("ucl: boolean: emit", "[ucl]")
-{
- auto b = nihil::ucl::boolean(true);
- auto str = std::format("{}", b);
- REQUIRE(str == "true");
+ REQUIRE(object_cast<boolean>(v).value() == true);
}
TEST_CASE("ucl: boolean: parse and emit", "[ucl]")
{
- auto ucl = nihil::ucl::parse("bool = true;");
- REQUIRE(ucl);
+ using namespace nihil::ucl;
+
+ auto ucl = parse("bool = true;").value();
auto output = std::string();
- emit(*ucl, nihil::ucl::emitter::configuration,
+ emit(ucl, nihil::ucl::emitter::configuration,
std::back_inserter(output));
REQUIRE(output == "bool = true;\n");
}
+
+TEST_CASE("ucl: boolean: format", "[ucl]")
+{
+ using namespace nihil::ucl;
+
+ SECTION("bare boolean") {
+ auto str = std::format("{}", boolean(true));
+ REQUIRE(str == "true");
+ }
+
+ SECTION("parsed boolean") {
+ auto obj = parse("bool = true;").value();
+ auto b = object_cast<boolean>(obj["bool"]).value();
+
+ auto str = std::format("{}", b);
+ REQUIRE(str == "true");
+ }
+
+ SECTION("with format string") {
+ auto str = std::format("{: >5}", boolean(true));
+ REQUIRE(str == " true");
+ }
+}
+
+TEST_CASE("ucl: boolean: print to ostream", "[ucl]")
+{
+ using namespace nihil::ucl;
+
+ SECTION("bare boolean") {
+ auto strm = std::ostringstream();
+ strm << boolean(true);
+
+ REQUIRE(strm.str() == "true");
+ }
+
+ SECTION("parsed boolean") {
+ auto obj = parse("bool = true;").value();
+ auto i = object_cast<boolean>(obj["bool"]).value();
+
+ auto strm = std::ostringstream();
+ strm << i;
+
+ REQUIRE(strm.str() == "true");
+ }
+}
diff --git a/nihil.ucl/tests/integer.cc b/nihil.ucl/tests/integer.cc
index 05647fe..6584764 100644
--- a/nihil.ucl/tests/integer.cc
+++ b/nihil.ucl/tests/integer.cc
@@ -28,16 +28,90 @@ TEST_CASE("ucl: integer: invariants", "[ucl]")
static_assert(std::swappable<integer>);
}
-TEST_CASE("ucl: integer: default construct", "[ucl]")
+TEST_CASE("ucl: integer: constructor", "[ucl]")
{
- auto i = nihil::ucl::integer();
- REQUIRE(i == 0);
+ using namespace nihil::ucl;
+
+ SECTION("default") {
+ auto i = integer();
+ REQUIRE(i == 0);
+ }
+
+ SECTION("with value") {
+ auto i = integer(42);
+ REQUIRE(i == 42);
+ }
}
-TEST_CASE("ucl: integer: construct", "[ucl]")
+TEST_CASE("ucl: integer: literal", "[ucl]")
{
- auto i = nihil::ucl::integer(42);
- REQUIRE(i == 42);
+ SECTION("with namespace nihil::ucl::literals") {
+ using namespace nihil::ucl::literals;
+
+ auto i = 42_ucl;
+ REQUIRE(i.type() == nihil::ucl::object_type::integer);
+ REQUIRE(i == 42);
+ }
+
+ SECTION("with namespace nihil::literals") {
+ using namespace nihil::literals;
+
+ auto i = 42_ucl;
+ REQUIRE(i.type() == nihil::ucl::object_type::integer);
+ REQUIRE(i == 42);
+ }
+}
+
+TEST_CASE("ucl: integer: construct from UCL object", "[ucl]")
+{
+ using namespace nihil::ucl;
+
+ SECTION("ref, correct type") {
+ auto uobj = ::ucl_object_fromint(42);
+
+ auto i = integer(ref, uobj);
+ REQUIRE(i == 42);
+
+ ::ucl_object_unref(uobj);
+ }
+
+ SECTION("noref, correct type") {
+ auto uobj = ::ucl_object_fromint(42);
+
+ auto i = integer(noref, uobj);
+ REQUIRE(i == 42);
+ }
+
+ SECTION("ref, wrong type") {
+ auto uobj = ::ucl_object_frombool(true);
+
+ REQUIRE_THROWS_AS(integer(ref, uobj), type_mismatch);
+
+ ::ucl_object_unref(uobj);
+ }
+
+ SECTION("noref, wrong type") {
+ auto uobj = ::ucl_object_frombool(true);
+
+ REQUIRE_THROWS_AS(integer(noref, uobj), type_mismatch);
+
+ ::ucl_object_unref(uobj);
+ }
+}
+
+TEST_CASE("ucl: integer: make_integer", "[ucl]")
+{
+ using namespace nihil::ucl;
+
+ SECTION("default value") {
+ auto i = make_integer().value();
+ REQUIRE(i == 0);
+ }
+
+ SECTION("explicit value") {
+ auto i = make_integer(42).value();
+ REQUIRE(i == 42);
+ }
}
TEST_CASE("ucl: integer: swap", "[ucl]")
@@ -55,7 +129,9 @@ TEST_CASE("ucl: integer: swap", "[ucl]")
TEST_CASE("ucl: integer: value()", "[ucl]")
{
- auto i = nihil::ucl::integer(42);
+ using namespace nihil::ucl;
+
+ auto i = 42_ucl;
REQUIRE(i.value() == 42);
}
@@ -63,67 +139,109 @@ TEST_CASE("ucl: integer: key()", "[ucl]")
{
using namespace nihil::ucl;
- auto err = parse("an_int = 42");
- REQUIRE(err);
+ SECTION("parsed with key") {
+ auto obj = parse("an_int = 42").value();
+ auto i = object_cast<integer>(obj["an_int"]).value();
+ REQUIRE(i.key() == "an_int");
+ }
- auto obj = *err;
- REQUIRE(object_cast<integer>(obj["an_int"])->key() == "an_int");
-
- auto i = nihil::ucl::integer(42);
- REQUIRE(i.key() == "");
+ SECTION("bare integer, no key") {
+ auto i = 42_ucl;
+ REQUIRE(i.key() == "");
+ }
}
-TEST_CASE("ucl: integer: operator==", "[ucl]")
+TEST_CASE("ucl: integer: comparison", "[ucl]")
{
- auto i = nihil::ucl::integer(42);
+ using namespace nihil::ucl;
- REQUIRE(i == 42);
- REQUIRE(i == nihil::ucl::integer(42));
+ auto i = 42_ucl;
- REQUIRE(i != 1);
- REQUIRE(i != nihil::ucl::integer(1));
-}
+ SECTION("operator==") {
+ REQUIRE(i == 42);
+ REQUIRE(i == 42_ucl);
+ }
-TEST_CASE("ucl: integer: operator<=>", "[ucl]")
-{
- auto i = nihil::ucl::integer(42);
+ SECTION("operator!=") {
+ REQUIRE(i != 1);
+ REQUIRE(i != 1_ucl);
+ }
- REQUIRE(i < 43);
- REQUIRE(i < nihil::ucl::integer(43));
+ SECTION("operator<") {
+ REQUIRE(i < 43);
+ REQUIRE(i < 43_ucl);
+ }
- REQUIRE(i > 1);
- REQUIRE(i > nihil::ucl::integer(1));
+ SECTION("operator>") {
+ REQUIRE(i > 1);
+ REQUIRE(i > 1_ucl);
+ }
}
TEST_CASE("ucl: integer: parse", "[ucl]")
{
- using namespace std::literals;
-
- auto err = nihil::ucl::parse("value = 42"sv);
- REQUIRE(err);
+ using namespace nihil::ucl;
- auto obj = *err;
+ auto obj = parse("value = 42").value();
auto v = obj["value"];
REQUIRE(v.key() == "value");
- REQUIRE(object_cast<nihil::ucl::integer>(v) == 42);
-}
-
-TEST_CASE("ucl: integer: emit", "[ucl]")
-{
- auto i = nihil::ucl::integer(42);
- auto str = std::format("{}", i);
- REQUIRE(str == "42");
+ REQUIRE(object_cast<integer>(v) == 42);
}
TEST_CASE("ucl: integer: parse and emit", "[ucl]")
{
- auto ucl = nihil::ucl::parse("int = 42;");
- REQUIRE(ucl);
+ using namespace nihil::ucl;
+
+ auto ucl = parse("int = 42;").value();
auto output = std::string();
- emit(*ucl, nihil::ucl::emitter::configuration,
- std::back_inserter(output));
+ emit(ucl, emitter::configuration, std::back_inserter(output));
REQUIRE(output == "int = 42;\n");
}
+
+TEST_CASE("ucl: integer: format", "[ucl]")
+{
+ using namespace nihil::ucl;
+
+ SECTION("bare integer") {
+ auto str = std::format("{}", 42_ucl);
+ REQUIRE(str == "42");
+ }
+
+ SECTION("parsed integer") {
+ auto obj = parse("int = 42;").value();
+ auto i = object_cast<integer>(obj["int"]).value();
+
+ auto str = std::format("{}", i);
+ REQUIRE(str == "42");
+ }
+
+ SECTION("with format string") {
+ auto str = std::format("{:-05}", 42_ucl);
+ REQUIRE(str == "00042");
+ }
+}
+
+TEST_CASE("ucl: integer: print to ostream", "[ucl]")
+{
+ using namespace nihil::ucl;
+
+ SECTION("bare integer") {
+ auto strm = std::ostringstream();
+ strm << 42_ucl;
+
+ REQUIRE(strm.str() == "42");
+ }
+
+ SECTION("parsed integer") {
+ auto obj = parse("int = 42;").value();
+ auto i = object_cast<integer>(obj["int"]).value();
+
+ auto strm = std::ostringstream();
+ strm << i;
+
+ REQUIRE(strm.str() == "42");
+ }
+}
diff --git a/nihil.ucl/tests/real.cc b/nihil.ucl/tests/real.cc
index be4e213..421917e 100644
--- a/nihil.ucl/tests/real.cc
+++ b/nihil.ucl/tests/real.cc
@@ -28,17 +28,90 @@ TEST_CASE("ucl: real: invariants", "[ucl]")
static_assert(std::swappable<real>);
}
-TEST_CASE("ucl: real: construct", "[ucl]")
+TEST_CASE("ucl: real: constructor", "[ucl]")
{
- auto obj = nihil::ucl::real(42.1);
- REQUIRE_THAT(object_cast<nihil::ucl::real>(obj)->value(),
- Catch::Matchers::WithinRel(42.1));
+ using namespace nihil::ucl;
+
+ SECTION("default") {
+ auto r = real();
+ REQUIRE(r == 0);
+ }
+
+ SECTION("with value") {
+ auto r = real(42.1);
+ REQUIRE_THAT(r.value(), Catch::Matchers::WithinRel(42.1));
+ }
}
-TEST_CASE("ucl: real: default construct", "[ucl]")
+TEST_CASE("ucl: real: literal", "[ucl]")
{
- auto i = nihil::ucl::real();
- REQUIRE(i == 0);
+ SECTION("with namespace nihil::ucl::literals") {
+ using namespace nihil::ucl::literals;
+
+ auto r = 42.5_ucl;
+ REQUIRE(r.type() == nihil::ucl::object_type::real);
+ REQUIRE_THAT(r.value(), Catch::Matchers::WithinRel(42.5));
+ }
+
+ SECTION("with namespace nihil::literals") {
+ using namespace nihil::literals;
+
+ auto r = 42.5_ucl;
+ REQUIRE(r.type() == nihil::ucl::object_type::real);
+ REQUIRE_THAT(r.value(), Catch::Matchers::WithinRel(42.5));
+ }
+}
+
+TEST_CASE("ucl: real: construct from UCL object", "[ucl]")
+{
+ using namespace nihil::ucl;
+
+ SECTION("ref, correct type") {
+ auto uobj = ::ucl_object_fromdouble(42);
+
+ auto r = real(ref, uobj);
+ REQUIRE(r == 42);
+
+ ::ucl_object_unref(uobj);
+ }
+
+ SECTION("noref, correct type") {
+ auto uobj = ::ucl_object_fromdouble(42);
+
+ auto r = real(noref, uobj);
+ REQUIRE(r == 42);
+ }
+
+ SECTION("ref, wrong type") {
+ auto uobj = ::ucl_object_fromint(42);
+
+ REQUIRE_THROWS_AS(real(ref, uobj), type_mismatch);
+
+ ::ucl_object_unref(uobj);
+ }
+
+ SECTION("noref, wrong type") {
+ auto uobj = ::ucl_object_fromint(42);
+
+ REQUIRE_THROWS_AS(real(noref, uobj), type_mismatch);
+
+ ::ucl_object_unref(uobj);
+ }
+}
+
+TEST_CASE("ucl: real: make_real", "[ucl]")
+{
+ using namespace nihil::ucl;
+
+ SECTION("default value") {
+ auto i = make_real().value();
+ REQUIRE(i == 0);
+ }
+
+ SECTION("explicit value") {
+ auto i = make_real(42).value();
+ REQUIRE(i == 42);
+ }
}
TEST_CASE("ucl: real: swap", "[ucl]")
@@ -54,53 +127,122 @@ TEST_CASE("ucl: real: swap", "[ucl]")
REQUIRE(r2 == 1.);
}
-TEST_CASE("ucl: real: operator==", "[ucl]")
+TEST_CASE("ucl: real: value()", "[ucl]")
{
- auto i = nihil::ucl::real(42.5);
+ using namespace nihil::ucl;
+
+ auto r = 42.5_ucl;
+ REQUIRE_THAT(r.value(), Catch::Matchers::WithinRel(42.5));
+}
+
+TEST_CASE("ucl: real: key()", "[ucl]")
+{
+ using namespace nihil::ucl;
- REQUIRE(i == 42.5);
- REQUIRE(i == nihil::ucl::real(42.5));
+ SECTION("parsed with key") {
+ auto obj = parse("a_real = 42.5").value();
+ auto r = object_cast<real>(obj["a_real"]).value();
+ REQUIRE(r.key() == "a_real");
+ }
- REQUIRE(i != 1);
- REQUIRE(i != nihil::ucl::real(1));
+ SECTION("bare real, no key") {
+ auto i = 42.5_ucl;
+ REQUIRE(i.key() == "");
+ }
}
-TEST_CASE("ucl: real: operator<=>", "[ucl]")
+TEST_CASE("ucl: real: comparison", "[ucl]")
{
- auto i = nihil::ucl::real(42.5);
+ using namespace nihil::ucl;
- REQUIRE(i < 43);
- REQUIRE(i < nihil::ucl::real(43));
+ auto i = nihil::ucl::real(42.5);
- REQUIRE(i > 1);
- REQUIRE(i > nihil::ucl::real(1));
+ SECTION("operator==") {
+ REQUIRE(i == 42.5);
+ REQUIRE(i == 42.5_ucl);
+ }
+
+ SECTION("operator!=") {
+ REQUIRE(i != 1);
+ REQUIRE(i != 1._ucl);
+ }
+
+ SECTION("operator<") {
+ REQUIRE(i < 43);
+ REQUIRE(i < 43._ucl);
+ }
+
+ SECTION("operator>") {
+ REQUIRE(i > 1);
+ REQUIRE(i > 1._ucl);
+ }
}
TEST_CASE("ucl: real: parse", "[ucl]")
{
- using namespace std::literals;
-
- auto err = nihil::ucl::parse("value = 42.1"sv);
- REQUIRE(err);
+ using namespace nihil::ucl;
- auto obj = *err;
+ auto obj = parse("value = 42.1").value();
auto v = obj["value"];
REQUIRE(v.key() == "value");
- REQUIRE_THAT(object_cast<nihil::ucl::real>(v)->value(),
+ REQUIRE_THAT(object_cast<real>(v).value().value(),
Catch::Matchers::WithinRel(42.1));
}
-TEST_CASE("ucl: real: emit", "[ucl]")
+TEST_CASE("ucl: real: parse and emit", "[ucl]")
{
- auto err = nihil::ucl::parse("real = 42.2");
- REQUIRE(err);
+ using namespace nihil::ucl;
- auto obj = *err;
+ auto ucl = parse("real = 42.2").value();
auto output = std::string();
- emit(obj, nihil::ucl::emitter::configuration,
- std::back_inserter(output));
+ emit(ucl, emitter::configuration, std::back_inserter(output));
REQUIRE(output == "real = 42.2;\n");
}
+
+TEST_CASE("ucl: real: format", "[ucl]")
+{
+ using namespace nihil::ucl;
+
+ SECTION("bare real") {
+ auto str = std::format("{}", 42.5_ucl);
+ REQUIRE(str == "42.5");
+ }
+
+ SECTION("parsed real") {
+ auto obj = parse("real = 42.5;").value();
+ auto r = object_cast<real>(obj["real"]).value();
+
+ auto str = std::format("{}", r);
+ REQUIRE(str == "42.5");
+ }
+
+ SECTION("with format string") {
+ auto str = std::format("{:10.5f}", 42.5_ucl);
+ REQUIRE(str == " 42.50000");
+ }
+}
+
+TEST_CASE("ucl: real: print to ostream", "[ucl]")
+{
+ using namespace nihil::ucl;
+
+ SECTION("bare real") {
+ auto strm = std::ostringstream();
+ strm << 42.5_ucl;
+
+ REQUIRE(strm.str() == "42.5");
+ }
+
+ SECTION("parsed real") {
+ auto obj = parse("real = 42.5;").value();
+ auto i = object_cast<real>(obj["real"]).value();
+
+ auto strm = std::ostringstream();
+ strm << i;
+
+ REQUIRE(strm.str() == "42.5");
+ }
+}
diff --git a/nihil.ucl/tests/string.cc b/nihil.ucl/tests/string.cc
index 995e95a..6409b8d 100644
--- a/nihil.ucl/tests/string.cc
+++ b/nihil.ucl/tests/string.cc
@@ -4,6 +4,7 @@
#include <concepts>
#include <list>
+#include <sstream>
#include <string>
#include <vector>
@@ -32,58 +33,188 @@ TEST_CASE("ucl: string: invariants", "[ucl]")
static_assert(std::same_as<char, std::ranges::range_value_t<string>>);
}
-TEST_CASE("ucl: string: default construct", "[ucl]")
+TEST_CASE("ucl: string: literal", "[ucl]")
{
- auto str = nihil::ucl::string();
- REQUIRE(str == "");
-}
+ SECTION("with namespace nihil::ucl::literals") {
+ using namespace nihil::ucl::literals;
-TEST_CASE("ucl: string: construct from string literal", "[ucl]")
-{
- auto str = nihil::ucl::string("testing");
- REQUIRE(str == "testing");
-}
+ auto s = "testing"_ucl;
+ REQUIRE(s.type() == nihil::ucl::object_type::string);
+ REQUIRE(s == "testing");
+ }
-TEST_CASE("ucl: string: construct from std::string", "[ucl]")
-{
- auto str = nihil::ucl::string(std::string("testing"));
- REQUIRE(str == "testing");
-}
+ SECTION("with namespace nihil::literals") {
+ using namespace nihil::literals;
-TEST_CASE("ucl: string: construct from std::string_view", "[ucl]")
-{
- auto str = nihil::ucl::string(std::string_view("testing"));
- REQUIRE(str == "testing");
+ auto s = "testing"_ucl;
+ REQUIRE(s.type() == nihil::ucl::object_type::string);
+ REQUIRE(s == "testing");
+ }
}
-TEST_CASE("ucl: string: construct from contiguous range", "[ucl]")
+TEST_CASE("ucl: string: construct", "[ucl]")
{
- auto s = std::vector{'t', 'e', 's', 't', 'i', 'n', 'g'};
- auto str = nihil::ucl::string(std::from_range, s);
- REQUIRE(str == "testing");
-}
+ using namespace nihil::ucl;
+ using namespace std::literals;
-TEST_CASE("ucl: string: construct from non-contiguous range", "[ucl]")
-{
- auto s = std::list{'t', 'e', 's', 't', 'i', 'n', 'g'};
- auto str = nihil::ucl::string(std::from_range, s);
- REQUIRE(str == "testing");
+ SECTION("empty string") {
+ auto str = string();
+ REQUIRE(str.type() == object_type::string);
+ REQUIRE(str == "");
+ }
+
+ SECTION("with integer-like value") {
+ auto str = "42"_ucl;
+ REQUIRE(str.type() == object_type::string);
+ REQUIRE(str == "42");
+ }
+
+ SECTION("with boolean-like value") {
+ auto str = "true"_ucl;
+ REQUIRE(str.type() == object_type::string);
+ REQUIRE(str == "true");
+ }
+
+ SECTION("from string literal") {
+ auto str = string("testing");
+ REQUIRE(str.type() == object_type::string);
+ REQUIRE(str == "testing");
+ }
+
+ SECTION("from std::string") {
+ auto str = string("testing"s);
+ REQUIRE(str.type() == object_type::string);
+ REQUIRE(str == "testing");
+ }
+
+ SECTION("from std::string_view") {
+ auto str = string("testing"sv);
+ REQUIRE(str.type() == object_type::string);
+ REQUIRE(str == "testing");
+ }
+
+ SECTION("from contiguous range") {
+ auto s = std::vector{'t', 'e', 's', 't', 'i', 'n', 'g'};
+ auto str = string(s);
+ REQUIRE(str.type() == object_type::string);
+ REQUIRE(str == "testing");
+ }
+
+ SECTION("from non-contiguous range") {
+ auto s = std::list{'t', 'e', 's', 't', 'i', 'n', 'g'};
+ auto str = string(s);
+ REQUIRE(str.type() == object_type::string);
+ REQUIRE(str == "testing");
+ }
+
+ SECTION("from contiguous iterator pair") {
+ auto s = std::vector{'t', 'e', 's', 't', 'i', 'n', 'g'};
+ auto str = string(s.begin(), s.end());
+ REQUIRE(str.type() == object_type::string);
+ REQUIRE(str == "testing");
+ }
+
+ SECTION("from non-contiguous iterator pair") {
+ auto s = std::list{'t', 'e', 's', 't', 'i', 'n', 'g'};
+ auto str = string(s.begin(), s.end());
+ REQUIRE(str.type() == object_type::string);
+ REQUIRE(str == "testing");
+ }
}
-TEST_CASE("ucl: string: construct from contiguous iterator", "[ucl]")
+TEST_CASE("ucl: string: construct from UCL object", "[ucl]")
{
- auto s = std::vector{'t', 'e', 's', 't', 'i', 'n', 'g'};
- auto str = nihil::ucl::string(std::ranges::begin(s),
- std::ranges::end(s));
- REQUIRE(str == "testing");
+ using namespace nihil::ucl;
+
+ SECTION("ref, correct type") {
+ auto uobj = ::ucl_object_fromstring("testing");
+
+ auto s = string(ref, uobj);
+ REQUIRE(s == "testing");
+
+ ::ucl_object_unref(uobj);
+ }
+
+ SECTION("noref, correct type") {
+ auto uobj = ::ucl_object_fromstring("testing");
+
+ auto s = string(noref, uobj);
+ REQUIRE(s == "testing");
+ }
+
+ SECTION("ref, wrong type") {
+ auto uobj = ::ucl_object_frombool(true);
+
+ REQUIRE_THROWS_AS(string(ref, uobj), type_mismatch);
+
+ ::ucl_object_unref(uobj);
+ }
+
+ SECTION("noref, wrong type") {
+ auto uobj = ::ucl_object_frombool(true);
+
+ REQUIRE_THROWS_AS(string(noref, uobj), type_mismatch);
+
+ ::ucl_object_unref(uobj);
+ }
}
-TEST_CASE("ucl: string: construct from non-contiguous iterator", "[ucl]")
+TEST_CASE("ucl: string: make_string", "[ucl]")
{
- auto s = std::list{'t', 'e', 's', 't', 'i', 'n', 'g'};
- auto str = nihil::ucl::string(std::ranges::begin(s),
- std::ranges::end(s));
- REQUIRE(str == "testing");
+ using namespace nihil::ucl;
+ using namespace std::literals;
+
+ SECTION("empty string") {
+ auto str = make_string().value();
+ REQUIRE(str.type() == object_type::string);
+ REQUIRE(str == "");
+ }
+
+ SECTION("from string literal") {
+ auto str = make_string("testing").value();
+ REQUIRE(str.type() == object_type::string);
+ REQUIRE(str == "testing");
+ }
+
+ SECTION("from std::string") {
+ auto str = make_string("testing"s).value();
+ REQUIRE(str.type() == object_type::string);
+ REQUIRE(str == "testing");
+ }
+
+ SECTION("from std::string_view") {
+ auto str = make_string("testing"sv).value();
+ REQUIRE(str.type() == object_type::string);
+ REQUIRE(str == "testing");
+ }
+
+ SECTION("from contiguous range") {
+ auto s = std::vector{'t', 'e', 's', 't', 'i', 'n', 'g'};
+ auto str = make_string(s).value();
+ REQUIRE(str.type() == object_type::string);
+ REQUIRE(str == "testing");
+ }
+
+ SECTION("from non-contiguous range") {
+ auto s = std::list{'t', 'e', 's', 't', 'i', 'n', 'g'};
+ auto str = make_string(s).value();
+ REQUIRE(str.type() == object_type::string);
+ REQUIRE(str == "testing");
+ }
+
+ SECTION("from contiguous iterator pair") {
+ auto s = std::vector{'t', 'e', 's', 't', 'i', 'n', 'g'};
+ auto str = make_string(s.begin(), s.end()).value();
+ REQUIRE(str.type() == object_type::string);
+ REQUIRE(str == "testing");
+ }
+
+ SECTION("from non-contiguous iterator pair") {
+ auto s = std::list{'t', 'e', 's', 't', 'i', 'n', 'g'};
+ auto str = make_string(s.begin(), s.end()).value();
+ REQUIRE(str.type() == object_type::string);
+ REQUIRE(str == "testing");
+ }
}
TEST_CASE("ucl: string: swap", "[ucl]")
@@ -101,7 +232,9 @@ TEST_CASE("ucl: string: swap", "[ucl]")
TEST_CASE("ucl: string: value()", "[ucl]")
{
- auto s = nihil::ucl::string("te\"st");
+ using namespace nihil::ucl;
+
+ auto s = string("te\"st");
REQUIRE(s.value() == "te\"st");
}
@@ -115,7 +248,7 @@ TEST_CASE("ucl: string: key()", "[ucl]")
auto obj = *err;
REQUIRE(object_cast<string>(obj["a_string"])->key() == "a_string");
- auto s = nihil::ucl::string("test");
+ auto s = string("test");
REQUIRE(s.key() == "");
}
@@ -135,86 +268,148 @@ TEST_CASE("ucl: string: empty", "[ucl]")
REQUIRE(string("test").empty() == false);
}
-TEST_CASE("ucl: string: iterator", "[ucl]")
+TEST_CASE("ucl: string: iterate", "[ucl]")
{
- auto str = nihil::ucl::string("test");
-
- auto begin = std::ranges::begin(str);
- static_assert(std::contiguous_iterator<decltype(begin)>);
+ using namespace nihil::ucl;
- auto end = std::ranges::end(str);
- static_assert(std::sentinel_for<decltype(end), decltype(begin)>);
+ auto str = "test"_ucl;
- REQUIRE(*begin == 't');
- ++begin;
- REQUIRE(*begin == 'e');
- ++begin;
- REQUIRE(*begin == 's');
- ++begin;
- REQUIRE(*begin == 't');
- ++begin;
+ SECTION("as iterator pair") {
+ auto begin = str.begin();
+ static_assert(std::contiguous_iterator<decltype(begin)>);
- REQUIRE(begin == end);
-}
+ auto end = str.end();
+ static_assert(std::sentinel_for<decltype(end),
+ decltype(begin)>);
-TEST_CASE("ucl: string: operator==", "[ucl]")
-{
- auto str = nihil::ucl::string("testing");
+ REQUIRE(*begin == 't');
+ ++begin;
+ REQUIRE(*begin == 'e');
+ ++begin;
+ REQUIRE(*begin == 's');
+ ++begin;
+ REQUIRE(*begin == 't');
+ ++begin;
- REQUIRE(str == nihil::ucl::string("testing"));
- REQUIRE(str == std::string_view("testing"));
- REQUIRE(str == std::string("testing"));
- REQUIRE(str == "testing");
+ REQUIRE(begin == end);
+ }
- REQUIRE(str != nihil::ucl::string("test"));
- REQUIRE(str != std::string_view("test"));
- REQUIRE(str != std::string("test"));
- REQUIRE(str != "test");
+ SECTION("as range") {
+ auto s = std::string(std::from_range, str);
+ REQUIRE(s == "test");
+ }
}
-TEST_CASE("ucl: string: operator<=>", "[ucl]")
+TEST_CASE("ucl: string: comparison", "[ucl]")
{
- auto str = nihil::ucl::string("testing");
-
- REQUIRE(str < nihil::ucl::string("zzz"));
- REQUIRE(str < std::string_view("zzz"));
- REQUIRE(str < std::string("zzz"));
- REQUIRE(str < "zzz");
+ using namespace nihil::ucl;
- REQUIRE(str > nihil::ucl::string("aaa"));
- REQUIRE(str > std::string_view("aaa"));
- REQUIRE(str > std::string("aaa"));
- REQUIRE(str > "aaa");
+ auto str = "testing"_ucl;
+
+ SECTION("operator==") {
+ REQUIRE(str == "testing"_ucl);
+ REQUIRE(str == std::string_view("testing"));
+ REQUIRE(str == std::string("testing"));
+ REQUIRE(str == "testing");
+ }
+
+ SECTION("operator!=") {
+ REQUIRE(str != "test"_ucl);
+ REQUIRE(str != std::string_view("test"));
+ REQUIRE(str != std::string("test"));
+ REQUIRE(str != "test");
+ }
+
+ SECTION("operator<") {
+ REQUIRE(str < "zzz"_ucl);
+ REQUIRE(str < std::string_view("zzz"));
+ REQUIRE(str < std::string("zzz"));
+ REQUIRE(str < "zzz");
+ }
+
+ SECTION("operator>") {
+ REQUIRE(str > "aaa"_ucl);
+ REQUIRE(str > std::string_view("aaa"));
+ REQUIRE(str > std::string("aaa"));
+ REQUIRE(str > "aaa");
+ }
}
TEST_CASE("ucl: string: parse", "[ucl]")
{
- using namespace std::literals;
+ using namespace nihil::ucl;
- auto err = nihil::ucl::parse("value = \"te\\\"st\""sv);
- REQUIRE(err);
+ auto obj = parse("value = \"te\\\"st\"").value();
- auto obj = *err;
auto v = obj["value"];
REQUIRE(v.key() == "value");
- REQUIRE(object_cast<nihil::ucl::string>(v) == "te\"st");
+ REQUIRE(object_cast<nihil::ucl::string>(v).value() == "te\"st");
}
TEST_CASE("ucl: string: emit", "[ucl]")
{
- auto s = nihil::ucl::string("te\"st");
- auto str = std::format("{}", s);
- REQUIRE(str == "\"te\\\"st\"");
-}
+ using namespace nihil::ucl;
-TEST_CASE("ucl: string: parse and emit", "[ucl]")
-{
- auto ucl = nihil::ucl::parse("str = \"te\\\"st\";");
- REQUIRE(ucl);
+ auto ucl = parse("str = \"te\\\"st\";").value();
auto output = std::string();
- emit(*ucl, nihil::ucl::emitter::configuration,
- std::back_inserter(output));
+ emit(ucl, emitter::configuration, std::back_inserter(output));
REQUIRE(output == "str = \"te\\\"st\";\n");
}
+
+TEST_CASE("ucl: string: format", "[ucl]")
+{
+ using namespace nihil::ucl;
+ using namespace std::literals;
+
+ auto constexpr test_string = "te\"st"sv;
+
+ SECTION("bare string") {
+ auto str = std::format("{}", string(test_string));
+ REQUIRE(str == test_string);
+ }
+
+ SECTION("parsed string") {
+ auto obj = parse("string = \"te\\\"st\";").value();
+ auto s = object_cast<string>(obj["string"]).value();
+
+ auto str = std::format("{}", s);
+ REQUIRE(str == test_string);
+ }
+
+ SECTION("with format string") {
+ auto str = std::format("{: >10}", string(test_string));
+ REQUIRE(str == " te\"st");
+ }
+}
+
+TEST_CASE("ucl: string: print to ostream", "[ucl]")
+{
+ using namespace nihil::ucl;
+ using namespace std::literals;
+
+ auto constexpr test_string = "te\"st"sv;
+
+ SECTION("bare string") {
+ auto strm = std::ostringstream();
+ strm << string(test_string);
+
+ REQUIRE(strm.str() == test_string);
+ }
+
+ SECTION("parsed string") {
+ auto obj = parse("string = \"te\\\"st\";").value();
+ auto s = object_cast<string>(obj["string"]).value();
+
+ auto strm = std::ostringstream();
+ strm << s;
+
+ REQUIRE(strm.str() == test_string);
+ }
+
+ SECTION("with format string") {
+ auto str = std::format("{: >10}", string(test_string));
+ REQUIRE(str == " te\"st");
+ }
+}