blob: 27aff7ff33dad3e83c5256bacab4bc4d7f2be531 [file] [log] [blame]
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
| | |__ | | | | | | version 2.0.2
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
Copyright (c) 2013-2016 Niels Lohmann <http://nlohmann.me>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include <array>
#include <deque>
#include <forward_list>
#include <list>
#include <map>
#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#define private public
#include "json.hpp"
using nlohmann::json;
// disable float-equal warnings on GCC/clang
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
#pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
TEST_CASE("constructors")
{
SECTION("create an empty value with a given type")
{
SECTION("null")
{
auto t = json::value_t::null;
json j(t);
CHECK(j.type() == t);
}
SECTION("discarded")
{
auto t = json::value_t::discarded;
json j(t);
CHECK(j.type() == t);
}
SECTION("object")
{
auto t = json::value_t::object;
json j(t);
CHECK(j.type() == t);
}
SECTION("array")
{
auto t = json::value_t::array;
json j(t);
CHECK(j.type() == t);
}
SECTION("boolean")
{
auto t = json::value_t::boolean;
json j(t);
CHECK(j.type() == t);
}
SECTION("string")
{
auto t = json::value_t::string;
json j(t);
CHECK(j.type() == t);
}
SECTION("number_integer")
{
auto t = json::value_t::number_integer;
json j(t);
CHECK(j.type() == t);
}
SECTION("number_unsigned")
{
auto t = json::value_t::number_unsigned;
json j(t);
CHECK(j.type() == t);
}
SECTION("number_float")
{
auto t = json::value_t::number_float;
json j(t);
CHECK(j.type() == t);
}
}
SECTION("create a null object (implicitly)")
{
SECTION("no parameter")
{
json j{};
CHECK(j.type() == json::value_t::null);
}
}
SECTION("create a null object (explicitly)")
{
SECTION("parameter")
{
json j(nullptr);
CHECK(j.type() == json::value_t::null);
}
}
SECTION("create an object (explicit)")
{
SECTION("empty object")
{
json::object_t o;
json j(o);
CHECK(j.type() == json::value_t::object);
}
SECTION("filled object")
{
json::object_t o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
json j(o);
CHECK(j.type() == json::value_t::object);
}
}
SECTION("create an object (implicit)")
{
// reference object
json::object_t o_reference {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
json j_reference(o_reference);
SECTION("std::map<json::string_t, json>")
{
std::map<json::string_t, json> o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
json j(o);
CHECK(j.type() == json::value_t::object);
CHECK(j == j_reference);
}
SECTION("std::map<const char*, json>")
{
std::map<const char*, json> o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
json j(o);
CHECK(j.type() == json::value_t::object);
CHECK(j == j_reference);
}
SECTION("std::multimap<json::string_t, json>")
{
std::multimap<json::string_t, json> o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
json j(o);
CHECK(j.type() == json::value_t::object);
CHECK(j == j_reference);
}
SECTION("std::unordered_map<json::string_t, json>")
{
std::unordered_map<json::string_t, json> o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
json j(o);
CHECK(j.type() == json::value_t::object);
CHECK(j == j_reference);
}
SECTION("std::unordered_multimap<json::string_t, json>")
{
std::unordered_multimap<json::string_t, json> o {{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}};
json j(o);
CHECK(j.type() == json::value_t::object);
CHECK(j == j_reference);
}
SECTION("associative container literal")
{
json j({{"a", json(1)}, {"b", json(1u)}, {"c", json(2.2)}, {"d", json(false)}, {"e", json("string")}, {"f", json()}});
CHECK(j.type() == json::value_t::object);
CHECK(j == j_reference);
}
}
SECTION("create an array (explicit)")
{
SECTION("empty array")
{
json::array_t a;
json j(a);
CHECK(j.type() == json::value_t::array);
}
SECTION("filled array")
{
json::array_t a {json(1), json(1u), json(2.2), json(false), json("string"), json()};
json j(a);
CHECK(j.type() == json::value_t::array);
}
}
SECTION("create an array (implicit)")
{
// reference array
json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()};
json j_reference(a_reference);
SECTION("std::list<json>")
{
std::list<json> a {json(1), json(1u), json(2.2), json(false), json("string"), json()};
json j(a);
CHECK(j.type() == json::value_t::array);
CHECK(j == j_reference);
}
SECTION("std::forward_list<json>")
{
std::forward_list<json> a {json(1), json(1u), json(2.2), json(false), json("string"), json()};
json j(a);
CHECK(j.type() == json::value_t::array);
CHECK(j == j_reference);
}
SECTION("std::array<json, 5>")
{
std::array<json, 6> a {{json(1), json(1u), json(2.2), json(false), json("string"), json()}};
json j(a);
CHECK(j.type() == json::value_t::array);
CHECK(j == j_reference);
}
SECTION("std::vector<json>")
{
std::vector<json> a {json(1), json(1u), json(2.2), json(false), json("string"), json()};
json j(a);
CHECK(j.type() == json::value_t::array);
CHECK(j == j_reference);
}
SECTION("std::deque<json>")
{
std::deque<json> a {json(1), json(1u), json(2.2), json(false), json("string"), json()};
json j(a);
CHECK(j.type() == json::value_t::array);
CHECK(j == j_reference);
}
SECTION("std::set<json>")
{
std::set<json> a {json(1), json(1u), json(2.2), json(false), json("string"), json()};
json j(a);
CHECK(j.type() == json::value_t::array);
// we cannot really check for equality here
}
SECTION("std::unordered_set<json>")
{
std::unordered_set<json> a {json(1), json(1u), json(2.2), json(false), json("string"), json()};
json j(a);
CHECK(j.type() == json::value_t::array);
// we cannot really check for equality here
}
SECTION("sequence container literal")
{
json j({json(1), json(1u), json(2.2), json(false), json("string"), json()});
CHECK(j.type() == json::value_t::array);
CHECK(j == j_reference);
}
}
SECTION("create a string (explicit)")
{
SECTION("empty string")
{
json::string_t s;
json j(s);
CHECK(j.type() == json::value_t::string);
}
SECTION("filled string")
{
json::string_t s {"Hello world"};
json j(s);
CHECK(j.type() == json::value_t::string);
}
}
SECTION("create a string (implicit)")
{
// reference string
json::string_t s_reference {"Hello world"};
json j_reference(s_reference);
SECTION("std::string")
{
std::string s {"Hello world"};
json j(s);
CHECK(j.type() == json::value_t::string);
CHECK(j == j_reference);
}
SECTION("char[]")
{
char s[] {"Hello world"};
json j(s);
CHECK(j.type() == json::value_t::string);
CHECK(j == j_reference);
}
SECTION("const char*")
{
const char* s {"Hello world"};
json j(s);
CHECK(j.type() == json::value_t::string);
CHECK(j == j_reference);
}
SECTION("string literal")
{
json j("Hello world");
CHECK(j.type() == json::value_t::string);
CHECK(j == j_reference);
}
}
SECTION("create a boolean (explicit)")
{
SECTION("empty boolean")
{
json::boolean_t b{};
json j(b);
CHECK(j.type() == json::value_t::boolean);
}
SECTION("filled boolean (true)")
{
json j(true);
CHECK(j.type() == json::value_t::boolean);
}
SECTION("filled boolean (false)")
{
json j(false);
CHECK(j.type() == json::value_t::boolean);
}
}
SECTION("create an integer number (explicit)")
{
SECTION("uninitialized value")
{
json::number_integer_t n{};
json j(n);
CHECK(j.type() == json::value_t::number_integer);
}
SECTION("initialized value")
{
json::number_integer_t n(42);
json j(n);
CHECK(j.type() == json::value_t::number_integer);
}
}
SECTION("create an integer number (implicit)")
{
// reference objects
json::number_integer_t n_reference = 42;
json j_reference(n_reference);
json::number_unsigned_t n_unsigned_reference = 42;
json j_unsigned_reference(n_unsigned_reference);
SECTION("short")
{
short n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_integer);
CHECK(j == j_reference);
}
SECTION("unsigned short")
{
unsigned short n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_unsigned);
CHECK(j == j_unsigned_reference);
}
SECTION("int")
{
int n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_integer);
CHECK(j == j_reference);
}
SECTION("unsigned int")
{
unsigned int n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_unsigned);
CHECK(j == j_unsigned_reference);
}
SECTION("long")
{
long n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_integer);
CHECK(j == j_reference);
}
SECTION("unsigned long")
{
unsigned long n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_unsigned);
CHECK(j == j_unsigned_reference);
}
SECTION("long long")
{
long long n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_integer);
CHECK(j == j_reference);
}
SECTION("unsigned long long")
{
unsigned long long n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_unsigned);
CHECK(j == j_unsigned_reference);
}
SECTION("int8_t")
{
int8_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_integer);
CHECK(j == j_reference);
}
SECTION("int16_t")
{
int16_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_integer);
CHECK(j == j_reference);
}
SECTION("int32_t")
{
int32_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_integer);
CHECK(j == j_reference);
}
SECTION("int64_t")
{
int64_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_integer);
CHECK(j == j_reference);
}
SECTION("int_fast8_t")
{
int_fast8_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_integer);
CHECK(j == j_reference);
}
SECTION("int_fast16_t")
{
int_fast16_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_integer);
CHECK(j == j_reference);
}
SECTION("int_fast32_t")
{
int_fast32_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_integer);
CHECK(j == j_reference);
}
SECTION("int_fast64_t")
{
int_fast64_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_integer);
CHECK(j == j_reference);
}
SECTION("int_least8_t")
{
int_least8_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_integer);
CHECK(j == j_reference);
}
SECTION("int_least16_t")
{
int_least16_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_integer);
CHECK(j == j_reference);
}
SECTION("int_least32_t")
{
int_least32_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_integer);
CHECK(j == j_reference);
}
SECTION("int_least64_t")
{
int_least64_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_integer);
CHECK(j == j_reference);
}
SECTION("uint8_t")
{
uint8_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_unsigned);
CHECK(j == j_unsigned_reference);
}
SECTION("uint16_t")
{
uint16_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_unsigned);
CHECK(j == j_unsigned_reference);
}
SECTION("uint32_t")
{
uint32_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_unsigned);
CHECK(j == j_unsigned_reference);
}
SECTION("uint64_t")
{
uint64_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_unsigned);
CHECK(j == j_unsigned_reference);
}
SECTION("uint_fast8_t")
{
uint_fast8_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_unsigned);
CHECK(j == j_unsigned_reference);
}
SECTION("uint_fast16_t")
{
uint_fast16_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_unsigned);
CHECK(j == j_unsigned_reference);
}
SECTION("uint_fast32_t")
{
uint_fast32_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_unsigned);
CHECK(j == j_unsigned_reference);
}
SECTION("uint_fast64_t")
{
uint_fast64_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_unsigned);
CHECK(j == j_unsigned_reference);
}
SECTION("uint_least8_t")
{
uint_least8_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_unsigned);
CHECK(j == j_unsigned_reference);
}
SECTION("uint_least16_t")
{
uint_least16_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_unsigned);
CHECK(j == j_unsigned_reference);
}
SECTION("uint_least32_t")
{
uint_least32_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_unsigned);
CHECK(j == j_unsigned_reference);
}
SECTION("uint_least64_t")
{
uint_least64_t n = 42;
json j(n);
CHECK(j.type() == json::value_t::number_unsigned);
CHECK(j == j_unsigned_reference);
}
SECTION("integer literal without suffix")
{
json j(42);
CHECK(j.type() == json::value_t::number_integer);
CHECK(j == j_reference);
}
SECTION("integer literal with u suffix")
{
json j(42u);
CHECK(j.type() == json::value_t::number_unsigned);
CHECK(j == j_unsigned_reference);
}
SECTION("integer literal with l suffix")
{
json j(42l);
CHECK(j.type() == json::value_t::number_integer);
CHECK(j == j_reference);
}
SECTION("integer literal with ul suffix")
{
json j(42ul);
CHECK(j.type() == json::value_t::number_unsigned);
CHECK(j == j_unsigned_reference);
}
SECTION("integer literal with ll suffix")
{
json j(42ll);
CHECK(j.type() == json::value_t::number_integer);
CHECK(j == j_reference);
}
SECTION("integer literal with ull suffix")
{
json j(42ull);
CHECK(j.type() == json::value_t::number_unsigned);
CHECK(j == j_unsigned_reference);
}
}
SECTION("create a floating-point number (explicit)")
{
SECTION("uninitialized value")
{
json::number_float_t n{};
json j(n);
CHECK(j.type() == json::value_t::number_float);
}
SECTION("initialized value")
{
json::number_float_t n(42.23);
json j(n);
CHECK(j.type() == json::value_t::number_float);
}
}
SECTION("create a floating-point number (implicit)")
{
// reference object
json::number_float_t n_reference = 42.23;
json j_reference(n_reference);
SECTION("float")
{
float n = 42.23;
json j(n);
CHECK(j.type() == json::value_t::number_float);
CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float));
}
SECTION("double")
{
double n = 42.23;
json j(n);
CHECK(j.type() == json::value_t::number_float);
CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float));
}
SECTION("long double")
{
long double n = 42.23;
json j(n);
CHECK(j.type() == json::value_t::number_float);
CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float));
}
SECTION("floating-point literal without suffix")
{
json j(42.23);
CHECK(j.type() == json::value_t::number_float);
CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float));
}
SECTION("integer literal with f suffix")
{
json j(42.23f);
CHECK(j.type() == json::value_t::number_float);
CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float));
}
SECTION("integer literal with l suffix")
{
json j(42.23l);
CHECK(j.type() == json::value_t::number_float);
CHECK(j.m_value.number_float == Approx(j_reference.m_value.number_float));
}
}
SECTION("create a container (array or object) from an initializer list")
{
SECTION("empty initializer list")
{
SECTION("explicit")
{
std::initializer_list<json> 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<json> 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<json> 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<json> 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<json> 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<json> l = {json(1)};
json j(l);
CHECK(j.type() == json::value_t::array);
}
SECTION("implicit")
{
json j {1};
CHECK(j.type() == json::value_t::array);
}
}
SECTION("number (unsigned)")
{
SECTION("explicit")
{
std::initializer_list<json> l = {json(1u)};
json j(l);
CHECK(j.type() == json::value_t::array);
}
SECTION("implicit")
{
json j {1u};
CHECK(j.type() == json::value_t::array);
}
}
SECTION("number (floating-point)")
{
SECTION("explicit")
{
std::initializer_list<json> 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<json> l = {1, 1u, 42.23, true, nullptr, json::object_t(), json::array_t()};
json j(l);
CHECK(j.type() == json::value_t::array);
}
SECTION("implicit")
{
json j {1, 1u, 42.23, true, nullptr, json::object_t(), json::array_t()};
CHECK(j.type() == json::value_t::array);
}
}
SECTION("implicit type deduction")
{
SECTION("object")
{
json j { {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} };
CHECK(j.type() == json::value_t::object);
}
SECTION("array")
{
json j { {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} , 13 };
CHECK(j.type() == json::value_t::array);
}
}
SECTION("explicit type deduction")
{
SECTION("empty object")
{
json j = json::object();
CHECK(j.type() == json::value_t::object);
}
SECTION("object")
{
json j = json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} });
CHECK(j.type() == json::value_t::object);
}
SECTION("object with error")
{
CHECK_THROWS_AS(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }),
std::logic_error);
CHECK_THROWS_WITH(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }),
"cannot create object from initializer list");
}
SECTION("empty array")
{
json j = json::array();
CHECK(j.type() == json::value_t::array);
}
SECTION("array")
{
json j = json::array({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false} });
CHECK(j.type() == json::value_t::array);
}
}
}
SECTION("create an array of n copies of a given value")
{
json v = {1, "foo", 34.23, {1, 2, 3}, {{"A", 1}, {"B", 2u}}};
json arr(3, v);
CHECK(arr.size() == 3);
for (auto& x : arr)
{
CHECK(x == v);
}
}
SECTION("create a JSON container from an iterator range")
{
SECTION("object")
{
SECTION("json(begin(), end())")
{
{
json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
json j_new(jobject.begin(), jobject.end());
CHECK(j_new == jobject);
}
{
json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
json j_new(jobject.cbegin(), jobject.cend());
CHECK(j_new == jobject);
}
}
SECTION("json(begin(), begin())")
{
{
json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
json j_new(jobject.begin(), jobject.begin());
CHECK(j_new == json::object());
}
{
json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
json j_new(jobject.cbegin(), jobject.cbegin());
CHECK(j_new == json::object());
}
}
SECTION("construct from subrange")
{
json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
json j_new(jobject.find("b"), jobject.find("e"));
CHECK(j_new == json({{"b", 1}, {"c", 17u}, {"d", false}}));
}
SECTION("incompatible iterators")
{
{
json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}};
CHECK_THROWS_AS(json(jobject.begin(), jobject2.end()), std::domain_error);
CHECK_THROWS_AS(json(jobject2.begin(), jobject.end()), std::domain_error);
CHECK_THROWS_WITH(json(jobject.begin(), jobject2.end()), "iterators are not compatible");
CHECK_THROWS_WITH(json(jobject2.begin(), jobject.end()), "iterators are not compatible");
}
{
json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}};
CHECK_THROWS_AS(json(jobject.cbegin(), jobject2.cend()), std::domain_error);
CHECK_THROWS_AS(json(jobject2.cbegin(), jobject.cend()), std::domain_error);
CHECK_THROWS_WITH(json(jobject.cbegin(), jobject2.cend()), "iterators are not compatible");
CHECK_THROWS_WITH(json(jobject2.cbegin(), jobject.cend()), "iterators are not compatible");
}
}
}
SECTION("array")
{
SECTION("json(begin(), end())")
{
{
json jarray = {1, 2, 3, 4, 5};
json j_new(jarray.begin(), jarray.end());
CHECK(j_new == jarray);
}
{
json jarray = {1, 2, 3, 4, 5};
json j_new(jarray.cbegin(), jarray.cend());
CHECK(j_new == jarray);
}
}
SECTION("json(begin(), begin())")
{
{
json jarray = {1, 2, 3, 4, 5};
json j_new(jarray.begin(), jarray.begin());
CHECK(j_new == json::array());
}
{
json jarray = {1, 2, 3, 4, 5};
json j_new(jarray.cbegin(), jarray.cbegin());
CHECK(j_new == json::array());
}
}
SECTION("construct from subrange")
{
{
json jarray = {1, 2, 3, 4, 5};
json j_new(jarray.begin() + 1, jarray.begin() + 3);
CHECK(j_new == json({2, 3}));
}
{
json jarray = {1, 2, 3, 4, 5};
json j_new(jarray.cbegin() + 1, jarray.cbegin() + 3);
CHECK(j_new == json({2, 3}));
}
}
SECTION("incompatible iterators")
{
{
json jarray = {1, 2, 3, 4};
json jarray2 = {2, 3, 4, 5};
CHECK_THROWS_AS(json(jarray.begin(), jarray2.end()), std::domain_error);
CHECK_THROWS_AS(json(jarray2.begin(), jarray.end()), std::domain_error);
CHECK_THROWS_WITH(json(jarray.begin(), jarray2.end()), "iterators are not compatible");
CHECK_THROWS_WITH(json(jarray2.begin(), jarray.end()), "iterators are not compatible");
}
{
json jarray = {1, 2, 3, 4};
json jarray2 = {2, 3, 4, 5};
CHECK_THROWS_AS(json(jarray.cbegin(), jarray2.cend()), std::domain_error);
CHECK_THROWS_AS(json(jarray2.cbegin(), jarray.cend()), std::domain_error);
CHECK_THROWS_WITH(json(jarray.cbegin(), jarray2.cend()), "iterators are not compatible");
CHECK_THROWS_WITH(json(jarray2.cbegin(), jarray.cend()), "iterators are not compatible");
}
}
}
SECTION("other values")
{
SECTION("construct with two valid iterators")
{
SECTION("null")
{
{
json j;
CHECK_THROWS_AS(json(j.begin(), j.end()), std::domain_error);
CHECK_THROWS_WITH(json(j.begin(), j.end()), "cannot use construct with iterators from null");
}
{
json j;
CHECK_THROWS_AS(json(j.cbegin(), j.cend()), std::domain_error);
CHECK_THROWS_WITH(json(j.cbegin(), j.cend()), "cannot use construct with iterators from null");
}
}
SECTION("string")
{
{
json j = "foo";
json j_new(j.begin(), j.end());
CHECK(j == j_new);
}
{
json j = "bar";
json j_new(j.cbegin(), j.cend());
CHECK(j == j_new);
}
}
SECTION("number (boolean)")
{
{
json j = false;
json j_new(j.begin(), j.end());
CHECK(j == j_new);
}
{
json j = true;
json j_new(j.cbegin(), j.cend());
CHECK(j == j_new);
}
}
SECTION("number (integer)")
{
{
json j = 17;
json j_new(j.begin(), j.end());
CHECK(j == j_new);
}
{
json j = 17;
json j_new(j.cbegin(), j.cend());
CHECK(j == j_new);
}
}
SECTION("number (unsigned)")
{
{
json j = 17u;
json j_new(j.begin(), j.end());
CHECK(j == j_new);
}
{
json j = 17u;
json j_new(j.cbegin(), j.cend());
CHECK(j == j_new);
}
}
SECTION("number (floating point)")
{
{
json j = 23.42;
json j_new(j.begin(), j.end());
CHECK(j == j_new);
}
{
json j = 23.42;
json j_new(j.cbegin(), j.cend());
CHECK(j == j_new);
}
}
}
SECTION("construct with two invalid iterators")
{
SECTION("string")
{
{
json j = "foo";
CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range);
CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range);
CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range");
CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range");
}
{
json j = "bar";
CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range);
CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range);
CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range");
CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range");
}
}
SECTION("number (boolean)")
{
{
json j = false;
CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range);
CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range);
CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range");
CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range");
}
{
json j = true;
CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range);
CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range);
CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range");
CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range");
}
}
SECTION("number (integer)")
{
{
json j = 17;
CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range);
CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range);
CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range");
CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range");
}
{
json j = 17;
CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range);
CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range);
CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range");
CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range");
}
}
SECTION("number (integer)")
{
{
json j = 17u;
CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range);
CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range);
CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range");
CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range");
}
{
json j = 17u;
CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range);
CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range);
CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range");
CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range");
}
}
SECTION("number (floating point)")
{
{
json j = 23.42;
CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range);
CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range);
CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range");
CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range");
}
{
json j = 23.42;
CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range);
CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range);
CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range");
CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range");
}
}
}
}
}
SECTION("create a JSON value from an input stream")
{
SECTION("std::stringstream")
{
std::stringstream ss;
ss << "[\"foo\",1,2,3,false,{\"one\":1}]";
json j(ss);
CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}}));
}
SECTION("with callback function")
{
std::stringstream ss;
ss << "[\"foo\",1,2,3,false,{\"one\":1}]";
json j(ss, [](int, json::parse_event_t, const json & val)
{
// filter all number(2) elements
if (val == json(2))
{
return false;
}
else
{
return true;
}
});
CHECK(j == json({"foo", 1, 3, false, {{"one", 1}}}));
}
SECTION("std::ifstream")
{
std::ifstream f("test/data/json_tests/pass1.json");
json j(f);
}
}
}
TEST_CASE("other constructors and destructor")
{
SECTION("copy constructor")
{
SECTION("object")
{
json j {{"foo", 1}, {"bar", false}};
json k(j);
CHECK(j == k);
}
SECTION("array")
{
json j {"foo", 1, 42.23, false};
json k(j);
CHECK(j == k);
}
SECTION("null")
{
json j(nullptr);
json k(j);
CHECK(j == k);
}
SECTION("boolean")
{
json j(true);
json k(j);
CHECK(j == k);
}
SECTION("string")
{
json j("Hello world");
json k(j);
CHECK(j == k);
}
SECTION("number (integer)")
{
json j(42);
json k(j);
CHECK(j == k);
}
SECTION("number (unsigned)")
{
json j(42u);
json k(j);
CHECK(j == k);
}
SECTION("number (floating-point)")
{
json j(42.23);
json k(j);
CHECK(j == k);
}
}
SECTION("move constructor")
{
json j {{"foo", "bar"}, {"baz", {1, 2, 3, 4}}, {"a", 42u}, {"b", 42.23}, {"c", nullptr}};
CHECK(j.type() == json::value_t::object);
json k(std::move(j));
CHECK(k.type() == json::value_t::object);
CHECK(j.type() == json::value_t::null);
}
SECTION("copy assignment")
{
SECTION("object")
{
json j {{"foo", 1}, {"bar", false}};
json k;
k = j;
CHECK(j == k);
}
SECTION("array")
{
json j {"foo", 1, 42.23, false};
json k;
k = j;
CHECK(j == k);
}
SECTION("null")
{
json j(nullptr);
json k;
k = j;
CHECK(j == k);
}
SECTION("boolean")
{
json j(true);
json k;
k = j;
CHECK(j == k);
}
SECTION("string")
{
json j("Hello world");
json k;
k = j;
CHECK(j == k);
}
SECTION("number (integer)")
{
json j(42);
json k;
k = j;
CHECK(j == k);
}
SECTION("number (unsigned)")
{
json j(42u);
json k;
k = j;
CHECK(j == k);
}
SECTION("number (floating-point)")
{
json j(42.23);
json k;
k = j;
CHECK(j == k);
}
}
SECTION("destructor")
{
SECTION("object")
{
auto j = new json {{"foo", 1}, {"bar", false}};
delete j;
}
SECTION("array")
{
auto j = new json {"foo", 1, 1u, false, 23.42};
delete j;
}
SECTION("string")
{
auto j = new json("Hello world");
delete j;
}
}
}
TEST_CASE("object inspection")
{
SECTION("convenience type checker")
{
SECTION("object")
{
json j {{"foo", 1}, {"bar", false}};
CHECK(not j.is_null());
CHECK(not j.is_boolean());
CHECK(not j.is_number());
CHECK(not j.is_number_integer());
CHECK(not j.is_number_unsigned());
CHECK(not j.is_number_float());
CHECK(j.is_object());
CHECK(not j.is_array());
CHECK(not j.is_string());
CHECK(not j.is_discarded());
CHECK(not j.is_primitive());
CHECK(j.is_structured());
}
SECTION("array")
{
json j {"foo", 1, 1u, 42.23, false};
CHECK(not j.is_null());
CHECK(not j.is_boolean());
CHECK(not j.is_number());
CHECK(not j.is_number_integer());
CHECK(not j.is_number_unsigned());
CHECK(not j.is_number_float());
CHECK(not j.is_object());
CHECK(j.is_array());
CHECK(not j.is_string());
CHECK(not j.is_discarded());
CHECK(not j.is_primitive());
CHECK(j.is_structured());
}
SECTION("null")
{
json j(nullptr);
CHECK(j.is_null());
CHECK(not j.is_boolean());
CHECK(not j.is_number());
CHECK(not j.is_number_integer());
CHECK(not j.is_number_unsigned());
CHECK(not j.is_number_float());
CHECK(not j.is_object());
CHECK(not j.is_array());
CHECK(not j.is_string());
CHECK(not j.is_discarded());
CHECK(j.is_primitive());
CHECK(not j.is_structured());
}
SECTION("boolean")
{
json j(true);
CHECK(not j.is_null());
CHECK(j.is_boolean());
CHECK(not j.is_number());
CHECK(not j.is_number_integer());
CHECK(not j.is_number_unsigned());
CHECK(not j.is_number_float());
CHECK(not j.is_object());
CHECK(not j.is_array());
CHECK(not j.is_string());
CHECK(not j.is_discarded());
CHECK(j.is_primitive());
CHECK(not j.is_structured());
}
SECTION("string")
{
json j("Hello world");
CHECK(not j.is_null());
CHECK(not j.is_boolean());
CHECK(not j.is_number());
CHECK(not j.is_number_integer());
CHECK(not j.is_number_unsigned());
CHECK(not j.is_number_float());
CHECK(not j.is_object());
CHECK(not j.is_array());
CHECK(j.is_string());
CHECK(not j.is_discarded());
CHECK(j.is_primitive());
CHECK(not j.is_structured());
}
SECTION("number (integer)")
{
json j(42);
CHECK(not j.is_null());
CHECK(not j.is_boolean());
CHECK(j.is_number());
CHECK(j.is_number_integer());
CHECK(not j.is_number_unsigned());
CHECK(not j.is_number_float());
CHECK(not j.is_object());
CHECK(not j.is_array());
CHECK(not j.is_string());
CHECK(not j.is_discarded());
CHECK(j.is_primitive());
CHECK(not j.is_structured());
}
SECTION("number (unsigned)")
{
json j(42u);
CHECK(not j.is_null());
CHECK(not j.is_boolean());
CHECK(j.is_number());
CHECK(j.is_number_integer());
CHECK(j.is_number_unsigned());
CHECK(not j.is_number_float());
CHECK(not j.is_object());
CHECK(not j.is_array());
CHECK(not j.is_string());
CHECK(not j.is_discarded());
CHECK(j.is_primitive());
CHECK(not j.is_structured());
}
SECTION("number (floating-point)")
{
json j(42.23);
CHECK(not j.is_null());
CHECK(not j.is_boolean());
CHECK(j.is_number());
CHECK(not j.is_number_integer());
CHECK(not j.is_number_unsigned());
CHECK(j.is_number_float());
CHECK(not j.is_object());
CHECK(not j.is_array());
CHECK(not j.is_string());
CHECK(not j.is_discarded());
CHECK(j.is_primitive());
CHECK(not j.is_structured());
}
SECTION("discarded")
{
json j(json::value_t::discarded);
CHECK(not j.is_null());
CHECK(not j.is_boolean());
CHECK(not j.is_number());
CHECK(not j.is_number_integer());
CHECK(not j.is_number_unsigned());
CHECK(not j.is_number_float());
CHECK(not j.is_object());
CHECK(not j.is_array());
CHECK(not j.is_string());
CHECK(j.is_discarded());
CHECK(not j.is_primitive());
CHECK(not j.is_structured());
}
}
SECTION("serialization")
{
json j {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} };
SECTION("no indent / indent=-1")
{
CHECK(j.dump() ==
"{\"array\":[1,2,3,4],\"boolean\":false,\"null\":null,\"number\":42,\"object\":{},\"string\":\"Hello world\"}");
CHECK(j.dump() == j.dump(-1));
}
SECTION("indent=0")
{
CHECK(j.dump(0) ==
"{\n\"array\": [\n1,\n2,\n3,\n4\n],\n\"boolean\": false,\n\"null\": null,\n\"number\": 42,\n\"object\": {},\n\"string\": \"Hello world\"\n}");
}
SECTION("indent=4")
{
CHECK(j.dump(4) ==
"{\n \"array\": [\n 1,\n 2,\n 3,\n 4\n ],\n \"boolean\": false,\n \"null\": null,\n \"number\": 42,\n \"object\": {},\n \"string\": \"Hello world\"\n}");
}
SECTION("dump and floating-point numbers")
{
auto s = json(42.23).dump();
CHECK(s.find("42.23") != std::string::npos);
}
SECTION("dump and small floating-point numbers")
{
auto s = json(1.23456e-78).dump();
CHECK(s.find("1.23456e-78") != std::string::npos);
}
SECTION("dump and non-ASCII characters")
{
CHECK(json("ä").dump() == "\"ä\"");
CHECK(json("Ö").dump() == "\"Ö\"");
CHECK(json("❤️").dump() == "\"❤️\"");
}
SECTION("serialization of discarded element")
{
json j_discarded(json::value_t::discarded);
CHECK(j_discarded.dump() == "<discarded>");
}
SECTION("check that precision is reset after serialization")
{
// create stringstream and set precision
std::stringstream ss;
ss.precision(3);
ss << 3.141592653589793 << std::fixed;
CHECK(ss.str() == "3.14");
// reset stringstream
ss.str(std::string());
// use stringstream for JSON serialization
json j_number = 3.141592653589793;
ss << j_number;
// check that precision has been overridden during serialization
CHECK(ss.str() == "3.141592653589793");
// check that precision has been restored
CHECK(ss.precision() == 3);
}
}
SECTION("return the type of the object (explicit)")
{
SECTION("null")
{
json j = nullptr;
CHECK(j.type() == json::value_t::null);
}
SECTION("object")
{
json j = {{"foo", "bar"}};
CHECK(j.type() == json::value_t::object);
}
SECTION("array")
{
json j = {1, 2, 3, 4};
CHECK(j.type() == json::value_t::array);
}
SECTION("boolean")
{
json j = true;
CHECK(j.type() == json::value_t::boolean);
}
SECTION("string")
{
json j = "Hello world";
CHECK(j.type() == json::value_t::string);
}
SECTION("number (integer)")
{
json j = 23;
CHECK(j.type() == json::value_t::number_integer);
}
SECTION("number (unsigned)")
{
json j = 23u;
CHECK(j.type() == json::value_t::number_unsigned);
}
SECTION("number (floating-point)")
{
json j = 42.23;
CHECK(j.type() == json::value_t::number_float);
}
}
SECTION("return the type of the object (implicit)")
{
SECTION("null")
{
json j = nullptr;
json::value_t t = j;
CHECK(t == j.type());
}
SECTION("object")
{
json j = {{"foo", "bar"}};
json::value_t t = j;
CHECK(t == j.type());
}
SECTION("array")
{
json j = {1, 2, 3, 4};
json::value_t t = j;
CHECK(t == j.type());
}
SECTION("boolean")
{
json j = true;
json::value_t t = j;
CHECK(t == j.type());
}
SECTION("string")
{
json j = "Hello world";
json::value_t t = j;
CHECK(t == j.type());
}
SECTION("number (integer)")
{
json j = 23;
json::value_t t = j;
CHECK(t == j.type());
}
SECTION("number (unsigned)")
{
json j = 23u;
json::value_t t = j;
CHECK(t == j.type());
}
SECTION("number (floating-point)")
{
json j = 42.23;
json::value_t t = j;
CHECK(t == j.type());
}
}
}
TEST_CASE("value conversion")
{
SECTION("get an object (explicit)")
{
json::object_t o_reference = {{"object", json::object()}, {"array", {1, 2, 3, 4}}, {"number", 42}, {"boolean", false}, {"null", nullptr}, {"string", "Hello world"} };
json j(o_reference);
SECTION("json::object_t")
{
json::object_t o = j.get<json::object_t>();
CHECK(json(o) == j);
}
SECTION("std::map<json::string_t, json>")
{
std::map<json::string_t, json> o = j.get<std::map<json::string_t, json>>();
CHECK(json(o) == j);
}
SECTION("std::multimap<json::string_t, json>")
{
std::multimap<json::string_t, json> o = j.get<std::multimap<json::string_t, json>>();
CHECK(json(o) == j);
}
SECTION("std::unordered_map<json::string_t, json>")
{
std::unordered_map<json::string_t, json> o = j.get<std::unordered_map<json::string_t, json>>();
CHECK(json(o) == j);
}
SECTION("std::unordered_multimap<json::string_t, json>")
{
std::unordered_multimap<json::string_t, json> o =
j.get<std::unordered_multimap<json::string_t, json>>();
CHECK(json(o) == j);
}
SECTION("exception in case of a non-object type")
{
CHECK_THROWS_AS(json(json::value_t::null).get<json::object_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::array).get<json::object_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::string).get<json::object_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::boolean).get<json::object_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::number_integer).get<json::object_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::number_unsigned).get<json::object_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::number_float).get<json::object_t>(), std::logic_error);
CHECK_THROWS_WITH(json(json::value_t::null).get<json::object_t>(),
"type must be object, but is null");
CHECK_THROWS_WITH(json(json::value_t::array).get<json::object_t>(),
"type must be object, but is array");
CHECK_THROWS_WITH(json(json::value_t::string).get<json::object_t>(),
"type must be object, but is string");
CHECK_THROWS_WITH(json(json::value_t::boolean).get<json::object_t>(),
"type must be object, but is boolean");
CHECK_THROWS_WITH(json(json::value_t::number_integer).get<json::object_t>(),
"type must be object, but is number");
CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get<json::object_t>(),
"type must be object, but is number");
CHECK_THROWS_WITH(json(json::value_t::number_float).get<json::object_t>(),
"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<json::string_t, json>")
{
std::map<json::string_t, json> o = j;
CHECK(json(o) == j);
}
SECTION("std::multimap<json::string_t, json>")
{
std::multimap<json::string_t, json> o = j;
CHECK(json(o) == j);
}
SECTION("std::unordered_map<json::string_t, json>")
{
std::unordered_map<json::string_t, json> o = j;
CHECK(json(o) == j);
}
SECTION("std::unordered_multimap<json::string_t, json>")
{
std::unordered_multimap<json::string_t, json> o = j;
CHECK(json(o) == j);
}
}
SECTION("get an array (explicit)")
{
json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()};
json j(a_reference);
SECTION("json::array_t")
{
json::array_t a = j.get<json::array_t>();
CHECK(json(a) == j);
}
SECTION("std::list<json>")
{
std::list<json> a = j.get<std::list<json>>();
CHECK(json(a) == j);
}
SECTION("std::forward_list<json>")
{
std::forward_list<json> a = j.get<std::forward_list<json>>();
CHECK(json(a) == j);
}
SECTION("std::vector<json>")
{
std::vector<json> a = j.get<std::vector<json>>();
CHECK(json(a) == j);
}
SECTION("std::deque<json>")
{
std::deque<json> a = j.get<std::deque<json>>();
CHECK(json(a) == j);
}
SECTION("exception in case of a non-array type")
{
CHECK_THROWS_AS(json(json::value_t::null).get<json::array_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::object).get<json::array_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::string).get<json::array_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::boolean).get<json::array_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::number_integer).get<json::array_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::number_unsigned).get<json::array_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::number_float).get<json::array_t>(), std::logic_error);
CHECK_THROWS_WITH(json(json::value_t::null).get<json::array_t>(),
"type must be array, but is null");
CHECK_THROWS_WITH(json(json::value_t::object).get<json::array_t>(),
"type must be array, but is object");
CHECK_THROWS_WITH(json(json::value_t::string).get<json::array_t>(),
"type must be array, but is string");
CHECK_THROWS_WITH(json(json::value_t::boolean).get<json::array_t>(),
"type must be array, but is boolean");
CHECK_THROWS_WITH(json(json::value_t::number_integer).get<json::array_t>(),
"type must be array, but is number");
CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get<json::array_t>(),
"type must be array, but is number");
CHECK_THROWS_WITH(json(json::value_t::number_float).get<json::array_t>(),
"type must be array, but is number");
}
}
SECTION("get an array (implicit)")
{
json::array_t a_reference {json(1), json(1u), json(2.2), json(false), json("string"), json()};
json j(a_reference);
SECTION("json::array_t")
{
json::array_t a = j;
CHECK(json(a) == j);
}
SECTION("std::list<json>")
{
std::list<json> a = j;
CHECK(json(a) == j);
}
SECTION("std::forward_list<json>")
{
std::forward_list<json> a = j;
CHECK(json(a) == j);
}
SECTION("std::vector<json>")
{
std::vector<json> a = j;
CHECK(json(a) == j);
}
SECTION("std::deque<json>")
{
std::deque<json> 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<json::string_t>();
CHECK(json(s) == j);
}
SECTION("std::string")
{
std::string s = j.get<std::string>();
CHECK(json(s) == j);
}
SECTION("exception in case of a non-string type")
{
CHECK_THROWS_AS(json(json::value_t::null).get<json::string_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::object).get<json::string_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::array).get<json::string_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::boolean).get<json::string_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::number_integer).get<json::string_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::number_unsigned).get<json::string_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::number_float).get<json::string_t>(), std::logic_error);
CHECK_THROWS_WITH(json(json::value_t::null).get<json::string_t>(),
"type must be string, but is null");
CHECK_THROWS_WITH(json(json::value_t::object).get<json::string_t>(),
"type must be string, but is object");
CHECK_THROWS_WITH(json(json::value_t::array).get<json::string_t>(),
"type must be string, but is array");
CHECK_THROWS_WITH(json(json::value_t::boolean).get<json::string_t>(),
"type must be string, but is boolean");
CHECK_THROWS_WITH(json(json::value_t::number_integer).get<json::string_t>(),
"type must be string, but is number");
CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get<json::string_t>(),
"type must be string, but is number");
CHECK_THROWS_WITH(json(json::value_t::number_float).get<json::string_t>(),
"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<json::boolean_t>();
CHECK(json(b) == j);
}
SECTION("bool")
{
bool b = j.get<bool>();
CHECK(json(b) == j);
}
SECTION("exception in case of a non-string type")
{
CHECK_THROWS_AS(json(json::value_t::null).get<json::boolean_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::object).get<json::boolean_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::array).get<json::boolean_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::string).get<json::boolean_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::number_integer).get<json::boolean_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::number_unsigned).get<json::boolean_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::number_float).get<json::boolean_t>(), std::logic_error);
CHECK_THROWS_WITH(json(json::value_t::null).get<json::boolean_t>(),
"type must be boolean, but is null");
CHECK_THROWS_WITH(json(json::value_t::object).get<json::boolean_t>(),
"type must be boolean, but is object");
CHECK_THROWS_WITH(json(json::value_t::array).get<json::boolean_t>(),
"type must be boolean, but is array");
CHECK_THROWS_WITH(json(json::value_t::string).get<json::boolean_t>(),
"type must be boolean, but is string");
CHECK_THROWS_WITH(json(json::value_t::number_integer).get<json::boolean_t>(),
"type must be boolean, but is number");
CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get<json::boolean_t>(),
"type must be boolean, but is number");
CHECK_THROWS_WITH(json(json::value_t::number_float).get<json::boolean_t>(),
"type must be boolean, but is number");
}
}
SECTION("get a boolean (implicit)")
{
json::boolean_t b_reference {true};
json j(b_reference);
SECTION("boolean_t")
{
json::boolean_t b = j;
CHECK(json(b) == j);
}
SECTION("bool")
{
bool b = j;
CHECK(json(b) == j);
}
}
SECTION("get an integer number (explicit)")
{
json::number_integer_t n_reference {42};
json j(n_reference);
json::number_unsigned_t n_unsigned_reference {42u};
json j_unsigned(n_unsigned_reference);
SECTION("number_integer_t")
{
json::number_integer_t n = j.get<json::number_integer_t>();
CHECK(json(n) == j);
}
SECTION("number_unsigned_t")
{
json::number_unsigned_t n = j_unsigned.get<json::number_unsigned_t>();
CHECK(json(n) == j_unsigned);
}
SECTION("short")
{
short n = j.get<short>();
CHECK(json(n) == j);
}
SECTION("unsigned short")
{
unsigned short n = j.get<unsigned short>();
CHECK(json(n) == j);
}
SECTION("int")
{
int n = j.get<int>();
CHECK(json(n) == j);
}
SECTION("unsigned int")
{
unsigned int n = j.get<unsigned int>();
CHECK(json(n) == j);
}
SECTION("long")
{
long n = j.get<long>();
CHECK(json(n) == j);
}
SECTION("unsigned long")
{
unsigned long n = j.get<unsigned long>();
CHECK(json(n) == j);
}
SECTION("long long")
{
long long n = j.get<long long>();
CHECK(json(n) == j);
}
SECTION("unsigned long long")
{
unsigned long long n = j.get<unsigned long long>();
CHECK(json(n) == j);
}
SECTION("int8_t")
{
int8_t n = j.get<int8_t>();
CHECK(json(n) == j);
}
SECTION("int16_t")
{
int16_t n = j.get<int16_t>();
CHECK(json(n) == j);
}
SECTION("int32_t")
{
int32_t n = j.get<int32_t>();
CHECK(json(n) == j);
}
SECTION("int64_t")
{
int64_t n = j.get<int64_t>();
CHECK(json(n) == j);
}
SECTION("int8_fast_t")
{
int_fast8_t n = j.get<int_fast8_t>();
CHECK(json(n) == j);
}
SECTION("int16_fast_t")
{
int_fast16_t n = j.get<int_fast16_t>();
CHECK(json(n) == j);
}
SECTION("int32_fast_t")
{
int_fast32_t n = j.get<int_fast32_t>();
CHECK(json(n) == j);
}
SECTION("int64_fast_t")
{
int_fast64_t n = j.get<int_fast64_t>();
CHECK(json(n) == j);
}
SECTION("int8_least_t")
{
int_least8_t n = j.get<int_least8_t>();
CHECK(json(n) == j);
}
SECTION("int16_least_t")
{
int_least16_t n = j.get<int_least16_t>();
CHECK(json(n) == j);
}
SECTION("int32_least_t")
{
int_least32_t n = j.get<int_least32_t>();
CHECK(json(n) == j);
}
SECTION("int64_least_t")
{
int_least64_t n = j.get<int_least64_t>();
CHECK(json(n) == j);
}
SECTION("uint8_t")
{
uint8_t n = j.get<uint8_t>();
CHECK(json(n) == j);
}
SECTION("uint16_t")
{
uint16_t n = j.get<uint16_t>();
CHECK(json(n) == j);
}
SECTION("uint32_t")
{
uint32_t n = j.get<uint32_t>();
CHECK(json(n) == j);
}
SECTION("uint64_t")
{
uint64_t n = j.get<uint64_t>();
CHECK(json(n) == j);
}
SECTION("uint8_fast_t")
{
uint_fast8_t n = j.get<uint_fast8_t>();
CHECK(json(n) == j);
}
SECTION("uint16_fast_t")
{
uint_fast16_t n = j.get<uint_fast16_t>();
CHECK(json(n) == j);
}
SECTION("uint32_fast_t")
{
uint_fast32_t n = j.get<uint_fast32_t>();
CHECK(json(n) == j);
}
SECTION("uint64_fast_t")
{
uint_fast64_t n = j.get<uint_fast64_t>();
CHECK(json(n) == j);
}
SECTION("uint8_least_t")
{
uint_least8_t n = j.get<uint_least8_t>();
CHECK(json(n) == j);
}
SECTION("uint16_least_t")
{
uint_least16_t n = j.get<uint_least16_t>();
CHECK(json(n) == j);
}
SECTION("uint32_least_t")
{
uint_least32_t n = j.get<uint_least32_t>();
CHECK(json(n) == j);
}
SECTION("uint64_least_t")
{
uint_least64_t n = j.get<uint_least64_t>();
CHECK(json(n) == j);
}
SECTION("exception in case of a non-number type")
{
CHECK_THROWS_AS(json(json::value_t::null).get<json::number_integer_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::object).get<json::number_integer_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::array).get<json::number_integer_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::string).get<json::number_integer_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::boolean).get<json::number_integer_t>(), std::logic_error);
CHECK_THROWS_WITH(json(json::value_t::null).get<json::number_integer_t>(),
"type must be number, but is null");
CHECK_THROWS_WITH(json(json::value_t::object).get<json::number_integer_t>(),
"type must be number, but is object");
CHECK_THROWS_WITH(json(json::value_t::array).get<json::number_integer_t>(),
"type must be number, but is array");
CHECK_THROWS_WITH(json(json::value_t::string).get<json::number_integer_t>(),
"type must be number, but is string");
CHECK_THROWS_WITH(json(json::value_t::boolean).get<json::number_integer_t>(),
"type must be number, but is boolean");
CHECK_NOTHROW(json(json::value_t::number_float).get<json::number_integer_t>());
CHECK_NOTHROW(json(json::value_t::number_float).get<json::number_unsigned_t>());
}
}
SECTION("get an integer number (implicit)")
{
json::number_integer_t n_reference {42};
json j(n_reference);
json::number_unsigned_t n_unsigned_reference {42u};
json j_unsigned(n_unsigned_reference);
SECTION("number_integer_t")
{
json::number_integer_t n = j.get<json::number_integer_t>();
CHECK(json(n) == j);
}
SECTION("number_unsigned_t")
{
json::number_unsigned_t n = j_unsigned.get<json::number_unsigned_t>();
CHECK(json(n) == j_unsigned);
}
SECTION("short")
{
short n = j;
CHECK(json(n) == j);
}
SECTION("unsigned short")
{
unsigned short n = j_unsigned;
CHECK(json(n) == j_unsigned);
}
SECTION("int")
{
int n = j;
CHECK(json(n) == j);
}
SECTION("unsigned int")
{
unsigned int n = j_unsigned;
CHECK(json(n) == j_unsigned);
}
SECTION("long")
{
long n = j;
CHECK(json(n) == j);
}
SECTION("unsigned long")
{
unsigned long n = j_unsigned;
CHECK(json(n) == j_unsigned);
}
SECTION("long long")
{
long long n = j;
CHECK(json(n) == j);
}
SECTION("unsigned long long")
{
unsigned long long n = j_unsigned;
CHECK(json(n) == j_unsigned);
}
SECTION("int8_t")
{
int8_t n = j;
CHECK(json(n) == j);
}
SECTION("int16_t")
{
int16_t n = j;
CHECK(json(n) == j);
}
SECTION("int32_t")
{
int32_t n = j;
CHECK(json(n) == j);
}
SECTION("int64_t")
{
int64_t n = j;
CHECK(json(n) == j);
}
SECTION("int8_fast_t")
{
int_fast8_t n = j;
CHECK(json(n) == j);
}
SECTION("int16_fast_t")
{
int_fast16_t n = j;
CHECK(json(n) == j);
}
SECTION("int32_fast_t")
{
int_fast32_t n = j;
CHECK(json(n) == j);
}
SECTION("int64_fast_t")
{
int_fast64_t n = j;
CHECK(json(n) == j);
}
SECTION("int8_least_t")
{
int_least8_t n = j;
CHECK(json(n) == j);
}
SECTION("int16_least_t")
{
int_least16_t n = j;
CHECK(json(n) == j);
}
SECTION("int32_least_t")
{
int_least32_t n = j;
CHECK(json(n) == j);
}
SECTION("int64_least_t")
{
int_least64_t n = j;
CHECK(json(n) == j);
}
SECTION("uint8_t")
{
uint8_t n = j_unsigned;
CHECK(json(n) == j_unsigned);
}
SECTION("uint16_t")
{
uint16_t n = j_unsigned;
CHECK(json(n) == j_unsigned);
}
SECTION("uint32_t")
{
uint32_t n = j_unsigned;
CHECK(json(n) == j_unsigned);
}
SECTION("uint64_t")
{
uint64_t n = j_unsigned;
CHECK(json(n) == j_unsigned);
}
SECTION("uint8_fast_t")
{
uint_fast8_t n = j_unsigned;
CHECK(json(n) == j_unsigned);
}
SECTION("uint16_fast_t")
{
uint_fast16_t n = j_unsigned;
CHECK(json(n) == j_unsigned);
}
SECTION("uint32_fast_t")
{
uint_fast32_t n = j_unsigned;
CHECK(json(n) == j_unsigned);
}
SECTION("uint64_fast_t")
{
uint_fast64_t n = j_unsigned;
CHECK(json(n) == j_unsigned);
}
SECTION("uint8_least_t")
{
uint_least8_t n = j_unsigned;
CHECK(json(n) == j_unsigned);
}
SECTION("uint16_least_t")
{
uint_least16_t n = j_unsigned;
CHECK(json(n) == j_unsigned);
}
SECTION("uint32_least_t")
{
uint_least32_t n = j_unsigned;
CHECK(json(n) == j_unsigned);
}
SECTION("uint64_least_t")
{
uint_least64_t n = j_unsigned;
CHECK(json(n) == j_unsigned);
}
}
SECTION("get a floating-point number (explicit)")
{
json::number_float_t n_reference {42.23};
json j(n_reference);
SECTION("number_float_t")
{
json::number_float_t n = j.get<json::number_float_t>();
CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float));
}
SECTION("float")
{
float n = j.get<float>();
CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float));
}
SECTION("double")
{
double n = j.get<double>();
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<json::number_float_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::object).get<json::number_float_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::array).get<json::number_float_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::string).get<json::number_float_t>(), std::logic_error);
CHECK_THROWS_AS(json(json::value_t::boolean).get<json::number_float_t>(), std::logic_error);
CHECK_THROWS_WITH(json(json::value_t::null).get<json::number_float_t>(),
"type must be number, but is null");
CHECK_THROWS_WITH(json(json::value_t::object).get<json::number_float_t>(),
"type must be number, but is object");
CHECK_THROWS_WITH(json(json::value_t::array).get<json::number_float_t>(),
"type must be number, but is array");
CHECK_THROWS_WITH(json(json::value_t::string).get<json::number_float_t>(),
"type must be number, but is string");
CHECK_THROWS_WITH(json(json::value_t::boolean).get<json::number_float_t>(),
"type must be number, but is boolean");
CHECK_NOTHROW(json(json::value_t::number_integer).get<json::number_float_t>());
CHECK_NOTHROW(json(json::value_t::number_unsigned).get<json::number_float_t>());
}
}
SECTION("get a floating-point number (implicit)")
{
json::number_float_t n_reference {42.23};
json j(n_reference);
SECTION("number_float_t")
{
json::number_float_t n = j;
CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float));
}
SECTION("float")
{
float n = j;
CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float));
}
SECTION("double")
{
double n = j;
CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float));
}
}
SECTION("more involved conversions")
{
SECTION("object-like STL containers")
{
json j1 = {{"one", 1}, {"two", 2}, {"three", 3}};
json j2 = {{"one", 1u}, {"two", 2u}, {"three", 3u}};
json j3 = {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}};
json j4 = {{"one", true}, {"two", false}, {"three", true}};
json j5 = {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}};
SECTION("std::map")
{
auto m1 = j1.get<std::map<std::string, int>>();
auto m2 = j2.get<std::map<std::string, unsigned int>>();
auto m3 = j3.get<std::map<std::string, double>>();
auto m4 = j4.get<std::map<std::string, bool>>();
//auto m5 = j5.get<std::map<std::string, std::string>>();
}
SECTION("std::unordered_map")
{
auto m1 = j1.get<std::unordered_map<std::string, int>>();
auto m2 = j2.get<std::unordered_map<std::string, unsigned int>>();
auto m3 = j3.get<std::unordered_map<std::string, double>>();
auto m4 = j4.get<std::unordered_map<std::string, bool>>();
//auto m5 = j5.get<std::unordered_map<std::string, std::string>>();
//CHECK(m5["one"] == "eins");
}
SECTION("std::multimap")
{
auto m1 = j1.get<std::multimap<std::string, int>>();
auto m2 = j2.get<std::multimap<std::string, unsigned int>>();
auto m3 = j3.get<std::multimap<std::string, double>>();
auto m4 = j4.get<std::multimap<std::string, bool>>();
//auto m5 = j5.get<std::multimap<std::string, std::string>>();
//CHECK(m5["one"] == "eins");
}
SECTION("std::unordered_multimap")
{
auto m1 = j1.get<std::unordered_multimap<std::string, int>>();
auto m2 = j2.get<std::unordered_multimap<std::string, unsigned int>>();
auto m3 = j3.get<std::unordered_multimap<std::string, double>>();
auto m4 = j4.get<std::unordered_multimap<std::string, bool>>();
//auto m5 = j5.get<std::unordered_multimap<std::string, std::string>>();
//CHECK(m5["one"] == "eins");
}
SECTION("exception in case of a non-object type")
{
CHECK_THROWS_AS((json().get<std::map<std::string, int>>()), std::logic_error);
CHECK_THROWS_WITH((json().get<std::map<std::string, int>>()), "type must be object, but is null");
}
}
SECTION("array-like STL containers")
{
json j1 = {1, 2, 3, 4};
json j2 = {1u, 2u, 3u, 4u};
json j3 = {1.2, 2.3, 3.4, 4.5};
json j4 = {true, false, true};
json j5 = {"one", "two", "three"};
SECTION("std::list")
{
auto m1 = j1.get<std::list<int>>();
auto m2 = j2.get<std::list<unsigned int>>();
auto m3 = j3.get<std::list<double>>();
auto m4 = j4.get<std::list<bool>>();
auto m5 = j5.get<std::list<std::string>>();
}
//SECTION("std::forward_list")
//{
// auto m1 = j1.get<std::forward_list<int>>();
// auto m2 = j2.get<std::forward_list<unsigned int>>();
// auto m3 = j3.get<std::forward_list<double>>();
// auto m4 = j4.get<std::forward_list<bool>>();
// auto m5 = j5.get<std::forward_list<std::string>>();
//}
SECTION("std::vector")
{
auto m1 = j1.get<std::vector<int>>();
auto m2 = j2.get<std::vector<unsigned int>>();
auto m3 = j3.get<std::vector<double>>();
auto m4 = j4.get<std::vector<bool>>();
auto m5 = j5.get<std::vector<std::string>>();
}
SECTION("std::deque")
{
auto m1 = j1.get<std::deque<int>>();
auto m2 = j2.get<std::deque<unsigned int>>();
auto m3 = j2.get<std::deque<double>>();
auto m4 = j4.get<std::deque<bool>>();
auto m5 = j5.get<std::deque<std::string>>();
}
SECTION("std::set")
{
auto m1 = j1.get<std::set<int>>();
auto m2 = j2.get<std::set<unsigned int>>();
auto m3 = j3.get<std::set<double>>();
auto m4 = j4.get<std::set<bool>>();
auto m5 = j5.get<std::set<std::string>>();
}
SECTION("std::unordered_set")
{
auto m1 = j1.get<std::unordered_set<int>>();
auto m2 = j2.get<std::unordered_set<unsigned int>>();
auto m3 = j3.get<std::unordered_set<double>>();
auto m4 = j4.get<std::unordered_set<bool>>();
auto m5 = j5.get<std::unordered_set<std::string>>();
}
SECTION("exception in case of a non-object type")
{
CHECK_THROWS_AS((json().get<std::list<int>>()), std::logic_error);
CHECK_THROWS_AS((json().get<std::vector<int>>()), std::logic_error);
CHECK_THROWS_AS((json().get<std::vector<json>>()), std::logic_error);
CHECK_THROWS_AS((json().get<std::list<json>>()), std::logic_error);
CHECK_THROWS_WITH((json().get<std::list<int>>()), "type must be array, but is null");
CHECK_THROWS_WITH((json().get<std::vector<int>>()), "type must be array, but is null");
CHECK_THROWS_WITH((json().get<std::vector<json>>()), "type must be array, but is null");
CHECK_THROWS_WITH((json().get<std::list<json>>()), "type must be array, but is null");
}
}
}
}
TEST_CASE("pointer access")
{
// create a JSON value with different types
json json_types =
{
{"boolean", true},
{
"number", {
{"integer", 42},
{"unsigned", 42u},
{"floating-point", 17.23}
}
},
{"string", "Hello, world!"},
{"array", {1, 2, 3, 4, 5}},
{"null", nullptr}
};
SECTION("pointer access to object_t")
{
using test_type = json::object_t;
json value = {{"one", 1}, {"two", 2}};
// check if pointers are returned correctly
test_type* p1 = value.get_ptr<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
CHECK(*p1 == value.get<test_type>());
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p1 == value.get_ptr<const test_type*>());
CHECK(*p2 == value.get<test_type>());
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p1 == value.get_ptr<const test_type* const>());
CHECK(*p3 == value.get<test_type>());
// check if null pointers are returned correctly
CHECK(value.get_ptr<json::object_t*>() != nullptr);
CHECK(value.get_ptr<json::array_t*>() == nullptr);
CHECK(value.get_ptr<json::string_t*>() == nullptr);
CHECK(value.get_ptr<json::boolean_t*>() == nullptr);
CHECK(value.get_ptr<json::number_integer_t*>() == nullptr);
CHECK(value.get_ptr<json::number_unsigned_t*>() == nullptr);
CHECK(value.get_ptr<json::number_float_t*>() == 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<test_type*>();
// check if pointers are returned correctly
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(*p2 == value.get<test_type>());
const test_type* const p3 = value.get_ptr<const test_type* const>();
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<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
CHECK(*p1 == value.get<test_type>());
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p1 == value.get_ptr<const test_type*>());
CHECK(*p2 == value.get<test_type>());
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p1 == value.get_ptr<const test_type* const>());
CHECK(*p3 == value.get<test_type>());
// check if null pointers are returned correctly
CHECK(value.get_ptr<json::object_t*>() == nullptr);
CHECK(value.get_ptr<json::array_t*>() != nullptr);
CHECK(value.get_ptr<json::string_t*>() == nullptr);
CHECK(value.get_ptr<json::boolean_t*>() == nullptr);
CHECK(value.get_ptr<json::number_integer_t*>() == nullptr);
CHECK(value.get_ptr<json::number_unsigned_t*>() == nullptr);
CHECK(value.get_ptr<json::number_float_t*>() == 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<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
CHECK(*p1 == value.get<test_type>());
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p1 == value.get_ptr<const test_type*>());
CHECK(*p2 == value.get<test_type>());
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p1 == value.get_ptr<const test_type* const>());
CHECK(*p3 == value.get<test_type>());
// check if null pointers are returned correctly
CHECK(value.get_ptr<json::object_t*>() == nullptr);
CHECK(value.get_ptr<json::array_t*>() == nullptr);
CHECK(value.get_ptr<json::string_t*>() != nullptr);
CHECK(value.get_ptr<json::boolean_t*>() == nullptr);
CHECK(value.get_ptr<json::number_integer_t*>() == nullptr);
CHECK(value.get_ptr<json::number_unsigned_t*>() == nullptr);
CHECK(value.get_ptr<json::number_float_t*>() == 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<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
CHECK(*p1 == value.get<test_type>());
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p1 == value.get_ptr<const test_type*>());
CHECK(*p2 == value.get<test_type>());
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p1 == value.get_ptr<const test_type* const>());
CHECK(*p3 == value.get<test_type>());
// check if null pointers are returned correctly
CHECK(value.get_ptr<json::object_t*>() == nullptr);
CHECK(value.get_ptr<json::array_t*>() == nullptr);
CHECK(value.get_ptr<json::string_t*>() == nullptr);
CHECK(value.get_ptr<json::boolean_t*>() != nullptr);
CHECK(value.get_ptr<json::number_integer_t*>() == nullptr);
CHECK(value.get_ptr<json::number_unsigned_t*>() == nullptr);
CHECK(value.get_ptr<json::number_float_t*>() == 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<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
CHECK(*p1 == value.get<test_type>());
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p1 == value.get_ptr<const test_type*>());
CHECK(*p2 == value.get<test_type>());
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p1 == value.get_ptr<const test_type* const>());
CHECK(*p3 == value.get<test_type>());
// check if null pointers are returned correctly
CHECK(value.get_ptr<json::object_t*>() == nullptr);
CHECK(value.get_ptr<json::array_t*>() == nullptr);
CHECK(value.get_ptr<json::string_t*>() == nullptr);
CHECK(value.get_ptr<json::boolean_t*>() == nullptr);
CHECK(value.get_ptr<json::number_integer_t*>() != nullptr);
CHECK(value.get_ptr<json::number_unsigned_t*>() == nullptr);
CHECK(value.get_ptr<json::number_float_t*>() == nullptr);
}
SECTION("pointer access to number_unsigned_t")
{
using test_type = json::number_unsigned_t;
json value = 23u;
// check if pointers are returned correctly
test_type* p1 = value.get_ptr<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
CHECK(*p1 == value.get<test_type>());
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p1 == value.get_ptr<const test_type*>());
CHECK(*p2 == value.get<test_type>());
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p1 == value.get_ptr<const test_type* const>());
CHECK(*p3 == value.get<test_type>());
// check if null pointers are returned correctly
CHECK(value.get_ptr<json::object_t*>() == nullptr);
CHECK(value.get_ptr<json::array_t*>() == nullptr);
CHECK(value.get_ptr<json::string_t*>() == nullptr);
CHECK(value.get_ptr<json::boolean_t*>() == nullptr);
CHECK(value.get_ptr<json::number_integer_t*>() != nullptr);
CHECK(value.get_ptr<json::number_unsigned_t*>() != nullptr);
CHECK(value.get_ptr<json::number_float_t*>() == 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<test_type*>();
CHECK(p1 == value.get_ptr<test_type*>());
CHECK(*p1 == Approx(value.get<test_type>()));
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(p1 == value.get_ptr<const test_type*>());
CHECK(*p2 == Approx(value.get<test_type>()));
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p1 == value.get_ptr<const test_type* const>());
CHECK(*p3 == Approx(value.get<test_type>()));
// check if null pointers are returned correctly
CHECK(value.get_ptr<json::object_t*>() == nullptr);
CHECK(value.get_ptr<json::array_t*>() == nullptr);
CHECK(value.get_ptr<json::string_t*>() == nullptr);
CHECK(value.get_ptr<json::boolean_t*>() == nullptr);
CHECK(value.get_ptr<json::number_integer_t*>() == nullptr);
CHECK(value.get_ptr<json::number_unsigned_t*>() == nullptr);
CHECK(value.get_ptr<json::number_float_t*>() != 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<test_type&>();
CHECK(&p1 == value.get_ptr<test_type*>());
CHECK(p1 == value.get<test_type>());
const test_type& p2 = value.get_ref<const test_type&>();
CHECK(&p2 == value.get_ptr<const test_type*>());
CHECK(p2 == value.get<test_type>());
// check if mismatching references throw correctly
CHECK_NOTHROW(value.get_ref<json::object_t&>());
CHECK_THROWS(value.get_ref<json::array_t&>());
CHECK_THROWS(value.get_ref<json::string_t&>());
CHECK_THROWS(value.get_ref<json::boolean_t&>());
CHECK_THROWS(value.get_ref<json::number_integer_t&>());
CHECK_THROWS(value.get_ref<json::number_float_t&>());
}
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<test_type&>();
// check if references are returned correctly
const test_type& p2 = value.get_ref<const test_type&>();
CHECK(&p2 == value.get_ptr<const test_type*>());
CHECK(p2 == value.get<test_type>());
}
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<test_type&>();
CHECK(&p1 == value.get_ptr<test_type*>());
CHECK(p1 == value.get<test_type>());
const test_type& p2 = value.get_ref<const test_type&>();
CHECK(&p2 == value.get_ptr<const test_type*>());
CHECK(p2 == value.get<test_type>());
// check if mismatching references throw correctly
CHECK_THROWS(value.get_ref<json::object_t&>());
CHECK_NOTHROW(value.get_ref<json::array_t&>());
CHECK_THROWS(value.get_ref<json::string_t&>());
CHECK_THROWS(value.get_ref<json::boolean_t&>());
CHECK_THROWS(value.get_ref<json::number_integer_t&>());
CHECK_THROWS(value.get_ref<json::number_float_t&>());
}
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<test_type&>();
CHECK(&p1 == value.get_ptr<test_type*>());
CHECK(p1 == value.get<test_type>());
const test_type& p2 = value.get_ref<const test_type&>();
CHECK(&p2 == value.get_ptr<const test_type*>());
CHECK(p2 == value.get<test_type>());
// check if mismatching references throw correctly
CHECK_THROWS(value.get_ref<json::object_t&>());
CHECK_THROWS(value.get_ref<json::array_t&>());
CHECK_NOTHROW(value.get_ref<json::string_t&>());
CHECK_THROWS(value.get_ref<json::boolean_t&>());
CHECK_THROWS(value.get_ref<json::number_integer_t&>());
CHECK_THROWS(value.get_ref<json::number_float_t&>());
}
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<test_type&>();
CHECK(&p1 == value.get_ptr<test_type*>());
CHECK(p1 == value.get<test_type>());
const test_type& p2 = value.get_ref<const test_type&>();
CHECK(&p2 == value.get_ptr<const test_type*>());
CHECK(p2 == value.get<test_type>());
// check if mismatching references throw correctly
CHECK_THROWS(value.get_ref<json::object_t&>());
CHECK_THROWS(value.get_ref<json::array_t&>());
CHECK_THROWS(value.get_ref<json::string_t&>());
CHECK_NOTHROW(value.get_ref<json::boolean_t&>());
CHECK_THROWS(value.get_ref<json::number_integer_t&>());
CHECK_THROWS(value.get_ref<json::number_float_t&>());
}
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<test_type&>();
CHECK(&p1 == value.get_ptr<test_type*>());
CHECK(p1 == value.get<test_type>());
const test_type& p2 = value.get_ref<const test_type&>();
CHECK(&p2 == value.get_ptr<const test_type*>());
CHECK(p2 == value.get<test_type>());
// check if mismatching references throw correctly
CHECK_THROWS(value.get_ref<json::object_t&>());
CHECK_THROWS(value.get_ref<json::array_t&>());
CHECK_THROWS(value.get_ref<json::string_t&>());
CHECK_THROWS(value.get_ref<json::boolean_t&>());
CHECK_NOTHROW(value.get_ref<json::number_integer_t&>());
CHECK_THROWS(value.get_ref<json::number_float_t&>());
}
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<test_type&>();
CHECK(&p1 == value.get_ptr<test_type*>());
CHECK(p1 == value.get<test_type>());
const test_type& p2 = value.get_ref<const test_type&>();
CHECK(&p2 == value.get_ptr<const test_type*>());
CHECK(p2 == value.get<test_type>());
// check if mismatching references throw correctly
CHECK_THROWS(value.get_ref<json::object_t&>());
CHECK_THROWS(value.get_ref<json::array_t&>());
CHECK_THROWS(value.get_ref<json::string_t&>());
CHECK_THROWS(value.get_ref<json::boolean_t&>());
CHECK_THROWS(value.get_ref<json::number_integer_t&>());
CHECK_NOTHROW(value.get_ref<json::number_float_t&>());
}
}
TEST_CASE("element access")
{
SECTION("array")
{
json j = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
const json j_const = j;
SECTION("access specified element with bounds checking")
{
SECTION("access within bounds")
{
CHECK(j.at(0) == json(1));
CHECK(j.at(1) == json(1u));
CHECK(j.at(2) == json(true));
CHECK(j.at(3) == json(nullptr));
CHECK(j.at(4) == json("string"));
CHECK(j.at(5) == json(42.23));
CHECK(j.at(6) == json(json::object()));
CHECK(j.at(7) == json({1, 2, 3}));
CHECK(j_const.at(0) == json(1));
CHECK(j_const.at(1) == json(1u));
CHECK(j_const.at(2) == json(true));
CHECK(j_const.at(3) == json(nullptr));
CHECK(j_const.at(4) == json("string"));
CHECK(j_const.at(5) == json(42.23));
CHECK(j_const.at(6) == json(json::object()));
CHECK(j_const.at(7) == json({1, 2, 3}));
}
SECTION("access outside bounds")
{
CHECK_THROWS_AS(j.at(8), std::out_of_range);
CHECK_THROWS_AS(j_const.at(8), std::out_of_range);
CHECK_THROWS_WITH(j.at(8), "array index 8 is out of range");
CHECK_THROWS_WITH(j_const.at(8), "array index 8 is out of range");
}
SECTION("access on non-array type")
{
SECTION("null")
{
json j_nonarray(json::value_t::null);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error);
CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error);
CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with null");
CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with null");
}
SECTION("boolean")
{
json j_nonarray(json::value_t::boolean);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error);
CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error);
CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with boolean");
CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with boolean");
}
SECTION("string")
{
json j_nonarray(json::value_t::string);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error);
CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error);
CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with string");
CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with string");
}
SECTION("object")
{
json j_nonarray(json::value_t::object);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error);
CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error);
CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with object");
CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with object");
}
SECTION("number (integer)")
{
json j_nonarray(json::value_t::number_integer);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error);
CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error);
CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number");
CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number");
}
SECTION("number (unsigned)")
{
json j_nonarray(json::value_t::number_unsigned);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error);
CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error);
CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number");
CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number");
}
SECTION("number (floating-point)")
{
json j_nonarray(json::value_t::number_float);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error);
CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error);
CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number");
CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number");
}
}
}
SECTION("front and back")
{
CHECK(j.front() == json(1));
CHECK(j_const.front() == json(1));
CHECK(j.back() == json({1, 2, 3}));
CHECK(j_const.back() == json({1, 2, 3}));
}
SECTION("access specified element")
{
SECTION("access within bounds")
{
CHECK(j[0] == json(1));
CHECK(j[1] == json(1u));
CHECK(j[2] == json(true));
CHECK(j[3] == json(nullptr));
CHECK(j[4] == json("string"));
CHECK(j[5] == json(42.23));
CHECK(j[6] == json(json::object()));
CHECK(j[7] == json({1, 2, 3}));
CHECK(j_const[0] == json(1));
CHECK(j_const[1] == json(1u));
CHECK(j_const[2] == json(true));
CHECK(j_const[3] == json(nullptr));
CHECK(j_const[4] == json("string"));
CHECK(j_const[5] == json(42.23));
CHECK(j_const[6] == json(json::object()));
CHECK(j_const[7] == json({1, 2, 3}));
}
SECTION("access on non-array type")
{
SECTION("null")
{
SECTION("standard tests")
{
json j_nonarray(json::value_t::null);
const json j_nonarray_const(j_nonarray);
CHECK_NOTHROW(j_nonarray[0]);
CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error);
CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with null");
}
SECTION("implicit transformation to properly filled array")
{
json j_nonarray;
j_nonarray[3] = 42;
CHECK(j_nonarray == json({nullptr, nullptr, nullptr, 42}));
}
}
SECTION("boolean")
{
json j_nonarray(json::value_t::boolean);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_AS(j_nonarray[0], std::domain_error);
CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error);
CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with boolean");
CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with boolean");
}
SECTION("string")
{
json j_nonarray(json::value_t::string);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_AS(j_nonarray[0], std::domain_error);
CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error);
CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with string");
CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with string");
}
SECTION("object")
{
json j_nonarray(json::value_t::object);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_AS(j_nonarray[0], std::domain_error);
CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error);
CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with object");
CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with object");
}
SECTION("number (integer)")
{
json j_nonarray(json::value_t::number_integer);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_AS(j_nonarray[0], std::domain_error);
CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error);
CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number");
CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number");
}
SECTION("number (unsigned)")
{
json j_nonarray(json::value_t::number_unsigned);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_AS(j_nonarray[0], std::domain_error);
CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error);
CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number");
CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number");
}
SECTION("number (floating-point)")
{
json j_nonarray(json::value_t::number_float);
const json j_nonarray_const(j_nonarray);
CHECK_THROWS_AS(j_nonarray[0], std::domain_error);
CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error);
CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number");
CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number");
}
}
}
SECTION("remove specified element")
{
SECTION("remove element by index")
{
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
jarray.erase(0);
CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
jarray.erase(1);
CHECK(jarray == json({1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
jarray.erase(2);
CHECK(jarray == json({1, 1u, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
jarray.erase(3);
CHECK(jarray == json({1, 1u, true, "string", 42.23, json::object(), {1, 2, 3}}));
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
jarray.erase(4);
CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}}));
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
jarray.erase(5);
CHECK(jarray == json({1, 1u, true, nullptr, "string", json::object(), {1, 2, 3}}));
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
jarray.erase(6);
CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, {1, 2, 3}}));
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
jarray.erase(7);
CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object()}));
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
CHECK_THROWS_AS(jarray.erase(8), std::out_of_range);
CHECK_THROWS_WITH(jarray.erase(8), "array index 8 is out of range");
}
}
SECTION("remove element by iterator")
{
SECTION("erase(begin())")
{
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json::iterator it2 = jarray.erase(jarray.begin());
CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
CHECK(*it2 == json(1u));
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json::const_iterator it2 = jarray.erase(jarray.cbegin());
CHECK(jarray == json({1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
CHECK(*it2 == json(1u));
}
}
SECTION("erase(begin(), end())")
{
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json::iterator it2 = jarray.erase(jarray.begin(), jarray.end());
CHECK(jarray == json::array());
CHECK(it2 == jarray.end());
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cend());
CHECK(jarray == json::array());
CHECK(it2 == jarray.cend());
}
}
SECTION("erase(begin(), begin())")
{
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json::iterator it2 = jarray.erase(jarray.begin(), jarray.begin());
CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
CHECK(*it2 == json(1));
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cbegin());
CHECK(jarray == json({1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
CHECK(*it2 == json(1));
}
}
SECTION("erase at offset")
{
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json::iterator it = jarray.begin() + 4;
json::iterator it2 = jarray.erase(it);
CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}}));
CHECK(*it2 == json(42.23));
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json::const_iterator it = jarray.cbegin() + 4;
json::const_iterator it2 = jarray.erase(it);
CHECK(jarray == json({1, 1u, true, nullptr, 42.23, json::object(), {1, 2, 3}}));
CHECK(*it2 == json(42.23));
}
}
SECTION("erase subrange")
{
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json::iterator it2 = jarray.erase(jarray.begin() + 3, jarray.begin() + 6);
CHECK(jarray == json({1, 1u, true, json::object(), {1, 2, 3}}));
CHECK(*it2 == json::object());
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json::const_iterator it2 = jarray.erase(jarray.cbegin() + 3, jarray.cbegin() + 6);
CHECK(jarray == json({1, 1u, true, json::object(), {1, 2, 3}}));
CHECK(*it2 == json::object());
}
}
SECTION("different arrays")
{
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json jarray2 = {"foo", "bar"};
CHECK_THROWS_AS(jarray.erase(jarray2.begin()), std::domain_error);
CHECK_THROWS_AS(jarray.erase(jarray.begin(), jarray2.end()), std::domain_error);
CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray.end()), std::domain_error);
CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray2.end()), std::domain_error);
CHECK_THROWS_WITH(jarray.erase(jarray2.begin()), "iterator does not fit current value");
CHECK_THROWS_WITH(jarray.erase(jarray.begin(), jarray2.end()),
"iterators do not fit current value");
CHECK_THROWS_WITH(jarray.erase(jarray2.begin(), jarray.end()),
"iterators do not fit current value");
CHECK_THROWS_WITH(jarray.erase(jarray2.begin(), jarray2.end()),
"iterators do not fit current value");
}
{
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
json jarray2 = {"foo", "bar"};
CHECK_THROWS_AS(jarray.erase(jarray2.cbegin()), std::domain_error);
CHECK_THROWS_AS(jarray.erase(jarray.cbegin(), jarray2.cend()), std::domain_error);
CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray.cend()), std::domain_error);
CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray2.cend()), std::domain_error);
CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin()), "iterator does not fit current value");
CHECK_THROWS_WITH(jarray.erase(jarray.cbegin(), jarray2.cend()),
"iterators do not fit current value");
CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin(), jarray.cend()),
"iterators do not fit current value");
CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin(), jarray2.cend()),
"iterators do not fit current value");
}
}
}
SECTION("remove element by index in non-array type")
{
SECTION("null")
{
json j_nonobject(json::value_t::null);
CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with null");
}
SECTION("boolean")
{
json j_nonobject(json::value_t::boolean);
CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with boolean");
}
SECTION("string")
{
json j_nonobject(json::value_t::string);
CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with string");
}
SECTION("object")
{
json j_nonobject(json::value_t::object);
CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with object");
}
SECTION("number (integer)")
{
json j_nonobject(json::value_t::number_integer);
CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number");
}
SECTION("number (unsigned)")
{
json j_nonobject(json::value_t::number_unsigned);
CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number");
}
SECTION("number (floating-point)")
{
json j_nonobject(json::value_t::number_float);
CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number");
}
}
}
}
SECTION("object")
{
json j = {{"integer", 1}, {"unsigned", 1u}, {"floating", 42.23}, {"null", nullptr}, {"string", "hello world"}, {"boolean", true}, {"object", json::object()}, {"array", {1, 2, 3}}};
const json j_const = j;
SECTION("access specified element with bounds checking")
{
SECTION("access within bounds")
{
CHECK(j.at("integer") == json(1));
CHECK(j.at("unsigned") == json(1u));
CHECK(j.at("boolean") == json(true));
CHECK(j.at("null") == json(nullptr));
CHECK(j.at("string") == json("hello world"));
CHECK(j.at("floating") == json(42.23));
CHECK(j.at("object") == json(json::object()));
CHECK(j.at("array") == json({1, 2, 3}));
CHECK(j_const.at("integer") == json(1));
CHECK(j_const.at("unsigned") == json(1u));
CHECK(j_const.at("boolean") == json(true));
CHECK(j_const.at("null") == json(nullptr));
CHECK(j_const.at("string") == json("hello world"));
CHECK(j_const.at("floating") == json(42.23));
CHECK(j_const.at("object") == json(json::object()));
CHECK(j_const.at("array") == json({1, 2, 3}));
}
SECTION("access outside bounds")
{
CHECK_THROWS_AS(j.at("foo"), std::out_of_range);
CHECK_THROWS_AS(j_const.at("foo"), std::out_of_range);
CHECK_THROWS_WITH(j.at("foo"), "key 'foo' not found");
CHECK_THROWS_WITH(j_const.at("foo"), "key 'foo' not found");
}
SECTION("access on non-object type")
{
SECTION("null")
{
json j_nonobject(json::value_t::null);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with null");
CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with null");
}
SECTION("boolean")
{
json j_nonobject(json::value_t::boolean);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with boolean");
CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with boolean");
}
SECTION("string")
{
json j_nonobject(json::value_t::string);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with string");
CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with string");
}
SECTION("array")
{
json j_nonobject(json::value_t::array);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with array");
CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with array");
}
SECTION("number (integer)")
{
json j_nonobject(json::value_t::number_integer);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number");
CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number");
}
SECTION("number (unsigned)")
{
json j_nonobject(json::value_t::number_unsigned);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number");
CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number");
}
SECTION("number (floating-point)")
{
json j_nonobject(json::value_t::number_float);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number");
CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number");
}
}
}
SECTION("access specified element with default value")
{
SECTION("given a key")
{
SECTION("access existing value")
{
CHECK(j.value("integer", 2) == 1);
CHECK(j.value("integer", 1.0) == Approx(1));
CHECK(j.value("unsigned", 2) == 1u);
CHECK(j.value("unsigned", 1.0) == Approx(1u));
CHECK(j.value("null", json(1)) == json());
CHECK(j.value("boolean", false) == true);
CHECK(j.value("string", "bar") == "hello world");
CHECK(j.value("string", std::string("bar")) == "hello world");
CHECK(j.value("floating", 12.34) == Approx(42.23));
CHECK(j.value("floating", 12) == 42);
CHECK(j.value("object", json({{"foo", "bar"}})) == json(json::object()));
CHECK(j.value("array", json({10, 100})) == json({1, 2, 3}));
CHECK(j_const.value("integer", 2) == 1);
CHECK(j_const.value("integer", 1.0) == Approx(1));
CHECK(j_const.value("unsigned", 2) == 1u);
CHECK(j_const.value("unsigned", 1.0) == Approx(1u));
CHECK(j_const.value("boolean", false) == true);
CHECK(j_const.value("string", "bar") == "hello world");
CHECK(j_const.value("string", std::string("bar")) == "hello world");
CHECK(j_const.value("floating", 12.34) == Approx(42.23));
CHECK(j_const.value("floating", 12) == 42);
CHECK(j_const.value("object", json({{"foo", "bar"}})) == json(json::object()));
CHECK(j_const.value("array", json({10, 100})) == json({1, 2, 3}));
}
SECTION("access non-existing value")
{
CHECK(j.value("_", 2) == 2);
CHECK(j.value("_", 2u) == 2u);
CHECK(j.value("_", false) == false);
CHECK(j.value("_", "bar") == "bar");
CHECK(j.value("_", 12.34) == Approx(12.34));
CHECK(j.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
CHECK(j.value("_", json({10, 100})) == json({10, 100}));
CHECK(j_const.value("_", 2) == 2);
CHECK(j_const.value("_", 2u) == 2u);
CHECK(j_const.value("_", false) == false);
CHECK(j_const.value("_", "bar") == "bar");
CHECK(j_const.value("_", 12.34) == Approx(12.34));
CHECK(j_const.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
CHECK(j_const.value("_", json({10, 100})) == json({10, 100}));
}
SECTION("access on non-object type")
{
SECTION("null")
{
json j_nonobject(json::value_t::null);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with null");
CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with null");
}
SECTION("boolean")
{
json j_nonobject(json::value_t::boolean);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with boolean");
CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with boolean");
}
SECTION("string")
{
json j_nonobject(json::value_t::string);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with string");
CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with string");
}
SECTION("array")
{
json j_nonobject(json::value_t::array);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with array");
CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with array");
}
SECTION("number (integer)")
{
json j_nonobject(json::value_t::number_integer);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number");
}
SECTION("number (unsigned)")
{
json j_nonobject(json::value_t::number_unsigned);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number");
}
SECTION("number (floating-point)")
{
json j_nonobject(json::value_t::number_float);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number");
}
}
}
SECTION("given a JSON pointer")
{
SECTION("access existing value")
{
CHECK(j.value("/integer"_json_pointer, 2) == 1);
CHECK(j.value("/integer"_json_pointer, 1.0) == Approx(1));
CHECK(j.value("/unsigned"_json_pointer, 2) == 1u);
CHECK(j.value("/unsigned"_json_pointer, 1.0) == Approx(1u));
CHECK(j.value("/null"_json_pointer, json(1)) == json());
CHECK(j.value("/boolean"_json_pointer, false) == true);
CHECK(j.value("/string"_json_pointer, "bar") == "hello world");
CHECK(j.value("/string"_json_pointer, std::string("bar")) == "hello world");
CHECK(j.value("/floating"_json_pointer, 12.34) == Approx(42.23));
CHECK(j.value("/floating"_json_pointer, 12) == 42);
CHECK(j.value("/object"_json_pointer, json({{"foo", "bar"}})) == json(json::object()));
CHECK(j.value("/array"_json_pointer, json({10, 100})) == json({1, 2, 3}));
CHECK(j_const.value("/integer"_json_pointer, 2) == 1);
CHECK(j_const.value("/integer"_json_pointer, 1.0) == Approx(1));
CHECK(j_const.value("/unsigned"_json_pointer, 2) == 1u);
CHECK(j_const.value("/unsigned"_json_pointer, 1.0) == Approx(1u));
CHECK(j_const.value("/boolean"_json_pointer, false) == true);
CHECK(j_const.value("/string"_json_pointer, "bar") == "hello world");
CHECK(j_const.value("/string"_json_pointer, std::string("bar")) == "hello world");
CHECK(j_const.value("/floating"_json_pointer, 12.34) == Approx(42.23));
CHECK(j_const.value("/floating"_json_pointer, 12) == 42);
CHECK(j_const.value("/object"_json_pointer, json({{"foo", "bar"}})) == json(json::object()));
CHECK(j_const.value("/array"_json_pointer, json({10, 100})) == json({1, 2, 3}));
}
SECTION("access non-existing value")
{
CHECK(j.value("/not/existing"_json_pointer, 2) == 2);
CHECK(j.value("/not/existing"_json_pointer, 2u) == 2u);
CHECK(j.value("/not/existing"_json_pointer, false) == false);
CHECK(j.value("/not/existing"_json_pointer, "bar") == "bar");
CHECK(j.value("/not/existing"_json_pointer, 12.34) == Approx(12.34));
CHECK(j.value("/not/existing"_json_pointer, json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
CHECK(j.value("/not/existing"_json_pointer, json({10, 100})) == json({10, 100}));
CHECK(j_const.value("/not/existing"_json_pointer, 2) == 2);
CHECK(j_const.value("/not/existing"_json_pointer, 2u) == 2u);
CHECK(j_const.value("/not/existing"_json_pointer, false) == false);
CHECK(j_const.value("/not/existing"_json_pointer, "bar") == "bar");
CHECK(j_const.value("/not/existing"_json_pointer, 12.34) == Approx(12.34));
CHECK(j_const.value("/not/existing"_json_pointer, json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
CHECK(j_const.value("/not/existing"_json_pointer, 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"_json_pointer, 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with null");
CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 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"_json_pointer, 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with boolean");
CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 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"_json_pointer, 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with string");
CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 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"_json_pointer, 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with array");
CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 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"_json_pointer, 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number");
CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
"cannot use value() with number");
}
SECTION("number (unsigned)")
{
json j_nonobject(json::value_t::number_unsigned);
const json j_nonobject_const(j_nonobject);
CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number");
CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 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"_json_pointer, 1), std::domain_error);
CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number");
CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
"cannot use value() with number");
}
}
}
}
SECTION("front and back")
{
// "array" is the smallest key
CHECK(j.front() == json({1, 2, 3}));
CHECK(j_const.front() == json({1, 2, 3}));
// "unsigned" is the largest key
CHECK(j.back() == json(1u));
CHECK(j_const.back() == json(1u));
}
SECTION("access specified element")
{
SECTION("access within bounds")
{
CHECK(j["integer"] == json(1));
CHECK(j[json::object_t::key_type("integer")] == j["integer"]);
CHECK(j["unsigned"] == json(1u));
CHECK(j[json::object_t::key_type("unsigned")] == j["unsigned"]);
CHECK(j["boolean"] == json(true));
CHECK(j[json::object_t::key_type("boolean")] == j["boolean"]);
CHECK(j["null"] == json(nullptr));
CHECK(j[json::object_t::key_type("null")] == j["null"]);
CHECK(j["string"] == json("hello world"));
CHECK(j[json::object_t::key_type("string")] == j["string"]);
CHECK(j["floating"] == json(42.23));
CHECK(j[json::object_t::key_type("floating")] == j["floating"]);
CHECK(j["object"] == json(json::object()));
CHECK(j[json::object_t::key_type("object")] == j["object"]);
CHECK(j["array"] == json({1, 2, 3}));
CHECK(j[json::object_t::key_type("array")] == j["array"]);
CHECK(j_const["integer"] == json(1));
CHECK(j_const[json::object_t::key_type("integer")] == j["integer"]);
CHECK(j_const["boolean"] == json(true));
CHECK(j_const[json::object_t::key_type("boolean")] == j["boolean"]);
CHECK(j_const["null"] == json(nullptr));
CHECK(j_const[json::object_t::key_type("null")] == j["null"]);
CHECK(j_const["string"] == json("hello world"));
CHECK(j_const[json::object_t::key_type("string")] == j["string"]);
CHECK(j_const["floating"] == json(42.23));
CHECK(j_const[json::object_t::key_type("floating")] == j["floating"]);
CHECK(j_const["object"] == json(json::object()));
CHECK(j_const[json::object_t::key_type("object")] == j["object"]);
CHECK(j_const["array"] == json({1, 2, 3}));
CHECK(j_const[json::object_t::key_type("array")] == j["array"]);
}
SECTION("access on non-object type")
{
SECTION("null")
{
json j_nonobject(json::value_t::null);
json j_nonobject2(json::value_t::null);
const json j_const_nonobject(j_nonobject);
CHECK_NOTHROW(j_nonobject["foo"]);
CHECK_NOTHROW(j_nonobject2[json::object_t::key_type("foo")]);
CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error);
CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error);
CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with null");
CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")],
"cannot use operator[] with null");
}
SECTION("boolean")
{
json j_nonobject(json::value_t::boolean);
const json j_const_nonobject(j_nonobject);
CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error);
CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error);
CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error);
CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error);
CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with boolean");
CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")],
"cannot use operator[] with boolean");
CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with boolean");
CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")],
"cannot use operator[] with boolean");
}
SECTION("string")
{
json j_nonobject(json::value_t::string);
const json j_const_nonobject(j_nonobject);
CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error);
CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error);
CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error);
CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error);
CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with string");
CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")],
"cannot use operator[] with string");
CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with string");
CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")],
"cannot use operator[] with string");
}
SECTION("array")
{
json j_nonobject(json::value_t::array);
const json j_const_nonobject(j_nonobject);
CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error);
CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error);
CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error);
CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error);
CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with array");
CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], "cannot use operator[] with array");
CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with array");
CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")],
"cannot use operator[] with array");
}
SECTION("number (integer)")
{
json j_nonobject(json::value_t::number_integer);
const json j_const_nonobject(j_nonobject);
CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error);
CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error);
CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error);
CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error);
CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number");
CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")],
"cannot use operator[] with number");
CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number");
CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")],
"cannot use operator[] with number");
}
SECTION("number (unsigned)")
{
json j_nonobject(json::value_t::number_unsigned);
const json j_const_nonobject(j_nonobject);
CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error);
CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error);
CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error);
CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error);
CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number");
CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")],
"cannot use operator[] with number");
CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number");
CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")],
"cannot use operator[] with number");
}
SECTION("number (floating-point)")
{
json j_nonobject(json::value_t::number_float);
const json j_const_nonobject(j_nonobject);
CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error);
CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error);
CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error);
CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error);
CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number");
CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")],
"cannot use operator[] with number");
CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number");
CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")],
"cannot use operator[] with number");
}
}
}
SECTION("remove specified element")
{
SECTION("remove element by key")
{
CHECK(j.find("integer") != j.end());
CHECK(j.erase("integer") == 1);
CHECK(j.find("integer") == j.end());
CHECK(j.erase("integer") == 0);
CHECK(j.find("unsigned") != j.end());
CHECK(j.erase("unsigned") == 1);
CHECK(j.find("unsigned") == j.end());
CHECK(j.erase("unsigned") == 0);
CHECK(j.find("boolean") != j.end());
CHECK(j.erase("boolean") == 1);
CHECK(j.find("boolean") == j.end());
CHECK(j.erase("boolean") == 0);
CHECK(j.find("null") != j.end());
CHECK(j.erase("null") == 1);
CHECK(j.find("null") == j.end());
CHECK(j.erase("null") == 0);
CHECK(j.find("string") != j.end());
CHECK(j.erase("string") == 1);
CHECK(j.find("string") == j.end());
CHECK(j.erase("string") == 0);
CHECK(j.find("floating") != j.end());
CHECK(j.erase("floating") == 1);
CHECK(j.find("floating") == j.end());
CHECK(j.erase("floating") == 0);
CHECK(j.find("object") != j.end());
CHECK(j.erase("object") == 1);
CHECK(j.find("object") == j.end());
CHECK(j.erase("object") == 0);
CHECK(j.find("array") != j.end());
CHECK(j.erase("array") == 1);
CHECK(j.find("array") == j.end());
CHECK(j.erase("array") == 0);
}
SECTION("remove element by iterator")
{
SECTION("erase(begin())")
{
{
json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
json::iterator it2 = jobject.erase(jobject.begin());
CHECK(jobject == json({{"b", 1}, {"c", 17u}}));
CHECK(*it2 == json(1));
}
{
json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
json::const_iterator it2 = jobject.erase(jobject.cbegin());
CHECK(jobject == json({{"b", 1}, {"c", 17u}}));
CHECK(*it2 == json(1));
}
}
SECTION("erase(begin(), end())")
{
{
json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
json::iterator it2 = jobject.erase(jobject.begin(), jobject.end());
CHECK(jobject == json::object());
CHECK(it2 == jobject.end());
}
{
json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cend());
CHECK(jobject == json::object());
CHECK(it2 == jobject.cend());
}
}
SECTION("erase(begin(), begin())")
{
{
json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
json::iterator it2 = jobject.erase(jobject.begin(), jobject.begin());
CHECK(jobject == json({{"a", "a"}, {"b", 1}, {"c", 17u}}));
CHECK(*it2 == json("a"));
}
{
json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cbegin());
CHECK(jobject == json({{"a", "a"}, {"b", 1}, {"c", 17u}}));
CHECK(*it2 == json("a"));
}
}
SECTION("erase at offset")
{
{
json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
json::iterator it = jobject.find("b");
json::iterator it2 = jobject.erase(it);
CHECK(jobject == json({{"a", "a"}, {"c", 17u}}));
CHECK(*it2 == json(17));
}
{
json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}};
json::const_iterator it = jobject.find("b");
json::const_iterator it2 = jobject.erase(it);
CHECK(jobject == json({{"a", "a"}, {"c", 17u}}));
CHECK(*it2 == json(17));
}
}
SECTION("erase subrange")
{
{
json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
json::iterator it2 = jobject.erase(jobject.find("b"), jobject.find("e"));
CHECK(jobject == json({{"a", "a"}, {"e", true}}));
CHECK(*it2 == json(true));
}
{
json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
json::const_iterator it2 = jobject.erase(jobject.find("b"), jobject.find("e"));
CHECK(jobject == json({{"a", "a"}, {"e", true}}));
CHECK(*it2 == json(true));
}
}
SECTION("different objects")
{
{
json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}};
CHECK_THROWS_AS(jobject.erase(jobject2.begin()), std::domain_error);
CHECK_THROWS_AS(jobject.erase(jobject.begin(), jobject2.end()), std::domain_error);
CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject.end()), std::domain_error);
CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject2.end()), std::domain_error);
CHECK_THROWS_WITH(jobject.erase(jobject2.begin()), "iterator does not fit current value");
CHECK_THROWS_WITH(jobject.erase(jobject.begin(), jobject2.end()),
"iterators do not fit current value");
CHECK_THROWS_WITH(jobject.erase(jobject2.begin(), jobject.end()),
"iterators do not fit current value");
CHECK_THROWS_WITH(jobject.erase(jobject2.begin(), jobject2.end()),
"iterators do not fit current value");
}
{
json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}};
CHECK_THROWS_AS(jobject.erase(jobject2.cbegin()), std::domain_error);
CHECK_THROWS_AS(jobject.erase(jobject.cbegin(), jobject2.cend()), std::domain_error);
CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject.cend()), std::domain_error);
CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject2.cend()), std::domain_error);
CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin()), "iterator does not fit current value");
CHECK_THROWS_WITH(jobject.erase(jobject.cbegin(), jobject2.cend()),
"iterators do not fit current value");
CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin(), jobject.cend()),
"iterators do not fit current value");
CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin(), jobject2.cend()),
"iterators do not fit current value");
}
}
}
SECTION("remove element by key in non-object type")
{
SECTION("null")
{
json j_nonobject(json::value_t::null);
CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with null");
}
SECTION("boolean")
{
json j_nonobject(json::value_t::boolean);
CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with boolean");
}
SECTION("string")
{
json j_nonobject(json::value_t::string);
CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with string");
}
SECTION("array")
{
json j_nonobject(json::value_t::array);
CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with array");
}
SECTION("number (integer)")
{
json j_nonobject(json::value_t::number_integer);
CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with number");
}
SECTION("number (floating-point)")
{
json j_nonobject(json::value_t::number_float);
CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error);
CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with number");
}
}
}
SECTION("find an element in an object")
{
SECTION("existing element")
{
for (auto key :
{"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array"
})
{
CHECK(j.find(key) != j.end());
CHECK(*j.find(key) == j.at(key));
CHECK(j_const.find(key) != j_const.end());
CHECK(*j_const.find(key) == j_const.at(key));
}
}
SECTION("nonexisting element")
{
CHECK(j.find("foo") == j.end());
CHECK(j_const.find("foo") == j_const.end());
}
SECTION("all types")
{
SECTION("null")
{
json j_nonarray(json::value_t::null);
const json j_nonarray_const(j_nonarray);
CHECK(j_nonarray.find("foo") == j_nonarray.end());
CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end());
}
SECTION("string")
{
json j_nonarray(json::value_t::string);
const json j_nonarray_const(j_nonarray);
CHECK(j_nonarray.find("foo") == j_nonarray.end());
CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end());
}
SECTION("object")
{
json j_nonarray(json::value_t::object);
const json j_nonarray_const(j_nonarray);
CHECK(j_nonarray.find("foo") == j_nonarray.end());
CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end());
}
SECTION("array")
{
json j_nonarray(json::value_t::array);
const json j_nonarray_const(j_nonarray);
CHECK(j_nonarray.find("foo") == j_nonarray.end());
CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end());
}
SECTION("boolean")
{
json j_nonarray(json::value_t::boolean);
const json j_nonarray_const(j_nonarray);
CHECK(j_nonarray.find("foo") == j_nonarray.end());
CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end());
}
SECTION("number (integer)")
{
json j_nonarray(json::value_t::number_integer);
const json j_nonarray_const(j_nonarray);
CHECK(j_nonarray.find("foo") == j_nonarray.end());
CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end());
}
SECTION("number (unsigned)")
{
json j_nonarray(json::value_t::number_unsigned);
const json j_nonarray_const(j_nonarray);
CHECK(j_nonarray.find("foo") == j_nonarray.end());
CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end());
}
SECTION("number (floating-point)")
{
json j_nonarray(json::value_t::number_float);
const json j_nonarray_const(j_nonarray);
CHECK(j_nonarray.find("foo") == j_nonarray.end());
CHECK(j_nonarray_const.find("foo") == j_nonarray_const.end());
}
}
}
SECTION("count keys in an object")
{
SECTION("existing element")
{
for (auto key :
{"integer", "unsigned", "floating", "null", "string", "boolean", "object", "array"
})
{
CHECK(j.count(key) == 1);
CHECK(j_const.count(key) == 1);
}
}
SECTION("nonexisting element")
{
CHECK(j.count("foo") == 0);
CHECK(j_const.count("foo") == 0);
}
SECTION("all types")
{
SECTION("null")
{
json j_nonobject(json::value_t::null);
const json j_nonobject_const(j_nonobject);
CHECK(j_nonobject.count("foo") == 0);
CHECK(j_nonobject_const.count("foo") == 0);
}
SECTION("string")
{
json j_nonobject(json::value_t::string);
const json j_nonobject_const(j_nonobject);
CHECK(j_nonobject.count("foo") == 0);
CHECK(j_nonobject_const.count("foo") == 0);
}
SECTION("object")
{
json j_nonobject(json::value_t::object);
const json j_nonobject_const(j_nonobject);
CHECK(j_nonobject.count("foo") == 0);
CHECK(j_nonobject_const.count("foo") == 0);
}
SECTION("array")
{
json j_nonobject(json::value_t::array);
const json j_nonobject_const(j_nonobject);
CHECK(j_nonobject.count("foo") == 0);
CHECK(j_nonobject_const.count("foo") == 0);
}
SECTION("boolean")
{
json j_nonobject(json::value_t::boolean);
const json j_nonobject_const(j_nonobject);
CHECK(j_nonobject.count("foo") == 0);
CHECK(j_nonobject_const.count("foo") == 0);
}
SECTION("number (integer)")
{
json j_nonobject(json::value_t::number_integer);
const json j_nonobject_const(j_nonobject);
CHECK(j_nonobject.count("foo") == 0);
CHECK(j_nonobject_const.count("foo") == 0);
}
SECTION("number (unsigned)")
{
json j_nonobject(json::value_t::number_unsigned);
const json j_nonobject_const(j_nonobject);
CHECK(j_nonobject.count("foo") == 0);
CHECK(j_nonobject_const.count("foo") == 0);
}
SECTION("number (floating-point)")
{
json j_nonobject(json::value_t::number_float);
const json j_nonobject_const(j_nonobject);
CHECK(j_nonobject.count("foo") == 0);
CHECK(j_nonobject_const.count("foo") == 0);
}
}
}
}
SECTION("other values")
{
SECTION("front and back")
{
SECTION("null")
{
{
json j;
CHECK_THROWS_AS(j.front(), std::out_of_range);
CHECK_THROWS_AS(j.back(), std::out_of_range);
CHECK_THROWS_WITH(j.front(), "cannot get value");
CHECK_THROWS_WITH(j.back(), "cannot get value");
}
{
const json j{};
CHECK_THROWS_AS(j.front(), std::out_of_range);
CHECK_THROWS_AS(j.back(), std::out_of_range);
CHECK_THROWS_WITH(j.front(), "cannot get value");
CHECK_THROWS_WITH(j.back(), "cannot get value");
}
}
SECTION("string")
{
{
json j = "foo";
CHECK(j.front() == j);
CHECK(j.back() == j);
}
{
const json j = "bar";
CHECK(j.front() == j);
CHECK(j.back() == j);
}
}
SECTION("number (boolean)")
{
{
json j = false;
CHECK(j.front() == j);
CHECK(j.back() == j);
}
{
const json j = true;
CHECK(j.front() == j);
CHECK(j.back() == j);
}
}
SECTION("number (integer)")
{
{
json j = 17;
CHECK(j.front() == j);
CHECK(j.back() == j);
}
{
const json j = 17;
CHECK(j.front() == j);
CHECK(j.back() == j);
}
}
SECTION("number (unsigned)")
{
{
json j = 17u;
CHECK(j.front() == j);
CHECK(j.back() == j);
}
{
const json j = 17u;
CHECK(j.front() == j);
CHECK(j.back() == j);
}
}
SECTION("number (floating point)")
{
{
json j = 23.42;
CHECK(j.front() == j);
CHECK(j.back() == j);
}
{
const json j = 23.42;
CHECK(j.front() == j);
CHECK(j.back() == j);
}
}
}
SECTION("erase with one valid iterator")
{
SECTION("null")
{
{
json j;
CHECK_THROWS_AS(j.erase(j.begin()), std::domain_error);
CHECK_THROWS_WITH(j.erase(j.begin()), "cannot use erase() with null");
}
{
json j;
CHECK_THROWS_AS(j.erase(j.cbegin()), std::domain_error);
CHECK_THROWS_WITH(j.erase(j.begin()), "cannot use erase() with null");
}
}
SECTION("string")
{
{
json j = "foo";
json::iterator it = j.erase(j.begin());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
{
json j = "bar";
json::const_iterator it = j.erase(j.cbegin());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
}
SECTION("number (boolean)")
{
{
json j = false;
json::iterator it = j.erase(j.begin());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
{
json j = true;
json::const_iterator it = j.erase(j.cbegin());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
}
SECTION("number (integer)")
{
{
json j = 17;
json::iterator it = j.erase(j.begin());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
{
json j = 17;
json::const_iterator it = j.erase(j.cbegin());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
}
SECTION("number (unsigned)")
{
{
json j = 17u;
json::iterator it = j.erase(j.begin());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
{
json j = 17u;
json::const_iterator it = j.erase(j.cbegin());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
}
SECTION("number (floating point)")
{
{
json j = 23.42;
json::iterator it = j.erase(j.begin());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
{
json j = 23.42;
json::const_iterator it = j.erase(j.cbegin());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
}
}
SECTION("erase with one invalid iterator")
{
SECTION("string")
{
{
json j = "foo";
CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range);
CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range");
}
{
json j = "bar";
CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range);
CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range");
}
}
SECTION("number (boolean)")
{
{
json j = false;
CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range);
CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range");
}
{
json j = true;
CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range);
CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range");
}
}
SECTION("number (integer)")
{
{
json j = 17;
CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range);
CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range");
}
{
json j = 17;
CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range);
CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range");
}
}
SECTION("number (unsigned)")
{
{
json j = 17u;
CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range);
CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range");
}
{
json j = 17u;
CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range);
CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range");
}
}
SECTION("number (floating point)")
{
{
json j = 23.42;
CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range);
CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range");
}
{
json j = 23.42;
CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range);
CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range");
}
}
}
SECTION("erase with two valid iterators")
{
SECTION("null")
{
{
json j;
CHECK_THROWS_AS(j.erase(j.begin(), j.end()), std::domain_error);
CHECK_THROWS_WITH(j.erase(j.begin(), j.end()), "cannot use erase() with null");
}
{
json j;
CHECK_THROWS_AS(j.erase(j.cbegin(), j.cend()), std::domain_error);
CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cend()), "cannot use erase() with null");
}
}
SECTION("string")
{
{
json j = "foo";
json::iterator it = j.erase(j.begin(), j.end());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
{
json j = "bar";
json::const_iterator it = j.erase(j.cbegin(), j.cend());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
}
SECTION("number (boolean)")
{
{
json j = false;
json::iterator it = j.erase(j.begin(), j.end());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
{
json j = true;
json::const_iterator it = j.erase(j.cbegin(), j.cend());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
}
SECTION("number (integer)")
{
{
json j = 17;
json::iterator it = j.erase(j.begin(), j.end());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
{
json j = 17;
json::const_iterator it = j.erase(j.cbegin(), j.cend());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
}
SECTION("number (unsigned)")
{
{
json j = 17u;
json::iterator it = j.erase(j.begin(), j.end());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
{
json j = 17u;
json::const_iterator it = j.erase(j.cbegin(), j.cend());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
}
SECTION("number (floating point)")
{
{
json j = 23.42;
json::iterator it = j.erase(j.begin(), j.end());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
{
json j = 23.42;
json::const_iterator it = j.erase(j.cbegin(), j.cend());
CHECK(j.type() == json::value_t::null);
CHECK(it == j.end());
}
}
}
SECTION("erase with two invalid iterators")
{
SECTION("string")
{
{
json j = "foo";
CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range);
CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range);
CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range");
CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range");
}
{
json j = "bar";
CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range);
CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range);
CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range");
CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range");
}
}
SECTION("number (boolean)")
{
{
json j = false;
CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range);
CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range);
CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range");
CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range");
}
{
json j = true;
CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range);
CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range);
CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range");
CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range");
}
}
SECTION("number (integer)")
{
{
json j = 17;
CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range);
CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range);
CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range");
CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range");
}
{
json j = 17;
CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range);
CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range);
CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range");
CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range");
}
}
SECTION("number (unsigned)")
{
{
json j = 17u;
CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range);
CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range);
CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range");
CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range");
}
{
json j = 17u;
CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range);
CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range);
CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range");
CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range");
}
}
SECTION("number (floating point)")
{
{
json j = 23.42;
CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range);
CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range);
CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range");
CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range");
}
{
json j = 23.42;
CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range);
CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range);
CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range");
CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range");
}
}
}
}
}
TEST_CASE("iterators")
{
SECTION("basic behavior")
{
SECTION("uninitialized")
{
json::iterator it;
CHECK(it.m_object == nullptr);
json::const_iterator cit;
CHECK(cit.m_object == nullptr);
}
SECTION("boolean")
{
json j = true;
json j_const(j);
SECTION("json + begin/end")
{
json::iterator it = j.begin();
CHECK(it != j.end());
CHECK(*it == j);
it++;
CHECK(it != j.begin());
CHECK(it == j.end());
it--;
CHECK(it == j.begin());
CHECK(it != j.end());
CHECK(*it == j);
++it;
CHECK(it != j.begin());
CHECK(it == j.end());
--it;
CHECK(it == j.begin());
CHECK(it != j.end());
CHECK(*it == j);
}
SECTION("const json + begin/end")
{
json::const_iterator it = j_const.begin();
CHECK(it != j_const.end());
CHECK(*it == j_const);
it++;
CHECK(it != j_const.begin());
CHECK(it == j_const.end());
it--;
CHECK(it == j_const.begin());
CHECK(it != j_const.end());
CHECK(*it == j_const);
++it;
CHECK(it != j_const.begin());
CHECK(it == j_const.end());
--it;
CHECK(it == j_const.begin());
CHECK(it != j_const.end());
CHECK(*it == j_const);
}
SECTION("json + cbegin/cend")
{
json::const_iterator it = j.cbegin();
CHECK(it != j.cend());
CHECK(*it == j);
it++;
CHECK(it != j.cbegin());
CHECK(it == j.cend());
it--;
CHECK(it == j.cbegin());
CHECK(it != j.cend());
CHECK(*it == j);
++it;
CHECK(it != j.cbegin());
CHECK(it == j.cend());
--it;
CHECK(it == j.cbegin());
CHECK(it != j.cend());
CHECK(*it == j);
}
SECTION("const json + cbegin/cend")
{
json::const_iterator it = j_const.cbegin();
CHECK(it != j_const.cend());
CHECK(*it == j_const);
it++;
CHECK(it != j_const.cbegin());
CHECK(it == j_const.cend());
it--;
CHECK(it == j_const.cbegin());
CHECK(it != j_const.cend());
CHECK(*it == j_const);
++it;
CHECK(it != j_const.cbegin());
CHECK(it == j_const.cend());
--it;
CHECK(it == j_const.cbegin());
CHECK(it != j_const.cend());
CHECK(*it == j_const);
}
SECTION("json + rbegin/rend")
{
json::reverse_iterator it = j.rbegin();
CHECK(it != j.rend());
CHECK(*it == j);
it++;
CHECK(it != j.rbegin());
CHECK(it == j.rend());
it--;
CHECK(it == j.rbegin());
CHECK(it != j.rend());
CHECK(*it == j);
++it;
CHECK(it != j.rbegin());
CHECK(it == j.rend());
--it;
CHECK(it == j.rbegin());
CHECK(it != j.rend());
CHECK(*it == j);
}
SECTION("json + crbegin/crend")
{
json::const_reverse_iterator it = j.crbegin();
CHECK(it != j.crend());
CHECK(*it == j);
it++;
CHECK(it != j.crbegin());
CHECK(it == j.crend());
it--;
CHECK(it == j.crbegin());
CHECK(it != j.crend());
CHECK(*it == j);
++it;
CHECK(it != j.crbegin());
CHECK(it == j.crend());
--it;
CHECK(it == j.crbegin());
CHECK(it != j.crend());
CHECK(*it == j);
}
SECTION("const json + crbegin/crend")
{
json::const_reverse_iterator it = j_const.crbegin();
CHECK(it != j_const.crend());
CHECK(*it == j_const);
it++;
CHECK(it != j_const.crbegin());
CHECK(it == j_const.crend());
it--;
CHECK(it == j_const.crbegin());
CHECK(it != j_const.crend());
CHECK(*it == j_const);
++it;
CHECK(it != j_const.crbegin());
CHECK(it == j_const.crend());
--it;
CHECK(it == j_const.crbegin());
CHECK(it != j_const.crend());
CHECK(*it == j_const);
}
SECTION("key/value")
{
auto it = j.begin();
auto cit = j_const.cbegin();
CHECK_THROWS_AS(it.key(), std::domain_error);
CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators");
CHECK(it.value() == json(true));
CHECK_THROWS_AS(cit.key(), std::domain_error);
CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators");
CHECK(cit.value() == json(true));
auto rit = j.rend();
auto crit = j.crend();
CHECK_THROWS_AS(rit.key(), std::domain_error);
CHECK_THROWS_AS(rit.value(), std::out_of_range);
CHECK_THROWS_AS(crit.key(), std::domain_error);
CHECK_THROWS_AS(crit.value(), std::out_of_range);
CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators");
CHECK_THROWS_WITH(rit.value(), "cannot get value");
CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators");
CHECK_THROWS_WITH(crit.value(), "cannot get value");
}
}
SECTION("string")
{
json j = "hello world";
json j_const(j);
SECTION("json + begin/end")
{
json::iterator it = j.begin();
CHECK(it != j.end());
CHECK(*it == j);
it++;
CHECK(it != j.begin());
CHECK(it == j.end());
it--;
CHECK(it == j.begin());
CHECK(it != j.end());
CHECK(*it == j);
++it;
CHECK(it != j.begin());
CHECK(it == j.end());
--it;
CHECK(it == j.begin());
CHECK(it != j.end());
CHECK(*it == j);
}
SECTION("const json + begin/end")
{
json::const_iterator it = j_const.begin();
CHECK(it != j_const.end());
CHECK(*it == j_const);
it++;
CHECK(it != j_const.begin());
CHECK(it == j_const.end());
it--;
CHECK(it == j_const.begin());
CHECK(it != j_const.end());
CHECK(*it == j_const);
++it;
CHECK(it != j_const.begin());
CHECK(it == j_const.end());
--it;
CHECK(it == j_const.begin());
CHECK(it != j_const.end());
CHECK(*it == j_const);
}
SECTION("json + cbegin/cend")
{
json::const_iterator it = j.cbegin();
CHECK(it != j.cend());
CHECK(*it == j);
it++;
CHECK(it != j.cbegin());
CHECK(it == j.cend());
it--;
CHECK(it == j.cbegin());
CHECK(it != j.cend());
CHECK(*it == j);
++it;
CHECK(it != j.cbegin());
CHECK(it == j.cend());
--it;
CHECK(it == j.cbegin());
CHECK(it != j.cend());
CHECK(*it == j);
}
SECTION("const json + cbegin/cend")
{
json::const_iterator it = j_const.cbegin();
CHECK(it != j_const.cend());
CHECK(*it == j_const);
it++;
CHECK(it != j_const.cbegin());
CHECK(it == j_const.cend());
it--;
CHECK(it == j_const.cbegin());
CHECK(it != j_const.cend());
CHECK(*it == j_const);
++it;
CHECK(it != j_const.cbegin());
CHECK(it == j_const.cend());
--it;
CHECK(it == j_const.cbegin());
CHECK(it != j_const.cend());
CHECK(*it == j_const);
}
SECTION("json + rbegin/rend")
{
json::reverse_iterator it = j.rbegin();
CHECK(it != j.rend());
CHECK(*it == j);
it++;
CHECK(it != j.rbegin());
CHECK(it == j.rend());
it--;
CHECK(it == j.rbegin());
CHECK(it != j.rend());
CHECK(*it == j);
++it;
CHECK(it != j.rbegin());
CHECK(it == j.rend());
--it;
CHECK(it == j.rbegin());
CHECK(it != j.rend());
CHECK(*it == j);
}
SECTION("json + crbegin/crend")
{
json::const_reverse_iterator it = j.crbegin();
CHECK(it != j.crend());
CHECK(*it == j);
it++;
CHECK(it != j.crbegin());
CHECK(it == j.crend());
it--;
CHECK(it == j.crbegin());
CHECK(it != j.crend());
CHECK(*it == j);
++it;
CHECK(it != j.crbegin());
CHECK(it == j.crend());
--it;
CHECK(it == j.crbegin());
CHECK(it != j.crend());
CHECK(*it == j);
}
SECTION("const json + crbegin/crend")
{
json::const_reverse_iterator it = j_const.crbegin();
CHECK(it != j_const.crend());
CHECK(*it == j_const);
it++;
CHECK(it != j_const.crbegin());
CHECK(it == j_const.crend());
it--;
CHECK(it == j_const.crbegin());
CHECK(it != j_const.crend());
CHECK(*it == j_const);
++it;
CHECK(it != j_const.crbegin());
CHECK(it == j_const.crend());
--it;
CHECK(it == j_const.crbegin());
CHECK(it != j_const.crend());
CHECK(*it == j_const);
}
SECTION("key/value")
{
auto it = j.begin();
auto cit = j_const.cbegin();
CHECK_THROWS_AS(it.key(), std::domain_error);
CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators");
CHECK(it.value() == json("hello world"));
CHECK_THROWS_AS(cit.key(), std::domain_error);
CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators");
CHECK(cit.value() == json("hello world"));
auto rit = j.rend();
auto crit = j.crend();
CHECK_THROWS_AS(rit.key(), std::domain_error);
CHECK_THROWS_AS(rit.value(), std::out_of_range);
CHECK_THROWS_AS(crit.key(), std::domain_error);
CHECK_THROWS_AS(crit.value(), std::out_of_range);
CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators");
CHECK_THROWS_WITH(rit.value(), "cannot get value");
CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators");
CHECK_THROWS_WITH(crit.value(), "cannot get value");
}
}
SECTION("array")
{
json j = {1, 2, 3};
json j_const(j);
SECTION("json + begin/end")
{
json::iterator it_begin = j.begin();
json::iterator it_end = j.end();
auto it = it_begin;
CHECK(it != it_end);
CHECK(*it == j[0]);
it++;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j[1]);
++it;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j[2]);
++it;
CHECK(it != it_begin);
CHECK(it == it_end);
}
SECTION("const json + begin/end")
{
json::const_iterator it_begin = j_const.begin();
json::const_iterator it_end = j_const.end();
auto it = it_begin;
CHECK(it != it_end);
CHECK(*it == j_const[0]);
it++;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j_const[1]);
++it;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j_const[2]);
++it;
CHECK(it != it_begin);
CHECK(it == it_end);
}
SECTION("json + cbegin/cend")
{
json::const_iterator it_begin = j.cbegin();
json::const_iterator it_end = j.cend();
auto it = it_begin;
CHECK(it != it_end);
CHECK(*it == j[0]);
it++;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j[1]);
++it;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j[2]);
++it;
CHECK(it != it_begin);
CHECK(it == it_end);
}
SECTION("const json + cbegin/cend")
{
json::const_iterator it_begin = j_const.cbegin();
json::const_iterator it_end = j_const.cend();
auto it = it_begin;
CHECK(it != it_end);
CHECK(*it == j[0]);
it++;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j[1]);
++it;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j[2]);
++it;
CHECK(it != it_begin);
CHECK(it == it_end);
}
SECTION("json + rbegin/rend")
{
json::reverse_iterator it_begin = j.rbegin();
json::reverse_iterator it_end = j.rend();
auto it = it_begin;
CHECK(it != it_end);
CHECK(*it == j[2]);
it++;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j[1]);
++it;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j[0]);
++it;
CHECK(it != it_begin);
CHECK(it == it_end);
}
SECTION("json + crbegin/crend")
{
json::const_reverse_iterator it_begin = j.crbegin();
json::const_reverse_iterator it_end = j.crend();
auto it = it_begin;
CHECK(it != it_end);
CHECK(*it == j[2]);
it++;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j[1]);
++it;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j[0]);
++it;
CHECK(it != it_begin);
CHECK(it == it_end);
}
SECTION("const json + crbegin/crend")
{
json::const_reverse_iterator it_begin = j_const.crbegin();
json::const_reverse_iterator it_end = j_const.crend();
auto it = it_begin;
CHECK(it != it_end);
CHECK(*it == j[2]);
it++;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j[1]);
++it;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j[0]);
++it;
CHECK(it != it_begin);
CHECK(it == it_end);
}
SECTION("key/value")
{
auto it = j.begin();
auto cit = j_const.cbegin();
CHECK_THROWS_AS(it.key(), std::domain_error);
CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators");
CHECK(it.value() == json(1));
CHECK_THROWS_AS(cit.key(), std::domain_error);
CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators");
CHECK(cit.value() == json(1));
}
}
SECTION("object")
{
json j = {{"A", 1}, {"B", 2}, {"C", 3}};
json j_const(j);
SECTION("json + begin/end")
{
json::iterator it_begin = j.begin();
json::iterator it_end = j.end();
auto it = it_begin;
CHECK(it != it_end);
CHECK(*it == j["A"]);
it++;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j["B"]);
++it;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j["C"]);
++it;
CHECK(it != it_begin);
CHECK(it == it_end);
}
SECTION("const json + begin/end")
{
json::const_iterator it_begin = j_const.begin();
json::const_iterator it_end = j_const.end();
auto it = it_begin;
CHECK(it != it_end);
CHECK(*it == j_const["A"]);
it++;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j_const["B"]);
++it;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j_const["C"]);
++it;
CHECK(it != it_begin);
CHECK(it == it_end);
}
SECTION("json + cbegin/cend")
{
json::const_iterator it_begin = j.cbegin();
json::const_iterator it_end = j.cend();
auto it = it_begin;
CHECK(it != it_end);
CHECK(*it == j["A"]);
it++;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j["B"]);
++it;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j["C"]);
++it;
CHECK(it != it_begin);
CHECK(it == it_end);
}
SECTION("const json + cbegin/cend")
{
json::const_iterator it_begin = j_const.cbegin();
json::const_iterator it_end = j_const.cend();
auto it = it_begin;
CHECK(it != it_end);
CHECK(*it == j_const["A"]);
it++;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j_const["B"]);
++it;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j_const["C"]);
++it;
CHECK(it != it_begin);
CHECK(it == it_end);
}
SECTION("json + rbegin/rend")
{
json::reverse_iterator it_begin = j.rbegin();
json::reverse_iterator it_end = j.rend();
auto it = it_begin;
CHECK(it != it_end);
CHECK(*it == j["C"]);
it++;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j["B"]);
++it;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j["A"]);
++it;
CHECK(it != it_begin);
CHECK(it == it_end);
}
SECTION("json + crbegin/crend")
{
json::const_reverse_iterator it_begin = j.crbegin();
json::const_reverse_iterator it_end = j.crend();
auto it = it_begin;
CHECK(it != it_end);
CHECK(*it == j["C"]);
it++;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j["B"]);
++it;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j["A"]);
++it;
CHECK(it != it_begin);
CHECK(it == it_end);
}
SECTION("const json + crbegin/crend")
{
json::const_reverse_iterator it_begin = j_const.crbegin();
json::const_reverse_iterator it_end = j_const.crend();
auto it = it_begin;
CHECK(it != it_end);
CHECK(*it == j["C"]);
it++;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j["B"]);
++it;
CHECK(it != it_begin);
CHECK(it != it_end);
CHECK(*it == j["A"]);
++it;
CHECK(it != it_begin);
CHECK(it == it_end);
}
SECTION("key/value")
{
auto it = j.begin();
auto cit = j_const.cbegin();
CHECK(it.key() == "A");
CHECK(it.value() == json(1));
CHECK(cit.key() == "A");
CHECK(cit.value() == json(1));
}
}
SECTION("number (integer)")
{
json j = 23;
json j_const(j);
SECTION("json + begin/end")
{
json::iterator it = j.begin();
CHECK(it != j.end());
CHECK(*it == j);
it++;
CHECK(it != j.begin());
CHECK(it == j.end());
it--;
CHECK(it == j.begin());
CHECK(it != j.end());
CHECK(*it == j);
++it;
CHECK(it != j.begin());
CHECK(it == j.end());
--it;
CHECK(it == j.begin());
CHECK(it != j.end());
CHECK(*it == j);
}
SECTION("const json + begin/end")
{
json::const_iterator it = j_const.begin();
CHECK(it != j_const.end());
CHECK(*it == j_const);
it++;
CHECK(it != j_const.begin());
CHECK(it == j_const.end());
it--;
CHECK(it == j_const.begin());
CHECK(it != j_const.end());
CHECK(*it == j_const);
++it;
CHECK(it != j_const.begin());
CHECK(it == j_const.end());
--it;
CHECK(it == j_const.begin());
CHECK(it != j_const.end());
CHECK(*it == j_const);
}
SECTION("json + cbegin/cend")
{
json::const_iterator it = j.cbegin();
CHECK(it != j.cend());
CHECK(*it == j);
it++;
CHECK(it != j.cbegin());
CHECK(it == j.cend());
it--;
CHECK(it == j.cbegin());
CHECK(it != j.cend());
CHECK(*it == j);
++it;
CHECK(it != j.cbegin());
CHECK(it == j.cend());
--it;
CHECK(it == j.cbegin());
CHECK(it != j.cend());
CHECK(*it == j);
}
SECTION("const json + cbegin/cend")
{
json::const_iterator it = j_const.cbegin();
CHECK(it != j_const.cend());
CHECK(*it == j_const);
it++;
CHECK(it != j_const.cbegin());
CHECK(it == j_const.cend());
it--;
CHECK(it == j_const.cbegin());
CHECK(it != j_const.cend());
CHECK(*it == j_const);
++it;
CHECK(it != j_const.cbegin());
CHECK(it == j_const.cend());
--it;
CHECK(it == j_const.cbegin());
CHECK(it != j_const.cend());
CHECK(*it == j_const);
}
SECTION("json + rbegin/rend")
{
json::reverse_iterator it = j.rbegin();
CHECK(it != j.rend());
CHECK(*it == j);
it++;
CHECK(it != j.rbegin());
CHECK(it == j.rend());
it--;
CHECK(it == j.rbegin());
CHECK(it != j.rend());
CHECK(*it == j);
++it;
CHECK(it != j.rbegin());
CHECK(it == j.rend());
--it;
CHECK(it == j.rbegin());
CHECK(it != j.rend());
CHECK(*it == j);
}
SECTION("json + crbegin/crend")
{
json::const_reverse_iterator it = j.crbegin();
CHECK(it != j.crend());
CHECK(*it == j);
it++;
CHECK(it != j.crbegin());
CHECK(it == j.crend());
it--;
CHECK(it == j.crbegin());
CHECK(it != j.crend());
CHECK(*it == j);
++it;
CHECK(it != j.crbegin());
CHECK(it == j.crend());
--it;
CHECK(it == j.crbegin());
CHECK(it != j.crend());
CHECK(*it == j);
}
SECTION("const json + crbegin/crend")
{
json::const_reverse_iterator it = j_const.crbegin();
CHECK(it != j_const.crend());
CHECK(*it == j_const);
it++;
CHECK(it != j_const.crbegin());
CHECK(it == j_const.crend());
it--;
CHECK(it == j_const.crbegin());
CHECK(it != j_const.crend());
CHECK(*it == j_const);
++it;
CHECK(it != j_const.crbegin());
CHECK(it == j_const.crend());
--it;
CHECK(it == j_const.crbegin());
CHECK(it != j_const.crend());
CHECK(*it == j_const);
}
SECTION("key/value")
{
auto it = j.begin();
auto cit = j_const.cbegin();
CHECK_THROWS_AS(it.key(), std::domain_error);
CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators");
CHECK(it.value() == json(23));
CHECK_THROWS_AS(cit.key(), std::domain_error);
CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators");
CHECK(cit.value() == json(23));
auto rit = j.rend();
auto crit = j.crend();
CHECK_THROWS_AS(rit.key(), std::domain_error);
CHECK_THROWS_AS(rit.value(), std::out_of_range);
CHECK_THROWS_AS(crit.key(), std::domain_error);
CHECK_THROWS_AS(crit.value(), std::out_of_range);
CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators");
CHECK_THROWS_WITH(rit.value(), "cannot get value");
CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators");
CHECK_THROWS_WITH(crit.value(), "cannot get value");
}
}
SECTION("number (unsigned)")
{
json j = 23u;
json j_const(j);
SECTION("json + begin/end")
{
json::iterator it = j.begin();
CHECK(it != j.end());
CHECK(*it == j);
it++;
CHECK(it != j.begin());
CHECK(it == j.end());
it--;
CHECK(it == j.begin());
CHECK(it != j.end());
CHECK(*it == j);
++it;
CHECK(it != j.begin());
CHECK(it == j.end());
--it;
CHECK(it == j.begin());
CHECK(it != j.end());
CHECK(*it == j);
}
SECTION("const json + begin/end")
{
json::const_iterator it = j_const.begin();
CHECK(it != j_const.end());
CHECK(*it == j_const);
it++;
CHECK(it != j_const.begin());
CHECK(it == j_const.end());
it--;
CHECK(it == j_const.begin());
CHECK(it != j_const.end());
CHECK(*it == j_const);
++it;
CHECK(it != j_const.begin());
CHECK(it == j_const.end());
--it;
CHECK(it == j_const.begin());
CHECK(it != j_const.end());
CHECK(*it == j_const);
}
SECTION("json + cbegin/cend")
{
json::const_iterator it = j.cbegin();
CHECK(it != j.cend());
CHECK(*it == j);
it++;
CHECK(it != j.cbegin());
CHECK(it == j.cend());
it--;
CHECK(it == j.cbegin());
CHECK(it != j.cend());
CHECK(*it == j);
++it;
CHECK(it != j.cbegin());
CHECK(it == j.cend());
--it;
CHECK(it == j.cbegin());
CHECK(it != j.cend());
CHECK(*it == j);
}
SECTION("const json + cbegin/cend")
{
json::const_iterator it = j_const.cbegin();
CHECK(it != j_const.cend());
CHECK(*it == j_const);
it++;
CHECK(it != j_const.cbegin());
CHECK(it == j_const.cend());
it--;
CHECK(it == j_const.cbegin());
CHECK(it != j_const.cend());
CHECK(*it == j_const);
++it;
CHECK(it != j_const.cbegin());
CHECK(it == j_const.cend());
--it;
CHECK(it == j_const.cbegin());
CHECK(it != j_const.cend());
CHECK(*it == j_const);
}
SECTION("json + rbegin/rend")
{
json::reverse_iterator it = j.rbegin();
CHECK(it != j.rend());
CHECK(*it == j);
it++;
CHECK(it != j.rbegin());
CHECK(it == j.rend());
it--;
CHECK(it == j.rbegin());
CHECK(it != j.rend());
CHECK(*it == j);
++it;
CHECK(it != j.rbegin());
CHECK(it == j.rend());
--it;
CHECK(it == j.rbegin());
CHECK(it != j.rend());
CHECK(*it == j);
}
SECTION("json + crbegin/crend")
{
json::const_reverse_iterator it = j.crbegin();
CHECK(it != j.crend());
CHECK(*it == j);
it++;
CHECK(it != j.crbegin());
CHECK(it == j.crend());
it--;
CHECK(it == j.crbegin());
CHECK(it != j.crend());
CHECK(*it == j);
++it;
CHECK(it != j.crbegin());
CHECK(it == j.crend());
--it;
CHECK(it == j.crbegin());
CHECK(it != j.crend());
CHECK(*it == j);
}
SECTION("const json + crbegin/crend")
{
json::const_reverse_iterator it = j_const.crbegin();
CHECK(it != j_const.crend());
CHECK(*it == j_const);
it++;
CHECK(it != j_const.crbegin());
CHECK(it == j_const.crend());
it--;
CHECK(it == j_const.crbegin());
CHECK(it != j_const.crend());
CHECK(*it == j_const);
++it;
CHECK(it != j_const.crbegin());
CHECK(it == j_const.crend());
--it;
CHECK(it == j_const.crbegin());
CHECK(it != j_const.crend());
CHECK(*it == j_const);
}
SECTION("key/value")
{
auto it = j.begin();
auto cit = j_const.cbegin();
CHECK_THROWS_AS(it.key(), std::domain_error);
CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators");
CHECK(it.value() == json(23));
CHECK_THROWS_AS(cit.key(), std::domain_error);
CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators");
CHECK(cit.value() == json(23));
auto rit = j.rend();
auto crit = j.crend();
CHECK_THROWS_AS(rit.key(), std::domain_error);
CHECK_THROWS_AS(rit.value(), std::out_of_range);
CHECK_THROWS_AS(crit.key(), std::domain_error);
CHECK_THROWS_AS(crit.value(), std::out_of_range);
CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators");
CHECK_THROWS_WITH(rit.value(), "cannot get value");
CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators");
CHECK_THROWS_WITH(crit.value(), "cannot get value");
}
}
SECTION("number (float)")
{
json j = 23.42;
json j_const(j);
SECTION("json + begin/end")
{
json::iterator it = j.begin();
CHECK(it != j.end());
CHECK(*it == j);
it++;
CHECK(it != j.begin());
CHECK(it == j.end());
it--;
CHECK(it == j.begin());
CHECK(it != j.end());
CHECK(*it == j);
++it;
CHECK(it != j.begin());
CHECK(it == j.end());
--it;
CHECK(it == j.begin());
CHECK(it != j.end());
CHECK(*it == j);
}
SECTION("const json + begin/end")
{
json::const_iterator it = j_const.begin();
CHECK(it != j_const.end());
CHECK(*it == j_const);
it++;
CHECK(it != j_const.begin());
CHECK(it == j_const.end());
it--;
CHECK(it == j_const.begin());
CHECK(it != j_const.end());
CHECK(*it == j_const);
++it;
CHECK(it != j_const.begin());
CHECK(it == j_const.end());
--it;
CHECK(it == j_const.begin());
CHECK(it != j_const.end());
CHECK(*it == j_const);
}
SECTION("json + cbegin/cend")
{
json::const_iterator it = j.cbegin();
CHECK(it != j.cend());
CHECK(*it == j);
it++;
CHECK(it != j.cbegin());
CHECK(it == j.cend());
it--;
CHECK(it == j.cbegin());
CHECK(it != j.cend());
CHECK(*it == j);
++it;
CHECK(it != j.cbegin());
CHECK(it == j.cend());
--it;
CHECK(it == j.cbegin());
CHECK(it != j.cend());
CHECK(*it == j);
}
SECTION("const json + cbegin/cend")
{
json::const_iterator it = j_const.cbegin();
CHECK(it != j_const.cend());
CHECK(*it == j_const);
it++;
CHECK(it != j_const.cbegin());
CHECK(it == j_const.cend());
it--;
CHECK(it == j_const.cbegin());
CHECK(it != j_const.cend());
CHECK(*it == j_const);
++it;
CHECK(it != j_const.cbegin());
CHECK(it == j_const.cend());
--it;
CHECK(it == j_const.cbegin());
CHECK(it != j_const.cend());
CHECK(*it == j_const);
}
SECTION("json + rbegin/rend")
{
json::reverse_iterator it = j.rbegin();
CHECK(it != j.rend());
CHECK(*it == j);
it++;
CHECK(it != j.rbegin());
CHECK(it == j.rend());
it--;
CHECK(it == j.rbegin());
CHECK(it != j.rend());
CHECK(*it == j);
++it;
CHECK(it != j.rbegin());
CHECK(it == j.rend());
--it;
CHECK(it == j.rbegin());
CHECK(it != j.rend());
CHECK(*it == j);
}
SECTION("json + crbegin/crend")
{
json::const_reverse_iterator it = j.crbegin();
CHECK(it != j.crend());
CHECK(*it == j);
it++;
CHECK(it != j.crbegin());
CHECK(it == j.crend());
it--;
CHECK(it == j.crbegin());
CHECK(it != j.crend());
CHECK(*it == j);
++it;
CHECK(it != j.crbegin());
CHECK(it == j.crend());
--it;
CHECK(it == j.crbegin());
CHECK(it != j.crend());
CHECK(*it == j);
}
SECTION("const json + crbegin/crend")
{
json::const_reverse_iterator it = j_const.crbegin();
CHECK(it != j_const.crend());
CHECK(*it == j_const);
it++;
CHECK(it != j_const.crbegin());
CHECK(it == j_const.crend());
it--;
CHECK(it == j_const.crbegin());
CHECK(it != j_const.crend());
CHECK(*it == j_const);
++it;
CHECK(it != j_const.crbegin());
CHECK(it == j_const.crend());
--it;
CHECK(it == j_const.crbegin());
CHECK(it != j_const.crend());
CHECK(*it == j_const);
}
SECTION("key/value")
{
auto it = j.begin();
auto cit = j_const.cbegin();
CHECK_THROWS_AS(it.key(), std::domain_error);
CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators");
CHECK(it.value() == json(23.42));
CHECK_THROWS_AS(cit.key(), std::domain_error);
CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators");
CHECK(cit.value() == json(23.42));
auto rit = j.rend();
auto crit = j.crend();
CHECK_THROWS_AS(rit.key(), std::domain_error);
CHECK_THROWS_AS(rit.value(), std::out_of_range);
CHECK_THROWS_AS(crit.key(), std::domain_error);
CHECK_THROWS_AS(crit.value(), std::out_of_range);
CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators");
CHECK_THROWS_WITH(rit.value(), "cannot get value");
CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators");
CHECK_THROWS_WITH(crit.value(), "cannot get value");
}
}
SECTION("null")
{
json j = nullptr;
json j_const(j);
SECTION("json + begin/end")
{
json::iterator it = j.begin();
CHECK(it == j.end());
}
SECTION("const json + begin/end")
{
json::const_iterator it_begin = j_const.begin();
json::const_iterator it_end = j_const.end();
CHECK(it_begin == it_end);
}
SECTION("json + cbegin/cend")
{
json::const_iterator it_begin = j.cbegin();
json::const_iterator it_end = j.cend();
CHECK(it_begin == it_end);
}
SECTION("const json + cbegin/cend")
{
json::const_iterator it_begin = j_const.cbegin();
json::const_iterator it_end = j_const.cend();
CHECK(it_begin == it_end);
}
SECTION("json + rbegin/rend")
{
json::reverse_iterator it = j.rbegin();
CHECK(it == j.rend());
}
SECTION("json + crbegin/crend")
{
json::const_reverse_iterator it = j.crbegin();
CHECK(it == j.crend());
}
SECTION("const json + crbegin/crend")
{
json::const_reverse_iterator it = j_const.crbegin();
CHECK(it == j_const.crend());
}
SECTION("key/value")
{
auto it = j.begin();
auto cit = j_const.cbegin();
CHECK_THROWS_AS(it.key(), std::domain_error);
CHECK_THROWS_AS(it.value(), std::out_of_range);
CHECK_THROWS_AS(cit.key(), std::domain_error);
CHECK_THROWS_AS(cit.value(), std::out_of_range);
CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators");
CHECK_THROWS_WITH(it.value(), "cannot get value");
CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators");
CHECK_THROWS_WITH(cit.value(), "cannot get value");
auto rit = j.rend();
auto crit = j.crend();
CHECK_THROWS_AS(rit.key(), std::domain_error);
CHECK_THROWS_AS(rit.value(), std::out_of_range);
CHECK_THROWS_AS(crit.key(), std::domain_error);
CHECK_THROWS_AS(crit.value(), std::out_of_range);
CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators");
CHECK_THROWS_WITH(rit.value(), "cannot get value");
CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators");
CHECK_THROWS_WITH(crit.value(), "cannot get value");
}
}
}
SECTION("iterator comparisons")
{
json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"};
for (json& j : j_values)
{
auto it1 = j.begin();
auto it2 = j.begin();
auto it3 = j.begin();
++it2;
++it3;
++it3;
auto it1_c = j.cbegin();
auto it2_c = j.cbegin();
auto it3_c = j.cbegin();
++it2_c;
++it3_c;
++it3_c;
// comparison: equal
{
CHECK(it1 == it1);
CHECK(not (it1 == it2));
CHECK(not (it1 == it3));
CHECK(not (it2 == it3));
CHECK(it1_c == it1_c);
CHECK(not (it1_c == it2_c));
CHECK(not (it1_c == it3_c));
CHECK(not (it2_c == it3_c));
}
// comparison: not equal
{
// check definition
CHECK( (it1 != it1) == not(it1 == it1) );
CHECK( (it1 != it2) == not(it1 == it2) );
CHECK( (it1 != it3) == not(it1 == it3) );
CHECK( (it2 != it3) == not(it2 == it3) );
CHECK( (it1_c != it1_c) == not(it1_c == it1_c) );
CHECK( (it1_c != it2_c) == not(it1_c == it2_c) );
CHECK( (it1_c != it3_c) == not(it1_c == it3_c) );
CHECK( (it2_c != it3_c) == not(it2_c == it3_c) );
}
// comparison: smaller
{
if (j.type() == json::value_t::object)
{
CHECK_THROWS_AS(it1 < it1, std::domain_error);
CHECK_THROWS_AS(it1 < it2, std::domain_error);
CHECK_THROWS_AS(it2 < it3, std::domain_error);
CHECK_THROWS_AS(it1 < it3, std::domain_error);
CHECK_THROWS_AS(it1_c < it1_c, std::domain_error);
CHECK_THROWS_AS(it1_c < it2_c, std::domain_error);
CHECK_THROWS_AS(it2_c < it3_c, std::domain_error);
CHECK_THROWS_AS(it1_c < it3_c, std::domain_error);
CHECK_THROWS_WITH(it1 < it1, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 < it2, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 < it3, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 < it3, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c < it1_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c < it2_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c < it3_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c < it3_c, "cannot compare order of object iterators");
}
else
{
CHECK(not (it1 < it1));
CHECK(it1 < it2);
CHECK(it1 < it3);
CHECK(it2 < it3);
CHECK(not (it1_c < it1_c));
CHECK(it1_c < it2_c);
CHECK(it1_c < it3_c);
CHECK(it2_c < it3_c);
}
}
// comparison: less than or equal
{
if (j.type() == json::value_t::object)
{
CHECK_THROWS_AS(it1 <= it1, std::domain_error);
CHECK_THROWS_AS(it1 <= it2, std::domain_error);
CHECK_THROWS_AS(it2 <= it3, std::domain_error);
CHECK_THROWS_AS(it1 <= it3, std::domain_error);
CHECK_THROWS_AS(it1_c <= it1_c, std::domain_error);
CHECK_THROWS_AS(it1_c <= it2_c, std::domain_error);
CHECK_THROWS_AS(it2_c <= it3_c, std::domain_error);
CHECK_THROWS_AS(it1_c <= it3_c, std::domain_error);
CHECK_THROWS_WITH(it1 <= it1, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 <= it2, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 <= it3, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 <= it3, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c <= it1_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c <= it2_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c <= it3_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c <= it3_c, "cannot compare order of object iterators");
}
else
{
// check definition
CHECK( (it1 <= it1) == not(it1 < it1) );
CHECK( (it1 <= it2) == not(it2 < it1) );
CHECK( (it1 <= it3) == not(it3 < it1) );
CHECK( (it2 <= it3) == not(it3 < it2) );
CHECK( (it1_c <= it1_c) == not(it1_c < it1_c) );
CHECK( (it1_c <= it2_c) == not(it2_c < it1_c) );
CHECK( (it1_c <= it3_c) == not(it3_c < it1_c) );
CHECK( (it2_c <= it3_c) == not(it3_c < it2_c) );
}
}
// comparison: greater than
{
if (j.type() == json::value_t::object)
{
CHECK_THROWS_AS(it1 > it1, std::domain_error);
CHECK_THROWS_AS(it1 > it2, std::domain_error);
CHECK_THROWS_AS(it2 > it3, std::domain_error);
CHECK_THROWS_AS(it1 > it3, std::domain_error);
CHECK_THROWS_AS(it1_c > it1_c, std::domain_error);
CHECK_THROWS_AS(it1_c > it2_c, std::domain_error);
CHECK_THROWS_AS(it2_c > it3_c, std::domain_error);
CHECK_THROWS_AS(it1_c > it3_c, std::domain_error);
CHECK_THROWS_WITH(it1 > it1, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 > it2, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 > it3, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 > it3, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c > it1_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c > it2_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c > it3_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c > it3_c, "cannot compare order of object iterators");
}
else
{
// check definition
CHECK( (it1 > it1) == (it1 < it1) );
CHECK( (it1 > it2) == (it2 < it1) );
CHECK( (it1 > it3) == (it3 < it1) );
CHECK( (it2 > it3) == (it3 < it2) );
CHECK( (it1_c > it1_c) == (it1_c < it1_c) );
CHECK( (it1_c > it2_c) == (it2_c < it1_c) );
CHECK( (it1_c > it3_c) == (it3_c < it1_c) );
CHECK( (it2_c > it3_c) == (it3_c < it2_c) );
}
}
// comparison: greater than or equal
{
if (j.type() == json::value_t::object)
{
CHECK_THROWS_AS(it1 >= it1, std::domain_error);
CHECK_THROWS_AS(it1 >= it2, std::domain_error);
CHECK_THROWS_AS(it2 >= it3, std::domain_error);
CHECK_THROWS_AS(it1 >= it3, std::domain_error);
CHECK_THROWS_AS(it1_c >= it1_c, std::domain_error);
CHECK_THROWS_AS(it1_c >= it2_c, std::domain_error);
CHECK_THROWS_AS(it2_c >= it3_c, std::domain_error);
CHECK_THROWS_AS(it1_c >= it3_c, std::domain_error);
CHECK_THROWS_WITH(it1 >= it1, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 >= it2, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 >= it3, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 >= it3, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c >= it1_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c >= it2_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c >= it3_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c >= it3_c, "cannot compare order of object iterators");
}
else
{
// check definition
CHECK( (it1 >= it1) == not(it1 < it1) );
CHECK( (it1 >= it2) == not(it1 < it2) );
CHECK( (it1 >= it3) == not(it1 < it3) );
CHECK( (it2 >= it3) == not(it2 < it3) );
CHECK( (it1_c >= it1_c) == not(it1_c < it1_c) );
CHECK( (it1_c >= it2_c) == not(it1_c < it2_c) );
CHECK( (it1_c >= it3_c) == not(it1_c < it3_c) );
CHECK( (it2_c >= it3_c) == not(it2_c < it3_c) );
}
}
}
// check exceptions if different objects are compared
for (auto j : j_values)
{
for (auto k : j_values)
{
if (j != k)
{
CHECK_THROWS_AS(j.begin() == k.begin(), std::domain_error);
CHECK_THROWS_AS(j.cbegin() == k.cbegin(), std::domain_error);
CHECK_THROWS_WITH(j.begin() == k.begin(), "cannot compare iterators of different containers");
CHECK_THROWS_WITH(j.cbegin() == k.cbegin(), "cannot compare iterators of different containers");
CHECK_THROWS_AS(j.begin() < k.begin(), std::domain_error);
CHECK_THROWS_AS(j.cbegin() < k.cbegin(), std::domain_error);
CHECK_THROWS_WITH(j.begin() < k.begin(), "cannot compare iterators of different containers");
CHECK_THROWS_WITH(j.cbegin() < k.cbegin(), "cannot compare iterators of different containers");
}
}
}
}
SECTION("iterator arithmetic")
{
json j_object = {{"one", 1}, {"two", 2}, {"three", 3}};
json j_array = {1, 2, 3, 4, 5, 6};
json j_null = nullptr;
json j_value = 42;
SECTION("addition and subtraction")
{
SECTION("object")
{
{
auto it = j_object.begin();
CHECK_THROWS_AS(it += 1, std::domain_error);
CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators");
}
{
auto it = j_object.cbegin();
CHECK_THROWS_AS(it += 1, std::domain_error);
CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators");
}
{
auto it = j_object.begin();
CHECK_THROWS_AS(it + 1, std::domain_error);
CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators");
}
{
auto it = j_object.cbegin();
CHECK_THROWS_AS(it + 1, std::domain_error);
CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators");
}
{
auto it = j_object.begin();
CHECK_THROWS_AS(it -= 1, std::domain_error);
CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators");
}
{
auto it = j_object.cbegin();
CHECK_THROWS_AS(it -= 1, std::domain_error);
CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators");
}
{
auto it = j_object.begin();
CHECK_THROWS_AS(it - 1, std::domain_error);
CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators");
}
{
auto it = j_object.cbegin();
CHECK_THROWS_AS(it - 1, std::domain_error);
CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators");
}
{
auto it = j_object.begin();
CHECK_THROWS_AS(it - it, std::domain_error);
CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators");
}
{
auto it = j_object.cbegin();
CHECK_THROWS_AS(it - it, std::domain_error);
CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators");
}
}
SECTION("array")
{
{
auto it = j_array.begin();
it += 3;
CHECK((j_array.begin() + 3) == it);
CHECK((it - 3) == j_array.begin());
CHECK((it - j_array.begin()) == 3);
CHECK(*it == json(4));
it -= 2;
CHECK(*it == json(2));
}
{
auto it = j_array.cbegin();
it += 3;
CHECK((j_array.cbegin() + 3) == it);
CHECK((it - 3) == j_array.cbegin());
CHECK((it - j_array.cbegin()) == 3);
CHECK(*it == json(4));
it -= 2;
CHECK(*it == json(2));
}
}
SECTION("null")
{
{
auto it = j_null.begin();
it += 3;
CHECK((j_null.begin() + 3) == it);
CHECK((it - 3) == j_null.begin());
CHECK((it - j_null.begin()) == 3);
CHECK(it != j_null.end());
it -= 3;
CHECK(it == j_null.end());
}
{
auto it = j_null.cbegin();
it += 3;
CHECK((j_null.cbegin() + 3) == it);
CHECK((it - 3) == j_null.cbegin());
CHECK((it - j_null.cbegin()) == 3);
CHECK(it != j_null.cend());
it -= 3;
CHECK(it == j_null.cend());
}
}
SECTION("value")
{
{
auto it = j_value.begin();
it += 3;
CHECK((j_value.begin() + 3) == it);
CHECK((it - 3) == j_value.begin());
CHECK((it - j_value.begin()) == 3);
CHECK(it != j_value.end());
it -= 3;
CHECK(*it == json(42));
}
{
auto it = j_value.cbegin();
it += 3;
CHECK((j_value.cbegin() + 3) == it);
CHECK((it - 3) == j_value.cbegin());
CHECK((it - j_value.cbegin()) == 3);
CHECK(it != j_value.cend());
it -= 3;
CHECK(*it == json(42));
}
}
}
SECTION("subscript operator")
{
SECTION("object")
{
{
auto it = j_object.begin();
CHECK_THROWS_AS(it[0], std::domain_error);
CHECK_THROWS_AS(it[1], std::domain_error);
CHECK_THROWS_WITH(it[0], "cannot use operator[] for object iterators");
CHECK_THROWS_WITH(it[1], "cannot use operator[] for object iterators");
}
{
auto it = j_object.cbegin();
CHECK_THROWS_AS(it[0], std::domain_error);
CHECK_THROWS_AS(it[1], std::domain_error);
CHECK_THROWS_WITH(it[0], "cannot use operator[] for object iterators");
CHECK_THROWS_WITH(it[1], "cannot use operator[] for object iterators");
}
}
SECTION("array")
{
{
auto it = j_array.begin();
CHECK(it[0] == json(1));
CHECK(it[1] == json(2));
CHECK(it[2] == json(3));
CHECK(it[3] == json(4));
CHECK(it[4] == json(5));
CHECK(it[5] == json(6));
}
{
auto it = j_array.cbegin();
CHECK(it[0] == json(1));
CHECK(it[1] == json(2));
CHECK(it[2] == json(3));
CHECK(it[3] == json(4));
CHECK(it[4] == json(5));
CHECK(it[5] == json(6));
}
}
SECTION("null")
{
{
auto it = j_null.begin();
CHECK_THROWS_AS(it[0], std::out_of_range);
CHECK_THROWS_AS(it[1], std::out_of_range);
CHECK_THROWS_WITH(it[0], "cannot get value");
CHECK_THROWS_WITH(it[1], "cannot get value");
}
{
auto it = j_null.cbegin();
CHECK_THROWS_AS(it[0], std::out_of_range);
CHECK_THROWS_AS(it[1], std::out_of_range);
CHECK_THROWS_WITH(it[0], "cannot get value");
CHECK_THROWS_WITH(it[1], "cannot get value");
}
}
SECTION("value")
{
{
auto it = j_value.begin();
CHECK(it[0] == json(42));
CHECK_THROWS_AS(it[1], std::out_of_range);
CHECK_THROWS_WITH(it[1], "cannot get value");
}
{
auto it = j_value.cbegin();
CHECK(it[0] == json(42));
CHECK_THROWS_AS(it[1], std::out_of_range);
CHECK_THROWS_WITH(it[1], "cannot get value");
}
}
}
}
SECTION("reverse iterator comparisons")
{
json j_values = {nullptr, true, 42, 42u, 23.23, {{"one", 1}, {"two", 2}}, {1, 2, 3, 4, 5}, "Hello, world"};
for (json& j : j_values)
{
auto it1 = j.rbegin();
auto it2 = j.rbegin();
auto it3 = j.rbegin();
++it2;
++it3;
++it3;
auto it1_c = j.crbegin();
auto it2_c = j.crbegin();
auto it3_c = j.crbegin();
++it2_c;
++it3_c;
++it3_c;
// comparison: equal
{
CHECK(it1 == it1);
CHECK(not (it1 == it2));
CHECK(not (it1 == it3));
CHECK(not (it2 == it3));
CHECK(it1_c == it1_c);
CHECK(not (it1_c == it2_c));
CHECK(not (it1_c == it3_c));
CHECK(not (it2_c == it3_c));
}
// comparison: not equal
{
// check definition
CHECK( (it1 != it1) == not(it1 == it1) );
CHECK( (it1 != it2) == not(it1 == it2) );
CHECK( (it1 != it3) == not(it1 == it3) );
CHECK( (it2 != it3) == not(it2 == it3) );
CHECK( (it1_c != it1_c) == not(it1_c == it1_c) );
CHECK( (it1_c != it2_c) == not(it1_c == it2_c) );
CHECK( (it1_c != it3_c) == not(it1_c == it3_c) );
CHECK( (it2_c != it3_c) == not(it2_c == it3_c) );
}
// comparison: smaller
{
if (j.type() == json::value_t::object)
{
CHECK_THROWS_AS(it1 < it1, std::domain_error);
CHECK_THROWS_AS(it1 < it2, std::domain_error);
CHECK_THROWS_AS(it2 < it3, std::domain_error);
CHECK_THROWS_AS(it1 < it3, std::domain_error);
CHECK_THROWS_AS(it1_c < it1_c, std::domain_error);
CHECK_THROWS_AS(it1_c < it2_c, std::domain_error);
CHECK_THROWS_AS(it2_c < it3_c, std::domain_error);
CHECK_THROWS_AS(it1_c < it3_c, std::domain_error);
CHECK_THROWS_WITH(it1 < it1, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 < it2, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 < it3, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 < it3, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c < it1_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c < it2_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c < it3_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c < it3_c, "cannot compare order of object iterators");
}
else
{
CHECK(not (it1 < it1));
CHECK(it1 < it2);
CHECK(it1 < it3);
CHECK(it2 < it3);
CHECK(not (it1_c < it1_c));
CHECK(it1_c < it2_c);
CHECK(it1_c < it3_c);
CHECK(it2_c < it3_c);
}
}
// comparison: less than or equal
{
if (j.type() == json::value_t::object)
{
CHECK_THROWS_AS(it1 <= it1, std::domain_error);
CHECK_THROWS_AS(it1 <= it2, std::domain_error);
CHECK_THROWS_AS(it2 <= it3, std::domain_error);
CHECK_THROWS_AS(it1 <= it3, std::domain_error);
CHECK_THROWS_AS(it1_c <= it1_c, std::domain_error);
CHECK_THROWS_AS(it1_c <= it2_c, std::domain_error);
CHECK_THROWS_AS(it2_c <= it3_c, std::domain_error);
CHECK_THROWS_AS(it1_c <= it3_c, std::domain_error);
CHECK_THROWS_WITH(it1 <= it1, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 <= it2, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 <= it3, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 <= it3, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c <= it1_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c <= it2_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c <= it3_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c <= it3_c, "cannot compare order of object iterators");
}
else
{
// check definition
CHECK( (it1 <= it1) == not(it1 < it1) );
CHECK( (it1 <= it2) == not(it2 < it1) );
CHECK( (it1 <= it3) == not(it3 < it1) );
CHECK( (it2 <= it3) == not(it3 < it2) );
CHECK( (it1_c <= it1_c) == not(it1_c < it1_c) );
CHECK( (it1_c <= it2_c) == not(it2_c < it1_c) );
CHECK( (it1_c <= it3_c) == not(it3_c < it1_c) );
CHECK( (it2_c <= it3_c) == not(it3_c < it2_c) );
}
}
// comparison: greater than
{
if (j.type() == json::value_t::object)
{
CHECK_THROWS_AS(it1 > it1, std::domain_error);
CHECK_THROWS_AS(it1 > it2, std::domain_error);
CHECK_THROWS_AS(it2 > it3, std::domain_error);
CHECK_THROWS_AS(it1 > it3, std::domain_error);
CHECK_THROWS_AS(it1_c > it1_c, std::domain_error);
CHECK_THROWS_AS(it1_c > it2_c, std::domain_error);
CHECK_THROWS_AS(it2_c > it3_c, std::domain_error);
CHECK_THROWS_AS(it1_c > it3_c, std::domain_error);
CHECK_THROWS_WITH(it1 > it1, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 > it2, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 > it3, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 > it3, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c > it1_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c > it2_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c > it3_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c > it3_c, "cannot compare order of object iterators");
}
else
{
// check definition
CHECK( (it1 > it1) == (it1 < it1) );
CHECK( (it1 > it2) == (it2 < it1) );
CHECK( (it1 > it3) == (it3 < it1) );
CHECK( (it2 > it3) == (it3 < it2) );
CHECK( (it1_c > it1_c) == (it1_c < it1_c) );
CHECK( (it1_c > it2_c) == (it2_c < it1_c) );
CHECK( (it1_c > it3_c) == (it3_c < it1_c) );
CHECK( (it2_c > it3_c) == (it3_c < it2_c) );
}
}
// comparison: greater than or equal
{
if (j.type() == json::value_t::object)
{
CHECK_THROWS_AS(it1 >= it1, std::domain_error);
CHECK_THROWS_AS(it1 >= it2, std::domain_error);
CHECK_THROWS_AS(it2 >= it3, std::domain_error);
CHECK_THROWS_AS(it1 >= it3, std::domain_error);
CHECK_THROWS_AS(it1_c >= it1_c, std::domain_error);
CHECK_THROWS_AS(it1_c >= it2_c, std::domain_error);
CHECK_THROWS_AS(it2_c >= it3_c, std::domain_error);
CHECK_THROWS_AS(it1_c >= it3_c, std::domain_error);
CHECK_THROWS_WITH(it1 >= it1, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 >= it2, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 >= it3, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 >= it3, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c >= it1_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c >= it2_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c >= it3_c, "cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c >= it3_c, "cannot compare order of object iterators");
}
else
{
// check definition
CHECK( (it1 >= it1) == not(it1 < it1) );
CHECK( (it1 >= it2) == not(it1 < it2) );
CHECK( (it1 >= it3) == not(it1 < it3) );
CHECK( (it2 >= it3) == not(it2 < it3) );
CHECK( (it1_c >= it1_c) == not(it1_c < it1_c) );
CHECK( (it1_c >= it2_c) == not(it1_c < it2_c) );
CHECK( (it1_c >= it3_c) == not(it1_c < it3_c) );
CHECK( (it2_c >= it3_c) == not(it2_c < it3_c) );
}
}
}
// check exceptions if different objects are compared
for (auto j : j_values)
{
for (auto k : j_values)
{
if (j != k)
{
CHECK_THROWS_AS(j.rbegin() == k.rbegin(), std::domain_error);
CHECK_THROWS_AS(j.crbegin() == k.crbegin(), std::domain_error);
CHECK_THROWS_WITH(j.rbegin() == k.rbegin(), "cannot compare iterators of different containers");
CHECK_THROWS_WITH(j.crbegin() == k.crbegin(), "cannot compare iterators of different containers");
CHECK_THROWS_AS(j.rbegin() < k.rbegin(), std::domain_error);
CHECK_THROWS_AS(j.crbegin() < k.crbegin(), std::domain_error);
CHECK_THROWS_WITH(j.rbegin() < k.rbegin(), "cannot compare iterators of different containers");
CHECK_THROWS_WITH(j.crbegin() < k.crbegin(), "cannot compare iterators of different containers");
}
}
}
}
SECTION("reverse iterator arithmetic")
{
json j_object = {{"one", 1}, {"two", 2}, {"three", 3}};
json j_array = {1, 2, 3, 4, 5, 6};
json j_null = nullptr;
json j_value = 42;
SECTION("addition and subtraction")
{
SECTION("object")
{
{
auto it = j_object.rbegin();
CHECK_THROWS_AS(it += 1, std::domain_error);
CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators");
}
{
auto it = j_object.crbegin();
CHECK_THROWS_AS(it += 1, std::domain_error);
CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators");
}
{
auto it = j_object.rbegin();
CHECK_THROWS_AS(it + 1, std::domain_error);
CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators");
}
{
auto it = j_object.crbegin();
CHECK_THROWS_AS(it + 1, std::domain_error);
CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators");
}
{
auto it = j_object.rbegin();
CHECK_THROWS_AS(it -= 1, std::domain_error);
CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators");
}
{
auto it = j_object.crbegin();
CHECK_THROWS_AS(it -= 1, std::domain_error);
CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators");
}
{
auto it = j_object.rbegin();
CHECK_THROWS_AS(it - 1, std::domain_error);
CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators");
}
{
auto it = j_object.crbegin();
CHECK_THROWS_AS(it - 1, std::domain_error);
CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators");
}
{
auto it = j_object.rbegin();
CHECK_THROWS_AS(it - it, std::domain_error);
CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators");
}
{
auto it = j_object.crbegin();
CHECK_THROWS_AS(it - it, std::domain_error);
CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators");
}
}
SECTION("array")
{
{
auto it = j_array.rbegin();
it += 3;
CHECK((j_array.rbegin() + 3) == it);
CHECK((it - 3) == j_array.rbegin());
CHECK((j_array.rbegin() - it) == 3);
CHECK(*it == json(3));
it -= 2;
CHECK(*it == json(5));
}
{
auto it = j_array.crbegin();
it += 3;
CHECK((j_array.crbegin() + 3) == it);
CHECK((it - 3) == j_array.crbegin());
CHECK((j_array.crbegin() - it) == 3);
CHECK(*it == json(3));
it -= 2;
CHECK(*it == json(5));
}
}
SECTION("null")
{
{
auto it = j_null.rbegin();
it += 3;
CHECK((j_null.rbegin() + 3) == it);
CHECK((it - 3) == j_null.rbegin());
CHECK((j_null.rbegin() - it) == 3);
CHECK(it != j_null.rend());
it -= 3;
CHECK(it == j_null.rend());
}
{
auto it = j_null.crbegin();
it += 3;
CHECK((j_null.crbegin() + 3) == it);
CHECK((it - 3) == j_null.crbegin());
CHECK((j_null.crbegin() - it) == 3);
CHECK(it != j_null.crend());
it -= 3;
CHECK(it == j_null.crend());
}
}
SECTION("value")
{
{
auto it = j_value.rbegin();
it += 3;
CHECK((j_value.rbegin() + 3) == it);
CHECK((it - 3) == j_value.rbegin());
CHECK((j_value.rbegin() - it) == 3);
CHECK(it != j_value.rend());
it -= 3;
CHECK(*it == json(42));
}
{
auto it = j_value.crbegin();
it += 3;
CHECK((j_value.crbegin() + 3) == it);
CHECK((it - 3) == j_value.crbegin());
CHECK((j_value.crbegin() - it) == 3);
CHECK(it != j_value.crend());
it -= 3;
CHECK(*it == json(42));
}
}
}
SECTION("subscript operator")
{
SECTION("object")
{
{
auto it = j_object.rbegin();
CHECK_THROWS_AS(it[0], std::domain_error);
CHECK_THROWS_AS(it[1], std::domain_error);
CHECK_THROWS_WITH(it[0], "cannot use offsets with object iterators");
CHECK_THROWS_WITH(it[1], "cannot use offsets with object iterators");
}
{
auto it = j_object.crbegin();
CHECK_THROWS_AS(it[0], std::domain_error);
CHECK_THROWS_AS(it[1], std::domain_error);
CHECK_THROWS_WITH(it[0], "cannot use offsets with object iterators");
CHECK_THROWS_WITH(it[1], "cannot use offsets with object iterators");
}
}
SECTION("array")
{
{
auto it = j_array.rbegin();
CHECK(it[0] == json(6));
CHECK(it[1] == json(5));
CHECK(it[2] == json(4));
CHECK(it[3] == json(3));
CHECK(it[4] == json(2));
CHECK(it[5] == json(1));
}
{
auto it = j_array.crbegin();
CHECK(it[0] == json(6));
CHECK(it[1] == json(5));
CHECK(it[2] == json(4));
CHECK(it[3] == json(3));
CHECK(it[4] == json(2));
CHECK(it[5] == json(1));
}
}
SECTION("null")
{
{
auto it = j_null.rbegin();
CHECK_THROWS_AS(it[0], std::out_of_range);
CHECK_THROWS_AS(it[1], std::out_of_range);
CHECK_THROWS_WITH(it[0], "cannot get value");
CHECK_THROWS_WITH(it[1], "cannot get value");
}
{
auto it = j_null.crbegin();
CHECK_THROWS_AS(it[0], std::out_of_range);
CHECK_THROWS_AS(it[1], std::out_of_range);
CHECK_THROWS_WITH(it[0], "cannot get value");
CHECK_THROWS_WITH(it[1], "cannot get value");
}
}
SECTION("value")
{
{
auto it = j_value.rbegin();
CHECK(it[0] == json(42));
CHECK_THROWS_AS(it[1], std::out_of_range);
CHECK_THROWS_WITH(it[1], "cannot get value");
}
{
auto it = j_value.crbegin();
CHECK(it[0] == json(42));
CHECK_THROWS_AS(it[1], std::out_of_range);
CHECK_THROWS_WITH(it[1], "cannot get value");
}
}
}
}
}
TEST_CASE("capacity")
{
SECTION("empty()")
{
SECTION("boolean")
{
json j = true;
json j_const(j);
SECTION("result of empty")
{
CHECK(j.empty() == false);
CHECK(j_const.empty() == false);
}
SECTION("definition of empty")
{
CHECK(j.begin() != j.end());
CHECK(j_const.begin() != j_const.end());
}
}
SECTION("string")
{
json j = "hello world";
json j_const(j);
SECTION("result of empty")
{
CHECK(j.empty() == false);
CHECK(j_const.empty() == false);
}
SECTION("definition of empty")
{
CHECK(j.begin() != j.end());
CHECK(j_const.begin() != j_const.end());
}
}
SECTION("array")
{
SECTION("empty array")
{
json j = json::array();
json j_const(j);
SECTION("result of empty")
{
CHECK(j.empty() == true);
CHECK(j_const.empty() == true);
}
SECTION("definition of empty")
{
CHECK(j.begin() == j.end());
CHECK(j_const.begin() == j_const.end());
}
}
SECTION("filled array")
{
json j = {1, 2, 3};
json j_const(j);
SECTION("result of empty")
{
CHECK(j.empty() == false);
CHECK(j_const.empty() == false);
}
SECTION("definition of empty")
{
CHECK(j.begin() != j.end());
CHECK(j_const.begin() != j_const.end());
}
}
}
SECTION("object")
{
SECTION("empty object")
{
json j = json::object();
json j_const(j);
SECTION("result of empty")
{
CHECK(j.empty() == true);
CHECK(j_const.empty() == true);
}
SECTION("definition of empty")
{
CHECK(j.begin() == j.end());
CHECK(j_const.begin() == j_const.end());
}
}
SECTION("filled object")
{
json j = {{"one", 1}, {"two", 2}, {"three", 3}};
json j_const(j);
SECTION("result of empty")
{
CHECK(j.empty() == false);
CHECK(j_const.empty() == false);
}
SECTION("definition of empty")
{
CHECK(j.begin() != j.end());
CHECK(j_const.begin() != j_const.end());
}
}
}
SECTION("number (integer)")
{
json j = 23;
json j_const(j);
SECTION("result of empty")
{
CHECK(j.empty() == false);
CHECK(j_const.empty() == false);
}
SECTION("definition of empty")
{
CHECK(j.begin() != j.end());
CHECK(j_const.begin() != j_const.end());
}
}
SECTION("number (unsigned)")
{
json j = 23u;
json j_const(j);
SECTION("result of empty")
{
CHECK(j.empty() == false);
CHECK(j_const.empty() == false);
}
SECTION("definition of empty")
{
CHECK(j.begin() != j.end());
CHECK(j_const.begin() != j_const.end());
}
}
SECTION("number (float)")
{
json j = 23.42;
json j_const(j);
SECTION("result of empty")
{
CHECK(j.empty() == false);
CHECK(j_const.empty() == false);
}
SECTION("definition of empty")
{
CHECK(j.begin() != j.end());
CHECK(j_const.begin() != j_const.end());
}
}
SECTION("null")
{
json j = nullptr;
json j_const(j);
SECTION("result of empty")
{
CHECK(j.empty() == true);
CHECK(j_const.empty() == true);
}
SECTION("definition of empty")
{
CHECK(j.begin() == j.end());
CHECK(j_const.begin() == j_const.end());
}
}
}
SECTION("size()")
{
SECTION("boolean")
{
json j = true;
json j_const(j);
SECTION("result of size")
{
CHECK(j.size() == 1);
CHECK(j_const.size() == 1);
}
SECTION("definition of size")
{
CHECK(std::distance(j.begin(), j.end()) == j.size());
CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size());
CHECK(std::distance(j.rbegin(), j.rend()) == j.size());
CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size());
}
}
SECTION("string")
{
json j = "hello world";
json j_const(j);
SECTION("result of size")
{
CHECK(j.size() == 1);
CHECK(j_const.size() == 1);
}
SECTION("definition of size")
{
CHECK(std::distance(j.begin(), j.end()) == j.size());
CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size());
CHECK(std::distance(j.rbegin(), j.rend()) == j.size());
CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size());
}
}
SECTION("array")
{
SECTION("empty array")
{
json j = json::array();
json j_const(j);
SECTION("result of size")
{
CHECK(j.size() == 0);
CHECK(j_const.size() == 0);
}
SECTION("definition of size")
{
CHECK(std::distance(j.begin(), j.end()) == j.size());
CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size());
CHECK(std::distance(j.rbegin(), j.rend()) == j.size());
CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size());
}
}
SECTION("filled array")
{
json j = {1, 2, 3};
json j_const(j);
SECTION("result of size")
{
CHECK(j.size() == 3);
CHECK(j_const.size() == 3);
}
SECTION("definition of size")
{
CHECK(std::distance(j.begin(), j.end()) == j.size());
CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size());
CHECK(std::distance(j.rbegin(), j.rend()) == j.size());
CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size());
}
}
}
SECTION("object")
{
SECTION("empty object")
{
json j = json::object();
json j_const(j);
SECTION("result of size")
{
CHECK(j.size() == 0);
CHECK(j_const.size() == 0);
}
SECTION("definition of size")
{
CHECK(std::distance(j.begin(), j.end()) == j.size());
CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size());
CHECK(std::distance(j.rbegin(), j.rend()) == j.size());
CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size());
}
}
SECTION("filled object")
{
json j = {{"one", 1}, {"two", 2}, {"three", 3}};
json j_const(j);
SECTION("result of size")
{
CHECK(j.size() == 3);
CHECK(j_const.size() == 3);
}
SECTION("definition of size")
{
CHECK(std::distance(j.begin(), j.end()) == j.size());
CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size());
CHECK(std::distance(j.rbegin(), j.rend()) == j.size());
CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size());
}
}
}
SECTION("number (integer)")
{
json j = 23;
json j_const(j);
SECTION("result of size")
{
CHECK(j.size() == 1);
CHECK(j_const.size() == 1);
}
SECTION("definition of size")
{
CHECK(std::distance(j.begin(), j.end()) == j.size());
CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size());
CHECK(std::distance(j.rbegin(), j.rend()) == j.size());
CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size());
}
}
SECTION("number (unsigned)")
{
json j = 23u;
json j_const(j);
SECTION("result of size")
{
CHECK(j.size() == 1);
CHECK(j_const.size() == 1);
}
SECTION("definition of size")
{
CHECK(std::distance(j.begin(), j.end()) == j.size());
CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size());
CHECK(std::distance(j.rbegin(), j.rend()) == j.size());
CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size());
}
}
SECTION("number (float)")
{
json j = 23.42;
json j_const(j);
SECTION("result of size")
{
CHECK(j.size() == 1);
CHECK(j_const.size() == 1);
}
SECTION("definition of size")
{
CHECK(std::distance(j.begin(), j.end()) == j.size());
CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size());
CHECK(std::distance(j.rbegin(), j.rend()) == j.size());
CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size());
}
}
SECTION("null")
{
json j = nullptr;
json j_const(j);
SECTION("result of size")
{
CHECK(j.size() == 0);
CHECK(j_const.size() == 0);
}
SECTION("definition of size")
{
CHECK(std::distance(j.begin(), j.end()) == j.size());
CHECK(std::distance(j_const.begin(), j_const.end()) == j_const.size());
CHECK(std::distance(j.rbegin(), j.rend()) == j.size());
CHECK(std::distance(j_const.crbegin(), j_const.crend()) == j_const.size());
}
}
}
SECTION("max_size()")
{
SECTION("boolean")
{
json j = true;
json j_const(j);
SECTION("result of max_size")
{
CHECK(j.max_size() == 1);
CHECK(j_const.max_size() == 1);
}
}
SECTION("string")
{
json j = "hello world";
json j_const(j);
SECTION("result of max_size")
{
CHECK(j.max_size() == 1);
CHECK(j_const.max_size() == 1);
}
}
SECTION("array")
{
SECTION("empty array")
{
json j = json::array();
json j_const(j);
SECTION("result of max_size")
{
CHECK(j.max_size() >= j.size());
CHECK(j_const.max_size() >= j_const.size());
}
}
SECTION("filled array")
{
json j = {1, 2, 3};
json j_const(j);
SECTION("result of max_size")
{
CHECK(j.max_size() >= j.size());
CHECK(j_const.max_size() >= j_const.size());
}
}
}
SECTION("object")
{
SECTION("empty object")
{
json j = json::object();
json j_const(j);
SECTION("result of max_size")
{
CHECK(j.max_size() >= j.size());
CHECK(j_const.max_size() >= j_const.size());
}
}
SECTION("filled object")
{
json j = {{"one", 1}, {"two", 2}, {"three", 3}};
json j_const(j);
SECTION("result of max_size")
{
CHECK(j.max_size() >= j.size());
CHECK(j_const.max_size() >= j_const.size());
}
}
}
SECTION("number (integer)")
{
json j = 23;
json j_const(j);
SECTION("result of max_size")
{
CHECK(j.max_size() == 1);
CHECK(j_const.max_size() == 1);
}
}
SECTION("number (unsigned)")
{
json j = 23u;
json j_const(j);
SECTION("result of max_size")
{
CHECK(j.max_size() == 1);
CHECK(j_const.max_size() == 1);
}
}
SECTION("number (float)")
{
json j = 23.42;
json j_const(j);
SECTION("result of max_size")
{
CHECK(j.max_size() == 1);
CHECK(j_const.max_size() == 1);
}
}
SECTION("null")
{
json j = nullptr;
json j_const(j);
SECTION("result of max_size")
{
CHECK(j.max_size() == 0);
CHECK(j_const.max_size() == 0);
}
}
}
}
TEST_CASE("modifiers")
{
SECTION("clear()")
{
SECTION("boolean")
{
json j = true;
j.clear();
CHECK(j == json(json::value_t::boolean));
}
SECTION("string")
{
json j = "hello world";
j.clear();
CHECK(j == json(json::value_t::string));
}
SECTION("array")
{
SECTION("empty array")
{
json j = json::array();
j.clear();
CHECK(j.empty());
CHECK(j == json(json::value_t::array));
}
SECTION("filled array")
{
json j = {1, 2, 3};
j.clear();
CHECK(j.empty());
CHECK(j == json(json::value_t::array));
}
}
SECTION("object")
{
SECTION("empty object")
{
json j = json::object();
j.clear();
CHECK(j.empty());
CHECK(j == json(json::value_t::object));
}
SECTION("filled object")
{
json j = {{"one", 1}, {"two", 2}, {"three", 3}};
j.clear();
CHECK(j.empty());
CHECK(j == json(json::value_t::object));
}
}
SECTION("number (integer)")
{
json j = 23;
j.clear();
CHECK(j == json(json::value_t::number_integer));
}
SECTION("number (unsigned)")
{
json j = 23u;
j.clear();
CHECK(j == json(json::value_t::number_integer));
}
SECTION("number (float)")
{
json j = 23.42;
j.clear();
CHECK(j == json(json::value_t::number_float));
}
SECTION("null")
{
json j = nullptr;
j.clear();
CHECK(j == json(json::value_t::null));
}
}
SECTION("push_back()")
{
SECTION("to array")
{
SECTION("json&&")
{
SECTION("null")
{
json j;
j.push_back(1);
j.push_back(2);
CHECK(j.type() == json::value_t::array);
CHECK(j == json({1, 2}));
}
SECTION("array")
{
json j = {1, 2, 3};
j.push_back("Hello");
CHECK(j.type() == json::value_t::array);
CHECK(j == json({1, 2, 3, "Hello"}));
}
SECTION("other type")
{
json j = 1;
CHECK_THROWS_AS(j.push_back("Hello"), std::domain_error);
CHECK_THROWS_WITH(j.push_back("Hello"), "cannot use push_back() with number");
}
}
SECTION("const json&")
{
SECTION("null")
{
json j;
json k(1);
j.push_back(k);
j.push_back(k);
CHECK(j.type() == json::value_t::array);
CHECK(j == json({1, 1}));
}
SECTION("array")
{
json j = {1, 2, 3};
json k("Hello");
j.push_back(k);
CHECK(j.type() == json::value_t::array);
CHECK(j == json({1, 2, 3, "Hello"}));
}
SECTION("other type")
{
json j = 1;
json k("Hello");
CHECK_THROWS_AS(j.push_back(k), std::domain_error);
CHECK_THROWS_WITH(j.push_back(k), "cannot use push_back() with number");
}
}
}
SECTION("to object")
{
SECTION("null")
{
json j;
j.push_back(json::object_t::value_type({"one", 1}));
j.push_back(json::object_t::value_type({"two", 2}));
CHECK(j.type() == json::value_t::object);
CHECK(j.size() == 2);
CHECK(j["one"] == json(1));
CHECK(j["two"] == json(2));
}
SECTION("object")
{
json j(json::value_t::object);
j.push_back(json::object_t::value_type({"one", 1}));
j.push_back(json::object_t::value_type({"two", 2}));
CHECK(j.size() == 2);
CHECK(j["one"] == json(1));
CHECK(j["two"] == json(2));
}
SECTION("other type")
{
json j = 1;
json k("Hello");
CHECK_THROWS_AS(j.push_back(json::object_t::value_type({"one", 1})), std::domain_error);
CHECK_THROWS_WITH(j.push_back(json::object_t::value_type({"one", 1})),
"cannot use push_back() with number");
}
}
SECTION("with initializer_list")
{
SECTION("null")
{
json j;
j.push_back({"foo", "bar"});
CHECK(j == json::array({{"foo", "bar"}}));
json k;
k.push_back({1, 2, 3});
CHECK(k == json::array({{1, 2, 3}}));
}
SECTION("array")
{
json j = {1, 2, 3};
j.push_back({"foo", "bar"});
CHECK(j == json({1, 2, 3, {"foo", "bar"}}));
json k = {1, 2, 3};
k.push_back({1, 2, 3});
CHECK(k == json({1, 2, 3, {1, 2, 3}}));
}
SECTION("object")
{
json j = {{"key1", 1}};
j.push_back({"key2", "bar"});
CHECK(j == json({{"key1", 1}, {"key2", "bar"}}));
json k = {{"key1", 1}};
CHECK_THROWS_AS(k.push_back({1, 2, 3, 4}), std::domain_error);
CHECK_THROWS_WITH(k.push_back({1, 2, 3, 4}), "cannot use push_back() with object");
}
}
}
SECTION("operator+=")
{
SECTION("to array")
{
SECTION("json&&")
{
SECTION("null")
{
json j;
j += 1;
j += 2;
CHECK(j.type() == json::value_t::array);
CHECK(j == json({1, 2}));
}
SECTION("array")
{
json j = {1, 2, 3};
j += "Hello";
CHECK(j.type() == json::value_t::array);
CHECK(j == json({1, 2, 3, "Hello"}));
}
SECTION("other type")
{
json j = 1;
CHECK_THROWS_AS(j += "Hello", std::domain_error);
CHECK_THROWS_WITH(j += "Hello", "cannot use push_back() with number");
}
}
SECTION("const json&")
{
SECTION("null")
{
json j;
json k(1);
j += k;
j += k;
CHECK(j.type() == json::value_t::array);
CHECK(j == json({1, 1}));
}
SECTION("array")
{
json j = {1, 2, 3};
json k("Hello");
j += k;
CHECK(j.type() == json::value_t::array);
CHECK(j == json({1, 2, 3, "Hello"}));
}
SECTION("other type")
{
json j = 1;
json k("Hello");
CHECK_THROWS_AS(j += k, std::domain_error);
CHECK_THROWS_WITH(j += k, "cannot use push_back() with number");
}
}
}
SECTION("to object")
{
SECTION("null")
{
json j;
j += json::object_t::value_type({"one", 1});
j += json::object_t::value_type({"two", 2});
CHECK(j.type() == json::value_t::object);
CHECK(j.size() == 2);
CHECK(j["one"] == json(1));
CHECK(j["two"] == json(2));
}
SECTION("object")
{
json j(json::value_t::object);
j += json::object_t::value_type({"one", 1});
j += json::object_t::value_type({"two", 2});
CHECK(j.size() == 2);
CHECK(j["one"] == json(1));
CHECK(j["two"] == json(2));
}
SECTION("other type")
{
json j = 1;
json k("Hello");
CHECK_THROWS_AS(j += json::object_t::value_type({"one", 1}), std::domain_error);
CHECK_THROWS_WITH(j += json::object_t::value_type({"one", 1}),
"cannot use push_back() with number");
}
}
SECTION("with initializer_list")
{
SECTION("null")
{
json j;
j += {"foo", "bar"};
CHECK(j == json::array({{"foo", "bar"}}));
json k;
k += {1, 2, 3};
CHECK(k == json::array({{1, 2, 3}}));
}
SECTION("array")
{
json j = {1, 2, 3};
j += {"foo", "bar"};
CHECK(j == json({1, 2, 3, {"foo", "bar"}}));
json k = {1, 2, 3};
k += {1, 2, 3};
CHECK(k == json({1, 2, 3, {1, 2, 3}}));
}
SECTION("object")
{
json j = {{"key1", 1}};
j += {"key2", "bar"};
CHECK(j == json({{"key1", 1}, {"key2", "bar"}}));
json k = {{"key1", 1}};
CHECK_THROWS_AS((k += {1, 2, 3, 4}), std::domain_error);
CHECK_THROWS_WITH((k += {1, 2, 3, 4}), "cannot use push_back() with object");
}
}
}
SECTION("insert")
{
json j_array = {1, 2, 3, 4};
json j_value = 5;
SECTION("value at position")
{
SECTION("insert before begin()")
{
auto it = j_array.insert(j_array.begin(), j_value);
CHECK(j_array.size() == 5);
CHECK(*it == j_value);
CHECK(j_array.begin() == it);
CHECK(j_array == json({5, 1, 2, 3, 4}));
}
SECTION("insert in the middle")
{
auto it = j_array.insert(j_array.begin() + 2, j_value);
CHECK(j_array.size() == 5);
CHECK(*it == j_value);
CHECK((it - j_array.begin()) == 2);
CHECK(j_array == json({1, 2, 5, 3, 4}));
}
SECTION("insert before end()")
{
auto it = j_array.insert(j_array.end(), j_value);
CHECK(j_array.size() == 5);
CHECK(*it == j_value);
CHECK((j_array.end() - it) == 1);
CHECK(j_array == json({1, 2, 3, 4, 5}));
}
}
SECTION("rvalue at position")
{
SECTION("insert before begin()")
{
auto it = j_array.insert(j_array.begin(), 5);
CHECK(j_array.size() == 5);
CHECK(*it == j_value);
CHECK(j_array.begin() == it);
CHECK(j_array == json({5, 1, 2, 3, 4}));
}
SECTION("insert in the middle")
{
auto it = j_array.insert(j_array.begin() + 2, 5);
CHECK(j_array.size() == 5);
CHECK(*it == j_value);
CHECK((it - j_array.begin()) == 2);
CHECK(j_array == json({1, 2, 5, 3, 4}));
}
SECTION("insert before end()")
{
auto it = j_array.insert(j_array.end(), 5);
CHECK(j_array.size() == 5);
CHECK(*it == j_value);
CHECK((j_array.end() - it) == 1);
CHECK(j_array == json({1, 2, 3, 4, 5}));
}
}
SECTION("copies at position")
{
SECTION("insert before begin()")
{
auto it = j_array.insert(j_array.begin(), 3, 5);
CHECK(j_array.size() == 7);
CHECK(*it == j_value);
CHECK(j_array.begin() == it);
CHECK(j_array == json({5, 5, 5, 1, 2, 3, 4}));
}
SECTION("insert in the middle")
{
auto it = j_array.insert(j_array.begin() + 2, 3, 5);
CHECK(j_array.size() == 7);
CHECK(*it == j_value);
CHECK((it - j_array.begin()) == 2);
CHECK(j_array == json({1, 2, 5, 5, 5, 3, 4}));
}
SECTION("insert before end()")
{
auto it = j_array.insert(j_array.end(), 3, 5);
CHECK(j_array.size() == 7);
CHECK(*it == j_value);
CHECK((j_array.end() - it) == 3);
CHECK(j_array == json({1, 2, 3, 4, 5, 5, 5}));
}
SECTION("insert nothing (count = 0)")
{
auto pos = j_array.end();
auto it = j_array.insert(j_array.end(), 0, 5);
CHECK(j_array.size() == 4);
CHECK(it == pos);
CHECK(j_array == json({1, 2, 3, 4}));
}
}
SECTION("range")
{
json j_other_array = {"first", "second"};
SECTION("proper usage")
{
auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.end());
CHECK(j_array.size() == 6);
CHECK(*it == *j_other_array.begin());
CHECK((j_array.end() - it) == 2);
CHECK(j_array == json({1, 2, 3, 4, "first", "second"}));
}
SECTION("empty range")
{
auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.begin());
CHECK(j_array.size() == 4);
CHECK(it == j_array.end());
CHECK(j_array == json({1, 2, 3, 4}));
}
SECTION("invalid iterators")
{
json j_other_array2 = {"first", "second"};
CHECK_THROWS_AS(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), std::domain_error);
CHECK_THROWS_AS(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()),
std::domain_error);
CHECK_THROWS_WITH(j_array.insert(j_array.end(), j_array.begin(), j_array.end()),
"passed iterators may not belong to container");
CHECK_THROWS_WITH(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()),
"iterators do not fit");
}
}
SECTION("initializer list at position")
{
SECTION("insert before begin()")
{
auto it = j_array.insert(j_array.begin(), {7, 8, 9});
CHECK(j_array.size() == 7);
CHECK(*it == json(7));
CHECK(j_array.begin() == it);
CHECK(j_array == json({7, 8, 9, 1, 2, 3, 4}));
}
SECTION("insert in the middle")
{
auto it = j_array.insert(j_array.begin() + 2, {7, 8, 9});
CHECK(j_array.size() == 7);
CHECK(*it == json(7));
CHECK((it - j_array.begin()) == 2);
CHECK(j_array == json({1, 2, 7, 8, 9, 3, 4}));
}
SECTION("insert before end()")
{
auto it = j_array.insert(j_array.end(), {7, 8, 9});
CHECK(j_array.size() == 7);
CHECK(*it == json(7));
CHECK((j_array.end() - it) == 3);
CHECK(j_array == json({1, 2, 3, 4, 7, 8, 9}));
}
}
SECTION("invalid iterator")
{
// pass iterator to a different array
json j_another_array = {1, 2};
json j_yet_another_array = {"first", "second"};
CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10), std::domain_error);
CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_value), std::domain_error);
CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10, 11), std::domain_error);
CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_yet_another_array.begin(),
j_yet_another_array.end()), std::domain_error);
CHECK_THROWS_AS(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), std::domain_error);
CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), 10), "iterator does not fit current value");
CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), j_value),
"iterator does not fit current value");
CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), 10, 11),
"iterator does not fit current value");
CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), j_yet_another_array.begin(),
j_yet_another_array.end()), "iterator does not fit current value");
CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), {1, 2, 3, 4}),
"iterator does not fit current value");
}
SECTION("non-array type")
{
// call insert on a non-array type
json j_nonarray = 3;
json j_yet_another_array = {"first", "second"};
CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10), std::domain_error);
CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_value), std::domain_error);
CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10, 11), std::domain_error);
CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(),
j_yet_another_array.end()), std::domain_error);
CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), std::domain_error);
CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), 10), "cannot use insert() with number");
CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), j_value), "cannot use insert() with number");
CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), 10, 11), "cannot use insert() with number");
CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(),
j_yet_another_array.end()), "cannot use insert() with number");
CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}),
"cannot use insert() with number");
}
}
SECTION("swap()")
{
SECTION("json")
{
SECTION("member swap")
{
json j("hello world");
json k(42.23);
j.swap(k);
CHECK(j == json(42.23));
CHECK(k == json("hello world"));
}
SECTION("nonmember swap")
{
json j("hello world");
json k(42.23);
std::swap(j, k);
CHECK(j == json(42.23));
CHECK(k == json("hello world"));
}
}
SECTION("array_t")
{
SECTION("array_t type")
{
json j = {1, 2, 3, 4};
json::array_t a = {"foo", "bar", "baz"};
j.swap(a);
CHECK(j == json({"foo", "bar", "baz"}));
j.swap(a);
CHECK(j == json({1, 2, 3, 4}));
}
SECTION("non-array_t type")
{
json j = 17;
json::array_t a = {"foo", "bar", "baz"};
CHECK_THROWS_AS(j.swap(a), std::domain_error);
CHECK_THROWS_WITH(j.swap(a), "cannot use swap() with number");
}
}
SECTION("object_t")
{
SECTION("object_t type")
{
json j = {{"one", 1}, {"two", 2}};
json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}};
j.swap(o);
CHECK(j == json({{"cow", "Kuh"}, {"chicken", "Huhn"}}));
j.swap(o);
CHECK(j == json({{"one", 1}, {"two", 2}}));
}
SECTION("non-object_t type")
{
json j = 17;
json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}};
CHECK_THROWS_AS(j.swap(o), std::domain_error);
CHECK_THROWS_WITH(j.swap(o), "cannot use swap() with number");
}
}
SECTION("string_t")
{
SECTION("string_t type")
{
json j = "Hello world";
json::string_t s = "Hallo Welt";
j.swap(s);
CHECK(j == json("Hallo Welt"));
j.swap(s);
CHECK(j == json("Hello world"));
}
SECTION("non-string_t type")
{
json j = 17;
json::string_t s = "Hallo Welt";
CHECK_THROWS_AS(j.swap(s), std::domain_error);
CHECK_THROWS_WITH(j.swap(s), "cannot use swap() with number");
}
}
}
}
TEST_CASE("lexicographical comparison operators")
{
SECTION("types")
{
std::vector<json::value_t> j_types =
{
json::value_t::null,
json::value_t::boolean,
json::value_t::number_integer,
json::value_t::number_unsigned,
json::value_t::number_float,
json::value_t::object,
json::value_t::array,
json::value_t::string
};
SECTION("comparison: less")
{
std::vector<std::vector<bool>> expected =
{
{false, true, true, true, true, true, true, true},
{false, false, true, true, true, true, true, true},
{false, false, false, false, false, true, true, true},
{false, false, false, false, false, true, true, true},
{false, false, false, false, false, true, true, true},
{false, false, false, false, false, false, true, true},
{false, false, false, false, false, false, false, true},
{false, false, false, false, false, false, false, false}
};
for (size_t i = 0; i < j_types.size(); ++i)
{
for (size_t j = 0; j < j_types.size(); ++j)
{
CAPTURE(i);
CAPTURE(j);
// check precomputed values
CHECK(operator<(j_types[i], j_types[j]) == expected[i][j]);
}
}
}
}
SECTION("values")
{
json j_values =
{
nullptr, nullptr,
17, 42,
8u, 13u,
3.14159, 23.42,
"foo", "bar",
true, false,
{1, 2, 3}, {"one", "two", "three"},
{{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}}
};
SECTION("comparison: equal")
{
std::vector<std::vector<bool>> expected =
{
{true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
{true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
{false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false},
{false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false},
{false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false},
{false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false},
{false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false},
{false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true}
};
for (size_t i = 0; i < j_values.size(); ++i)
{
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i);
CAPTURE(j);
// check precomputed values
CHECK( (j_values[i] == j_values[j]) == expected[i][j] );
}
}
// comparison with discarded elements
json j_discarded(json::value_t::discarded);
for (size_t i = 0; i < j_values.size(); ++i)
{
CHECK( (j_values[i] == j_discarded) == false);
CHECK( (j_discarded == j_values[i]) == false);
CHECK( (j_discarded == j_discarded) == false);
}
// compare with null pointer
json j_null;
CHECK(j_null == nullptr);
CHECK(nullptr == j_null);
}
SECTION("comparison: not equal")
{
for (size_t i = 0; i < j_values.size(); ++i)
{
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i);
CAPTURE(j);
// check definition
CHECK( (j_values[i] != j_values[j]) == not(j_values[i] == j_values[j]) );
}
}
// compare with null pointer
json j_null;
CHECK( (j_null != nullptr) == false);
CHECK( (nullptr != j_null) == false);
CHECK( (j_null != nullptr) == not(j_null == nullptr));
CHECK( (nullptr != j_null) == not(nullptr == j_null));
}
SECTION("comparison: less")
{
std::vector<std::vector<bool>> expected =
{
{false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true},
{false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true},
{false, false, false, true, false, false, false, true, true, true, false, false, true, true, true, true},
{false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, true},
{false, false, true, true, false, true, false, true, true, true, false, false, true, true, true, true},
{false, false, true, true, false, false, false, true, true, true, false, false, true, true, true, true},
{false, false, true, true, true, true, false, true, true, true, false, false, true, true, true, true},
{false, false, false, true, false, false, false, false, true, true, false, false, true, true, true, true},
{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false},
{false, false, true, true, true, true, true, true, true, true, false, false, true, true, true, true},
{false, false, true, true, true, true, true, true, true, true, true, false, true, true, true, true},
{false, false, false, false, false, false, false, false, true, true, false, false, false, true, false, false},
{false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false},
{false, false, false, false, false, false, false, false, true, true, false, false, true, true, false, false},
{false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, false}
};
for (size_t i = 0; i < j_values.size(); ++i)
{
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i);
CAPTURE(j);
// check precomputed values
CHECK( (j_values[i] < j_values[j]) == expected[i][j] );
}
}
// comparison with discarded elements
json j_discarded(json::value_t::discarded);
for (size_t i = 0; i < j_values.size(); ++i)
{
CAPTURE(i);
CHECK( (j_values[i] < j_discarded) == false);
CHECK( (j_discarded < j_values[i]) == false);
CHECK( (j_discarded < j_discarded) == false);
}
}
SECTION("comparison: less than or equal equal")
{
for (size_t i = 0; i < j_values.size(); ++i)
{
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i);
CAPTURE(j);
// check definition
CHECK( (j_values[i] <= j_values[j]) == not(j_values[j] < j_values[i]) );
}
}
}
SECTION("comparison: greater than")
{
for (size_t i = 0; i < j_values.size(); ++i)
{
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i);
CAPTURE(j);
// check definition
CHECK( (j_values[i] > j_values[j]) == (j_values[j] < j_values[i]) );
}
}
}
SECTION("comparison: greater than or equal")
{
for (size_t i = 0; i < j_values.size(); ++i)
{
for (size_t j = 0; j < j_values.size(); ++j)
{
CAPTURE(i);
CAPTURE(j);
// check definition
CHECK( (j_values[i] >= j_values[j]) == not(j_values[i] < j_values[j]) );
}
}
}
}
}
TEST_CASE("serialization")
{
SECTION("operator<<")
{
SECTION("no given width")
{
std::stringstream ss;
json j = {"foo", 1, 2, 3, false, {{"one", 1}}};
ss << j;
CHECK(ss.str() == "[\"foo\",1,2,3,false,{\"one\":1}]");
}
SECTION("given width")
{
std::stringstream ss;
json j = {"foo", 1, 2, 3, false, {{"one", 1}}};
ss << std::setw(4) << j;
CHECK(ss.str() ==
"[\n \"foo\",\n 1,\n 2,\n 3,\n false,\n {\n \"one\": 1\n }\n]");
}
}
SECTION("operator>>")
{
SECTION("no given width")
{
std::stringstream ss;
json j = {"foo", 1, 2, 3, false, {{"one", 1}}};
j >> ss;
CHECK(ss.str() == "[\"foo\",1,2,3,false,{\"one\":1}]");
}
SECTION("given width")
{
std::stringstream ss;
json j = {"foo", 1, 2, 3, false, {{"one", 1}}};
ss.width(4);
j >> ss;
CHECK(ss.str() ==
"[\n \"foo\",\n 1,\n 2,\n 3,\n false,\n {\n \"one\": 1\n }\n]");
}
}
}
TEST_CASE("deserialization")
{
SECTION("stream")
{
std::stringstream ss;
ss << "[\"foo\",1,2,3,false,{\"one\":1}]";
json j = json::parse(ss);
CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}}));
}
SECTION("string")
{
auto s = "[\"foo\",1,2,3,false,{\"one\":1}]";
json j = json::parse(s);
CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}}));
}
SECTION("operator<<")
{
std::stringstream ss;
ss << "[\"foo\",1,2,3,false,{\"one\":1}]";
json j;
j << ss;
CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}}));
}
SECTION("operator>>")
{
std::stringstream ss;
ss << "[\"foo\",1,2,3,false,{\"one\":1}]";
json j;
ss >> j;
CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}}));
}
SECTION("user-defined string literal")
{
CHECK("[\"foo\",1,2,3,false,{\"one\":1}]"_json == json({"foo", 1, 2, 3, false, {{"one", 1}}}));
}
}
TEST_CASE("iterator class")
{
SECTION("construction")
{
SECTION("constructor")
{
SECTION("null")
{
json j(json::value_t::null);
json::iterator it(&j);
}
SECTION("object")
{
json j(json::value_t::object);
json::iterator it(&j);
}
SECTION("array")
{
json j(json::value_t::array);
json::iterator it(&j);
}
}
SECTION("copy assignment")
{
json j(json::value_t::null);
json::iterator it(&j);
json::iterator it2(&j);
it2 = it;
}
}
SECTION("initialization")
{
SECTION("set_begin")
{
SECTION("null")
{
json j(json::value_t::null);
json::iterator it(&j);
it.set_begin();
CHECK(it == j.begin());
}
SECTION("object")
{
json j(json::value_t::object);
json::iterator it(&j);
it.set_begin();
CHECK(it == j.begin());
}
SECTION("array")
{
json j(json::value_t::array);
json::iterator it(&j);
it.set_begin();
CHECK(it == j.begin());
}
}
SECTION("set_end")
{
SECTION("null")
{
json j(json::value_t::null);
json::iterator it(&j);
it.set_end();
CHECK(it == j.end());
}
SECTION("object")
{
json j(json::value_t::object);
json::iterator it(&j);
it.set_end();
CHECK(it == j.end());
}
SECTION("array")
{
json j(json::value_t::array);
json::iterator it(&j);
it.set_end();
CHECK(it == j.end());
}
}
}
SECTION("element access")
{
SECTION("operator*")
{
SECTION("null")
{
json j(json::value_t::null);
json::iterator it = j.begin();
CHECK_THROWS_AS(*it, std::out_of_range);
CHECK_THROWS_WITH(*it, "cannot get value");
}
SECTION("number")
{
json j(17);
json::iterator it = j.begin();
CHECK(*it == json(17));
it = j.end();
CHECK_THROWS_AS(*it, std::out_of_range);
CHECK_THROWS_WITH(*it, "cannot get value");
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::iterator it = j.begin();
CHECK(*it == json("bar"));
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::iterator it = j.begin();
CHECK(*it == json(1));
}
}
SECTION("operator->")
{
SECTION("null")
{
json j(json::value_t::null);
json::iterator it = j.begin();
CHECK_THROWS_AS(it->type_name(), std::out_of_range);
CHECK_THROWS_WITH(it->type_name(), "cannot get value");
}
SECTION("number")
{
json j(17);
json::iterator it = j.begin();
CHECK(it->type_name() == "number");
it = j.end();
CHECK_THROWS_AS(it->type_name(), std::out_of_range);
CHECK_THROWS_WITH(it->type_name(), "cannot get value");
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::iterator it = j.begin();
CHECK(it->type_name() == "string");
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::iterator it = j.begin();
CHECK(it->type_name() == "number");
}
}
}
SECTION("increment/decrement")
{
SECTION("post-increment")
{
SECTION("null")
{
json j(json::value_t::null);
json::iterator it = j.begin();
CHECK(it.m_it.primitive_iterator == 1);
it++;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
}
SECTION("number")
{
json j(17);
json::iterator it = j.begin();
CHECK(it.m_it.primitive_iterator == 0);
it++;
CHECK(it.m_it.primitive_iterator == 1);
it++;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::iterator it = j.begin();
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin());
it++;
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end());
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::iterator it = j.begin();
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin());
it++;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
it++;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
it++;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
it++;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end());
}
}
SECTION("pre-increment")
{
SECTION("null")
{
json j(json::value_t::null);
json::iterator it = j.begin();
CHECK(it.m_it.primitive_iterator == 1);
++it;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
}
SECTION("number")
{
json j(17);
json::iterator it = j.begin();
CHECK(it.m_it.primitive_iterator == 0);
++it;
CHECK(it.m_it.primitive_iterator == 1);
++it;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::iterator it = j.begin();
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin());
++it;
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end());
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::iterator it = j.begin();
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin());
++it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
++it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
++it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
++it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end());
}
}
SECTION("post-decrement")
{
SECTION("null")
{
json j(json::value_t::null);
json::iterator it = j.end();
CHECK(it.m_it.primitive_iterator == 1);
}
SECTION("number")
{
json j(17);
json::iterator it = j.end();
CHECK(it.m_it.primitive_iterator == 1);
it--;
CHECK(it.m_it.primitive_iterator == 0);
it--;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::iterator it = j.end();
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end());
it--;
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin());
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::iterator it = j.end();
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end());
it--;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
it--;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
it--;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
it--;
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
}
}
SECTION("pre-decrement")
{
SECTION("null")
{
json j(json::value_t::null);
json::iterator it = j.end();
CHECK(it.m_it.primitive_iterator == 1);
}
SECTION("number")
{
json j(17);
json::iterator it = j.end();
CHECK(it.m_it.primitive_iterator == 1);
--it;
CHECK(it.m_it.primitive_iterator == 0);
--it;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::iterator it = j.end();
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end());
--it;
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin());
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::iterator it = j.end();
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end());
--it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
--it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
--it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
--it;
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
}
}
}
}
TEST_CASE("const_iterator class")
{
SECTION("construction")
{
SECTION("constructor")
{
SECTION("null")
{
json j(json::value_t::null);
json::const_iterator it(&j);
}
SECTION("object")
{
json j(json::value_t::object);
json::const_iterator it(&j);
}
SECTION("array")
{
json j(json::value_t::array);
json::const_iterator it(&j);
}
}
SECTION("copy assignment")
{
json j(json::value_t::null);
json::const_iterator it(&j);
json::const_iterator it2(&j);
it2 = it;
}
}
SECTION("initialization")
{
SECTION("set_begin")
{
SECTION("null")
{
json j(json::value_t::null);
json::const_iterator it(&j);
it.set_begin();
CHECK(it == j.cbegin());
}
SECTION("object")
{
json j(json::value_t::object);
json::const_iterator it(&j);
it.set_begin();
CHECK(it == j.cbegin());
}
SECTION("array")
{
json j(json::value_t::array);
json::const_iterator it(&j);
it.set_begin();
CHECK(it == j.cbegin());
}
}
SECTION("set_end")
{
SECTION("null")
{
json j(json::value_t::null);
json::const_iterator it(&j);
it.set_end();
CHECK(it == j.cend());
}
SECTION("object")
{
json j(json::value_t::object);
json::const_iterator it(&j);
it.set_end();
CHECK(it == j.cend());
}
SECTION("array")
{
json j(json::value_t::array);
json::const_iterator it(&j);
it.set_end();
CHECK(it == j.cend());
}
}
}
SECTION("element access")
{
SECTION("operator*")
{
SECTION("null")
{
json j(json::value_t::null);
json::const_iterator it = j.cbegin();
CHECK_THROWS_AS(*it, std::out_of_range);
CHECK_THROWS_WITH(*it, "cannot get value");
}
SECTION("number")
{
json j(17);
json::const_iterator it = j.cbegin();
CHECK(*it == json(17));
it = j.cend();
CHECK_THROWS_AS(*it, std::out_of_range);
CHECK_THROWS_WITH(*it, "cannot get value");
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::const_iterator it = j.cbegin();
CHECK(*it == json("bar"));
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::const_iterator it = j.cbegin();
CHECK(*it == json(1));
}
}
SECTION("operator->")
{
SECTION("null")
{
json j(json::value_t::null);
json::const_iterator it = j.cbegin();
CHECK_THROWS_AS(it->type_name(), std::out_of_range);
CHECK_THROWS_WITH(it->type_name(), "cannot get value");
}
SECTION("number")
{
json j(17);
json::const_iterator it = j.cbegin();
CHECK(it->type_name() == "number");
it = j.cend();
CHECK_THROWS_AS(it->type_name(), std::out_of_range);
CHECK_THROWS_WITH(it->type_name(), "cannot get value");
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::const_iterator it = j.cbegin();
CHECK(it->type_name() == "string");
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::const_iterator it = j.cbegin();
CHECK(it->type_name() == "number");
}
}
}
SECTION("increment/decrement")
{
SECTION("post-increment")
{
SECTION("null")
{
json j(json::value_t::null);
json::const_iterator it = j.cbegin();
CHECK(it.m_it.primitive_iterator == 1);
it++;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
}
SECTION("number")
{
json j(17);
json::const_iterator it = j.cbegin();
CHECK(it.m_it.primitive_iterator == 0);
it++;
CHECK(it.m_it.primitive_iterator == 1);
it++;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::const_iterator it = j.cbegin();
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin());
it++;
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end());
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::const_iterator it = j.cbegin();
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin());
it++;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
it++;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
it++;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
it++;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end());
}
}
SECTION("pre-increment")
{
SECTION("null")
{
json j(json::value_t::null);
json::const_iterator it = j.cbegin();
CHECK(it.m_it.primitive_iterator == 1);
++it;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
}
SECTION("number")
{
json j(17);
json::const_iterator it = j.cbegin();
CHECK(it.m_it.primitive_iterator == 0);
++it;
CHECK(it.m_it.primitive_iterator == 1);
++it;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::const_iterator it = j.cbegin();
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin());
++it;
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end());
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::const_iterator it = j.cbegin();
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin());
++it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
++it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
++it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
++it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end());
}
}
SECTION("post-decrement")
{
SECTION("null")
{
json j(json::value_t::null);
json::const_iterator it = j.cend();
CHECK(it.m_it.primitive_iterator == 1);
}
SECTION("number")
{
json j(17);
json::const_iterator it = j.cend();
CHECK(it.m_it.primitive_iterator == 1);
it--;
CHECK(it.m_it.primitive_iterator == 0);
it--;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::const_iterator it = j.cend();
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end());
it--;
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin());
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::const_iterator it = j.cend();
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end());
it--;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
it--;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
it--;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
it--;
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
}
}
SECTION("pre-decrement")
{
SECTION("null")
{
json j(json::value_t::null);
json::const_iterator it = j.cend();
CHECK(it.m_it.primitive_iterator == 1);
}
SECTION("number")
{
json j(17);
json::const_iterator it = j.cend();
CHECK(it.m_it.primitive_iterator == 1);
--it;
CHECK(it.m_it.primitive_iterator == 0);
--it;
CHECK((it.m_it.primitive_iterator != 0 and it.m_it.primitive_iterator != 1));
}
SECTION("object")
{
json j({{"foo", "bar"}});
json::const_iterator it = j.cend();
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->end());
--it;
CHECK(it.m_it.object_iterator == it.m_object->m_value.object->begin());
}
SECTION("array")
{
json j({1, 2, 3, 4});
json::const_iterator it = j.cend();
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->end());
--it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
--it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
--it;
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
--it;
CHECK(it.m_it.array_iterator == it.m_object->m_value.array->begin());
CHECK(it.m_it.array_iterator != it.m_object->m_value.array->end());
}
}
}
}
TEST_CASE("convenience functions")
{
SECTION("type name as string")
{
CHECK(json(json::value_t::null).type_name() == "null");
CHECK(json(json::value_t::object).type_name() == "object");
CHECK(json(json::value_t::array).type_name() == "array");
CHECK(json(json::value_t::number_integer).type_name() == "number");
CHECK(json(json::value_t::number_float).type_name() == "number");
CHECK(json(json::value_t::boolean).type_name() == "boolean");
CHECK(json(json::value_t::string).type_name() == "string");
CHECK(json(json::value_t::discarded).type_name() == "discarded");
}
SECTION("string escape")
{
CHECK(json::escape_string("\"") == "\\\"");
CHECK(json::escape_string("\\") == "\\\\");
CHECK(json::escape_string("\b") == "\\b");
CHECK(json::escape_string("\f") == "\\f");
CHECK(json::escape_string("\n") == "\\n");
CHECK(json::escape_string("\r") == "\\r");
CHECK(json::escape_string("\t") == "\\t");
CHECK(json::escape_string("\x01") == "\\u0001");
CHECK(json::escape_string("\x02") == "\\u0002");
CHECK(json::escape_string("\x03") == "\\u0003");
CHECK(json::escape_string("\x04") == "\\u0004");
CHECK(json::escape_string("\x05") == "\\u0005");
CHECK(json::escape_string("\x06") == "\\u0006");
CHECK(json::escape_string("\x07") == "\\u0007");
CHECK(json::escape_string("\x08") == "\\b");
CHECK(json::escape_string("\x09") == "\\t");
CHECK(json::escape_string("\x0a") == "\\n");
CHECK(json::escape_string("\x0b") == "\\u000b");
CHECK(json::escape_string("\x0c") == "\\f");
CHECK(json::escape_string("\x0d") == "\\r");
CHECK(json::escape_string("\x0e") == "\\u000e");
CHECK(json::escape_string("\x0f") == "\\u000f");
CHECK(json::escape_string("\x10") == "\\u0010");
CHECK(json::escape_string("\x11") == "\\u0011");
CHECK(json::escape_string("\x12") == "\\u0012");
CHECK(json::escape_string("\x13") == "\\u0013");
CHECK(json::escape_string("\x14") == "\\u0014");
CHECK(json::escape_string("\x15") == "\\u0015");
CHECK(json::escape_string("\x16") == "\\u0016");
CHECK(json::escape_string("\x17") == "\\u0017");
CHECK(json::escape_string("\x18") == "\\u0018");
CHECK(json::escape_string("\x19") == "\\u0019");
CHECK(json::escape_string("\x1a") == "\\u001a");
CHECK(json::escape_string("\x1b") == "\\u001b");
CHECK(json::escape_string("\x1c") == "\\u001c");
CHECK(json::escape_string("\x1d") == "\\u001d");
CHECK(json::escape_string("\x1e") == "\\u001e");
CHECK(json::escape_string("\x1f") == "\\u001f");
}
}
TEST_CASE("lexer class")
{
SECTION("scan")
{
SECTION("structural characters")
{
CHECK(json::lexer("[").scan() == json::lexer::token_type::begin_array);
CHECK(json::lexer("]").scan() == json::lexer::token_type::end_array);
CHECK(json::lexer("{").scan() == json::lexer::token_type::begin_object);
CHECK(json::lexer("}").scan() == json::lexer::token_type::end_object);
CHECK(json::lexer(",").scan() == json::lexer::token_type::value_separator);
CHECK(json::lexer(":").scan() == json::lexer::token_type::name_separator);
}
SECTION("literal names")
{
CHECK(json::lexer("null").scan() == json::lexer::token_type::literal_null);
CHECK(json::lexer("true").scan() == json::lexer::token_type::literal_true);
CHECK(json::lexer("false").scan() == json::lexer::token_type::literal_false);
}
SECTION("numbers")
{
CHECK(json::lexer("0").scan() == json::lexer::token_type::value_number);
CHECK(json::lexer("1").scan() == json::lexer::token_type::value_number);
CHECK(json::lexer("2").scan() == json::lexer::token_type::value_number);
CHECK(json::lexer("3").scan() == json::lexer::token_type::value_number);
CHECK(json::lexer("4").scan() == json::lexer::token_type::value_number);
CHECK(json::lexer("5").scan() == json::lexer::token_type::value_number);
CHECK(json::lexer("6").scan() == json::lexer::token_type::value_number);
CHECK(json::lexer("7").scan() == json::lexer::token_type::value_number);
CHECK(json::lexer("8").scan() == json::lexer::token_type::value_number);
CHECK(json::lexer("9").scan() == json::lexer::token_type::value_number);
}
SECTION("whitespace")
{
// result is end_of_input, because not token is following
CHECK(json::lexer(" ").scan() == json::lexer::token_type::end_of_input);
CHECK(json::lexer("\t").scan() == json::lexer::token_type::end_of_input);
CHECK(json::lexer("\n").scan() == json::lexer::token_type::end_of_input);
CHECK(json::lexer("\r").scan() == json::lexer::token_type::end_of_input);
CHECK(json::lexer(" \t\n\r\n\t ").scan() == json::lexer::token_type::end_of_input);
}
}
SECTION("token_type_name")
{
CHECK(json::lexer::token_type_name(json::lexer::token_type::uninitialized) == "<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) == "<parse error>");
CHECK(json::lexer::token_type_name(json::lexer::token_type::end_of_input) == "end of input");
}
SECTION("parse errors on first character")
{
for (int c = 1; c < 128; ++c)
{
auto s = std::string(1, c);
switch (c)
{
// single characters that are valid tokens
case ('['):
case (']'):
case ('{'):
case ('}'):
case (','):
case (':'):
case ('0'):
case ('1'):
case ('2'):
case ('3'):
case ('4'):
case ('5'):
case ('6'):
case ('7'):
case ('8'):
case ('9'):
{
CHECK(json::lexer(s.c_str()).scan() != json::lexer::token_type::parse_error);
break;
}
// whitespace
case (' '):
case ('\t'):
case ('\n'):
case ('\r'):
{
CHECK(json::lexer(s.c_str()).scan() == json::lexer::token_type::end_of_input);
break;
}
// anything else is not expected
default:
{
CHECK(json::lexer(s.c_str()).scan() == json::lexer::token_type::parse_error);
break;
}
}
}
}
SECTION("to_unicode")
{
CHECK(json::lexer::to_unicode(0x1F4A9) == "💩");
CHECK_THROWS_AS(json::lexer::to_unicode(0x200000), std::out_of_range);
CHECK_THROWS_WITH(json::lexer::to_unicode(0x200000), "code points above 0x10FFFF are invalid");
}
}
TEST_CASE("parser class")
{
SECTION("parse")
{
SECTION("null")
{
CHECK(json::parser("null").parse() == json(nullptr));
}
SECTION("true")
{
CHECK(json::parser("true").parse() == json(true));
}
SECTION("false")
{
CHECK(json::parser("false").parse() == json(false));
}
SECTION("array")
{
SECTION("empty array")
{
CHECK(json::parser("[]").parse() == json(json::value_t::array));
CHECK(json::parser("[ ]").parse() == json(json::value_t::array));
}
SECTION("nonempty array")
{
CHECK(json::parser("[true, false, null]").parse() == json({true, false, nullptr}));
}
}
SECTION("object")
{
SECTION("empty object")
{
CHECK(json::parser("{}").parse() == json(json::value_t::object));
CHECK(json::parser("{ }").parse() == json(json::value_t::object));
}
SECTION("nonempty object")
{
CHECK(json::parser("{\"\": true, \"one\": 1, \"two\": null}").parse() == json({{"", true}, {"one", 1}, {"two", nullptr}}));
}
}
SECTION("string")
{
// empty string
CHECK(json::parser("\"\"").parse() == json(json::value_t::string));
SECTION("errors")
{
// error: tab in string
CHECK_THROWS_AS(json::parser("\"\t\"").parse(), std::invalid_argument);
CHECK_THROWS_WITH(json::parser("\"\t\"").parse(), "parse error - unexpected '\"'");
// error: newline in string
CHECK_THROWS_AS(json::parser("\"\n\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\r\"").parse(), std::invalid_argument);
CHECK_THROWS_WITH(json::parser("\"\n\"").parse(), "parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser("\"\r\"").parse(), "parse error - unexpected '\"'");
// error: backspace in string
CHECK_THROWS_AS(json::parser("\"\b\"").parse(), std::invalid_argument);
CHECK_THROWS_WITH(json::parser("\"\b\"").parse(), "parse error - unexpected '\"'");
// improve code coverage
CHECK_THROWS_AS(json::parser("\uFF01").parse(), std::invalid_argument);
// unescaped control characters
CHECK_THROWS_AS(json::parser("\"\x00\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x01\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x02\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x03\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x04\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x05\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x06\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x07\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x08\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x09\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x0a\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x0b\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x0c\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x0d\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x0e\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x0f\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x10\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x11\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x12\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x13\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x14\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x15\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x16\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x17\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x18\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x19\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x1a\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x1b\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x1c\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x1d\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x1e\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\x1f\"").parse(), std::invalid_argument);
}
SECTION("escaped")
{
// quotation mark "\""
auto r1 = R"("\"")"_json;
CHECK(json::parser("\"\\\"\"").parse() == r1);
// reverse solidus "\\"
auto r2 = R"("\\")"_json;
CHECK(json::parser("\"\\\\\"").parse() == r2);
// solidus
CHECK(json::parser("\"\\/\"").parse() == R"("/")"_json);
// backspace
CHECK(json::parser("\"\\b\"").parse() == json("\b"));
// formfeed
CHECK(json::parser("\"\\f\"").parse() == json("\f"));
// newline
CHECK(json::parser("\"\\n\"").parse() == json("\n"));
// carriage return
CHECK(json::parser("\"\\r\"").parse() == json("\r"));
// horizontal tab
CHECK(json::parser("\"\\t\"").parse() == json("\t"));
CHECK(json::parser("\"\\u0001\"").parse().get<json::string_t>() == "\x01");
CHECK(json::parser("\"\\u000a\"").parse().get<json::string_t>() == "\n");
CHECK(json::parser("\"\\u00b0\"").parse().get<json::string_t>() == "°");
CHECK(json::parser("\"\\u0c00\"").parse().get<json::string_t>() == "ఀ");
CHECK(json::parser("\"\\ud000\"").parse().get<json::string_t>() == "퀀");
CHECK(json::parser("\"\\u000E\"").parse().get<json::string_t>() == "\x0E");
CHECK(json::parser("\"\\u00F0\"").parse().get<json::string_t>() == "ð");
CHECK(json::parser("\"\\u0100\"").parse().get<json::string_t>() == "Ā");
CHECK(json::parser("\"\\u2000\"").parse().get<json::string_t>() == " ");
CHECK(json::parser("\"\\uFFFF\"").parse().get<json::string_t>() == "￿");
CHECK(json::parser("\"\\u20AC\"").parse().get<json::string_t>() == "€");
CHECK(json::parser("\"€\"").parse().get<json::string_t>() == "€");
CHECK(json::parser("\"🎈\"").parse().get<json::string_t>() == "🎈");
CHECK(json::parse("\"\\ud80c\\udc60\"").get<json::string_t>() == u8"\U00013060");
CHECK(json::parse("\"\\ud83c\\udf1e\"").get<json::string_t>() == "🌞");
}
}
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<int64_t>() == -9007199254740991);
// (2**53)-1
CHECK(json::parser("9007199254740991").parse().get<int64_t>() == 9007199254740991);
}
SECTION("over the edge cases") // issue #178 - Integer conversion to unsigned (incorrect handling of 64 bit integers)
{
// While RFC7159, Section 6 specifies a preference for support
// for ranges in range of IEEE 754-2008 binary64 (double precision)
// this does not accommodate 64 bit integers without loss of accuracy.
// As 64 bit integers are now widely used in software, it is desirable
// to expand support to to the full 64 bit (signed and unsigned) range
// i.e. -(2**63) -> (2**64)-1.
// -(2**63) ** Note: compilers see negative literals as negated positive numbers (hence the -1))
CHECK(json::parser("-9223372036854775808").parse().get<int64_t>() == -9223372036854775807 - 1);
// (2**63)-1
CHECK(json::parser("9223372036854775807").parse().get<int64_t>() == 9223372036854775807);
// (2**64)-1
CHECK(json::parser("18446744073709551615").parse().get<uint64_t>() == 18446744073709551615u);
}
}
SECTION("floating-point")
{
SECTION("without exponent")
{
CHECK(json::parser("-128.5").parse() == json(-128.5));
CHECK(json::parser("0.999").parse() == json(0.999));
CHECK(json::parser("128.5").parse() == json(128.5));
CHECK(json::parser("-0.0").parse() == json(-0.0));
}
SECTION("with exponent")
{
CHECK(json::parser("-128.5E3").parse() == json(-128.5E3));
CHECK(json::parser("-128.5E-3").parse() == json(-128.5E-3));
CHECK(json::parser("-0.0e1").parse() == json(-0.0e1));
CHECK(json::parser("-0.0E1").parse() == json(-0.0e1));
}
}
SECTION("invalid numbers")
{
CHECK_THROWS_AS(json::parser("01").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("--1").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("1.").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("1E").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("1E-").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("1.E1").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("-1E").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("-0E#").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("-0E-#").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("-0#").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("-0.0:").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("-0.0Z").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("-0E123:").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("-0e0-:").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("-0e-:").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("-0f").parse(), std::invalid_argument);
// numbers must not begin with "+"
CHECK_THROWS_AS(json::parser("+1").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("+0").parse(), std::invalid_argument);
CHECK_THROWS_WITH(json::parser("01").parse(),
"parse error - unexpected number literal; expected end of input");
CHECK_THROWS_WITH(json::parser("--1").parse(), "parse error - unexpected '-'");
CHECK_THROWS_WITH(json::parser("1.").parse(),
"parse error - unexpected '.'; expected end of input");
CHECK_THROWS_WITH(json::parser("1E").parse(),
"parse error - unexpected 'E'; expected end of input");
CHECK_THROWS_WITH(json::parser("1E-").parse(),
"parse error - unexpected 'E'; expected end of input");
CHECK_THROWS_WITH(json::parser("1.E1").parse(),
"parse error - unexpected '.'; expected end of input");
CHECK_THROWS_WITH(json::parser("-1E").parse(),
"parse error - unexpected 'E'; expected end of input");
CHECK_THROWS_WITH(json::parser("-0E#").parse(),
"parse error - unexpected 'E'; expected end of input");
CHECK_THROWS_WITH(json::parser("-0E-#").parse(),
"parse error - unexpected 'E'; expected end of input");
CHECK_THROWS_WITH(json::parser("-0#").parse(),
"parse error - unexpected '#'; expected end of input");
CHECK_THROWS_WITH(json::parser("-0.0:").parse(),
"parse error - unexpected ':'; expected end of input");
CHECK_THROWS_WITH(json::parser("-0.0Z").parse(),
"parse error - unexpected 'Z'; expected end of input");
CHECK_THROWS_WITH(json::parser("-0E123:").parse(),
"parse error - unexpected ':'; expected end of input");
CHECK_THROWS_WITH(json::parser("-0e0-:").parse(),
"parse error - unexpected '-'; expected end of input");
CHECK_THROWS_WITH(json::parser("-0e-:").parse(),
"parse error - unexpected 'e'; expected end of input");
CHECK_THROWS_WITH(json::parser("-0f").parse(),
"parse error - unexpected 'f'; expected end of input");
}
}
}
SECTION("parse errors")
{
// unexpected end of number
CHECK_THROWS_AS(json::parser("0.").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("-").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("--").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("-0.").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("-.").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("-:").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("0.:").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("e.").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("1e.").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("1e/").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("1e:").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("1E.").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("1E/").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("1E:").parse(), std::invalid_argument);
CHECK_THROWS_WITH(json::parser("0.").parse(),
"parse error - unexpected '.'; expected end of input");
CHECK_THROWS_WITH(json::parser("-").parse(), "parse error - unexpected '-'");
CHECK_THROWS_WITH(json::parser("--").parse(),
"parse error - unexpected '-'");
CHECK_THROWS_WITH(json::parser("-0.").parse(),
"parse error - unexpected '.'; expected end of input");
CHECK_THROWS_WITH(json::parser("-.").parse(),
"parse error - unexpected '-'");
CHECK_THROWS_WITH(json::parser("-:").parse(),
"parse error - unexpected '-'");
CHECK_THROWS_WITH(json::parser("0.:").parse(),
"parse error - unexpected '.'; expected end of input");
CHECK_THROWS_WITH(json::parser("e.").parse(),
"parse error - unexpected 'e'");
CHECK_THROWS_WITH(json::parser("1e.").parse(),
"parse error - unexpected 'e'; expected end of input");
CHECK_THROWS_WITH(json::parser("1e/").parse(),
"parse error - unexpected 'e'; expected end of input");
CHECK_THROWS_WITH(json::parser("1e:").parse(),
"parse error - unexpected 'e'; expected end of input");
CHECK_THROWS_WITH(json::parser("1E.").parse(),
"parse error - unexpected 'E'; expected end of input");
CHECK_THROWS_WITH(json::parser("1E/").parse(),
"parse error - unexpected 'E'; expected end of input");
CHECK_THROWS_WITH(json::parser("1E:").parse(),
"parse error - unexpected 'E'; expected end of input");
// unexpected end of null
CHECK_THROWS_AS(json::parser("n").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("nu").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("nul").parse(), std::invalid_argument);
CHECK_THROWS_WITH(json::parser("n").parse(), "parse error - unexpected 'n'");
CHECK_THROWS_WITH(json::parser("nu").parse(),
"parse error - unexpected 'n'");
CHECK_THROWS_WITH(json::parser("nul").parse(),
"parse error - unexpected 'n'");
// unexpected end of true
CHECK_THROWS_AS(json::parser("t").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("tr").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("tru").parse(), std::invalid_argument);
CHECK_THROWS_WITH(json::parser("t").parse(), "parse error - unexpected 't'");
CHECK_THROWS_WITH(json::parser("tr").parse(),
"parse error - unexpected 't'");
CHECK_THROWS_WITH(json::parser("tru").parse(),
"parse error - unexpected 't'");
// unexpected end of false
CHECK_THROWS_AS(json::parser("f").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("fa").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("fal").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("fals").parse(), std::invalid_argument);
CHECK_THROWS_WITH(json::parser("f").parse(), "parse error - unexpected 'f'");
CHECK_THROWS_WITH(json::parser("fa").parse(),
"parse error - unexpected 'f'");
CHECK_THROWS_WITH(json::parser("fal").parse(),
"parse error - unexpected 'f'");
CHECK_THROWS_WITH(json::parser("fals").parse(),
"parse error - unexpected 'f'");
// missing/unexpected end of array
CHECK_THROWS_AS(json::parser("[").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("[1").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("[1,").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("[1,]").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("]").parse(), std::invalid_argument);
CHECK_THROWS_WITH(json::parser("[").parse(),
"parse error - unexpected end of input");
CHECK_THROWS_WITH(json::parser("[1").parse(),
"parse error - unexpected end of input; expected ']'");
CHECK_THROWS_WITH(json::parser("[1,").parse(),
"parse error - unexpected end of input");
CHECK_THROWS_WITH(json::parser("[1,]").parse(),
"parse error - unexpected ']'");
CHECK_THROWS_WITH(json::parser("]").parse(), "parse error - unexpected ']'");
// missing/unexpected end of object
CHECK_THROWS_AS(json::parser("{").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("{\"foo\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("{\"foo\":").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("{\"foo\":}").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("{\"foo\":1,}").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("}").parse(), std::invalid_argument);
CHECK_THROWS_WITH(json::parser("{").parse(),
"parse error - unexpected end of input; expected string literal");
CHECK_THROWS_WITH(json::parser("{\"foo\"").parse(),
"parse error - unexpected end of input; expected ':'");
CHECK_THROWS_WITH(json::parser("{\"foo\":").parse(),
"parse error - unexpected end of input");
CHECK_THROWS_WITH(json::parser("{\"foo\":}").parse(),
"parse error - unexpected '}'");
CHECK_THROWS_WITH(json::parser("{\"foo\":1,}").parse(),
"parse error - unexpected '}'; expected string literal");
CHECK_THROWS_WITH(json::parser("}").parse(), "parse error - unexpected '}'");
// missing/unexpected end of string
CHECK_THROWS_AS(json::parser("\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\\\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\\u\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\\u0\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\\u01\"").parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser("\"\\u012\"").parse(), std::invalid_argument);
CHECK_THROWS_WITH(json::parser("\"").parse(),
"parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser("\"\\\"").parse(),
"parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser("\"\\u\"").parse(),
"parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser("\"\\u0\"").parse(),
"parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser("\"\\u01\"").parse(),
"parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser("\"\\u012\"").parse(),
"parse error - unexpected '\"'");
// invalid escapes
for (int c = 1; c < 128; ++c)
{
auto s = std::string("\"\\") + std::string(1, c) + "\"";
switch (c)
{
// valid escapes
case ('"'):
case ('\\'):
case ('/'):
case ('b'):
case ('f'):
case ('n'):
case ('r'):
case ('t'):
{
CHECK_NOTHROW(json::parser(s).parse());
break;
}
// \u must be followed with four numbers, so we skip it here
case ('u'):
{
break;
}
// any other combination of backslash and character is invalid
default:
{
CHECK_THROWS_AS(json::parser(s).parse(), std::invalid_argument);
CHECK_THROWS_WITH(json::parser(s).parse(), "parse error - unexpected '\"'");
break;
}
}
}
// invalid \uxxxx escapes
{
// check whether character is a valid hex character
const auto valid = [](int c)
{
switch (c)
{
case ('0'):
case ('1'):
case ('2'):
case ('3'):
case ('4'):
case ('5'):
case ('6'):
case ('7'):
case ('8'):
case ('9'):
case ('a'):
case ('b'):
case ('c'):
case ('d'):
case ('e'):
case ('f'):
case ('A'):
case ('B'):
case ('C'):
case ('D'):
case ('E'):
case ('F'):
{
return true;
}
default:
{
return false;
}
}
};
for (int c = 1; c < 128; ++c)
{
std::string s = "\"\\u";
// create a string with the iterated character at each position
auto s1 = s + "000" + std::string(1, c) + "\"";
auto s2 = s + "00" + std::string(1, c) + "0\"";
auto s3 = s + "0" + std::string(1, c) + "00\"";
auto s4 = s + std::string(1, c) + "000\"";
if (valid(c))
{
CHECK_NOTHROW(json::parser(s1).parse());
CHECK_NOTHROW(json::parser(s2).parse());
CHECK_NOTHROW(json::parser(s3).parse());
CHECK_NOTHROW(json::parser(s4).parse());
}
else
{
CHECK_THROWS_AS(json::parser(s1).parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser(s2).parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser(s3).parse(), std::invalid_argument);
CHECK_THROWS_AS(json::parser(s4).parse(), std::invalid_argument);
CHECK_THROWS_WITH(json::parser(s1).parse(), "parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser(s2).parse(), "parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser(s3).parse(), "parse error - unexpected '\"'");
CHECK_THROWS_WITH(json::parser(s4).parse(), "parse error - unexpected '\"'");
}
}
}
// missing part of a surrogate pair
CHECK_THROWS_AS(json::parse("\"\\uD80C\""), std::invalid_argument);
CHECK_THROWS_WITH(json::parse("\"\\uD80C\""), "missing low surrogate");
// invalid surrogate pair
CHECK_THROWS_AS(json::parse("\"\\uD80C\\uD80C\""), std::invalid_argument);
CHECK_THROWS_AS(json::parse("\"\\uD80C\\u0000\""), std::invalid_argument);
CHECK_THROWS_AS(json::parse("\"\\uD80C\\uFFFF\""), std::invalid_argument);
CHECK_THROWS_WITH(json::parse("\"\\uD80C\\uD80C\""),
"missing or wrong low surrogate");
CHECK_THROWS_WITH(json::parse("\"\\uD80C\\u0000\""),
"missing or wrong low surrogate");
CHECK_THROWS_WITH(json::parse("\"\\uD80C\\uFFFF\""),
"missing or wrong low surrogate");
}
SECTION("callback function")
{
auto s_object = R"(
{
"foo": 2,
"bar": {
"baz": 1
}
}
)";
auto s_array = R"(
[1,2,[3,4,5],4,5]
)";
SECTION("filter nothing")
{
json j_object = json::parse(s_object, [](int, json::parse_event_t, const json&)
{
return true;
});
CHECK (j_object == json({{"foo", 2}, {"bar", {{"baz", 1}}}}));
json j_array = json::parse(s_array, [](int, json::parse_event_t, const json&)
{
return true;
});
CHECK (j_array == json({1, 2, {3, 4, 5}, 4, 5}));
}
SECTION("filter everything")
{
json j_object = json::parse(s_object, [](int, json::parse_event_t, const json&)
{
return false;
});
// the top-level object will be discarded, leaving a null
CHECK (j_object.is_null());
json j_array = json::parse(s_array, [](int, json::parse_event_t, const json&)
{
return false;
});
// the top-level array will be discarded, leaving a null
CHECK (j_array.is_null());
}
SECTION("filter specific element")
{
json j_object = json::parse(s_object, [](int, json::parse_event_t, const json & j)
{
// filter all number(2) elements
if (j == json(2))
{
return false;
}
else
{
return true;
}
});
CHECK (j_object == json({{"bar", {{"baz", 1}}}}));
json j_array = json::parse(s_array, [](int, json::parse_event_t, const json & j)
{
if (j == json(2))
{
return false;
}
else
{
return true;
}
});
CHECK (j_array == json({1, {3, 4, 5}, 4, 5}));
}
SECTION("filter specific events")
{
SECTION("first closing event")
{
{
json j_object = json::parse(s_object, [](int, json::parse_event_t e, const json&)
{
static bool first = true;
if (e == json::parse_event_t::object_end and first)
{
first = false;
return false;
}
else
{
return true;
}
});
// the first completed object will be discarded
CHECK (j_object == json({{"foo", 2}}));
}
{
json j_array = json::parse(s_array, [](int, json::parse_event_t e, const json&)
{
static bool first = true;
if (e == json::parse_event_t::array_end and first)
{
first = false;
return false;
}
else
{
return true;
}
});
// the first completed array will be discarded
CHECK (j_array == json({1, 2, 4, 5}));
}
}
}
SECTION("special cases")
{
// the following test cases cover the situation in which an empty
// object and array is discarded only after the closing character
// has been read
json j_empty_object = json::parse("{}", [](int, json::parse_event_t e, const json&)
{
if (e == json::parse_event_t::object_end)
{
return false;
}
else
{
return true;
}
});
CHECK(j_empty_object == json());
json j_empty_array = json::parse("[]", [](int, json::parse_event_t e, const json&)
{
if (e == json::parse_event_t::array_end)
{
return false;
}
else
{
return true;
}
});
CHECK(j_empty_array == json());
}
}
SECTION("copy constructor")
{
json::string_t* s = new json::string_t("[1,2,3,4]");
json::parser p(*s);
delete s;
CHECK(p.parse() == json({1, 2, 3, 4}));
}
}
TEST_CASE("README", "[hide]")
{
{
// redirect std::cout for the README file
auto old_cout_buffer = std::cout.rdbuf();
std::ostringstream new_stream;
std::cout.rdbuf(new_stream.rdbuf());
{
// create an empty structure (null)
json j;
// add a number that is stored as double (note the implicit conversion of j to an object)
j["pi"] = 3.141;
// add a Boolean that is stored as bool
j["happy"] = true;
// add a string that is stored as std::string
j["name"] = "Niels";
// add another null object by passing nullptr
j["nothing"] = nullptr;
// add an object inside the object
j["answer"]["everything"] = 42;
// add an array that is stored as std::vector (using an initializer list)
j["list"] = { 1, 0, 2 };
// add another object (using an initializer list of pairs)
j["object"] = { {"currency", "USD"}, {"value", 42.99} };
// instead, you could also write (which looks very similar to the JSON above)
json j2 =
{
{"pi", 3.141},
{"happy", true},
{"name", "Niels"},
{"nothing", nullptr},
{
"answer", {
{"everything", 42}
}
},
{"list", {1, 0, 2}},
{
"object", {
{"currency", "USD"},
{"value", 42.99}
}
}
};
}
{
// ways to express the empty array []
json empty_array_implicit = {{}};
json empty_array_explicit = json::array();
// a way to express the empty object {}
json empty_object_explicit = json::object();
// a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]]
json array_not_object = { json::array({"currency", "USD"}), json::array({"value", 42.99}) };
}
{
// create object from string literal
json j = "{ \"happy\": true, \"pi\": 3.141 }"_json;
// or even nicer with a raw string literal
auto j2 = R"(
{
"happy": true,
"pi": 3.141
}
)"_json;
// or explicitly
auto j3 = json::parse("{ \"happy\": true, \"pi\": 3.141 }");
// explicit conversion to string
std::string s = j.dump(); // {\"happy\":true,\"pi\":3.141}
// serialization with pretty printing
// pass in the amount of spaces to indent
std::cout << j.dump(4) << std::endl;
// {
// "happy": true,
// "pi": 3.141
// }
std::cout << std::setw(2) << j << std::endl;
}
{
// create an array using push_back
json j;
j.push_back("foo");
j.push_back(1);
j.push_back(true);
// iterate the array
for (json::iterator it = j.begin(); it != j.end(); ++it)
{
std::cout << *it << '\n';
}
// range-based for
for (auto element : j)
{
std::cout << element << '\n';
}
// getter/setter
const std::string tmp = j[0];
j[1] = 42;
bool foo = j.at(2);
// other stuff
j.size(); // 3 entries
j.empty(); // false
j.type(); // json::value_t::array
j.clear(); // the array is empty again
// comparison
j == "[\"foo\", 1, true]"_json; // true
// create an object
json o;
o["foo"] = 23;
o["bar"] = false;
o["baz"] = 3.141;
// find an entry
if (o.find("foo") != o.end())
{
// there is an entry with key "foo"
}
}
{
std::vector<int> c_vector {1, 2, 3, 4};
json j_vec(c_vector);
// [1, 2, 3, 4]
std::deque<float> 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<bool> c_list {true, true, false, true};
json j_list(c_list);
// [true, true, false, true]
std::forward_list<int64_t> c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543};
json j_flist(c_flist);
// [12345678909876, 23456789098765, 34567890987654, 45678909876543]
std::array<unsigned long, 4> c_array {{1, 2, 3, 4}};
json j_array(c_array);
// [1, 2, 3, 4]
std::set<std::string> 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<std::string> 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<std::string> 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<std::string> c_umset {"one", "two", "one", "four"};
json j_umset(c_umset); // both entries for "one" are used
// maybe ["one", "two", "one", "four"]
}
{
std::map<std::string, int> c_map { {"one", 1}, {"two", 2}, {"three", 3} };
json j_map(c_map);
// {"one": 1, "two": 2, "three": 3}
std::unordered_map<const char*, float> 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<std::string, bool> 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<std::string, bool> 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<std::string>();
bool vb = jb.get<bool>();
int vi = jn.get<int>();
// etc.
}
{
// a JSON value
json j_original = R"({
"baz": ["one", "two", "three"],
"foo": "bar"
})"_json;
// access members with a JSON pointer (RFC 6901)
j_original["/baz/1"_json_pointer];
// "two"
// a JSON patch (RFC 6902)
json j_patch = R"([
{ "op": "replace", "path": "/baz", "value": "boo" },
{ "op": "add", "path": "/hello", "value": ["world"] },
{ "op": "remove", "path": "/foo"}
])"_json;
// apply the patch
json j_result = j_original.patch(j_patch);
// {
// "baz": "boo",
// "hello": ["world"]
// }
// calculate a JSON patch from two JSON values
json::diff(j_result, j_original);
// [
// { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] },
// { "op":"remove","path":"/hello" },
// { "op":"add","path":"/foo","value":"bar" }
// ]
}
// restore old std::cout
std::cout.rdbuf(old_cout_buffer);
}
}
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<std::string>() == "foo";
}));
CHECK(std::any_of(j_object.begin(), j_object.end(), [](const json & value)
{
return value.get<int>() > 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<int>() <= 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<int>(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<json::value_type, json>::value));
// X::reference must return lvalue of T
CHECK((std::is_same<json::reference, json&>::value));
// X::const_reference must return const lvalue of T
CHECK((std::is_same<json::const_reference, const json&>::value));
// X::iterator must return iterator whose value_type is T
CHECK((std::is_same<json::iterator::value_type, json>::value));
// X::iterator must meet the forward iterator requirements
CHECK((std::is_base_of<std::forward_iterator_tag, typename std::iterator_traits<json::iterator>::iterator_category>::value));
// X::iterator must be convertible to X::const_iterator
CHECK((std::is_convertible<json::iterator, json::const_iterator>::value));
// X::const_iterator must return iterator whose value_type is T
CHECK((std::is_same<json::const_iterator::value_type, json>::value));
// X::const_iterator must meet the forward iterator requirements
CHECK((std::is_base_of<std::forward_iterator_tag, typename std::iterator_traits<json::const_iterator>::iterator_category>::value));
// X::difference_type must return a signed integer
CHECK((std::is_signed<json::difference_type>::value));
// X::difference_type must be identical to X::iterator::difference_type
CHECK((std::is_same<json::difference_type, json::iterator::difference_type>::value));
// X::difference_type must be identical to X::const_iterator::difference_type
CHECK((std::is_same<json::difference_type, json::const_iterator::difference_type>::value));
// X::size_type must return an unsigned integer
CHECK((std::is_unsigned<json::size_type>::value));
// X::size_type can represent any non-negative value of X::difference_type
CHECK(std::numeric_limits<json::difference_type>::max() <=
std::numeric_limits<json::size_type>::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<json>::value);
}
SECTION("MoveConstructible")
{
CHECK(std::is_nothrow_move_constructible<json>::value);
}
SECTION("CopyConstructible")
{
CHECK(std::is_copy_constructible<json>::value);
}
SECTION("MoveAssignable")
{
CHECK(std::is_nothrow_move_assignable<json>::value);
}
SECTION("CopyAssignable")
{
CHECK(std::is_copy_assignable<json>::value);
}
SECTION("Destructible")
{
CHECK(std::is_nothrow_destructible<json>::value);
}
SECTION("StandardLayoutType")
{
CHECK(std::is_standard_layout<json>::value);
}
}
SECTION("class iterator")
{
SECTION("CopyConstructible")
{
CHECK(std::is_nothrow_copy_constructible<json::iterator>::value);
CHECK(std::is_nothrow_copy_constructible<json::const_iterator>::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<json::iterator>::value);
CHECK(std::is_nothrow_copy_assignable<json::const_iterator>::value);
#endif
}
SECTION("Destructible")
{
CHECK(std::is_nothrow_destructible<json::iterator>::value);
CHECK(std::is_nothrow_destructible<json::const_iterator>::value);
}
SECTION("Swappable")
{
{
json j {1, 2, 3};
json::iterator it1 = j.begin();
json::iterator it2 = j.end();
std::swap(it1, it2);
CHECK(it1 == j.end());
CHECK(it2 == j.begin());
}
{
json j {1, 2, 3};
json::const_iterator it1 = j.cbegin();
json::const_iterator it2 = j.cend();
std::swap(it1, it2);
CHECK(it1 == j.end());
CHECK(it2 == j.begin());
}
}
}
}
TEST_CASE("iterator_wrapper")
{
SECTION("object")
{
SECTION("value")
{
json j = {{"A", 1}, {"B", 2}};
int counter = 1;
for (auto i : json::iterator_wrapper(j))
{
switch (counter++)
{
case 1:
{
CHECK(i.key() == "A");
CHECK(i.value() == json(1));
break;
}
case 2:
{
CHECK(i.key() == "B");
CHECK(i.value() == json(2));
break;
}
default:
{
break;
}
}
}
CHECK(counter == 3);
}
SECTION("reference")
{
json j = {{"A", 1}, {"B", 2}};
int counter = 1;
for (auto& i : json::iterator_wrapper(j))
{
switch (counter++)
{
case 1:
{
CHECK(i.key() == "A");
CHECK(i.value() == json(1));
// change the value
i.value() = json(11);
CHECK(i.value() == json(11));
break;
}
case 2:
{
CHECK(i.key() == "B");
CHECK(i.value() == json(2));
// change the value
i.value() = json(22);
CHECK(i.value() == json(22));
break;
}
default:
{
break;
}
}
}
CHECK(counter == 3);
// check if values where changed
CHECK(j == json({{"A", 11}, {"B", 22}}));
}
SECTION("const value")
{
json j = {{"A", 1}, {"B", 2}};
int counter = 1;
for (const auto i : json::iterator_wrapper(j))
{
switch (counter++)
{
case 1:
{
CHECK(i.key() == "A");
CHECK(i.value() == json(1));
break;
}
case 2:
{
CHECK(i.key() == "B");
CHECK(i.value() == json(2));
break;
}
default:
{
break;
}
}
}
CHECK(counter == 3);
}
SECTION("const reference")
{
json j = {{"A", 1}, {"B", 2}};
int counter = 1;
for (const auto& i : json::iterator_wrapper(j))
{
switch (counter++)
{
case 1:
{
CHECK(i.key() == "A");
CHECK(i.value() == json(1));
break;
}
case 2:
{
CHECK(i.key() == "B");
CHECK(i.value() == json(2));
break;
}
default:
{
break;
}
}
}
CHECK(counter == 3);
}
}
SECTION("const object")
{
SECTION("value")
{
const json j = {{"A", 1}, {"B", 2}};
int counter = 1;
for (auto i : json::iterator_wrapper(j))
{
switch (counter++)
{
case 1:
{
CHECK(i.key() == "A");
CHECK(i.value() == json(1));
break;
}
case 2:
{
CHECK(i.key() == "B");
CHECK(i.value() == json(2));
break;
}
default:
{
break;
}
}
}
CHECK(counter == 3);
}
SECTION("reference")
{
const json j = {{"A", 1}, {"B", 2}};
int counter = 1;
for (auto& i : json::iterator_wrapper(j))
{
switch (counter++)
{
case 1:
{
CHECK(i.key() == "A");
CHECK(i.value() == json(1));
break;
}
case 2:
{
CHECK(i.key() == "B");
CHECK(i.value() == json(2));
break;
}
default:
{
break;
}
}
}
CHECK(counter == 3);
}
SECTION("const value")
{
const json j = {{"A", 1}, {"B", 2}};
int counter = 1;
for (const auto i : json::iterator_wrapper(j))
{
switch (counter++)
{
case 1:
{
CHECK(i.key() == "A");
CHECK(i.value() == json(1));
break;
}
case 2:
{
CHECK(i.key() == "B");
CHECK(i.value() == json(2));
break;
}
default:
{
break;
}
}
}
CHECK(counter == 3);
}
SECTION("const reference")
{
const json j = {{"A", 1}, {"B", 2}};
int counter = 1;
for (const auto& i : json::iterator_wrapper(j))
{
switch (counter++)
{
case 1:
{
CHECK(i.key() == "A");
CHECK(i.value() == json(1));
break;
}
case 2:
{
CHECK(i.key() == "B");
CHECK(i.value() == json(2));
break;
}
default:
{
break;
}
}
}
CHECK(counter == 3);
}
}
SECTION("array")
{
SECTION("value")
{
json j = {"A", "B"};
int counter = 1;
for (auto i : json::iterator_wrapper(j))
{
switch (counter++)
{
case 1:
{
CHECK(i.key() == "0");
CHECK(i.value() == "A");
break;
}
case 2:
{
CHECK(i.key() == "1");
CHECK(i.value() == "B");
break;
}
default:
{
break;
}
}
}
CHECK(counter == 3);
}
SECTION("reference")
{
json j = {"A", "B"};
int counter = 1;
for (auto& i : json::iterator_wrapper(j))
{
switch (counter++)
{
case 1:
{
CHECK(i.key() == "0");
CHECK(i.value() == "A");
// change the value
i.value() = "AA";
CHECK(i.value() == "AA");
break;
}
case 2:
{
CHECK(i.key() == "1");
CHECK(i.value() == "B");
// change the value
i.value() = "BB";
CHECK(i.value() == "BB");
break;
}
default:
{
break;
}
}
}
CHECK(counter == 3);
// check if values where changed
CHECK(j == json({"AA", "BB"}));
}
SECTION("const value")
{
json j = {"A", "B"};
int counter = 1;
for (const auto i : json::iterator_wrapper(j))
{
switch (counter++)
{
case 1:
{
CHECK(i.key() == "0");
CHECK(i.value() == "A");
break;
}
case 2:
{
CHECK(i.key() == "1");
CHECK(i.value() == "B");
break;
}
default:
{
break;
}
}
}
CHECK(counter == 3);
}
SECTION("const reference")
{
json j = {"A", "B"};
int counter = 1;
for (const auto& i : json::iterator_wrapper(j))
{
switch (counter++)
{
case 1:
{
CHECK(i.key() == "0");
CHECK(i.value() == "A");
break;
}
case 2:
{
CHECK(i.key() == "1");
CHECK(i.value() == "B");
break;
}
default:
{
break;
}
}
}
CHECK(counter == 3);
}
}
SECTION("const array")
{
SECTION("value")
{
const json j = {"A", "B"};
int counter = 1;
for (auto i : json::iterator_wrapper(j))
{
switch (counter++)
{
case 1:
{
CHECK(i.key() == "0");
CHECK(i.value() == "A");
break;
}
case 2:
{
CHECK(i.key() == "1");
CHECK(i.value() == "B");
break;
}
default:
{
break;
}
}
}
CHECK(counter == 3);
}
SECTION("reference")
{
const json j = {"A", "B"};
int counter = 1;
for (auto& i : json::iterator_wrapper(j))
{
switch (counter++)
{
case 1:
{
CHECK(i.key() == "0");
CHECK(i.value() == "A");
break;
}
case 2:
{
CHECK(i.key() == "1");
CHECK(i.value() == "B");
break;
}
default:
{
break;
}
}
}
CHECK(counter == 3);
}
SECTION("const value")
{
const json j = {"A", "B"};
int counter = 1;
for (const auto i : json::iterator_wrapper(j))
{
switch (counter++)
{
case 1:
{
CHECK(i.key() == "0");
CHECK(i.value() == "A");
break;
}
case 2:
{
CHECK(i.key() == "1");
CHECK(i.value() == "B");
break;
}
default:
{
break;
}
}
}
CHECK(counter == 3);
}
SECTION("const reference")
{
const json j = {"A", "B"};
int counter = 1;
for (const auto& i : json::iterator_wrapper(j))
{
switch (counter++)
{
case 1:
{
CHECK(i.key() == "0");
CHECK(i.value() == "A");
break;
}
case 2:
{
CHECK(i.key() == "1");
CHECK(i.value() == "B");
break;
}
default:
{
break;
}
}
}
CHECK(counter == 3);
}
}
SECTION("primitive")
{
SECTION("value")
{
json j = 1;
int counter = 1;
for (auto i : json::iterator_wrapper(j))
{
++counter;
CHECK(i.key() == "");
CHECK(i.value() == json(1));
}
CHECK(counter == 2);
}
SECTION("reference")
{
json j = 1;
int counter = 1;
for (auto& i : json::iterator_wrapper(j))
{
++counter;
CHECK(i.key() == "");
CHECK(i.value() == json(1));
// change value
i.value() = json(2);
}
CHECK(counter == 2);
// check if value has changed
CHECK(j == json(2));
}
SECTION("const value")
{
json j = 1;
int counter = 1;
for (const auto i : json::iterator_wrapper(j))
{
++counter;
CHECK(i.key() == "");
CHECK(i.value() == json(1));
}
CHECK(counter == 2);
}
SECTION("const reference")
{
json j = 1;
int counter = 1;
for (const auto& i : json::iterator_wrapper(j))
{
++counter;
CHECK(i.key() == "");
CHECK(i.value() == json(1));
}
CHECK(counter == 2);
}
}
SECTION("const primitive")
{
SECTION("value")
{
const json j = 1;
int counter = 1;
for (auto i : json::iterator_wrapper(j))
{
++counter;
CHECK(i.key() == "");
CHECK(i.value() == json(1));
}
CHECK(counter == 2);
}
SECTION("reference")
{
const json j = 1;
int counter = 1;
for (auto& i : json::iterator_wrapper(j))
{
++counter;
CHECK(i.key() == "");
CHECK(i.value() == json(1));
}
CHECK(counter == 2);
}
SECTION("const value")
{
const json j = 1;
int counter = 1;
for (const auto i : json::iterator_wrapper(j))
{
++counter;
CHECK(i.key() == "");
CHECK(i.value() == json(1));
}
CHECK(counter == 2);
}
SECTION("const reference")
{
const json j = 1;
int counter = 1;
for (const auto& i : json::iterator_wrapper(j))
{
++counter;
CHECK(i.key() == "");
CHECK(i.value() == json(1));
}
CHECK(counter == 2);
}
}
}
TEST_CASE("compliance tests from json.org")
{
// test cases are from http://json.org/JSON_checker/
SECTION("expected failures")
{
for (auto filename :
{
//"test/data/json_tests/fail1.json",
"test/data/json_tests/fail2.json",
"test/data/json_tests/fail3.json",
"test/data/json_tests/fail4.json",
"test/data/json_tests/fail5.json",
"test/data/json_tests/fail6.json",
"test/data/json_tests/fail7.json",
"test/data/json_tests/fail8.json",
"test/data/json_tests/fail9.json",
"test/data/json_tests/fail10.json",
"test/data/json_tests/fail11.json",
"test/data/json_tests/fail12.json",
"test/data/json_tests/fail13.json",
"test/data/json_tests/fail14.json",
"test/data/json_tests/fail15.json",
"test/data/json_tests/fail16.json",
"test/data/json_tests/fail17.json",
//"test/data/json_tests/fail18.json",
"test/data/json_tests/fail19.json",
"test/data/json_tests/fail20.json",
"test/data/json_tests/fail21.json",
"test/data/json_tests/fail22.json",
"test/data/json_tests/fail23.json",
"test/data/json_tests/fail24.json",
"test/data/json_tests/fail25.json",
"test/data/json_tests/fail26.json",
"test/data/json_tests/fail27.json",
"test/data/json_tests/fail28.json",
"test/data/json_tests/fail29.json",
"test/data/json_tests/fail30.json",
"test/data/json_tests/fail31.json",
"test/data/json_tests/fail32.json",
"test/data/json_tests/fail33.json"
})
{
CAPTURE(filename);
json j;
std::ifstream f(filename);
CHECK_THROWS_AS(j << f, std::invalid_argument);
}
}
SECTION("expected passes")
{
for (auto filename :
{
"test/data/json_tests/pass1.json",
"test/data/json_tests/pass2.json",
"test/data/json_tests/pass3.json"
})
{
CAPTURE(filename);
json j;
std::ifstream f(filename);
CHECK_NOTHROW(j << f);
}
}
}
TEST_CASE("compliance tests from nativejson-benchmark")
{
// test cases from https://github.com/miloyip/nativejson-benchmark/blob/master/src/main.cpp
SECTION("doubles")
{
auto TEST_DOUBLE = [](const std::string & json_string, const double expected)
{
CAPTURE(json_string);
CAPTURE(expected);
CHECK(json::parse(json_string)[0].get<double>() == 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<std::string>() == expected);
};
TEST_STRING("[\"\"]", "");
TEST_STRING("[\"Hello\"]", "Hello");
TEST_STRING("[\"Hello\\nWorld\"]", "Hello\nWorld");
//TEST_STRING("[\"Hello\\u0000World\"]", "Hello\0World");
TEST_STRING("[\"\\\"\\\\/\\b\\f\\n\\r\\t\"]", "\"\\/\b\f\n\r\t");
TEST_STRING("[\"\\u0024\"]", "\x24"); // Dollar sign U+0024
TEST_STRING("[\"\\u00A2\"]", "\xC2\xA2"); // Cents sign U+00A2
TEST_STRING("[\"\\u20AC\"]", "\xE2\x82\xAC"); // Euro sign U+20AC
TEST_STRING("[\"\\uD834\\uDD1E\"]", "\xF0\x9D\x84\x9E"); // G clef sign U+1D11E
}
SECTION("roundtrip")
{
// test cases are from https://github.com/miloyip/nativejson-benchmark/tree/master/test/data/roundtrip
for (auto filename :
{
"test/data/json_roundtrip/roundtrip01.json",
"test/data/json_roundtrip/roundtrip02.json",
"test/data/json_roundtrip/roundtrip03.json",
"test/data/json_roundtrip/roundtrip04.json",
"test/data/json_roundtrip/roundtrip05.json",
"test/data/json_roundtrip/roundtrip06.json",
"test/data/json_roundtrip/roundtrip07.json",
"test/data/json_roundtrip/roundtrip08.json",
"test/data/json_roundtrip/roundtrip09.json",
"test/data/json_roundtrip/roundtrip10.json",
"test/data/json_roundtrip/roundtrip11.json",
"test/data/json_roundtrip/roundtrip12.json",
"test/data/json_roundtrip/roundtrip13.json",
"test/data/json_roundtrip/roundtrip14.json",
"test/data/json_roundtrip/roundtrip15.json",
"test/data/json_roundtrip/roundtrip16.json",
"test/data/json_roundtrip/roundtrip17.json",
"test/data/json_roundtrip/roundtrip18.json",
"test/data/json_roundtrip/roundtrip19.json",
"test/data/json_roundtrip/roundtrip20.json",
"test/data/json_roundtrip/roundtrip21.json",
"test/data/json_roundtrip/roundtrip22.json",
"test/data/json_roundtrip/roundtrip23.json",
//"test/data/json_roundtrip/roundtrip24.json", // roundtrip error
//"test/data/json_roundtrip/roundtrip25.json", // roundtrip error
//"test/data/json_roundtrip/roundtrip26.json", // roundtrip error
//"test/data/json_roundtrip/roundtrip27.json", // roundtrip error
//"test/data/json_roundtrip/roundtrip28.json", // roundtrip error
"test/data/json_roundtrip/roundtrip29.json",
//"test/data/json_roundtrip/roundtrip30.json", // roundtrip error
//"test/data/json_roundtrip/roundtrip31.json", // roundtrip error
"test/data/json_roundtrip/roundtrip32.json"
})
{
CAPTURE(filename);
std::ifstream f(filename);
std::string json_string( (std::istreambuf_iterator<char>(f) ),
(std::istreambuf_iterator<char>()) );
json j = json::parse(json_string);
CHECK(j.dump() == json_string);
}
}
}
TEST_CASE("test suite from json-test-suite")
{
SECTION("read all sample.json")
{
// read a file with all unicode characters stored as single-character
// strings in a JSON array
std::ifstream f("test/data/json_testsuite/sample.json");
json j;
CHECK_NOTHROW(j << f);
// the array has 3 elements
CHECK(j.size() == 3);
}
}
TEST_CASE("json.org examples")
{
// here, we list all JSON values from http://json.org/example
SECTION("1.json")
{
std::ifstream f("test/data/json.org/1.json");
json j;
CHECK_NOTHROW(j << f);
}
SECTION("2.json")
{
std::ifstream f("test/data/json.org/2.json");
json j;
CHECK_NOTHROW(j << f);
}
SECTION("3.json")
{
std::ifstream f("test/data/json.org/3.json");
json j;
CHECK_NOTHROW(j << f);
}
SECTION("4.json")
{
std::ifstream f("test/data/json.org/4.json");
json j;
CHECK_NOTHROW(j << f);
}
SECTION("5.json")
{
std::ifstream f("test/data/json.org/5.json");
json j;
CHECK_NOTHROW(j << f);
}
}
TEST_CASE("RFC 7159 examples")
{
// here, we list all JSON values from the RFC 7159 document
SECTION("7. Strings")
{
CHECK(json::parse("\"\\u005C\"") == json("\\"));
CHECK(json::parse("\"\\uD834\\uDD1E\"") == json("𝄞"));
CHECK(json::parse("\"𝄞\"") == json("𝄞"));
}
SECTION("8.3 String Comparison")
{
CHECK(json::parse("\"a\\b\"") == json::parse("\"a\u005Cb\""));
}
SECTION("13 Examples")
{
{
CHECK_NOTHROW(json(R"(
{
"Image": {
"Width": 800,
"Height": 600,
"Title": "View from 15th Floor",
"Thumbnail": {
"Url": "http://www.example.com/image/481989943",
"Height": 125,
"Width": 100
},
"Animated" : false,
"IDs": [116, 943, 234, 38793]
}
}
)"));
}
{
CHECK_NOTHROW(json(R"(
[
{
"precision": "zip",
"Latitude": 37.7668,
"Longitude": -122.3959,
"Address": "",
"City": "SAN FRANCISCO",
"State": "CA",
"Zip": "94107",
"Country": "US"
},
{
"precision": "zip",
"Latitude": 37.371991,
"Longitude": -122.026020,
"Address": "",
"City": "SUNNYVALE",
"State": "CA",
"Zip": "94085",
"Country": "US"
}
])"));
}
CHECK(json::parse("\"Hello world!\"") == json("Hello world!"));
CHECK(json::parse("42") == json(42));
CHECK(json::parse("true") == json(true));
}
}
TEST_CASE("Unicode", "[hide]")
{
SECTION("full enumeration of Unicode code points")
{
// create an escaped string from a code point
const auto codepoint_to_unicode = [](std::size_t cp)
{
// copd points are represented as a six-character sequence: a
// reverse solidus, followed by the lowercase letter u, followed
// by four hexadecimal digits that encode the character's code
// point
std::stringstream ss;
ss << "\\u" << std::setw(4) << std::setfill('0') << std::hex << cp;
return ss.str();
};
// generate all UTF-8 code points; in total, 1112064 code points are
// generated: 0x1FFFFF code points - 2048 invalid values between
// 0xD800 and 0xDFFF.
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)
{
// if we would not skip these code points, we would get a
// "missing low surrogate" exception
continue;
}
// string to store the code point as in \uxxxx format
std::string escaped_string;
// string to store the code point as unescaped character sequence
std::string unescaped_string;
if (cp < 0x10000u)
{
// code points in the Basic Multilingual Plane can be
// represented with one \\uxxxx sequence
escaped_string = codepoint_to_unicode(cp);
// All Unicode characters may be placed within the quotation
// marks, except for the characters that must be escaped:
// quotation mark, reverse solidus, and the control characters
// (U+0000 through U+001F); we ignore these code points as
// they are checked with codepoint_to_unicode.
if (cp > 0x1f and cp != 0x22 and cp != 0x5c)
{
unescaped_string = json::lexer::to_unicode(cp);
}
}
else
{
// To escape an extended character that is not in the Basic
// Multilingual Plane, the character is represented as a
// 12-character sequence, encoding the UTF-16 surrogate pair
const auto codepoint1 = 0xd800u + (((cp - 0x10000u) >> 10) & 0x3ffu);
const auto codepoint2 = 0xdc00u + ((cp - 0x10000u) & 0x3ffu);
escaped_string = codepoint_to_unicode(codepoint1);
escaped_string += codepoint_to_unicode(codepoint2);
unescaped_string += json::lexer::to_unicode(codepoint1, codepoint2);
}
// all other code points are valid and must not yield parse errors
CAPTURE(cp);
CAPTURE(escaped_string);
CAPTURE(unescaped_string);
json j1, j2, j3, j4;
CHECK_NOTHROW(j1 = json::parse("\"" + escaped_string + "\""));
CHECK_NOTHROW(j2 = json::parse(j1.dump()));
CHECK(j1 == j2);
CHECK_NOTHROW(j3 = json::parse("\"" + unescaped_string + "\""));
CHECK_NOTHROW(j4 = json::parse(j3.dump()));
CHECK(j3 == j4);
}
}
SECTION("read all unicode characters")
{
// read a file with all unicode characters stored as single-character
// strings in a JSON array
std::ifstream f("test/data/json_nlohmann_tests/all_unicode.json");
json j;
CHECK_NOTHROW(j << f);
// the array has 1112064 + 1 elemnts (a terminating "null" value)
// Note: 1112064 = 0x1FFFFF code points - 2048 invalid values between
// 0xD800 and 0xDFFF.
CHECK(j.size() == 1112065);
SECTION("check JSON Pointers")
{
for (auto s : j)
{
// skip non-string JSON values
if (not s.is_string())
{
continue;
}
std::string ptr = s;
// tilde must be followed by 0 or 1
if (ptr == "~")
{
ptr += "0";
}
// JSON Pointers must begin with "/"
ptr = "/" + ptr;
CHECK_NOTHROW(json::json_pointer("/" + ptr));
// check escape/unescape roundtrip
auto escaped = json::json_pointer::escape(ptr);
json::json_pointer::unescape(escaped);
CHECK(escaped == ptr);
}
}
}
SECTION("ignore byte-order-mark")
{
// read a file with a UTF-8 BOM
std::ifstream f("test/data/json_nlohmann_tests/bom.json");
json j;
CHECK_NOTHROW(j << f);
}
SECTION("error for incomplete/wrong BOM")
{
CHECK_THROWS_AS(json::parse("\xef\xbb"), std::invalid_argument);
CHECK_THROWS_AS(json::parse("\xef\xbb\xbb"), std::invalid_argument);
}
}
TEST_CASE("JSON pointers")
{
SECTION("errors")
{
CHECK_THROWS_AS(json::json_pointer("foo"), std::domain_error);
CHECK_THROWS_WITH(json::json_pointer("foo"), "JSON pointer must be empty or begin with '/'");
CHECK_THROWS_AS(json::json_pointer("/~~"), std::domain_error);
CHECK_THROWS_WITH(json::json_pointer("/~~"), "escape error: '~' must be followed with '0' or '1'");
CHECK_THROWS_AS(json::json_pointer("/~"), std::domain_error);
CHECK_THROWS_WITH(json::json_pointer("/~"), "escape error: '~' must be followed with '0' or '1'");
json::json_pointer p;
CHECK_THROWS_AS(p.top(), std::domain_error);
CHECK_THROWS_WITH(p.top(), "JSON pointer has no parent");
CHECK_THROWS_AS(p.pop_back(), std::domain_error);
CHECK_THROWS_WITH(p.pop_back(), "JSON pointer has no parent");
}
SECTION("examples from RFC 6901")
{
SECTION("nonconst access")
{
json j = R"(
{
"foo": ["bar", "baz"],
"": 0,
"a/b": 1,
"c%d": 2,
"e^f": 3,
"g|h": 4,
"i\\j": 5,
"k\"l": 6,
" ": 7,
"m~n": 8
}
)"_json;
// the whole document
CHECK(j[json::json_pointer()] == j);
CHECK(j[json::json_pointer("")] == j);
// array access
CHECK(j[json::json_pointer("/foo")] == j["foo"]);
CHECK(j[json::json_pointer("/foo/0")] == j["foo"][0]);
CHECK(j[json::json_pointer("/foo/1")] == j["foo"][1]);
CHECK(j["/foo/1"_json_pointer] == j["foo"][1]);
// checked array access
CHECK(j.at(json::json_pointer("/foo/0")) == j["foo"][0]);
CHECK(j.at(json::json_pointer("/foo/1")) == j["foo"][1]);
// empty string access
CHECK(j[json::json_pointer("/")] == j[""]);
// other cases
CHECK(j[json::json_pointer("/ ")] == j[" "]);
CHECK(j[json::json_pointer("/c%d")] == j["c%d"]);
CHECK(j[json::json_pointer("/e^f")] == j["e^f"]);
CHECK(j[json::json_pointer("/g|h")] == j["g|h"]);
CHECK(j[json::json_pointer("/i\\j")] == j["i\\j"]);
CHECK(j[json::json_pointer("/k\"l")] == j["k\"l"]);
// checked access
CHECK(j.at(json::json_pointer("/ ")) == j[" "]);
CHECK(j.at(json::json_pointer("/c%d")) == j["c%d"]);
CHECK(j.at(json::json_pointer("/e^f")) == j["e^f"]);
CHECK(j.at(json::json_pointer("/g|h")) == j["g|h"]);
CHECK(j.at(json::json_pointer("/i\\j")) == j["i\\j"]);
CHECK(j.at(json::json_pointer("/k\"l")) == j["k\"l"]);
// escaped access
CHECK(j[json::json_pointer("/a~1b")] == j["a/b"]);
CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]);
// unescaped access
CHECK_THROWS_AS(j[json::json_pointer("/a/b")], std::out_of_range);
CHECK_THROWS_WITH(j[json::json_pointer("/a/b")], "unresolved reference token 'b'");
// "/a/b" works for JSON {"a": {"b": 42}}
CHECK(json({{"a", {{"b", 42}}}})[json::json_pointer("/a/b")] == json(42));
// unresolved access
json j_primitive = 1;
CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], std::out_of_range);
CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer], "unresolved reference token 'foo'");
CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), std::out_of_range);
CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer), "unresolved reference token 'foo'");
}
SECTION("const access")
{
const json j = R"(
{
"foo": ["bar", "baz"],
"": 0,
"a/b": 1,
"c%d": 2,
"e^f": 3,
"g|h": 4,
"i\\j": 5,
"k\"l": 6,
" ": 7,
"m~n": 8
}
)"_json;
// the whole document
CHECK(j[json::json_pointer()] == j);
CHECK(j[json::json_pointer("")] == j);
// array access
CHECK(j[json::json_pointer("/foo")] == j["foo"]);
CHECK(j[json::json_pointer("/foo/0")] == j["foo"][0]);
CHECK(j[json::json_pointer("/foo/1")] == j["foo"][1]);
CHECK(j["/foo/1"_json_pointer] == j["foo"][1]);
// checked array access
CHECK(j.at(json::json_pointer("/foo/0")) == j["foo"][0]);
CHECK(j.at(json::json_pointer("/foo/1")) == j["foo"][1]);
// empty string access
CHECK(j[json::json_pointer("/")] == j[""]);
// other cases
CHECK(j[json::json_pointer("/ ")] == j[" "]);
CHECK(j[json::json_pointer("/c%d")] == j["c%d"]);
CHECK(j[json::json_pointer("/e^f")] == j["e^f"]);
CHECK(j[json::json_pointer("/g|h")] == j["g|h"]);
CHECK(j[json::json_pointer("/i\\j")] == j["i\\j"]);
CHECK(j[json::json_pointer("/k\"l")] == j["k\"l"]);
// checked access
CHECK(j.at(json::json_pointer("/ ")) == j[" "]);
CHECK(j.at(json::json_pointer("/c%d")) == j["c%d"]);
CHECK(j.at(json::json_pointer("/e^f")) == j["e^f"]);
CHECK(j.at(json::json_pointer("/g|h")) == j["g|h"]);
CHECK(j.at(json::json_pointer("/i\\j")) == j["i\\j"]);
CHECK(j.at(json::json_pointer("/k\"l")) == j["k\"l"]);
// escaped access
CHECK(j[json::json_pointer("/a~1b")] == j["a/b"]);
CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]);
// unescaped access
CHECK_THROWS_AS(j.at(json::json_pointer("/a/b")), std::out_of_range);
CHECK_THROWS_WITH(j.at(json::json_pointer("/a/b")), "key 'a' not found");
// unresolved access
const json j_primitive = 1;
CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], std::out_of_range);
CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer], "unresolved reference token 'foo'");
CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), std::out_of_range);
CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer), "unresolved reference token 'foo'");
}
SECTION("user-defined string literal")
{
json j = R"(
{
"foo": ["bar", "baz"],
"": 0,
"a/b": 1,
"c%d": 2,
"e^f": 3,
"g|h": 4,
"i\\j": 5,
"k\"l": 6,
" ": 7,
"m~n": 8
}
)"_json;
// the whole document
CHECK(j[""_json_pointer] == j);
// array access
CHECK(j["/foo"_json_pointer] == j["foo"]);
CHECK(j["/foo/0"_json_pointer] == j["foo"][0]);
CHECK(j["/foo/1"_json_pointer] == j["foo"][1]);
}
}
SECTION("array access")
{
SECTION("nonconst access")
{
json j = {1, 2, 3};
const json j_const = j;
// check reading access
CHECK(j["/0"_json_pointer] == j[0]);
CHECK(j["/1"_json_pointer] == j[1]);
CHECK(j["/2"_json_pointer] == j[2]);
// assign to existing index
j["/1"_json_pointer] = 13;
CHECK(j[1] == json(13));
// assign to nonexisting index
j["/3"_json_pointer] = 33;
CHECK(j[3] == json(33));
// assign to nonexisting index (with gap)
j["/5"_json_pointer] = 55;
CHECK(j == json({1, 13, 3, 33, nullptr, 55}));
// error with leading 0
CHECK_THROWS_AS(j["/01"_json_pointer], std::domain_error);
CHECK_THROWS_WITH(j["/01"_json_pointer], "array index must not begin with '0'");
CHECK_THROWS_AS(j_const["/01"_json_pointer], std::domain_error);
CHECK_THROWS_WITH(j_const["/01"_json_pointer], "array index must not begin with '0'");
CHECK_THROWS_AS(j.at("/01"_json_pointer), std::domain_error);
CHECK_THROWS_WITH(j.at("/01"_json_pointer), "array index must not begin with '0'");
CHECK_THROWS_AS(j_const.at("/01"_json_pointer), std::domain_error);
CHECK_THROWS_WITH(j_const.at("/01"_json_pointer), "array index must not begin with '0'");
// error with incorrect numbers
CHECK_THROWS_AS(j["/one"_json_pointer] = 1, std::invalid_argument);
// assign to "-"
j["/-"_json_pointer] = 99;
CHECK(j == json({1, 13, 3, 33, nullptr, 55, 99}));
// error when using "-" in const object
CHECK_THROWS_AS(j_const["/-"_json_pointer], std::out_of_range);
CHECK_THROWS_WITH(j_const["/-"_json_pointer], "array index '-' (3) is out of range");
// error when using "-" with at
CHECK_THROWS_AS(j.at("/-"_json_pointer), std::out_of_range);
CHECK_THROWS_WITH(j.at("/-"_json_pointer), "array index '-' (7) is out of range");
CHECK_THROWS_AS(j_const.at("/-"_json_pointer), std::out_of_range);
CHECK_THROWS_WITH(j_const.at("/-"_json_pointer), "array index '-' (3) is out of range");
}
SECTION("const access")
{
const json j = {1, 2, 3};
// check reading access
CHECK(j["/0"_json_pointer] == j[0]);
CHECK(j["/1"_json_pointer] == j[1]);
CHECK(j["/2"_json_pointer] == j[2]);
// assign to nonexisting index
CHECK_THROWS_AS(j.at("/3"_json_pointer), std::out_of_range);
CHECK_THROWS_WITH(j.at("/3"_json_pointer), "array index 3 is out of range");
// assign to nonexisting index (with gap)
CHECK_THROWS_AS(j.at("/5"_json_pointer), std::out_of_range);
CHECK_THROWS_WITH(j.at("/5"_json_pointer), "array index 5 is out of range");
// assign to "-"
CHECK_THROWS_AS(j["/-"_json_pointer], std::out_of_range);
CHECK_THROWS_WITH(j["/-"_json_pointer], "array index '-' (3) is out of range");
CHECK_THROWS_AS(j.at("/-"_json_pointer), std::out_of_range);
CHECK_THROWS_WITH(j.at("/-"_json_pointer), "array index '-' (3) is out of range");
}
}
SECTION("flatten")
{
json j =
{
{"pi", 3.141},
{"happy", true},
{"name", "Niels"},
{"nothing", nullptr},
{
"answer", {
{"everything", 42}
}
},
{"list", {1, 0, 2}},
{
"object", {
{"currency", "USD"},
{"value", 42.99},
{"", "empty string"},
{"/", "slash"},
{"~", "tilde"},
{"~1", "tilde1"}
}
}
};
json j_flatten =
{
{"/pi", 3.141},
{"/happy", true},
{"/name", "Niels"},
{"/nothing", nullptr},
{"/answer/everything", 42},
{"/list/0", 1},
{"/list/1", 0},
{"/list/2", 2},
{"/object/currency", "USD"},
{"/object/value", 42.99},
{"/object/", "empty string"},
{"/object/~1", "slash"},
{"/object/~0", "tilde"},
{"/object/~01", "tilde1"}
};
// check if flattened result is as expected
CHECK(j.flatten() == j_flatten);
// check if unflattened result is as expected
CHECK(j_flatten.unflatten() == j);
// error for nonobjects
CHECK_THROWS_AS(json(1).unflatten(), std::domain_error);
CHECK_THROWS_WITH(json(1).unflatten(), "only objects can be unflattened");
// error for nonprimitve values
CHECK_THROWS_AS(json({{"/1", {1, 2, 3}}}).unflatten(), std::domain_error);
CHECK_THROWS_WITH(json({{"/1", {1, 2, 3}}}).unflatten(), "values in object must be primitive");
// error for conflicting values
json j_error = {{"", 42}, {"/foo", 17}};
CHECK_THROWS_AS(j_error.unflatten(), std::domain_error);
CHECK_THROWS_WITH(j_error.unflatten(), "invalid value to unflatten");
// explicit roundtrip check
CHECK(j.flatten().unflatten() == j);
// roundtrip for primitive values
json j_null;
CHECK(j_null.flatten().unflatten() == j_null);
json j_number = 42;
CHECK(j_number.flatten().unflatten() == j_number);
json j_boolean = false;
CHECK(j_boolean.flatten().unflatten() == j_boolean);
json j_string = "foo";
CHECK(j_string.flatten().unflatten() == j_string);
// roundtrip for empty structured values (will be unflattened to null)
json j_array(json::value_t::array);
CHECK(j_array.flatten().unflatten() == json());
json j_object(json::value_t::object);
CHECK(j_object.flatten().unflatten() == json());
}
SECTION("string representation")
{
for (auto ptr :
{"", "/foo", "/foo/0", "/", "/a~1b", "/c%d", "/e^f", "/g|h", "/i\\j", "/k\"l", "/ ", "/m~0n"
})
{
CHECK(json::json_pointer(ptr).to_string() == ptr);
}
}
}
TEST_CASE("JSON patch")
{
SECTION("examples from RFC 6902")
{
SECTION("4. Operations")
{
// the ordering of members in JSON objects is not significant:
json op1 = R"({ "op": "add", "path": "/a/b/c", "value": "foo" })"_json;
json op2 = R"({ "path": "/a/b/c", "op": "add", "value": "foo" })"_json;
json op3 = R"({ "value": "foo", "path": "/a/b/c", "op": "add" })"_json;
// check if the operation objects are equivalent
CHECK(op1 == op2);
CHECK(op1 == op3);
}
SECTION("4.1 add")
{
json patch = R"([{ "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] }])"_json;
// However, the object itself or an array containing it does need
// to exist, and it remains an error for that not to be the case.
// For example, an "add" with a target location of "/a/b" starting
// with this document
json doc1 = R"({ "a": { "foo": 1 } })"_json;
// is not an error, because "a" exists, and "b" will be added to
// its value.
CHECK_NOTHROW(doc1.patch(patch));
CHECK(doc1.patch(patch) == R"(
{
"a": {
"foo": 1,
"b": {
"c": [ "foo", "bar" ]
}
}
}
)"_json);
// It is an error in this document:
json doc2 = R"({ "q": { "bar": 2 } })"_json;
// because "a" does not exist.
CHECK_THROWS_AS(doc2.patch(patch), std::out_of_range);
CHECK_THROWS_WITH(doc2.patch(patch), "key 'a' not found");
}
SECTION("4.2 remove")
{
// If removing an element from an array, any elements above the
// specified index are shifted one position to the left.
json doc = {1, 2, 3, 4};
json patch = {{{"op", "remove"}, {"path", "/1"}}};
CHECK(doc.patch(patch) == json({1, 3, 4}));
}
SECTION("A.1. Adding an Object Member")
{
// An example target JSON document:
json doc = R"(
{ "foo": "bar"}
)"_json;
// A JSON Patch document:
json patch = R"(
[
{ "op": "add", "path": "/baz", "value": "qux" }
]
)"_json;
// The resulting JSON document:
json expected = R"(
{
"baz": "qux",
"foo": "bar"
}
)"_json;
// check if patched value is as expected
CHECK(doc.patch(patch) == expected);
// check roundtrip
CHECK(doc.patch(json::diff(doc, expected)) == expected);
}
SECTION("A.2. Adding an Array Element")
{
// An example target JSON document:
json doc = R"(
{ "foo": [ "bar", "baz" ] }
)"_json;
// A JSON Patch document:
json patch = R"(
[
{ "op": "add", "path": "/foo/1", "value": "qux" }
]
)"_json;
// The resulting JSON document:
json expected = R"(
{ "foo": [ "bar", "qux", "baz" ] }
)"_json;
// check if patched value is as expected
CHECK(doc.patch(patch) == expected);
// check roundtrip
CHECK(doc.patch(json::diff(doc, expected)) == expected);
}
SECTION("A.3. Removing an Object Member")
{
// An example target JSON document:
json doc = R"(
{
"baz": "qux",
"foo": "bar"
}
)"_json;
// A JSON Patch document:
json patch = R"(
[
{ "op": "remove", "path": "/baz" }
]
)"_json;
// The resulting JSON document:
json expected = R"(
{ "foo": "bar" }
)"_json;
// check if patched value is as expected
CHECK(doc.patch(patch) == expected);
// check roundtrip
CHECK(doc.patch(json::diff(doc, expected)) == expected);
}
SECTION("A.4. Removing an Array Element")
{
// An example target JSON document:
json doc = R"(
{ "foo": [ "bar", "qux", "baz" ] }
)"_json;
// A JSON Patch document:
json patch = R"(
[
{ "op": "remove", "path": "/foo/1" }
]
)"_json;
// The resulting JSON document:
json expected = R"(
{ "foo": [ "bar", "baz" ] }
)"_json;
// check if patched value is as expected
CHECK(doc.patch(patch) == expected);
// check roundtrip
CHECK(doc.patch(json::diff(doc, expected)) == expected);
}
SECTION("A.5. Replacing a Value")
{
// An example target JSON document:
json doc = R"(
{
"baz": "qux",
"foo": "bar"
}
)"_json;
// A JSON Patch document:
json patch = R"(
[
{ "op": "replace", "path": "/baz", "value": "boo" }
]
)"_json;
json expected = R"(
{
"baz": "boo",
"foo": "bar"
}
)"_json;
// check if patched value is as expected
CHECK(doc.patch(patch) == expected);
// check roundtrip
CHECK(doc.patch(json::diff(doc, expected)) == expected);
}
SECTION("A.6. Moving a Value")
{
// An example target JSON document:
json doc = R"(
{
"foo": {
"bar": "baz",
"waldo": "fred"
},
"qux": {
"corge": "grault"
}
}
)"_json;
// A JSON Patch document:
json patch = R"(
[
{ "op": "move", "from": "/foo/waldo", "path": "/qux/thud" }
]
)"_json;
// The resulting JSON document:
json expected = R"(
{
"foo": {
"bar": "baz"
},
"qux": {
"corge": "grault",
"thud": "fred"
}
}
)"_json;
// check if patched value is as expected
CHECK(doc.patch(patch) == expected);
// check roundtrip
CHECK(doc.patch(json::diff(doc, expected)) == expected);
}
SECTION("A.7. Moving a Value")
{
// An example target JSON document:
json doc = R"(
{ "foo": [ "all", "grass", "cows", "eat" ] }
)"_json;
// A JSON Patch document:
json patch = R"(
[
{ "op": "move", "from": "/foo/1", "path": "/foo/3" }
]
)"_json;
// The resulting JSON document:
json expected = R"(
{ "foo": [ "all", "cows", "eat", "grass" ] }
)"_json;
// check if patched value is as expected
CHECK(doc.patch(patch) == expected);
// check roundtrip
CHECK(doc.patch(json::diff(doc, expected)) == expected);
}
SECTION("A.8. Testing a Value: Success")
{
// An example target JSON document:
json doc = R"(
{
"baz": "qux",
"foo": [ "a", 2, "c" ]
}
)"_json;
// A JSON Patch document that will result in successful evaluation:
json patch = R"(
[
{ "op": "test", "path": "/baz", "value": "qux" },
{ "op": "test", "path": "/foo/1", "value": 2 }
]
)"_json;
// check if evaluation does not throw
CHECK_NOTHROW(doc.patch(patch));
// check if patched document is unchanged
CHECK(doc.patch(patch) == doc);
}
SECTION("A.9. Testing a Value: Error")
{
// An example target JSON document:
json doc = R"(
{ "baz": "qux" }
)"_json;
// A JSON Patch document that will result in an error condition:
json patch = R"(
[
{ "op": "test", "path": "/baz", "value": "bar" }
]
)"_json;
// check that evaluation throws
CHECK_THROWS_AS(doc.patch(patch), std::domain_error);
CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump());
}
SECTION("A.10. Adding a Nested Member Object")
{
// An example target JSON document:
json doc = R"(
{ "foo": "bar" }
)"_json;
// A JSON Patch document:
json patch = R"(
[
{ "op": "add", "path": "/child", "value": { "grandchild": { } } }
]
)"_json;
// The resulting JSON document:
json expected = R"(
{
"foo": "bar",
"child": {
"grandchild": {
}
}
}
)"_json;
// check if patched value is as expected
CHECK(doc.patch(patch) == expected);
// check roundtrip
CHECK(doc.patch(json::diff(doc, expected)) == expected);
}
SECTION("A.11. Ignoring Unrecognized Elements")
{
// An example target JSON document:
json doc = R"(
{ "foo": "bar" }
)"_json;
// A JSON Patch document:
json patch = R"(
[
{ "op": "add", "path": "/baz", "value": "qux", "xyz": 123 }
]
)"_json;
json expected = R"(
{
"foo": "bar",
"baz": "qux"
}
)"_json;
// check if patched value is as expected
CHECK(doc.patch(patch) == expected);
// check roundtrip
CHECK(doc.patch(json::diff(doc, expected)) == expected);
}
SECTION("A.12. Adding to a Nonexistent Target")
{
// An example target JSON document:
json doc = R"(
{ "foo": "bar" }
)"_json;
// A JSON Patch document:
json patch = R"(
[
{ "op": "add", "path": "/baz/bat", "value": "qux" }
]
)"_json;
// This JSON Patch document, applied to the target JSON document
// above, would result in an error (therefore, it would not be
// applied), because the "add" operation's target location that
// references neither the root of the document, nor a member of
// an existing object, nor a member of an existing array.
CHECK_THROWS_AS(doc.patch(patch), std::out_of_range);
CHECK_THROWS_WITH(doc.patch(patch), "key 'baz' not found");
}
// A.13. Invalid JSON Patch Document
// not applicable
SECTION("A.14. Escape Ordering")
{
// An example target JSON document:
json doc = R"(
{
"/": 9,
"~1": 10
}
)"_json;
// A JSON Patch document:
json patch = R"(
[
{"op": "test", "path": "/~01", "value": 10}
]
)"_json;
json expected = R"(
{
"/": 9,
"~1": 10
}
)"_json;
// check if patched value is as expected
CHECK(doc.patch(patch) == expected);
// check roundtrip
CHECK(doc.patch(json::diff(doc, expected)) == expected);
}
SECTION("A.15. Comparing Strings and Numbers")
{
// An example target JSON document:
json doc = R"(
{
"/": 9,
"~1": 10
}
)"_json;
// A JSON Patch document that will result in an error condition:
json patch = R"(
[
{"op": "test", "path": "/~01", "value": "10"}
]
)"_json;
// check that evaluation throws
CHECK_THROWS_AS(doc.patch(patch), std::domain_error);
CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump());
}
SECTION("A.16. Adding an Array Value")
{
// An example target JSON document:
json doc = R"(
{ "foo": ["bar"] }
)"_json;
// A JSON Patch document:
json patch = R"(
[
{ "op": "add", "path": "/foo/-", "value": ["abc", "def"] }
]
)"_json;
// The resulting JSON document:
json expected = R"(
{ "foo": ["bar", ["abc", "def"]] }
)"_json;
// check if patched value is as expected
CHECK(doc.patch(patch) == expected);
// check roundtrip
CHECK(doc.patch(json::diff(doc, expected)) == expected);
}
}
SECTION("own examples")
{
SECTION("add")
{
SECTION("add to the root element")
{
// If the path is the root of the target document - the
// specified value becomes the entire content of the target
// document.
// An example target JSON document:
json doc = 17;
// A JSON Patch document:
json patch = R"(
[
{ "op": "add", "path": "", "value": [1,2,3] }
]
)"_json;
// The resulting JSON document:
json expected = {1, 2, 3};
// check if patched value is as expected
CHECK(doc.patch(patch) == expected);
// check roundtrip
CHECK(doc.patch(json::diff(doc, expected)) == expected);
}
SECTION("add to end of the array")
{
// The specified index MUST NOT be greater than the number of
// elements in the array. The example below uses and index of
// exactly the number of elements in the array which is legal.
// An example target JSON document:
json doc = {0, 1, 2};
// A JSON Patch document:
json patch = R"(
[
{ "op": "add", "path": "/3", "value": 3 }
]
)"_json;
// The resulting JSON document:
json expected = {0, 1, 2, 3};
// check if patched value is as expected
CHECK(doc.patch(patch) == expected);
// check roundtrip
CHECK(doc.patch(json::diff(doc, expected)) == expected);
}
}
SECTION("copy")
{
// An example target JSON document:
json doc = R"(
{
"foo": {
"bar": "baz",
"waldo": "fred"
},
"qux": {
"corge": "grault"
}
}
)"_json;
// A JSON Patch document:
json patch = R"(
[
{ "op": "copy", "from": "/foo/waldo", "path": "/qux/thud" }
]
)"_json;
// The resulting JSON document:
json expected = R"(
{
"foo": {
"bar": "baz",
"waldo": "fred"
},
"qux": {
"corge": "grault",
"thud": "fred"
}
}
)"_json;
// check if patched value is as expected
CHECK(doc.patch(patch) == expected);
// check roundtrip
CHECK(doc.patch(json::diff(doc, expected)) == expected);
}
SECTION("replace")
{
json j = "string";
json patch = {{{"op", "replace"}, {"path", ""}, {"value", 1}}};
CHECK(j.patch(patch) == json(1));
}
SECTION("documentation GIF")
{
{
// a JSON patch
json p1 = R"(
[{"op": "add", "path": "/GB", "value": "London"}]
)"_json;
// a JSON value
json source = R"(
{"D": "Berlin", "F": "Paris"}
)"_json;
// apply the patch
json target = source.patch(p1);
// target = { "D": "Berlin", "F": "Paris", "GB": "London" }
CHECK(target == R"({ "D": "Berlin", "F": "Paris", "GB": "London" })"_json);
// create a diff from two JSONs
json p2 = json::diff(target, source);
// p2 = [{"op": "delete", "path": "/GB"}]
CHECK(p2 == R"([{"op":"remove","path":"/GB"}])"_json);
}
{
// a JSON value
json j = {"good", "bad", "ugly"};
// a JSON pointer
auto ptr = json::json_pointer("/2");
// use to access elements
j[ptr] = {{"it", "cattivo"}};
CHECK(j == R"(["good","bad",{"it":"cattivo"}])"_json);
// use user-defined string literal
j["/2/en"_json_pointer] = "ugly";
CHECK(j == R"(["good","bad",{"en":"ugly","it":"cattivo"}])"_json);
json flat = j.flatten();
CHECK(flat == R"({"/0":"good","/1":"bad","/2/en":"ugly","/2/it":"cattivo"})"_json);
}
}
}
SECTION("errors")
{
SECTION("unknown operation")
{
SECTION("not an array")
{
json j;
json patch = {{"op", "add"}, {"path", ""}, {"value", 1}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "JSON patch must be an array of objects");
}
SECTION("not an array of objects")
{
json j;
json patch = {"op", "add", "path", "", "value", 1};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "JSON patch must be an array of objects");
}
SECTION("missing 'op'")
{
json j;
json patch = {{{"foo", "bar"}}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "operation must have member 'op'");
}
SECTION("non-string 'op'")
{
json j;
json patch = {{{"op", 1}}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "operation must have string member 'op'");
}
SECTION("invalid operation")
{
json j;
json patch = {{{"op", "foo"}, {"path", ""}}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "operation value 'foo' is invalid");
}
}
SECTION("add")
{
SECTION("missing 'path'")
{
json j;
json patch = {{{"op", "add"}}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have member 'path'");
}
SECTION("non-string 'path'")
{
json j;
json patch = {{{"op", "add"}, {"path", 1}}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have string member 'path'");
}
SECTION("missing 'value'")
{
json j;
json patch = {{{"op", "add"}, {"path", ""}}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have member 'value'");
}
SECTION("invalid array index")
{
json j = {1, 2};
json patch = {{{"op", "add"}, {"path", "/4"}, {"value", 4}}};
CHECK_THROWS_AS(j.patch(patch), std::out_of_range);
CHECK_THROWS_WITH(j.patch(patch), "array index 4 is out of range");
}
}
SECTION("remove")
{
SECTION("missing 'path'")
{
json j;
json patch = {{{"op", "remove"}}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "operation 'remove' must have member 'path'");
}
SECTION("non-string 'path'")
{
json j;
json patch = {{{"op", "remove"}, {"path", 1}}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "operation 'remove' must have string member 'path'");
}
SECTION("nonexisting target location (array)")
{
json j = {1, 2, 3};
json patch = {{{"op", "remove"}, {"path", "/17"}}};
CHECK_THROWS_AS(j.patch(patch), std::out_of_range);
CHECK_THROWS_WITH(j.patch(patch), "array index 17 is out of range");
}
SECTION("nonexisting target location (object)")
{
json j = {{"foo", 1}, {"bar", 2}};
json patch = {{{"op", "remove"}, {"path", "/baz"}}};
CHECK_THROWS_AS(j.patch(patch), std::out_of_range);
CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found");
}
SECTION("root element as target location")
{
json j = "string";
json patch = {{{"op", "remove"}, {"path", ""}}};
CHECK_THROWS_AS(j.patch(patch), std::domain_error);
CHECK_THROWS_WITH(j.patch(patch), "JSON pointer has no parent");
}
}
SECTION("replace")
{
SECTION("missing 'path'")
{
json j;
json patch = {{{"op", "replace"}}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have member 'path'");
}
SECTION("non-string 'path'")
{
json j;
json patch = {{{"op", "replace"}, {"path", 1}}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have string member 'path'");
}
SECTION("missing 'value'")
{
json j;
json patch = {{{"op", "replace"}, {"path", ""}}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have member 'value'");
}
SECTION("nonexisting target location (array)")
{
json j = {1, 2, 3};
json patch = {{{"op", "replace"}, {"path", "/17"}, {"value", 19}}};
CHECK_THROWS_AS(j.patch(patch), std::out_of_range);
CHECK_THROWS_WITH(j.patch(patch), "array index 17 is out of range");
}
SECTION("nonexisting target location (object)")
{
json j = {{"foo", 1}, {"bar", 2}};
json patch = {{{"op", "replace"}, {"path", "/baz"}, {"value", 3}}};
CHECK_THROWS_AS(j.patch(patch), std::out_of_range);
CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found");
}
}
SECTION("move")
{
SECTION("missing 'path'")
{
json j;
json patch = {{{"op", "move"}}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have member 'path'");
}
SECTION("non-string 'path'")
{
json j;
json patch = {{{"op", "move"}, {"path", 1}}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have string member 'path'");
}
SECTION("missing 'from'")
{
json j;
json patch = {{{"op", "move"}, {"path", ""}}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have member 'from'");
}
SECTION("non-string 'from'")
{
json j;
json patch = {{{"op", "move"}, {"path", ""}, {"from", 1}}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have string member 'from'");
}
SECTION("nonexisting from location (array)")
{
json j = {1, 2, 3};
json patch = {{{"op", "move"}, {"path", "/0"}, {"from", "/5"}}};
CHECK_THROWS_AS(j.patch(patch), std::out_of_range);
CHECK_THROWS_WITH(j.patch(patch), "array index 5 is out of range");
}
SECTION("nonexisting from location (object)")
{
json j = {{"foo", 1}, {"bar", 2}};
json patch = {{{"op", "move"}, {"path", "/baz"}, {"from", "/baz"}}};
CHECK_THROWS_AS(j.patch(patch), std::out_of_range);
CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found");
}
}
SECTION("copy")
{
SECTION("missing 'path'")
{
json j;
json patch = {{{"op", "copy"}}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have member 'path'");
}
SECTION("non-string 'path'")
{
json j;
json patch = {{{"op", "copy"}, {"path", 1}}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have string member 'path'");
}
SECTION("missing 'from'")
{
json j;
json patch = {{{"op", "copy"}, {"path", ""}}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have member 'from'");
}
SECTION("non-string 'from'")
{
json j;
json patch = {{{"op", "copy"}, {"path", ""}, {"from", 1}}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have string member 'from'");
}
SECTION("nonexisting from location (array)")
{
json j = {1, 2, 3};
json patch = {{{"op", "copy"}, {"path", "/0"}, {"from", "/5"}}};
CHECK_THROWS_AS(j.patch(patch), std::out_of_range);
CHECK_THROWS_WITH(j.patch(patch), "array index 5 is out of range");
}
SECTION("nonexisting from location (object)")
{
json j = {{"foo", 1}, {"bar", 2}};
json patch = {{{"op", "copy"}, {"path", "/fob"}, {"from", "/baz"}}};
CHECK_THROWS_AS(j.patch(patch), std::out_of_range);
CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found");
}
}
SECTION("test")
{
SECTION("missing 'path'")
{
json j;
json patch = {{{"op", "test"}}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have member 'path'");
}
SECTION("non-string 'path'")
{
json j;
json patch = {{{"op", "test"}, {"path", 1}}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have string member 'path'");
}
SECTION("missing 'value'")
{
json j;
json patch = {{{"op", "test"}, {"path", ""}}};
CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have member 'value'");
}
}
}
SECTION("Examples from jsonpatch.com")
{
SECTION("Simple Example")
{
// The original document
json doc = R"(
{
"baz": "qux",
"foo": "bar"
}
)"_json;
// The patch
json patch = R"(
[
{ "op": "replace", "path": "/baz", "value": "boo" },
{ "op": "add", "path": "/hello", "value": ["world"] },
{ "op": "remove", "path": "/foo"}
]
)"_json;
// The result
json result = R"(
{
"baz": "boo",
"hello": ["world"]
}
)"_json;
// check if patched value is as expected
CHECK(doc.patch(patch) == result);
// check roundtrip
CHECK(doc.patch(json::diff(doc, result)) == result);
}
SECTION("Operations")
{
// The original document
json doc = R"(
{
"biscuits": [
{"name":"Digestive"},
{"name": "Choco Liebniz"}
]
}
)"_json;
SECTION("add")
{
// The patch
json patch = R"(
[
{"op": "add", "path": "/biscuits/1", "value": {"name": "Ginger Nut"}}
]
)"_json;
// The result
json result = R"(
{
"biscuits": [
{"name": "Digestive"},
{"name": "Ginger Nut"},
{"name": "Choco Liebniz"}
]
}
)"_json;
// check if patched value is as expected
CHECK(doc.patch(patch) == result);
// check roundtrip
CHECK(doc.patch(json::diff(doc, result)) == result);
}
SECTION("remove")
{
// The patch
json patch = R"(
[
{"op": "remove", "path": "/biscuits"}
]
)"_json;
// The result
json result = R"(
{}
)"_json;
// check if patched value is as expected
CHECK(doc.patch(patch) == result);
// check roundtrip
CHECK(doc.patch(json::diff(doc, result)) == result);
}
SECTION("replace")
{
// The patch
json patch = R"(
[
{"op": "replace", "path": "/biscuits/0/name", "value": "Chocolate Digestive"}
]
)"_json;
// The result
json result = R"(
{
"biscuits": [
{"name": "Chocolate Digestive"},
{"name": "Choco Liebniz"}
]
}
)"_json;
// check if patched value is as expected
CHECK(doc.patch(patch) == result);
// check roundtrip
CHECK(doc.patch(json::diff(doc, result)) == result);
}
SECTION("copy")
{
// The patch
json patch = R"(
[
{"op": "copy", "from": "/biscuits/0", "path": "/best_biscuit"}
]
)"_json;
// The result
json result = R"(
{
"biscuits": [
{"name": "Digestive"},
{"name": "Choco Liebniz"}
],
"best_biscuit": {
"name": "Digestive"
}
}
)"_json;
// check if patched value is as expected
CHECK(doc.patch(patch) == result);
// check roundtrip
CHECK(doc.patch(json::diff(doc, result)) == result);
}
SECTION("move")
{
// The patch
json patch = R"(
[
{"op": "move", "from": "/biscuits", "path": "/cookies"}
]
)"_json;
// The result
json result = R"(
{
"cookies": [
{"name": "Digestive"},
{"name": "Choco Liebniz"}
]
}
)"_json;
// check if patched value is as expected
CHECK(doc.patch(patch) == result);
// check roundtrip
CHECK(doc.patch(json::diff(doc, result)) == result);
}
SECTION("test")
{
// The patch
json patch = R"(
[
{"op": "test", "path": "/best_biscuit/name", "value": "Choco Liebniz"}
]
)"_json;
// the test will fail
CHECK_THROWS_AS(doc.patch(patch), std::domain_error);
CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump());
}
}
}
SECTION("Examples from bruth.github.io/jsonpatch-js")
{
SECTION("add")
{
CHECK(R"( {} )"_json.patch(
R"( [{"op": "add", "path": "/foo", "value": "bar"}] )"_json
) == R"( {"foo": "bar"} )"_json);
CHECK(R"( {"foo": [1, 3]} )"_json.patch(
R"( [{"op": "add", "path": "/foo", "value": "bar"}] )"_json
) == R"( {"foo": "bar"} )"_json);
CHECK(R"( {"foo": [{}]} )"_json.patch(
R"( [{"op": "add", "path": "/foo/0/bar", "value": "baz"}] )"_json
) == R"( {"foo": [{"bar": "baz"}]} )"_json);
}
SECTION("remove")
{
CHECK(R"( {"foo": "bar"} )"_json.patch(
R"( [{"op": "remove", "path": "/foo"}] )"_json
) == R"( {} )"_json);
CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch(
R"( [{"op": "remove", "path": "/foo/1"}] )"_json
) == R"( {"foo": [1, 3]} )"_json);
CHECK(R"( {"foo": [{"bar": "baz"}]} )"_json.patch(
R"( [{"op": "remove", "path": "/foo/0/bar"}] )"_json
) == R"( {"foo": [{}]} )"_json);
}
SECTION("replace")
{
CHECK(R"( {"foo": "bar"} )"_json.patch(
R"( [{"op": "replace", "path": "/foo", "value": 1}] )"_json
) == R"( {"foo": 1} )"_json);
CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch(
R"( [{"op": "replace", "path": "/foo/1", "value": 4}] )"_json
) == R"( {"foo": [1, 4, 3]} )"_json);
CHECK(R"( {"foo": [{"bar": "baz"}]} )"_json.patch(
R"( [{"op": "replace", "path": "/foo/0/bar", "value": 1}] )"_json
) == R"( {"foo": [{"bar": 1}]} )"_json);
}
SECTION("move")
{
CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch(
R"( [{"op": "move", "from": "/foo", "path": "/bar"}] )"_json
) == R"( {"bar": [1, 2, 3]} )"_json);
}
SECTION("copy")
{
CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch(
R"( [{"op": "copy", "from": "/foo/1", "path": "/bar"}] )"_json
) == R"( {"foo": [1, 2, 3], "bar": 2} )"_json);
}
SECTION("copy")
{
CHECK_NOTHROW(R"( {"foo": "bar"} )"_json.patch(
R"( [{"op": "test", "path": "/foo", "value": "bar"}] )"_json));
}
}
}
TEST_CASE("regression tests")
{
SECTION("issue #60 - Double quotation mark is not parsed correctly")
{
SECTION("escape_dobulequote")
{
auto s = "[\"\\\"foo\\\"\"]";
json j = json::parse(s);
auto expected = R"(["\"foo\""])"_json;
CHECK(j == expected);
}
}
SECTION("issue #70 - Handle infinity and NaN cases")
{
SECTION("NAN value")
{
CHECK(json(NAN) == json());
CHECK(json(json::number_float_t(NAN)) == json());
}
SECTION("infinity")
{
CHECK(json(INFINITY) == json());
CHECK(json(json::number_float_t(INFINITY)) == json());
}
}
SECTION("pull request #71 - handle enum type")
{
enum { t = 0 };
json j = json::array();
j.push_back(t);
j.push_back(json::object(
{
{"game_type", t}
}));
}
SECTION("issue #76 - dump() / parse() not idempotent")
{
// create JSON object
json fields;
fields["one"] = std::string("one");
fields["two"] = std::string("two three");
fields["three"] = std::string("three \"four\"");
// create another JSON object by deserializing the serialization
std::string payload = fields.dump();
json parsed_fields = json::parse(payload);
// check individual fields to match both objects
CHECK(parsed_fields["one"] == fields["one"]);
CHECK(parsed_fields["two"] == fields["two"]);
CHECK(parsed_fields["three"] == fields["three"]);
// check individual fields to match original input
CHECK(parsed_fields["one"] == std::string("one"));
CHECK(parsed_fields["two"] == std::string("two three"));
CHECK(parsed_fields["three"] == std::string("three \"four\""));
// check equality of the objects
CHECK(parsed_fields == fields);
// check equality of the serialized objects
CHECK(fields.dump() == parsed_fields.dump());
// check everything in one line
CHECK(fields == json::parse(fields.dump()));
}
SECTION("issue #82 - lexer::get_number return NAN")
{
const auto content = R"(
{
"Test":"Test1",
"Number":100,
"Foo":42.42
})";
std::stringstream ss;
ss << content;
json j;
ss >> j;
std::string test = j["Test"];
CHECK(test == "Test1");
int number = j["Number"];
CHECK(number == 100);
float foo = j["Foo"];
CHECK(foo == Approx(42.42));
}
SECTION("issue #89 - nonstandard integer type")
{
// create JSON class with nonstandard integer number type
using custom_json =
nlohmann::basic_json<std::map, std::vector, std::string, bool, int32_t, uint32_t, float>;
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<int>(j["int_1"]) == 1);
// tests for correct handling of non-standard integers that overflow the type selected by the user
// unsigned integer object creation - expected to wrap and still be stored as an integer
j = 4294967296U; // 2^32
CHECK(static_cast<int>(j.type()) == static_cast<int>(custom_json::value_t::number_unsigned));
CHECK(j.get<uint32_t>() == 0); // Wrap
// unsigned integer parsing - expected to overflow and be stored as a float
j = custom_json::parse("4294967296"); // 2^32
CHECK(static_cast<int>(j.type()) == static_cast<int>(custom_json::value_t::number_float));
CHECK(j.get<float>() == 4294967296.0f);
// integer object creation - expected to wrap and still be stored as an integer
j = -2147483649LL; // -2^31-1
CHECK(static_cast<int>(j.type()) == static_cast<int>(custom_json::value_t::number_integer));
CHECK(j.get<int32_t>() == 2147483647); // Wrap
// integer parsing - expected to overflow and be stored as a float with rounding
j = custom_json::parse("-2147483649"); // -2^31
CHECK(static_cast<int>(j.type()) == static_cast<int>(custom_json::value_t::number_float));
CHECK(j.get<float>() == -2147483650.0f);
}
SECTION("issue #93 reverse_iterator operator inheritance problem")
{
{
json a = {1, 2, 3};
json::reverse_iterator rit = a.rbegin();
++rit;
CHECK(*rit == json(2));
CHECK(rit.value() == json(2));
}
{
json a = {1, 2, 3};
json::reverse_iterator rit = ++a.rbegin();
}
{
json a = {1, 2, 3};
json::reverse_iterator rit = a.rbegin();
++rit;
json b = {0, 0, 0};
std::transform(rit, a.rend(), b.rbegin(), [](json el)
{
return el;
});
CHECK(b == json({0, 1, 2}));
}
{
json a = {1, 2, 3};
json b = {0, 0, 0};
std::transform(++a.rbegin(), a.rend(), b.rbegin(), [](json el)
{
return el;
});
CHECK(b == json({0, 1, 2}));
}
}
SECTION("issue #100 - failed to iterator json object with reverse_iterator")
{
json config =
{
{ "111", 111 },
{ "112", 112 },
{ "113", 113 }
};
std::stringstream ss;
for (auto it = config.begin(); it != config.end(); ++it)
{
ss << it.key() << ": " << it.value() << '\n';
}
for (auto it = config.rbegin(); it != config.rend(); ++it)
{
ss << it.key() << ": " << it.value() << '\n';
}
CHECK(ss.str() == "111: 111\n112: 112\n113: 113\n113: 113\n112: 112\n111: 111\n");
}
SECTION("issue #101 - binary string causes numbers to be dumped as hex")
{
int64_t number = 10;
std::string bytes{"\x00" "asdf\n", 6};
json j;
j["int64"] = number;
j["binary string"] = bytes;
// make sure the number is really printed as decimal "10" and not as
// hexadecimal "a"
CHECK(j.dump() == "{\"binary string\":\"\\u0000asdf\\n\",\"int64\":10}");
}
SECTION("issue #111 - subsequent unicode chars")
{
std::string bytes{0x7, 0x7};
json j;
j["string"] = bytes;
CHECK(j["string"] == "\u0007\u0007");
}
SECTION("issue #144 - implicit assignment to std::string fails")
{
json o = {{"name", "value"}};
std::string s1 = o["name"];
CHECK(s1 == "value");
std::string s2;
s2 = o["name"];
CHECK(s2 == "value");
}
SECTION("issue #146 - character following a surrogate pair is skipped")
{
CHECK(json::parse("\"\\ud80c\\udc60abc\"").get<json::string_t>() == u8"\U00013060abc");
}
SECTION("issue #171 - Cannot index by key of type static constexpr const char*")
{
json j;
// Non-const access with key as "char []"
char array_key[] = "Key1";
CHECK_NOTHROW(j[array_key] = 1);
CHECK(j[array_key] == json(1));
// Non-const access with key as "const char[]"
const char const_array_key[] = "Key2";
CHECK_NOTHROW(j[const_array_key] = 2);
CHECK(j[const_array_key] == json(2));
// Non-const access with key as "char *"
char _ptr_key[] = "Key3";
char* ptr_key = &_ptr_key[0];
CHECK_NOTHROW(j[ptr_key] = 3);
CHECK(j[ptr_key] == json(3));
// Non-const access with key as "const char *"
const char* const_ptr_key = "Key4";
CHECK_NOTHROW(j[const_ptr_key] = 4);
CHECK(j[const_ptr_key] == json(4));
// Non-const access with key as "static constexpr const char *"
static constexpr const char* constexpr_ptr_key = "Key5";
CHECK_NOTHROW(j[constexpr_ptr_key] = 5);
CHECK(j[constexpr_ptr_key] == json(5));
const json j_const = j;
// Const access with key as "char []"
CHECK(j_const[array_key] == json(1));
// Const access with key as "const char[]"
CHECK(j_const[const_array_key] == json(2));
// Const access with key as "char *"
CHECK(j_const[ptr_key] == json(3));
// Const access with key as "const char *"
CHECK(j_const[const_ptr_key] == json(4));
// Const access with key as "static constexpr const char *"
CHECK(j_const[constexpr_ptr_key] == json(5));
}
SECTION("issue #186 miloyip/nativejson-benchmark: floating-point parsing")
{
json j;
j = json::parse("-0.0");
CHECK(j.get<double>() == -0.0);
j = json::parse("2.22507385850720113605740979670913197593481954635164564e-308");
CHECK(j.get<double>() == 2.2250738585072009e-308);
j = json::parse("0.999999999999999944488848768742172978818416595458984374");
CHECK(j.get<double>() == 0.99999999999999989);
j = json::parse("1.00000000000000011102230246251565404236316680908203126");
CHECK(j.get<double>() == 1.00000000000000022);
j = json::parse("7205759403792793199999e-5");
CHECK(j.get<double>() == 72057594037927928.0);
j = json::parse("922337203685477529599999e-5");
CHECK(j.get<double>() == 9223372036854774784.0);
j = json::parse("1014120480182583464902367222169599999e-5");
CHECK(j.get<double>() == 10141204801825834086073718800384.0);
j = json::parse("5708990770823839207320493820740630171355185151999e-3");
CHECK(j.get<double>() == 5708990770823838890407843763683279797179383808.0);
// create JSON class with nonstandard float number type
// float
nlohmann::basic_json<std::map, std::vector, std::string, bool, int32_t, uint32_t, float> j_float =
1.23e25f;
CHECK(j_float.get<float>() == 1.23e25f);
// double
nlohmann::basic_json<std::map, std::vector, std::string, bool, int64_t, uint64_t, double> j_double =
1.23e35f;
CHECK(j_double.get<double>() == 1.23e35f);
// long double
nlohmann::basic_json<std::map, std::vector, std::string, bool, int64_t, uint64_t, long double>
j_long_double = 1.23e45L;
CHECK(j_long_double.get<long double>() == 1.23e45L);
}
SECTION("issue #228 - double values are serialized with commas as decimal points")
{
json j1a = 23.42;
json j1b = json::parse("23.42");
json j2a = 2342e-2;
//issue #230
//json j2b = json::parse("2342e-2");
json j3a = 10E3;
json j3b = json::parse("10E3");
json j3c = json::parse("10e3");
// class to create a locale that would use a comma for decimals
class CommaDecimalSeparator : public std::numpunct<char>
{
protected:
char do_decimal_point() const
{
return ',';
}
};
// change locale to mess with decimal points
std::locale::global(std::locale(std::locale(), new CommaDecimalSeparator));
CHECK(j1a.dump() == "23.42");
CHECK(j1b.dump() == "23.42");
// check if locale is properly reset
std::stringstream ss;
ss.imbue(std::locale(std::locale(), new CommaDecimalSeparator));
ss << 47.11;
CHECK(ss.str() == "47,11");
ss << j1a;
CHECK(ss.str() == "47,1123.42");
ss << 47.11;
CHECK(ss.str() == "47,1123.4247,11");
CHECK(j2a.dump() == "23.42");
//issue #230
//CHECK(j2b.dump() == "23.42");
CHECK(j3a.dump() == "10000");
CHECK(j3b.dump() == "10000");
CHECK(j3c.dump() == "10000");
//CHECK(j3b.dump() == "1E04"); // roundtrip error
//CHECK(j3c.dump() == "1e04"); // roundtrip error
}
SECTION("issue #233 - Can't use basic_json::iterator as a base iterator for std::move_iterator")
{
json source = {"a", "b", "c"};
json expected = {"a", "b"};
json dest;
std::copy_n(std::make_move_iterator(source.begin()), 2, std::back_inserter(dest));
CHECK(dest == expected);
}
SECTION("issue #235 - ambiguous overload for 'push_back' and 'operator+='")
{
json data = {{"key", "value"}};
data.push_back({"key2", "value2"});
data += {"key3", "value3"};
CHECK(data == json({{"key", "value"}, {"key2", "value2"}, {"key3", "value3"}}));
}
SECTION("issue #269 - diff generates incorrect patch when removing multiple array elements")
{
json doc = R"( { "arr1": [1, 2, 3, 4] } )"_json;
json expected = R"( { "arr1": [1, 2] } )"_json;
// check roundtrip
CHECK(doc.patch(json::diff(doc, expected)) == expected);
}
SECTION("issue #283 - value() does not work with _json_pointer types")
{
json j =
{
{"object", {{"key1", 1}, {"key2", 2}}},
};
int at_integer = j.at("/object/key2"_json_pointer);
int val_integer = j.value("/object/key2"_json_pointer, 0);
CHECK(at_integer == val_integer);
}
SECTION("issue #278 - Do not throw exception when default_value's type does not match the actual type")
{
json j =
{
{"port", 1234}, {"proxy", true}
};
int port1 = j.value_with_type("port", 8080);
int port2 = j.value_with_type("the_port", 8080);
int port3 = j.value_with_type("proxy", 8080);
bool proxy1 = j.value_with_type("proxy", false);
bool proxy2 = j.value_with_type("the_proxy", false);
bool proxy3 = j.value_with_type("port", false);
CHECK(port1 == 1234);
CHECK(port2 == 8080);
CHECK(port3 == 8080);
CHECK(proxy1 == true);
CHECK(proxy2 == false);
CHECK(proxy3 == false);
}
}
// special test case to check if memory is leaked if constructor throws
template<class T>
struct my_allocator : std::allocator<T>
{
template<class... Args>
void construct(T*, Args&& ...)
{
throw std::bad_alloc();
}
};
TEST_CASE("bad_alloc")
{
SECTION("bad_alloc")
{
// create JSON type using the throwing allocator
using my_json = nlohmann::basic_json<std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
my_allocator>;
// creating an object should throw
CHECK_THROWS_AS(my_json j(my_json::value_t::object), std::bad_alloc);
}
}