/*! @file @copyright The code is licensed under the MIT License , Copyright (c) 2013-2015 Niels Lohmann. @author Niels Lohmann @see https://github.com/nlohmann/json */ #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; 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_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(2.2)}, {"c", json(false)}, {"d", json("string")}, {"e", 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(2.2)}, {"c", json(false)}, {"d", json("string")}, {"e", json()}}; json j_reference(o_reference); SECTION("std::map") { std::map o {{"a", json(1)}, {"b", json(2.2)}, {"c", json(false)}, {"d", json("string")}, {"e", 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(2.2)}, {"c", json(false)}, {"d", json("string")}, {"e", 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(2.2)}, {"c", json(false)}, {"d", json("string")}, {"e", 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(2.2)}, {"c", json(false)}, {"d", json("string")}, {"e", 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(2.2)}, {"c", json(false)}, {"d", json("string")}, {"e", 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(2.2)}, {"c", json(false)}, {"d", json("string")}, {"e", 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(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(2.2), json(false), json("string"), json()}; json j_reference(a_reference); SECTION("std::list") { std::list a {json(1), 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(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(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(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(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(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(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(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 object json::number_integer_t n_reference = 42; json j_reference(n_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_integer); CHECK(j == j_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_integer); CHECK(j == j_reference); } SECTION("long") { long n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("unsigned long") { short n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_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_integer); CHECK(j == j_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_integer); CHECK(j == j_reference); } SECTION("uint16_t") { uint16_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("uint32_t") { uint32_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("uint64_t") { uint64_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("uint_fast8_t") { uint_fast8_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("uint_fast16_t") { uint_fast16_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("uint_fast32_t") { uint_fast32_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("uint_fast64_t") { uint_fast64_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("uint_least8_t") { uint_least8_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("uint_least16_t") { uint_least16_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("uint_least32_t") { uint_least32_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_reference); } SECTION("uint_least64_t") { uint_least64_t n = 42; json j(n); CHECK(j.type() == json::value_t::number_integer); CHECK(j == j_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_integer); CHECK(j == j_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_integer); CHECK(j == j_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_integer); CHECK(j == j_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 (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, 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, 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", 2.2}, {"three", false} }; CHECK(j.type() == json::value_t::object); } SECTION("array") { json j { {"one", 1}, {"two", 2.2}, {"three", 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", 2.2}, {"three", false} }); CHECK(j.type() == json::value_t::object); } SECTION("object with error") { CHECK_THROWS_AS(json::object({ {"one", 1}, {"two", 2.2}, {"three", false}, 13 }), std::logic_error); CHECK_THROWS_WITH(json::object({ {"one", 1}, {"two", 2.2}, {"three", 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", 2.2}, {"three", 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", 2}}}; 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", 17}}; json j_new(jobject.begin(), jobject.end()); CHECK(j_new == jobject); } { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; json j_new(jobject.cbegin(), jobject.cend()); CHECK(j_new == jobject); } } SECTION("json(begin(), begin())") { { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; json j_new(jobject.begin(), jobject.begin()); CHECK(j_new == json::object()); } { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; json j_new(jobject.cbegin(), jobject.cbegin()); CHECK(j_new == json::object()); } } SECTION("construct from subrange") { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}, {"d", false}, {"e", true}}; json j_new(jobject.find("b"), jobject.find("e")); CHECK(j_new == json({{"b", 1}, {"c", 17}, {"d", false}})); } SECTION("incompatible iterators") { { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}, {"d", false}, {"e", true}}; json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17}}; 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", 17}, {"d", false}, {"e", true}}; json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17}}; 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 (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 (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"); } } } } } } 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 (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", 42.23}, {"b", 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 (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, 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_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, 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_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_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_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_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_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(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_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 (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 (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_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_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(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_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_float).get(), "type must be array, but is number"); } } SECTION("get an array (implicit)") { json::array_t a_reference {json(1), 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_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_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_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_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); SECTION("number_integer_t") { json::number_integer_t n = j.get(); CHECK(json(n) == j); } 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()); } } SECTION("get an integer number (implicit)") { json::number_integer_t n_reference {42}; json j(n_reference); SECTION("number_integer_t") { json::number_integer_t n = j.get(); CHECK(json(n) == j); } SECTION("short") { short n = j; CHECK(json(n) == j); } SECTION("unsigned short") { unsigned short n = j; CHECK(json(n) == j); } SECTION("int") { int n = j; CHECK(json(n) == j); } SECTION("unsigned int") { unsigned int n = j; CHECK(json(n) == j); } SECTION("long") { long n = j; CHECK(json(n) == j); } SECTION("unsigned long") { unsigned long n = j; CHECK(json(n) == j); } SECTION("long long") { long long n = j; CHECK(json(n) == j); } SECTION("unsigned long long") { unsigned long long n = j; CHECK(json(n) == j); } 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; CHECK(json(n) == j); } SECTION("uint16_t") { uint16_t n = j; CHECK(json(n) == j); } SECTION("uint32_t") { uint32_t n = j; CHECK(json(n) == j); } SECTION("uint64_t") { uint64_t n = j; CHECK(json(n) == j); } SECTION("uint8_fast_t") { uint_fast8_t n = j; CHECK(json(n) == j); } SECTION("uint16_fast_t") { uint_fast16_t n = j; CHECK(json(n) == j); } SECTION("uint32_fast_t") { uint_fast32_t n = j; CHECK(json(n) == j); } SECTION("uint64_fast_t") { uint_fast64_t n = j; CHECK(json(n) == j); } SECTION("uint8_least_t") { uint_least8_t n = j; CHECK(json(n) == j); } SECTION("uint16_least_t") { uint_least16_t n = j; CHECK(json(n) == j); } SECTION("uint32_least_t") { uint_least32_t n = j; CHECK(json(n) == j); } SECTION("uint64_least_t") { uint_least64_t n = j; CHECK(json(n) == j); } } 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()); } } 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", 1.1}, {"two", 2.2}, {"three", 3.3}}; json j3 = {{"one", true}, {"two", false}, {"three", true}}; json j4 = {{"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>(); } SECTION("std::unordered_map") { auto m1 = j1.get>(); auto m2 = j2.get>(); auto m3 = j3.get>(); //auto m4 = j4.get>(); //CHECK(m4["one"] == "eins"); } SECTION("std::multimap") { auto m1 = j1.get>(); auto m2 = j2.get>(); auto m3 = j3.get>(); //auto m4 = j4.get>(); //CHECK(m4["one"] == "eins"); } SECTION("std::unordered_multimap") { auto m1 = j1.get>(); auto m2 = j2.get>(); auto m3 = j3.get>(); //auto m4 = j4.get>(); //CHECK(m4["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 = {1.2, 2.3, 3.4, 4.5}; json j3 = {true, false, true}; json j4 = {"one", "two", "three"}; SECTION("std::list") { auto m1 = j1.get>(); auto m2 = j2.get>(); auto m3 = j3.get>(); auto m4 = j4.get>(); } //SECTION("std::forward_list") //{ // auto m1 = j1.get>(); // auto m2 = j2.get>(); // auto m3 = j3.get>(); // auto m4 = j4.get>(); //} SECTION("std::vector") { auto m1 = j1.get>(); auto m2 = j2.get>(); auto m3 = j3.get>(); auto m4 = j4.get>(); } SECTION("std::deque") { auto m1 = j1.get>(); auto m2 = j2.get>(); auto m3 = j3.get>(); auto m4 = j4.get>(); } SECTION("std::set") { auto m1 = j1.get>(); auto m2 = j2.get>(); auto m3 = j3.get>(); auto m4 = j4.get>(); } SECTION("std::unordered_set") { auto m1 = j1.get>(); auto m2 = j2.get>(); auto m3 = j3.get>(); auto m4 = j4.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}, {"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); } 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); } 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); } 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); } 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); } 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); } } 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, 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(true)); CHECK(j.at(2) == json(nullptr)); CHECK(j.at(3) == json("string")); CHECK(j.at(4) == json(42.23)); CHECK(j.at(5) == json(json::object())); CHECK(j.at(6) == json({1, 2, 3})); CHECK(j_const.at(0) == json(1)); CHECK(j_const.at(1) == json(true)); CHECK(j_const.at(2) == json(nullptr)); CHECK(j_const.at(3) == json("string")); CHECK(j_const.at(4) == json(42.23)); CHECK(j_const.at(5) == json(json::object())); CHECK(j_const.at(6) == json({1, 2, 3})); } SECTION("access outside bounds") { CHECK_THROWS_AS(j.at(7), std::out_of_range); CHECK_THROWS_AS(j_const.at(7), std::out_of_range); CHECK_THROWS_WITH(j.at(7), "array index 7 is out of range"); CHECK_THROWS_WITH(j_const.at(7), "array index 7 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 (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(true)); CHECK(j[2] == json(nullptr)); CHECK(j[3] == json("string")); CHECK(j[4] == json(42.23)); CHECK(j[5] == json(json::object())); CHECK(j[6] == json({1, 2, 3})); CHECK(j_const[0] == json(1)); CHECK(j_const[1] == json(true)); CHECK(j_const[2] == json(nullptr)); CHECK(j_const[3] == json("string")); CHECK(j_const[4] == json(42.23)); CHECK(j_const[5] == json(json::object())); CHECK(j_const[6] == 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 (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, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; jarray.erase(0); CHECK(jarray == json({true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); } { json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; jarray.erase(1); CHECK(jarray == json({1, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); } { json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; jarray.erase(2); CHECK(jarray == json({1, true, "string", 42.23, json::object(), {1, 2, 3}})); } { json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; jarray.erase(3); CHECK(jarray == json({1, true, nullptr, 42.23, json::object(), {1, 2, 3}})); } { json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; jarray.erase(4); CHECK(jarray == json({1, true, nullptr, "string", json::object(), {1, 2, 3}})); } { json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; jarray.erase(5); CHECK(jarray == json({1, true, nullptr, "string", 42.23, {1, 2, 3}})); } { json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; jarray.erase(6); CHECK(jarray == json({1, true, nullptr, "string", 42.23, json::object()})); } { json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; CHECK_THROWS_AS(jarray.erase(7), std::out_of_range); CHECK_THROWS_WITH(jarray.erase(7), "index out of range"); } } SECTION("remove element by iterator") { SECTION("erase(begin())") { { json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::iterator it2 = jarray.erase(jarray.begin()); CHECK(jarray == json({true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); CHECK(*it2 == json(true)); } { json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::const_iterator it2 = jarray.erase(jarray.cbegin()); CHECK(jarray == json({true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); CHECK(*it2 == json(true)); } } SECTION("erase(begin(), end())") { { json jarray = {1, 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, 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, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::iterator it2 = jarray.erase(jarray.begin(), jarray.begin()); CHECK(jarray == json({1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); CHECK(*it2 == json(1)); } { json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cbegin()); CHECK(jarray == json({1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}})); CHECK(*it2 == json(1)); } } SECTION("erase at offset") { { json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::iterator it = jarray.begin() + 3; json::iterator it2 = jarray.erase(it); CHECK(jarray == json({1, true, nullptr, 42.23, json::object(), {1, 2, 3}})); CHECK(*it2 == json(42.23)); } { json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::const_iterator it = jarray.cbegin() + 3; json::const_iterator it2 = jarray.erase(it); CHECK(jarray == json({1, true, nullptr, 42.23, json::object(), {1, 2, 3}})); CHECK(*it2 == json(42.23)); } } SECTION("erase subrange") { { json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::iterator it2 = jarray.erase(jarray.begin() + 2, jarray.begin() + 5); CHECK(jarray == json({1, true, json::object(), {1, 2, 3}})); CHECK(*it2 == json::object()); } { json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}; json::const_iterator it2 = jarray.erase(jarray.cbegin() + 2, jarray.cbegin() + 5); CHECK(jarray == json({1, true, json::object(), {1, 2, 3}})); CHECK(*it2 == json::object()); } } SECTION("different arrays") { { json jarray = {1, 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, 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 (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}, {"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("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("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 (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("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("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("_", 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("_", 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 (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})); // "string" is the largest key CHECK(j.back() == json("hello world")); CHECK(j_const.back() == json("hello world")); } 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["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 (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("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", 17}}; json::iterator it2 = jobject.erase(jobject.begin()); CHECK(jobject == json({{"b", 1}, {"c", 17}})); CHECK(*it2 == json(1)); } { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; json::const_iterator it2 = jobject.erase(jobject.cbegin()); CHECK(jobject == json({{"b", 1}, {"c", 17}})); CHECK(*it2 == json(1)); } } SECTION("erase(begin(), end())") { { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; json::iterator it2 = jobject.erase(jobject.begin(), jobject.end()); CHECK(jobject == json::object()); CHECK(it2 == jobject.end()); } { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; 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", 17}}; json::iterator it2 = jobject.erase(jobject.begin(), jobject.begin()); CHECK(jobject == json({{"a", "a"}, {"b", 1}, {"c", 17}})); CHECK(*it2 == json("a")); } { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cbegin()); CHECK(jobject == json({{"a", "a"}, {"b", 1}, {"c", 17}})); CHECK(*it2 == json("a")); } } SECTION("erase at offset") { { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; json::iterator it = jobject.find("b"); json::iterator it2 = jobject.erase(it); CHECK(jobject == json({{"a", "a"}, {"c", 17}})); CHECK(*it2 == json(17)); } { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; json::const_iterator it = jobject.find("b"); json::const_iterator it2 = jobject.erase(it); CHECK(jobject == json({{"a", "a"}, {"c", 17}})); CHECK(*it2 == json(17)); } } SECTION("erase subrange") { { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}, {"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", 17}, {"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", 17}, {"d", false}, {"e", true}}; json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17}}; 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", 17}, {"d", false}, {"e", true}}; json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17}}; 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", "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 (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", "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 (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 (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 (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 (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 (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 (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 (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, 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, 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 (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 (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 (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 (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("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("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_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}, {false, false, true, true, true, true, true}, {false, false, false, false, true, true, true}, {false, false, false, false, true, true, true}, {false, false, false, false, false, true, true}, {false, false, false, false, false, false, true}, {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, 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}, {true, true, 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, true, 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, true, 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, true, 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, true, 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, true, 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, true} }; for (size_t i = 0; i < j_values.size(); ++i) { for (size_t j = 0; j < j_values.size(); ++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) { // 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}, {false, false, true, true, true, true, true, true, true, true, true, true, true, true}, {false, false, false, true, false, true, true, true, false, false, true, true, true, true}, {false, false, false, false, false, false, true, true, false, false, true, true, true, true}, {false, false, true, true, false, true, true, true, false, false, true, true, true, true}, {false, false, false, true, 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, true, false, false, false, false, false, false, false}, {false, false, true, true, true, true, true, true, false, false, true, true, true, true}, {false, false, true, true, true, true, true, true, true, false, true, true, true, true}, {false, false, false, false, false, false, true, true, false, false, false, true, false, false}, {false, false, false, false, false, false, true, true, 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, 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) { // 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); } } 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) { // 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) { // 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) { // 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 '\"'"); } 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("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); CHECK_THROWS_WITH(json::parser("01").parse(), "parse error - 0 is not a number"); CHECK_THROWS_WITH(json::parser("--1").parse(), "parse error - unexpected '-'"); CHECK_THROWS_WITH(json::parser("1.").parse(), "parse error - 1 is not a number"); 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 - 1 is not a number"); 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 - 0 is not a number"); 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 - -0 is not a number"); 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 - 0 is not a number"); 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 (thanks http://isocpp.org/blog/2015/01/json-for-modern-cpp) 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. } } 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/json_tests/fail1.json", "test/json_tests/fail2.json", "test/json_tests/fail3.json", "test/json_tests/fail4.json", "test/json_tests/fail5.json", "test/json_tests/fail6.json", "test/json_tests/fail7.json", "test/json_tests/fail8.json", "test/json_tests/fail9.json", "test/json_tests/fail10.json", "test/json_tests/fail11.json", "test/json_tests/fail12.json", "test/json_tests/fail13.json", "test/json_tests/fail14.json", "test/json_tests/fail15.json", "test/json_tests/fail16.json", "test/json_tests/fail17.json", //"test/json_tests/fail18.json", "test/json_tests/fail19.json", "test/json_tests/fail20.json", "test/json_tests/fail21.json", "test/json_tests/fail22.json", "test/json_tests/fail23.json", "test/json_tests/fail24.json", "test/json_tests/fail25.json", "test/json_tests/fail26.json", "test/json_tests/fail27.json", "test/json_tests/fail28.json", "test/json_tests/fail29.json", "test/json_tests/fail30.json", "test/json_tests/fail31.json", "test/json_tests/fail32.json", "test/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/json_tests/pass1.json", "test/json_tests/pass2.json", "test/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/data/roundtrip for (auto filename : { "test/json_roundtrip/roundtrip01.json", "test/json_roundtrip/roundtrip02.json", "test/json_roundtrip/roundtrip03.json", "test/json_roundtrip/roundtrip04.json", "test/json_roundtrip/roundtrip05.json", "test/json_roundtrip/roundtrip06.json", "test/json_roundtrip/roundtrip07.json", "test/json_roundtrip/roundtrip08.json", "test/json_roundtrip/roundtrip09.json", "test/json_roundtrip/roundtrip10.json", "test/json_roundtrip/roundtrip11.json", "test/json_roundtrip/roundtrip12.json", //"test/json_roundtrip/roundtrip13.json", "test/json_roundtrip/roundtrip14.json", "test/json_roundtrip/roundtrip15.json", "test/json_roundtrip/roundtrip16.json", "test/json_roundtrip/roundtrip17.json", //"test/json_roundtrip/roundtrip18.json", //"test/json_roundtrip/roundtrip19.json", //"test/json_roundtrip/roundtrip20.json", "test/json_roundtrip/roundtrip21.json", "test/json_roundtrip/roundtrip22.json", "test/json_roundtrip/roundtrip23.json", //"test/json_roundtrip/roundtrip24.json", //"test/json_roundtrip/roundtrip25.json", //"test/json_roundtrip/roundtrip26.json", //"test/json_roundtrip/roundtrip27.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/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/json.org/1.json"); json j; CHECK_NOTHROW(j << f); } SECTION("2.json") { std::ifstream f("test/json.org/2.json"); json j; CHECK_NOTHROW(j << f); } SECTION("3.json") { std::ifstream f("test/json.org/3.json"); json j; CHECK_NOTHROW(j << f); } SECTION("4.json") { std::ifstream f("test/json.org/4.json"); json j; CHECK_NOTHROW(j << f); } SECTION("5.json") { std::ifstream f("test/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/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("ignore byte-order-mark") { // read a file with a UTF-8 BOM std::ifstream f("test/json_nlohmann_tests/bom.json"); json j; CHECK_NOTHROW(j << f); } } 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); } 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("character following a surrogate pair is skipped") { CHECK(json::parse("\"\\ud80c\\udc60abc\"").get() == u8"\U00013060abc"); } 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.23e45; CHECK(j_double.get() == 1.23e45); // long double nlohmann::basic_json j_long_double = 1.23e45L; CHECK(j_long_double.get() == 1.23e45L); } }