/* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) | | |__ | | | | | | version 2.0.1 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . Copyright (c) 2013-2016 Niels Lohmann . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define CATCH_CONFIG_MAIN #include "catch.hpp" #include #include #include #include #include #include #include #include #include #include #define private public #include "json.hpp" using nlohmann::json; // disable float-equal warnings on GCC/clang #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) #pragma GCC diagnostic ignored "-Wfloat-equal" #endif TEST_CASE("constructors") { SECTION("create an empty value with a given type") { SECTION("null") { auto t = json::value_t::null; json j(t); CHECK(j.type() == t); } SECTION("discarded") { auto t = json::value_t::discarded; json j(t); CHECK(j.type() == t); } SECTION("object") { auto t = json::value_t::object; json j(t); CHECK(j.type() == t); } SECTION("array") { auto t = json::value_t::array; json j(t); CHECK(j.type() == t); } SECTION("boolean") { auto t = json::value_t::boolean; json j(t); CHECK(j.type() == t); } SECTION("string") { auto t = json::value_t::string; json j(t); CHECK(j.type() == t); } SECTION("number_integer") { auto t = json::value_t::number_integer; json j(t); CHECK(j.type() == t); } SECTION("number_unsigned") { auto t = json::value_t::number_unsigned; json j(t); CHECK(j.type() == t); } SECTION("number_float") { auto t = json::value_t::number_float; json j(t); CHECK(j.type() == t); } } SECTION("create a null object (implicitly)") { SECTION("no parameter") { json j{}; CHECK(j.type() == json::value_t::null); } } SECTION("create a null object (explicitly)") { SECTION("parameter") { json j(nullptr); CHECK(j.type() == json::value_t::null); } } SECTION("create an object (explicit)") { SECTION("empty object") { json::object_t o; json j(o); CHECK(j.type() == json::value_t::object); } SECTION("filled object") { json::object_t o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; json j(o); CHECK(j.type() == json::value_t::object); } } SECTION("create an object (implicit)") { // reference object json::object_t o_reference {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; json j_reference(o_reference); SECTION("std::map") { std::map o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; json j(o); CHECK(j.type() == json::value_t::object); CHECK(j == j_reference); } SECTION("std::map") { std::map o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; json j(o); CHECK(j.type() == json::value_t::object); CHECK(j == j_reference); } SECTION("std::multimap") { std::multimap o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; json j(o); CHECK(j.type() == json::value_t::object); CHECK(j == j_reference); } SECTION("std::unordered_map") { std::unordered_map o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; json j(o); CHECK(j.type() == json::value_t::object); CHECK(j == j_reference); } SECTION("std::unordered_multimap") { std::unordered_multimap o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}; json j(o); CHECK(j.type() == json::value_t::object); CHECK(j == j_reference); } SECTION("associative container literal") { json j({{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}}); CHECK(j.type() == json::value_t::object); CHECK(j == j_reference); } } SECTION("create an array (explicit)") { SECTION("empty array") { json::array_t a; json j(a); CHECK(j.type() == json::value_t::array); } SECTION("filled array") { json::array_t a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; json j(a); CHECK(j.type() == json::value_t::array); } } SECTION("create an array (implicit)") { // reference array json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()}; json j_reference(a_reference); SECTION("std::list") { std::list a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; json j(a); CHECK(j.type() == json::value_t::array); CHECK(j == j_reference); } SECTION("std::forward_list") { std::forward_list a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; json j(a); CHECK(j.type() == json::value_t::array); CHECK(j == j_reference); } SECTION("std::array") { std::array a {{json(1), json(1u), json(2.2), json(false), json("string"), json()}}; json j(a); CHECK(j.type() == json::value_t::array); CHECK(j == j_reference); } SECTION("std::vector") { std::vector a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; json j(a); CHECK(j.type() == json::value_t::array); CHECK(j == j_reference); } SECTION("std::deque") { std::deque a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; json j(a); CHECK(j.type() == json::value_t::array); CHECK(j == j_reference); } SECTION("std::set") { std::set a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; json j(a); CHECK(j.type() == json::value_t::array); // we cannot really check for equality here } SECTION("std::unordered_set") { std::unordered_set a {json(1), json(1u), json(2.2), json(false), json("string"), json()}; json j(a); CHECK(j.type() == json::value_t::array); // we cannot really check for equality here } SECTION("sequence container literal") { json j({json(1), json(1u), json(2.2), json(false), json("string"), json()}); CHECK(j.type() == json::value_t::array); CHECK(j == j_reference); } } SECTION("create a string (explicit)") { SECTION("empty string") { json::string_t s; json j(s); CHECK(j.type() == json::value_t::string); } SECTION("filled string") { json::string_t s {"Hello world"}; json j(s); CHECK(j.type() == json::value_t::string); } } SECTION("create a string (implicit)") { // reference string json::string_t s_reference {"Hello world"}; json j_reference(s_reference); SECTION("std::string") { std::string s {"Hello world"}; json j(s); CHECK(j.type() == json::value_t::string); CHECK(j == j_reference); } SECTION("char[]") { char s[] {"Hello world"}; json j(s); CHECK(j.type() == json::value_t::string); CHECK(j == j_reference); } SECTION("const char*") { const char* s {"Hello world"}; json j(s); CHECK(j.type() == json::value_t::string); CHECK(j == j_reference); } SECTION("string literal") { json j("Hello world"); CHECK(j.type() == json::value_t::string); CHECK(j == j_reference); } } SECTION("create a boolean (explicit)") { SECTION("empty boolean") { json::boolean_t b{}; json j(b); CHECK(j.type() == json::value_t::boolean); } SECTION("filled boolean (true)") { json j(true); CHECK(j.type() == json::value_t::boolean); } SECTION("filled boolean (false)") { json j(false); CHECK(j.type() == json::value_t::boolean); } } SECTION("create an integer number (explicit)") { SECTION("uninitialized value") { json::number_integer_t n{}; json j(n); CHECK(j.type() == json::value_t::number_integer); } SECTION("initialized value") { json::number_integer_t n(42); json j(n); CHECK(j.type() == json::value_t::number_integer); } } SECTION("create an integer number (implicit)") { // reference objects json::number_integer_t n_reference = 42; json j_reference(n_reference); json::number_unsigned_t n_unsigned_reference = 42; json j_unsigned_reference(n_unsigned_reference); SECTION("short") { short n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("unsigned short") { unsigned short n = 42; json j(n); CHECK(j.type() == json::value_t::number_unsigned); CHECK(j == j_unsigned_reference); } SECTION("int") { int n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("unsigned int") { unsigned int n = 42; json j(n); CHECK(j.type() == json::value_t::number_unsigned); CHECK(j == j_unsigned_reference); } SECTION("long") { long n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("unsigned long") { unsigned long n = 42; json j(n); CHECK(j.type() == json::value_t::number_unsigned); CHECK(j == j_unsigned_reference); } SECTION("long long") { long long n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("unsigned long long") { unsigned long long n = 42; json j(n); CHECK(j.type() == json::value_t::number_unsigned); CHECK(j == j_unsigned_reference); } SECTION("int8_t") { int8_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("int16_t") { int16_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("int32_t") { int32_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("int64_t") { int64_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("int_fast8_t") { int_fast8_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("int_fast16_t") { int_fast16_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("int_fast32_t") { int_fast32_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("int_fast64_t") { int_fast64_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("int_least8_t") { int_least8_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("int_least16_t") { int_least16_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("int_least32_t") { int_least32_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("int_least64_t") { int_least64_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("uint8_t") { uint8_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_unsigned); CHECK(j == j_unsigned_reference); } SECTION("uint16_t") { uint16_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_unsigned); CHECK(j == j_unsigned_reference); } SECTION("uint32_t") { uint32_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_unsigned); CHECK(j == j_unsigned_reference); } SECTION("uint64_t") { uint64_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_unsigned); CHECK(j == j_unsigned_reference); } SECTION("uint_fast8_t") { uint_fast8_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_unsigned); CHECK(j == j_unsigned_reference); } SECTION("uint_fast16_t") { uint_fast16_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_unsigned); CHECK(j == j_unsigned_reference); } SECTION("uint_fast32_t") { uint_fast32_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_unsigned); CHECK(j == j_unsigned_reference); } SECTION("uint_fast64_t") { uint_fast64_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_unsigned); CHECK(j == j_unsigned_reference); } SECTION("uint_least8_t") { uint_least8_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_unsigned); CHECK(j == j_unsigned_reference); } SECTION("uint_least16_t") { uint_least16_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_unsigned); CHECK(j == j_unsigned_reference); } SECTION("uint_least32_t") { uint_least32_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_unsigned); CHECK(j == j_unsigned_reference); } SECTION("uint_least64_t") { uint_least64_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_unsigned); CHECK(j == j_unsigned_reference); } SECTION("integer literal without suffix") { json j(42); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("integer literal with u suffix") { json j(42u); CHECK(j.type() == json::value_t::number_unsigned); CHECK(j == j_unsigned_reference); } SECTION("integer literal with l suffix") { json j(42l); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("integer literal with ul suffix") { json j(42ul); CHECK(j.type() == json::value_t::number_unsigned); CHECK(j == j_unsigned_reference); } SECTION("integer literal with ll suffix") { json j(42ll); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("integer literal with ull suffix") { json j(42ull); CHECK(j.type() == json::value_t::number_unsigned); CHECK(j == j_unsigned_reference); } } SECTION("create a floating-point number (explicit)") { SECTION("uninitialized value") { json::number_float_t n{}; json j(n); CHECK(j.type() == json::value_t::number_float); } SECTION("initialized value") { json::number_float_t n(42.23); json j(n); CHECK(j.type() == json::value_t::number_float); } } SECTION("create a floating-point number (implicit)") { // reference object json::number_float_t n_reference = 42.23; json j_reference(n_reference); SECTION("float") { float n = 42.23; json j(n); CHECK(j.type() == json::value_t::number_float); CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); } SECTION("double") { double n = 42.23; json j(n); CHECK(j.type() == json::value_t::number_float); CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); } SECTION("long double") { long double n = 42.23; json j(n); CHECK(j.type() == json::value_t::number_float); CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); } SECTION("floating-point literal without suffix") { json j(42.23); CHECK(j.type() == json::value_t::number_float); CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); } SECTION("integer literal with f suffix") { json j(42.23f); CHECK(j.type() == json::value_t::number_float); CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); } SECTION("integer literal with l suffix") { json j(42.23l); CHECK(j.type() == json::value_t::number_float); CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float)); } } SECTION("create a container (array or object) from an initializer list") { SECTION("empty initializer list") { SECTION("explicit") { std::initializer_list l; json j(l); CHECK(j.type() == json::value_t::object); } SECTION("implicit") { json j {}; CHECK(j.type() == json::value_t::null); } } SECTION("one element") { SECTION("array") { SECTION("explicit") { std::initializer_list l = {json(json::array_t())}; json j(l); CHECK(j.type() == json::value_t::array); } SECTION("implicit") { json j {json::array_t()}; CHECK(j.type() == json::value_t::array); } } SECTION("object") { SECTION("explicit") { std::initializer_list l = {json(json::object_t())}; json j(l); CHECK(j.type() == json::value_t::array); } SECTION("implicit") { json j {json::object_t()}; CHECK(j.type() == json::value_t::array); } } SECTION("string") { SECTION("explicit") { std::initializer_list l = {json("Hello world")}; json j(l); CHECK(j.type() == json::value_t::array); } SECTION("implicit") { json j {"Hello world"}; CHECK(j.type() == json::value_t::array); } } SECTION("boolean") { SECTION("explicit") { std::initializer_list l = {json(true)}; json j(l); CHECK(j.type() == json::value_t::array); } SECTION("implicit") { json j {true}; CHECK(j.type() == json::value_t::array); } } SECTION("number (integer)") { SECTION("explicit") { std::initializer_list l = {json(1)}; json j(l); CHECK(j.type() == json::value_t::array); } SECTION("implicit") { json j {1}; CHECK(j.type() == json::value_t::array); } } SECTION("number (unsigned)") { SECTION("explicit") { std::initializer_list l = {json(1u)}; json j(l); CHECK(j.type() == json::value_t::array); } SECTION("implicit") { json j {1u}; CHECK(j.type() == json::value_t::array); } } SECTION("number (floating-point)") { SECTION("explicit") { std::initializer_list l = {json(42.23)}; json j(l); CHECK(j.type() == json::value_t::array); } SECTION("implicit") { json j {42.23}; CHECK(j.type() == json::value_t::array); } } } SECTION("more elements") { SECTION("explicit") { std::initializer_list l = {1, 1u, 42.23, true, nullptr, json::object_t(), json::array_t()}; json j(l); CHECK(j.type() == json::value_t::array); } SECTION("implicit") { json j {1, 1u, 42.23, true, nullptr, json::object_t(), json::array_t()}; CHECK(j.type() == json::value_t::array); } } SECTION("implicit type deduction") { SECTION("object") { json j { {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} }; CHECK(j.type() == json::value_t::object); } SECTION("array") { json j { {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} , 13 }; CHECK(j.type() == json::value_t::array); } } SECTION("explicit type deduction") { SECTION("empty object") { json j = json::object(); CHECK(j.type() == json::value_t::object); } SECTION("object") { json j = json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} }); CHECK(j.type() == json::value_t::object); } SECTION("object with error") { CHECK_THROWS_AS(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }), std::logic_error); CHECK_THROWS_WITH(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }), "cannot create object from initializer list"); } SECTION("empty array") { json j = json::array(); CHECK(j.type() == json::value_t::array); } SECTION("array") { json j = json::array({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} }); CHECK(j.type() == json::value_t::array); } } } SECTION("create an array of n copies of a given value") { json v = {1, "foo", 34.23, {1, 2, 3}, {{"A", 1}, {"B", 2u}}}; json arr(3, v); CHECK(arr.size() == 3); for (auto& x : arr) { CHECK(x == v); } } SECTION("create a JSON container from an iterator range") { SECTION("object") { SECTION("json(begin(), end())") { { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json j_new(jobject.begin(), jobject.end()); CHECK(j_new == jobject); } { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json j_new(jobject.cbegin(), jobject.cend()); CHECK(j_new == jobject); } } SECTION("json(begin(), begin())") { { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json j_new(jobject.begin(), jobject.begin()); CHECK(j_new == json::object()); } { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json j_new(jobject.cbegin(), jobject.cbegin()); CHECK(j_new == json::object()); } } SECTION("construct from subrange") { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; json j_new(jobject.find("b"), jobject.find("e")); CHECK(j_new == json({{"b", 1}, {"c", 17u}, {"d", false}})); } SECTION("incompatible iterators") { { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; CHECK_THROWS_AS(json(jobject.begin(), jobject2.end()), std::domain_error); CHECK_THROWS_AS(json(jobject2.begin(), jobject.end()), std::domain_error); CHECK_THROWS_WITH(json(jobject.begin(), jobject2.end()), "iterators are not compatible"); CHECK_THROWS_WITH(json(jobject2.begin(), jobject.end()), "iterators are not compatible"); } { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; CHECK_THROWS_AS(json(jobject.cbegin(), jobject2.cend()), std::domain_error); CHECK_THROWS_AS(json(jobject2.cbegin(), jobject.cend()), std::domain_error); CHECK_THROWS_WITH(json(jobject.cbegin(), jobject2.cend()), "iterators are not compatible"); CHECK_THROWS_WITH(json(jobject2.cbegin(), jobject.cend()), "iterators are not compatible"); } } } SECTION("array") { SECTION("json(begin(), end())") { { json jarray = {1, 2, 3, 4, 5}; json j_new(jarray.begin(), jarray.end()); CHECK(j_new == jarray); } { json jarray = {1, 2, 3, 4, 5}; json j_new(jarray.cbegin(), jarray.cend()); CHECK(j_new == jarray); } } SECTION("json(begin(), begin())") { { json jarray = {1, 2, 3, 4, 5}; json j_new(jarray.begin(), jarray.begin()); CHECK(j_new == json::array()); } { json jarray = {1, 2, 3, 4, 5}; json j_new(jarray.cbegin(), jarray.cbegin()); CHECK(j_new == json::array()); } } SECTION("construct from subrange") { { json jarray = {1, 2, 3, 4, 5}; json j_new(jarray.begin() + 1, jarray.begin() + 3); CHECK(j_new == json({2, 3})); } { json jarray = {1, 2, 3, 4, 5}; json j_new(jarray.cbegin() + 1, jarray.cbegin() + 3); CHECK(j_new == json({2, 3})); } } SECTION("incompatible iterators") { { json jarray = {1, 2, 3, 4}; json jarray2 = {2, 3, 4, 5}; CHECK_THROWS_AS(json(jarray.begin(), jarray2.end()), std::domain_error); CHECK_THROWS_AS(json(jarray2.begin(), jarray.end()), std::domain_error); CHECK_THROWS_WITH(json(jarray.begin(), jarray2.end()), "iterators are not compatible"); CHECK_THROWS_WITH(json(jarray2.begin(), jarray.end()), "iterators are not compatible"); } { json jarray = {1, 2, 3, 4}; json jarray2 = {2, 3, 4, 5}; CHECK_THROWS_AS(json(jarray.cbegin(), jarray2.cend()), std::domain_error); CHECK_THROWS_AS(json(jarray2.cbegin(), jarray.cend()), std::domain_error); CHECK_THROWS_WITH(json(jarray.cbegin(), jarray2.cend()), "iterators are not compatible"); CHECK_THROWS_WITH(json(jarray2.cbegin(), jarray.cend()), "iterators are not compatible"); } } } SECTION("other values") { SECTION("construct with two valid iterators") { SECTION("null") { { json j; CHECK_THROWS_AS(json(j.begin(), j.end()), std::domain_error); CHECK_THROWS_WITH(json(j.begin(), j.end()), "cannot use construct with iterators from null"); } { json j; CHECK_THROWS_AS(json(j.cbegin(), j.cend()), std::domain_error); CHECK_THROWS_WITH(json(j.cbegin(), j.cend()), "cannot use construct with iterators from null"); } } SECTION("string") { { json j = "foo"; json j_new(j.begin(), j.end()); CHECK(j == j_new); } { json j = "bar"; json j_new(j.cbegin(), j.cend()); CHECK(j == j_new); } } SECTION("number (boolean)") { { json j = false; json j_new(j.begin(), j.end()); CHECK(j == j_new); } { json j = true; json j_new(j.cbegin(), j.cend()); CHECK(j == j_new); } } SECTION("number (integer)") { { json j = 17; json j_new(j.begin(), j.end()); CHECK(j == j_new); } { json j = 17; json j_new(j.cbegin(), j.cend()); CHECK(j == j_new); } } SECTION("number (unsigned)") { { json j = 17u; json j_new(j.begin(), j.end()); CHECK(j == j_new); } { json j = 17u; json j_new(j.cbegin(), j.cend()); CHECK(j == j_new); } } SECTION("number (floating point)") { { json j = 23.42; json j_new(j.begin(), j.end()); CHECK(j == j_new); } { json j = 23.42; json j_new(j.cbegin(), j.cend()); CHECK(j == j_new); } } } SECTION("construct with two invalid iterators") { SECTION("string") { { json j = "foo"; CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); } { json j = "bar"; CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); } } SECTION("number (boolean)") { { json j = false; CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); } { json j = true; CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); } } SECTION("number (integer)") { { json j = 17; CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); } { json j = 17; CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); } } SECTION("number (integer)") { { json j = 17u; CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); } { json j = 17u; CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); } } SECTION("number (floating point)") { { json j = 23.42; CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range"); CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range"); } { json j = 23.42; CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range"); CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range"); } } } } } SECTION("create a JSON value from an input stream") { SECTION("std::stringstream") { std::stringstream ss; ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; json j(ss); CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); } SECTION("with callback function") { std::stringstream ss; ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; json j(ss, [](int, json::parse_event_t, const json & val) { // filter all number(2) elements if (val == json(2)) { return false; } else { return true; } }); CHECK(j == json({"foo", 1, 3, false, {{"one", 1}}})); } SECTION("std::ifstream") { std::ifstream f("test/data/json_tests/pass1.json"); json j(f); } } } TEST_CASE("other constructors and destructor") { SECTION("copy constructor") { SECTION("object") { json j {{"foo", 1}, {"bar", false}}; json k(j); CHECK(j == k); } SECTION("array") { json j {"foo", 1, 42.23, false}; json k(j); CHECK(j == k); } SECTION("null") { json j(nullptr); json k(j); CHECK(j == k); } SECTION("boolean") { json j(true); json k(j); CHECK(j == k); } SECTION("string") { json j("Hello world"); json k(j); CHECK(j == k); } SECTION("number (integer)") { json j(42); json k(j); CHECK(j == k); } SECTION("number (unsigned)") { json j(42u); json k(j); CHECK(j == k); } SECTION("number (floating-point)") { json j(42.23); json k(j); CHECK(j == k); } } SECTION("move constructor") { json j {{"foo", "bar"}, {"baz", {1, 2, 3, 4}}, {"a", 42u}, {"b", 42.23}, {"c", nullptr}}; CHECK(j.type() == json::value_t::object); json k(std::move(j)); CHECK(k.type() == json::value_t::object); CHECK(j.type() == json::value_t::null); } SECTION("copy assignment") { SECTION("object") { json j {{"foo", 1}, {"bar", false}}; json k; k = j; CHECK(j == k); } SECTION("array") { json j {"foo", 1, 42.23, false}; json k; k = j; CHECK(j == k); } SECTION("null") { json j(nullptr); json k; k = j; CHECK(j == k); } SECTION("boolean") { json j(true); json k; k = j; CHECK(j == k); } SECTION("string") { json j("Hello world"); json k; k = j; CHECK(j == k); } SECTION("number (integer)") { json j(42); json k; k = j; CHECK(j == k); } SECTION("number (unsigned)") { json j(42u); json k; k = j; CHECK(j == k); } SECTION("number (floating-point)") { json j(42.23); json k; k = j; CHECK(j == k); } } SECTION("destructor") { SECTION("object") { auto j = new json {{"foo", 1}, {"bar", false}}; delete j; } SECTION("array") { auto j = new json {"foo", 1, 1u, false, 23.42}; delete j; } SECTION("string") { auto j = new json("Hello world"); delete j; } } } TEST_CASE("object inspection") { SECTION("convenience type checker") { SECTION("object") { json j {{"foo", 1}, {"bar", false}}; CHECK(not j.is_null()); CHECK(not j.is_boolean()); CHECK(not j.is_number()); CHECK(not j.is_number_integer()); CHECK(not j.is_number_unsigned()); CHECK(not j.is_number_float()); CHECK(j.is_object()); CHECK(not j.is_array()); CHECK(not j.is_string()); CHECK(not j.is_discarded()); CHECK(not j.is_primitive()); CHECK(j.is_structured()); } SECTION("array") { json j {"foo", 1, 1u, 42.23, false}; CHECK(not j.is_null()); CHECK(not j.is_boolean()); CHECK(not j.is_number()); CHECK(not j.is_number_integer()); CHECK(not j.is_number_unsigned()); CHECK(not j.is_number_float()); CHECK(not j.is_object()); CHECK(j.is_array()); CHECK(not j.is_string()); CHECK(not j.is_discarded()); CHECK(not j.is_primitive()); CHECK(j.is_structured()); } SECTION("null") { json j(nullptr); CHECK(j.is_null()); CHECK(not j.is_boolean()); CHECK(not j.is_number()); CHECK(not j.is_number_integer()); CHECK(not j.is_number_unsigned()); CHECK(not j.is_number_float()); CHECK(not j.is_object()); CHECK(not j.is_array()); CHECK(not j.is_string()); CHECK(not j.is_discarded()); CHECK(j.is_primitive()); CHECK(not j.is_structured()); } SECTION("boolean") { json j(true); CHECK(not j.is_null()); CHECK(j.is_boolean()); CHECK(not j.is_number()); CHECK(not j.is_number_integer()); CHECK(not j.is_number_unsigned()); CHECK(not j.is_number_float()); CHECK(not j.is_object()); CHECK(not j.is_array()); CHECK(not j.is_string()); CHECK(not j.is_discarded()); CHECK(j.is_primitive()); CHECK(not j.is_structured()); } SECTION("string") { json j("Hello world"); CHECK(not j.is_null()); CHECK(not j.is_boolean()); CHECK(not j.is_number()); CHECK(not j.is_number_integer()); CHECK(not j.is_number_unsigned()); CHECK(not j.is_number_float()); CHECK(not j.is_object()); CHECK(not j.is_array()); CHECK(j.is_string()); CHECK(not j.is_discarded()); CHECK(j.is_primitive()); CHECK(not j.is_structured()); } SECTION("number (integer)") { json j(42); CHECK(not j.is_null()); CHECK(not j.is_boolean()); CHECK(j.is_number()); CHECK(j.is_number_integer()); CHECK(not j.is_number_unsigned()); CHECK(not j.is_number_float()); CHECK(not j.is_object()); CHECK(not j.is_array()); CHECK(not j.is_string()); CHECK(not j.is_discarded()); CHECK(j.is_primitive()); CHECK(not j.is_structured()); } SECTION("number (unsigned)") { json j(42u); CHECK(not j.is_null()); CHECK(not j.is_boolean()); CHECK(j.is_number()); CHECK(j.is_number_integer()); CHECK(j.is_number_unsigned()); CHECK(not j.is_number_float()); CHECK(not j.is_object()); CHECK(not j.is_array()); CHECK(not j.is_string()); CHECK(not j.is_discarded()); CHECK(j.is_primitive()); CHECK(not j.is_structured()); } SECTION("number (floating-point)") { json j(42.23); CHECK(not j.is_null()); CHECK(not j.is_boolean()); CHECK(j.is_number()); CHECK(not j.is_number_integer()); CHECK(not j.is_number_unsigned()); CHECK(j.is_number_float()); CHECK(not j.is_object()); CHECK(not j.is_array()); CHECK(not j.is_string()); CHECK(not j.is_discarded()); CHECK(j.is_primitive()); CHECK(not j.is_structured()); } SECTION("discarded") { json j(json::value_t::discarded); CHECK(not j.is_null()); CHECK(not j.is_boolean()); CHECK(not j.is_number()); CHECK(not j.is_number_integer()); CHECK(not j.is_number_unsigned()); CHECK(not j.is_number_float()); CHECK(not j.is_object()); CHECK(not j.is_array()); CHECK(not j.is_string()); CHECK(j.is_discarded()); CHECK(not j.is_primitive()); CHECK(not j.is_structured()); } } SECTION("serialization") { json j {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} }; SECTION("no indent / indent=-1") { CHECK(j.dump() == "{\"array\":[1,2,3,4],\"boolean\":false,\"null\":null,\"number\":42,\"object\":{},\"string\":\"Hello world\"}"); CHECK(j.dump() == j.dump(-1)); } SECTION("indent=0") { CHECK(j.dump(0) == "{\n\"array\": [\n1,\n2,\n3,\n4\n],\n\"boolean\": false,\n\"null\": null,\n\"number\": 42,\n\"object\": {},\n\"string\": \"Hello world\"\n}"); } SECTION("indent=4") { CHECK(j.dump(4) == "{\n \"array\": [\n 1,\n 2,\n 3,\n 4\n ],\n \"boolean\": false,\n \"null\": null,\n \"number\": 42,\n \"object\": {},\n \"string\": \"Hello world\"\n}"); } SECTION("dump and floating-point numbers") { auto s = json(42.23).dump(); CHECK(s.find("42.23") != std::string::npos); } SECTION("dump and small floating-point numbers") { auto s = json(1.23456e-78).dump(); CHECK(s.find("1.23456e-78") != std::string::npos); } SECTION("dump and non-ASCII characters") { CHECK(json("ä").dump() == "\"ä\""); CHECK(json("Ö").dump() == "\"Ö\""); CHECK(json("❤️").dump() == "\"❤️\""); } SECTION("serialization of discarded element") { json j_discarded(json::value_t::discarded); CHECK(j_discarded.dump() == ""); } } SECTION("return the type of the object (explicit)") { SECTION("null") { json j = nullptr; CHECK(j.type() == json::value_t::null); } SECTION("object") { json j = {{"foo", "bar"}}; CHECK(j.type() == json::value_t::object); } SECTION("array") { json j = {1, 2, 3, 4}; CHECK(j.type() == json::value_t::array); } SECTION("boolean") { json j = true; CHECK(j.type() == json::value_t::boolean); } SECTION("string") { json j = "Hello world"; CHECK(j.type() == json::value_t::string); } SECTION("number (integer)") { json j = 23; CHECK(j.type() == json::value_t::number_integer); } SECTION("number (unsigned)") { json j = 23u; CHECK(j.type() == json::value_t::number_unsigned); } SECTION("number (floating-point)") { json j = 42.23; CHECK(j.type() == json::value_t::number_float); } } SECTION("return the type of the object (implicit)") { SECTION("null") { json j = nullptr; json::value_t t = j; CHECK(t == j.type()); } SECTION("object") { json j = {{"foo", "bar"}}; json::value_t t = j; CHECK(t == j.type()); } SECTION("array") { json j = {1, 2, 3, 4}; json::value_t t = j; CHECK(t == j.type()); } SECTION("boolean") { json j = true; json::value_t t = j; CHECK(t == j.type()); } SECTION("string") { json j = "Hello world"; json::value_t t = j; CHECK(t == j.type()); } SECTION("number (integer)") { json j = 23; json::value_t t = j; CHECK(t == j.type()); } SECTION("number (unsigned)") { json j = 23u; json::value_t t = j; CHECK(t == j.type()); } SECTION("number (floating-point)") { json j = 42.23; json::value_t t = j; CHECK(t == j.type()); } } } TEST_CASE("value conversion") { SECTION("get an object (explicit)") { json::object_t o_reference = {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} }; json j(o_reference); SECTION("json::object_t") { json::object_t o = j.get(); CHECK(json(o) == j); } SECTION("std::map") { std::map o = j.get>(); CHECK(json(o) == j); } SECTION("std::multimap") { std::multimap o = j.get>(); CHECK(json(o) == j); } SECTION("std::unordered_map") { std::unordered_map o = j.get>(); CHECK(json(o) == j); } SECTION("std::unordered_multimap") { std::unordered_multimap o = j.get>(); CHECK(json(o) == j); } SECTION("exception in case of a non-object type") { CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); CHECK_THROWS_WITH(json(json::value_t::null).get(), "type must be object, but is null"); CHECK_THROWS_WITH(json(json::value_t::array).get(), "type must be object, but is array"); CHECK_THROWS_WITH(json(json::value_t::string).get(), "type must be object, but is string"); CHECK_THROWS_WITH(json(json::value_t::boolean).get(), "type must be object, but is boolean"); CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), "type must be object, but is number"); CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), "type must be object, but is number"); CHECK_THROWS_WITH(json(json::value_t::number_float).get(), "type must be object, but is number"); } } SECTION("get an object (implicit)") { json::object_t o_reference = {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} }; json j(o_reference); SECTION("json::object_t") { json::object_t o = j; CHECK(json(o) == j); } SECTION("std::map") { std::map o = j; CHECK(json(o) == j); } SECTION("std::multimap") { std::multimap o = j; CHECK(json(o) == j); } SECTION("std::unordered_map") { std::unordered_map o = j; CHECK(json(o) == j); } SECTION("std::unordered_multimap") { std::unordered_multimap o = j; CHECK(json(o) == j); } } SECTION("get an array (explicit)") { json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()}; json j(a_reference); SECTION("json::array_t") { json::array_t a = j.get(); CHECK(json(a) == j); } SECTION("std::list") { std::list a = j.get>(); CHECK(json(a) == j); } SECTION("std::forward_list") { std::forward_list a = j.get>(); CHECK(json(a) == j); } SECTION("std::vector") { std::vector a = j.get>(); CHECK(json(a) == j); } SECTION("std::deque") { std::deque a = j.get>(); CHECK(json(a) == j); } SECTION("exception in case of a non-array type") { CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); CHECK_THROWS_WITH(json(json::value_t::null).get(), "type must be array, but is null"); CHECK_THROWS_WITH(json(json::value_t::object).get(), "type must be array, but is object"); CHECK_THROWS_WITH(json(json::value_t::string).get(), "type must be array, but is string"); CHECK_THROWS_WITH(json(json::value_t::boolean).get(), "type must be array, but is boolean"); CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), "type must be array, but is number"); CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), "type must be array, but is number"); CHECK_THROWS_WITH(json(json::value_t::number_float).get(), "type must be array, but is number"); } } SECTION("get an array (implicit)") { json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()}; json j(a_reference); SECTION("json::array_t") { json::array_t a = j; CHECK(json(a) == j); } SECTION("std::list") { std::list a = j; CHECK(json(a) == j); } SECTION("std::forward_list") { std::forward_list a = j; CHECK(json(a) == j); } SECTION("std::vector") { std::vector a = j; CHECK(json(a) == j); } SECTION("std::deque") { std::deque a = j; CHECK(json(a) == j); } } SECTION("get a string (explicit)") { json::string_t s_reference {"Hello world"}; json j(s_reference); SECTION("string_t") { json::string_t s = j.get(); CHECK(json(s) == j); } SECTION("std::string") { std::string s = j.get(); CHECK(json(s) == j); } SECTION("exception in case of a non-string type") { CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); CHECK_THROWS_WITH(json(json::value_t::null).get(), "type must be string, but is null"); CHECK_THROWS_WITH(json(json::value_t::object).get(), "type must be string, but is object"); CHECK_THROWS_WITH(json(json::value_t::array).get(), "type must be string, but is array"); CHECK_THROWS_WITH(json(json::value_t::boolean).get(), "type must be string, but is boolean"); CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), "type must be string, but is number"); CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), "type must be string, but is number"); CHECK_THROWS_WITH(json(json::value_t::number_float).get(), "type must be string, but is number"); } } SECTION("get a string (implicit)") { json::string_t s_reference {"Hello world"}; json j(s_reference); SECTION("string_t") { json::string_t s = j; CHECK(json(s) == j); } SECTION("std::string") { std::string s = j; CHECK(json(s) == j); } } SECTION("get a boolean (explicit)") { json::boolean_t b_reference {true}; json j(b_reference); SECTION("boolean_t") { json::boolean_t b = j.get(); CHECK(json(b) == j); } SECTION("bool") { bool b = j.get(); CHECK(json(b) == j); } SECTION("exception in case of a non-string type") { CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::number_integer).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::number_unsigned).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::number_float).get(), std::logic_error); CHECK_THROWS_WITH(json(json::value_t::null).get(), "type must be boolean, but is null"); CHECK_THROWS_WITH(json(json::value_t::object).get(), "type must be boolean, but is object"); CHECK_THROWS_WITH(json(json::value_t::array).get(), "type must be boolean, but is array"); CHECK_THROWS_WITH(json(json::value_t::string).get(), "type must be boolean, but is string"); CHECK_THROWS_WITH(json(json::value_t::number_integer).get(), "type must be boolean, but is number"); CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get(), "type must be boolean, but is number"); CHECK_THROWS_WITH(json(json::value_t::number_float).get(), "type must be boolean, but is number"); } } SECTION("get a boolean (implicit)") { json::boolean_t b_reference {true}; json j(b_reference); SECTION("boolean_t") { json::boolean_t b = j; CHECK(json(b) == j); } SECTION("bool") { bool b = j; CHECK(json(b) == j); } } SECTION("get an integer number (explicit)") { json::number_integer_t n_reference {42}; json j(n_reference); json::number_unsigned_t n_unsigned_reference {42u}; json j_unsigned(n_unsigned_reference); SECTION("number_integer_t") { json::number_integer_t n = j.get(); CHECK(json(n) == j); } SECTION("number_unsigned_t") { json::number_unsigned_t n = j_unsigned.get(); CHECK(json(n) == j_unsigned); } SECTION("short") { short n = j.get(); CHECK(json(n) == j); } SECTION("unsigned short") { unsigned short n = j.get(); CHECK(json(n) == j); } SECTION("int") { int n = j.get(); CHECK(json(n) == j); } SECTION("unsigned int") { unsigned int n = j.get(); CHECK(json(n) == j); } SECTION("long") { long n = j.get(); CHECK(json(n) == j); } SECTION("unsigned long") { unsigned long n = j.get(); CHECK(json(n) == j); } SECTION("long long") { long long n = j.get(); CHECK(json(n) == j); } SECTION("unsigned long long") { unsigned long long n = j.get(); CHECK(json(n) == j); } SECTION("int8_t") { int8_t n = j.get(); CHECK(json(n) == j); } SECTION("int16_t") { int16_t n = j.get(); CHECK(json(n) == j); } SECTION("int32_t") { int32_t n = j.get(); CHECK(json(n) == j); } SECTION("int64_t") { int64_t n = j.get(); CHECK(json(n) == j); } SECTION("int8_fast_t") { int_fast8_t n = j.get(); CHECK(json(n) == j); } SECTION("int16_fast_t") { int_fast16_t n = j.get(); CHECK(json(n) == j); } SECTION("int32_fast_t") { int_fast32_t n = j.get(); CHECK(json(n) == j); } SECTION("int64_fast_t") { int_fast64_t n = j.get(); CHECK(json(n) == j); } SECTION("int8_least_t") { int_least8_t n = j.get(); CHECK(json(n) == j); } SECTION("int16_least_t") { int_least16_t n = j.get(); CHECK(json(n) == j); } SECTION("int32_least_t") { int_least32_t n = j.get(); CHECK(json(n) == j); } SECTION("int64_least_t") { int_least64_t n = j.get(); CHECK(json(n) == j); } SECTION("uint8_t") { uint8_t n = j.get(); CHECK(json(n) == j); } SECTION("uint16_t") { uint16_t n = j.get(); CHECK(json(n) == j); } SECTION("uint32_t") { uint32_t n = j.get(); CHECK(json(n) == j); } SECTION("uint64_t") { uint64_t n = j.get(); CHECK(json(n) == j); } SECTION("uint8_fast_t") { uint_fast8_t n = j.get(); CHECK(json(n) == j); } SECTION("uint16_fast_t") { uint_fast16_t n = j.get(); CHECK(json(n) == j); } SECTION("uint32_fast_t") { uint_fast32_t n = j.get(); CHECK(json(n) == j); } SECTION("uint64_fast_t") { uint_fast64_t n = j.get(); CHECK(json(n) == j); } SECTION("uint8_least_t") { uint_least8_t n = j.get(); CHECK(json(n) == j); } SECTION("uint16_least_t") { uint_least16_t n = j.get(); CHECK(json(n) == j); } SECTION("uint32_least_t") { uint_least32_t n = j.get(); CHECK(json(n) == j); } SECTION("uint64_least_t") { uint_least64_t n = j.get(); CHECK(json(n) == j); } SECTION("exception in case of a non-number type") { CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); CHECK_THROWS_WITH(json(json::value_t::null).get(), "type must be number, but is null"); CHECK_THROWS_WITH(json(json::value_t::object).get(), "type must be number, but is object"); CHECK_THROWS_WITH(json(json::value_t::array).get(), "type must be number, but is array"); CHECK_THROWS_WITH(json(json::value_t::string).get(), "type must be number, but is string"); CHECK_THROWS_WITH(json(json::value_t::boolean).get(), "type must be number, but is boolean"); CHECK_NOTHROW(json(json::value_t::number_float).get()); CHECK_NOTHROW(json(json::value_t::number_float).get()); } } SECTION("get an integer number (implicit)") { json::number_integer_t n_reference {42}; json j(n_reference); json::number_unsigned_t n_unsigned_reference {42u}; json j_unsigned(n_unsigned_reference); SECTION("number_integer_t") { json::number_integer_t n = j.get(); CHECK(json(n) == j); } SECTION("number_unsigned_t") { json::number_unsigned_t n = j_unsigned.get(); CHECK(json(n) == j_unsigned); } SECTION("short") { short n = j; CHECK(json(n) == j); } SECTION("unsigned short") { unsigned short n = j_unsigned; CHECK(json(n) == j_unsigned); } SECTION("int") { int n = j; CHECK(json(n) == j); } SECTION("unsigned int") { unsigned int n = j_unsigned; CHECK(json(n) == j_unsigned); } SECTION("long") { long n = j; CHECK(json(n) == j); } SECTION("unsigned long") { unsigned long n = j_unsigned; CHECK(json(n) == j_unsigned); } SECTION("long long") { long long n = j; CHECK(json(n) == j); } SECTION("unsigned long long") { unsigned long long n = j_unsigned; CHECK(json(n) == j_unsigned); } SECTION("int8_t") { int8_t n = j; CHECK(json(n) == j); } SECTION("int16_t") { int16_t n = j; CHECK(json(n) == j); } SECTION("int32_t") { int32_t n = j; CHECK(json(n) == j); } SECTION("int64_t") { int64_t n = j; CHECK(json(n) == j); } SECTION("int8_fast_t") { int_fast8_t n = j; CHECK(json(n) == j); } SECTION("int16_fast_t") { int_fast16_t n = j; CHECK(json(n) == j); } SECTION("int32_fast_t") { int_fast32_t n = j; CHECK(json(n) == j); } SECTION("int64_fast_t") { int_fast64_t n = j; CHECK(json(n) == j); } SECTION("int8_least_t") { int_least8_t n = j; CHECK(json(n) == j); } SECTION("int16_least_t") { int_least16_t n = j; CHECK(json(n) == j); } SECTION("int32_least_t") { int_least32_t n = j; CHECK(json(n) == j); } SECTION("int64_least_t") { int_least64_t n = j; CHECK(json(n) == j); } SECTION("uint8_t") { uint8_t n = j_unsigned; CHECK(json(n) == j_unsigned); } SECTION("uint16_t") { uint16_t n = j_unsigned; CHECK(json(n) == j_unsigned); } SECTION("uint32_t") { uint32_t n = j_unsigned; CHECK(json(n) == j_unsigned); } SECTION("uint64_t") { uint64_t n = j_unsigned; CHECK(json(n) == j_unsigned); } SECTION("uint8_fast_t") { uint_fast8_t n = j_unsigned; CHECK(json(n) == j_unsigned); } SECTION("uint16_fast_t") { uint_fast16_t n = j_unsigned; CHECK(json(n) == j_unsigned); } SECTION("uint32_fast_t") { uint_fast32_t n = j_unsigned; CHECK(json(n) == j_unsigned); } SECTION("uint64_fast_t") { uint_fast64_t n = j_unsigned; CHECK(json(n) == j_unsigned); } SECTION("uint8_least_t") { uint_least8_t n = j_unsigned; CHECK(json(n) == j_unsigned); } SECTION("uint16_least_t") { uint_least16_t n = j_unsigned; CHECK(json(n) == j_unsigned); } SECTION("uint32_least_t") { uint_least32_t n = j_unsigned; CHECK(json(n) == j_unsigned); } SECTION("uint64_least_t") { uint_least64_t n = j_unsigned; CHECK(json(n) == j_unsigned); } } SECTION("get a floating-point number (explicit)") { json::number_float_t n_reference {42.23}; json j(n_reference); SECTION("number_float_t") { json::number_float_t n = j.get(); CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); } SECTION("float") { float n = j.get(); CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); } SECTION("double") { double n = j.get(); CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); } SECTION("exception in case of a non-string type") { CHECK_THROWS_AS(json(json::value_t::null).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::object).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::array).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::string).get(), std::logic_error); CHECK_THROWS_AS(json(json::value_t::boolean).get(), std::logic_error); CHECK_THROWS_WITH(json(json::value_t::null).get(), "type must be number, but is null"); CHECK_THROWS_WITH(json(json::value_t::object).get(), "type must be number, but is object"); CHECK_THROWS_WITH(json(json::value_t::array).get(), "type must be number, but is array"); CHECK_THROWS_WITH(json(json::value_t::string).get(), "type must be number, but is string"); CHECK_THROWS_WITH(json(json::value_t::boolean).get(), "type must be number, but is boolean"); CHECK_NOTHROW(json(json::value_t::number_integer).get()); CHECK_NOTHROW(json(json::value_t::number_unsigned).get()); } } SECTION("get a floating-point number (implicit)") { json::number_float_t n_reference {42.23}; json j(n_reference); SECTION("number_float_t") { json::number_float_t n = j; CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); } SECTION("float") { float n = j; CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); } SECTION("double") { double n = j; CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); } } SECTION("more involved conversions") { SECTION("object-like STL containers") { json j1 = {{"one", 1}, {"two", 2}, {"three", 3}}; json j2 = {{"one", 1u}, {"two", 2u}, {"three", 3u}}; json j3 = {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}}; json j4 = {{"one", true}, {"two", false}, {"three", true}}; json j5 = {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}}; SECTION("std::map") { auto m1 = j1.get>(); auto m2 = j2.get>(); auto m3 = j3.get>(); auto m4 = j4.get>(); //auto m5 = j5.get>(); } SECTION("std::unordered_map") { auto m1 = j1.get>(); auto m2 = j2.get>(); auto m3 = j3.get>(); auto m4 = j4.get>(); //auto m5 = j5.get>(); //CHECK(m5["one"] == "eins"); } SECTION("std::multimap") { auto m1 = j1.get>(); auto m2 = j2.get>(); auto m3 = j3.get>(); auto m4 = j4.get>(); //auto m5 = j5.get>(); //CHECK(m5["one"] == "eins"); } SECTION("std::unordered_multimap") { auto m1 = j1.get>(); auto m2 = j2.get>(); auto m3 = j3.get>(); auto m4 = j4.get>(); //auto m5 = j5.get>(); //CHECK(m5["one"] == "eins"); } SECTION("exception in case of a non-object type") { CHECK_THROWS_AS((json().get>()), std::logic_error); CHECK_THROWS_WITH((json().get>()), "type must be object, but is null"); } } SECTION("array-like STL containers") { json j1 = {1, 2, 3, 4}; json j2 = {1u, 2u, 3u, 4u}; json j3 = {1.2, 2.3, 3.4, 4.5}; json j4 = {true, false, true}; json j5 = {"one", "two", "three"}; SECTION("std::list") { auto m1 = j1.get>(); auto m2 = j2.get>(); auto m3 = j3.get>(); auto m4 = j4.get>(); auto m5 = j5.get>(); } //SECTION("std::forward_list") //{ // auto m1 = j1.get>(); // auto m2 = j2.get>(); // auto m3 = j3.get>(); // auto m4 = j4.get>(); // auto m5 = j5.get>(); //} SECTION("std::vector") { auto m1 = j1.get>(); auto m2 = j2.get>(); auto m3 = j3.get>(); auto m4 = j4.get>(); auto m5 = j5.get>(); } SECTION("std::deque") { auto m1 = j1.get>(); auto m2 = j2.get>(); auto m3 = j2.get>(); auto m4 = j4.get>(); auto m5 = j5.get>(); } SECTION("std::set") { auto m1 = j1.get>(); auto m2 = j2.get>(); auto m3 = j3.get>(); auto m4 = j4.get>(); auto m5 = j5.get>(); } SECTION("std::unordered_set") { auto m1 = j1.get>(); auto m2 = j2.get>(); auto m3 = j3.get>(); auto m4 = j4.get>(); auto m5 = j5.get>(); } SECTION("exception in case of a non-object type") { CHECK_THROWS_AS((json().get>()), std::logic_error); CHECK_THROWS_AS((json().get>()), std::logic_error); CHECK_THROWS_AS((json().get>()), std::logic_error); CHECK_THROWS_AS((json().get>()), std::logic_error); CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); CHECK_THROWS_WITH((json().get>()), "type must be array, but is null"); } } } } TEST_CASE("pointer access") { // create a JSON value with different types json json_types = { {"boolean", true}, { "number", { {"integer", 42}, {"unsigned", 42u}, {"floating-point", 17.23} } }, {"string", "Hello, world!"}, {"array", {1, 2, 3, 4, 5}}, {"null", nullptr} }; SECTION("pointer access to object_t") { using test_type = json::object_t; json value = {{"one", 1}, {"two", 2}}; // check if pointers are returned correctly test_type* p1 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p1 == value.get()); const test_type* p2 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p2 == value.get()); const test_type* const p3 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p3 == value.get()); // check if null pointers are returned correctly CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to const object_t") { using test_type = json::object_t; const json value = {{"one", 1}, {"two", 2}}; // this should not compile // test_type* p1 = value.get_ptr(); // check if pointers are returned correctly const test_type* p2 = value.get_ptr(); CHECK(*p2 == value.get()); const test_type* const p3 = value.get_ptr(); CHECK(p2 == p3); } SECTION("pointer access to array_t") { using test_type = json::array_t; json value = {1, 2, 3, 4}; // check if pointers are returned correctly test_type* p1 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p1 == value.get()); const test_type* p2 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p2 == value.get()); const test_type* const p3 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p3 == value.get()); // check if null pointers are returned correctly CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to string_t") { using test_type = json::string_t; json value = "hello"; // check if pointers are returned correctly test_type* p1 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p1 == value.get()); const test_type* p2 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p2 == value.get()); const test_type* const p3 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p3 == value.get()); // check if null pointers are returned correctly CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to boolean_t") { using test_type = json::boolean_t; json value = false; // check if pointers are returned correctly test_type* p1 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p1 == value.get()); const test_type* p2 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p2 == value.get()); const test_type* const p3 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p3 == value.get()); // check if null pointers are returned correctly CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to number_integer_t") { using test_type = json::number_integer_t; json value = 23; // check if pointers are returned correctly test_type* p1 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p1 == value.get()); const test_type* p2 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p2 == value.get()); const test_type* const p3 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p3 == value.get()); // check if null pointers are returned correctly CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to number_unsigned_t") { using test_type = json::number_unsigned_t; json value = 23u; // check if pointers are returned correctly test_type* p1 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p1 == value.get()); const test_type* p2 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p2 == value.get()); const test_type* const p3 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p3 == value.get()); // check if null pointers are returned correctly CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to number_float_t") { using test_type = json::number_float_t; json value = 42.23; // check if pointers are returned correctly test_type* p1 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p1 == Approx(value.get())); const test_type* p2 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p2 == Approx(value.get())); const test_type* const p3 = value.get_ptr(); CHECK(p1 == value.get_ptr()); CHECK(*p3 == Approx(value.get())); // check if null pointers are returned correctly CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() != nullptr); } } TEST_CASE("reference access") { // create a JSON value with different types json json_types = { {"boolean", true}, { "number", { {"integer", 42}, {"floating-point", 17.23} } }, {"string", "Hello, world!"}, {"array", {1, 2, 3, 4, 5}}, {"null", nullptr} }; SECTION("reference access to object_t") { using test_type = json::object_t; json value = {{"one", 1}, {"two", 2}}; // check if references are returned correctly test_type& p1 = value.get_ref(); CHECK(&p1 == value.get_ptr()); CHECK(p1 == value.get()); const test_type& p2 = value.get_ref(); CHECK(&p2 == value.get_ptr()); CHECK(p2 == value.get()); // check if mismatching references throw correctly CHECK_NOTHROW(value.get_ref()); CHECK_THROWS(value.get_ref()); CHECK_THROWS(value.get_ref()); CHECK_THROWS(value.get_ref()); CHECK_THROWS(value.get_ref()); CHECK_THROWS(value.get_ref()); } SECTION("const reference access to const object_t") { using test_type = json::object_t; const json value = {{"one", 1}, {"two", 2}}; // this should not compile // test_type& p1 = value.get_ref(); // check if references are returned correctly const test_type& p2 = value.get_ref(); CHECK(&p2 == value.get_ptr()); CHECK(p2 == value.get()); } SECTION("reference access to array_t") { using test_type = json::array_t; json value = {1, 2, 3, 4}; // check if references are returned correctly test_type& p1 = value.get_ref(); CHECK(&p1 == value.get_ptr()); CHECK(p1 == value.get()); const test_type& p2 = value.get_ref(); CHECK(&p2 == value.get_ptr()); CHECK(p2 == value.get()); // check if mismatching references throw correctly CHECK_THROWS(value.get_ref()); CHECK_NOTHROW(value.get_ref()); CHECK_THROWS(value.get_ref()); CHECK_THROWS(value.get_ref()); CHECK_THROWS(value.get_ref()); CHECK_THROWS(value.get_ref()); } SECTION("reference access to string_t") { using test_type = json::string_t; json value = "hello"; // check if references are returned correctly test_type& p1 = value.get_ref(); CHECK(&p1 == value.get_ptr()); CHECK(p1 == value.get()); const test_type& p2 = value.get_ref(); CHECK(&p2 == value.get_ptr()); CHECK(p2 == value.get()); // check if mismatching references throw correctly CHECK_THROWS(value.get_ref()); CHECK_THROWS(value.get_ref()); CHECK_NOTHROW(value.get_ref()); CHECK_THROWS(value.get_ref()); CHECK_THROWS(value.get_ref()); CHECK_THROWS(value.get_ref()); } SECTION("reference access to boolean_t") { using test_type = json::boolean_t; json value = false; // check if references are returned correctly test_type& p1 = value.get_ref(); CHECK(&p1 == value.get_ptr()); CHECK(p1 == value.get()); const test_type& p2 = value.get_ref(); CHECK(&p2 == value.get_ptr()); CHECK(p2 == value.get()); // check if mismatching references throw correctly CHECK_THROWS(value.get_ref()); CHECK_THROWS(value.get_ref()); CHECK_THROWS(value.get_ref()); CHECK_NOTHROW(value.get_ref()); CHECK_THROWS(value.get_ref()); CHECK_THROWS(value.get_ref()); } SECTION("reference access to number_integer_t") { using test_type = json::number_integer_t; json value = 23; // check if references are returned correctly test_type& p1 = value.get_ref(); CHECK(&p1 == value.get_ptr()); CHECK(p1 == value.get()); const test_type& p2 = value.get_ref(); CHECK(&p2 == value.get_ptr()); CHECK(p2 == value.get()); // check if mismatching references throw correctly CHECK_THROWS(value.get_ref()); CHECK_THROWS(value.get_ref()); CHECK_THROWS(value.get_ref()); CHECK_THROWS(value.get_ref()); CHECK_NOTHROW(value.get_ref()); CHECK_THROWS(value.get_ref()); } SECTION("reference access to number_float_t") { using test_type = json::number_float_t; json value = 42.23; // check if references are returned correctly test_type& p1 = value.get_ref(); CHECK(&p1 == value.get_ptr()); CHECK(p1 == value.get()); const test_type& p2 = value.get_ref(); CHECK(&p2 == value.get_ptr()); CHECK(p2 == value.get()); // check if mismatching references throw correctly CHECK_THROWS(value.get_ref()); CHECK_THROWS(value.get_ref()); CHECK_THROWS(value.get_ref()); CHECK_THROWS(value.get_ref()); CHECK_THROWS(value.get_ref()); CHECK_NOTHROW(value.get_ref()); } } TEST_CASE("element access") { SECTION("array") { json j = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; const json j_const = j; SECTION("access specified element with bounds checking") { SECTION("access within bounds") { CHECK(j.at(0) == json(1)); CHECK(j.at(1) == json(1u)); CHECK(j.at(2) == json(true)); CHECK(j.at(3) == json(nullptr)); CHECK(j.at(4) == json("string")); CHECK(j.at(5) == json(42.23)); CHECK(j.at(6) == json(json::object())); CHECK(j.at(7) == json({1, 2, 3})); CHECK(j_const.at(0) == json(1)); CHECK(j_const.at(1) == json(1u)); CHECK(j_const.at(2) == json(true)); CHECK(j_const.at(3) == json(nullptr)); CHECK(j_const.at(4) == json("string")); CHECK(j_const.at(5) == json(42.23)); CHECK(j_const.at(6) == json(json::object())); CHECK(j_const.at(7) == json({1, 2, 3})); } SECTION("access outside bounds") { CHECK_THROWS_AS(j.at(8), std::out_of_range); CHECK_THROWS_AS(j_const.at(8), std::out_of_range); CHECK_THROWS_WITH(j.at(8), "array index 8 is out of range"); CHECK_THROWS_WITH(j_const.at(8), "array index 8 is out of range"); } SECTION("access on non-array type") { SECTION("null") { json j_nonarray(json::value_t::null); const json j_nonarray_const(j_nonarray); CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with null"); CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with null"); } SECTION("boolean") { json j_nonarray(json::value_t::boolean); const json j_nonarray_const(j_nonarray); CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with boolean"); CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with boolean"); } SECTION("string") { json j_nonarray(json::value_t::string); const json j_nonarray_const(j_nonarray); CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with string"); CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with string"); } SECTION("object") { json j_nonarray(json::value_t::object); const json j_nonarray_const(j_nonarray); CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with object"); CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with object"); } SECTION("number (integer)") { json j_nonarray(json::value_t::number_integer); const json j_nonarray_const(j_nonarray); CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); } SECTION("number (unsigned)") { json j_nonarray(json::value_t::number_unsigned); const json j_nonarray_const(j_nonarray); CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); } SECTION("number (floating-point)") { json j_nonarray(json::value_t::number_float); const json j_nonarray_const(j_nonarray); CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error); CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error); CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number"); CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number"); } } } SECTION("front and back") { CHECK(j.front() == json(1)); CHECK(j_const.front() == json(1)); CHECK(j.back() == json({1, 2, 3})); CHECK(j_const.back() == json({1, 2, 3})); } SECTION("access specified element") { SECTION("access within bounds") { CHECK(j[0] == json(1)); CHECK(j[1] == json(1u)); CHECK(j[2] == json(true)); CHECK(j[3] == json(nullptr)); CHECK(j[4] == json("string")); CHECK(j[5] == json(42.23)); CHECK(j[6] == json(json::object())); CHECK(j[7] == json({1, 2, 3})); CHECK(j_const[0] == json(1)); CHECK(j_const[1] == json(1u)); CHECK(j_const[2] == json(true)); CHECK(j_const[3] == json(nullptr)); CHECK(j_const[4] == json("string")); CHECK(j_const[5] == json(42.23)); CHECK(j_const[6] == json(json::object())); CHECK(j_const[7] == json({1, 2, 3})); } SECTION("access on non-array type") { SECTION("null") { SECTION("standard tests") { json j_nonarray(json::value_t::null); const json j_nonarray_const(j_nonarray); CHECK_NOTHROW(j_nonarray[0]); CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with null"); } SECTION("implicit transformation to properly filled array") { json j_nonarray; j_nonarray[3] = 42; CHECK(j_nonarray == json({nullptr, nullptr, nullptr, 42})); } } SECTION("boolean") { json j_nonarray(json::value_t::boolean); const json j_nonarray_const(j_nonarray); CHECK_THROWS_AS(j_nonarray[0], std::domain_error); CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with boolean"); CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with boolean"); } SECTION("string") { json j_nonarray(json::value_t::string); const json j_nonarray_const(j_nonarray); CHECK_THROWS_AS(j_nonarray[0], std::domain_error); CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with string"); CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with string"); } SECTION("object") { json j_nonarray(json::value_t::object); const json j_nonarray_const(j_nonarray); CHECK_THROWS_AS(j_nonarray[0], std::domain_error); CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with object"); CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with object"); } SECTION("number (integer)") { json j_nonarray(json::value_t::number_integer); const json j_nonarray_const(j_nonarray); CHECK_THROWS_AS(j_nonarray[0], std::domain_error); CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); } SECTION("number (unsigned)") { json j_nonarray(json::value_t::number_unsigned); const json j_nonarray_const(j_nonarray); CHECK_THROWS_AS(j_nonarray[0], std::domain_error); CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); } SECTION("number (floating-point)") { json j_nonarray(json::value_t::number_float); const json j_nonarray_const(j_nonarray); CHECK_THROWS_AS(j_nonarray[0], std::domain_error); CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error); CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number"); CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number"); } } } SECTION("remove specified element") { SECTION("remove element by index") { { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; jarray.erase(0); CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); } { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; jarray.erase(1); CHECK(jarray == json({1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); } { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; jarray.erase(2); CHECK(jarray == json({1, 1u, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); } { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; jarray.erase(3); CHECK(jarray == json({1, 1u, true, "string", 42.23, json::object(), {1, 2, 3}})); } { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; jarray.erase(4); CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); } { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; jarray.erase(5); CHECK(jarray == json({1, 1u, true, nullptr, "string", json::object(), {1, 2, 3}})); } { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; jarray.erase(6); CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, {1, 2, 3}})); } { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; jarray.erase(7); CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object()})); } { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; CHECK_THROWS_AS(jarray.erase(8), std::out_of_range); CHECK_THROWS_WITH(jarray.erase(8), "array index 8 is out of range"); } } SECTION("remove element by iterator") { SECTION("erase(begin())") { { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::iterator it2 = jarray.erase(jarray.begin()); CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); CHECK(*it2 == json(1u)); } { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::const_iterator it2 = jarray.erase(jarray.cbegin()); CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); CHECK(*it2 == json(1u)); } } SECTION("erase(begin(), end())") { { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::iterator it2 = jarray.erase(jarray.begin(), jarray.end()); CHECK(jarray == json::array()); CHECK(it2 == jarray.end()); } { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cend()); CHECK(jarray == json::array()); CHECK(it2 == jarray.cend()); } } SECTION("erase(begin(), begin())") { { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::iterator it2 = jarray.erase(jarray.begin(), jarray.begin()); CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); CHECK(*it2 == json(1)); } { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cbegin()); CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); CHECK(*it2 == json(1)); } } SECTION("erase at offset") { { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::iterator it = jarray.begin() + 4; json::iterator it2 = jarray.erase(it); CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); CHECK(*it2 == json(42.23)); } { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::const_iterator it = jarray.cbegin() + 4; json::const_iterator it2 = jarray.erase(it); CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}})); CHECK(*it2 == json(42.23)); } } SECTION("erase subrange") { { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::iterator it2 = jarray.erase(jarray.begin() + 3, jarray.begin() + 6); CHECK(jarray == json({1, 1u, true, json::object(), {1, 2, 3}})); CHECK(*it2 == json::object()); } { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::const_iterator it2 = jarray.erase(jarray.cbegin() + 3, jarray.cbegin() + 6); CHECK(jarray == json({1, 1u, true, json::object(), {1, 2, 3}})); CHECK(*it2 == json::object()); } } SECTION("different arrays") { { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json jarray2 = {"foo", "bar"}; CHECK_THROWS_AS(jarray.erase(jarray2.begin()), std::domain_error); CHECK_THROWS_AS(jarray.erase(jarray.begin(), jarray2.end()), std::domain_error); CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray.end()), std::domain_error); CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray2.end()), std::domain_error); CHECK_THROWS_WITH(jarray.erase(jarray2.begin()), "iterator does not fit current value"); CHECK_THROWS_WITH(jarray.erase(jarray.begin(), jarray2.end()), "iterators do not fit current value"); CHECK_THROWS_WITH(jarray.erase(jarray2.begin(), jarray.end()), "iterators do not fit current value"); CHECK_THROWS_WITH(jarray.erase(jarray2.begin(), jarray2.end()), "iterators do not fit current value"); } { json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json jarray2 = {"foo", "bar"}; CHECK_THROWS_AS(jarray.erase(jarray2.cbegin()), std::domain_error); CHECK_THROWS_AS(jarray.erase(jarray.cbegin(), jarray2.cend()), std::domain_error); CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray.cend()), std::domain_error); CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray2.cend()), std::domain_error); CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin()), "iterator does not fit current value"); CHECK_THROWS_WITH(jarray.erase(jarray.cbegin(), jarray2.cend()), "iterators do not fit current value"); CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin(), jarray.cend()), "iterators do not fit current value"); CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin(), jarray2.cend()), "iterators do not fit current value"); } } } SECTION("remove element by index in non-array type") { SECTION("null") { json j_nonobject(json::value_t::null); CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with null"); } SECTION("boolean") { json j_nonobject(json::value_t::boolean); CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with boolean"); } SECTION("string") { json j_nonobject(json::value_t::string); CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with string"); } SECTION("object") { json j_nonobject(json::value_t::object); CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with object"); } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); } SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error); CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number"); } } } } SECTION("object") { json j = {{"integer", 1}, {"unsigned", 1u}, {"floating", 42.23}, {"null", nullptr}, {"string", "hello world"}, {"boolean", true}, {"object", json::object()}, {"array", {1, 2, 3}}}; const json j_const = j; SECTION("access specified element with bounds checking") { SECTION("access within bounds") { CHECK(j.at("integer") == json(1)); CHECK(j.at("unsigned") == json(1u)); CHECK(j.at("boolean") == json(true)); CHECK(j.at("null") == json(nullptr)); CHECK(j.at("string") == json("hello world")); CHECK(j.at("floating") == json(42.23)); CHECK(j.at("object") == json(json::object())); CHECK(j.at("array") == json({1, 2, 3})); CHECK(j_const.at("integer") == json(1)); CHECK(j_const.at("unsigned") == json(1u)); CHECK(j_const.at("boolean") == json(true)); CHECK(j_const.at("null") == json(nullptr)); CHECK(j_const.at("string") == json("hello world")); CHECK(j_const.at("floating") == json(42.23)); CHECK(j_const.at("object") == json(json::object())); CHECK(j_const.at("array") == json({1, 2, 3})); } SECTION("access outside bounds") { CHECK_THROWS_AS(j.at("foo"), std::out_of_range); CHECK_THROWS_AS(j_const.at("foo"), std::out_of_range); CHECK_THROWS_WITH(j.at("foo"), "key 'foo' not found"); CHECK_THROWS_WITH(j_const.at("foo"), "key 'foo' not found"); } SECTION("access on non-object type") { SECTION("null") { json j_nonobject(json::value_t::null); const json j_nonobject_const(j_nonobject); CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with null"); CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with null"); } SECTION("boolean") { json j_nonobject(json::value_t::boolean); const json j_nonobject_const(j_nonobject); CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with boolean"); CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with boolean"); } SECTION("string") { json j_nonobject(json::value_t::string); const json j_nonobject_const(j_nonobject); CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with string"); CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with string"); } SECTION("array") { json j_nonobject(json::value_t::array); const json j_nonobject_const(j_nonobject); CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with array"); CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with array"); } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); const json j_nonobject_const(j_nonobject); CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number"); CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number"); } SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); const json j_nonobject_const(j_nonobject); CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number"); CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number"); } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); const json j_nonobject_const(j_nonobject); CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error); CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error); CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number"); CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number"); } } } SECTION("access specified element with default value") { SECTION("access existing value") { CHECK(j.value("integer", 2) == 1); CHECK(j.value("integer", 1.0) == Approx(1)); CHECK(j.value("unsigned", 2) == 1u); CHECK(j.value("unsigned", 1.0) == Approx(1u)); CHECK(j.value("null", json(1)) == json()); CHECK(j.value("boolean", false) == true); CHECK(j.value("string", "bar") == "hello world"); CHECK(j.value("string", std::string("bar")) == "hello world"); CHECK(j.value("floating", 12.34) == Approx(42.23)); CHECK(j.value("floating", 12) == 42); CHECK(j.value("object", json({{"foo", "bar"}})) == json(json::object())); CHECK(j.value("array", json({10, 100})) == json({1, 2, 3})); CHECK(j_const.value("integer", 2) == 1); CHECK(j_const.value("integer", 1.0) == Approx(1)); CHECK(j_const.value("unsigned", 2) == 1u); CHECK(j_const.value("unsigned", 1.0) == Approx(1u)); CHECK(j_const.value("boolean", false) == true); CHECK(j_const.value("string", "bar") == "hello world"); CHECK(j_const.value("string", std::string("bar")) == "hello world"); CHECK(j_const.value("floating", 12.34) == Approx(42.23)); CHECK(j_const.value("floating", 12) == 42); CHECK(j_const.value("object", json({{"foo", "bar"}})) == json(json::object())); CHECK(j_const.value("array", json({10, 100})) == json({1, 2, 3})); } SECTION("access non-existing value") { CHECK(j.value("_", 2) == 2); CHECK(j.value("_", 2u) == 2u); CHECK(j.value("_", false) == false); CHECK(j.value("_", "bar") == "bar"); CHECK(j.value("_", 12.34) == Approx(12.34)); CHECK(j.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}})); CHECK(j.value("_", json({10, 100})) == json({10, 100})); CHECK(j_const.value("_", 2) == 2); CHECK(j_const.value("_", 2u) == 2u); CHECK(j_const.value("_", false) == false); CHECK(j_const.value("_", "bar") == "bar"); CHECK(j_const.value("_", 12.34) == Approx(12.34)); CHECK(j_const.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}})); CHECK(j_const.value("_", json({10, 100})) == json({10, 100})); } SECTION("access on non-object type") { SECTION("null") { json j_nonobject(json::value_t::null); const json j_nonobject_const(j_nonobject); CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with null"); CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with null"); } SECTION("boolean") { json j_nonobject(json::value_t::boolean); const json j_nonobject_const(j_nonobject); CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with boolean"); CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with boolean"); } SECTION("string") { json j_nonobject(json::value_t::string); const json j_nonobject_const(j_nonobject); CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with string"); CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with string"); } SECTION("array") { json j_nonobject(json::value_t::array); const json j_nonobject_const(j_nonobject); CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with array"); CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with array"); } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); const json j_nonobject_const(j_nonobject); CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number"); CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number"); } SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); const json j_nonobject_const(j_nonobject); CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number"); CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number"); } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); const json j_nonobject_const(j_nonobject); CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error); CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error); CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number"); CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number"); } } } SECTION("front and back") { // "array" is the smallest key CHECK(j.front() == json({1, 2, 3})); CHECK(j_const.front() == json({1, 2, 3})); // "unsigned" is the largest key CHECK(j.back() == json(1u)); CHECK(j_const.back() == json(1u)); } SECTION("access specified element") { SECTION("access within bounds") { CHECK(j["integer"] == json(1)); CHECK(j[json::object_t::key_type("integer")] == j["integer"]); CHECK(j["unsigned"] == json(1u)); CHECK(j[json::object_t::key_type("unsigned")] == j["unsigned"]); CHECK(j["boolean"] == json(true)); CHECK(j[json::object_t::key_type("boolean")] == j["boolean"]); CHECK(j["null"] == json(nullptr)); CHECK(j[json::object_t::key_type("null")] == j["null"]); CHECK(j["string"] == json("hello world")); CHECK(j[json::object_t::key_type("string")] == j["string"]); CHECK(j["floating"] == json(42.23)); CHECK(j[json::object_t::key_type("floating")] == j["floating"]); CHECK(j["object"] == json(json::object())); CHECK(j[json::object_t::key_type("object")] == j["object"]); CHECK(j["array"] == json({1, 2, 3})); CHECK(j[json::object_t::key_type("array")] == j["array"]); CHECK(j_const["integer"] == json(1)); CHECK(j_const[json::object_t::key_type("integer")] == j["integer"]); CHECK(j_const["boolean"] == json(true)); CHECK(j_const[json::object_t::key_type("boolean")] == j["boolean"]); CHECK(j_const["null"] == json(nullptr)); CHECK(j_const[json::object_t::key_type("null")] == j["null"]); CHECK(j_const["string"] == json("hello world")); CHECK(j_const[json::object_t::key_type("string")] == j["string"]); CHECK(j_const["floating"] == json(42.23)); CHECK(j_const[json::object_t::key_type("floating")] == j["floating"]); CHECK(j_const["object"] == json(json::object())); CHECK(j_const[json::object_t::key_type("object")] == j["object"]); CHECK(j_const["array"] == json({1, 2, 3})); CHECK(j_const[json::object_t::key_type("array")] == j["array"]); } SECTION("access on non-object type") { SECTION("null") { json j_nonobject(json::value_t::null); json j_nonobject2(json::value_t::null); const json j_const_nonobject(j_nonobject); CHECK_NOTHROW(j_nonobject["foo"]); CHECK_NOTHROW(j_nonobject2[json::object_t::key_type("foo")]); CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with null"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], "cannot use operator[] with null"); } SECTION("boolean") { json j_nonobject(json::value_t::boolean); const json j_const_nonobject(j_nonobject); CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with boolean"); CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], "cannot use operator[] with boolean"); CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with boolean"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], "cannot use operator[] with boolean"); } SECTION("string") { json j_nonobject(json::value_t::string); const json j_const_nonobject(j_nonobject); CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with string"); CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], "cannot use operator[] with string"); CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with string"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], "cannot use operator[] with string"); } SECTION("array") { json j_nonobject(json::value_t::array); const json j_const_nonobject(j_nonobject); CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with array"); CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], "cannot use operator[] with array"); CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with array"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], "cannot use operator[] with array"); } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); const json j_const_nonobject(j_nonobject); CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number"); CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], "cannot use operator[] with number"); CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], "cannot use operator[] with number"); } SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); const json j_const_nonobject(j_nonobject); CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number"); CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], "cannot use operator[] with number"); CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], "cannot use operator[] with number"); } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); const json j_const_nonobject(j_nonobject); CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error); CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error); CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error); CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error); CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number"); CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], "cannot use operator[] with number"); CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number"); CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")], "cannot use operator[] with number"); } } } SECTION("remove specified element") { SECTION("remove element by key") { CHECK(j.find("integer") != j.end()); CHECK(j.erase("integer") == 1); CHECK(j.find("integer") == j.end()); CHECK(j.erase("integer") == 0); CHECK(j.find("unsigned") != j.end()); CHECK(j.erase("unsigned") == 1); CHECK(j.find("unsigned") == j.end()); CHECK(j.erase("unsigned") == 0); CHECK(j.find("boolean") != j.end()); CHECK(j.erase("boolean") == 1); CHECK(j.find("boolean") == j.end()); CHECK(j.erase("boolean") == 0); CHECK(j.find("null") != j.end()); CHECK(j.erase("null") == 1); CHECK(j.find("null") == j.end()); CHECK(j.erase("null") == 0); CHECK(j.find("string") != j.end()); CHECK(j.erase("string") == 1); CHECK(j.find("string") == j.end()); CHECK(j.erase("string") == 0); CHECK(j.find("floating") != j.end()); CHECK(j.erase("floating") == 1); CHECK(j.find("floating") == j.end()); CHECK(j.erase("floating") == 0); CHECK(j.find("object") != j.end()); CHECK(j.erase("object") == 1); CHECK(j.find("object") == j.end()); CHECK(j.erase("object") == 0); CHECK(j.find("array") != j.end()); CHECK(j.erase("array") == 1); CHECK(j.find("array") == j.end()); CHECK(j.erase("array") == 0); } SECTION("remove element by iterator") { SECTION("erase(begin())") { { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json::iterator it2 = jobject.erase(jobject.begin()); CHECK(jobject == json({{"b", 1}, {"c", 17u}})); CHECK(*it2 == json(1)); } { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json::const_iterator it2 = jobject.erase(jobject.cbegin()); CHECK(jobject == json({{"b", 1}, {"c", 17u}})); CHECK(*it2 == json(1)); } } SECTION("erase(begin(), end())") { { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json::iterator it2 = jobject.erase(jobject.begin(), jobject.end()); CHECK(jobject == json::object()); CHECK(it2 == jobject.end()); } { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cend()); CHECK(jobject == json::object()); CHECK(it2 == jobject.cend()); } } SECTION("erase(begin(), begin())") { { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json::iterator it2 = jobject.erase(jobject.begin(), jobject.begin()); CHECK(jobject == json({{"a", "a"}, {"b", 1}, {"c", 17u}})); CHECK(*it2 == json("a")); } { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cbegin()); CHECK(jobject == json({{"a", "a"}, {"b", 1}, {"c", 17u}})); CHECK(*it2 == json("a")); } } SECTION("erase at offset") { { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json::iterator it = jobject.find("b"); json::iterator it2 = jobject.erase(it); CHECK(jobject == json({{"a", "a"}, {"c", 17u}})); CHECK(*it2 == json(17)); } { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}}; json::const_iterator it = jobject.find("b"); json::const_iterator it2 = jobject.erase(it); CHECK(jobject == json({{"a", "a"}, {"c", 17u}})); CHECK(*it2 == json(17)); } } SECTION("erase subrange") { { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; json::iterator it2 = jobject.erase(jobject.find("b"), jobject.find("e")); CHECK(jobject == json({{"a", "a"}, {"e", true}})); CHECK(*it2 == json(true)); } { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; json::const_iterator it2 = jobject.erase(jobject.find("b"), jobject.find("e")); CHECK(jobject == json({{"a", "a"}, {"e", true}})); CHECK(*it2 == json(true)); } } SECTION("different objects") { { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; CHECK_THROWS_AS(jobject.erase(jobject2.begin()), std::domain_error); CHECK_THROWS_AS(jobject.erase(jobject.begin(), jobject2.end()), std::domain_error); CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject.end()), std::domain_error); CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject2.end()), std::domain_error); CHECK_THROWS_WITH(jobject.erase(jobject2.begin()), "iterator does not fit current value"); CHECK_THROWS_WITH(jobject.erase(jobject.begin(), jobject2.end()), "iterators do not fit current value"); CHECK_THROWS_WITH(jobject.erase(jobject2.begin(), jobject.end()), "iterators do not fit current value"); CHECK_THROWS_WITH(jobject.erase(jobject2.begin(), jobject2.end()), "iterators do not fit current value"); } { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}}; json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}}; CHECK_THROWS_AS(jobject.erase(jobject2.cbegin()), std::domain_error); CHECK_THROWS_AS(jobject.erase(jobject.cbegin(), jobject2.cend()), std::domain_error); CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject.cend()), std::domain_error); CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject2.cend()), std::domain_error); CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin()), "iterator does not fit current value"); CHECK_THROWS_WITH(jobject.erase(jobject.cbegin(), jobject2.cend()), "iterators do not fit current value"); CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin(), jobject.cend()), "iterators do not fit current value"); CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin(), jobject2.cend()), "iterators do not fit current value"); } } } SECTION("remove element by key in non-object type") { SECTION("null") { json j_nonobject(json::value_t::null); CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with null"); } SECTION("boolean") { json j_nonobject(json::value_t::boolean); CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with boolean"); } SECTION("string") { json j_nonobject(json::value_t::string); CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with string"); } SECTION("array") { json j_nonobject(json::value_t::array); CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with array"); } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with number"); } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error); CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with number"); } } } SECTION("find an element in an object") { SECTION("existing element") { for (auto key : {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" }) { CHECK(j.find(key) != j.end()); CHECK(*j.find(key) == j.at(key)); CHECK(j_const.find(key) != j_const.end()); CHECK(*j_const.find(key) == j_const.at(key)); } } SECTION("nonexisting element") { CHECK(j.find("foo") == j.end()); CHECK(j_const.find("foo") == j_const.end()); } SECTION("all types") { SECTION("null") { json j_nonarray(json::value_t::null); const json j_nonarray_const(j_nonarray); CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); } SECTION("string") { json j_nonarray(json::value_t::string); const json j_nonarray_const(j_nonarray); CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); } SECTION("object") { json j_nonarray(json::value_t::object); const json j_nonarray_const(j_nonarray); CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); } SECTION("array") { json j_nonarray(json::value_t::array); const json j_nonarray_const(j_nonarray); CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); } SECTION("boolean") { json j_nonarray(json::value_t::boolean); const json j_nonarray_const(j_nonarray); CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); } SECTION("number (integer)") { json j_nonarray(json::value_t::number_integer); const json j_nonarray_const(j_nonarray); CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); } SECTION("number (unsigned)") { json j_nonarray(json::value_t::number_unsigned); const json j_nonarray_const(j_nonarray); CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); } SECTION("number (floating-point)") { json j_nonarray(json::value_t::number_float); const json j_nonarray_const(j_nonarray); CHECK(j_nonarray.find("foo") == j_nonarray.end()); CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end()); } } } SECTION("count keys in an object") { SECTION("existing element") { for (auto key : {"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array" }) { CHECK(j.count(key) == 1); CHECK(j_const.count(key) == 1); } } SECTION("nonexisting element") { CHECK(j.count("foo") == 0); CHECK(j_const.count("foo") == 0); } SECTION("all types") { SECTION("null") { json j_nonobject(json::value_t::null); const json j_nonobject_const(j_nonobject); CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); } SECTION("string") { json j_nonobject(json::value_t::string); const json j_nonobject_const(j_nonobject); CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); } SECTION("object") { json j_nonobject(json::value_t::object); const json j_nonobject_const(j_nonobject); CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); } SECTION("array") { json j_nonobject(json::value_t::array); const json j_nonobject_const(j_nonobject); CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); } SECTION("boolean") { json j_nonobject(json::value_t::boolean); const json j_nonobject_const(j_nonobject); CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); } SECTION("number (integer)") { json j_nonobject(json::value_t::number_integer); const json j_nonobject_const(j_nonobject); CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); } SECTION("number (unsigned)") { json j_nonobject(json::value_t::number_unsigned); const json j_nonobject_const(j_nonobject); CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); } SECTION("number (floating-point)") { json j_nonobject(json::value_t::number_float); const json j_nonobject_const(j_nonobject); CHECK(j_nonobject.count("foo") == 0); CHECK(j_nonobject_const.count("foo") == 0); } } } } SECTION("other values") { SECTION("front and back") { SECTION("null") { { json j; CHECK_THROWS_AS(j.front(), std::out_of_range); CHECK_THROWS_AS(j.back(), std::out_of_range); CHECK_THROWS_WITH(j.front(), "cannot get value"); CHECK_THROWS_WITH(j.back(), "cannot get value"); } { const json j{}; CHECK_THROWS_AS(j.front(), std::out_of_range); CHECK_THROWS_AS(j.back(), std::out_of_range); CHECK_THROWS_WITH(j.front(), "cannot get value"); CHECK_THROWS_WITH(j.back(), "cannot get value"); } } SECTION("string") { { json j = "foo"; CHECK(j.front() == j); CHECK(j.back() == j); } { const json j = "bar"; CHECK(j.front() == j); CHECK(j.back() == j); } } SECTION("number (boolean)") { { json j = false; CHECK(j.front() == j); CHECK(j.back() == j); } { const json j = true; CHECK(j.front() == j); CHECK(j.back() == j); } } SECTION("number (integer)") { { json j = 17; CHECK(j.front() == j); CHECK(j.back() == j); } { const json j = 17; CHECK(j.front() == j); CHECK(j.back() == j); } } SECTION("number (unsigned)") { { json j = 17u; CHECK(j.front() == j); CHECK(j.back() == j); } { const json j = 17u; CHECK(j.front() == j); CHECK(j.back() == j); } } SECTION("number (floating point)") { { json j = 23.42; CHECK(j.front() == j); CHECK(j.back() == j); } { const json j = 23.42; CHECK(j.front() == j); CHECK(j.back() == j); } } } SECTION("erase with one valid iterator") { SECTION("null") { { json j; CHECK_THROWS_AS(j.erase(j.begin()), std::domain_error); CHECK_THROWS_WITH(j.erase(j.begin()), "cannot use erase() with null"); } { json j; CHECK_THROWS_AS(j.erase(j.cbegin()), std::domain_error); CHECK_THROWS_WITH(j.erase(j.begin()), "cannot use erase() with null"); } } SECTION("string") { { json j = "foo"; json::iterator it = j.erase(j.begin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { json j = "bar"; json::const_iterator it = j.erase(j.cbegin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } } SECTION("number (boolean)") { { json j = false; json::iterator it = j.erase(j.begin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { json j = true; json::const_iterator it = j.erase(j.cbegin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } } SECTION("number (integer)") { { json j = 17; json::iterator it = j.erase(j.begin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { json j = 17; json::const_iterator it = j.erase(j.cbegin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } } SECTION("number (unsigned)") { { json j = 17u; json::iterator it = j.erase(j.begin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { json j = 17u; json::const_iterator it = j.erase(j.cbegin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } } SECTION("number (floating point)") { { json j = 23.42; json::iterator it = j.erase(j.begin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { json j = 23.42; json::const_iterator it = j.erase(j.cbegin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } } } SECTION("erase with one invalid iterator") { SECTION("string") { { json j = "foo"; CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); } { json j = "bar"; CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); } } SECTION("number (boolean)") { { json j = false; CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); } { json j = true; CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); } } SECTION("number (integer)") { { json j = 17; CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); } { json j = 17; CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); } } SECTION("number (unsigned)") { { json j = 17u; CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); } { json j = 17u; CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); } } SECTION("number (floating point)") { { json j = 23.42; CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range); CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range"); } { json j = 23.42; CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range); CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range"); } } } SECTION("erase with two valid iterators") { SECTION("null") { { json j; CHECK_THROWS_AS(j.erase(j.begin(), j.end()), std::domain_error); CHECK_THROWS_WITH(j.erase(j.begin(), j.end()), "cannot use erase() with null"); } { json j; CHECK_THROWS_AS(j.erase(j.cbegin(), j.cend()), std::domain_error); CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cend()), "cannot use erase() with null"); } } SECTION("string") { { json j = "foo"; json::iterator it = j.erase(j.begin(), j.end()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { json j = "bar"; json::const_iterator it = j.erase(j.cbegin(), j.cend()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } } SECTION("number (boolean)") { { json j = false; json::iterator it = j.erase(j.begin(), j.end()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { json j = true; json::const_iterator it = j.erase(j.cbegin(), j.cend()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } } SECTION("number (integer)") { { json j = 17; json::iterator it = j.erase(j.begin(), j.end()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { json j = 17; json::const_iterator it = j.erase(j.cbegin(), j.cend()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } } SECTION("number (unsigned)") { { json j = 17u; json::iterator it = j.erase(j.begin(), j.end()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { json j = 17u; json::const_iterator it = j.erase(j.cbegin(), j.cend()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } } SECTION("number (floating point)") { { json j = 23.42; json::iterator it = j.erase(j.begin(), j.end()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { json j = 23.42; json::const_iterator it = j.erase(j.cbegin(), j.cend()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } } } SECTION("erase with two invalid iterators") { SECTION("string") { { json j = "foo"; CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); } { json j = "bar"; CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); } } SECTION("number (boolean)") { { json j = false; CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); } { json j = true; CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); } } SECTION("number (integer)") { { json j = 17; CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); } { json j = 17; CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); } } SECTION("number (unsigned)") { { json j = 17u; CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); } { json j = 17u; CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); } } SECTION("number (floating point)") { { json j = 23.42; CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range); CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range); CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range"); CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range"); } { json j = 23.42; CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range); CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range); CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range"); CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range"); } } } } } TEST_CASE("iterators") { SECTION("basic behavior") { SECTION("uninitialized") { json::iterator it; CHECK(it.m_object == nullptr); json::const_iterator cit; CHECK(cit.m_object == nullptr); } SECTION("boolean") { json j = true; json j_const(j); SECTION("json + begin/end") { json::iterator it = j.begin(); CHECK(it != j.end()); CHECK(*it == j); it++; CHECK(it != j.begin()); CHECK(it == j.end()); it--; CHECK(it == j.begin()); CHECK(it != j.end()); CHECK(*it == j); ++it; CHECK(it != j.begin()); CHECK(it == j.end()); --it; CHECK(it == j.begin()); CHECK(it != j.end()); CHECK(*it == j); } SECTION("const json + begin/end") { json::const_iterator it = j_const.begin(); CHECK(it != j_const.end()); CHECK(*it == j_const); it++; CHECK(it != j_const.begin()); CHECK(it == j_const.end()); it--; CHECK(it == j_const.begin()); CHECK(it != j_const.end()); CHECK(*it == j_const); ++it; CHECK(it != j_const.begin()); CHECK(it == j_const.end()); --it; CHECK(it == j_const.begin()); CHECK(it != j_const.end()); CHECK(*it == j_const); } SECTION("json + cbegin/cend") { json::const_iterator it = j.cbegin(); CHECK(it != j.cend()); CHECK(*it == j); it++; CHECK(it != j.cbegin()); CHECK(it == j.cend()); it--; CHECK(it == j.cbegin()); CHECK(it != j.cend()); CHECK(*it == j); ++it; CHECK(it != j.cbegin()); CHECK(it == j.cend()); --it; CHECK(it == j.cbegin()); CHECK(it != j.cend()); CHECK(*it == j); } SECTION("const json + cbegin/cend") { json::const_iterator it = j_const.cbegin(); CHECK(it != j_const.cend()); CHECK(*it == j_const); it++; CHECK(it != j_const.cbegin()); CHECK(it == j_const.cend()); it--; CHECK(it == j_const.cbegin()); CHECK(it != j_const.cend()); CHECK(*it == j_const); ++it; CHECK(it != j_const.cbegin()); CHECK(it == j_const.cend()); --it; CHECK(it == j_const.cbegin()); CHECK(it != j_const.cend()); CHECK(*it == j_const); } SECTION("json + rbegin/rend") { json::reverse_iterator it = j.rbegin(); CHECK(it != j.rend()); CHECK(*it == j); it++; CHECK(it != j.rbegin()); CHECK(it == j.rend()); it--; CHECK(it == j.rbegin()); CHECK(it != j.rend()); CHECK(*it == j); ++it; CHECK(it != j.rbegin()); CHECK(it == j.rend()); --it; CHECK(it == j.rbegin()); CHECK(it != j.rend()); CHECK(*it == j); } SECTION("json + crbegin/crend") { json::const_reverse_iterator it = j.crbegin(); CHECK(it != j.crend()); CHECK(*it == j); it++; CHECK(it != j.crbegin()); CHECK(it == j.crend()); it--; CHECK(it == j.crbegin()); CHECK(it != j.crend()); CHECK(*it == j); ++it; CHECK(it != j.crbegin()); CHECK(it == j.crend()); --it; CHECK(it == j.crbegin()); CHECK(it != j.crend()); CHECK(*it == j); } SECTION("const json + crbegin/crend") { json::const_reverse_iterator it = j_const.crbegin(); CHECK(it != j_const.crend()); CHECK(*it == j_const); it++; CHECK(it != j_const.crbegin()); CHECK(it == j_const.crend()); it--; CHECK(it == j_const.crbegin()); CHECK(it != j_const.crend()); CHECK(*it == j_const); ++it; CHECK(it != j_const.crbegin()); CHECK(it == j_const.crend()); --it; CHECK(it == j_const.crbegin()); CHECK(it != j_const.crend()); CHECK(*it == j_const); } SECTION("key/value") { auto it = j.begin(); auto cit = j_const.cbegin(); CHECK_THROWS_AS(it.key(), std::domain_error); CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); CHECK(it.value() == json(true)); CHECK_THROWS_AS(cit.key(), std::domain_error); CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); CHECK(cit.value() == json(true)); auto rit = j.rend(); auto crit = j.crend(); CHECK_THROWS_AS(rit.key(), std::domain_error); CHECK_THROWS_AS(rit.value(), std::out_of_range); CHECK_THROWS_AS(crit.key(), std::domain_error); CHECK_THROWS_AS(crit.value(), std::out_of_range); CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); CHECK_THROWS_WITH(rit.value(), "cannot get value"); CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); CHECK_THROWS_WITH(crit.value(), "cannot get value"); } } SECTION("string") { json j = "hello world"; json j_const(j); SECTION("json + begin/end") { json::iterator it = j.begin(); CHECK(it != j.end()); CHECK(*it == j); it++; CHECK(it != j.begin()); CHECK(it == j.end()); it--; CHECK(it == j.begin()); CHECK(it != j.end()); CHECK(*it == j); ++it; CHECK(it != j.begin()); CHECK(it == j.end()); --it; CHECK(it == j.begin()); CHECK(it != j.end()); CHECK(*it == j); } SECTION("const json + begin/end") { json::const_iterator it = j_const.begin(); CHECK(it != j_const.end()); CHECK(*it == j_const); it++; CHECK(it != j_const.begin()); CHECK(it == j_const.end()); it--; CHECK(it == j_const.begin()); CHECK(it != j_const.end()); CHECK(*it == j_const); ++it; CHECK(it != j_const.begin()); CHECK(it == j_const.end()); --it; CHECK(it == j_const.begin()); CHECK(it != j_const.end()); CHECK(*it == j_const); } SECTION("json + cbegin/cend") { json::const_iterator it = j.cbegin(); CHECK(it != j.cend()); CHECK(*it == j); it++; CHECK(it != j.cbegin()); CHECK(it == j.cend()); it--; CHECK(it == j.cbegin()); CHECK(it != j.cend()); CHECK(*it == j); ++it; CHECK(it != j.cbegin()); CHECK(it == j.cend()); --it; CHECK(it == j.cbegin()); CHECK(it != j.cend()); CHECK(*it == j); } SECTION("const json + cbegin/cend") { json::const_iterator it = j_const.cbegin(); CHECK(it != j_const.cend()); CHECK(*it == j_const); it++; CHECK(it != j_const.cbegin()); CHECK(it == j_const.cend()); it--; CHECK(it == j_const.cbegin()); CHECK(it != j_const.cend()); CHECK(*it == j_const); ++it; CHECK(it != j_const.cbegin()); CHECK(it == j_const.cend()); --it; CHECK(it == j_const.cbegin()); CHECK(it != j_const.cend()); CHECK(*it == j_const); } SECTION("json + rbegin/rend") { json::reverse_iterator it = j.rbegin(); CHECK(it != j.rend()); CHECK(*it == j); it++; CHECK(it != j.rbegin()); CHECK(it == j.rend()); it--; CHECK(it == j.rbegin()); CHECK(it != j.rend()); CHECK(*it == j); ++it; CHECK(it != j.rbegin()); CHECK(it == j.rend()); --it; CHECK(it == j.rbegin()); CHECK(it != j.rend()); CHECK(*it == j); } SECTION("json + crbegin/crend") { json::const_reverse_iterator it = j.crbegin(); CHECK(it != j.crend()); CHECK(*it == j); it++; CHECK(it != j.crbegin()); CHECK(it == j.crend()); it--; CHECK(it == j.crbegin()); CHECK(it != j.crend()); CHECK(*it == j); ++it; CHECK(it != j.crbegin()); CHECK(it == j.crend()); --it; CHECK(it == j.crbegin()); CHECK(it != j.crend()); CHECK(*it == j); } SECTION("const json + crbegin/crend") { json::const_reverse_iterator it = j_const.crbegin(); CHECK(it != j_const.crend()); CHECK(*it == j_const); it++; CHECK(it != j_const.crbegin()); CHECK(it == j_const.crend()); it--; CHECK(it == j_const.crbegin()); CHECK(it != j_const.crend()); CHECK(*it == j_const); ++it; CHECK(it != j_const.crbegin()); CHECK(it == j_const.crend()); --it; CHECK(it == j_const.crbegin()); CHECK(it != j_const.crend()); CHECK(*it == j_const); } SECTION("key/value") { auto it = j.begin(); auto cit = j_const.cbegin(); CHECK_THROWS_AS(it.key(), std::domain_error); CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); CHECK(it.value() == json("hello world")); CHECK_THROWS_AS(cit.key(), std::domain_error); CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); CHECK(cit.value() == json("hello world")); auto rit = j.rend(); auto crit = j.crend(); CHECK_THROWS_AS(rit.key(), std::domain_error); CHECK_THROWS_AS(rit.value(), std::out_of_range); CHECK_THROWS_AS(crit.key(), std::domain_error); CHECK_THROWS_AS(crit.value(), std::out_of_range); CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); CHECK_THROWS_WITH(rit.value(), "cannot get value"); CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); CHECK_THROWS_WITH(crit.value(), "cannot get value"); } } SECTION("array") { json j = {1, 2, 3}; json j_const(j); SECTION("json + begin/end") { json::iterator it_begin = j.begin(); json::iterator it_end = j.end(); auto it = it_begin; CHECK(it != it_end); CHECK(*it == j[0]); it++; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j[1]); ++it; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j[2]); ++it; CHECK(it != it_begin); CHECK(it == it_end); } SECTION("const json + begin/end") { json::const_iterator it_begin = j_const.begin(); json::const_iterator it_end = j_const.end(); auto it = it_begin; CHECK(it != it_end); CHECK(*it == j_const[0]); it++; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j_const[1]); ++it; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j_const[2]); ++it; CHECK(it != it_begin); CHECK(it == it_end); } SECTION("json + cbegin/cend") { json::const_iterator it_begin = j.cbegin(); json::const_iterator it_end = j.cend(); auto it = it_begin; CHECK(it != it_end); CHECK(*it == j[0]); it++; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j[1]); ++it; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j[2]); ++it; CHECK(it != it_begin); CHECK(it == it_end); } SECTION("const json + cbegin/cend") { json::const_iterator it_begin = j_const.cbegin(); json::const_iterator it_end = j_const.cend(); auto it = it_begin; CHECK(it != it_end); CHECK(*it == j[0]); it++; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j[1]); ++it; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j[2]); ++it; CHECK(it != it_begin); CHECK(it == it_end); } SECTION("json + rbegin/rend") { json::reverse_iterator it_begin = j.rbegin(); json::reverse_iterator it_end = j.rend(); auto it = it_begin; CHECK(it != it_end); CHECK(*it == j[2]); it++; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j[1]); ++it; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j[0]); ++it; CHECK(it != it_begin); CHECK(it == it_end); } SECTION("json + crbegin/crend") { json::const_reverse_iterator it_begin = j.crbegin(); json::const_reverse_iterator it_end = j.crend(); auto it = it_begin; CHECK(it != it_end); CHECK(*it == j[2]); it++; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j[1]); ++it; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j[0]); ++it; CHECK(it != it_begin); CHECK(it == it_end); } SECTION("const json + crbegin/crend") { json::const_reverse_iterator it_begin = j_const.crbegin(); json::const_reverse_iterator it_end = j_const.crend(); auto it = it_begin; CHECK(it != it_end); CHECK(*it == j[2]); it++; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j[1]); ++it; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j[0]); ++it; CHECK(it != it_begin); CHECK(it == it_end); } SECTION("key/value") { auto it = j.begin(); auto cit = j_const.cbegin(); CHECK_THROWS_AS(it.key(), std::domain_error); CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); CHECK(it.value() == json(1)); CHECK_THROWS_AS(cit.key(), std::domain_error); CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); CHECK(cit.value() == json(1)); } } SECTION("object") { json j = {{"A", 1}, {"B", 2}, {"C", 3}}; json j_const(j); SECTION("json + begin/end") { json::iterator it_begin = j.begin(); json::iterator it_end = j.end(); auto it = it_begin; CHECK(it != it_end); CHECK(*it == j["A"]); it++; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j["B"]); ++it; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j["C"]); ++it; CHECK(it != it_begin); CHECK(it == it_end); } SECTION("const json + begin/end") { json::const_iterator it_begin = j_const.begin(); json::const_iterator it_end = j_const.end(); auto it = it_begin; CHECK(it != it_end); CHECK(*it == j_const["A"]); it++; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j_const["B"]); ++it; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j_const["C"]); ++it; CHECK(it != it_begin); CHECK(it == it_end); } SECTION("json + cbegin/cend") { json::const_iterator it_begin = j.cbegin(); json::const_iterator it_end = j.cend(); auto it = it_begin; CHECK(it != it_end); CHECK(*it == j["A"]); it++; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j["B"]); ++it; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j["C"]); ++it; CHECK(it != it_begin); CHECK(it == it_end); } SECTION("const json + cbegin/cend") { json::const_iterator it_begin = j_const.cbegin(); json::const_iterator it_end = j_const.cend(); auto it = it_begin; CHECK(it != it_end); CHECK(*it == j_const["A"]); it++; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j_const["B"]); ++it; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j_const["C"]); ++it; CHECK(it != it_begin); CHECK(it == it_end); } SECTION("json + rbegin/rend") { json::reverse_iterator it_begin = j.rbegin(); json::reverse_iterator it_end = j.rend(); auto it = it_begin; CHECK(it != it_end); CHECK(*it == j["C"]); it++; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j["B"]); ++it; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j["A"]); ++it; CHECK(it != it_begin); CHECK(it == it_end); } SECTION("json + crbegin/crend") { json::const_reverse_iterator it_begin = j.crbegin(); json::const_reverse_iterator it_end = j.crend(); auto it = it_begin; CHECK(it != it_end); CHECK(*it == j["C"]); it++; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j["B"]); ++it; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j["A"]); ++it; CHECK(it != it_begin); CHECK(it == it_end); } SECTION("const json + crbegin/crend") { json::const_reverse_iterator it_begin = j_const.crbegin(); json::const_reverse_iterator it_end = j_const.crend(); auto it = it_begin; CHECK(it != it_end); CHECK(*it == j["C"]); it++; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j["B"]); ++it; CHECK(it != it_begin); CHECK(it != it_end); CHECK(*it == j["A"]); ++it; CHECK(it != it_begin); CHECK(it == it_end); } SECTION("key/value") { auto it = j.begin(); auto cit = j_const.cbegin(); CHECK(it.key() == "A"); CHECK(it.value() == json(1)); CHECK(cit.key() == "A"); CHECK(cit.value() == json(1)); } } SECTION("number (integer)") { json j = 23; json j_const(j); SECTION("json + begin/end") { json::iterator it = j.begin(); CHECK(it != j.end()); CHECK(*it == j); it++; CHECK(it != j.begin()); CHECK(it == j.end()); it--; CHECK(it == j.begin()); CHECK(it != j.end()); CHECK(*it == j); ++it; CHECK(it != j.begin()); CHECK(it == j.end()); --it; CHECK(it == j.begin()); CHECK(it != j.end()); CHECK(*it == j); } SECTION("const json + begin/end") { json::const_iterator it = j_const.begin(); CHECK(it != j_const.end()); CHECK(*it == j_const); it++; CHECK(it != j_const.begin()); CHECK(it == j_const.end()); it--; CHECK(it == j_const.begin()); CHECK(it != j_const.end()); CHECK(*it == j_const); ++it; CHECK(it != j_const.begin()); CHECK(it == j_const.end()); --it; CHECK(it == j_const.begin()); CHECK(it != j_const.end()); CHECK(*it == j_const); } SECTION("json + cbegin/cend") { json::const_iterator it = j.cbegin(); CHECK(it != j.cend()); CHECK(*it == j); it++; CHECK(it != j.cbegin()); CHECK(it == j.cend()); it--; CHECK(it == j.cbegin()); CHECK(it != j.cend()); CHECK(*it == j); ++it; CHECK(it != j.cbegin()); CHECK(it == j.cend()); --it; CHECK(it == j.cbegin()); CHECK(it != j.cend()); CHECK(*it == j); } SECTION("const json + cbegin/cend") { json::const_iterator it = j_const.cbegin(); CHECK(it != j_const.cend()); CHECK(*it == j_const); it++; CHECK(it != j_const.cbegin()); CHECK(it == j_const.cend()); it--; CHECK(it == j_const.cbegin()); CHECK(it != j_const.cend()); CHECK(*it == j_const); ++it; CHECK(it != j_const.cbegin()); CHECK(it == j_const.cend()); --it; CHECK(it == j_const.cbegin()); CHECK(it != j_const.cend()); CHECK(*it == j_const); } SECTION("json + rbegin/rend") { json::reverse_iterator it = j.rbegin(); CHECK(it != j.rend()); CHECK(*it == j); it++; CHECK(it != j.rbegin()); CHECK(it == j.rend()); it--; CHECK(it == j.rbegin()); CHECK(it != j.rend()); CHECK(*it == j); ++it; CHECK(it != j.rbegin()); CHECK(it == j.rend()); --it; CHECK(it == j.rbegin()); CHECK(it != j.rend()); CHECK(*it == j); } SECTION("json + crbegin/crend") { json::const_reverse_iterator it = j.crbegin(); CHECK(it != j.crend()); CHECK(*it == j); it++; CHECK(it != j.crbegin()); CHECK(it == j.crend()); it--; CHECK(it == j.crbegin()); CHECK(it != j.crend()); CHECK(*it == j); ++it; CHECK(it != j.crbegin()); CHECK(it == j.crend()); --it; CHECK(it == j.crbegin()); CHECK(it != j.crend()); CHECK(*it == j); } SECTION("const json + crbegin/crend") { json::const_reverse_iterator it = j_const.crbegin(); CHECK(it != j_const.crend()); CHECK(*it == j_const); it++; CHECK(it != j_const.crbegin()); CHECK(it == j_const.crend()); it--; CHECK(it == j_const.crbegin()); CHECK(it != j_const.crend()); CHECK(*it == j_const); ++it; CHECK(it != j_const.crbegin()); CHECK(it == j_const.crend()); --it; CHECK(it == j_const.crbegin()); CHECK(it != j_const.crend()); CHECK(*it == j_const); } SECTION("key/value") { auto it = j.begin(); auto cit = j_const.cbegin(); CHECK_THROWS_AS(it.key(), std::domain_error); CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); CHECK(it.value() == json(23)); CHECK_THROWS_AS(cit.key(), std::domain_error); CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); CHECK(cit.value() == json(23)); auto rit = j.rend(); auto crit = j.crend(); CHECK_THROWS_AS(rit.key(), std::domain_error); CHECK_THROWS_AS(rit.value(), std::out_of_range); CHECK_THROWS_AS(crit.key(), std::domain_error); CHECK_THROWS_AS(crit.value(), std::out_of_range); CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); CHECK_THROWS_WITH(rit.value(), "cannot get value"); CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); CHECK_THROWS_WITH(crit.value(), "cannot get value"); } } SECTION("number (unsigned)") { json j = 23u; json j_const(j); SECTION("json + begin/end") { json::iterator it = j.begin(); CHECK(it != j.end()); CHECK(*it == j); it++; CHECK(it != j.begin()); CHECK(it == j.end()); it--; CHECK(it == j.begin()); CHECK(it != j.end()); CHECK(*it == j); ++it; CHECK(it != j.begin()); CHECK(it == j.end()); --it; CHECK(it == j.begin()); CHECK(it != j.end()); CHECK(*it == j); } SECTION("const json + begin/end") { json::const_iterator it = j_const.begin(); CHECK(it != j_const.end()); CHECK(*it == j_const); it++; CHECK(it != j_const.begin()); CHECK(it == j_const.end()); it--; CHECK(it == j_const.begin()); CHECK(it != j_const.end()); CHECK(*it == j_const); ++it; CHECK(it != j_const.begin()); CHECK(it == j_const.end()); --it; CHECK(it == j_const.begin()); CHECK(it != j_const.end()); CHECK(*it == j_const); } SECTION("json + cbegin/cend") { json::const_iterator it = j.cbegin(); CHECK(it != j.cend()); CHECK(*it == j); it++; CHECK(it != j.cbegin()); CHECK(it == j.cend()); it--; CHECK(it == j.cbegin()); CHECK(it != j.cend()); CHECK(*it == j); ++it; CHECK(it != j.cbegin()); CHECK(it == j.cend()); --it; CHECK(it == j.cbegin()); CHECK(it != j.cend()); CHECK(*it == j); } SECTION("const json + cbegin/cend") { json::const_iterator it = j_const.cbegin(); CHECK(it != j_const.cend()); CHECK(*it == j_const); it++; CHECK(it != j_const.cbegin()); CHECK(it == j_const.cend()); it--; CHECK(it == j_const.cbegin()); CHECK(it != j_const.cend()); CHECK(*it == j_const); ++it; CHECK(it != j_const.cbegin()); CHECK(it == j_const.cend()); --it; CHECK(it == j_const.cbegin()); CHECK(it != j_const.cend()); CHECK(*it == j_const); } SECTION("json + rbegin/rend") { json::reverse_iterator it = j.rbegin(); CHECK(it != j.rend()); CHECK(*it == j); it++; CHECK(it != j.rbegin()); CHECK(it == j.rend()); it--; CHECK(it == j.rbegin()); CHECK(it != j.rend()); CHECK(*it == j); ++it; CHECK(it != j.rbegin()); CHECK(it == j.rend()); --it; CHECK(it == j.rbegin()); CHECK(it != j.rend()); CHECK(*it == j); } SECTION("json + crbegin/crend") { json::const_reverse_iterator it = j.crbegin(); CHECK(it != j.crend()); CHECK(*it == j); it++; CHECK(it != j.crbegin()); CHECK(it == j.crend()); it--; CHECK(it == j.crbegin()); CHECK(it != j.crend()); CHECK(*it == j); ++it; CHECK(it != j.crbegin()); CHECK(it == j.crend()); --it; CHECK(it == j.crbegin()); CHECK(it != j.crend()); CHECK(*it == j); } SECTION("const json + crbegin/crend") { json::const_reverse_iterator it = j_const.crbegin(); CHECK(it != j_const.crend()); CHECK(*it == j_const); it++; CHECK(it != j_const.crbegin()); CHECK(it == j_const.crend()); it--; CHECK(it == j_const.crbegin()); CHECK(it != j_const.crend()); CHECK(*it == j_const); ++it; CHECK(it != j_const.crbegin()); CHECK(it == j_const.crend()); --it; CHECK(it == j_const.crbegin()); CHECK(it != j_const.crend()); CHECK(*it == j_const); } SECTION("key/value") { auto it = j.begin(); auto cit = j_const.cbegin(); CHECK_THROWS_AS(it.key(), std::domain_error); CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); CHECK(it.value() == json(23)); CHECK_THROWS_AS(cit.key(), std::domain_error); CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); CHECK(cit.value() == json(23)); auto rit = j.rend(); auto crit = j.crend(); CHECK_THROWS_AS(rit.key(), std::domain_error); CHECK_THROWS_AS(rit.value(), std::out_of_range); CHECK_THROWS_AS(crit.key(), std::domain_error); CHECK_THROWS_AS(crit.value(), std::out_of_range); CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); CHECK_THROWS_WITH(rit.value(), "cannot get value"); CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); CHECK_THROWS_WITH(crit.value(), "cannot get value"); } } SECTION("number (float)") { json j = 23.42; json j_const(j); SECTION("json + begin/end") { json::iterator it = j.begin(); CHECK(it != j.end()); CHECK(*it == j); it++; CHECK(it != j.begin()); CHECK(it == j.end()); it--; CHECK(it == j.begin()); CHECK(it != j.end()); CHECK(*it == j); ++it; CHECK(it != j.begin()); CHECK(it == j.end()); --it; CHECK(it == j.begin()); CHECK(it != j.end()); CHECK(*it == j); } SECTION("const json + begin/end") { json::const_iterator it = j_const.begin(); CHECK(it != j_const.end()); CHECK(*it == j_const); it++; CHECK(it != j_const.begin()); CHECK(it == j_const.end()); it--; CHECK(it == j_const.begin()); CHECK(it != j_const.end()); CHECK(*it == j_const); ++it; CHECK(it != j_const.begin()); CHECK(it == j_const.end()); --it; CHECK(it == j_const.begin()); CHECK(it != j_const.end()); CHECK(*it == j_const); } SECTION("json + cbegin/cend") { json::const_iterator it = j.cbegin(); CHECK(it != j.cend()); CHECK(*it == j); it++; CHECK(it != j.cbegin()); CHECK(it == j.cend()); it--; CHECK(it == j.cbegin()); CHECK(it != j.cend()); CHECK(*it == j); ++it; CHECK(it != j.cbegin()); CHECK(it == j.cend()); --it; CHECK(it == j.cbegin()); CHECK(it != j.cend()); CHECK(*it == j); } SECTION("const json + cbegin/cend") { json::const_iterator it = j_const.cbegin(); CHECK(it != j_const.cend()); CHECK(*it == j_const); it++; CHECK(it != j_const.cbegin()); CHECK(it == j_const.cend()); it--; CHECK(it == j_const.cbegin()); CHECK(it != j_const.cend()); CHECK(*it == j_const); ++it; CHECK(it != j_const.cbegin()); CHECK(it == j_const.cend()); --it; CHECK(it == j_const.cbegin()); CHECK(it != j_const.cend()); CHECK(*it == j_const); } SECTION("json + rbegin/rend") { json::reverse_iterator it = j.rbegin(); CHECK(it != j.rend()); CHECK(*it == j); it++; CHECK(it != j.rbegin()); CHECK(it == j.rend()); it--; CHECK(it == j.rbegin()); CHECK(it != j.rend()); CHECK(*it == j); ++it; CHECK(it != j.rbegin()); CHECK(it == j.rend()); --it; CHECK(it == j.rbegin()); CHECK(it != j.rend()); CHECK(*it == j); } SECTION("json + crbegin/crend") { json::const_reverse_iterator it = j.crbegin(); CHECK(it != j.crend()); CHECK(*it == j); it++; CHECK(it != j.crbegin()); CHECK(it == j.crend()); it--; CHECK(it == j.crbegin()); CHECK(it != j.crend()); CHECK(*it == j); ++it; CHECK(it != j.crbegin()); CHECK(it == j.crend()); --it; CHECK(it == j.crbegin()); CHECK(it != j.crend()); CHECK(*it == j); } SECTION("const json + crbegin/crend") { json::const_reverse_iterator it = j_const.crbegin(); CHECK(it != j_const.crend()); CHECK(*it == j_const); it++; CHECK(it != j_const.crbegin()); CHECK(it == j_const.crend()); it--; CHECK(it == j_const.crbegin()); CHECK(it != j_const.crend()); CHECK(*it == j_const); ++it; CHECK(it != j_const.crbegin()); CHECK(it == j_const.crend()); --it; CHECK(it == j_const.crbegin()); CHECK(it != j_const.crend()); CHECK(*it == j_const); } SECTION("key/value") { auto it = j.begin(); auto cit = j_const.cbegin(); CHECK_THROWS_AS(it.key(), std::domain_error); CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); CHECK(it.value() == json(23.42)); CHECK_THROWS_AS(cit.key(), std::domain_error); CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); CHECK(cit.value() == json(23.42)); auto rit = j.rend(); auto crit = j.crend(); CHECK_THROWS_AS(rit.key(), std::domain_error); CHECK_THROWS_AS(rit.value(), std::out_of_range); CHECK_THROWS_AS(crit.key(), std::domain_error); CHECK_THROWS_AS(crit.value(), std::out_of_range); CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); CHECK_THROWS_WITH(rit.value(), "cannot get value"); CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); CHECK_THROWS_WITH(crit.value(), "cannot get value"); } } SECTION("null") { json j = nullptr; json j_const(j); SECTION("json + begin/end") { json::iterator it = j.begin(); CHECK(it == j.end()); } SECTION("const json + begin/end") { json::const_iterator it_begin = j_const.begin(); json::const_iterator it_end = j_const.end(); CHECK(it_begin == it_end); } SECTION("json + cbegin/cend") { json::const_iterator it_begin = j.cbegin(); json::const_iterator it_end = j.cend(); CHECK(it_begin == it_end); } SECTION("const json + cbegin/cend") { json::const_iterator it_begin = j_const.cbegin(); json::const_iterator it_end = j_const.cend(); CHECK(it_begin == it_end); } SECTION("json + rbegin/rend") { json::reverse_iterator it = j.rbegin(); CHECK(it == j.rend()); } SECTION("json + crbegin/crend") { json::const_reverse_iterator it = j.crbegin(); CHECK(it == j.crend()); } SECTION("const json + crbegin/crend") { json::const_reverse_iterator it = j_const.crbegin(); CHECK(it == j_const.crend()); } SECTION("key/value") { auto it = j.begin(); auto cit = j_const.cbegin(); CHECK_THROWS_AS(it.key(), std::domain_error); CHECK_THROWS_AS(it.value(), std::out_of_range); CHECK_THROWS_AS(cit.key(), std::domain_error); CHECK_THROWS_AS(cit.value(), std::out_of_range); CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators"); CHECK_THROWS_WITH(it.value(), "cannot get value"); CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators"); CHECK_THROWS_WITH(cit.value(), "cannot get value"); auto rit = j.rend(); auto crit = j.crend(); CHECK_THROWS_AS(rit.key(), std::domain_error); CHECK_THROWS_AS(rit.value(), std::out_of_range); CHECK_THROWS_AS(crit.key(), std::domain_error); CHECK_THROWS_AS(crit.value(), std::out_of_range); CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators"); CHECK_THROWS_WITH(rit.value(), "cannot get value"); CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators"); CHECK_THROWS_WITH(crit.value(), "cannot get value"); } } } SECTION("iterator comparisons") { json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"}; for (json& j : j_values) { auto it1 = j.begin(); auto it2 = j.begin(); auto it3 = j.begin(); ++it2; ++it3; ++it3; auto it1_c = j.cbegin(); auto it2_c = j.cbegin(); auto it3_c = j.cbegin(); ++it2_c; ++it3_c; ++it3_c; // comparison: equal { CHECK(it1 == it1); CHECK(not (it1 == it2)); CHECK(not (it1 == it3)); CHECK(not (it2 == it3)); CHECK(it1_c == it1_c); CHECK(not (it1_c == it2_c)); CHECK(not (it1_c == it3_c)); CHECK(not (it2_c == it3_c)); } // comparison: not equal { // check definition CHECK( (it1 != it1) == not(it1 == it1) ); CHECK( (it1 != it2) == not(it1 == it2) ); CHECK( (it1 != it3) == not(it1 == it3) ); CHECK( (it2 != it3) == not(it2 == it3) ); CHECK( (it1_c != it1_c) == not(it1_c == it1_c) ); CHECK( (it1_c != it2_c) == not(it1_c == it2_c) ); CHECK( (it1_c != it3_c) == not(it1_c == it3_c) ); CHECK( (it2_c != it3_c) == not(it2_c == it3_c) ); } // comparison: smaller { if (j.type() == json::value_t::object) { CHECK_THROWS_AS(it1 < it1, std::domain_error); CHECK_THROWS_AS(it1 < it2, std::domain_error); CHECK_THROWS_AS(it2 < it3, std::domain_error); CHECK_THROWS_AS(it1 < it3, std::domain_error); CHECK_THROWS_AS(it1_c < it1_c, std::domain_error); CHECK_THROWS_AS(it1_c < it2_c, std::domain_error); CHECK_THROWS_AS(it2_c < it3_c, std::domain_error); CHECK_THROWS_AS(it1_c < it3_c, std::domain_error); CHECK_THROWS_WITH(it1 < it1, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 < it2, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it2 < it3, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 < it3, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c < it1_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c < it2_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it2_c < it3_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c < it3_c, "cannot compare order of object iterators"); } else { CHECK(not (it1 < it1)); CHECK(it1 < it2); CHECK(it1 < it3); CHECK(it2 < it3); CHECK(not (it1_c < it1_c)); CHECK(it1_c < it2_c); CHECK(it1_c < it3_c); CHECK(it2_c < it3_c); } } // comparison: less than or equal { if (j.type() == json::value_t::object) { CHECK_THROWS_AS(it1 <= it1, std::domain_error); CHECK_THROWS_AS(it1 <= it2, std::domain_error); CHECK_THROWS_AS(it2 <= it3, std::domain_error); CHECK_THROWS_AS(it1 <= it3, std::domain_error); CHECK_THROWS_AS(it1_c <= it1_c, std::domain_error); CHECK_THROWS_AS(it1_c <= it2_c, std::domain_error); CHECK_THROWS_AS(it2_c <= it3_c, std::domain_error); CHECK_THROWS_AS(it1_c <= it3_c, std::domain_error); CHECK_THROWS_WITH(it1 <= it1, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 <= it2, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it2 <= it3, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 <= it3, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c <= it1_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c <= it2_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it2_c <= it3_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c <= it3_c, "cannot compare order of object iterators"); } else { // check definition CHECK( (it1 <= it1) == not(it1 < it1) ); CHECK( (it1 <= it2) == not(it2 < it1) ); CHECK( (it1 <= it3) == not(it3 < it1) ); CHECK( (it2 <= it3) == not(it3 < it2) ); CHECK( (it1_c <= it1_c) == not(it1_c < it1_c) ); CHECK( (it1_c <= it2_c) == not(it2_c < it1_c) ); CHECK( (it1_c <= it3_c) == not(it3_c < it1_c) ); CHECK( (it2_c <= it3_c) == not(it3_c < it2_c) ); } } // comparison: greater than { if (j.type() == json::value_t::object) { CHECK_THROWS_AS(it1 > it1, std::domain_error); CHECK_THROWS_AS(it1 > it2, std::domain_error); CHECK_THROWS_AS(it2 > it3, std::domain_error); CHECK_THROWS_AS(it1 > it3, std::domain_error); CHECK_THROWS_AS(it1_c > it1_c, std::domain_error); CHECK_THROWS_AS(it1_c > it2_c, std::domain_error); CHECK_THROWS_AS(it2_c > it3_c, std::domain_error); CHECK_THROWS_AS(it1_c > it3_c, std::domain_error); CHECK_THROWS_WITH(it1 > it1, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 > it2, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it2 > it3, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 > it3, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c > it1_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c > it2_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it2_c > it3_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c > it3_c, "cannot compare order of object iterators"); } else { // check definition CHECK( (it1 > it1) == (it1 < it1) ); CHECK( (it1 > it2) == (it2 < it1) ); CHECK( (it1 > it3) == (it3 < it1) ); CHECK( (it2 > it3) == (it3 < it2) ); CHECK( (it1_c > it1_c) == (it1_c < it1_c) ); CHECK( (it1_c > it2_c) == (it2_c < it1_c) ); CHECK( (it1_c > it3_c) == (it3_c < it1_c) ); CHECK( (it2_c > it3_c) == (it3_c < it2_c) ); } } // comparison: greater than or equal { if (j.type() == json::value_t::object) { CHECK_THROWS_AS(it1 >= it1, std::domain_error); CHECK_THROWS_AS(it1 >= it2, std::domain_error); CHECK_THROWS_AS(it2 >= it3, std::domain_error); CHECK_THROWS_AS(it1 >= it3, std::domain_error); CHECK_THROWS_AS(it1_c >= it1_c, std::domain_error); CHECK_THROWS_AS(it1_c >= it2_c, std::domain_error); CHECK_THROWS_AS(it2_c >= it3_c, std::domain_error); CHECK_THROWS_AS(it1_c >= it3_c, std::domain_error); CHECK_THROWS_WITH(it1 >= it1, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 >= it2, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it2 >= it3, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 >= it3, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c >= it1_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c >= it2_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it2_c >= it3_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c >= it3_c, "cannot compare order of object iterators"); } else { // check definition CHECK( (it1 >= it1) == not(it1 < it1) ); CHECK( (it1 >= it2) == not(it1 < it2) ); CHECK( (it1 >= it3) == not(it1 < it3) ); CHECK( (it2 >= it3) == not(it2 < it3) ); CHECK( (it1_c >= it1_c) == not(it1_c < it1_c) ); CHECK( (it1_c >= it2_c) == not(it1_c < it2_c) ); CHECK( (it1_c >= it3_c) == not(it1_c < it3_c) ); CHECK( (it2_c >= it3_c) == not(it2_c < it3_c) ); } } } // check exceptions if different objects are compared for (auto j : j_values) { for (auto k : j_values) { if (j != k) { CHECK_THROWS_AS(j.begin() == k.begin(), std::domain_error); CHECK_THROWS_AS(j.cbegin() == k.cbegin(), std::domain_error); CHECK_THROWS_WITH(j.begin() == k.begin(), "cannot compare iterators of different containers"); CHECK_THROWS_WITH(j.cbegin() == k.cbegin(), "cannot compare iterators of different containers"); CHECK_THROWS_AS(j.begin() < k.begin(), std::domain_error); CHECK_THROWS_AS(j.cbegin() < k.cbegin(), std::domain_error); CHECK_THROWS_WITH(j.begin() < k.begin(), "cannot compare iterators of different containers"); CHECK_THROWS_WITH(j.cbegin() < k.cbegin(), "cannot compare iterators of different containers"); } } } } SECTION("iterator arithmetic") { json j_object = {{"one", 1}, {"two", 2}, {"three", 3}}; json j_array = {1, 2, 3, 4, 5, 6}; json j_null = nullptr; json j_value = 42; SECTION("addition and subtraction") { SECTION("object") { { auto it = j_object.begin(); CHECK_THROWS_AS(it += 1, std::domain_error); CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); } { auto it = j_object.cbegin(); CHECK_THROWS_AS(it += 1, std::domain_error); CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); } { auto it = j_object.begin(); CHECK_THROWS_AS(it + 1, std::domain_error); CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); } { auto it = j_object.cbegin(); CHECK_THROWS_AS(it + 1, std::domain_error); CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); } { auto it = j_object.begin(); CHECK_THROWS_AS(it -= 1, std::domain_error); CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); } { auto it = j_object.cbegin(); CHECK_THROWS_AS(it -= 1, std::domain_error); CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); } { auto it = j_object.begin(); CHECK_THROWS_AS(it - 1, std::domain_error); CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); } { auto it = j_object.cbegin(); CHECK_THROWS_AS(it - 1, std::domain_error); CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); } { auto it = j_object.begin(); CHECK_THROWS_AS(it - it, std::domain_error); CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); } { auto it = j_object.cbegin(); CHECK_THROWS_AS(it - it, std::domain_error); CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); } } SECTION("array") { { auto it = j_array.begin(); it += 3; CHECK((j_array.begin() + 3) == it); CHECK((it - 3) == j_array.begin()); CHECK((it - j_array.begin()) == 3); CHECK(*it == json(4)); it -= 2; CHECK(*it == json(2)); } { auto it = j_array.cbegin(); it += 3; CHECK((j_array.cbegin() + 3) == it); CHECK((it - 3) == j_array.cbegin()); CHECK((it - j_array.cbegin()) == 3); CHECK(*it == json(4)); it -= 2; CHECK(*it == json(2)); } } SECTION("null") { { auto it = j_null.begin(); it += 3; CHECK((j_null.begin() + 3) == it); CHECK((it - 3) == j_null.begin()); CHECK((it - j_null.begin()) == 3); CHECK(it != j_null.end()); it -= 3; CHECK(it == j_null.end()); } { auto it = j_null.cbegin(); it += 3; CHECK((j_null.cbegin() + 3) == it); CHECK((it - 3) == j_null.cbegin()); CHECK((it - j_null.cbegin()) == 3); CHECK(it != j_null.cend()); it -= 3; CHECK(it == j_null.cend()); } } SECTION("value") { { auto it = j_value.begin(); it += 3; CHECK((j_value.begin() + 3) == it); CHECK((it - 3) == j_value.begin()); CHECK((it - j_value.begin()) == 3); CHECK(it != j_value.end()); it -= 3; CHECK(*it == json(42)); } { auto it = j_value.cbegin(); it += 3; CHECK((j_value.cbegin() + 3) == it); CHECK((it - 3) == j_value.cbegin()); CHECK((it - j_value.cbegin()) == 3); CHECK(it != j_value.cend()); it -= 3; CHECK(*it == json(42)); } } } SECTION("subscript operator") { SECTION("object") { { auto it = j_object.begin(); CHECK_THROWS_AS(it[0], std::domain_error); CHECK_THROWS_AS(it[1], std::domain_error); CHECK_THROWS_WITH(it[0], "cannot use operator[] for object iterators"); CHECK_THROWS_WITH(it[1], "cannot use operator[] for object iterators"); } { auto it = j_object.cbegin(); CHECK_THROWS_AS(it[0], std::domain_error); CHECK_THROWS_AS(it[1], std::domain_error); CHECK_THROWS_WITH(it[0], "cannot use operator[] for object iterators"); CHECK_THROWS_WITH(it[1], "cannot use operator[] for object iterators"); } } SECTION("array") { { auto it = j_array.begin(); CHECK(it[0] == json(1)); CHECK(it[1] == json(2)); CHECK(it[2] == json(3)); CHECK(it[3] == json(4)); CHECK(it[4] == json(5)); CHECK(it[5] == json(6)); } { auto it = j_array.cbegin(); CHECK(it[0] == json(1)); CHECK(it[1] == json(2)); CHECK(it[2] == json(3)); CHECK(it[3] == json(4)); CHECK(it[4] == json(5)); CHECK(it[5] == json(6)); } } SECTION("null") { { auto it = j_null.begin(); CHECK_THROWS_AS(it[0], std::out_of_range); CHECK_THROWS_AS(it[1], std::out_of_range); CHECK_THROWS_WITH(it[0], "cannot get value"); CHECK_THROWS_WITH(it[1], "cannot get value"); } { auto it = j_null.cbegin(); CHECK_THROWS_AS(it[0], std::out_of_range); CHECK_THROWS_AS(it[1], std::out_of_range); CHECK_THROWS_WITH(it[0], "cannot get value"); CHECK_THROWS_WITH(it[1], "cannot get value"); } } SECTION("value") { { auto it = j_value.begin(); CHECK(it[0] == json(42)); CHECK_THROWS_AS(it[1], std::out_of_range); CHECK_THROWS_WITH(it[1], "cannot get value"); } { auto it = j_value.cbegin(); CHECK(it[0] == json(42)); CHECK_THROWS_AS(it[1], std::out_of_range); CHECK_THROWS_WITH(it[1], "cannot get value"); } } } } SECTION("reverse iterator comparisons") { json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"}; for (json& j : j_values) { auto it1 = j.rbegin(); auto it2 = j.rbegin(); auto it3 = j.rbegin(); ++it2; ++it3; ++it3; auto it1_c = j.crbegin(); auto it2_c = j.crbegin(); auto it3_c = j.crbegin(); ++it2_c; ++it3_c; ++it3_c; // comparison: equal { CHECK(it1 == it1); CHECK(not (it1 == it2)); CHECK(not (it1 == it3)); CHECK(not (it2 == it3)); CHECK(it1_c == it1_c); CHECK(not (it1_c == it2_c)); CHECK(not (it1_c == it3_c)); CHECK(not (it2_c == it3_c)); } // comparison: not equal { // check definition CHECK( (it1 != it1) == not(it1 == it1) ); CHECK( (it1 != it2) == not(it1 == it2) ); CHECK( (it1 != it3) == not(it1 == it3) ); CHECK( (it2 != it3) == not(it2 == it3) ); CHECK( (it1_c != it1_c) == not(it1_c == it1_c) ); CHECK( (it1_c != it2_c) == not(it1_c == it2_c) ); CHECK( (it1_c != it3_c) == not(it1_c == it3_c) ); CHECK( (it2_c != it3_c) == not(it2_c == it3_c) ); } // comparison: smaller { if (j.type() == json::value_t::object) { CHECK_THROWS_AS(it1 < it1, std::domain_error); CHECK_THROWS_AS(it1 < it2, std::domain_error); CHECK_THROWS_AS(it2 < it3, std::domain_error); CHECK_THROWS_AS(it1 < it3, std::domain_error); CHECK_THROWS_AS(it1_c < it1_c, std::domain_error); CHECK_THROWS_AS(it1_c < it2_c, std::domain_error); CHECK_THROWS_AS(it2_c < it3_c, std::domain_error); CHECK_THROWS_AS(it1_c < it3_c, std::domain_error); CHECK_THROWS_WITH(it1 < it1, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 < it2, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it2 < it3, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 < it3, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c < it1_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c < it2_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it2_c < it3_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c < it3_c, "cannot compare order of object iterators"); } else { CHECK(not (it1 < it1)); CHECK(it1 < it2); CHECK(it1 < it3); CHECK(it2 < it3); CHECK(not (it1_c < it1_c)); CHECK(it1_c < it2_c); CHECK(it1_c < it3_c); CHECK(it2_c < it3_c); } } // comparison: less than or equal { if (j.type() == json::value_t::object) { CHECK_THROWS_AS(it1 <= it1, std::domain_error); CHECK_THROWS_AS(it1 <= it2, std::domain_error); CHECK_THROWS_AS(it2 <= it3, std::domain_error); CHECK_THROWS_AS(it1 <= it3, std::domain_error); CHECK_THROWS_AS(it1_c <= it1_c, std::domain_error); CHECK_THROWS_AS(it1_c <= it2_c, std::domain_error); CHECK_THROWS_AS(it2_c <= it3_c, std::domain_error); CHECK_THROWS_AS(it1_c <= it3_c, std::domain_error); CHECK_THROWS_WITH(it1 <= it1, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 <= it2, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it2 <= it3, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 <= it3, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c <= it1_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c <= it2_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it2_c <= it3_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c <= it3_c, "cannot compare order of object iterators"); } else { // check definition CHECK( (it1 <= it1) == not(it1 < it1) ); CHECK( (it1 <= it2) == not(it2 < it1) ); CHECK( (it1 <= it3) == not(it3 < it1) ); CHECK( (it2 <= it3) == not(it3 < it2) ); CHECK( (it1_c <= it1_c) == not(it1_c < it1_c) ); CHECK( (it1_c <= it2_c) == not(it2_c < it1_c) ); CHECK( (it1_c <= it3_c) == not(it3_c < it1_c) ); CHECK( (it2_c <= it3_c) == not(it3_c < it2_c) ); } } // comparison: greater than { if (j.type() == json::value_t::object) { CHECK_THROWS_AS(it1 > it1, std::domain_error); CHECK_THROWS_AS(it1 > it2, std::domain_error); CHECK_THROWS_AS(it2 > it3, std::domain_error); CHECK_THROWS_AS(it1 > it3, std::domain_error); CHECK_THROWS_AS(it1_c > it1_c, std::domain_error); CHECK_THROWS_AS(it1_c > it2_c, std::domain_error); CHECK_THROWS_AS(it2_c > it3_c, std::domain_error); CHECK_THROWS_AS(it1_c > it3_c, std::domain_error); CHECK_THROWS_WITH(it1 > it1, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 > it2, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it2 > it3, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 > it3, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c > it1_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c > it2_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it2_c > it3_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c > it3_c, "cannot compare order of object iterators"); } else { // check definition CHECK( (it1 > it1) == (it1 < it1) ); CHECK( (it1 > it2) == (it2 < it1) ); CHECK( (it1 > it3) == (it3 < it1) ); CHECK( (it2 > it3) == (it3 < it2) ); CHECK( (it1_c > it1_c) == (it1_c < it1_c) ); CHECK( (it1_c > it2_c) == (it2_c < it1_c) ); CHECK( (it1_c > it3_c) == (it3_c < it1_c) ); CHECK( (it2_c > it3_c) == (it3_c < it2_c) ); } } // comparison: greater than or equal { if (j.type() == json::value_t::object) { CHECK_THROWS_AS(it1 >= it1, std::domain_error); CHECK_THROWS_AS(it1 >= it2, std::domain_error); CHECK_THROWS_AS(it2 >= it3, std::domain_error); CHECK_THROWS_AS(it1 >= it3, std::domain_error); CHECK_THROWS_AS(it1_c >= it1_c, std::domain_error); CHECK_THROWS_AS(it1_c >= it2_c, std::domain_error); CHECK_THROWS_AS(it2_c >= it3_c, std::domain_error); CHECK_THROWS_AS(it1_c >= it3_c, std::domain_error); CHECK_THROWS_WITH(it1 >= it1, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 >= it2, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it2 >= it3, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1 >= it3, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c >= it1_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c >= it2_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it2_c >= it3_c, "cannot compare order of object iterators"); CHECK_THROWS_WITH(it1_c >= it3_c, "cannot compare order of object iterators"); } else { // check definition CHECK( (it1 >= it1) == not(it1 < it1) ); CHECK( (it1 >= it2) == not(it1 < it2) ); CHECK( (it1 >= it3) == not(it1 < it3) ); CHECK( (it2 >= it3) == not(it2 < it3) ); CHECK( (it1_c >= it1_c) == not(it1_c < it1_c) ); CHECK( (it1_c >= it2_c) == not(it1_c < it2_c) ); CHECK( (it1_c >= it3_c) == not(it1_c < it3_c) ); CHECK( (it2_c >= it3_c) == not(it2_c < it3_c) ); } } } // check exceptions if different objects are compared for (auto j : j_values) { for (auto k : j_values) { if (j != k) { CHECK_THROWS_AS(j.rbegin() == k.rbegin(), std::domain_error); CHECK_THROWS_AS(j.crbegin() == k.crbegin(), std::domain_error); CHECK_THROWS_WITH(j.rbegin() == k.rbegin(), "cannot compare iterators of different containers"); CHECK_THROWS_WITH(j.crbegin() == k.crbegin(), "cannot compare iterators of different containers"); CHECK_THROWS_AS(j.rbegin() < k.rbegin(), std::domain_error); CHECK_THROWS_AS(j.crbegin() < k.crbegin(), std::domain_error); CHECK_THROWS_WITH(j.rbegin() < k.rbegin(), "cannot compare iterators of different containers"); CHECK_THROWS_WITH(j.crbegin() < k.crbegin(), "cannot compare iterators of different containers"); } } } } SECTION("reverse iterator arithmetic") { json j_object = {{"one", 1}, {"two", 2}, {"three", 3}}; json j_array = {1, 2, 3, 4, 5, 6}; json j_null = nullptr; json j_value = 42; SECTION("addition and subtraction") { SECTION("object") { { auto it = j_object.rbegin(); CHECK_THROWS_AS(it += 1, std::domain_error); CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); } { auto it = j_object.crbegin(); CHECK_THROWS_AS(it += 1, std::domain_error); CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators"); } { auto it = j_object.rbegin(); CHECK_THROWS_AS(it + 1, std::domain_error); CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); } { auto it = j_object.crbegin(); CHECK_THROWS_AS(it + 1, std::domain_error); CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators"); } { auto it = j_object.rbegin(); CHECK_THROWS_AS(it -= 1, std::domain_error); CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); } { auto it = j_object.crbegin(); CHECK_THROWS_AS(it -= 1, std::domain_error); CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators"); } { auto it = j_object.rbegin(); CHECK_THROWS_AS(it - 1, std::domain_error); CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); } { auto it = j_object.crbegin(); CHECK_THROWS_AS(it - 1, std::domain_error); CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators"); } { auto it = j_object.rbegin(); CHECK_THROWS_AS(it - it, std::domain_error); CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); } { auto it = j_object.crbegin(); CHECK_THROWS_AS(it - it, std::domain_error); CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators"); } } SECTION("array") { { auto it = j_array.rbegin(); it += 3; CHECK((j_array.rbegin() + 3) == it); CHECK((it - 3) == j_array.rbegin()); CHECK((j_array.rbegin() - it) == 3); CHECK(*it == json(3)); it -= 2; CHECK(*it == json(5)); } { auto it = j_array.crbegin(); it += 3; CHECK((j_array.crbegin() + 3) == it); CHECK((it - 3) == j_array.crbegin()); CHECK((j_array.crbegin() - it) == 3); CHECK(*it == json(3)); it -= 2; CHECK(*it == json(5)); } } SECTION("null") { { auto it = j_null.rbegin(); it += 3; CHECK((j_null.rbegin() + 3) == it); CHECK((it - 3) == j_null.rbegin()); CHECK((j_null.rbegin() - it) == 3); CHECK(it != j_null.rend()); it -= 3; CHECK(it == j_null.rend()); } { auto it = j_null.crbegin(); it += 3; CHECK((j_null.crbegin() + 3) == it); CHECK((it - 3) == j_null.crbegin()); CHECK((j_null.crbegin() - it) == 3); CHECK(it != j_null.crend()); it -= 3; CHECK(it == j_null.crend()); } } SECTION("value") { { auto it = j_value.rbegin(); it += 3; CHECK((j_value.rbegin() + 3) == it); CHECK((it - 3) == j_value.rbegin()); CHECK((j_value.rbegin() - it) == 3); CHECK(it != j_value.rend()); it -= 3; CHECK(*it == json(42)); } { auto it = j_value.crbegin(); it += 3; CHECK((j_value.crbegin() + 3) == it); CHECK((it - 3) == j_value.crbegin()); CHECK((j_value.crbegin() - it) == 3); CHECK(it != j_value.crend()); it -= 3; CHECK(*it == json(42)); } } } SECTION("subscript operator") { SECTION("object") { { auto it = j_object.rbegin(); CHECK_THROWS_AS(it[0], std::domain_error); CHECK_THROWS_AS(it[1], std::domain_error); CHECK_THROWS_WITH(it[0], "cannot use offsets with object iterators"); CHECK_THROWS_WITH(it[1], "cannot use offsets with object iterators"); } { auto it = j_object.crbegin(); CHECK_THROWS_AS(it[0], std::domain_error); CHECK_THROWS_AS(it[1], std::domain_error); CHECK_THROWS_WITH(it[0], "cannot use offsets with object iterators"); CHECK_THROWS_WITH(it[1], "cannot use offsets with object iterators"); } } SECTION("array") { { auto it = j_array.rbegin(); CHECK(it[0] == json(6)); CHECK(it[1] == json(5)); CHECK(it[2] == json(4)); CHECK(it[3] == json(3)); CHECK(it[4] == json(2)); CHECK(it[5] == json(1)); } { auto it = j_array.crbegin(); CHECK(it[0] == json(6)); CHECK(it[1] == json(5)); CHECK(it[2] == json(4)); CHECK(it[3] == json(3)); CHECK(it[4] == json(2)); CHECK(it[5] == json(1)); } } SECTION("null") { { auto it = j_null.rbegin(); CHECK_THROWS_AS(it[0], std::out_of_range); CHECK_THROWS_AS(it[1], std::out_of_range); CHECK_THROWS_WITH(it[0], "cannot get value"); CHECK_THROWS_WITH(it[1], "cannot get value"); } { auto it = j_null.crbegin(); CHECK_THROWS_AS(it[0], std::out_of_range); CHECK_THROWS_AS(it[1], std::out_of_range); CHECK_THROWS_WITH(it[0], "cannot get value"); CHECK_THROWS_WITH(it[1], "cannot get value"); } } SECTION("value") { { auto it = j_value.rbegin(); CHECK(it[0] == json(42)); CHECK_THROWS_AS(it[1], std::out_of_range); CHECK_THROWS_WITH(it[1], "cannot get value"); } { auto it = j_value.crbegin(); CHECK(it[0] == json(42)); CHECK_THROWS_AS(it[1], std::out_of_range); CHECK_THROWS_WITH(it[1], "cannot get value"); } } } } } TEST_CASE("capacity") { SECTION("empty()") { SECTION("boolean") { json j = true; json j_const(j); SECTION("result of empty") { CHECK(j.empty() == false); CHECK(j_const.empty() == false); } SECTION("definition of empty") { CHECK(j.begin() != j.end()); CHECK(j_const.begin() != j_const.end()); } } SECTION("string") { json j = "hello world"; json j_const(j); SECTION("result of empty") { CHECK(j.empty() == false); CHECK(j_const.empty() == false); } SECTION("definition of empty") { CHECK(j.begin() != j.end()); CHECK(j_const.begin() != j_const.end()); } } SECTION("array") { SECTION("empty array") { json j = json::array(); json j_const(j); SECTION("result of empty") { CHECK(j.empty() == true); CHECK(j_const.empty() == true); } SECTION("definition of empty") { CHECK(j.begin() == j.end()); CHECK(j_const.begin() == j_const.end()); } } SECTION("filled array") { json j = {1, 2, 3}; json j_const(j); SECTION("result of empty") { CHECK(j.empty() == false); CHECK(j_const.empty() == false); } SECTION("definition of empty") { CHECK(j.begin() != j.end()); CHECK(j_const.begin() != j_const.end()); } } } SECTION("object") { SECTION("empty object") { json j = json::object(); json j_const(j); SECTION("result of empty") { CHECK(j.empty() == true); CHECK(j_const.empty() == true); } SECTION("definition of empty") { CHECK(j.begin() == j.end()); CHECK(j_const.begin() == j_const.end()); } } SECTION("filled object") { json j = {{"one", 1}, {"two", 2}, {"three", 3}}; json j_const(j); SECTION("result of empty") { CHECK(j.empty() == false); CHECK(j_const.empty() == false); } SECTION("definition of empty") { CHECK(j.begin() != j.end()); CHECK(j_const.begin() != j_const.end()); } } } SECTION("number (integer)") { json j = 23; json j_const(j); SECTION("result of empty") { CHECK(j.empty() == false); CHECK(j_const.empty() == false); } SECTION("definition of empty") { CHECK(j.begin() != j.end()); CHECK(j_const.begin() != j_const.end()); } } SECTION("number (unsigned)") { json j = 23u; json j_const(j); SECTION("result of empty") { CHECK(j.empty() == false); CHECK(j_const.empty() == false); } SECTION("definition of empty") { CHECK(j.begin() != j.end()); CHECK(j_const.begin() != j_const.end()); } } SECTION("number (float)") { json j = 23.42; json j_const(j); SECTION("result of empty") { CHECK(j.empty() == false); CHECK(j_const.empty() == false); } SECTION("definition of empty") { CHECK(j.begin() != j.end()); CHECK(j_const.begin() != j_const.end()); } } SECTION("null") { json j = nullptr; json j_const(j); SECTION("result of empty") { CHECK(j.empty() == true); CHECK(j_const.empty() == true); } SECTION("definition of empty") { CHECK(j.begin() == j.end()); CHECK(j_const.begin() == j_const.end()); } } } SECTION("size()") { SECTION("boolean") { json j = true; json j_const(j); SECTION("result of size") { CHECK(j.size() == 1); CHECK(j_const.size() == 1); } SECTION("definition of size") { CHECK(std::distance(j.begin(), j.end()) == j.size()); CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); } } SECTION("string") { json j = "hello world"; json j_const(j); SECTION("result of size") { CHECK(j.size() == 1); CHECK(j_const.size() == 1); } SECTION("definition of size") { CHECK(std::distance(j.begin(), j.end()) == j.size()); CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); } } SECTION("array") { SECTION("empty array") { json j = json::array(); json j_const(j); SECTION("result of size") { CHECK(j.size() == 0); CHECK(j_const.size() == 0); } SECTION("definition of size") { CHECK(std::distance(j.begin(), j.end()) == j.size()); CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); } } SECTION("filled array") { json j = {1, 2, 3}; json j_const(j); SECTION("result of size") { CHECK(j.size() == 3); CHECK(j_const.size() == 3); } SECTION("definition of size") { CHECK(std::distance(j.begin(), j.end()) == j.size()); CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); } } } SECTION("object") { SECTION("empty object") { json j = json::object(); json j_const(j); SECTION("result of size") { CHECK(j.size() == 0); CHECK(j_const.size() == 0); } SECTION("definition of size") { CHECK(std::distance(j.begin(), j.end()) == j.size()); CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); } } SECTION("filled object") { json j = {{"one", 1}, {"two", 2}, {"three", 3}}; json j_const(j); SECTION("result of size") { CHECK(j.size() == 3); CHECK(j_const.size() == 3); } SECTION("definition of size") { CHECK(std::distance(j.begin(), j.end()) == j.size()); CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); } } } SECTION("number (integer)") { json j = 23; json j_const(j); SECTION("result of size") { CHECK(j.size() == 1); CHECK(j_const.size() == 1); } SECTION("definition of size") { CHECK(std::distance(j.begin(), j.end()) == j.size()); CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); } } SECTION("number (unsigned)") { json j = 23u; json j_const(j); SECTION("result of size") { CHECK(j.size() == 1); CHECK(j_const.size() == 1); } SECTION("definition of size") { CHECK(std::distance(j.begin(), j.end()) == j.size()); CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); } } SECTION("number (float)") { json j = 23.42; json j_const(j); SECTION("result of size") { CHECK(j.size() == 1); CHECK(j_const.size() == 1); } SECTION("definition of size") { CHECK(std::distance(j.begin(), j.end()) == j.size()); CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); } } SECTION("null") { json j = nullptr; json j_const(j); SECTION("result of size") { CHECK(j.size() == 0); CHECK(j_const.size() == 0); } SECTION("definition of size") { CHECK(std::distance(j.begin(), j.end()) == j.size()); CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size()); CHECK(std::distance(j.rbegin(), j.rend()) == j.size()); CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size()); } } } SECTION("max_size()") { SECTION("boolean") { json j = true; json j_const(j); SECTION("result of max_size") { CHECK(j.max_size() == 1); CHECK(j_const.max_size() == 1); } } SECTION("string") { json j = "hello world"; json j_const(j); SECTION("result of max_size") { CHECK(j.max_size() == 1); CHECK(j_const.max_size() == 1); } } SECTION("array") { SECTION("empty array") { json j = json::array(); json j_const(j); SECTION("result of max_size") { CHECK(j.max_size() >= j.size()); CHECK(j_const.max_size() >= j_const.size()); } } SECTION("filled array") { json j = {1, 2, 3}; json j_const(j); SECTION("result of max_size") { CHECK(j.max_size() >= j.size()); CHECK(j_const.max_size() >= j_const.size()); } } } SECTION("object") { SECTION("empty object") { json j = json::object(); json j_const(j); SECTION("result of max_size") { CHECK(j.max_size() >= j.size()); CHECK(j_const.max_size() >= j_const.size()); } } SECTION("filled object") { json j = {{"one", 1}, {"two", 2}, {"three", 3}}; json j_const(j); SECTION("result of max_size") { CHECK(j.max_size() >= j.size()); CHECK(j_const.max_size() >= j_const.size()); } } } SECTION("number (integer)") { json j = 23; json j_const(j); SECTION("result of max_size") { CHECK(j.max_size() == 1); CHECK(j_const.max_size() == 1); } } SECTION("number (unsigned)") { json j = 23u; json j_const(j); SECTION("result of max_size") { CHECK(j.max_size() == 1); CHECK(j_const.max_size() == 1); } } SECTION("number (float)") { json j = 23.42; json j_const(j); SECTION("result of max_size") { CHECK(j.max_size() == 1); CHECK(j_const.max_size() == 1); } } SECTION("null") { json j = nullptr; json j_const(j); SECTION("result of max_size") { CHECK(j.max_size() == 0); CHECK(j_const.max_size() == 0); } } } } TEST_CASE("modifiers") { SECTION("clear()") { SECTION("boolean") { json j = true; j.clear(); CHECK(j == json(json::value_t::boolean)); } SECTION("string") { json j = "hello world"; j.clear(); CHECK(j == json(json::value_t::string)); } SECTION("array") { SECTION("empty array") { json j = json::array(); j.clear(); CHECK(j.empty()); CHECK(j == json(json::value_t::array)); } SECTION("filled array") { json j = {1, 2, 3}; j.clear(); CHECK(j.empty()); CHECK(j == json(json::value_t::array)); } } SECTION("object") { SECTION("empty object") { json j = json::object(); j.clear(); CHECK(j.empty()); CHECK(j == json(json::value_t::object)); } SECTION("filled object") { json j = {{"one", 1}, {"two", 2}, {"three", 3}}; j.clear(); CHECK(j.empty()); CHECK(j == json(json::value_t::object)); } } SECTION("number (integer)") { json j = 23; j.clear(); CHECK(j == json(json::value_t::number_integer)); } SECTION("number (unsigned)") { json j = 23u; j.clear(); CHECK(j == json(json::value_t::number_integer)); } SECTION("number (float)") { json j = 23.42; j.clear(); CHECK(j == json(json::value_t::number_float)); } SECTION("null") { json j = nullptr; j.clear(); CHECK(j == json(json::value_t::null)); } } SECTION("push_back()") { SECTION("to array") { SECTION("json&&") { SECTION("null") { json j; j.push_back(1); j.push_back(2); CHECK(j.type() == json::value_t::array); CHECK(j == json({1, 2})); } SECTION("array") { json j = {1, 2, 3}; j.push_back("Hello"); CHECK(j.type() == json::value_t::array); CHECK(j == json({1, 2, 3, "Hello"})); } SECTION("other type") { json j = 1; CHECK_THROWS_AS(j.push_back("Hello"), std::domain_error); CHECK_THROWS_WITH(j.push_back("Hello"), "cannot use push_back() with number"); } } SECTION("const json&") { SECTION("null") { json j; json k(1); j.push_back(k); j.push_back(k); CHECK(j.type() == json::value_t::array); CHECK(j == json({1, 1})); } SECTION("array") { json j = {1, 2, 3}; json k("Hello"); j.push_back(k); CHECK(j.type() == json::value_t::array); CHECK(j == json({1, 2, 3, "Hello"})); } SECTION("other type") { json j = 1; json k("Hello"); CHECK_THROWS_AS(j.push_back(k), std::domain_error); CHECK_THROWS_WITH(j.push_back(k), "cannot use push_back() with number"); } } } SECTION("to object") { SECTION("null") { json j; j.push_back(json::object_t::value_type({"one", 1})); j.push_back(json::object_t::value_type({"two", 2})); CHECK(j.type() == json::value_t::object); CHECK(j.size() == 2); CHECK(j["one"] == json(1)); CHECK(j["two"] == json(2)); } SECTION("object") { json j(json::value_t::object); j.push_back(json::object_t::value_type({"one", 1})); j.push_back(json::object_t::value_type({"two", 2})); CHECK(j.size() == 2); CHECK(j["one"] == json(1)); CHECK(j["two"] == json(2)); } SECTION("other type") { json j = 1; json k("Hello"); CHECK_THROWS_AS(j.push_back(json::object_t::value_type({"one", 1})), std::domain_error); CHECK_THROWS_WITH(j.push_back(json::object_t::value_type({"one", 1})), "cannot use push_back() with number"); } } SECTION("with initializer_list") { SECTION("null") { json j; j.push_back({"foo", "bar"}); CHECK(j == json::array({{"foo", "bar"}})); json k; k.push_back({1, 2, 3}); CHECK(k == json::array({{1, 2, 3}})); } SECTION("array") { json j = {1, 2, 3}; j.push_back({"foo", "bar"}); CHECK(j == json({1, 2, 3, {"foo", "bar"}})); json k = {1, 2, 3}; k.push_back({1, 2, 3}); CHECK(k == json({1, 2, 3, {1, 2, 3}})); } SECTION("object") { json j = {{"key1", 1}}; j.push_back({"key2", "bar"}); CHECK(j == json({{"key1", 1}, {"key2", "bar"}})); json k = {{"key1", 1}}; CHECK_THROWS_AS(k.push_back({1, 2, 3, 4}), std::domain_error); CHECK_THROWS_WITH(k.push_back({1, 2, 3, 4}), "cannot use push_back() with object"); } } } SECTION("operator+=") { SECTION("to array") { SECTION("json&&") { SECTION("null") { json j; j += 1; j += 2; CHECK(j.type() == json::value_t::array); CHECK(j == json({1, 2})); } SECTION("array") { json j = {1, 2, 3}; j += "Hello"; CHECK(j.type() == json::value_t::array); CHECK(j == json({1, 2, 3, "Hello"})); } SECTION("other type") { json j = 1; CHECK_THROWS_AS(j += "Hello", std::domain_error); CHECK_THROWS_WITH(j += "Hello", "cannot use push_back() with number"); } } SECTION("const json&") { SECTION("null") { json j; json k(1); j += k; j += k; CHECK(j.type() == json::value_t::array); CHECK(j == json({1, 1})); } SECTION("array") { json j = {1, 2, 3}; json k("Hello"); j += k; CHECK(j.type() == json::value_t::array); CHECK(j == json({1, 2, 3, "Hello"})); } SECTION("other type") { json j = 1; json k("Hello"); CHECK_THROWS_AS(j += k, std::domain_error); CHECK_THROWS_WITH(j += k, "cannot use push_back() with number"); } } } SECTION("to object") { SECTION("null") { json j; j += json::object_t::value_type({"one", 1}); j += json::object_t::value_type({"two", 2}); CHECK(j.type() == json::value_t::object); CHECK(j.size() == 2); CHECK(j["one"] == json(1)); CHECK(j["two"] == json(2)); } SECTION("object") { json j(json::value_t::object); j += json::object_t::value_type({"one", 1}); j += json::object_t::value_type({"two", 2}); CHECK(j.size() == 2); CHECK(j["one"] == json(1)); CHECK(j["two"] == json(2)); } SECTION("other type") { json j = 1; json k("Hello"); CHECK_THROWS_AS(j += json::object_t::value_type({"one", 1}), std::domain_error); CHECK_THROWS_WITH(j += json::object_t::value_type({"one", 1}), "cannot use push_back() with number"); } } SECTION("with initializer_list") { SECTION("null") { json j; j += {"foo", "bar"}; CHECK(j == json::array({{"foo", "bar"}})); json k; k += {1, 2, 3}; CHECK(k == json::array({{1, 2, 3}})); } SECTION("array") { json j = {1, 2, 3}; j += {"foo", "bar"}; CHECK(j == json({1, 2, 3, {"foo", "bar"}})); json k = {1, 2, 3}; k += {1, 2, 3}; CHECK(k == json({1, 2, 3, {1, 2, 3}})); } SECTION("object") { json j = {{"key1", 1}}; j += {"key2", "bar"}; CHECK(j == json({{"key1", 1}, {"key2", "bar"}})); json k = {{"key1", 1}}; CHECK_THROWS_AS((k += {1, 2, 3, 4}), std::domain_error); CHECK_THROWS_WITH((k += {1, 2, 3, 4}), "cannot use push_back() with object"); } } } SECTION("insert") { json j_array = {1, 2, 3, 4}; json j_value = 5; SECTION("value at position") { SECTION("insert before begin()") { auto it = j_array.insert(j_array.begin(), j_value); CHECK(j_array.size() == 5); CHECK(*it == j_value); CHECK(j_array.begin() == it); CHECK(j_array == json({5, 1, 2, 3, 4})); } SECTION("insert in the middle") { auto it = j_array.insert(j_array.begin() + 2, j_value); CHECK(j_array.size() == 5); CHECK(*it == j_value); CHECK((it - j_array.begin()) == 2); CHECK(j_array == json({1, 2, 5, 3, 4})); } SECTION("insert before end()") { auto it = j_array.insert(j_array.end(), j_value); CHECK(j_array.size() == 5); CHECK(*it == j_value); CHECK((j_array.end() - it) == 1); CHECK(j_array == json({1, 2, 3, 4, 5})); } } SECTION("rvalue at position") { SECTION("insert before begin()") { auto it = j_array.insert(j_array.begin(), 5); CHECK(j_array.size() == 5); CHECK(*it == j_value); CHECK(j_array.begin() == it); CHECK(j_array == json({5, 1, 2, 3, 4})); } SECTION("insert in the middle") { auto it = j_array.insert(j_array.begin() + 2, 5); CHECK(j_array.size() == 5); CHECK(*it == j_value); CHECK((it - j_array.begin()) == 2); CHECK(j_array == json({1, 2, 5, 3, 4})); } SECTION("insert before end()") { auto it = j_array.insert(j_array.end(), 5); CHECK(j_array.size() == 5); CHECK(*it == j_value); CHECK((j_array.end() - it) == 1); CHECK(j_array == json({1, 2, 3, 4, 5})); } } SECTION("copies at position") { SECTION("insert before begin()") { auto it = j_array.insert(j_array.begin(), 3, 5); CHECK(j_array.size() == 7); CHECK(*it == j_value); CHECK(j_array.begin() == it); CHECK(j_array == json({5, 5, 5, 1, 2, 3, 4})); } SECTION("insert in the middle") { auto it = j_array.insert(j_array.begin() + 2, 3, 5); CHECK(j_array.size() == 7); CHECK(*it == j_value); CHECK((it - j_array.begin()) == 2); CHECK(j_array == json({1, 2, 5, 5, 5, 3, 4})); } SECTION("insert before end()") { auto it = j_array.insert(j_array.end(), 3, 5); CHECK(j_array.size() == 7); CHECK(*it == j_value); CHECK((j_array.end() - it) == 3); CHECK(j_array == json({1, 2, 3, 4, 5, 5, 5})); } SECTION("insert nothing (count = 0)") { auto pos = j_array.end(); auto it = j_array.insert(j_array.end(), 0, 5); CHECK(j_array.size() == 4); CHECK(it == pos); CHECK(j_array == json({1, 2, 3, 4})); } } SECTION("range") { json j_other_array = {"first", "second"}; SECTION("proper usage") { auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.end()); CHECK(j_array.size() == 6); CHECK(*it == *j_other_array.begin()); CHECK((j_array.end() - it) == 2); CHECK(j_array == json({1, 2, 3, 4, "first", "second"})); } SECTION("empty range") { auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.begin()); CHECK(j_array.size() == 4); CHECK(it == j_array.end()); CHECK(j_array == json({1, 2, 3, 4})); } SECTION("invalid iterators") { json j_other_array2 = {"first", "second"}; CHECK_THROWS_AS(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), std::domain_error); CHECK_THROWS_AS(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()), std::domain_error); CHECK_THROWS_WITH(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), "passed iterators may not belong to container"); CHECK_THROWS_WITH(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()), "iterators do not fit"); } } SECTION("initializer list at position") { SECTION("insert before begin()") { auto it = j_array.insert(j_array.begin(), {7, 8, 9}); CHECK(j_array.size() == 7); CHECK(*it == json(7)); CHECK(j_array.begin() == it); CHECK(j_array == json({7, 8, 9, 1, 2, 3, 4})); } SECTION("insert in the middle") { auto it = j_array.insert(j_array.begin() + 2, {7, 8, 9}); CHECK(j_array.size() == 7); CHECK(*it == json(7)); CHECK((it - j_array.begin()) == 2); CHECK(j_array == json({1, 2, 7, 8, 9, 3, 4})); } SECTION("insert before end()") { auto it = j_array.insert(j_array.end(), {7, 8, 9}); CHECK(j_array.size() == 7); CHECK(*it == json(7)); CHECK((j_array.end() - it) == 3); CHECK(j_array == json({1, 2, 3, 4, 7, 8, 9})); } } SECTION("invalid iterator") { // pass iterator to a different array json j_another_array = {1, 2}; json j_yet_another_array = {"first", "second"}; CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10), std::domain_error); CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_value), std::domain_error); CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10, 11), std::domain_error); CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), j_yet_another_array.end()), std::domain_error); CHECK_THROWS_AS(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), std::domain_error); CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), 10), "iterator does not fit current value"); CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), j_value), "iterator does not fit current value"); CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), 10, 11), "iterator does not fit current value"); CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), j_yet_another_array.end()), "iterator does not fit current value"); CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), "iterator does not fit current value"); } SECTION("non-array type") { // call insert on a non-array type json j_nonarray = 3; json j_yet_another_array = {"first", "second"}; CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10), std::domain_error); CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_value), std::domain_error); CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10, 11), std::domain_error); CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(), j_yet_another_array.end()), std::domain_error); CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), std::domain_error); CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), 10), "cannot use insert() with number"); CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), j_value), "cannot use insert() with number"); CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), 10, 11), "cannot use insert() with number"); CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(), j_yet_another_array.end()), "cannot use insert() with number"); CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), "cannot use insert() with number"); } } SECTION("swap()") { SECTION("json") { SECTION("member swap") { json j("hello world"); json k(42.23); j.swap(k); CHECK(j == json(42.23)); CHECK(k == json("hello world")); } SECTION("nonmember swap") { json j("hello world"); json k(42.23); std::swap(j, k); CHECK(j == json(42.23)); CHECK(k == json("hello world")); } } SECTION("array_t") { SECTION("array_t type") { json j = {1, 2, 3, 4}; json::array_t a = {"foo", "bar", "baz"}; j.swap(a); CHECK(j == json({"foo", "bar", "baz"})); j.swap(a); CHECK(j == json({1, 2, 3, 4})); } SECTION("non-array_t type") { json j = 17; json::array_t a = {"foo", "bar", "baz"}; CHECK_THROWS_AS(j.swap(a), std::domain_error); CHECK_THROWS_WITH(j.swap(a), "cannot use swap() with number"); } } SECTION("object_t") { SECTION("object_t type") { json j = {{"one", 1}, {"two", 2}}; json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}}; j.swap(o); CHECK(j == json({{"cow", "Kuh"}, {"chicken", "Huhn"}})); j.swap(o); CHECK(j == json({{"one", 1}, {"two", 2}})); } SECTION("non-object_t type") { json j = 17; json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}}; CHECK_THROWS_AS(j.swap(o), std::domain_error); CHECK_THROWS_WITH(j.swap(o), "cannot use swap() with number"); } } SECTION("string_t") { SECTION("string_t type") { json j = "Hello world"; json::string_t s = "Hallo Welt"; j.swap(s); CHECK(j == json("Hallo Welt")); j.swap(s); CHECK(j == json("Hello world")); } SECTION("non-string_t type") { json j = 17; json::string_t s = "Hallo Welt"; CHECK_THROWS_AS(j.swap(s), std::domain_error); CHECK_THROWS_WITH(j.swap(s), "cannot use swap() with number"); } } } } TEST_CASE("lexicographical comparison operators") { SECTION("types") { std::vector j_types = { json::value_t::null, json::value_t::boolean, json::value_t::number_integer, json::value_t::number_unsigned, json::value_t::number_float, json::value_t::object, json::value_t::array, json::value_t::string }; SECTION("comparison: less") { std::vector> expected = { {false, true, true, true, true, true, true, true}, {false, false, true, true, true, true, true, true}, {false, false, false, false, false, true, true, true}, {false, false, false, false, false, true, true, true}, {false, false, false, false, false, true, true, true}, {false, false, false, false, false, false, true, true}, {false, false, false, false, false, false, false, true}, {false, false, false, false, false, false, false, false} }; for (size_t i = 0; i < j_types.size(); ++i) { for (size_t j = 0; j < j_types.size(); ++j) { CAPTURE(i); CAPTURE(j); // check precomputed values CHECK(operator<(j_types[i], j_types[j]) == expected[i][j]); } } } } SECTION("values") { json j_values = { nullptr, nullptr, 17, 42, 8u, 13u, 3.14159, 23.42, "foo", "bar", true, false, {1, 2, 3}, {"one", "two", "three"}, {{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}} }; SECTION("comparison: equal") { std::vector> expected = { {true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, {true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, {false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false}, {false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false}, {false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false}, {false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false}, {false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false}, {false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false}, {false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false}, {false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false}, {false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false}, {false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false}, {false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false}, {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false}, {false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false}, {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true} }; for (size_t i = 0; i < j_values.size(); ++i) { for (size_t j = 0; j < j_values.size(); ++j) { CAPTURE(i); CAPTURE(j); // check precomputed values CHECK( (j_values[i] == j_values[j]) == expected[i][j] ); } } // comparison with discarded elements json j_discarded(json::value_t::discarded); for (size_t i = 0; i < j_values.size(); ++i) { CHECK( (j_values[i] == j_discarded) == false); CHECK( (j_discarded == j_values[i]) == false); CHECK( (j_discarded == j_discarded) == false); } // compare with null pointer json j_null; CHECK(j_null == nullptr); CHECK(nullptr == j_null); } SECTION("comparison: not equal") { for (size_t i = 0; i < j_values.size(); ++i) { for (size_t j = 0; j < j_values.size(); ++j) { CAPTURE(i); CAPTURE(j); // check definition CHECK( (j_values[i] != j_values[j]) == not(j_values[i] == j_values[j]) ); } } // compare with null pointer json j_null; CHECK( (j_null != nullptr) == false); CHECK( (nullptr != j_null) == false); CHECK( (j_null != nullptr) == not(j_null == nullptr)); CHECK( (nullptr != j_null) == not(nullptr == j_null)); } SECTION("comparison: less") { std::vector> expected = { {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true}, {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true}, {false, false, false, true, false, false, false, true, true, true, false, false, true, true, true, true}, {false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, true}, {false, false, true, true, false, true, false, true, true, true, false, false, true, true, true, true}, {false, false, true, true, false, false, false, true, true, true, false, false, true, true, true, true}, {false, false, true, true, true, true, false, true, true, true, false, false, true, true, true, true}, {false, false, false, true, false, false, false, false, true, true, false, false, true, true, true, true}, {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, {false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false}, {false, false, true, true, true, true, true, true, true, true, false, false, true, true, true, true}, {false, false, true, true, true, true, true, true, true, true, true, false, true, true, true, true}, {false, false, false, false, false, false, false, false, true, true, false, false, false, true, false, false}, {false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false}, {false, false, false, false, false, false, false, false, true, true, false, false, true, true, false, false}, {false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, false} }; for (size_t i = 0; i < j_values.size(); ++i) { for (size_t j = 0; j < j_values.size(); ++j) { CAPTURE(i); CAPTURE(j); // check precomputed values CHECK( (j_values[i] < j_values[j]) == expected[i][j] ); } } // comparison with discarded elements json j_discarded(json::value_t::discarded); for (size_t i = 0; i < j_values.size(); ++i) { CAPTURE(i); CHECK( (j_values[i] < j_discarded) == false); CHECK( (j_discarded < j_values[i]) == false); CHECK( (j_discarded < j_discarded) == false); } } SECTION("comparison: less than or equal equal") { for (size_t i = 0; i < j_values.size(); ++i) { for (size_t j = 0; j < j_values.size(); ++j) { CAPTURE(i); CAPTURE(j); // check definition CHECK( (j_values[i] <= j_values[j]) == not(j_values[j] < j_values[i]) ); } } } SECTION("comparison: greater than") { for (size_t i = 0; i < j_values.size(); ++i) { for (size_t j = 0; j < j_values.size(); ++j) { CAPTURE(i); CAPTURE(j); // check definition CHECK( (j_values[i] > j_values[j]) == (j_values[j] < j_values[i]) ); } } } SECTION("comparison: greater than or equal") { for (size_t i = 0; i < j_values.size(); ++i) { for (size_t j = 0; j < j_values.size(); ++j) { CAPTURE(i); CAPTURE(j); // check definition CHECK( (j_values[i] >= j_values[j]) == not(j_values[i] < j_values[j]) ); } } } } } TEST_CASE("serialization") { SECTION("operator<<") { SECTION("no given width") { std::stringstream ss; json j = {"foo", 1, 2, 3, false, {{"one", 1}}}; ss << j; CHECK(ss.str() == "[\"foo\",1,2,3,false,{\"one\":1}]"); } SECTION("given width") { std::stringstream ss; json j = {"foo", 1, 2, 3, false, {{"one", 1}}}; ss << std::setw(4) << j; CHECK(ss.str() == "[\n \"foo\",\n 1,\n 2,\n 3,\n false,\n {\n \"one\": 1\n }\n]"); } } SECTION("operator>>") { SECTION("no given width") { std::stringstream ss; json j = {"foo", 1, 2, 3, false, {{"one", 1}}}; j >> ss; CHECK(ss.str() == "[\"foo\",1,2,3,false,{\"one\":1}]"); } SECTION("given width") { std::stringstream ss; json j = {"foo", 1, 2, 3, false, {{"one", 1}}}; ss.width(4); j >> ss; CHECK(ss.str() == "[\n \"foo\",\n 1,\n 2,\n 3,\n false,\n {\n \"one\": 1\n }\n]"); } } } TEST_CASE("deserialization") { SECTION("stream") { std::stringstream ss; ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; json j = json::parse(ss); CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); } SECTION("string") { auto s = "[\"foo\",1,2,3,false,{\"one\":1}]"; json j = json::parse(s); CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); } SECTION("operator<<") { std::stringstream ss; ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; json j; j << ss; CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); } SECTION("operator>>") { std::stringstream ss; ss << "[\"foo\",1,2,3,false,{\"one\":1}]"; json j; ss >> j; CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}})); } SECTION("user-defined string literal") { CHECK("[\"foo\",1,2,3,false,{\"one\":1}]"_json == json({"foo", 1, 2, 3, false, {{"one", 1}}})); } } TEST_CASE("iterator class") { SECTION("construction") { SECTION("constructor") { SECTION("null") { json j(json::value_t::null); json::iterator it(&j); } SECTION("object") { json j(json::value_t::object); json::iterator it(&j); } SECTION("array") { json j(json::value_t::array); json::iterator it(&j); } } SECTION("copy assignment") { json j(json::value_t::null); json::iterator it(&j); json::iterator it2(&j); it2 = it; } } SECTION("initialization") { SECTION("set_begin") { SECTION("null") { json j(json::value_t::null); json::iterator it(&j); it.set_begin(); CHECK(it == j.begin()); } SECTION("object") { json j(json::value_t::object); json::iterator it(&j); it.set_begin(); CHECK(it == j.begin()); } SECTION("array") { json j(json::value_t::array); json::iterator it(&j); it.set_begin(); CHECK(it == j.begin()); } } SECTION("set_end") { SECTION("null") { json j(json::value_t::null); json::iterator it(&j); it.set_end(); CHECK(it == j.end()); } SECTION("object") { json j(json::value_t::object); json::iterator it(&j); it.set_end(); CHECK(it == j.end()); } SECTION("array") { json j(json::value_t::array); json::iterator it(&j); it.set_end(); CHECK(it == j.end()); } } } SECTION("element access") { SECTION("operator*") { SECTION("null") { json j(json::value_t::null); json::iterator it = j.begin(); CHECK_THROWS_AS(*it, std::out_of_range); CHECK_THROWS_WITH(*it, "cannot get value"); } SECTION("number") { json j(17); json::iterator it = j.begin(); CHECK(*it == json(17)); it = j.end(); CHECK_THROWS_AS(*it, std::out_of_range); CHECK_THROWS_WITH(*it, "cannot get value"); } SECTION("object") { json j({{"foo", "bar"}}); json::iterator it = j.begin(); CHECK(*it == json("bar")); } SECTION("array") { json j({1, 2, 3, 4}); json::iterator it = j.begin(); CHECK(*it == json(1)); } } SECTION("operator->") { SECTION("null") { json j(json::value_t::null); json::iterator it = j.begin(); CHECK_THROWS_AS(it->type_name(), std::out_of_range); CHECK_THROWS_WITH(it->type_name(), "cannot get value"); } SECTION("number") { json j(17); json::iterator it = j.begin(); CHECK(it->type_name() == "number"); it = j.end(); CHECK_THROWS_AS(it->type_name(), std::out_of_range); CHECK_THROWS_WITH(it->type_name(), "cannot get value"); } SECTION("object") { json j({{"foo", "bar"}}); json::iterator it = j.begin(); CHECK(it->type_name() == "string"); } SECTION("array") { json j({1, 2, 3, 4}); json::iterator it = j.begin(); CHECK(it->type_name() == "number"); } } } SECTION("increment/decrement") { SECTION("post-increment") { SECTION("null") { json j(json::value_t::null); json::iterator it = j.begin(); CHECK(it.m_it.primitive_iterator == 1); it++; CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); } SECTION("number") { json j(17); json::iterator it = j.begin(); CHECK(it.m_it.primitive_iterator == 0); it++; CHECK(it.m_it.primitive_iterator == 1); it++; CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); } SECTION("object") { json j({{"foo", "bar"}}); json::iterator it = j.begin(); CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); it++; CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); } SECTION("array") { json j({1, 2, 3, 4}); json::iterator it = j.begin(); CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); it++; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); it++; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); it++; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); it++; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); } } SECTION("pre-increment") { SECTION("null") { json j(json::value_t::null); json::iterator it = j.begin(); CHECK(it.m_it.primitive_iterator == 1); ++it; CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); } SECTION("number") { json j(17); json::iterator it = j.begin(); CHECK(it.m_it.primitive_iterator == 0); ++it; CHECK(it.m_it.primitive_iterator == 1); ++it; CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); } SECTION("object") { json j({{"foo", "bar"}}); json::iterator it = j.begin(); CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); ++it; CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); } SECTION("array") { json j({1, 2, 3, 4}); json::iterator it = j.begin(); CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); ++it; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); ++it; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); ++it; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); ++it; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); } } SECTION("post-decrement") { SECTION("null") { json j(json::value_t::null); json::iterator it = j.end(); CHECK(it.m_it.primitive_iterator == 1); } SECTION("number") { json j(17); json::iterator it = j.end(); CHECK(it.m_it.primitive_iterator == 1); it--; CHECK(it.m_it.primitive_iterator == 0); it--; CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); } SECTION("object") { json j({{"foo", "bar"}}); json::iterator it = j.end(); CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); it--; CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); } SECTION("array") { json j({1, 2, 3, 4}); json::iterator it = j.end(); CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); it--; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); it--; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); it--; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); it--; CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); } } SECTION("pre-decrement") { SECTION("null") { json j(json::value_t::null); json::iterator it = j.end(); CHECK(it.m_it.primitive_iterator == 1); } SECTION("number") { json j(17); json::iterator it = j.end(); CHECK(it.m_it.primitive_iterator == 1); --it; CHECK(it.m_it.primitive_iterator == 0); --it; CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); } SECTION("object") { json j({{"foo", "bar"}}); json::iterator it = j.end(); CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); --it; CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); } SECTION("array") { json j({1, 2, 3, 4}); json::iterator it = j.end(); CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); --it; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); --it; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); --it; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); --it; CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); } } } } TEST_CASE("const_iterator class") { SECTION("construction") { SECTION("constructor") { SECTION("null") { json j(json::value_t::null); json::const_iterator it(&j); } SECTION("object") { json j(json::value_t::object); json::const_iterator it(&j); } SECTION("array") { json j(json::value_t::array); json::const_iterator it(&j); } } SECTION("copy assignment") { json j(json::value_t::null); json::const_iterator it(&j); json::const_iterator it2(&j); it2 = it; } } SECTION("initialization") { SECTION("set_begin") { SECTION("null") { json j(json::value_t::null); json::const_iterator it(&j); it.set_begin(); CHECK(it == j.cbegin()); } SECTION("object") { json j(json::value_t::object); json::const_iterator it(&j); it.set_begin(); CHECK(it == j.cbegin()); } SECTION("array") { json j(json::value_t::array); json::const_iterator it(&j); it.set_begin(); CHECK(it == j.cbegin()); } } SECTION("set_end") { SECTION("null") { json j(json::value_t::null); json::const_iterator it(&j); it.set_end(); CHECK(it == j.cend()); } SECTION("object") { json j(json::value_t::object); json::const_iterator it(&j); it.set_end(); CHECK(it == j.cend()); } SECTION("array") { json j(json::value_t::array); json::const_iterator it(&j); it.set_end(); CHECK(it == j.cend()); } } } SECTION("element access") { SECTION("operator*") { SECTION("null") { json j(json::value_t::null); json::const_iterator it = j.cbegin(); CHECK_THROWS_AS(*it, std::out_of_range); CHECK_THROWS_WITH(*it, "cannot get value"); } SECTION("number") { json j(17); json::const_iterator it = j.cbegin(); CHECK(*it == json(17)); it = j.cend(); CHECK_THROWS_AS(*it, std::out_of_range); CHECK_THROWS_WITH(*it, "cannot get value"); } SECTION("object") { json j({{"foo", "bar"}}); json::const_iterator it = j.cbegin(); CHECK(*it == json("bar")); } SECTION("array") { json j({1, 2, 3, 4}); json::const_iterator it = j.cbegin(); CHECK(*it == json(1)); } } SECTION("operator->") { SECTION("null") { json j(json::value_t::null); json::const_iterator it = j.cbegin(); CHECK_THROWS_AS(it->type_name(), std::out_of_range); CHECK_THROWS_WITH(it->type_name(), "cannot get value"); } SECTION("number") { json j(17); json::const_iterator it = j.cbegin(); CHECK(it->type_name() == "number"); it = j.cend(); CHECK_THROWS_AS(it->type_name(), std::out_of_range); CHECK_THROWS_WITH(it->type_name(), "cannot get value"); } SECTION("object") { json j({{"foo", "bar"}}); json::const_iterator it = j.cbegin(); CHECK(it->type_name() == "string"); } SECTION("array") { json j({1, 2, 3, 4}); json::const_iterator it = j.cbegin(); CHECK(it->type_name() == "number"); } } } SECTION("increment/decrement") { SECTION("post-increment") { SECTION("null") { json j(json::value_t::null); json::const_iterator it = j.cbegin(); CHECK(it.m_it.primitive_iterator == 1); it++; CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); } SECTION("number") { json j(17); json::const_iterator it = j.cbegin(); CHECK(it.m_it.primitive_iterator == 0); it++; CHECK(it.m_it.primitive_iterator == 1); it++; CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); } SECTION("object") { json j({{"foo", "bar"}}); json::const_iterator it = j.cbegin(); CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); it++; CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); } SECTION("array") { json j({1, 2, 3, 4}); json::const_iterator it = j.cbegin(); CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); it++; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); it++; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); it++; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); it++; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); } } SECTION("pre-increment") { SECTION("null") { json j(json::value_t::null); json::const_iterator it = j.cbegin(); CHECK(it.m_it.primitive_iterator == 1); ++it; CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); } SECTION("number") { json j(17); json::const_iterator it = j.cbegin(); CHECK(it.m_it.primitive_iterator == 0); ++it; CHECK(it.m_it.primitive_iterator == 1); ++it; CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); } SECTION("object") { json j({{"foo", "bar"}}); json::const_iterator it = j.cbegin(); CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); ++it; CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); } SECTION("array") { json j({1, 2, 3, 4}); json::const_iterator it = j.cbegin(); CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); ++it; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); ++it; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); ++it; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); ++it; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); } } SECTION("post-decrement") { SECTION("null") { json j(json::value_t::null); json::const_iterator it = j.cend(); CHECK(it.m_it.primitive_iterator == 1); } SECTION("number") { json j(17); json::const_iterator it = j.cend(); CHECK(it.m_it.primitive_iterator == 1); it--; CHECK(it.m_it.primitive_iterator == 0); it--; CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); } SECTION("object") { json j({{"foo", "bar"}}); json::const_iterator it = j.cend(); CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); it--; CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); } SECTION("array") { json j({1, 2, 3, 4}); json::const_iterator it = j.cend(); CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); it--; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); it--; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); it--; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); it--; CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); } } SECTION("pre-decrement") { SECTION("null") { json j(json::value_t::null); json::const_iterator it = j.cend(); CHECK(it.m_it.primitive_iterator == 1); } SECTION("number") { json j(17); json::const_iterator it = j.cend(); CHECK(it.m_it.primitive_iterator == 1); --it; CHECK(it.m_it.primitive_iterator == 0); --it; CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1)); } SECTION("object") { json j({{"foo", "bar"}}); json::const_iterator it = j.cend(); CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end()); --it; CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin()); } SECTION("array") { json j({1, 2, 3, 4}); json::const_iterator it = j.cend(); CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end()); --it; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); --it; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); --it; CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); --it; CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin()); CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end()); } } } } TEST_CASE("convenience functions") { SECTION("type name as string") { CHECK(json(json::value_t::null).type_name() == "null"); CHECK(json(json::value_t::object).type_name() == "object"); CHECK(json(json::value_t::array).type_name() == "array"); CHECK(json(json::value_t::number_integer).type_name() == "number"); CHECK(json(json::value_t::number_float).type_name() == "number"); CHECK(json(json::value_t::boolean).type_name() == "boolean"); CHECK(json(json::value_t::string).type_name() == "string"); CHECK(json(json::value_t::discarded).type_name() == "discarded"); } SECTION("string escape") { CHECK(json::escape_string("\"") == "\\\""); CHECK(json::escape_string("\\") == "\\\\"); CHECK(json::escape_string("\b") == "\\b"); CHECK(json::escape_string("\f") == "\\f"); CHECK(json::escape_string("\n") == "\\n"); CHECK(json::escape_string("\r") == "\\r"); CHECK(json::escape_string("\t") == "\\t"); CHECK(json::escape_string("\x01") == "\\u0001"); CHECK(json::escape_string("\x02") == "\\u0002"); CHECK(json::escape_string("\x03") == "\\u0003"); CHECK(json::escape_string("\x04") == "\\u0004"); CHECK(json::escape_string("\x05") == "\\u0005"); CHECK(json::escape_string("\x06") == "\\u0006"); CHECK(json::escape_string("\x07") == "\\u0007"); CHECK(json::escape_string("\x08") == "\\b"); CHECK(json::escape_string("\x09") == "\\t"); CHECK(json::escape_string("\x0a") == "\\n"); CHECK(json::escape_string("\x0b") == "\\u000b"); CHECK(json::escape_string("\x0c") == "\\f"); CHECK(json::escape_string("\x0d") == "\\r"); CHECK(json::escape_string("\x0e") == "\\u000e"); CHECK(json::escape_string("\x0f") == "\\u000f"); CHECK(json::escape_string("\x10") == "\\u0010"); CHECK(json::escape_string("\x11") == "\\u0011"); CHECK(json::escape_string("\x12") == "\\u0012"); CHECK(json::escape_string("\x13") == "\\u0013"); CHECK(json::escape_string("\x14") == "\\u0014"); CHECK(json::escape_string("\x15") == "\\u0015"); CHECK(json::escape_string("\x16") == "\\u0016"); CHECK(json::escape_string("\x17") == "\\u0017"); CHECK(json::escape_string("\x18") == "\\u0018"); CHECK(json::escape_string("\x19") == "\\u0019"); CHECK(json::escape_string("\x1a") == "\\u001a"); CHECK(json::escape_string("\x1b") == "\\u001b"); CHECK(json::escape_string("\x1c") == "\\u001c"); CHECK(json::escape_string("\x1d") == "\\u001d"); CHECK(json::escape_string("\x1e") == "\\u001e"); CHECK(json::escape_string("\x1f") == "\\u001f"); } } TEST_CASE("lexer class") { SECTION("scan") { SECTION("structural characters") { CHECK(json::lexer("[").scan() == json::lexer::token_type::begin_array); CHECK(json::lexer("]").scan() == json::lexer::token_type::end_array); CHECK(json::lexer("{").scan() == json::lexer::token_type::begin_object); CHECK(json::lexer("}").scan() == json::lexer::token_type::end_object); CHECK(json::lexer(",").scan() == json::lexer::token_type::value_separator); CHECK(json::lexer(":").scan() == json::lexer::token_type::name_separator); } SECTION("literal names") { CHECK(json::lexer("null").scan() == json::lexer::token_type::literal_null); CHECK(json::lexer("true").scan() == json::lexer::token_type::literal_true); CHECK(json::lexer("false").scan() == json::lexer::token_type::literal_false); } SECTION("numbers") { CHECK(json::lexer("0").scan() == json::lexer::token_type::value_number); CHECK(json::lexer("1").scan() == json::lexer::token_type::value_number); CHECK(json::lexer("2").scan() == json::lexer::token_type::value_number); CHECK(json::lexer("3").scan() == json::lexer::token_type::value_number); CHECK(json::lexer("4").scan() == json::lexer::token_type::value_number); CHECK(json::lexer("5").scan() == json::lexer::token_type::value_number); CHECK(json::lexer("6").scan() == json::lexer::token_type::value_number); CHECK(json::lexer("7").scan() == json::lexer::token_type::value_number); CHECK(json::lexer("8").scan() == json::lexer::token_type::value_number); CHECK(json::lexer("9").scan() == json::lexer::token_type::value_number); } SECTION("whitespace") { // result is end_of_input, because not token is following CHECK(json::lexer(" ").scan() == json::lexer::token_type::end_of_input); CHECK(json::lexer("\t").scan() == json::lexer::token_type::end_of_input); CHECK(json::lexer("\n").scan() == json::lexer::token_type::end_of_input); CHECK(json::lexer("\r").scan() == json::lexer::token_type::end_of_input); CHECK(json::lexer(" \t\n\r\n\t ").scan() == json::lexer::token_type::end_of_input); } } SECTION("token_type_name") { CHECK(json::lexer::token_type_name(json::lexer::token_type::uninitialized) == ""); CHECK(json::lexer::token_type_name(json::lexer::token_type::literal_true) == "true literal"); CHECK(json::lexer::token_type_name(json::lexer::token_type::literal_false) == "false literal"); CHECK(json::lexer::token_type_name(json::lexer::token_type::literal_null) == "null literal"); CHECK(json::lexer::token_type_name(json::lexer::token_type::value_string) == "string literal"); CHECK(json::lexer::token_type_name(json::lexer::token_type::value_number) == "number literal"); CHECK(json::lexer::token_type_name(json::lexer::token_type::begin_array) == "'['"); CHECK(json::lexer::token_type_name(json::lexer::token_type::begin_object) == "'{'"); CHECK(json::lexer::token_type_name(json::lexer::token_type::end_array) == "']'"); CHECK(json::lexer::token_type_name(json::lexer::token_type::end_object) == "'}'"); CHECK(json::lexer::token_type_name(json::lexer::token_type::name_separator) == "':'"); CHECK(json::lexer::token_type_name(json::lexer::token_type::value_separator) == "','"); CHECK(json::lexer::token_type_name(json::lexer::token_type::parse_error) == ""); CHECK(json::lexer::token_type_name(json::lexer::token_type::end_of_input) == "end of input"); } SECTION("parse errors on first character") { for (int c = 1; c < 128; ++c) { auto s = std::string(1, c); switch (c) { // single characters that are valid tokens case ('['): case (']'): case ('{'): case ('}'): case (','): case (':'): case ('0'): case ('1'): case ('2'): case ('3'): case ('4'): case ('5'): case ('6'): case ('7'): case ('8'): case ('9'): { CHECK(json::lexer(s.c_str()).scan() != json::lexer::token_type::parse_error); break; } // whitespace case (' '): case ('\t'): case ('\n'): case ('\r'): { CHECK(json::lexer(s.c_str()).scan() == json::lexer::token_type::end_of_input); break; } // anything else is not expected default: { CHECK(json::lexer(s.c_str()).scan() == json::lexer::token_type::parse_error); break; } } } } SECTION("to_unicode") { CHECK(json::lexer::to_unicode(0x1F4A9) == "💩"); CHECK_THROWS_AS(json::lexer::to_unicode(0x200000), std::out_of_range); CHECK_THROWS_WITH(json::lexer::to_unicode(0x200000), "code points above 0x10FFFF are invalid"); } } TEST_CASE("parser class") { SECTION("parse") { SECTION("null") { CHECK(json::parser("null").parse() == json(nullptr)); } SECTION("true") { CHECK(json::parser("true").parse() == json(true)); } SECTION("false") { CHECK(json::parser("false").parse() == json(false)); } SECTION("array") { SECTION("empty array") { CHECK(json::parser("[]").parse() == json(json::value_t::array)); CHECK(json::parser("[ ]").parse() == json(json::value_t::array)); } SECTION("nonempty array") { CHECK(json::parser("[true, false, null]").parse() == json({true, false, nullptr})); } } SECTION("object") { SECTION("empty object") { CHECK(json::parser("{}").parse() == json(json::value_t::object)); CHECK(json::parser("{ }").parse() == json(json::value_t::object)); } SECTION("nonempty object") { CHECK(json::parser("{\"\": true, \"one\": 1, \"two\": null}").parse() == json({{"", true}, {"one", 1}, {"two", nullptr}})); } } SECTION("string") { // empty string CHECK(json::parser("\"\"").parse() == json(json::value_t::string)); SECTION("errors") { // error: tab in string CHECK_THROWS_AS(json::parser("\"\t\"").parse(), std::invalid_argument); CHECK_THROWS_WITH(json::parser("\"\t\"").parse(), "parse error - unexpected '\"'"); // error: newline in string CHECK_THROWS_AS(json::parser("\"\n\"").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("\"\r\"").parse(), std::invalid_argument); CHECK_THROWS_WITH(json::parser("\"\n\"").parse(), "parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser("\"\r\"").parse(), "parse error - unexpected '\"'"); // error: backspace in string CHECK_THROWS_AS(json::parser("\"\b\"").parse(), std::invalid_argument); CHECK_THROWS_WITH(json::parser("\"\b\"").parse(), "parse error - unexpected '\"'"); // improve code coverage CHECK_THROWS_AS(json::parser("\uFF01").parse(), std::invalid_argument); } SECTION("escaped") { // quotation mark "\"" auto r1 = R"("\"")"_json; CHECK(json::parser("\"\\\"\"").parse() == r1); // reverse solidus "\\" auto r2 = R"("\\")"_json; CHECK(json::parser("\"\\\\\"").parse() == r2); // solidus CHECK(json::parser("\"\\/\"").parse() == R"("/")"_json); // backspace CHECK(json::parser("\"\\b\"").parse() == json("\b")); // formfeed CHECK(json::parser("\"\\f\"").parse() == json("\f")); // newline CHECK(json::parser("\"\\n\"").parse() == json("\n")); // carriage return CHECK(json::parser("\"\\r\"").parse() == json("\r")); // horizontal tab CHECK(json::parser("\"\\t\"").parse() == json("\t")); CHECK(json::parser("\"\\u0001\"").parse().get() == "\x01"); CHECK(json::parser("\"\\u000a\"").parse().get() == "\n"); CHECK(json::parser("\"\\u00b0\"").parse().get() == "°"); CHECK(json::parser("\"\\u0c00\"").parse().get() == "ఀ"); CHECK(json::parser("\"\\ud000\"").parse().get() == "퀀"); CHECK(json::parser("\"\\u000E\"").parse().get() == "\x0E"); CHECK(json::parser("\"\\u00F0\"").parse().get() == "ð"); CHECK(json::parser("\"\\u0100\"").parse().get() == "Ā"); CHECK(json::parser("\"\\u2000\"").parse().get() == " "); CHECK(json::parser("\"\\uFFFF\"").parse().get() == "￿"); CHECK(json::parser("\"\\u20AC\"").parse().get() == "€"); CHECK(json::parser("\"€\"").parse().get() == "€"); CHECK(json::parser("\"🎈\"").parse().get() == "🎈"); CHECK(json::parse("\"\\ud80c\\udc60\"").get() == u8"\U00013060"); CHECK(json::parse("\"\\ud83c\\udf1e\"").get() == "🌞"); } } SECTION("number") { SECTION("integers") { SECTION("without exponent") { CHECK(json::parser("-128").parse() == json(-128)); CHECK(json::parser("-0").parse() == json(-0)); CHECK(json::parser("0").parse() == json(0)); CHECK(json::parser("128").parse() == json(128)); } SECTION("with exponent") { CHECK(json::parser("0e1").parse() == json(0e1)); CHECK(json::parser("0E1").parse() == json(0e1)); CHECK(json::parser("10000E-4").parse() == json(10000e-4)); CHECK(json::parser("10000E-3").parse() == json(10000e-3)); CHECK(json::parser("10000E-2").parse() == json(10000e-2)); CHECK(json::parser("10000E-1").parse() == json(10000e-1)); CHECK(json::parser("10000E0").parse() == json(10000e0)); CHECK(json::parser("10000E1").parse() == json(10000e1)); CHECK(json::parser("10000E2").parse() == json(10000e2)); CHECK(json::parser("10000E3").parse() == json(10000e3)); CHECK(json::parser("10000E4").parse() == json(10000e4)); CHECK(json::parser("10000e-4").parse() == json(10000e-4)); CHECK(json::parser("10000e-3").parse() == json(10000e-3)); CHECK(json::parser("10000e-2").parse() == json(10000e-2)); CHECK(json::parser("10000e-1").parse() == json(10000e-1)); CHECK(json::parser("10000e0").parse() == json(10000e0)); CHECK(json::parser("10000e1").parse() == json(10000e1)); CHECK(json::parser("10000e2").parse() == json(10000e2)); CHECK(json::parser("10000e3").parse() == json(10000e3)); CHECK(json::parser("10000e4").parse() == json(10000e4)); CHECK(json::parser("-0e1").parse() == json(-0e1)); CHECK(json::parser("-0E1").parse() == json(-0e1)); CHECK(json::parser("-0E123").parse() == json(-0e123)); } SECTION("edge cases") { // From RFC7159, Section 6: // Note that when such software is used, numbers that are // integers and are in the range [-(2**53)+1, (2**53)-1] // are interoperable in the sense that implementations will // agree exactly on their numeric values. // -(2**53)+1 CHECK(json::parser("-9007199254740991").parse().get() == -9007199254740991); // (2**53)-1 CHECK(json::parser("9007199254740991").parse().get() == 9007199254740991); } SECTION("over the edge cases") // issue #178 - Integer conversion to unsigned (incorrect handling of 64 bit integers) { // While RFC7159, Section 6 specifies a preference for support // for ranges in range of IEEE 754-2008 binary64 (double precision) // this does not accommodate 64 bit integers without loss of accuracy. // As 64 bit integers are now widely used in software, it is desirable // to expand support to to the full 64 bit (signed and unsigned) range // i.e. -(2**63) -> (2**64)-1. // -(2**63) ** Note: compilers see negative literals as negated positive numbers (hence the -1)) CHECK(json::parser("-9223372036854775808").parse().get() == -9223372036854775807 - 1); // (2**63)-1 CHECK(json::parser("9223372036854775807").parse().get() == 9223372036854775807); // (2**64)-1 CHECK(json::parser("18446744073709551615").parse().get() == 18446744073709551615u); } } SECTION("floating-point") { SECTION("without exponent") { CHECK(json::parser("-128.5").parse() == json(-128.5)); CHECK(json::parser("0.999").parse() == json(0.999)); CHECK(json::parser("128.5").parse() == json(128.5)); CHECK(json::parser("-0.0").parse() == json(-0.0)); } SECTION("with exponent") { CHECK(json::parser("-128.5E3").parse() == json(-128.5E3)); CHECK(json::parser("-128.5E-3").parse() == json(-128.5E-3)); CHECK(json::parser("-0.0e1").parse() == json(-0.0e1)); CHECK(json::parser("-0.0E1").parse() == json(-0.0e1)); } } SECTION("invalid numbers") { CHECK_THROWS_AS(json::parser("01").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("--1").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("1.").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("1E").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("1E-").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("1.E1").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("-1E").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("-0E#").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("-0E-#").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("-0#").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("-0.0:").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("-0.0Z").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("-0E123:").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("-0e0-:").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("-0e-:").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("-0f").parse(), std::invalid_argument); // numbers must not begin with "+" CHECK_THROWS_AS(json::parser("+1").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("+0").parse(), std::invalid_argument); CHECK_THROWS_WITH(json::parser("01").parse(), "parse error - unexpected number literal; expected end of input"); CHECK_THROWS_WITH(json::parser("--1").parse(), "parse error - unexpected '-'"); CHECK_THROWS_WITH(json::parser("1.").parse(), "parse error - unexpected '.'; expected end of input"); CHECK_THROWS_WITH(json::parser("1E").parse(), "parse error - unexpected 'E'; expected end of input"); CHECK_THROWS_WITH(json::parser("1E-").parse(), "parse error - unexpected 'E'; expected end of input"); CHECK_THROWS_WITH(json::parser("1.E1").parse(), "parse error - unexpected '.'; expected end of input"); CHECK_THROWS_WITH(json::parser("-1E").parse(), "parse error - unexpected 'E'; expected end of input"); CHECK_THROWS_WITH(json::parser("-0E#").parse(), "parse error - unexpected 'E'; expected end of input"); CHECK_THROWS_WITH(json::parser("-0E-#").parse(), "parse error - unexpected 'E'; expected end of input"); CHECK_THROWS_WITH(json::parser("-0#").parse(), "parse error - unexpected '#'; expected end of input"); CHECK_THROWS_WITH(json::parser("-0.0:").parse(), "parse error - unexpected ':'; expected end of input"); CHECK_THROWS_WITH(json::parser("-0.0Z").parse(), "parse error - unexpected 'Z'; expected end of input"); CHECK_THROWS_WITH(json::parser("-0E123:").parse(), "parse error - unexpected ':'; expected end of input"); CHECK_THROWS_WITH(json::parser("-0e0-:").parse(), "parse error - unexpected '-'; expected end of input"); CHECK_THROWS_WITH(json::parser("-0e-:").parse(), "parse error - unexpected 'e'; expected end of input"); CHECK_THROWS_WITH(json::parser("-0f").parse(), "parse error - unexpected 'f'; expected end of input"); } } } SECTION("parse errors") { // unexpected end of number CHECK_THROWS_AS(json::parser("0.").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("-").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("--").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("-0.").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("-.").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("-:").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("0.:").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("e.").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("1e.").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("1e/").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("1e:").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("1E.").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("1E/").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("1E:").parse(), std::invalid_argument); CHECK_THROWS_WITH(json::parser("0.").parse(), "parse error - unexpected '.'; expected end of input"); CHECK_THROWS_WITH(json::parser("-").parse(), "parse error - unexpected '-'"); CHECK_THROWS_WITH(json::parser("--").parse(), "parse error - unexpected '-'"); CHECK_THROWS_WITH(json::parser("-0.").parse(), "parse error - unexpected '.'; expected end of input"); CHECK_THROWS_WITH(json::parser("-.").parse(), "parse error - unexpected '-'"); CHECK_THROWS_WITH(json::parser("-:").parse(), "parse error - unexpected '-'"); CHECK_THROWS_WITH(json::parser("0.:").parse(), "parse error - unexpected '.'; expected end of input"); CHECK_THROWS_WITH(json::parser("e.").parse(), "parse error - unexpected 'e'"); CHECK_THROWS_WITH(json::parser("1e.").parse(), "parse error - unexpected 'e'; expected end of input"); CHECK_THROWS_WITH(json::parser("1e/").parse(), "parse error - unexpected 'e'; expected end of input"); CHECK_THROWS_WITH(json::parser("1e:").parse(), "parse error - unexpected 'e'; expected end of input"); CHECK_THROWS_WITH(json::parser("1E.").parse(), "parse error - unexpected 'E'; expected end of input"); CHECK_THROWS_WITH(json::parser("1E/").parse(), "parse error - unexpected 'E'; expected end of input"); CHECK_THROWS_WITH(json::parser("1E:").parse(), "parse error - unexpected 'E'; expected end of input"); // unexpected end of null CHECK_THROWS_AS(json::parser("n").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("nu").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("nul").parse(), std::invalid_argument); CHECK_THROWS_WITH(json::parser("n").parse(), "parse error - unexpected 'n'"); CHECK_THROWS_WITH(json::parser("nu").parse(), "parse error - unexpected 'n'"); CHECK_THROWS_WITH(json::parser("nul").parse(), "parse error - unexpected 'n'"); // unexpected end of true CHECK_THROWS_AS(json::parser("t").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("tr").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("tru").parse(), std::invalid_argument); CHECK_THROWS_WITH(json::parser("t").parse(), "parse error - unexpected 't'"); CHECK_THROWS_WITH(json::parser("tr").parse(), "parse error - unexpected 't'"); CHECK_THROWS_WITH(json::parser("tru").parse(), "parse error - unexpected 't'"); // unexpected end of false CHECK_THROWS_AS(json::parser("f").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("fa").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("fal").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("fals").parse(), std::invalid_argument); CHECK_THROWS_WITH(json::parser("f").parse(), "parse error - unexpected 'f'"); CHECK_THROWS_WITH(json::parser("fa").parse(), "parse error - unexpected 'f'"); CHECK_THROWS_WITH(json::parser("fal").parse(), "parse error - unexpected 'f'"); CHECK_THROWS_WITH(json::parser("fals").parse(), "parse error - unexpected 'f'"); // missing/unexpected end of array CHECK_THROWS_AS(json::parser("[").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("[1").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("[1,").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("[1,]").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("]").parse(), std::invalid_argument); CHECK_THROWS_WITH(json::parser("[").parse(), "parse error - unexpected end of input"); CHECK_THROWS_WITH(json::parser("[1").parse(), "parse error - unexpected end of input; expected ']'"); CHECK_THROWS_WITH(json::parser("[1,").parse(), "parse error - unexpected end of input"); CHECK_THROWS_WITH(json::parser("[1,]").parse(), "parse error - unexpected ']'"); CHECK_THROWS_WITH(json::parser("]").parse(), "parse error - unexpected ']'"); // missing/unexpected end of object CHECK_THROWS_AS(json::parser("{").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("{\"foo\"").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("{\"foo\":").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("{\"foo\":}").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("{\"foo\":1,}").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("}").parse(), std::invalid_argument); CHECK_THROWS_WITH(json::parser("{").parse(), "parse error - unexpected end of input; expected string literal"); CHECK_THROWS_WITH(json::parser("{\"foo\"").parse(), "parse error - unexpected end of input; expected ':'"); CHECK_THROWS_WITH(json::parser("{\"foo\":").parse(), "parse error - unexpected end of input"); CHECK_THROWS_WITH(json::parser("{\"foo\":}").parse(), "parse error - unexpected '}'"); CHECK_THROWS_WITH(json::parser("{\"foo\":1,}").parse(), "parse error - unexpected '}'; expected string literal"); CHECK_THROWS_WITH(json::parser("}").parse(), "parse error - unexpected '}'"); // missing/unexpected end of string CHECK_THROWS_AS(json::parser("\"").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("\"\\\"").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("\"\\u\"").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("\"\\u0\"").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("\"\\u01\"").parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser("\"\\u012\"").parse(), std::invalid_argument); CHECK_THROWS_WITH(json::parser("\"").parse(), "parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser("\"\\\"").parse(), "parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser("\"\\u\"").parse(), "parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser("\"\\u0\"").parse(), "parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser("\"\\u01\"").parse(), "parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser("\"\\u012\"").parse(), "parse error - unexpected '\"'"); // invalid escapes for (int c = 1; c < 128; ++c) { auto s = std::string("\"\\") + std::string(1, c) + "\""; switch (c) { // valid escapes case ('"'): case ('\\'): case ('/'): case ('b'): case ('f'): case ('n'): case ('r'): case ('t'): { CHECK_NOTHROW(json::parser(s).parse()); break; } // \u must be followed with four numbers, so we skip it here case ('u'): { break; } // any other combination of backslash and character is invalid default: { CHECK_THROWS_AS(json::parser(s).parse(), std::invalid_argument); CHECK_THROWS_WITH(json::parser(s).parse(), "parse error - unexpected '\"'"); break; } } } // invalid \uxxxx escapes { // check whether character is a valid hex character const auto valid = [](int c) { switch (c) { case ('0'): case ('1'): case ('2'): case ('3'): case ('4'): case ('5'): case ('6'): case ('7'): case ('8'): case ('9'): case ('a'): case ('b'): case ('c'): case ('d'): case ('e'): case ('f'): case ('A'): case ('B'): case ('C'): case ('D'): case ('E'): case ('F'): { return true; } default: { return false; } } }; for (int c = 1; c < 128; ++c) { std::string s = "\"\\u"; // create a string with the iterated character at each position auto s1 = s + "000" + std::string(1, c) + "\""; auto s2 = s + "00" + std::string(1, c) + "0\""; auto s3 = s + "0" + std::string(1, c) + "00\""; auto s4 = s + std::string(1, c) + "000\""; if (valid(c)) { CHECK_NOTHROW(json::parser(s1).parse()); CHECK_NOTHROW(json::parser(s2).parse()); CHECK_NOTHROW(json::parser(s3).parse()); CHECK_NOTHROW(json::parser(s4).parse()); } else { CHECK_THROWS_AS(json::parser(s1).parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser(s2).parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser(s3).parse(), std::invalid_argument); CHECK_THROWS_AS(json::parser(s4).parse(), std::invalid_argument); CHECK_THROWS_WITH(json::parser(s1).parse(), "parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser(s2).parse(), "parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser(s3).parse(), "parse error - unexpected '\"'"); CHECK_THROWS_WITH(json::parser(s4).parse(), "parse error - unexpected '\"'"); } } } // missing part of a surrogate pair CHECK_THROWS_AS(json::parse("\"\\uD80C\""), std::invalid_argument); CHECK_THROWS_WITH(json::parse("\"\\uD80C\""), "missing low surrogate"); // invalid surrogate pair CHECK_THROWS_AS(json::parse("\"\\uD80C\\uD80C\""), std::invalid_argument); CHECK_THROWS_AS(json::parse("\"\\uD80C\\u0000\""), std::invalid_argument); CHECK_THROWS_AS(json::parse("\"\\uD80C\\uFFFF\""), std::invalid_argument); CHECK_THROWS_WITH(json::parse("\"\\uD80C\\uD80C\""), "missing or wrong low surrogate"); CHECK_THROWS_WITH(json::parse("\"\\uD80C\\u0000\""), "missing or wrong low surrogate"); CHECK_THROWS_WITH(json::parse("\"\\uD80C\\uFFFF\""), "missing or wrong low surrogate"); } SECTION("callback function") { auto s_object = R"( { "foo": 2, "bar": { "baz": 1 } } )"; auto s_array = R"( [1,2,[3,4,5],4,5] )"; SECTION("filter nothing") { json j_object = json::parse(s_object, [](int, json::parse_event_t, const json&) { return true; }); CHECK (j_object == json({{"foo", 2}, {"bar", {{"baz", 1}}}})); json j_array = json::parse(s_array, [](int, json::parse_event_t, const json&) { return true; }); CHECK (j_array == json({1, 2, {3, 4, 5}, 4, 5})); } SECTION("filter everything") { json j_object = json::parse(s_object, [](int, json::parse_event_t, const json&) { return false; }); // the top-level object will be discarded, leaving a null CHECK (j_object.is_null()); json j_array = json::parse(s_array, [](int, json::parse_event_t, const json&) { return false; }); // the top-level array will be discarded, leaving a null CHECK (j_array.is_null()); } SECTION("filter specific element") { json j_object = json::parse(s_object, [](int, json::parse_event_t, const json & j) { // filter all number(2) elements if (j == json(2)) { return false; } else { return true; } }); CHECK (j_object == json({{"bar", {{"baz", 1}}}})); json j_array = json::parse(s_array, [](int, json::parse_event_t, const json & j) { if (j == json(2)) { return false; } else { return true; } }); CHECK (j_array == json({1, {3, 4, 5}, 4, 5})); } SECTION("filter specific events") { SECTION("first closing event") { { json j_object = json::parse(s_object, [](int, json::parse_event_t e, const json&) { static bool first = true; if (e == json::parse_event_t::object_end and first) { first = false; return false; } else { return true; } }); // the first completed object will be discarded CHECK (j_object == json({{"foo", 2}})); } { json j_array = json::parse(s_array, [](int, json::parse_event_t e, const json&) { static bool first = true; if (e == json::parse_event_t::array_end and first) { first = false; return false; } else { return true; } }); // the first completed array will be discarded CHECK (j_array == json({1, 2, 4, 5})); } } } SECTION("special cases") { // the following test cases cover the situation in which an empty // object and array is discarded only after the closing character // has been read json j_empty_object = json::parse("{}", [](int, json::parse_event_t e, const json&) { if (e == json::parse_event_t::object_end) { return false; } else { return true; } }); CHECK(j_empty_object == json()); json j_empty_array = json::parse("[]", [](int, json::parse_event_t e, const json&) { if (e == json::parse_event_t::array_end) { return false; } else { return true; } }); CHECK(j_empty_array == json()); } } } TEST_CASE("README", "[hide]") { { // create an empty structure (null) json j; // add a number that is stored as double (note the implicit conversion of j to an object) j["pi"] = 3.141; // add a Boolean that is stored as bool j["happy"] = true; // add a string that is stored as std::string j["name"] = "Niels"; // add another null object by passing nullptr j["nothing"] = nullptr; // add an object inside the object j["answer"]["everything"] = 42; // add an array that is stored as std::vector (using an initializer list) j["list"] = { 1, 0, 2 }; // add another object (using an initializer list of pairs) j["object"] = { {"currency", "USD"}, {"value", 42.99} }; // instead, you could also write (which looks very similar to the JSON above) json j2 = { {"pi", 3.141}, {"happy", true}, {"name", "Niels"}, {"nothing", nullptr}, { "answer", { {"everything", 42} } }, {"list", {1, 0, 2}}, { "object", { {"currency", "USD"}, {"value", 42.99} } } }; } { // ways to express the empty array [] json empty_array_implicit = {{}}; json empty_array_explicit = json::array(); // a way to express the empty object {} json empty_object_explicit = json::object(); // a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]] json array_not_object = { json::array({"currency", "USD"}), json::array({"value", 42.99}) }; } { // create object from string literal json j = "{ \"happy\": true, \"pi\": 3.141 }"_json; // or even nicer with a raw string literal auto j2 = R"( { "happy": true, "pi": 3.141 } )"_json; // or explicitly auto j3 = json::parse("{ \"happy\": true, \"pi\": 3.141 }"); // explicit conversion to string std::string s = j.dump(); // {\"happy\":true,\"pi\":3.141} // serialization with pretty printing // pass in the amount of spaces to indent std::cout << j.dump(4) << std::endl; // { // "happy": true, // "pi": 3.141 // } std::cout << std::setw(2) << j << std::endl; } { // create an array using push_back json j; j.push_back("foo"); j.push_back(1); j.push_back(true); // iterate the array for (json::iterator it = j.begin(); it != j.end(); ++it) { std::cout << *it << '\n'; } // range-based for for (auto element : j) { std::cout << element << '\n'; } // getter/setter const std::string tmp = j[0]; j[1] = 42; bool foo = j.at(2); // other stuff j.size(); // 3 entries j.empty(); // false j.type(); // json::value_t::array j.clear(); // the array is empty again // comparison j == "[\"foo\", 1, true]"_json; // true // create an object json o; o["foo"] = 23; o["bar"] = false; o["baz"] = 3.141; // find an entry if (o.find("foo") != o.end()) { // there is an entry with key "foo" } } { std::vector c_vector {1, 2, 3, 4}; json j_vec(c_vector); // [1, 2, 3, 4] std::deque c_deque {1.2f, 2.3f, 3.4f, 5.6f}; json j_deque(c_deque); // [1.2, 2.3, 3.4, 5.6] std::list c_list {true, true, false, true}; json j_list(c_list); // [true, true, false, true] std::forward_list c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543}; json j_flist(c_flist); // [12345678909876, 23456789098765, 34567890987654, 45678909876543] std::array c_array {{1, 2, 3, 4}}; json j_array(c_array); // [1, 2, 3, 4] std::set c_set {"one", "two", "three", "four", "one"}; json j_set(c_set); // only one entry for "one" is used // ["four", "one", "three", "two"] std::unordered_set c_uset {"one", "two", "three", "four", "one"}; json j_uset(c_uset); // only one entry for "one" is used // maybe ["two", "three", "four", "one"] std::multiset c_mset {"one", "two", "one", "four"}; json j_mset(c_mset); // only one entry for "one" is used // maybe ["one", "two", "four"] std::unordered_multiset c_umset {"one", "two", "one", "four"}; json j_umset(c_umset); // both entries for "one" are used // maybe ["one", "two", "one", "four"] } { std::map c_map { {"one", 1}, {"two", 2}, {"three", 3} }; json j_map(c_map); // {"one": 1, "two": 2, "three": 3} std::unordered_map c_umap { {"one", 1.2f}, {"two", 2.3f}, {"three", 3.4f} }; json j_umap(c_umap); // {"one": 1.2, "two": 2.3, "three": 3.4} std::multimap c_mmap { {"one", true}, {"two", true}, {"three", false}, {"three", true} }; json j_mmap(c_mmap); // only one entry for key "three" is used // maybe {"one": true, "two": true, "three": true} std::unordered_multimap c_ummap { {"one", true}, {"two", true}, {"three", false}, {"three", true} }; json j_ummap(c_ummap); // only one entry for key "three" is used // maybe {"one": true, "two": true, "three": true} } { // strings std::string s1 = "Hello, world!"; json js = s1; std::string s2 = js; // Booleans bool b1 = true; json jb = b1; bool b2 = jb; // numbers int i = 42; json jn = i; double f = jn; // etc. std::string vs = js.get(); bool vb = jb.get(); int vi = jn.get(); // etc. } { // a JSON value json j_original = R"({ "baz": ["one", "two", "three"], "foo": "bar" })"_json; // access members with a JSON pointer (RFC 6901) j_original["/baz/1"_json_pointer]; // "two" // a JSON patch (RFC 6902) json j_patch = R"([ { "op": "replace", "path": "/baz", "value": "boo" }, { "op": "add", "path": "/hello", "value": ["world"] }, { "op": "remove", "path": "/foo"} ])"_json; // apply the patch json j_result = j_original.patch(j_patch); // { // "baz": "boo", // "hello": ["world"] // } // calculate a JSON patch from two JSON values json::diff(j_result, j_original); // [ // { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] }, // { "op":"remove","path":"/hello" }, // { "op":"add","path":"/foo","value":"bar" } // ] } } TEST_CASE("algorithms") { json j_array = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz"}; json j_object = {{"one", 1}, {"two", 2}}; SECTION("non-modifying sequence operations") { SECTION("std::all_of") { CHECK(std::all_of(j_array.begin(), j_array.end(), [](const json & value) { return value.size() > 0; })); CHECK(std::all_of(j_object.begin(), j_object.end(), [](const json & value) { return value.type() == json::value_t::number_integer; })); } SECTION("std::any_of") { CHECK(std::any_of(j_array.begin(), j_array.end(), [](const json & value) { return value.is_string() and value.get() == "foo"; })); CHECK(std::any_of(j_object.begin(), j_object.end(), [](const json & value) { return value.get() > 1; })); } SECTION("std::none_of") { CHECK(std::none_of(j_array.begin(), j_array.end(), [](const json & value) { return value.size() == 0; })); CHECK(std::none_of(j_object.begin(), j_object.end(), [](const json & value) { return value.get() <= 0; })); } SECTION("std::for_each") { SECTION("reading") { int sum = 0; std::for_each(j_array.cbegin(), j_array.cend(), [&sum](const json & value) { if (value.is_number()) { sum += static_cast(value); } }); CHECK(sum == 45); } SECTION("writing") { auto add17 = [](json & value) { if (value.is_array()) { value.push_back(17); } }; std::for_each(j_array.begin(), j_array.end(), add17); CHECK(j_array[6] == json({1, 2, 3, 17})); } } SECTION("std::count") { CHECK(std::count(j_array.begin(), j_array.end(), json(true)) == 1); } SECTION("std::count_if") { CHECK(std::count_if(j_array.begin(), j_array.end(), [](const json & value) { return (value.is_number()); }) == 3); CHECK(std::count_if(j_array.begin(), j_array.end(), [](const json&) { return true; }) == 9); } SECTION("std::mismatch") { json j_array2 = {13, 29, 3, {{"one", 1}, {"two", 2}, {"three", 3}}, true, false, {1, 2, 3}, "foo", "baz"}; auto res = std::mismatch(j_array.begin(), j_array.end(), j_array2.begin()); CHECK(*res.first == json({{"one", 1}, {"two", 2}})); CHECK(*res.second == json({{"one", 1}, {"two", 2}, {"three", 3}})); } SECTION("std::equal") { SECTION("using operator==") { CHECK(std::equal(j_array.begin(), j_array.end(), j_array.begin())); CHECK(std::equal(j_object.begin(), j_object.end(), j_object.begin())); CHECK(not std::equal(j_array.begin(), j_array.end(), j_object.begin())); } SECTION("using user-defined comparison") { // compare objects only by size of its elements json j_array2 = {13, 29, 3, {"Hello", "World"}, true, false, {{"one", 1}, {"two", 2}, {"three", 3}}, "foo", "baz"}; CHECK(not std::equal(j_array.begin(), j_array.end(), j_array2.begin())); CHECK(std::equal(j_array.begin(), j_array.end(), j_array2.begin(), [](const json & a, const json & b) { return (a.size() == b.size()); })); } } SECTION("std::find") { auto it = std::find(j_array.begin(), j_array.end(), json(false)); CHECK(std::distance(j_array.begin(), it) == 5); } SECTION("std::find_if") { auto it = std::find_if(j_array.begin(), j_array.end(), [](const json & value) { return value.is_boolean(); }); CHECK(std::distance(j_array.begin(), it) == 4); } SECTION("std::find_if_not") { auto it = std::find_if_not(j_array.begin(), j_array.end(), [](const json & value) { return value.is_number(); }); CHECK(std::distance(j_array.begin(), it) == 3); } SECTION("std::adjacent_find") { CHECK(std::adjacent_find(j_array.begin(), j_array.end()) == j_array.end()); CHECK(std::adjacent_find(j_array.begin(), j_array.end(), [](const json & v1, const json & v2) { return v1.type() == v2.type(); }) == j_array.begin()); } } SECTION("modifying sequence operations") { SECTION("std::reverse") { std::reverse(j_array.begin(), j_array.end()); CHECK(j_array == json({"baz", "foo", {1, 2, 3}, false, true, {{"one", 1}, {"two", 2}}, 3, 29, 13})); } SECTION("std::rotate") { std::rotate(j_array.begin(), j_array.begin() + 1, j_array.end()); CHECK(j_array == json({29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", 13})); } SECTION("std::partition") { auto it = std::partition(j_array.begin(), j_array.end(), [](const json & v) { return v.is_string(); }); CHECK(std::distance(j_array.begin(), it) == 2); CHECK(not it[2].is_string()); } } SECTION("sorting operations") { SECTION("std::sort") { SECTION("with standard comparison") { json j = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", nullptr}; std::sort(j.begin(), j.end()); CHECK(j == json({nullptr, false, true, 3, 13, 29, {{"one", 1}, {"two", 2}}, {1, 2, 3}, "baz", "foo"})); } SECTION("with user-defined comparison") { json j = {3, {{"one", 1}, {"two", 2}}, {1, 2, 3}, nullptr}; std::sort(j.begin(), j.end(), [](const json & a, const json & b) { return a.size() < b.size(); }); CHECK(j == json({nullptr, 3, {{"one", 1}, {"two", 2}}, {1, 2, 3}})); } SECTION("sorting an object") { json j({{"one", 1}, {"two", 2}}); CHECK_THROWS_AS(std::sort(j.begin(), j.end()), std::domain_error); CHECK_THROWS_WITH(std::sort(j.begin(), j.end()), "cannot use offsets with object iterators"); } } SECTION("std::partial_sort") { json j = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", nullptr}; std::partial_sort(j.begin(), j.begin() + 4, j.end()); CHECK(j == json({nullptr, false, true, 3, {{"one", 1}, {"two", 2}}, 29, {1, 2, 3}, "foo", "baz", 13})); } } SECTION("set operations") { SECTION("std::merge") { { json j1 = {2, 4, 6, 8}; json j2 = {1, 2, 3, 5, 7}; json j3; std::merge(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); CHECK(j3 == json({1, 2, 2, 3, 4, 5, 6, 7, 8})); } } SECTION("std::set_difference") { json j1 = {1, 2, 3, 4, 5, 6, 7, 8}; json j2 = {1, 2, 3, 5, 7}; json j3; std::set_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); CHECK(j3 == json({4, 6, 8})); } SECTION("std::set_intersection") { json j1 = {1, 2, 3, 4, 5, 6, 7, 8}; json j2 = {1, 2, 3, 5, 7}; json j3; std::set_intersection(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); CHECK(j3 == json({1, 2, 3, 5, 7})); } SECTION("std::set_union") { json j1 = {2, 4, 6, 8}; json j2 = {1, 2, 3, 5, 7}; json j3; std::set_union(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); CHECK(j3 == json({1, 2, 3, 4, 5, 6, 7, 8})); } SECTION("std::set_symmetric_difference") { json j1 = {2, 4, 6, 8}; json j2 = {1, 2, 3, 5, 7}; json j3; std::set_symmetric_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3)); CHECK(j3 == json({1, 3, 4, 5, 6, 7, 8})); } } SECTION("heap operations") { std::make_heap(j_array.begin(), j_array.end()); CHECK(std::is_heap(j_array.begin(), j_array.end())); std::sort_heap(j_array.begin(), j_array.end()); CHECK(j_array == json({false, true, 3, 13, 29, {{"one", 1}, {"two", 2}}, {1, 2, 3}, "baz", "foo"})); } } TEST_CASE("concepts") { SECTION("container requirements for json") { // X: container class: json // T: type of objects: json // a, b: values of type X: json // TABLE 96 - Container Requirements // X::value_type must return T CHECK((std::is_same::value)); // X::reference must return lvalue of T CHECK((std::is_same::value)); // X::const_reference must return const lvalue of T CHECK((std::is_same::value)); // X::iterator must return iterator whose value_type is T CHECK((std::is_same::value)); // X::iterator must meet the forward iterator requirements CHECK((std::is_base_of::iterator_category>::value)); // X::iterator must be convertible to X::const_iterator CHECK((std::is_convertible::value)); // X::const_iterator must return iterator whose value_type is T CHECK((std::is_same::value)); // X::const_iterator must meet the forward iterator requirements CHECK((std::is_base_of::iterator_category>::value)); // X::difference_type must return a signed integer CHECK((std::is_signed::value)); // X::difference_type must be identical to X::iterator::difference_type CHECK((std::is_same::value)); // X::difference_type must be identical to X::const_iterator::difference_type CHECK((std::is_same::value)); // X::size_type must return an unsigned integer CHECK((std::is_unsigned::value)); // X::size_type can represent any non-negative value of X::difference_type CHECK(std::numeric_limits::max() <= std::numeric_limits::max()); // the expression "X u" has the post-condition "u.empty()" { json u; CHECK(u.empty()); } // the expression "X()" has the post-condition "X().empty()" CHECK(json().empty()); } SECTION("class json") { SECTION("DefaultConstructible") { CHECK(std::is_nothrow_default_constructible::value); } SECTION("MoveConstructible") { CHECK(std::is_nothrow_move_constructible::value); } SECTION("CopyConstructible") { CHECK(std::is_copy_constructible::value); } SECTION("MoveAssignable") { CHECK(std::is_nothrow_move_assignable::value); } SECTION("CopyAssignable") { CHECK(std::is_copy_assignable::value); } SECTION("Destructible") { CHECK(std::is_nothrow_destructible::value); } SECTION("StandardLayoutType") { CHECK(std::is_standard_layout::value); } } SECTION("class iterator") { SECTION("CopyConstructible") { CHECK(std::is_nothrow_copy_constructible::value); CHECK(std::is_nothrow_copy_constructible::value); } SECTION("CopyAssignable") { // STL iterators used by json::iterator don't pass this test in Debug mode #if !defined(_MSC_VER) || (_ITERATOR_DEBUG_LEVEL == 0) CHECK(std::is_nothrow_copy_assignable::value); CHECK(std::is_nothrow_copy_assignable::value); #endif } SECTION("Destructible") { CHECK(std::is_nothrow_destructible::value); CHECK(std::is_nothrow_destructible::value); } SECTION("Swappable") { { json j {1, 2, 3}; json::iterator it1 = j.begin(); json::iterator it2 = j.end(); std::swap(it1, it2); CHECK(it1 == j.end()); CHECK(it2 == j.begin()); } { json j {1, 2, 3}; json::const_iterator it1 = j.cbegin(); json::const_iterator it2 = j.cend(); std::swap(it1, it2); CHECK(it1 == j.end()); CHECK(it2 == j.begin()); } } } } TEST_CASE("iterator_wrapper") { SECTION("object") { SECTION("value") { json j = {{"A", 1}, {"B", 2}}; int counter = 1; for (auto i : json::iterator_wrapper(j)) { switch (counter++) { case 1: { CHECK(i.key() == "A"); CHECK(i.value() == json(1)); break; } case 2: { CHECK(i.key() == "B"); CHECK(i.value() == json(2)); break; } default: { break; } } } CHECK(counter == 3); } SECTION("reference") { json j = {{"A", 1}, {"B", 2}}; int counter = 1; for (auto& i : json::iterator_wrapper(j)) { switch (counter++) { case 1: { CHECK(i.key() == "A"); CHECK(i.value() == json(1)); // change the value i.value() = json(11); CHECK(i.value() == json(11)); break; } case 2: { CHECK(i.key() == "B"); CHECK(i.value() == json(2)); // change the value i.value() = json(22); CHECK(i.value() == json(22)); break; } default: { break; } } } CHECK(counter == 3); // check if values where changed CHECK(j == json({{"A", 11}, {"B", 22}})); } SECTION("const value") { json j = {{"A", 1}, {"B", 2}}; int counter = 1; for (const auto i : json::iterator_wrapper(j)) { switch (counter++) { case 1: { CHECK(i.key() == "A"); CHECK(i.value() == json(1)); break; } case 2: { CHECK(i.key() == "B"); CHECK(i.value() == json(2)); break; } default: { break; } } } CHECK(counter == 3); } SECTION("const reference") { json j = {{"A", 1}, {"B", 2}}; int counter = 1; for (const auto& i : json::iterator_wrapper(j)) { switch (counter++) { case 1: { CHECK(i.key() == "A"); CHECK(i.value() == json(1)); break; } case 2: { CHECK(i.key() == "B"); CHECK(i.value() == json(2)); break; } default: { break; } } } CHECK(counter == 3); } } SECTION("const object") { SECTION("value") { const json j = {{"A", 1}, {"B", 2}}; int counter = 1; for (auto i : json::iterator_wrapper(j)) { switch (counter++) { case 1: { CHECK(i.key() == "A"); CHECK(i.value() == json(1)); break; } case 2: { CHECK(i.key() == "B"); CHECK(i.value() == json(2)); break; } default: { break; } } } CHECK(counter == 3); } SECTION("reference") { const json j = {{"A", 1}, {"B", 2}}; int counter = 1; for (auto& i : json::iterator_wrapper(j)) { switch (counter++) { case 1: { CHECK(i.key() == "A"); CHECK(i.value() == json(1)); break; } case 2: { CHECK(i.key() == "B"); CHECK(i.value() == json(2)); break; } default: { break; } } } CHECK(counter == 3); } SECTION("const value") { const json j = {{"A", 1}, {"B", 2}}; int counter = 1; for (const auto i : json::iterator_wrapper(j)) { switch (counter++) { case 1: { CHECK(i.key() == "A"); CHECK(i.value() == json(1)); break; } case 2: { CHECK(i.key() == "B"); CHECK(i.value() == json(2)); break; } default: { break; } } } CHECK(counter == 3); } SECTION("const reference") { const json j = {{"A", 1}, {"B", 2}}; int counter = 1; for (const auto& i : json::iterator_wrapper(j)) { switch (counter++) { case 1: { CHECK(i.key() == "A"); CHECK(i.value() == json(1)); break; } case 2: { CHECK(i.key() == "B"); CHECK(i.value() == json(2)); break; } default: { break; } } } CHECK(counter == 3); } } SECTION("array") { SECTION("value") { json j = {"A", "B"}; int counter = 1; for (auto i : json::iterator_wrapper(j)) { switch (counter++) { case 1: { CHECK(i.key() == "0"); CHECK(i.value() == "A"); break; } case 2: { CHECK(i.key() == "1"); CHECK(i.value() == "B"); break; } default: { break; } } } CHECK(counter == 3); } SECTION("reference") { json j = {"A", "B"}; int counter = 1; for (auto& i : json::iterator_wrapper(j)) { switch (counter++) { case 1: { CHECK(i.key() == "0"); CHECK(i.value() == "A"); // change the value i.value() = "AA"; CHECK(i.value() == "AA"); break; } case 2: { CHECK(i.key() == "1"); CHECK(i.value() == "B"); // change the value i.value() = "BB"; CHECK(i.value() == "BB"); break; } default: { break; } } } CHECK(counter == 3); // check if values where changed CHECK(j == json({"AA", "BB"})); } SECTION("const value") { json j = {"A", "B"}; int counter = 1; for (const auto i : json::iterator_wrapper(j)) { switch (counter++) { case 1: { CHECK(i.key() == "0"); CHECK(i.value() == "A"); break; } case 2: { CHECK(i.key() == "1"); CHECK(i.value() == "B"); break; } default: { break; } } } CHECK(counter == 3); } SECTION("const reference") { json j = {"A", "B"}; int counter = 1; for (const auto& i : json::iterator_wrapper(j)) { switch (counter++) { case 1: { CHECK(i.key() == "0"); CHECK(i.value() == "A"); break; } case 2: { CHECK(i.key() == "1"); CHECK(i.value() == "B"); break; } default: { break; } } } CHECK(counter == 3); } } SECTION("const array") { SECTION("value") { const json j = {"A", "B"}; int counter = 1; for (auto i : json::iterator_wrapper(j)) { switch (counter++) { case 1: { CHECK(i.key() == "0"); CHECK(i.value() == "A"); break; } case 2: { CHECK(i.key() == "1"); CHECK(i.value() == "B"); break; } default: { break; } } } CHECK(counter == 3); } SECTION("reference") { const json j = {"A", "B"}; int counter = 1; for (auto& i : json::iterator_wrapper(j)) { switch (counter++) { case 1: { CHECK(i.key() == "0"); CHECK(i.value() == "A"); break; } case 2: { CHECK(i.key() == "1"); CHECK(i.value() == "B"); break; } default: { break; } } } CHECK(counter == 3); } SECTION("const value") { const json j = {"A", "B"}; int counter = 1; for (const auto i : json::iterator_wrapper(j)) { switch (counter++) { case 1: { CHECK(i.key() == "0"); CHECK(i.value() == "A"); break; } case 2: { CHECK(i.key() == "1"); CHECK(i.value() == "B"); break; } default: { break; } } } CHECK(counter == 3); } SECTION("const reference") { const json j = {"A", "B"}; int counter = 1; for (const auto& i : json::iterator_wrapper(j)) { switch (counter++) { case 1: { CHECK(i.key() == "0"); CHECK(i.value() == "A"); break; } case 2: { CHECK(i.key() == "1"); CHECK(i.value() == "B"); break; } default: { break; } } } CHECK(counter == 3); } } SECTION("primitive") { SECTION("value") { json j = 1; int counter = 1; for (auto i : json::iterator_wrapper(j)) { ++counter; CHECK(i.key() == ""); CHECK(i.value() == json(1)); } CHECK(counter == 2); } SECTION("reference") { json j = 1; int counter = 1; for (auto& i : json::iterator_wrapper(j)) { ++counter; CHECK(i.key() == ""); CHECK(i.value() == json(1)); // change value i.value() = json(2); } CHECK(counter == 2); // check if value has changed CHECK(j == json(2)); } SECTION("const value") { json j = 1; int counter = 1; for (const auto i : json::iterator_wrapper(j)) { ++counter; CHECK(i.key() == ""); CHECK(i.value() == json(1)); } CHECK(counter == 2); } SECTION("const reference") { json j = 1; int counter = 1; for (const auto& i : json::iterator_wrapper(j)) { ++counter; CHECK(i.key() == ""); CHECK(i.value() == json(1)); } CHECK(counter == 2); } } SECTION("const primitive") { SECTION("value") { const json j = 1; int counter = 1; for (auto i : json::iterator_wrapper(j)) { ++counter; CHECK(i.key() == ""); CHECK(i.value() == json(1)); } CHECK(counter == 2); } SECTION("reference") { const json j = 1; int counter = 1; for (auto& i : json::iterator_wrapper(j)) { ++counter; CHECK(i.key() == ""); CHECK(i.value() == json(1)); } CHECK(counter == 2); } SECTION("const value") { const json j = 1; int counter = 1; for (const auto i : json::iterator_wrapper(j)) { ++counter; CHECK(i.key() == ""); CHECK(i.value() == json(1)); } CHECK(counter == 2); } SECTION("const reference") { const json j = 1; int counter = 1; for (const auto& i : json::iterator_wrapper(j)) { ++counter; CHECK(i.key() == ""); CHECK(i.value() == json(1)); } CHECK(counter == 2); } } } TEST_CASE("compliance tests from json.org") { // test cases are from http://json.org/JSON_checker/ SECTION("expected failures") { for (auto filename : { //"test/data/json_tests/fail1.json", "test/data/json_tests/fail2.json", "test/data/json_tests/fail3.json", "test/data/json_tests/fail4.json", "test/data/json_tests/fail5.json", "test/data/json_tests/fail6.json", "test/data/json_tests/fail7.json", "test/data/json_tests/fail8.json", "test/data/json_tests/fail9.json", "test/data/json_tests/fail10.json", "test/data/json_tests/fail11.json", "test/data/json_tests/fail12.json", "test/data/json_tests/fail13.json", "test/data/json_tests/fail14.json", "test/data/json_tests/fail15.json", "test/data/json_tests/fail16.json", "test/data/json_tests/fail17.json", //"test/data/json_tests/fail18.json", "test/data/json_tests/fail19.json", "test/data/json_tests/fail20.json", "test/data/json_tests/fail21.json", "test/data/json_tests/fail22.json", "test/data/json_tests/fail23.json", "test/data/json_tests/fail24.json", "test/data/json_tests/fail25.json", "test/data/json_tests/fail26.json", "test/data/json_tests/fail27.json", "test/data/json_tests/fail28.json", "test/data/json_tests/fail29.json", "test/data/json_tests/fail30.json", "test/data/json_tests/fail31.json", "test/data/json_tests/fail32.json", "test/data/json_tests/fail33.json" }) { CAPTURE(filename); json j; std::ifstream f(filename); CHECK_THROWS_AS(j << f, std::invalid_argument); } } SECTION("expected passes") { for (auto filename : { "test/data/json_tests/pass1.json", "test/data/json_tests/pass2.json", "test/data/json_tests/pass3.json" }) { CAPTURE(filename); json j; std::ifstream f(filename); CHECK_NOTHROW(j << f); } } } TEST_CASE("compliance tests from nativejson-benchmark") { // test cases from https://github.com/miloyip/nativejson-benchmark/blob/master/src/main.cpp SECTION("doubles") { auto TEST_DOUBLE = [](const std::string & json_string, const double expected) { CAPTURE(json_string); CAPTURE(expected); CHECK(json::parse(json_string)[0].get() == Approx(expected)); }; TEST_DOUBLE("[0.0]", 0.0); TEST_DOUBLE("[-0.0]", -0.0); TEST_DOUBLE("[1.0]", 1.0); TEST_DOUBLE("[-1.0]", -1.0); TEST_DOUBLE("[1.5]", 1.5); TEST_DOUBLE("[-1.5]", -1.5); TEST_DOUBLE("[3.1416]", 3.1416); TEST_DOUBLE("[1E10]", 1E10); TEST_DOUBLE("[1e10]", 1e10); TEST_DOUBLE("[1E+10]", 1E+10); TEST_DOUBLE("[1E-10]", 1E-10); TEST_DOUBLE("[-1E10]", -1E10); TEST_DOUBLE("[-1e10]", -1e10); TEST_DOUBLE("[-1E+10]", -1E+10); TEST_DOUBLE("[-1E-10]", -1E-10); TEST_DOUBLE("[1.234E+10]", 1.234E+10); TEST_DOUBLE("[1.234E-10]", 1.234E-10); TEST_DOUBLE("[1.79769e+308]", 1.79769e+308); TEST_DOUBLE("[2.22507e-308]", 2.22507e-308); TEST_DOUBLE("[-1.79769e+308]", -1.79769e+308); TEST_DOUBLE("[-2.22507e-308]", -2.22507e-308); TEST_DOUBLE("[4.9406564584124654e-324]", 4.9406564584124654e-324); // minimum denormal TEST_DOUBLE("[2.2250738585072009e-308]", 2.2250738585072009e-308); // Max subnormal double TEST_DOUBLE("[2.2250738585072014e-308]", 2.2250738585072014e-308); // Min normal positive double TEST_DOUBLE("[1.7976931348623157e+308]", 1.7976931348623157e+308); // Max double TEST_DOUBLE("[1e-10000]", 0.0); // must underflow TEST_DOUBLE("[18446744073709551616]", 18446744073709551616.0); // 2^64 (max of uint64_t + 1, force to use double) TEST_DOUBLE("[-9223372036854775809]", -9223372036854775809.0); // -2^63 - 1(min of int64_t + 1, force to use double) TEST_DOUBLE("[0.9868011474609375]", 0.9868011474609375); // https://github.com/miloyip/rapidjson/issues/120 TEST_DOUBLE("[123e34]", 123e34); // Fast Path Cases In Disguise TEST_DOUBLE("[45913141877270640000.0]", 45913141877270640000.0); TEST_DOUBLE("[2.2250738585072011e-308]", 2.2250738585072011e-308); //TEST_DOUBLE("[1e-00011111111111]", 0.0); //TEST_DOUBLE("[-1e-00011111111111]", -0.0); TEST_DOUBLE("[1e-214748363]", 0.0); TEST_DOUBLE("[1e-214748364]", 0.0); //TEST_DOUBLE("[1e-21474836311]", 0.0); TEST_DOUBLE("[0.017976931348623157e+310]", 1.7976931348623157e+308); // Max double in another form // Since // abs((2^-1022 - 2^-1074) - 2.2250738585072012e-308) = 3.109754131239141401123495768877590405345064751974375599... ¡Á 10^-324 // abs((2^-1022) - 2.2250738585072012e-308) = 1.830902327173324040642192159804623318305533274168872044... ¡Á 10 ^ -324 // So 2.2250738585072012e-308 should round to 2^-1022 = 2.2250738585072014e-308 TEST_DOUBLE("[2.2250738585072012e-308]", 2.2250738585072014e-308); // More closer to normal/subnormal boundary // boundary = 2^-1022 - 2^-1075 = 2.225073858507201136057409796709131975934819546351645648... ¡Á 10^-308 TEST_DOUBLE("[2.22507385850720113605740979670913197593481954635164564e-308]", 2.2250738585072009e-308); TEST_DOUBLE("[2.22507385850720113605740979670913197593481954635164565e-308]", 2.2250738585072014e-308); // 1.0 is in (1.0 - 2^-54, 1.0 + 2^-53) // 1.0 - 2^-54 = 0.999999999999999944488848768742172978818416595458984375 TEST_DOUBLE("[0.999999999999999944488848768742172978818416595458984375]", 1.0); // round to even TEST_DOUBLE("[0.999999999999999944488848768742172978818416595458984374]", 0.99999999999999989); // previous double TEST_DOUBLE("[0.999999999999999944488848768742172978818416595458984376]", 1.0); // next double // 1.0 + 2^-53 = 1.00000000000000011102230246251565404236316680908203125 TEST_DOUBLE("[1.00000000000000011102230246251565404236316680908203125]", 1.0); // round to even TEST_DOUBLE("[1.00000000000000011102230246251565404236316680908203124]", 1.0); // previous double TEST_DOUBLE("[1.00000000000000011102230246251565404236316680908203126]", 1.00000000000000022); // next double // Numbers from https://github.com/floitsch/double-conversion/blob/master/test/cctest/test-strtod.cc TEST_DOUBLE("[72057594037927928.0]", 72057594037927928.0); TEST_DOUBLE("[72057594037927936.0]", 72057594037927936.0); TEST_DOUBLE("[72057594037927932.0]", 72057594037927936.0); TEST_DOUBLE("[7205759403792793199999e-5]", 72057594037927928.0); TEST_DOUBLE("[7205759403792793200001e-5]", 72057594037927936.0); TEST_DOUBLE("[9223372036854774784.0]", 9223372036854774784.0); TEST_DOUBLE("[9223372036854775808.0]", 9223372036854775808.0); TEST_DOUBLE("[9223372036854775296.0]", 9223372036854775808.0); TEST_DOUBLE("[922337203685477529599999e-5]", 9223372036854774784.0); TEST_DOUBLE("[922337203685477529600001e-5]", 9223372036854775808.0); TEST_DOUBLE("[10141204801825834086073718800384]", 10141204801825834086073718800384.0); TEST_DOUBLE("[10141204801825835211973625643008]", 10141204801825835211973625643008.0); TEST_DOUBLE("[10141204801825834649023672221696]", 10141204801825835211973625643008.0); TEST_DOUBLE("[1014120480182583464902367222169599999e-5]", 10141204801825834086073718800384.0); TEST_DOUBLE("[1014120480182583464902367222169600001e-5]", 10141204801825835211973625643008.0); TEST_DOUBLE("[5708990770823838890407843763683279797179383808]", 5708990770823838890407843763683279797179383808.0); TEST_DOUBLE("[5708990770823839524233143877797980545530986496]", 5708990770823839524233143877797980545530986496.0); TEST_DOUBLE("[5708990770823839207320493820740630171355185152]", 5708990770823839524233143877797980545530986496.0); TEST_DOUBLE("[5708990770823839207320493820740630171355185151999e-3]", 5708990770823838890407843763683279797179383808.0); TEST_DOUBLE("[5708990770823839207320493820740630171355185152001e-3]", 5708990770823839524233143877797980545530986496.0); { char n1e308[312]; // '1' followed by 308 '0' n1e308[0] = '['; n1e308[1] = '1'; for (int j = 2; j < 310; j++) { n1e308[j] = '0'; } n1e308[310] = ']'; n1e308[311] = '\0'; TEST_DOUBLE(n1e308, 1E308); } // Cover trimming TEST_DOUBLE( "[2.22507385850720113605740979670913197593481954635164564802342610972482222202107694551652952390813508" "7914149158913039621106870086438694594645527657207407820621743379988141063267329253552286881372149012" "9811224514518898490572223072852551331557550159143974763979834118019993239625482890171070818506906306" "6665599493827577257201576306269066333264756530000924588831643303777979186961204949739037782970490505" "1080609940730262937128958950003583799967207254304360284078895771796150945516748243471030702609144621" "5722898802581825451803257070188608721131280795122334262883686223215037756666225039825343359745688844" "2390026549819838548794829220689472168983109969836584681402285424333066033985088644580400103493397042" "7567186443383770486037861622771738545623065874679014086723327636718751234567890123456789012345678901" "e-308]", 2.2250738585072014e-308); } SECTION("strings") { auto TEST_STRING = [](const std::string & json_string, const std::string & expected) { CAPTURE(json_string); CAPTURE(expected); CHECK(json::parse(json_string)[0].get() == expected); }; TEST_STRING("[\"\"]", ""); TEST_STRING("[\"Hello\"]", "Hello"); TEST_STRING("[\"Hello\\nWorld\"]", "Hello\nWorld"); //TEST_STRING("[\"Hello\\u0000World\"]", "Hello\0World"); TEST_STRING("[\"\\\"\\\\/\\b\\f\\n\\r\\t\"]", "\"\\/\b\f\n\r\t"); TEST_STRING("[\"\\u0024\"]", "\x24"); // Dollar sign U+0024 TEST_STRING("[\"\\u00A2\"]", "\xC2\xA2"); // Cents sign U+00A2 TEST_STRING("[\"\\u20AC\"]", "\xE2\x82\xAC"); // Euro sign U+20AC TEST_STRING("[\"\\uD834\\uDD1E\"]", "\xF0\x9D\x84\x9E"); // G clef sign U+1D11E } SECTION("roundtrip") { // test cases are from https://github.com/miloyip/nativejson-benchmark/tree/master/test/data/roundtrip for (auto filename : { "test/data/json_roundtrip/roundtrip01.json", "test/data/json_roundtrip/roundtrip02.json", "test/data/json_roundtrip/roundtrip03.json", "test/data/json_roundtrip/roundtrip04.json", "test/data/json_roundtrip/roundtrip05.json", "test/data/json_roundtrip/roundtrip06.json", "test/data/json_roundtrip/roundtrip07.json", "test/data/json_roundtrip/roundtrip08.json", "test/data/json_roundtrip/roundtrip09.json", "test/data/json_roundtrip/roundtrip10.json", "test/data/json_roundtrip/roundtrip11.json", "test/data/json_roundtrip/roundtrip12.json", "test/data/json_roundtrip/roundtrip13.json", "test/data/json_roundtrip/roundtrip14.json", "test/data/json_roundtrip/roundtrip15.json", "test/data/json_roundtrip/roundtrip16.json", "test/data/json_roundtrip/roundtrip17.json", "test/data/json_roundtrip/roundtrip18.json", "test/data/json_roundtrip/roundtrip19.json", "test/data/json_roundtrip/roundtrip20.json", "test/data/json_roundtrip/roundtrip21.json", "test/data/json_roundtrip/roundtrip22.json", "test/data/json_roundtrip/roundtrip23.json", //"test/data/json_roundtrip/roundtrip24.json", // roundtrip error //"test/data/json_roundtrip/roundtrip25.json", // roundtrip error //"test/data/json_roundtrip/roundtrip26.json", // roundtrip error //"test/data/json_roundtrip/roundtrip27.json", // roundtrip error //"test/data/json_roundtrip/roundtrip28.json", // roundtrip error "test/data/json_roundtrip/roundtrip29.json", //"test/data/json_roundtrip/roundtrip30.json", // roundtrip error //"test/data/json_roundtrip/roundtrip31.json", // roundtrip error "test/data/json_roundtrip/roundtrip32.json" }) { CAPTURE(filename); std::ifstream f(filename); std::string json_string( (std::istreambuf_iterator(f) ), (std::istreambuf_iterator()) ); json j = json::parse(json_string); CHECK(j.dump() == json_string); } } } TEST_CASE("test suite from json-test-suite") { SECTION("read all sample.json") { // read a file with all unicode characters stored as single-character // strings in a JSON array std::ifstream f("test/data/json_testsuite/sample.json"); json j; CHECK_NOTHROW(j << f); // the array has 3 elements CHECK(j.size() == 3); } } TEST_CASE("json.org examples") { // here, we list all JSON values from http://json.org/example SECTION("1.json") { std::ifstream f("test/data/json.org/1.json"); json j; CHECK_NOTHROW(j << f); } SECTION("2.json") { std::ifstream f("test/data/json.org/2.json"); json j; CHECK_NOTHROW(j << f); } SECTION("3.json") { std::ifstream f("test/data/json.org/3.json"); json j; CHECK_NOTHROW(j << f); } SECTION("4.json") { std::ifstream f("test/data/json.org/4.json"); json j; CHECK_NOTHROW(j << f); } SECTION("5.json") { std::ifstream f("test/data/json.org/5.json"); json j; CHECK_NOTHROW(j << f); } } TEST_CASE("RFC 7159 examples") { // here, we list all JSON values from the RFC 7159 document SECTION("7. Strings") { CHECK(json::parse("\"\\u005C\"") == json("\\")); CHECK(json::parse("\"\\uD834\\uDD1E\"") == json("𝄞")); CHECK(json::parse("\"𝄞\"") == json("𝄞")); } SECTION("8.3 String Comparison") { CHECK(json::parse("\"a\\b\"") == json::parse("\"a\u005Cb\"")); } SECTION("13 Examples") { { CHECK_NOTHROW(json(R"( { "Image": { "Width": 800, "Height": 600, "Title": "View from 15th Floor", "Thumbnail": { "Url": "http://www.example.com/image/481989943", "Height": 125, "Width": 100 }, "Animated" : false, "IDs": [116, 943, 234, 38793] } } )")); } { CHECK_NOTHROW(json(R"( [ { "precision": "zip", "Latitude": 37.7668, "Longitude": -122.3959, "Address": "", "City": "SAN FRANCISCO", "State": "CA", "Zip": "94107", "Country": "US" }, { "precision": "zip", "Latitude": 37.371991, "Longitude": -122.026020, "Address": "", "City": "SUNNYVALE", "State": "CA", "Zip": "94085", "Country": "US" } ])")); } CHECK(json::parse("\"Hello world!\"") == json("Hello world!")); CHECK(json::parse("42") == json(42)); CHECK(json::parse("true") == json(true)); } } TEST_CASE("Unicode", "[hide]") { SECTION("full enumeration of Unicode codepoints") { // create a string from a codepoint auto codepoint_to_unicode = [](std::size_t cp) { char* buffer = new char[10]; sprintf(buffer, "\\u%04lx", cp); std::string result(buffer); delete[] buffer; return result; }; // generate all codepoints for (std::size_t cp = 0; cp <= 0x10FFFFu; ++cp) { // The Unicode standard permanently reserves these code point // values for UTF-16 encoding of the high and low surrogates, and // they will never be assigned a character, so there should be no // reason to encode them. The official Unicode standard says that // no UTF forms, including UTF-16, can encode these code points. if (cp >= 0xD800u and cp <= 0xDFFFu) { continue; } std::string res; if (cp < 0x10000u) { // codepoint can be represented with 16 bit res += codepoint_to_unicode(cp); } else { // codepoint can be represented with a pair res += codepoint_to_unicode(0xd800u + (((cp - 0x10000u) >> 10) & 0x3ffu)); res += codepoint_to_unicode(0xdc00u + ((cp - 0x10000u) & 0x3ffu)); } try { json j1, j2; CHECK_NOTHROW(j1 = json::parse("\"" + res + "\"")); CHECK_NOTHROW(j2 = json::parse(j1.dump())); CHECK(j1 == j2); } catch (std::invalid_argument) { // we ignore parsing errors } } } SECTION("read all unicode characters") { // read a file with all unicode characters stored as single-character // strings in a JSON array std::ifstream f("test/data/json_nlohmann_tests/all_unicode.json"); json j; CHECK_NOTHROW(j << f); // the array has 1112064 + 1 elemnts (a terminating "null" value) CHECK(j.size() == 1112065); SECTION("check JSON Pointers") { for (auto s : j) { // skip non-string JSON values if (not s.is_string()) { continue; } std::string ptr = s; // tilde must be followed by 0 or 1 if (ptr == "~") { ptr += "0"; } // JSON Pointers must begin with "/" ptr = "/" + ptr; CHECK_NOTHROW(json::json_pointer("/" + ptr)); // check escape/unescape roundtrip auto escaped = json::json_pointer::escape(ptr); json::json_pointer::unescape(escaped); CHECK(escaped == ptr); } } } SECTION("ignore byte-order-mark") { // read a file with a UTF-8 BOM std::ifstream f("test/data/json_nlohmann_tests/bom.json"); json j; CHECK_NOTHROW(j << f); } SECTION("error for incomplete/wrong BOM") { CHECK_THROWS_AS(json::parse("\xef\xbb"), std::invalid_argument); CHECK_THROWS_AS(json::parse("\xef\xbb\xbb"), std::invalid_argument); } } TEST_CASE("JSON pointers") { SECTION("errors") { CHECK_THROWS_AS(json::json_pointer("foo"), std::domain_error); CHECK_THROWS_WITH(json::json_pointer("foo"), "JSON pointer must be empty or begin with '/'"); CHECK_THROWS_AS(json::json_pointer("/~~"), std::domain_error); CHECK_THROWS_WITH(json::json_pointer("/~~"), "escape error: '~' must be followed with '0' or '1'"); CHECK_THROWS_AS(json::json_pointer("/~"), std::domain_error); CHECK_THROWS_WITH(json::json_pointer("/~"), "escape error: '~' must be followed with '0' or '1'"); json::json_pointer p; CHECK_THROWS_AS(p.top(), std::domain_error); CHECK_THROWS_WITH(p.top(), "JSON pointer has no parent"); CHECK_THROWS_AS(p.pop_back(), std::domain_error); CHECK_THROWS_WITH(p.pop_back(), "JSON pointer has no parent"); } SECTION("examples from RFC 6901") { SECTION("nonconst access") { json j = R"( { "foo": ["bar", "baz"], "": 0, "a/b": 1, "c%d": 2, "e^f": 3, "g|h": 4, "i\\j": 5, "k\"l": 6, " ": 7, "m~n": 8 } )"_json; // the whole document CHECK(j[json::json_pointer()] == j); CHECK(j[json::json_pointer("")] == j); // array access CHECK(j[json::json_pointer("/foo")] == j["foo"]); CHECK(j[json::json_pointer("/foo/0")] == j["foo"][0]); CHECK(j[json::json_pointer("/foo/1")] == j["foo"][1]); CHECK(j["/foo/1"_json_pointer] == j["foo"][1]); // checked array access CHECK(j.at(json::json_pointer("/foo/0")) == j["foo"][0]); CHECK(j.at(json::json_pointer("/foo/1")) == j["foo"][1]); // empty string access CHECK(j[json::json_pointer("/")] == j[""]); // other cases CHECK(j[json::json_pointer("/ ")] == j[" "]); CHECK(j[json::json_pointer("/c%d")] == j["c%d"]); CHECK(j[json::json_pointer("/e^f")] == j["e^f"]); CHECK(j[json::json_pointer("/g|h")] == j["g|h"]); CHECK(j[json::json_pointer("/i\\j")] == j["i\\j"]); CHECK(j[json::json_pointer("/k\"l")] == j["k\"l"]); // checked access CHECK(j.at(json::json_pointer("/ ")) == j[" "]); CHECK(j.at(json::json_pointer("/c%d")) == j["c%d"]); CHECK(j.at(json::json_pointer("/e^f")) == j["e^f"]); CHECK(j.at(json::json_pointer("/g|h")) == j["g|h"]); CHECK(j.at(json::json_pointer("/i\\j")) == j["i\\j"]); CHECK(j.at(json::json_pointer("/k\"l")) == j["k\"l"]); // escaped access CHECK(j[json::json_pointer("/a~1b")] == j["a/b"]); CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]); // unescaped access CHECK_THROWS_AS(j[json::json_pointer("/a/b")], std::out_of_range); CHECK_THROWS_WITH(j[json::json_pointer("/a/b")], "unresolved reference token 'b'"); // "/a/b" works for JSON {"a": {"b": 42}} CHECK(json({{"a", {{"b", 42}}}})[json::json_pointer("/a/b")] == json(42)); // unresolved access json j_primitive = 1; CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], std::out_of_range); CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer], "unresolved reference token 'foo'"); CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), std::out_of_range); CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer), "unresolved reference token 'foo'"); } SECTION("const access") { const json j = R"( { "foo": ["bar", "baz"], "": 0, "a/b": 1, "c%d": 2, "e^f": 3, "g|h": 4, "i\\j": 5, "k\"l": 6, " ": 7, "m~n": 8 } )"_json; // the whole document CHECK(j[json::json_pointer()] == j); CHECK(j[json::json_pointer("")] == j); // array access CHECK(j[json::json_pointer("/foo")] == j["foo"]); CHECK(j[json::json_pointer("/foo/0")] == j["foo"][0]); CHECK(j[json::json_pointer("/foo/1")] == j["foo"][1]); CHECK(j["/foo/1"_json_pointer] == j["foo"][1]); // checked array access CHECK(j.at(json::json_pointer("/foo/0")) == j["foo"][0]); CHECK(j.at(json::json_pointer("/foo/1")) == j["foo"][1]); // empty string access CHECK(j[json::json_pointer("/")] == j[""]); // other cases CHECK(j[json::json_pointer("/ ")] == j[" "]); CHECK(j[json::json_pointer("/c%d")] == j["c%d"]); CHECK(j[json::json_pointer("/e^f")] == j["e^f"]); CHECK(j[json::json_pointer("/g|h")] == j["g|h"]); CHECK(j[json::json_pointer("/i\\j")] == j["i\\j"]); CHECK(j[json::json_pointer("/k\"l")] == j["k\"l"]); // checked access CHECK(j.at(json::json_pointer("/ ")) == j[" "]); CHECK(j.at(json::json_pointer("/c%d")) == j["c%d"]); CHECK(j.at(json::json_pointer("/e^f")) == j["e^f"]); CHECK(j.at(json::json_pointer("/g|h")) == j["g|h"]); CHECK(j.at(json::json_pointer("/i\\j")) == j["i\\j"]); CHECK(j.at(json::json_pointer("/k\"l")) == j["k\"l"]); // escaped access CHECK(j[json::json_pointer("/a~1b")] == j["a/b"]); CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]); // unescaped access CHECK_THROWS_AS(j.at(json::json_pointer("/a/b")), std::out_of_range); CHECK_THROWS_WITH(j.at(json::json_pointer("/a/b")), "key 'a' not found"); // unresolved access const json j_primitive = 1; CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], std::out_of_range); CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer], "unresolved reference token 'foo'"); CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), std::out_of_range); CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer), "unresolved reference token 'foo'"); } SECTION("user-defined string literal") { json j = R"( { "foo": ["bar", "baz"], "": 0, "a/b": 1, "c%d": 2, "e^f": 3, "g|h": 4, "i\\j": 5, "k\"l": 6, " ": 7, "m~n": 8 } )"_json; // the whole document CHECK(j[""_json_pointer] == j); // array access CHECK(j["/foo"_json_pointer] == j["foo"]); CHECK(j["/foo/0"_json_pointer] == j["foo"][0]); CHECK(j["/foo/1"_json_pointer] == j["foo"][1]); } } SECTION("array access") { SECTION("nonconst access") { json j = {1, 2, 3}; const json j_const = j; // check reading access CHECK(j["/0"_json_pointer] == j[0]); CHECK(j["/1"_json_pointer] == j[1]); CHECK(j["/2"_json_pointer] == j[2]); // assign to existing index j["/1"_json_pointer] = 13; CHECK(j[1] == json(13)); // assign to nonexisting index j["/3"_json_pointer] = 33; CHECK(j[3] == json(33)); // assign to nonexisting index (with gap) j["/5"_json_pointer] = 55; CHECK(j == json({1, 13, 3, 33, nullptr, 55})); // error with leading 0 CHECK_THROWS_AS(j["/01"_json_pointer], std::domain_error); CHECK_THROWS_WITH(j["/01"_json_pointer], "array index must not begin with '0'"); CHECK_THROWS_AS(j_const["/01"_json_pointer], std::domain_error); CHECK_THROWS_WITH(j_const["/01"_json_pointer], "array index must not begin with '0'"); CHECK_THROWS_AS(j.at("/01"_json_pointer), std::domain_error); CHECK_THROWS_WITH(j.at("/01"_json_pointer), "array index must not begin with '0'"); CHECK_THROWS_AS(j_const.at("/01"_json_pointer), std::domain_error); CHECK_THROWS_WITH(j_const.at("/01"_json_pointer), "array index must not begin with '0'"); // error with incorrect numbers CHECK_THROWS_AS(j["/one"_json_pointer] = 1, std::invalid_argument); // assign to "-" j["/-"_json_pointer] = 99; CHECK(j == json({1, 13, 3, 33, nullptr, 55, 99})); // error when using "-" in const object CHECK_THROWS_AS(j_const["/-"_json_pointer], std::out_of_range); CHECK_THROWS_WITH(j_const["/-"_json_pointer], "array index '-' (3) is out of range"); // error when using "-" with at CHECK_THROWS_AS(j.at("/-"_json_pointer), std::out_of_range); CHECK_THROWS_WITH(j.at("/-"_json_pointer), "array index '-' (7) is out of range"); CHECK_THROWS_AS(j_const.at("/-"_json_pointer), std::out_of_range); CHECK_THROWS_WITH(j_const.at("/-"_json_pointer), "array index '-' (3) is out of range"); } SECTION("const access") { const json j = {1, 2, 3}; // check reading access CHECK(j["/0"_json_pointer] == j[0]); CHECK(j["/1"_json_pointer] == j[1]); CHECK(j["/2"_json_pointer] == j[2]); // assign to nonexisting index CHECK_THROWS_AS(j.at("/3"_json_pointer), std::out_of_range); CHECK_THROWS_WITH(j.at("/3"_json_pointer), "array index 3 is out of range"); // assign to nonexisting index (with gap) CHECK_THROWS_AS(j.at("/5"_json_pointer), std::out_of_range); CHECK_THROWS_WITH(j.at("/5"_json_pointer), "array index 5 is out of range"); // assign to "-" CHECK_THROWS_AS(j["/-"_json_pointer], std::out_of_range); CHECK_THROWS_WITH(j["/-"_json_pointer], "array index '-' (3) is out of range"); CHECK_THROWS_AS(j.at("/-"_json_pointer), std::out_of_range); CHECK_THROWS_WITH(j.at("/-"_json_pointer), "array index '-' (3) is out of range"); } } SECTION("flatten") { json j = { {"pi", 3.141}, {"happy", true}, {"name", "Niels"}, {"nothing", nullptr}, { "answer", { {"everything", 42} } }, {"list", {1, 0, 2}}, { "object", { {"currency", "USD"}, {"value", 42.99}, {"", "empty string"}, {"/", "slash"}, {"~", "tilde"}, {"~1", "tilde1"} } } }; json j_flatten = { {"/pi", 3.141}, {"/happy", true}, {"/name", "Niels"}, {"/nothing", nullptr}, {"/answer/everything", 42}, {"/list/0", 1}, {"/list/1", 0}, {"/list/2", 2}, {"/object/currency", "USD"}, {"/object/value", 42.99}, {"/object/", "empty string"}, {"/object/~1", "slash"}, {"/object/~0", "tilde"}, {"/object/~01", "tilde1"} }; // check if flattened result is as expected CHECK(j.flatten() == j_flatten); // check if unflattened result is as expected CHECK(j_flatten.unflatten() == j); // error for nonobjects CHECK_THROWS_AS(json(1).unflatten(), std::domain_error); CHECK_THROWS_WITH(json(1).unflatten(), "only objects can be unflattened"); // error for nonprimitve values CHECK_THROWS_AS(json({{"/1", {1, 2, 3}}}).unflatten(), std::domain_error); CHECK_THROWS_WITH(json({{"/1", {1, 2, 3}}}).unflatten(), "values in object must be primitive"); // error for conflicting values json j_error = {{"", 42}, {"/foo", 17}}; CHECK_THROWS_AS(j_error.unflatten(), std::domain_error); CHECK_THROWS_WITH(j_error.unflatten(), "invalid value to unflatten"); // explicit roundtrip check CHECK(j.flatten().unflatten() == j); // roundtrip for primitive values json j_null; CHECK(j_null.flatten().unflatten() == j_null); json j_number = 42; CHECK(j_number.flatten().unflatten() == j_number); json j_boolean = false; CHECK(j_boolean.flatten().unflatten() == j_boolean); json j_string = "foo"; CHECK(j_string.flatten().unflatten() == j_string); // roundtrip for empty structured values (will be unflattened to null) json j_array(json::value_t::array); CHECK(j_array.flatten().unflatten() == json()); json j_object(json::value_t::object); CHECK(j_object.flatten().unflatten() == json()); } SECTION("string representation") { for (auto ptr : {"", "/foo", "/foo/0", "/", "/a~1b", "/c%d", "/e^f", "/g|h", "/i\\j", "/k\"l", "/ ", "/m~0n" }) { CHECK(json::json_pointer(ptr).to_string() == ptr); } } } TEST_CASE("JSON patch") { SECTION("examples from RFC 6902") { SECTION("4. Operations") { // the ordering of members in JSON objects is not significant: json op1 = R"({ "op": "add", "path": "/a/b/c", "value": "foo" })"_json; json op2 = R"({ "path": "/a/b/c", "op": "add", "value": "foo" })"_json; json op3 = R"({ "value": "foo", "path": "/a/b/c", "op": "add" })"_json; // check if the operation objects are equivalent CHECK(op1 == op2); CHECK(op1 == op3); } SECTION("4.1 add") { json patch = R"([{ "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] }])"_json; // However, the object itself or an array containing it does need // to exist, and it remains an error for that not to be the case. // For example, an "add" with a target location of "/a/b" starting // with this document json doc1 = R"({ "a": { "foo": 1 } })"_json; // is not an error, because "a" exists, and "b" will be added to // its value. CHECK_NOTHROW(doc1.patch(patch)); CHECK(doc1.patch(patch) == R"( { "a": { "foo": 1, "b": { "c": [ "foo", "bar" ] } } } )"_json); // It is an error in this document: json doc2 = R"({ "q": { "bar": 2 } })"_json; // because "a" does not exist. CHECK_THROWS_AS(doc2.patch(patch), std::out_of_range); CHECK_THROWS_WITH(doc2.patch(patch), "key 'a' not found"); } SECTION("4.2 remove") { // If removing an element from an array, any elements above the // specified index are shifted one position to the left. json doc = {1, 2, 3, 4}; json patch = {{{"op", "remove"}, {"path", "/1"}}}; CHECK(doc.patch(patch) == json({1, 3, 4})); } SECTION("A.1. Adding an Object Member") { // An example target JSON document: json doc = R"( { "foo": "bar"} )"_json; // A JSON Patch document: json patch = R"( [ { "op": "add", "path": "/baz", "value": "qux" } ] )"_json; // The resulting JSON document: json expected = R"( { "baz": "qux", "foo": "bar" } )"_json; // check if patched value is as expected CHECK(doc.patch(patch) == expected); // check roundtrip CHECK(doc.patch(json::diff(doc, expected)) == expected); } SECTION("A.2. Adding an Array Element") { // An example target JSON document: json doc = R"( { "foo": [ "bar", "baz" ] } )"_json; // A JSON Patch document: json patch = R"( [ { "op": "add", "path": "/foo/1", "value": "qux" } ] )"_json; // The resulting JSON document: json expected = R"( { "foo": [ "bar", "qux", "baz" ] } )"_json; // check if patched value is as expected CHECK(doc.patch(patch) == expected); // check roundtrip CHECK(doc.patch(json::diff(doc, expected)) == expected); } SECTION("A.3. Removing an Object Member") { // An example target JSON document: json doc = R"( { "baz": "qux", "foo": "bar" } )"_json; // A JSON Patch document: json patch = R"( [ { "op": "remove", "path": "/baz" } ] )"_json; // The resulting JSON document: json expected = R"( { "foo": "bar" } )"_json; // check if patched value is as expected CHECK(doc.patch(patch) == expected); // check roundtrip CHECK(doc.patch(json::diff(doc, expected)) == expected); } SECTION("A.4. Removing an Array Element") { // An example target JSON document: json doc = R"( { "foo": [ "bar", "qux", "baz" ] } )"_json; // A JSON Patch document: json patch = R"( [ { "op": "remove", "path": "/foo/1" } ] )"_json; // The resulting JSON document: json expected = R"( { "foo": [ "bar", "baz" ] } )"_json; // check if patched value is as expected CHECK(doc.patch(patch) == expected); // check roundtrip CHECK(doc.patch(json::diff(doc, expected)) == expected); } SECTION("A.5. Replacing a Value") { // An example target JSON document: json doc = R"( { "baz": "qux", "foo": "bar" } )"_json; // A JSON Patch document: json patch = R"( [ { "op": "replace", "path": "/baz", "value": "boo" } ] )"_json; json expected = R"( { "baz": "boo", "foo": "bar" } )"_json; // check if patched value is as expected CHECK(doc.patch(patch) == expected); // check roundtrip CHECK(doc.patch(json::diff(doc, expected)) == expected); } SECTION("A.6. Moving a Value") { // An example target JSON document: json doc = R"( { "foo": { "bar": "baz", "waldo": "fred" }, "qux": { "corge": "grault" } } )"_json; // A JSON Patch document: json patch = R"( [ { "op": "move", "from": "/foo/waldo", "path": "/qux/thud" } ] )"_json; // The resulting JSON document: json expected = R"( { "foo": { "bar": "baz" }, "qux": { "corge": "grault", "thud": "fred" } } )"_json; // check if patched value is as expected CHECK(doc.patch(patch) == expected); // check roundtrip CHECK(doc.patch(json::diff(doc, expected)) == expected); } SECTION("A.7. Moving a Value") { // An example target JSON document: json doc = R"( { "foo": [ "all", "grass", "cows", "eat" ] } )"_json; // A JSON Patch document: json patch = R"( [ { "op": "move", "from": "/foo/1", "path": "/foo/3" } ] )"_json; // The resulting JSON document: json expected = R"( { "foo": [ "all", "cows", "eat", "grass" ] } )"_json; // check if patched value is as expected CHECK(doc.patch(patch) == expected); // check roundtrip CHECK(doc.patch(json::diff(doc, expected)) == expected); } SECTION("A.8. Testing a Value: Success") { // An example target JSON document: json doc = R"( { "baz": "qux", "foo": [ "a", 2, "c" ] } )"_json; // A JSON Patch document that will result in successful evaluation: json patch = R"( [ { "op": "test", "path": "/baz", "value": "qux" }, { "op": "test", "path": "/foo/1", "value": 2 } ] )"_json; // check if evaluation does not throw CHECK_NOTHROW(doc.patch(patch)); // check if patched document is unchanged CHECK(doc.patch(patch) == doc); } SECTION("A.9. Testing a Value: Error") { // An example target JSON document: json doc = R"( { "baz": "qux" } )"_json; // A JSON Patch document that will result in an error condition: json patch = R"( [ { "op": "test", "path": "/baz", "value": "bar" } ] )"_json; // check that evaluation throws CHECK_THROWS_AS(doc.patch(patch), std::domain_error); CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump()); } SECTION("A.10. Adding a Nested Member Object") { // An example target JSON document: json doc = R"( { "foo": "bar" } )"_json; // A JSON Patch document: json patch = R"( [ { "op": "add", "path": "/child", "value": { "grandchild": { } } } ] )"_json; // The resulting JSON document: json expected = R"( { "foo": "bar", "child": { "grandchild": { } } } )"_json; // check if patched value is as expected CHECK(doc.patch(patch) == expected); // check roundtrip CHECK(doc.patch(json::diff(doc, expected)) == expected); } SECTION("A.11. Ignoring Unrecognized Elements") { // An example target JSON document: json doc = R"( { "foo": "bar" } )"_json; // A JSON Patch document: json patch = R"( [ { "op": "add", "path": "/baz", "value": "qux", "xyz": 123 } ] )"_json; json expected = R"( { "foo": "bar", "baz": "qux" } )"_json; // check if patched value is as expected CHECK(doc.patch(patch) == expected); // check roundtrip CHECK(doc.patch(json::diff(doc, expected)) == expected); } SECTION("A.12. Adding to a Nonexistent Target") { // An example target JSON document: json doc = R"( { "foo": "bar" } )"_json; // A JSON Patch document: json patch = R"( [ { "op": "add", "path": "/baz/bat", "value": "qux" } ] )"_json; // This JSON Patch document, applied to the target JSON document // above, would result in an error (therefore, it would not be // applied), because the "add" operation's target location that // references neither the root of the document, nor a member of // an existing object, nor a member of an existing array. CHECK_THROWS_AS(doc.patch(patch), std::out_of_range); CHECK_THROWS_WITH(doc.patch(patch), "key 'baz' not found"); } // A.13. Invalid JSON Patch Document // not applicable SECTION("A.14. Escape Ordering") { // An example target JSON document: json doc = R"( { "/": 9, "~1": 10 } )"_json; // A JSON Patch document: json patch = R"( [ {"op": "test", "path": "/~01", "value": 10} ] )"_json; json expected = R"( { "/": 9, "~1": 10 } )"_json; // check if patched value is as expected CHECK(doc.patch(patch) == expected); // check roundtrip CHECK(doc.patch(json::diff(doc, expected)) == expected); } SECTION("A.15. Comparing Strings and Numbers") { // An example target JSON document: json doc = R"( { "/": 9, "~1": 10 } )"_json; // A JSON Patch document that will result in an error condition: json patch = R"( [ {"op": "test", "path": "/~01", "value": "10"} ] )"_json; // check that evaluation throws CHECK_THROWS_AS(doc.patch(patch), std::domain_error); CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump()); } SECTION("A.16. Adding an Array Value") { // An example target JSON document: json doc = R"( { "foo": ["bar"] } )"_json; // A JSON Patch document: json patch = R"( [ { "op": "add", "path": "/foo/-", "value": ["abc", "def"] } ] )"_json; // The resulting JSON document: json expected = R"( { "foo": ["bar", ["abc", "def"]] } )"_json; // check if patched value is as expected CHECK(doc.patch(patch) == expected); // check roundtrip CHECK(doc.patch(json::diff(doc, expected)) == expected); } } SECTION("own examples") { SECTION("add") { SECTION("add to the root element") { // If the path is the root of the target document - the // specified value becomes the entire content of the target // document. // An example target JSON document: json doc = 17; // A JSON Patch document: json patch = R"( [ { "op": "add", "path": "", "value": [1,2,3] } ] )"_json; // The resulting JSON document: json expected = {1, 2, 3}; // check if patched value is as expected CHECK(doc.patch(patch) == expected); // check roundtrip CHECK(doc.patch(json::diff(doc, expected)) == expected); } SECTION("add to end of the array") { // The specified index MUST NOT be greater than the number of // elements in the array. The example below uses and index of // exactly the number of elements in the array which is legal. // An example target JSON document: json doc = {0, 1, 2}; // A JSON Patch document: json patch = R"( [ { "op": "add", "path": "/3", "value": 3 } ] )"_json; // The resulting JSON document: json expected = {0, 1, 2, 3}; // check if patched value is as expected CHECK(doc.patch(patch) == expected); // check roundtrip CHECK(doc.patch(json::diff(doc, expected)) == expected); } } SECTION("copy") { // An example target JSON document: json doc = R"( { "foo": { "bar": "baz", "waldo": "fred" }, "qux": { "corge": "grault" } } )"_json; // A JSON Patch document: json patch = R"( [ { "op": "copy", "from": "/foo/waldo", "path": "/qux/thud" } ] )"_json; // The resulting JSON document: json expected = R"( { "foo": { "bar": "baz", "waldo": "fred" }, "qux": { "corge": "grault", "thud": "fred" } } )"_json; // check if patched value is as expected CHECK(doc.patch(patch) == expected); // check roundtrip CHECK(doc.patch(json::diff(doc, expected)) == expected); } SECTION("replace") { json j = "string"; json patch = {{{"op", "replace"}, {"path", ""}, {"value", 1}}}; CHECK(j.patch(patch) == json(1)); } SECTION("documentation GIF") { { // a JSON patch json p1 = R"( [{"op": "add", "path": "/GB", "value": "London"}] )"_json; // a JSON value json source = R"( {"D": "Berlin", "F": "Paris"} )"_json; // apply the patch json target = source.patch(p1); // target = { "D": "Berlin", "F": "Paris", "GB": "London" } CHECK(target == R"({ "D": "Berlin", "F": "Paris", "GB": "London" })"_json); // create a diff from two JSONs json p2 = json::diff(target, source); // p2 = [{"op": "delete", "path": "/GB"}] CHECK(p2 == R"([{"op":"remove","path":"/GB"}])"_json); } { // a JSON value json j = {"good", "bad", "ugly"}; // a JSON pointer auto ptr = json::json_pointer("/2"); // use to access elements j[ptr] = {{"it", "cattivo"}}; CHECK(j == R"(["good","bad",{"it":"cattivo"}])"_json); // use user-defined string literal j["/2/en"_json_pointer] = "ugly"; CHECK(j == R"(["good","bad",{"en":"ugly","it":"cattivo"}])"_json); json flat = j.flatten(); CHECK(flat == R"({"/0":"good","/1":"bad","/2/en":"ugly","/2/it":"cattivo"})"_json); } } } SECTION("errors") { SECTION("unknown operation") { SECTION("not an array") { json j; json patch = {{"op", "add"}, {"path", ""}, {"value", 1}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "JSON patch must be an array of objects"); } SECTION("not an array of objects") { json j; json patch = {"op", "add", "path", "", "value", 1}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "JSON patch must be an array of objects"); } SECTION("missing 'op'") { json j; json patch = {{{"foo", "bar"}}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "operation must have member 'op'"); } SECTION("non-string 'op'") { json j; json patch = {{{"op", 1}}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "operation must have string member 'op'"); } SECTION("invalid operation") { json j; json patch = {{{"op", "foo"}, {"path", ""}}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "operation value 'foo' is invalid"); } } SECTION("add") { SECTION("missing 'path'") { json j; json patch = {{{"op", "add"}}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have member 'path'"); } SECTION("non-string 'path'") { json j; json patch = {{{"op", "add"}, {"path", 1}}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have string member 'path'"); } SECTION("missing 'value'") { json j; json patch = {{{"op", "add"}, {"path", ""}}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have member 'value'"); } SECTION("invalid array index") { json j = {1, 2}; json patch = {{{"op", "add"}, {"path", "/4"}, {"value", 4}}}; CHECK_THROWS_AS(j.patch(patch), std::out_of_range); CHECK_THROWS_WITH(j.patch(patch), "array index 4 is out of range"); } } SECTION("remove") { SECTION("missing 'path'") { json j; json patch = {{{"op", "remove"}}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "operation 'remove' must have member 'path'"); } SECTION("non-string 'path'") { json j; json patch = {{{"op", "remove"}, {"path", 1}}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "operation 'remove' must have string member 'path'"); } SECTION("nonexisting target location (array)") { json j = {1, 2, 3}; json patch = {{{"op", "remove"}, {"path", "/17"}}}; CHECK_THROWS_AS(j.patch(patch), std::out_of_range); CHECK_THROWS_WITH(j.patch(patch), "array index 17 is out of range"); } SECTION("nonexisting target location (object)") { json j = {{"foo", 1}, {"bar", 2}}; json patch = {{{"op", "remove"}, {"path", "/baz"}}}; CHECK_THROWS_AS(j.patch(patch), std::out_of_range); CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); } SECTION("root element as target location") { json j = "string"; json patch = {{{"op", "remove"}, {"path", ""}}}; CHECK_THROWS_AS(j.patch(patch), std::domain_error); CHECK_THROWS_WITH(j.patch(patch), "JSON pointer has no parent"); } } SECTION("replace") { SECTION("missing 'path'") { json j; json patch = {{{"op", "replace"}}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have member 'path'"); } SECTION("non-string 'path'") { json j; json patch = {{{"op", "replace"}, {"path", 1}}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have string member 'path'"); } SECTION("missing 'value'") { json j; json patch = {{{"op", "replace"}, {"path", ""}}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have member 'value'"); } SECTION("nonexisting target location (array)") { json j = {1, 2, 3}; json patch = {{{"op", "replace"}, {"path", "/17"}, {"value", 19}}}; CHECK_THROWS_AS(j.patch(patch), std::out_of_range); CHECK_THROWS_WITH(j.patch(patch), "array index 17 is out of range"); } SECTION("nonexisting target location (object)") { json j = {{"foo", 1}, {"bar", 2}}; json patch = {{{"op", "replace"}, {"path", "/baz"}, {"value", 3}}}; CHECK_THROWS_AS(j.patch(patch), std::out_of_range); CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); } } SECTION("move") { SECTION("missing 'path'") { json j; json patch = {{{"op", "move"}}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have member 'path'"); } SECTION("non-string 'path'") { json j; json patch = {{{"op", "move"}, {"path", 1}}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have string member 'path'"); } SECTION("missing 'from'") { json j; json patch = {{{"op", "move"}, {"path", ""}}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have member 'from'"); } SECTION("non-string 'from'") { json j; json patch = {{{"op", "move"}, {"path", ""}, {"from", 1}}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have string member 'from'"); } SECTION("nonexisting from location (array)") { json j = {1, 2, 3}; json patch = {{{"op", "move"}, {"path", "/0"}, {"from", "/5"}}}; CHECK_THROWS_AS(j.patch(patch), std::out_of_range); CHECK_THROWS_WITH(j.patch(patch), "array index 5 is out of range"); } SECTION("nonexisting from location (object)") { json j = {{"foo", 1}, {"bar", 2}}; json patch = {{{"op", "move"}, {"path", "/baz"}, {"from", "/baz"}}}; CHECK_THROWS_AS(j.patch(patch), std::out_of_range); CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); } } SECTION("copy") { SECTION("missing 'path'") { json j; json patch = {{{"op", "copy"}}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have member 'path'"); } SECTION("non-string 'path'") { json j; json patch = {{{"op", "copy"}, {"path", 1}}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have string member 'path'"); } SECTION("missing 'from'") { json j; json patch = {{{"op", "copy"}, {"path", ""}}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have member 'from'"); } SECTION("non-string 'from'") { json j; json patch = {{{"op", "copy"}, {"path", ""}, {"from", 1}}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have string member 'from'"); } SECTION("nonexisting from location (array)") { json j = {1, 2, 3}; json patch = {{{"op", "copy"}, {"path", "/0"}, {"from", "/5"}}}; CHECK_THROWS_AS(j.patch(patch), std::out_of_range); CHECK_THROWS_WITH(j.patch(patch), "array index 5 is out of range"); } SECTION("nonexisting from location (object)") { json j = {{"foo", 1}, {"bar", 2}}; json patch = {{{"op", "copy"}, {"path", "/fob"}, {"from", "/baz"}}}; CHECK_THROWS_AS(j.patch(patch), std::out_of_range); CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found"); } } SECTION("test") { SECTION("missing 'path'") { json j; json patch = {{{"op", "test"}}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have member 'path'"); } SECTION("non-string 'path'") { json j; json patch = {{{"op", "test"}, {"path", 1}}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have string member 'path'"); } SECTION("missing 'value'") { json j; json patch = {{{"op", "test"}, {"path", ""}}}; CHECK_THROWS_AS(j.patch(patch), std::invalid_argument); CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have member 'value'"); } } } SECTION("Examples from jsonpatch.com") { SECTION("Simple Example") { // The original document json doc = R"( { "baz": "qux", "foo": "bar" } )"_json; // The patch json patch = R"( [ { "op": "replace", "path": "/baz", "value": "boo" }, { "op": "add", "path": "/hello", "value": ["world"] }, { "op": "remove", "path": "/foo"} ] )"_json; // The result json result = R"( { "baz": "boo", "hello": ["world"] } )"_json; // check if patched value is as expected CHECK(doc.patch(patch) == result); // check roundtrip CHECK(doc.patch(json::diff(doc, result)) == result); } SECTION("Operations") { // The original document json doc = R"( { "biscuits": [ {"name":"Digestive"}, {"name": "Choco Liebniz"} ] } )"_json; SECTION("add") { // The patch json patch = R"( [ {"op": "add", "path": "/biscuits/1", "value": {"name": "Ginger Nut"}} ] )"_json; // The result json result = R"( { "biscuits": [ {"name": "Digestive"}, {"name": "Ginger Nut"}, {"name": "Choco Liebniz"} ] } )"_json; // check if patched value is as expected CHECK(doc.patch(patch) == result); // check roundtrip CHECK(doc.patch(json::diff(doc, result)) == result); } SECTION("remove") { // The patch json patch = R"( [ {"op": "remove", "path": "/biscuits"} ] )"_json; // The result json result = R"( {} )"_json; // check if patched value is as expected CHECK(doc.patch(patch) == result); // check roundtrip CHECK(doc.patch(json::diff(doc, result)) == result); } SECTION("replace") { // The patch json patch = R"( [ {"op": "replace", "path": "/biscuits/0/name", "value": "Chocolate Digestive"} ] )"_json; // The result json result = R"( { "biscuits": [ {"name": "Chocolate Digestive"}, {"name": "Choco Liebniz"} ] } )"_json; // check if patched value is as expected CHECK(doc.patch(patch) == result); // check roundtrip CHECK(doc.patch(json::diff(doc, result)) == result); } SECTION("copy") { // The patch json patch = R"( [ {"op": "copy", "from": "/biscuits/0", "path": "/best_biscuit"} ] )"_json; // The result json result = R"( { "biscuits": [ {"name": "Digestive"}, {"name": "Choco Liebniz"} ], "best_biscuit": { "name": "Digestive" } } )"_json; // check if patched value is as expected CHECK(doc.patch(patch) == result); // check roundtrip CHECK(doc.patch(json::diff(doc, result)) == result); } SECTION("move") { // The patch json patch = R"( [ {"op": "move", "from": "/biscuits", "path": "/cookies"} ] )"_json; // The result json result = R"( { "cookies": [ {"name": "Digestive"}, {"name": "Choco Liebniz"} ] } )"_json; // check if patched value is as expected CHECK(doc.patch(patch) == result); // check roundtrip CHECK(doc.patch(json::diff(doc, result)) == result); } SECTION("test") { // The patch json patch = R"( [ {"op": "test", "path": "/best_biscuit/name", "value": "Choco Liebniz"} ] )"_json; // the test will fail CHECK_THROWS_AS(doc.patch(patch), std::domain_error); CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump()); } } } SECTION("Examples from bruth.github.io/jsonpatch-js") { SECTION("add") { CHECK(R"( {} )"_json.patch( R"( [{"op": "add", "path": "/foo", "value": "bar"}] )"_json ) == R"( {"foo": "bar"} )"_json); CHECK(R"( {"foo": [1, 3]} )"_json.patch( R"( [{"op": "add", "path": "/foo", "value": "bar"}] )"_json ) == R"( {"foo": "bar"} )"_json); CHECK(R"( {"foo": [{}]} )"_json.patch( R"( [{"op": "add", "path": "/foo/0/bar", "value": "baz"}] )"_json ) == R"( {"foo": [{"bar": "baz"}]} )"_json); } SECTION("remove") { CHECK(R"( {"foo": "bar"} )"_json.patch( R"( [{"op": "remove", "path": "/foo"}] )"_json ) == R"( {} )"_json); CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( R"( [{"op": "remove", "path": "/foo/1"}] )"_json ) == R"( {"foo": [1, 3]} )"_json); CHECK(R"( {"foo": [{"bar": "baz"}]} )"_json.patch( R"( [{"op": "remove", "path": "/foo/0/bar"}] )"_json ) == R"( {"foo": [{}]} )"_json); } SECTION("replace") { CHECK(R"( {"foo": "bar"} )"_json.patch( R"( [{"op": "replace", "path": "/foo", "value": 1}] )"_json ) == R"( {"foo": 1} )"_json); CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( R"( [{"op": "replace", "path": "/foo/1", "value": 4}] )"_json ) == R"( {"foo": [1, 4, 3]} )"_json); CHECK(R"( {"foo": [{"bar": "baz"}]} )"_json.patch( R"( [{"op": "replace", "path": "/foo/0/bar", "value": 1}] )"_json ) == R"( {"foo": [{"bar": 1}]} )"_json); } SECTION("move") { CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( R"( [{"op": "move", "from": "/foo", "path": "/bar"}] )"_json ) == R"( {"bar": [1, 2, 3]} )"_json); } SECTION("copy") { CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch( R"( [{"op": "copy", "from": "/foo/1", "path": "/bar"}] )"_json ) == R"( {"foo": [1, 2, 3], "bar": 2} )"_json); } SECTION("copy") { CHECK_NOTHROW(R"( {"foo": "bar"} )"_json.patch( R"( [{"op": "test", "path": "/foo", "value": "bar"}] )"_json)); } } } TEST_CASE("regression tests") { SECTION("issue #60 - Double quotation mark is not parsed correctly") { SECTION("escape_dobulequote") { auto s = "[\"\\\"foo\\\"\"]"; json j = json::parse(s); auto expected = R"(["\"foo\""])"_json; CHECK(j == expected); } } SECTION("issue #70 - Handle infinity and NaN cases") { SECTION("NAN value") { CHECK(json(NAN) == json()); CHECK(json(json::number_float_t(NAN)) == json()); } SECTION("infinity") { CHECK(json(INFINITY) == json()); CHECK(json(json::number_float_t(INFINITY)) == json()); } } SECTION("pull request #71 - handle enum type") { enum { t = 0 }; json j = json::array(); j.push_back(t); j.push_back(json::object( { {"game_type", t} })); } SECTION("issue #76 - dump() / parse() not idempotent") { // create JSON object json fields; fields["one"] = std::string("one"); fields["two"] = std::string("two three"); fields["three"] = std::string("three \"four\""); // create another JSON object by deserializing the serialization std::string payload = fields.dump(); json parsed_fields = json::parse(payload); // check individual fields to match both objects CHECK(parsed_fields["one"] == fields["one"]); CHECK(parsed_fields["two"] == fields["two"]); CHECK(parsed_fields["three"] == fields["three"]); // check individual fields to match original input CHECK(parsed_fields["one"] == std::string("one")); CHECK(parsed_fields["two"] == std::string("two three")); CHECK(parsed_fields["three"] == std::string("three \"four\"")); // check equality of the objects CHECK(parsed_fields == fields); // check equality of the serialized objects CHECK(fields.dump() == parsed_fields.dump()); // check everything in one line CHECK(fields == json::parse(fields.dump())); } SECTION("issue #82 - lexer::get_number return NAN") { const auto content = R"( { "Test":"Test1", "Number":100, "Foo":42.42 })"; std::stringstream ss; ss << content; json j; ss >> j; std::string test = j["Test"]; CHECK(test == "Test1"); int number = j["Number"]; CHECK(number == 100); float foo = j["Foo"]; CHECK(foo == Approx(42.42)); } SECTION("issue #89 - nonstandard integer type") { // create JSON class with nonstandard integer number type using custom_json = nlohmann::basic_json; custom_json j; j["int_1"] = 1; // we need to cast to int to compile with Catch - the value is int32_t CHECK(static_cast(j["int_1"]) == 1); // tests for correct handling of non-standard integers that overflow the type selected by the user // unsigned integer object creation - expected to wrap and still be stored as an integer j = 4294967296U; // 2^32 CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_unsigned)); CHECK(j.get() == 0); // Wrap // unsigned integer parsing - expected to overflow and be stored as a float j = custom_json::parse("4294967296"); // 2^32 CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_float)); CHECK(j.get() == 4294967296.0f); // integer object creation - expected to wrap and still be stored as an integer j = -2147483649LL; // -2^31-1 CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_integer)); CHECK(j.get() == 2147483647); // Wrap // integer parsing - expected to overflow and be stored as a float with rounding j = custom_json::parse("-2147483649"); // -2^31 CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_float)); CHECK(j.get() == -2147483650.0f); } SECTION("issue #93 reverse_iterator operator inheritance problem") { { json a = {1, 2, 3}; json::reverse_iterator rit = a.rbegin(); ++rit; CHECK(*rit == json(2)); CHECK(rit.value() == json(2)); } { json a = {1, 2, 3}; json::reverse_iterator rit = ++a.rbegin(); } { json a = {1, 2, 3}; json::reverse_iterator rit = a.rbegin(); ++rit; json b = {0, 0, 0}; std::transform(rit, a.rend(), b.rbegin(), [](json el) { return el; }); CHECK(b == json({0, 1, 2})); } { json a = {1, 2, 3}; json b = {0, 0, 0}; std::transform(++a.rbegin(), a.rend(), b.rbegin(), [](json el) { return el; }); CHECK(b == json({0, 1, 2})); } } SECTION("issue #100 - failed to iterator json object with reverse_iterator") { json config = { { "111", 111 }, { "112", 112 }, { "113", 113 } }; std::stringstream ss; for (auto it = config.begin(); it != config.end(); ++it) { ss << it.key() << ": " << it.value() << '\n'; } for (auto it = config.rbegin(); it != config.rend(); ++it) { ss << it.key() << ": " << it.value() << '\n'; } CHECK(ss.str() == "111: 111\n112: 112\n113: 113\n113: 113\n112: 112\n111: 111\n"); } SECTION("issue #101 - binary string causes numbers to be dumped as hex") { int64_t number = 10; std::string bytes{"\x00" "asdf\n", 6}; json j; j["int64"] = number; j["binary string"] = bytes; // make sure the number is really printed as decimal "10" and not as // hexadecimal "a" CHECK(j.dump() == "{\"binary string\":\"\\u0000asdf\\n\",\"int64\":10}"); } SECTION("issue #111 - subsequent unicode chars") { std::string bytes{0x7, 0x7}; json j; j["string"] = bytes; CHECK(j["string"] == "\u0007\u0007"); } SECTION("issue #144 - implicit assignment to std::string fails") { json o = {{"name", "value"}}; std::string s1 = o["name"]; CHECK(s1 == "value"); std::string s2; s2 = o["name"]; CHECK(s2 == "value"); } SECTION("issue #146 - character following a surrogate pair is skipped") { CHECK(json::parse("\"\\ud80c\\udc60abc\"").get() == u8"\U00013060abc"); } SECTION("issue #171 - Cannot index by key of type static constexpr const char*") { json j; // Non-const access with key as "char []" char array_key[] = "Key1"; CHECK_NOTHROW(j[array_key] = 1); CHECK(j[array_key] == json(1)); // Non-const access with key as "const char[]" const char const_array_key[] = "Key2"; CHECK_NOTHROW(j[const_array_key] = 2); CHECK(j[const_array_key] == json(2)); // Non-const access with key as "char *" char _ptr_key[] = "Key3"; char* ptr_key = &_ptr_key[0]; CHECK_NOTHROW(j[ptr_key] = 3); CHECK(j[ptr_key] == json(3)); // Non-const access with key as "const char *" const char* const_ptr_key = "Key4"; CHECK_NOTHROW(j[const_ptr_key] = 4); CHECK(j[const_ptr_key] == json(4)); // Non-const access with key as "static constexpr const char *" static constexpr const char* constexpr_ptr_key = "Key5"; CHECK_NOTHROW(j[constexpr_ptr_key] = 5); CHECK(j[constexpr_ptr_key] == json(5)); const json j_const = j; // Const access with key as "char []" CHECK(j_const[array_key] == json(1)); // Const access with key as "const char[]" CHECK(j_const[const_array_key] == json(2)); // Const access with key as "char *" CHECK(j_const[ptr_key] == json(3)); // Const access with key as "const char *" CHECK(j_const[const_ptr_key] == json(4)); // Const access with key as "static constexpr const char *" CHECK(j_const[constexpr_ptr_key] == json(5)); } SECTION("issue #186 miloyip/nativejson-benchmark: floating-point parsing") { json j; j = json::parse("-0.0"); CHECK(j.get() == -0.0); j = json::parse("2.22507385850720113605740979670913197593481954635164564e-308"); CHECK(j.get() == 2.2250738585072009e-308); j = json::parse("0.999999999999999944488848768742172978818416595458984374"); CHECK(j.get() == 0.99999999999999989); j = json::parse("1.00000000000000011102230246251565404236316680908203126"); CHECK(j.get() == 1.00000000000000022); j = json::parse("7205759403792793199999e-5"); CHECK(j.get() == 72057594037927928.0); j = json::parse("922337203685477529599999e-5"); CHECK(j.get() == 9223372036854774784.0); j = json::parse("1014120480182583464902367222169599999e-5"); CHECK(j.get() == 10141204801825834086073718800384.0); j = json::parse("5708990770823839207320493820740630171355185151999e-3"); CHECK(j.get() == 5708990770823838890407843763683279797179383808.0); // create JSON class with nonstandard float number type // float nlohmann::basic_json j_float = 1.23e25f; CHECK(j_float.get() == 1.23e25f); // double nlohmann::basic_json j_double = 1.23e35f; CHECK(j_double.get() == 1.23e35f); // long double nlohmann::basic_json j_long_double = 1.23e45L; CHECK(j_long_double.get() == 1.23e45L); } SECTION("issue #228 - double values are serialized with commas as decimal points") { json j1a = 23.42; json j1b = json::parse("23.42"); json j2a = 2342e-2; //issue #230 //json j2b = json::parse("2342e-2"); json j3a = 10E3; json j3b = json::parse("10E3"); json j3c = json::parse("10e3"); // class to create a locale that would use a comma for decimals class CommaDecimalSeparator : public std::numpunct { protected: char do_decimal_point() const { return ','; } }; // change locale to mess with decimal points std::locale::global(std::locale(std::locale(), new CommaDecimalSeparator)); CHECK(j1a.dump() == "23.42"); CHECK(j1b.dump() == "23.42"); // check if locale is properly reset std::stringstream ss; ss.imbue(std::locale(std::locale(), new CommaDecimalSeparator)); ss << 47.11; CHECK(ss.str() == "47,11"); ss << j1a; CHECK(ss.str() == "47,1123.42"); ss << 47.11; CHECK(ss.str() == "47,1123.4247,11"); CHECK(j2a.dump() == "23.42"); //issue #230 //CHECK(j2b.dump() == "23.42"); CHECK(j3a.dump() == "10000"); CHECK(j3b.dump() == "10000"); CHECK(j3c.dump() == "10000"); //CHECK(j3b.dump() == "1E04"); // roundtrip error //CHECK(j3c.dump() == "1e04"); // roundtrip error } SECTION("issue #233 - Can't use basic_json::iterator as a base iterator for std::move_iterator") { json source = {"a", "b", "c"}; json expected = {"a", "b"}; json dest; std::copy_n(std::make_move_iterator(source.begin()), 2, std::back_inserter(dest)); CHECK(dest == expected); } SECTION("issue #235 - ambiguous overload for 'push_back' and 'operator+='") { json data = {{"key", "value"}}; data.push_back({"key2", "value2"}); data += {"key3", "value3"}; CHECK(data == json({{"key", "value"}, {"key2", "value2"}, {"key3", "value3"}})); } SECTION("issue #269 - diff generates incorrect patch when removing multiple array elements") { json doc = R"( { "arr1": [1, 2, 3, 4] } )"_json; json expected = R"( { "arr1": [1, 2] } )"_json; // check roundtrip CHECK(doc.patch(json::diff(doc, expected)) == expected); } } // special test case to check if memory is leaked if constructor throws template struct my_allocator : std::allocator { template void construct(T*, Args&& ...) { throw std::bad_alloc(); } }; TEST_CASE("bad_alloc") { SECTION("bad_alloc") { // create JSON type using the throwing allocator using my_json = nlohmann::basic_json; // creating an object should throw CHECK_THROWS_AS(my_json j(my_json::value_t::object), std::bad_alloc); } }