diff options
| author | Lexi Winter <lexi@le-fay.org> | 2025-06-27 12:08:58 +0100 |
|---|---|---|
| committer | Lexi Winter <lexi@le-fay.org> | 2025-06-27 12:08:58 +0100 |
| commit | 001c9917ace09f7b1c80d96eb067e1d37e86c546 (patch) | |
| tree | 89e360961b9659a8c6b897c5412b7d6834b8eed9 /nihil.ucl/tests | |
| parent | 90aa957ca9b7c217af7569009d1675e0f3ff8e9b (diff) | |
| download | nihil-001c9917ace09f7b1c80d96eb067e1d37e86c546.tar.gz nihil-001c9917ace09f7b1c80d96eb067e1d37e86c546.tar.bz2 | |
improve error handling
Diffstat (limited to 'nihil.ucl/tests')
| -rw-r--r-- | nihil.ucl/tests/array.cc | 251 | ||||
| -rw-r--r-- | nihil.ucl/tests/boolean.cc | 168 | ||||
| -rw-r--r-- | nihil.ucl/tests/integer.cc | 208 | ||||
| -rw-r--r-- | nihil.ucl/tests/real.cc | 204 | ||||
| -rw-r--r-- | nihil.ucl/tests/string.cc | 383 |
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"); + } +} |
