// __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) // | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // // SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT // cmake/test.cmake builds a unit test with C++ standards beyond C++11 only if the // source file mentions the corresponding version macro. To avoid rebuilding this // large file for every standard, tests that depend on the standard version (e.g., // those using JSON_HAS_FILESYSTEM, JSON_HAS_RANGES, or JSON_HAS_THREE_WAY_COMPARISON) // go into a separate file unit-conversions2-cpp.cpp. This file stays C++11-only. #include "doctest_compatibility.h" // skip tests if JSON_DisableEnumSerialization=ON (#4384) #if defined(JSON_DISABLE_ENUM_SERIALIZATION) && (JSON_DISABLE_ENUM_SERIALIZATION == 1) #define SKIP_TESTS_FOR_ENUM_SERIALIZATION #endif #define JSON_TESTS_PRIVATE #include using nlohmann::json; #include #include #include #include #include #include #include // NLOHMANN_JSON_SERIALIZE_ENUM uses a static std::pair DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors") TEST_CASE("value conversion") { SECTION("get a binary value (explicit)") { json::binary_t const n_reference{{1, 2, 3}}; json j(n_reference); SECTION("binary_t") { json::binary_t const b = j.get(); CHECK(*json(b).m_data.m_value.binary == *j.m_data.m_value.binary); } SECTION("get_binary()") { SECTION("non-const") { auto& b = j.get_binary(); CHECK(*json(b).m_data.m_value.binary == *j.m_data.m_value.binary); } SECTION("non-const") { const json j_const = j; // NOLINT(performance-unnecessary-copy-initialization) const auto& b = j_const.get_binary(); CHECK(*json(b).m_data.m_value.binary == *j.m_data.m_value.binary); } } SECTION("exception in case of a non-string type") { json j_null(json::value_t::null); json j_object(json::value_t::object); json j_array(json::value_t::array); json j_string(json::value_t::string); json j_boolean(json::value_t::boolean); const json j_null_const(json::value_t::null); const json j_object_const(json::value_t::object); const json j_array_const(json::value_t::array); const json j_string_const(json::value_t::string); const json j_boolean_const(json::value_t::boolean); CHECK_THROWS_WITH_AS(j_null.get(), "[json.exception.type_error.302] type must be binary, but is null", json::type_error&); CHECK_THROWS_WITH_AS(j_object.get(), "[json.exception.type_error.302] type must be binary, but is object", json::type_error&); CHECK_THROWS_WITH_AS(j_array.get(), "[json.exception.type_error.302] type must be binary, but is array", json::type_error&); CHECK_THROWS_WITH_AS(j_string.get(), "[json.exception.type_error.302] type must be binary, but is string", json::type_error&); CHECK_THROWS_WITH_AS(j_boolean.get(), "[json.exception.type_error.302] type must be binary, but is boolean", json::type_error&); CHECK_THROWS_WITH_AS(j_null_const.get(), "[json.exception.type_error.302] type must be binary, but is null", json::type_error&); CHECK_THROWS_WITH_AS(j_object_const.get(), "[json.exception.type_error.302] type must be binary, but is object", json::type_error&); CHECK_THROWS_WITH_AS(j_array_const.get(), "[json.exception.type_error.302] type must be binary, but is array", json::type_error&); CHECK_THROWS_WITH_AS(j_string_const.get(), "[json.exception.type_error.302] type must be binary, but is string", json::type_error&); CHECK_THROWS_WITH_AS(j_boolean_const.get(), "[json.exception.type_error.302] type must be binary, but is boolean", json::type_error&); CHECK_THROWS_WITH_AS(j_null.get_binary(), "[json.exception.type_error.302] type must be binary, but is null", json::type_error&); CHECK_THROWS_WITH_AS(j_object.get_binary(), "[json.exception.type_error.302] type must be binary, but is object", json::type_error&); CHECK_THROWS_WITH_AS(j_array.get_binary(), "[json.exception.type_error.302] type must be binary, but is array", json::type_error&); CHECK_THROWS_WITH_AS(j_string.get_binary(), "[json.exception.type_error.302] type must be binary, but is string", json::type_error&); CHECK_THROWS_WITH_AS(j_boolean.get_binary(), "[json.exception.type_error.302] type must be binary, but is boolean", json::type_error&); CHECK_THROWS_WITH_AS(j_null_const.get_binary(), "[json.exception.type_error.302] type must be binary, but is null", json::type_error&); CHECK_THROWS_WITH_AS(j_object_const.get_binary(), "[json.exception.type_error.302] type must be binary, but is object", json::type_error&); CHECK_THROWS_WITH_AS(j_array_const.get_binary(), "[json.exception.type_error.302] type must be binary, but is array", json::type_error&); CHECK_THROWS_WITH_AS(j_string_const.get_binary(), "[json.exception.type_error.302] type must be binary, but is string", json::type_error&); CHECK_THROWS_WITH_AS(j_boolean_const.get_binary(), "[json.exception.type_error.302] type must be binary, but is boolean", json::type_error&); } } #if JSON_USE_IMPLICIT_CONVERSIONS SECTION("get a binary value (implicit)") { json::binary_t const n_reference{{1, 2, 3}}; json const j(n_reference); SECTION("binary_t") { json::binary_t const b = j; CHECK(*json(b).m_data.m_value.binary == *j.m_data.m_value.binary); } } #endif #ifndef SKIP_TESTS_FOR_ENUM_SERIALIZATION SECTION("get an enum") { enum c_enum { value_1, value_2 }; // NOLINT(cppcoreguidelines-use-enum-class) enum class cpp_enum { value_1, value_2 }; CHECK(json(value_1).get() == value_1); CHECK(json(cpp_enum::value_1).get() == cpp_enum::value_1); } SECTION("get an enum with underlying type bool (#5671)") { enum class bool_enum : bool { off, on }; // the extra parentheses keep doctest from printing the enum via its // underlying type, which MSVC 2015 reports as C4800 CHECK((json(bool_enum::off).get() == bool_enum::off)); CHECK((json(bool_enum::on).get() == bool_enum::on)); } #endif SECTION("more involved conversions") { SECTION("object-like STL containers") { json const j1 = {{"one", 1}, {"two", 2}, {"three", 3}}; json const j2 = {{"one", 1u}, {"two", 2u}, {"three", 3u}}; json const j3 = {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}}; json const j4 = {{"one", true}, {"two", false}, {"three", true}}; json const j5 = {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}}; SECTION("std::map") { CHECK(j1.get>() == (std::map {{"one", 1}, {"two", 2}, {"three", 3}})); CHECK(j2.get>() == (std::map {{"one", 1u}, {"two", 2u}, {"three", 3u}})); CHECK(j3.get>() == (std::map {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}})); CHECK(j4.get>() == (std::map {{"one", true}, {"two", false}, {"three", true}})); CHECK(j5.get>() == (std::map {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}})); } SECTION("std::unordered_map") { CHECK(j1.get>() == (std::unordered_map {{"one", 1}, {"two", 2}, {"three", 3}})); CHECK(j2.get>() == (std::unordered_map {{"one", 1u}, {"two", 2u}, {"three", 3u}})); CHECK(j3.get>() == (std::unordered_map {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}})); CHECK(j4.get>() == (std::unordered_map {{"one", true}, {"two", false}, {"three", true}})); const auto m5 = j5.get>(); CHECK(m5 == (std::unordered_map {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}})); CHECK(m5.at("one") == "eins"); } SECTION("reserve is called on containers that support it (#5406)") { // build a larger object so that a missing/incorrect reserve() // call would be more likely to corrupt or drop elements json j_large; for (int i = 0; i < 100; ++i) { j_large[std::to_string(i)] = i; } SECTION("std::unordered_map (supports reserve)") { const auto m = j_large.get>(); CHECK(m.size() == 100); for (int i = 0; i < 100; ++i) { CHECK(m.at(std::to_string(i)) == i); } } SECTION("std::map (no reserve, fallback path)") { const auto m = j_large.get>(); CHECK(m.size() == 100); for (int i = 0; i < 100; ++i) { CHECK(m.at(std::to_string(i)) == i); } } } SECTION("std::multimap") { CHECK(j1.get>() == (std::multimap {{"one", 1}, {"two", 2}, {"three", 3}})); CHECK(j2.get>() == (std::multimap {{"one", 1u}, {"two", 2u}, {"three", 3u}})); CHECK(j3.get>() == (std::multimap {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}})); CHECK(j4.get>() == (std::multimap {{"one", true}, {"two", false}, {"three", true}})); const auto m5 = j5.get>(); CHECK(m5 == (std::multimap {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}})); CHECK(m5.find("one")->second == "eins"); } SECTION("std::unordered_multimap") { CHECK(j1.get>() == (std::unordered_multimap {{"one", 1}, {"two", 2}, {"three", 3}})); CHECK(j2.get>() == (std::unordered_multimap {{"one", 1u}, {"two", 2u}, {"three", 3u}})); CHECK(j3.get>() == (std::unordered_multimap {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}})); CHECK(j4.get>() == (std::unordered_multimap {{"one", true}, {"two", false}, {"three", true}})); const auto m5 = j5.get>(); CHECK(m5 == (std::unordered_multimap {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}})); CHECK(m5.find("one")->second == "eins"); } SECTION("exception in case of a non-object type") { CHECK_THROWS_WITH_AS( (json().get>()), "[json.exception.type_error.302] type must be object, but is null", json::type_error&); } } SECTION("array-like STL containers") { json const j1 = {1, 2, 3, 4}; json const j2 = {1u, 2u, 3u, 4u}; json const j3 = {1.2, 2.3, 3.4, 4.5}; json const j4 = {true, false, true}; json const j5 = {"one", "two", "three"}; SECTION("std::list") { CHECK(j1.get>() == (std::list {1, 2, 3, 4})); CHECK(j2.get>() == (std::list {1u, 2u, 3u, 4u})); CHECK(j3.get>() == (std::list {1.2, 2.3, 3.4, 4.5})); CHECK(j4.get>() == (std::list {true, false, true})); CHECK(j5.get>() == (std::list {"one", "two", "three"})); } SECTION("std::forward_list") { CHECK(j1.get>() == (std::forward_list {1, 2, 3, 4})); CHECK(j2.get>() == (std::forward_list {1u, 2u, 3u, 4u})); CHECK(j3.get>() == (std::forward_list {1.2, 2.3, 3.4, 4.5})); CHECK(j4.get>() == (std::forward_list {true, false, true})); CHECK(j5.get>() == (std::forward_list {"one", "two", "three"})); } SECTION("std::array") { CHECK(j1.get>() == (std::array {{1, 2, 3, 4}})); // only the first 3 elements of j2 are converted, since the target array is smaller CHECK(j2.get>() == (std::array {{1u, 2u, 3u}})); CHECK(j3.get>() == (std::array {{1.2, 2.3, 3.4, 4.5}})); CHECK(j4.get>() == (std::array {{true, false, true}})); CHECK(j5.get>() == (std::array {{"one", "two", "three"}})); SECTION("std::array is larger than JSON") { std::array arr6 = {{1, 2, 3, 4, 5, 6}}; CHECK_THROWS_WITH_AS(j1.get_to(arr6), "[json.exception.out_of_range.401] " "array index 4 is out of range", json::out_of_range&); } SECTION("std::array is smaller than JSON") { std::array arr2 = {{8, 9}}; j1.get_to(arr2); CHECK(arr2[0] == 1); CHECK(arr2[1] == 2); } } SECTION("std::valarray") { // valarray has no operator== that returns bool, so compare via a vector copy const auto v1 = j1.get>(); CHECK((std::vector(std::begin(v1), std::end(v1)) == std::vector {1, 2, 3, 4})); const auto v2 = j2.get>(); CHECK((std::vector(std::begin(v2), std::end(v2)) == std::vector {1u, 2u, 3u, 4u})); const auto v3 = j3.get>(); CHECK((std::vector(std::begin(v3), std::end(v3)) == std::vector {1.2, 2.3, 3.4, 4.5})); const auto v4 = j4.get>(); CHECK((std::vector(std::begin(v4), std::end(v4)) == std::vector {true, false, true})); const auto v5 = j5.get>(); CHECK((std::vector(std::begin(v5), std::end(v5)) == std::vector {"one", "two", "three"})); } SECTION("std::vector") { CHECK(j1.get>() == (std::vector {1, 2, 3, 4})); CHECK(j2.get>() == (std::vector {1u, 2u, 3u, 4u})); CHECK(j3.get>() == (std::vector {1.2, 2.3, 3.4, 4.5})); CHECK(j4.get>() == (std::vector {true, false, true})); CHECK(j5.get>() == (std::vector {"one", "two", "three"})); } SECTION("std::deque") { CHECK(j1.get>() == (std::deque {1, 2, 3, 4})); CHECK(j2.get>() == (std::deque {1u, 2u, 3u, 4u})); CHECK(j3.get>() == (std::deque {1.2, 2.3, 3.4, 4.5})); CHECK(j4.get>() == (std::deque {true, false, true})); CHECK(j5.get>() == (std::deque {"one", "two", "three"})); } SECTION("std::set") { CHECK(j1.get>() == (std::set {1, 2, 3, 4})); CHECK(j2.get>() == (std::set {1u, 2u, 3u, 4u})); CHECK(j3.get>() == (std::set {1.2, 2.3, 3.4, 4.5})); CHECK(j4.get>() == (std::set {true, false, true})); CHECK(j5.get>() == (std::set {"one", "two", "three"})); } SECTION("std::unordered_set") { CHECK(j1.get>() == (std::unordered_set {1, 2, 3, 4})); CHECK(j2.get>() == (std::unordered_set {1u, 2u, 3u, 4u})); CHECK(j3.get>() == (std::unordered_set {1.2, 2.3, 3.4, 4.5})); CHECK(j4.get>() == (std::unordered_set {true, false, true})); CHECK(j5.get>() == (std::unordered_set {"one", "two", "three"})); } SECTION("std::map (array of pairs)") { const std::map m{{0, 1}, {1, 2}, {2, 3}}; json const j6 = m; auto m2 = j6.get>(); CHECK(m == m2); json const j7 = {0, 1, 2, 3}; json const j8 = 2; #if JSON_DIAGNOSTICS CHECK_THROWS_WITH_AS((j7.get>()), "[json.exception.type_error.302] (/0) type must be array, " "but is number", json::type_error&); #else CHECK_THROWS_WITH_AS((j7.get>()), "[json.exception.type_error.302] type must be array, " "but is number", json::type_error&); #endif CHECK_THROWS_WITH_AS((j8.get>()), "[json.exception.type_error.302] type must be array, " "but is number", json::type_error&); SECTION("superfluous entries") { json const j9 = {{0, 1, 2}, {1, 2, 3}, {2, 3, 4}}; m2 = j9.get>(); CHECK(m == m2); } } SECTION("std::unordered_map (array of pairs)") { const std::unordered_map m{{0, 1}, {1, 2}, {2, 3}}; json const j6 = m; auto m2 = j6.get>(); CHECK(m == m2); json const j7 = {0, 1, 2, 3}; json const j8 = 2; #if JSON_DIAGNOSTICS CHECK_THROWS_WITH_AS((j7.get>()), "[json.exception.type_error.302] (/0) type must be array, " "but is number", json::type_error&); #else CHECK_THROWS_WITH_AS((j7.get>()), "[json.exception.type_error.302] type must be array, " "but is number", json::type_error&); #endif CHECK_THROWS_WITH_AS((j8.get>()), "[json.exception.type_error.302] type must be array, " "but is number", json::type_error&); SECTION("superfluous entries") { json const j9{{0, 1, 2}, {1, 2, 3}, {2, 3, 4}}; m2 = j9.get>(); CHECK(m == m2); } } SECTION("exception in case of a non-object type") { // does type really must be an array? or it rather must not be null? // that's what I thought when other test like this one broke CHECK_THROWS_WITH_AS( (json().get>()), "[json.exception.type_error.302] type must be array, but is null", json::type_error&); CHECK_THROWS_WITH_AS( (json().get>()), "[json.exception.type_error.302] type must be array, but is null", json::type_error&); CHECK_THROWS_WITH_AS( (json().get>()), "[json.exception.type_error.302] type must be array, but is null", json::type_error&); CHECK_THROWS_WITH_AS( (json().get>()), "[json.exception.type_error.302] type must be array, but is null", json::type_error&); CHECK_THROWS_WITH_AS( (json().get>()), "[json.exception.type_error.302] type must be array, but is null", json::type_error&); CHECK_THROWS_WITH_AS( (json().get>()), "[json.exception.type_error.302] type must be array, but is null", json::type_error&); } } } } enum class cards {kreuz, pik, herz, karo}; // NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness) - false positive NLOHMANN_JSON_SERIALIZE_ENUM(cards, { {cards::kreuz, "kreuz"}, {cards::pik, "pik"}, {cards::pik, "puk"}, // second entry for cards::puk; will not be used {cards::herz, "herz"}, {cards::karo, "karo"} }) enum TaskState // NOLINT(cert-int09-c,readability-enum-initial-value,cppcoreguidelines-use-enum-class) { TS_STOPPED, TS_RUNNING, TS_COMPLETED, TS_INVALID = -1, }; // NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage) - false positive NLOHMANN_JSON_SERIALIZE_ENUM(TaskState, { {TS_INVALID, nullptr}, {TS_STOPPED, "stopped"}, {TS_RUNNING, "running"}, {TS_COMPLETED, "completed"}, }) TEST_CASE("JSON to enum mapping") { SECTION("enum class") { // enum -> json CHECK(json(cards::kreuz) == "kreuz"); CHECK(json(cards::pik) == "pik"); CHECK(json(cards::herz) == "herz"); CHECK(json(cards::karo) == "karo"); // json -> enum CHECK(cards::kreuz == json("kreuz")); CHECK(cards::pik == json("pik")); CHECK(cards::herz == json("herz")); CHECK(cards::karo == json("karo")); // invalid json -> first enum CHECK(cards::kreuz == json("what?").get()); } SECTION("traditional enum") { // enum -> json CHECK(json(TS_STOPPED) == "stopped"); CHECK(json(TS_RUNNING) == "running"); CHECK(json(TS_COMPLETED) == "completed"); CHECK(json(TS_INVALID) == json()); // json -> enum CHECK(TS_STOPPED == json("stopped")); CHECK(TS_RUNNING == json("running")); CHECK(TS_COMPLETED == json("completed")); CHECK(TS_INVALID == json()); // invalid json -> first enum CHECK(TS_INVALID == json("what?").get()); } } enum class strict_cards {kreuz, pik, herz, karo, andere}; // andere not included in mapping // NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness) - false positive NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(strict_cards, { {strict_cards::kreuz, "kreuz"}, {strict_cards::pik, "pik"}, {strict_cards::pik, "puk"}, // second entry for cards::pik; will not be used {strict_cards::herz, "herz"}, {strict_cards::karo, "karo"} }) enum StrictTaskState // NOLINT(cert-int09-c,readability-enum-initial-value,cppcoreguidelines-use-enum-class) { STRICT_TS_STOPPED, STRICT_TS_RUNNING, STRICT_TS_COMPLETED, STRICT_TS_OTHER, // STRICT_TS_OTHER not in mapping STRICT_TS_INVALID = -1, }; // NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage) - false positive NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(StrictTaskState, { {STRICT_TS_INVALID, nullptr}, {STRICT_TS_STOPPED, "stopped"}, {STRICT_TS_RUNNING, "running"}, {STRICT_TS_COMPLETED, "completed"}, }) // regression test for #5708 item 2: NLOHMANN_JSON_SERIALIZE_ENUM_STRICT must not rely on // unqualified lookup of a helper name that a user's own namespace may also declare namespace ns_with_colliding_name { // NOLINTNEXTLINE(misc-use-internal-linkage) - used to shadow the library's internal helper name inline void templated_json_throw(int /*unused*/) {} enum class colliding_enum { a, b }; // NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(colliding_enum, { {colliding_enum::a, "a"}, {colliding_enum::b, "b"} }) } // namespace ns_with_colliding_name TEST_CASE("NLOHMANN_JSON_SERIALIZE_ENUM_STRICT in a namespace with a colliding name") { using ns_with_colliding_name::colliding_enum; CHECK(json(colliding_enum::a) == "a"); CHECK(colliding_enum::b == json("b")); json _; CHECK_THROWS_WITH_AS(_ = json("nope").get(), "[json.exception.out_of_range.410] enum value out of range for colliding_enum: \"nope\"", json::out_of_range&); } TEST_CASE("Strict JSON to enum mapping") { SECTION("enum class") { // enum -> json CHECK(json(strict_cards::kreuz) == "kreuz"); CHECK(json(strict_cards::pik) == "pik"); CHECK(json(strict_cards::herz) == "herz"); CHECK(json(strict_cards::karo) == "karo"); // json -> enum CHECK(json("kreuz").get() == strict_cards::kreuz); CHECK(json("pik").get() == strict_cards::pik); CHECK(json("herz").get() == strict_cards::herz); CHECK(json("karo").get() == strict_cards::karo); // comparison of enum and json CHECK(strict_cards::kreuz == json("kreuz")); CHECK(strict_cards::pik == json("pik")); CHECK(strict_cards::herz == json("herz")); CHECK(strict_cards::karo == json("karo")); // invalid json -> exception thrown json _; CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.410] enum value out of range for strict_cards: \"what?\"", json::out_of_range&); // conversion of unmapped enum -> exception thrown CHECK_THROWS_WITH_AS(json(strict_cards::andere), "[json.exception.out_of_range.410] enum value out of range for strict_cards", json::out_of_range&); // comparing an unmapped enum with json throws the same exception // (the scalar comparison operators used to be noexcept, so this // called std::terminate) CHECK_THROWS_WITH_AS(static_cast(strict_cards::andere == json("andere")), "[json.exception.out_of_range.410] enum value out of range for strict_cards", json::out_of_range&); CHECK_THROWS_WITH_AS(static_cast(json("andere") != strict_cards::andere), "[json.exception.out_of_range.410] enum value out of range for strict_cards", json::out_of_range&); // invalid UTF-8 -> out_of_range.410, not the type_error.316 thrown while building the // message (regression test for #5667); such strings can reach get() unvalidated, // e.g. from from_cbor()/from_msgpack() (#5529) const json j_invalid_utf8 = "\xFF"; CHECK_THROWS_WITH_AS(_ = j_invalid_utf8.get(), "[json.exception.out_of_range.410] enum value out of range for strict_cards: \"\xEF\xBF\xBD\"", json::out_of_range&); } SECTION("traditional enum") { // enum -> json CHECK(json(STRICT_TS_STOPPED) == "stopped"); CHECK(json(STRICT_TS_RUNNING) == "running"); CHECK(json(STRICT_TS_COMPLETED) == "completed"); CHECK(json(STRICT_TS_INVALID) == json()); // json -> enum CHECK(json("stopped").get() == STRICT_TS_STOPPED); CHECK(json("running").get() == STRICT_TS_RUNNING); CHECK(json("completed").get() == STRICT_TS_COMPLETED); CHECK(json().get() == STRICT_TS_INVALID); // comparison of enum and json CHECK(STRICT_TS_STOPPED == json("stopped")); CHECK(STRICT_TS_RUNNING == json("running")); CHECK(STRICT_TS_COMPLETED == json("completed")); CHECK(STRICT_TS_INVALID == json()); // invalid json -> exception thrown json _; CHECK_THROWS_WITH_AS(_ = json("what?").get(), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState: \"what?\"", json::out_of_range&); // conversion of unmapped enum -> exception thrown CHECK_THROWS_WITH_AS(json(STRICT_TS_OTHER), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState", json::out_of_range&); // comparing an unmapped enum with json throws the same exception CHECK_THROWS_WITH_AS(static_cast(STRICT_TS_OTHER < json("x")), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState", json::out_of_range&); } } DOCTEST_CLANG_SUPPRESS_WARNING_POP