json/test/json_unit.cc
2015-01-23 20:46:46 +01:00

2196 lines
71 KiB
C++

#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include "json.h"
#include <set>
#include <unordered_set>
#include <deque>
#include <list>
#include <forward_list>
#include <array>
#include <map>
#include <unordered_map>
using json = nlohmann::json;
TEST_CASE("array")
{
SECTION("Basics")
{
// construction with given type
json j(json::value_t::array);
CHECK(j.type() == json::value_t::array);
// const object
const json j_const (j);
// string representation of default value
CHECK(j.dump() == "[]");
// iterators
CHECK(j.begin() == j.end());
CHECK(j.cbegin() == j.cend());
// container members
CHECK(j.size() == 0);
CHECK(j.max_size() > 0);
CHECK(j.empty() == true);
CHECK_NOTHROW(size_t h = std::hash<json>()(j));
// implicit conversions
CHECK_NOTHROW(json::array_t v = j);
CHECK_THROWS_AS(json::object_t v = j, std::logic_error);
CHECK_THROWS_AS(std::string v = j, std::logic_error);
CHECK_THROWS_AS(bool v = j, std::logic_error);
CHECK_THROWS_AS(int v = j, std::logic_error);
CHECK_THROWS_AS(double v = j, std::logic_error);
// explicit conversions
CHECK_NOTHROW(auto v = j.get<json::array_t>());
CHECK_THROWS_AS(auto v = j.get<json::object_t>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<std::string>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<bool>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<int>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<int64_t>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<double>(), std::logic_error);
// transparent usage
auto id = [](json::array_t v)
{
return v;
};
CHECK(id(j) == j.get<json::array_t>());
// copy constructor
json k(j);
CHECK(k == j);
// copy assignment
k = j;
CHECK(k == j);
// move constructor
json l = std::move(k);
CHECK(l == j);
}
SECTION("Create from value")
{
json::array_t v1 = {"string", 1, 1.0, false, nullptr};
json j1 = v1;
CHECK(j1.get<json::array_t>() == v1);
json j2 = {"string", 1, 1.0, false, nullptr};
json::array_t v2 = j2;
CHECK(j2.get<json::array_t>() == v1);
CHECK(j2.get<json::array_t>() == v2);
// special tests to make sure construction from initializer list works
// case 1: there is an element that is not an array
json j3 = { {"foo", "bar"}, 3 };
CHECK(j3.type() == json::value_t::array);
// case 2: there is an element with more than two elements
json j4 = { {"foo", "bar"}, {"one", "two", "three"} };
CHECK(j4.type() == json::value_t::array);
// case 3: there is an element whose first element is not a string
json j5 = { {"foo", "bar"}, {true, "baz"} };
CHECK(j5.type() == json::value_t::array);
// check if nested arrays work and are recognized as arrays
json j6 = { {{"foo", "bar"}} };
CHECK(j6.type() == json::value_t::array);
CHECK(j6.size() == 1);
CHECK(j6[0].type() == json::value_t::object);
// move constructor
json j7(std::move(v1));
CHECK(j7 == j1);
}
SECTION("Array operators")
{
json j = {0, 1, 2, 3, 4, 5, 6};
// read
const int v1 = j[3];
CHECK(v1 == 3);
// write
j[4] = 9;
int v2 = j[4];
CHECK(v2 == 9);
// size
CHECK (j.size() == 7);
// push_back for different value types
j.push_back(7);
j.push_back("const char*");
j.push_back(42.23);
std::string s = "std::string";
j.push_back(s);
j.push_back(false);
j.push_back(nullptr);
j.push_back(j);
CHECK (j.size() == 14);
// operator+= for different value types
j += 7;
j += "const char*";
j += 42.23;
j += s;
j += false;
j += nullptr;
j += j;
CHECK (j.size() == 21);
// implicit transformation into an array
json empty1, empty2;
empty1 += "foo";
empty2.push_back("foo");
CHECK(empty1.type() == json::value_t::array);
CHECK(empty2.type() == json::value_t::array);
CHECK(empty1 == empty2);
// exceptions
json nonarray = 1;
CHECK_THROWS_AS(nonarray.at(0), std::domain_error);
CHECK_THROWS_AS(const int i = nonarray[0], std::domain_error);
CHECK_NOTHROW(j[21]);
CHECK_THROWS_AS(const int i = j.at(21), std::out_of_range);
CHECK_THROWS_AS(nonarray[0] = 10, std::domain_error);
// the next test is remove due to undefined behavior
//CHECK_NOTHROW(j[21] = 5);
CHECK_THROWS_AS(j.at(21) = 5, std::out_of_range);
CHECK_THROWS_AS(nonarray += 2, std::runtime_error);
const json nonarray_const = nonarray;
const json j_const = j;
CHECK_THROWS_AS(nonarray_const.at(0), std::domain_error);
CHECK_THROWS_AS(const int i = nonarray_const[0], std::domain_error);
CHECK_NOTHROW(j_const[21]);
CHECK_THROWS_AS(const int i = j.at(21), std::out_of_range);
{
json nonarray2 = json(1);
json nonarray3 = json(2);
json empty3 = json();
CHECK_THROWS_AS(nonarray2.push_back(nonarray3), std::runtime_error);
CHECK_THROWS_AS(nonarray2.push_back(3), std::runtime_error);
CHECK_NOTHROW(empty3.push_back(nonarray3));
CHECK(empty3.type() == json::value_t::array);
}
const json k = j;
CHECK_NOTHROW(k[21]);
CHECK_THROWS_AS(const int i = k.at(21), std::out_of_range);
// add initializer list
j.push_back({"a", "b", "c"});
CHECK (j.size() == 24);
// clear()
json j7 = {0, 1, 2, 3, 4, 5, 6};;
CHECK(j7.size() == 7);
j7.clear();
CHECK(j7.size() == 0);
}
SECTION("Iterators")
{
std::vector<int> vec = {0, 1, 2, 3, 4, 5, 6};
json j1 = {0, 1, 2, 3, 4, 5, 6};
const json j2 = {0, 1, 2, 3, 4, 5, 6};
{
// const_iterator
for (json::const_iterator cit = j1.begin(); cit != j1.end(); ++cit)
{
int v = *cit;
CHECK(v == vec[static_cast<size_t>(v)]);
if (cit == j1.begin())
{
CHECK(v == 0);
}
}
}
{
for (json::const_reverse_iterator it = j1.rbegin(); it != j1.crend(); ++it)
{
}
for (json::reverse_iterator it = j1.rbegin(); it != j1.rend(); ++it)
{
}
}
{
// const_iterator with cbegin/cend
for (json::const_iterator cit = j1.cbegin(); cit != j1.cend(); ++cit)
{
int v = *cit;
CHECK(v == vec[static_cast<size_t>(v)]);
if (cit == j1.cbegin())
{
CHECK(v == 0);
}
}
}
{
// range based for
for (auto el : j1)
{
int v = el;
CHECK(v == vec[static_cast<size_t>(v)]);
}
}
{
// iterator
for (json::iterator cit = j1.begin(); cit != j1.end(); ++cit)
{
int v_old = *cit;
*cit = cit->get<int>() * 2;
int v = *cit;
CHECK(v == vec[static_cast<size_t>(v_old)] * 2);
if (cit == j1.begin())
{
CHECK(v == 0);
}
}
}
{
// const_iterator (on const object)
for (json::const_iterator cit = j2.begin(); cit != j2.end(); ++cit)
{
int v = *cit;
CHECK(v == vec[static_cast<size_t>(v)]);
if (cit == j2.begin())
{
CHECK(v == 0);
}
}
}
{
// const_iterator with cbegin/cend (on const object)
for (json::const_iterator cit = j2.cbegin(); cit != j2.cend(); ++cit)
{
int v = *cit;
CHECK(v == vec[static_cast<size_t>(v)]);
if (cit == j2.cbegin())
{
CHECK(v == 0);
}
}
}
{
// range based for (on const object)
for (auto el : j2)
{
int v = el;
CHECK(v == vec[static_cast<size_t>(v)]);
}
}
// edge case: This should be an array with two elements which are in
// turn arrays with two strings. However, this is treated like the
// initializer list of an object.
json j_should_be_an_array = { {"foo", "bar"}, {"baz", "bat"} };
CHECK(j_should_be_an_array.type() == json::value_t::object);
}
SECTION("Iterators and empty arrays")
{
json empty_array(json::value_t::array);
for (json::iterator it = empty_array.begin(); it != empty_array.end(); ++it) {}
for (json::const_iterator it = empty_array.begin(); it != empty_array.end(); ++it) {}
for (json::const_iterator it = empty_array.cbegin(); it != empty_array.cend(); ++it) {}
for (auto el : empty_array) {}
for (const auto el : empty_array) {}
// create nonempty array, set iterators, clear array, and copy
// existing iterators to cover copy constructor's code
json array = {1, 2, 3};
json::iterator i1 = array.begin();
json::const_iterator i2 = array.cbegin();
array.clear();
json::iterator i3(i1);
json::const_iterator i4(i1);
json::const_iterator i5(i2);
}
SECTION("Container operations")
{
json a1 = {1, 2, 3, 4};
json a2 = {"one", "two", "three"};
a1.swap(a2);
CHECK(a1 == json({"one", "two", "three"}));
CHECK(a2 == json({1, 2, 3, 4}));
}
SECTION("Construct from sequence objects")
{
std::vector<int> c_vector {1, 2, 3, 4};
json j_vec(c_vector);
CHECK(j_vec.type() == json::value_t::array);
CHECK(j_vec.size() == 4);
std::set<std::string> c_set {"one", "two", "three", "four", "one"};
json j_set(c_set);
CHECK(j_set.type() == json::value_t::array);
CHECK(j_set.size() == 4);
std::unordered_set<std::string> c_uset {"one", "two", "three", "four", "one"};
json j_uset(c_uset);
CHECK(j_uset.type() == json::value_t::array);
CHECK(j_uset.size() == 4);
std::multiset<std::string> c_mset {"one", "two", "one", "four"};
json j_mset(c_mset);
CHECK(j_mset.type() == json::value_t::array);
CHECK(j_mset.size() == 4);
std::unordered_multiset<std::string> c_umset {"one", "two", "one", "four"};
json j_umset(c_umset);
CHECK(j_umset.type() == json::value_t::array);
CHECK(j_umset.size() == 4);
std::deque<float> c_deque {1.2, 2.3, 3.4, 5.6};
json j_deque(c_deque);
CHECK(j_deque.type() == json::value_t::array);
CHECK(j_deque.size() == 4);
std::list<bool> c_list {true, true, false, true};
json j_list(c_list);
CHECK(j_list.type() == json::value_t::array);
CHECK(j_list.size() == 4);
std::forward_list<int64_t> c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543};
json j_flist(c_flist);
CHECK(j_flist.type() == json::value_t::array);
CHECK(j_flist.size() == 4);
std::array<unsigned long, 4> c_array {{1, 2, 3, 4}};
json j_array(c_array);
CHECK(j_array.type() == json::value_t::array);
CHECK(j_array.size() == 4);
}
}
TEST_CASE("object")
{
SECTION("Basics")
{
// construction with given type
json j(json::value_t::object);
CHECK(j.type() == json::value_t::object);
// const object
const json j_const = j;
// string representation of default value
CHECK(j.dump() == "{}");
// iterators
CHECK(j.begin() == j.end());
CHECK(j.cbegin() == j.cend());
// container members
CHECK(j.size() == 0);
CHECK(j.max_size() > 0);
CHECK(j.empty() == true);
CHECK_NOTHROW(size_t h = std::hash<json>()(j));
// implicit conversions
CHECK_THROWS_AS(json::array_t v = j, std::logic_error);
CHECK_NOTHROW(json::object_t v = j);
CHECK_THROWS_AS(std::string v = j, std::logic_error);
CHECK_THROWS_AS(bool v = j, std::logic_error);
CHECK_THROWS_AS(int v = j, std::logic_error);
CHECK_THROWS_AS(double v = j, std::logic_error);
// explicit conversions
CHECK_THROWS_AS(auto v = j.get<json::array_t>(), std::logic_error);
CHECK_NOTHROW(auto v = j.get<json::object_t>());
CHECK_THROWS_AS(auto v = j.get<std::string>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<bool>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<int>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<int64_t>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<double>(), std::logic_error);
// transparent usage
auto id = [](json::object_t v)
{
return v;
};
CHECK(id(j) == j.get<json::object_t>());
// copy constructor
json k(j);
CHECK(k == j);
// copy assignment
k = j;
CHECK(k == j);
// move constructor
json l = std::move(k);
CHECK(l == j);
}
SECTION("Create from value")
{
json::object_t v1 = { {"v1", "string"}, {"v2", 1}, {"v3", 1.0}, {"v4", false} };
json j1 = v1;
CHECK(j1.get<json::object_t>() == v1);
json j2 = { {"v1", "string"}, {"v2", 1}, {"v3", 1.0}, {"v4", false} };
json::object_t v2 = j2;
CHECK(j2.get<json::object_t>() == v1);
CHECK(j2.get<json::object_t>() == v2);
// check if multiple keys are ignored
json j3 = { {"key", "value"}, {"key", 1} };
CHECK(j3.size() == 1);
// move constructor
json j7(std::move(v1));
CHECK(j7 == j1);
}
SECTION("Object operators")
{
json j = {{"k0", "v0"}, {"k1", nullptr}, {"k2", 42}, {"k3", 3.141}, {"k4", true}};
const json k = j;
// read
{
const std::string v0 = j["k0"];
CHECK(v0 == "v0");
auto v1 = j["k1"];
CHECK(v1 == nullptr);
int v2 = j["k2"];
CHECK(v2 == 42);
double v3 = j["k3"];
CHECK(v3 == 3.141);
bool v4 = j["k4"];
CHECK(v4 == true);
}
{
const std::string v0 = j[std::string("k0")];
CHECK(v0 == "v0");
auto v1 = j[std::string("k1")];
CHECK(v1 == nullptr);
int v2 = j[std::string("k2")];
CHECK(v2 == 42);
double v3 = j[std::string("k3")];
CHECK(v3 == 3.141);
bool v4 = j[std::string("k4")];
CHECK(v4 == true);
}
{
const std::string v0 = k["k0"];
CHECK(v0 == "v0");
auto v1 = k["k1"];
CHECK(v1 == nullptr);
int v2 = k["k2"];
CHECK(v2 == 42);
double v3 = k["k3"];
CHECK(v3 == 3.141);
bool v4 = k["k4"];
CHECK(v4 == true);
}
{
const std::string v0 = k[std::string("k0")];
CHECK(v0 == "v0");
auto v1 = k[std::string("k1")];
CHECK(v1 == nullptr);
int v2 = k[std::string("k2")];
CHECK(v2 == 42);
double v3 = k[std::string("k3")];
CHECK(v3 == 3.141);
bool v4 = k[std::string("k4")];
CHECK(v4 == true);
}
// write (replace)
j["k0"] = "new v0";
CHECK(j["k0"] == "new v0");
// write (add)
j["k5"] = false;
// size
CHECK(j.size() == 6);
// find
CHECK(j.find("k0") != j.end());
CHECK(j.find("v0") == j.end());
CHECK(j.find(std::string("v0")) == j.end());
json::const_iterator i1 = j.find("k0");
json::iterator i2 = j.find("k0");
CHECK(k.find("k0") != k.end());
CHECK(k.find("v0") == k.end());
CHECK(k.find(std::string("v0")) == k.end());
json::const_iterator i22 = k.find("k0");
// at
CHECK_THROWS_AS(j.at("foo"), std::out_of_range);
CHECK_THROWS_AS(k.at("foo"), std::out_of_range);
CHECK_THROWS_AS(j.at(std::string("foo")), std::out_of_range);
CHECK_THROWS_AS(k.at(std::string("foo")), std::out_of_range);
CHECK_NOTHROW(j.at(std::string("k0")));
CHECK_NOTHROW(k.at(std::string("k0")));
{
json noobject = 1;
const json noobject_const = noobject;
CHECK_THROWS_AS(noobject.at("foo"), std::domain_error);
CHECK_THROWS_AS(noobject.at(std::string("foo")), std::domain_error);
CHECK_THROWS_AS(noobject_const.at("foo"), std::domain_error);
CHECK_THROWS_AS(noobject_const.at(std::string("foo")), std::domain_error);
CHECK_THROWS_AS(noobject["foo"], std::domain_error);
CHECK_THROWS_AS(noobject[std::string("foo")], std::domain_error);
CHECK_THROWS_AS(noobject_const[std::string("foo")], std::domain_error);
}
// add pair
j.push_back(json::object_t::value_type {"int_key", 42});
CHECK(j["int_key"].get<int>() == 42);
j += json::object_t::value_type {"int_key2", 23};
CHECK(j["int_key2"].get<int>() == 23);
{
// make sure null objects are transformed
json je;
CHECK_NOTHROW(je.push_back(json::object_t::value_type {"int_key", 42}));
CHECK(je["int_key"].get<int>() == 42);
}
{
// make sure null objects are transformed
json je;
CHECK_NOTHROW((je += json::object_t::value_type {"int_key", 42}));
CHECK(je["int_key"].get<int>() == 42);
}
// add initializer list (of pairs)
{
json je;
je.push_back({ {"one", 1}, {"two", false}, {"three", {1, 2, 3}} });
CHECK(je["one"].get<int>() == 1);
CHECK(je["two"].get<bool>() == false);
CHECK(je["three"].size() == 3);
}
{
json je;
je += { {"one", 1}, {"two", false}, {"three", {1, 2, 3}} };
CHECK(je["one"].get<int>() == 1);
CHECK(je["two"].get<bool>() == false);
CHECK(je["three"].size() == 3);
}
// key/value for non-end iterator
CHECK(i1.key() == "k0");
CHECK(i1.value() == j["k0"]);
CHECK(i2.key() == "k0");
CHECK(i2.value() == j["k0"]);
// key/value for uninitialzed iterator
json::const_iterator i3;
json::iterator i4;
CHECK_THROWS_AS(i3.key(), std::out_of_range);
CHECK_THROWS_AS(i3.value(), std::out_of_range);
CHECK_THROWS_AS(i4.key(), std::out_of_range);
CHECK_THROWS_AS(i4.value(), std::out_of_range);
// key/value for end-iterator
json::const_iterator i5 = j.find("v0");
json::iterator i6 = j.find("v0");
CHECK_THROWS_AS(i5.key(), std::out_of_range);
CHECK_THROWS_AS(i5.value(), std::out_of_range);
CHECK_THROWS_AS(i6.key(), std::out_of_range);
CHECK_THROWS_AS(i6.value(), std::out_of_range);
// implicit transformation into an object
json empty;
empty["foo"] = "bar";
CHECK(empty.type() == json::value_t::object);
CHECK(empty["foo"] == "bar");
// exceptions
json nonarray = 1;
CHECK_THROWS_AS(const int i = nonarray["v1"], std::domain_error);
CHECK_THROWS_AS(nonarray["v1"] = 10, std::domain_error);
{
const json c = {{"foo", "bar"}};
CHECK_THROWS_AS(c[std::string("baz")], std::out_of_range);
}
// clear()
json j7 = {{"k0", 0}, {"k1", 1}, {"k2", 2}, {"k3", 3}};
CHECK(j7.size() == 4);
j7.clear();
CHECK(j7.size() == 0);
}
SECTION("Iterators")
{
json j1 = {{"k0", 0}, {"k1", 1}, {"k2", 2}, {"k3", 3}};
const json j2 = {{"k0", 0}, {"k1", 1}, {"k2", 2}, {"k3", 3}};
// iterator
for (json::iterator it = j1.begin(); it != j1.end(); ++it)
{
switch (static_cast<int>(it.value()))
{
case (0):
CHECK(it.key() == "k0");
break;
case (1):
CHECK(it.key() == "k1");
break;
case (2):
CHECK(it.key() == "k2");
break;
case (3):
CHECK(it.key() == "k3");
break;
default:
CHECK(false);
}
CHECK((*it).type() == json::value_t::number);
CHECK(it->type() == json::value_t::number);
}
for (json::reverse_iterator it = j1.rbegin(); it != j1.rend(); ++it)
{
}
// range-based for
for (auto& element : j1)
{
element = 2 * element.get<int>();
}
// const_iterator
for (json::const_iterator it = j1.begin(); it != j1.end(); ++it)
{
switch (static_cast<int>(it.value()))
{
case (0):
CHECK(it.key() == "k0");
break;
case (2):
CHECK(it.key() == "k1");
break;
case (4):
CHECK(it.key() == "k2");
break;
case (6):
CHECK(it.key() == "k3");
break;
default:
CHECK(false);
}
CHECK((*it).type() == json::value_t::number);
CHECK(it->type() == json::value_t::number);
}
for (json::const_reverse_iterator it = j1.crbegin(); it != j1.crend(); ++it)
{
}
// const_iterator using cbegin/cend
for (json::const_iterator it = j1.cbegin(); it != j1.cend(); ++it)
{
switch (static_cast<int>(it.value()))
{
case (0):
CHECK(it.key() == "k0");
break;
case (2):
CHECK(it.key() == "k1");
break;
case (4):
CHECK(it.key() == "k2");
break;
case (6):
CHECK(it.key() == "k3");
break;
default:
CHECK(false);
}
CHECK((*it).type() == json::value_t::number);
CHECK(it->type() == json::value_t::number);
}
// const_iterator (on const object)
for (json::const_iterator it = j2.begin(); it != j2.end(); ++it)
{
switch (static_cast<int>(it.value()))
{
case (0):
CHECK(it.key() == "k0");
break;
case (1):
CHECK(it.key() == "k1");
break;
case (2):
CHECK(it.key() == "k2");
break;
case (3):
CHECK(it.key() == "k3");
break;
default:
CHECK(false);
}
CHECK((*it).type() == json::value_t::number);
CHECK(it->type() == json::value_t::number);
}
// const_iterator using cbegin/cend (on const object)
for (json::const_iterator it = j2.cbegin(); it != j2.cend(); ++it)
{
switch (static_cast<int>(it.value()))
{
case (0):
CHECK(it.key() == "k0");
break;
case (1):
CHECK(it.key() == "k1");
break;
case (2):
CHECK(it.key() == "k2");
break;
case (3):
CHECK(it.key() == "k3");
break;
default:
CHECK(false);
}
CHECK((*it).type() == json::value_t::number);
CHECK(it->type() == json::value_t::number);
}
// range-based for (on const object)
for (auto element : j1)
{
CHECK(element.get<int>() >= 0);
}
}
SECTION("Iterators and empty objects")
{
json empty_object(json::value_t::object);
for (json::iterator it = empty_object.begin(); it != empty_object.end(); ++it) {}
for (json::const_iterator it = empty_object.begin(); it != empty_object.end(); ++it) {}
for (json::const_iterator it = empty_object.cbegin(); it != empty_object.cend(); ++it) {}
for (auto el : empty_object) {}
for (const auto el : empty_object) {}
// create nonempty object, set iterators, clear object, and copy
// existing iterators to cover copy constructor's code
json object = {{"foo", 1}};
json::iterator i1 = object.begin();
json::const_iterator i2 = object.cbegin();
object.clear();
json::iterator i3(i1);
json::const_iterator i4(i1);
json::const_iterator i5(i2);
}
SECTION("Container operations")
{
json o1 = { {"one", "eins"}, {"two", "zwei"} };
json o2 = { {"one", 1}, {"two", 2} };
o1.swap(o2);
CHECK(o1 == json({ {"one", 1}, {"two", 2} }));
CHECK(o2 == json({ {"one", "eins"}, {"two", "zwei"} }));
std::swap(o1, o2);
CHECK(o1 == json({ {"one", "eins"}, {"two", "zwei"} }));
CHECK(o2 == json({ {"one", 1}, {"two", 2} }));
}
SECTION("Construct from sequence objects")
{
std::map<std::string, int> c_map { {"one", 1}, {"two", 2}, {"three", 3} };
json j_map(c_map);
CHECK(j_map.type() == json::value_t::object);
CHECK(j_map.size() == 3);
std::unordered_map<const char*, float> c_umap { {"one", 1.2}, {"two", 2.3}, {"three", 3.4} };
json j_umap(c_umap);
CHECK(j_umap.type() == json::value_t::object);
CHECK(j_umap.size() == 3);
std::multimap<std::string, bool> c_mmap { {"one", true}, {"two", true}, {"three", false}, {"three", true} };
json j_mmap(c_mmap);
CHECK(j_mmap.type() == json::value_t::object);
CHECK(j_mmap.size() == 3);
std::unordered_multimap<std::string, bool> c_ummap { {"one", true}, {"two", true}, {"three", false}, {"three", true} };
json j_ummap(c_ummap);
CHECK(j_ummap.type() == json::value_t::object);
CHECK(j_ummap.size() == 3);
}
}
TEST_CASE("null")
{
SECTION("Basics")
{
// construction with given type
json j;
CHECK(j.type() == json::value_t::null);
// string representation of default value
CHECK(j.dump() == "null");
// iterators
CHECK(j.begin() != j.end());
CHECK(j.cbegin() != j.cend());
// container members
CHECK(j.size() == 0);
CHECK(j.max_size() == 0);
CHECK(j.empty() == true);
CHECK_NOTHROW(size_t h = std::hash<json>()(j));
// implicit conversions
CHECK_NOTHROW(json::array_t v = j);
CHECK_THROWS_AS(json::object_t v = j, std::logic_error);
CHECK_THROWS_AS(std::string v = j, std::logic_error);
CHECK_THROWS_AS(bool v = j, std::logic_error);
CHECK_THROWS_AS(int v = j, std::logic_error);
CHECK_THROWS_AS(double v = j, std::logic_error);
// explicit conversions
CHECK_NOTHROW(auto v = j.get<json::array_t>());
CHECK_THROWS_AS(auto v = j.get<json::object_t>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<std::string>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<bool>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<int>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<int64_t>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<double>(), std::logic_error);
// copy constructor
json k(j);
CHECK(k == j);
// copy assignment
k = j;
CHECK(k == j);
// move constructor
json l = std::move(k);
CHECK(l == j);
}
SECTION("Create from value")
{
json j1 = nullptr;
CHECK(j1.type() == json::value_t::null);
}
SECTION("Operators")
{
// clear()
json j1 = nullptr;
j1.clear();
CHECK(j1 == json(nullptr));
}
SECTION("Container operations")
{
json n1;
json n2;
n1.swap(n2);
CHECK(n1 == json());
CHECK(n2 == json());
std::swap(n1, n2);
CHECK(n1 == json());
CHECK(n2 == json());
json::iterator it = n1.begin();
--it;
json::const_iterator cit = n1.cbegin();
--cit;
}
}
TEST_CASE("string")
{
SECTION("Basics")
{
// construction with given type
json j(json::value_t::string);
CHECK(j.type() == json::value_t::string);
// const object
const json j_const = j;
// iterators
CHECK(j.begin() != j.end());
CHECK(j.cbegin() != j.cend());
// string representation of default value
CHECK(j.dump() == "\"\"");
// container members
CHECK(j.size() == 1);
CHECK(j.max_size() == 1);
CHECK(j.empty() == false);
CHECK_NOTHROW(size_t h = std::hash<json>()(j));
// implicit conversions
CHECK_NOTHROW(json::array_t v = j);
CHECK_THROWS_AS(json::object_t v = j, std::logic_error);
CHECK_NOTHROW(std::string v = j);
CHECK_THROWS_AS(bool v = j, std::logic_error);
CHECK_THROWS_AS(int v = j, std::logic_error);
CHECK_THROWS_AS(double v = j, std::logic_error);
// explicit conversions
CHECK_NOTHROW(auto v = j.get<json::array_t>());
CHECK_THROWS_AS(auto v = j.get<json::object_t>(), std::logic_error);
CHECK_NOTHROW(auto v = j.get<std::string>());
CHECK_NOTHROW(auto v = static_cast<std::string>(j));
CHECK_THROWS_AS(auto v = j.get<bool>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<int>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<int64_t>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<double>(), std::logic_error);
// transparent usage
auto id = [](std::string v)
{
return v;
};
CHECK(id(j) == j.get<std::string>());
// copy constructor
json k(j);
CHECK(k == j);
// copy assignment
k = j;
CHECK(k == j);
// move constructor
json l = std::move(k);
CHECK(l == j);
}
SECTION("Create from value")
{
json j1 = std::string("Hello, world");
std::string v1 = j1;
CHECK(j1.get<std::string>() == v1);
json j2 = "Hello, world";
CHECK(j2.get<std::string>() == "Hello, world");
std::string v3 = "Hello, world";
json j3 = std::move(v3);
CHECK(j3.get<std::string>() == "Hello, world");
}
SECTION("Operators")
{
// clear()
json j1 = std::string("Hello, world");
CHECK(j1.get<std::string>() == "Hello, world");
j1.clear();
CHECK(j1.get<std::string>() == "");
}
SECTION("Dumping")
{
CHECK(json("\"").dump(0) == "\"\\\"\"");
CHECK(json("\\").dump(0) == "\"\\\\\"");
CHECK(json("\n").dump(0) == "\"\\n\"");
CHECK(json("\t").dump(0) == "\"\\t\"");
CHECK(json("\b").dump(0) == "\"\\b\"");
CHECK(json("\f").dump(0) == "\"\\f\"");
CHECK(json("\r").dump(0) == "\"\\r\"");
}
SECTION("Container operations")
{
json s1 = "foo";
json s2 = "bar";
s1.swap(s2);
CHECK(s1 == json("bar"));
CHECK(s2 == json("foo"));
std::swap(s1, s2);
CHECK(s1 == json("foo"));
CHECK(s2 == json("bar"));
}
}
TEST_CASE("boolean")
{
SECTION("Basics")
{
// construction with given type
json j(json::value_t::boolean);
CHECK(j.type() == json::value_t::boolean);
// const object
const json j_const = j;
// iterators
CHECK(j.begin() != j.end());
CHECK(j.cbegin() != j.cend());
// string representation of default value
CHECK(j.dump() == "false");
// container members
CHECK(j.size() == 1);
CHECK(j.max_size() == 1);
CHECK(j.empty() == false);
CHECK_NOTHROW(size_t h = std::hash<json>()(j));
// implicit conversions
CHECK_NOTHROW(json::array_t v = j);
CHECK_THROWS_AS(json::object_t v = j, std::logic_error);
CHECK_THROWS_AS(std::string v = j, std::logic_error);
CHECK_NOTHROW(bool v = j);
CHECK_THROWS_AS(int v = j, std::logic_error);
CHECK_THROWS_AS(double v = j, std::logic_error);
// explicit conversions
CHECK_NOTHROW(auto v = j.get<json::array_t>());
CHECK_THROWS_AS(auto v = j.get<json::object_t>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<std::string>(), std::logic_error);
CHECK_NOTHROW(auto v = j.get<bool>());
CHECK_THROWS_AS(auto v = j.get<int>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<int64_t>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<double>(), std::logic_error);
// transparent usage
auto id = [](bool v)
{
return v;
};
CHECK(id(j) == j.get<bool>());
// copy constructor
json k(j);
CHECK(k == j);
// copy assignment
k = j;
CHECK(k == j);
// move constructor
json l = std::move(k);
CHECK(l == j);
}
SECTION("Create from value")
{
json j1 = true;
bool v1 = j1;
CHECK(j1.get<bool>() == v1);
json j2 = false;
bool v2 = j2;
CHECK(j2.get<bool>() == v2);
}
SECTION("Operators")
{
// clear()
json j1 = true;
CHECK(j1.get<bool>() == true);
j1.clear();
CHECK(j1.get<bool>() == false);
}
SECTION("Container operations")
{
json b1 = true;
json b2 = false;
b1.swap(b2);
CHECK(b1 == json(false));
CHECK(b2 == json(true));
std::swap(b1, b2);
CHECK(b1 == json(true));
CHECK(b2 == json(false));
}
}
TEST_CASE("number (int)")
{
SECTION("Basics")
{
// construction with given type
json j(json::value_t::number);
CHECK(j.type() == json::value_t::number);
// const object
const json j_const = j;
// iterators
CHECK(j.begin() != j.end());
CHECK(j.cbegin() != j.cend());
// string representation of default value
CHECK(j.dump() == "0");
// container members
CHECK(j.size() == 1);
CHECK(j.max_size() == 1);
CHECK(j.empty() == false);
CHECK_NOTHROW(size_t h = std::hash<json>()(j));
// implicit conversions
CHECK_NOTHROW(json::array_t v = j);
CHECK_THROWS_AS(json::object_t v = j, std::logic_error);
CHECK_THROWS_AS(std::string v = j, std::logic_error);
CHECK_THROWS_AS(bool v = j, std::logic_error);
CHECK_NOTHROW(int v = j);
CHECK_NOTHROW(double v = j);
// explicit conversions
CHECK_NOTHROW(auto v = j.get<json::array_t>());
CHECK_THROWS_AS(auto v = j.get<json::object_t>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<std::string>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<bool>(), std::logic_error);
CHECK_NOTHROW(auto v = j.get<int>());
CHECK_NOTHROW(auto v = j.get<int64_t>());
CHECK_NOTHROW(auto v = j.get<double>());
// transparent usage
auto id = [](int v)
{
return v;
};
CHECK(id(j) == j.get<int>());
// copy constructor
json k(j);
CHECK(k == j);
// copy assignment
k = j;
CHECK(k == j);
// move constructor
json l = std::move(k);
CHECK(l == j);
}
SECTION("Create from value")
{
json j1 = 23;
int v1 = j1;
CHECK(j1.get<int>() == v1);
json j2 = 42;
int v2 = j2;
CHECK(j2.get<int>() == v2);
json j3 = 2147483647;
int v3 = j3;
CHECK(j3.get<int>() == v3);
json j4 = 9223372036854775807;
int64_t v4 = j4;
CHECK(j4.get<int64_t>() == v4);
}
SECTION("Operators")
{
// clear()
json j1 = 42;
CHECK(j1.get<int>() == 42);
j1.clear();
CHECK(j1.get<int>() == 0);
// find()
CHECK(j1.find("foo") == j1.end());
CHECK(j1.find(std::string("foo")) == j1.end());
const json j2 = j1;
CHECK(j2.find("foo") == j2.end());
CHECK(j2.find(std::string("foo")) == j2.end());
}
SECTION("Container operations")
{
json n1 = 23;
json n2 = 42;
n1.swap(n2);
CHECK(n1 == json(42));
CHECK(n2 == json(23));
std::swap(n1, n2);
CHECK(n1 == json(23));
CHECK(n2 == json(42));
}
}
TEST_CASE("number (float)")
{
SECTION("Basics")
{
// construction with given type
json j(json::value_t::number_float);
CHECK(j.type() == json::value_t::number_float);
// const object
const json j_const = j;
// iterators
CHECK(j.begin() != j.end());
CHECK(j.cbegin() != j.cend());
// string representation of default value
CHECK(j.dump() == "0.000000");
// container members
CHECK(j.size() == 1);
CHECK(j.max_size() == 1);
CHECK(j.empty() == false);
CHECK_NOTHROW(size_t h = std::hash<json>()(j));
// implicit conversions
CHECK_NOTHROW(json::array_t v = j);
CHECK_THROWS_AS(json::object_t v = j, std::logic_error);
CHECK_THROWS_AS(std::string v = j, std::logic_error);
CHECK_THROWS_AS(bool v = j, std::logic_error);
CHECK_NOTHROW(int v = j);
CHECK_NOTHROW(double v = j);
// explicit conversions
CHECK_NOTHROW(auto v = j.get<json::array_t>());
CHECK_THROWS_AS(auto v = j.get<json::object_t>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<std::string>(), std::logic_error);
CHECK_THROWS_AS(auto v = j.get<bool>(), std::logic_error);
CHECK_NOTHROW(auto v = j.get<int>());
CHECK_NOTHROW(auto v = j.get<int64_t>());
CHECK_NOTHROW(auto v = j.get<double>());
// transparent usage
auto id = [](double v)
{
return v;
};
CHECK(id(j) == j.get<double>());
// copy constructor
json k(j);
CHECK(k == j);
// copy assignment
k = j;
CHECK(k == j);
// move constructor
json l = std::move(k);
CHECK(l == j);
}
SECTION("Create from value")
{
json j1 = 3.1415926;
double v1 = j1;
CHECK(j1.get<double>() == v1);
json j2 = 2.7182818;
double v2 = j2;
CHECK(j2.get<double>() == v2);
int64_t v3 = j2;
CHECK(j2.get<int64_t>() == v3);
}
SECTION("Operators")
{
// clear()
json j1 = 3.1415926;
CHECK(j1.get<double>() == 3.1415926);
j1.clear();
CHECK(j1.get<double>() == 0.0);
}
SECTION("Container operations")
{
json n1 = 23.42;
json n2 = 42.23;
n1.swap(n2);
CHECK(n1 == json(42.23));
CHECK(n2 == json(23.42));
std::swap(n1, n2);
CHECK(n1 == json(23.42));
CHECK(n2 == json(42.23));
}
}
TEST_CASE("Iterators")
{
json j1 = {0, 1, 2, 3, 4};
json j2 = {{"foo", "bar"}, {"baz", "bam"}};
json j3 = true;
json j4 = nullptr;
json j5 = 42;
json j6 = 23.42;
json j7 = "hello";
const json j1_const = {0, 1, 2, 3, 4};
const json j2_const = {{"foo", "bar"}, {"baz", "bam"}};
const json j3_const = true;
const json j4_const = nullptr;
const json j5_const = 42;
const json j6_const = 23.42;
const json j7_const = "hello";
// operator *
CHECK(* j1.begin() == json(0));
CHECK(* j1_const.begin() == json(0));
CHECK(* j2.begin() != json());
CHECK(* j2_const.begin() != json());
CHECK(* j3.begin() == json(true));
CHECK(* j3_const.begin() == json(true));
CHECK(* j4.begin() == json());
CHECK(* j4_const.begin() == json());
CHECK(* j5.begin() == json(42));
CHECK(* j5_const.begin() == json(42));
CHECK(* j6.begin() == json(23.42));
CHECK(* j6_const.begin() == json(23.42));
CHECK(* j7.begin() == json("hello"));
CHECK(* j7_const.begin() == json("hello"));
CHECK_THROWS_AS(* j1.end(), std::out_of_range);
CHECK_THROWS_AS(* j1.cend(), std::out_of_range);
CHECK_THROWS_AS(* j2.end(), std::out_of_range);
CHECK_THROWS_AS(* j2.cend(), std::out_of_range);
CHECK_THROWS_AS(* j3.end(), std::out_of_range);
CHECK_THROWS_AS(* j3.cend(), std::out_of_range);
CHECK_THROWS_AS(* j4.end(), std::out_of_range);
CHECK_THROWS_AS(* j4.cend(), std::out_of_range);
CHECK_THROWS_AS(* j5.end(), std::out_of_range);
CHECK_THROWS_AS(* j5.cend(), std::out_of_range);
CHECK_THROWS_AS(* j6.end(), std::out_of_range);
CHECK_THROWS_AS(* j6.cend(), std::out_of_range);
CHECK_THROWS_AS(* j7.end(), std::out_of_range);
CHECK_THROWS_AS(* j7.cend(), std::out_of_range);
CHECK_THROWS_AS(* j1_const.end(), std::out_of_range);
CHECK_THROWS_AS(* j1_const.cend(), std::out_of_range);
CHECK_THROWS_AS(* j2_const.end(), std::out_of_range);
CHECK_THROWS_AS(* j2_const.cend(), std::out_of_range);
CHECK_THROWS_AS(* j3_const.end(), std::out_of_range);
CHECK_THROWS_AS(* j3_const.cend(), std::out_of_range);
CHECK_THROWS_AS(* j4_const.end(), std::out_of_range);
CHECK_THROWS_AS(* j4_const.cend(), std::out_of_range);
CHECK_THROWS_AS(* j5_const.end(), std::out_of_range);
CHECK_THROWS_AS(* j5_const.cend(), std::out_of_range);
CHECK_THROWS_AS(* j6_const.end(), std::out_of_range);
CHECK_THROWS_AS(* j6_const.cend(), std::out_of_range);
CHECK_THROWS_AS(* j7_const.end(), std::out_of_range);
CHECK_THROWS_AS(* j7_const.cend(), std::out_of_range);
// operator ->
CHECK(j1.begin()->type() == json::value_t::number);
CHECK(j1.cbegin()->type() == json::value_t::number);
CHECK(j2.begin()->type() == json::value_t::string);
CHECK(j2.cbegin()->type() == json::value_t::string);
CHECK(j3.begin()->type() == json::value_t::boolean);
CHECK(j3.cbegin()->type() == json::value_t::boolean);
CHECK(j4.begin()->type() == json::value_t::null);
CHECK(j4.cbegin()->type() == json::value_t::null);
CHECK(j5.begin()->type() == json::value_t::number);
CHECK(j5.cbegin()->type() == json::value_t::number);
CHECK(j6.begin()->type() == json::value_t::number_float);
CHECK(j6.cbegin()->type() == json::value_t::number_float);
CHECK(j7.begin()->type() == json::value_t::string);
CHECK(j7.cbegin()->type() == json::value_t::string);
CHECK(j1_const.begin()->type() == json::value_t::number);
CHECK(j1_const.cbegin()->type() == json::value_t::number);
CHECK(j2_const.begin()->type() == json::value_t::string);
CHECK(j2_const.cbegin()->type() == json::value_t::string);
CHECK(j3_const.begin()->type() == json::value_t::boolean);
CHECK(j3_const.cbegin()->type() == json::value_t::boolean);
CHECK(j4_const.begin()->type() == json::value_t::null);
CHECK(j4_const.cbegin()->type() == json::value_t::null);
CHECK(j5_const.begin()->type() == json::value_t::number);
CHECK(j5_const.cbegin()->type() == json::value_t::number);
CHECK(j6_const.begin()->type() == json::value_t::number_float);
CHECK(j6_const.cbegin()->type() == json::value_t::number_float);
CHECK(j7_const.begin()->type() == json::value_t::string);
CHECK(j7_const.cbegin()->type() == json::value_t::string);
CHECK_THROWS_AS(j1.end()->type(), std::out_of_range);
CHECK_THROWS_AS(j1.cend()->type(), std::out_of_range);
CHECK_THROWS_AS(j2.end()->type(), std::out_of_range);
CHECK_THROWS_AS(j2.cend()->type(), std::out_of_range);
CHECK_THROWS_AS(j3.end()->type(), std::out_of_range);
CHECK_THROWS_AS(j3.cend()->type(), std::out_of_range);
CHECK_THROWS_AS(j4.end()->type(), std::out_of_range);
CHECK_THROWS_AS(j4.cend()->type(), std::out_of_range);
CHECK_THROWS_AS(j5.end()->type(), std::out_of_range);
CHECK_THROWS_AS(j5.cend()->type(), std::out_of_range);
CHECK_THROWS_AS(j6.end()->type(), std::out_of_range);
CHECK_THROWS_AS(j6.cend()->type(), std::out_of_range);
CHECK_THROWS_AS(j7.end()->type(), std::out_of_range);
CHECK_THROWS_AS(j7.cend()->type(), std::out_of_range);
CHECK_THROWS_AS(j1_const.end()->type(), std::out_of_range);
CHECK_THROWS_AS(j1_const.cend()->type(), std::out_of_range);
CHECK_THROWS_AS(j2_const.end()->type(), std::out_of_range);
CHECK_THROWS_AS(j2_const.cend()->type(), std::out_of_range);
CHECK_THROWS_AS(j3_const.end()->type(), std::out_of_range);
CHECK_THROWS_AS(j3_const.cend()->type(), std::out_of_range);
CHECK_THROWS_AS(j4_const.end()->type(), std::out_of_range);
CHECK_THROWS_AS(j4_const.cend()->type(), std::out_of_range);
CHECK_THROWS_AS(j5_const.end()->type(), std::out_of_range);
CHECK_THROWS_AS(j5_const.cend()->type(), std::out_of_range);
CHECK_THROWS_AS(j6_const.end()->type(), std::out_of_range);
CHECK_THROWS_AS(j6_const.cend()->type(), std::out_of_range);
CHECK_THROWS_AS(j7_const.end()->type(), std::out_of_range);
CHECK_THROWS_AS(j7_const.cend()->type(), std::out_of_range);
// value
CHECK(j1.begin().value().type() == json::value_t::number);
CHECK(j1.cbegin().value().type() == json::value_t::number);
CHECK(j2.begin().value().type() == json::value_t::string);
CHECK(j2.cbegin().value().type() == json::value_t::string);
CHECK(j3.begin().value().type() == json::value_t::boolean);
CHECK(j3.cbegin().value().type() == json::value_t::boolean);
CHECK(j4.begin().value().type() == json::value_t::null);
CHECK(j4.cbegin().value().type() == json::value_t::null);
CHECK(j5.begin().value().type() == json::value_t::number);
CHECK(j5.cbegin().value().type() == json::value_t::number);
CHECK(j6.begin().value().type() == json::value_t::number_float);
CHECK(j6.cbegin().value().type() == json::value_t::number_float);
CHECK(j7.begin().value().type() == json::value_t::string);
CHECK(j7.cbegin().value().type() == json::value_t::string);
CHECK(j1_const.begin().value().type() == json::value_t::number);
CHECK(j1_const.cbegin().value().type() == json::value_t::number);
CHECK(j2_const.begin().value().type() == json::value_t::string);
CHECK(j2_const.cbegin().value().type() == json::value_t::string);
CHECK(j3_const.begin().value().type() == json::value_t::boolean);
CHECK(j3_const.cbegin().value().type() == json::value_t::boolean);
CHECK(j4_const.begin().value().type() == json::value_t::null);
CHECK(j4_const.cbegin().value().type() == json::value_t::null);
CHECK(j5_const.begin().value().type() == json::value_t::number);
CHECK(j5_const.cbegin().value().type() == json::value_t::number);
CHECK(j6_const.begin().value().type() == json::value_t::number_float);
CHECK(j6_const.cbegin().value().type() == json::value_t::number_float);
CHECK(j7_const.begin().value().type() == json::value_t::string);
CHECK(j7_const.cbegin().value().type() == json::value_t::string);
CHECK_THROWS_AS(j1.end().value(), std::out_of_range);
CHECK_THROWS_AS(j1.cend().value(), std::out_of_range);
CHECK_THROWS_AS(j2.end().value(), std::out_of_range);
CHECK_THROWS_AS(j2.cend().value(), std::out_of_range);
CHECK_THROWS_AS(j3.end().value(), std::out_of_range);
CHECK_THROWS_AS(j3.cend().value(), std::out_of_range);
CHECK_THROWS_AS(j4.end().value(), std::out_of_range);
CHECK_THROWS_AS(j4.cend().value(), std::out_of_range);
CHECK_THROWS_AS(j5.end().value(), std::out_of_range);
CHECK_THROWS_AS(j5.cend().value(), std::out_of_range);
CHECK_THROWS_AS(j6.end().value(), std::out_of_range);
CHECK_THROWS_AS(j6.cend().value(), std::out_of_range);
CHECK_THROWS_AS(j7.end().value(), std::out_of_range);
CHECK_THROWS_AS(j7.cend().value(), std::out_of_range);
CHECK_THROWS_AS(j1_const.end().value(), std::out_of_range);
CHECK_THROWS_AS(j1_const.cend().value(), std::out_of_range);
CHECK_THROWS_AS(j2_const.end().value(), std::out_of_range);
CHECK_THROWS_AS(j2_const.cend().value(), std::out_of_range);
CHECK_THROWS_AS(j3_const.end().value(), std::out_of_range);
CHECK_THROWS_AS(j3_const.cend().value(), std::out_of_range);
CHECK_THROWS_AS(j4_const.end().value(), std::out_of_range);
CHECK_THROWS_AS(j4_const.cend().value(), std::out_of_range);
CHECK_THROWS_AS(j5_const.end().value(), std::out_of_range);
CHECK_THROWS_AS(j5_const.cend().value(), std::out_of_range);
CHECK_THROWS_AS(j6_const.end().value(), std::out_of_range);
CHECK_THROWS_AS(j6_const.cend().value(), std::out_of_range);
CHECK_THROWS_AS(j7_const.end().value(), std::out_of_range);
CHECK_THROWS_AS(j7_const.cend().value(), std::out_of_range);
// iterator comparison
CHECK(j1.begin() != j2.begin());
CHECK(j1.begin() != j3.begin());
CHECK(j1.begin() != j4.begin());
CHECK(j1.begin() != j5.begin());
CHECK(j1.begin() != j6.begin());
CHECK(j1.begin() != j7.begin());
CHECK(j1.cbegin() != j2.cbegin());
CHECK(j1.cbegin() != j3.cbegin());
CHECK(j1.cbegin() != j4.cbegin());
CHECK(j1.cbegin() != j5.cbegin());
CHECK(j1.cbegin() != j6.cbegin());
CHECK(j1.cbegin() != j7.cbegin());
CHECK(j2.begin() != j1.begin());
CHECK(j2.begin() != j3.begin());
CHECK(j2.begin() != j4.begin());
CHECK(j2.begin() != j5.begin());
CHECK(j2.begin() != j6.begin());
CHECK(j2.begin() != j7.begin());
CHECK(j2.cbegin() != j1.cbegin());
CHECK(j2.cbegin() != j3.cbegin());
CHECK(j2.cbegin() != j4.cbegin());
CHECK(j2.cbegin() != j5.cbegin());
CHECK(j2.cbegin() != j6.cbegin());
CHECK(j2.cbegin() != j7.cbegin());
CHECK(j3.begin() != j1.begin());
CHECK(j3.begin() != j2.begin());
CHECK(j3.begin() != j4.begin());
CHECK(j3.begin() != j5.begin());
CHECK(j3.begin() != j6.begin());
CHECK(j3.begin() != j7.begin());
CHECK(j3.cbegin() != j1.cbegin());
CHECK(j3.cbegin() != j2.cbegin());
CHECK(j3.cbegin() != j4.cbegin());
CHECK(j3.cbegin() != j5.cbegin());
CHECK(j3.cbegin() != j6.cbegin());
CHECK(j3.cbegin() != j7.cbegin());
CHECK(j4.begin() != j1.begin());
CHECK(j4.begin() != j2.begin());
CHECK(j4.begin() != j3.begin());
CHECK(j4.begin() != j5.begin());
CHECK(j4.begin() != j6.begin());
CHECK(j4.begin() != j7.begin());
CHECK(j4.cbegin() != j1.cbegin());
CHECK(j4.cbegin() != j2.cbegin());
CHECK(j4.cbegin() != j3.cbegin());
CHECK(j4.cbegin() != j5.cbegin());
CHECK(j4.cbegin() != j6.cbegin());
CHECK(j4.cbegin() != j7.cbegin());
CHECK(j5.begin() != j1.begin());
CHECK(j5.begin() != j2.begin());
CHECK(j5.begin() != j3.begin());
CHECK(j5.begin() != j4.begin());
CHECK(j5.begin() != j6.begin());
CHECK(j5.begin() != j7.begin());
CHECK(j5.cbegin() != j1.cbegin());
CHECK(j5.cbegin() != j2.cbegin());
CHECK(j5.cbegin() != j3.cbegin());
CHECK(j5.cbegin() != j4.cbegin());
CHECK(j5.cbegin() != j6.cbegin());
CHECK(j5.cbegin() != j7.cbegin());
CHECK(j6.begin() != j1.begin());
CHECK(j6.begin() != j2.begin());
CHECK(j6.begin() != j3.begin());
CHECK(j6.begin() != j4.begin());
CHECK(j6.begin() != j5.begin());
CHECK(j6.begin() != j7.begin());
CHECK(j6.cbegin() != j1.cbegin());
CHECK(j6.cbegin() != j2.cbegin());
CHECK(j6.cbegin() != j3.cbegin());
CHECK(j6.cbegin() != j4.cbegin());
CHECK(j6.cbegin() != j5.cbegin());
CHECK(j6.cbegin() != j7.cbegin());
CHECK(j7.begin() != j1.begin());
CHECK(j7.begin() != j2.begin());
CHECK(j7.begin() != j3.begin());
CHECK(j7.begin() != j4.begin());
CHECK(j7.begin() != j5.begin());
CHECK(j7.begin() != j6.begin());
CHECK(j7.cbegin() != j1.cbegin());
CHECK(j7.cbegin() != j2.cbegin());
CHECK(j7.cbegin() != j3.cbegin());
CHECK(j7.cbegin() != j4.cbegin());
CHECK(j7.cbegin() != j5.cbegin());
CHECK(j7.cbegin() != j6.cbegin());
// iterator copy constructors
{
json::iterator tmp1(j1.begin());
json::const_iterator tmp2(j1.cbegin());
}
{
json::iterator tmp1(j2.begin());
json::const_iterator tmp2(j2.cbegin());
}
{
json::iterator tmp1(j3.begin());
json::const_iterator tmp2(j3.cbegin());
}
{
json::iterator tmp1(j4.begin());
json::const_iterator tmp2(j4.cbegin());
}
{
json::iterator tmp1(j5.begin());
json::const_iterator tmp2(j5.cbegin());
}
{
json::iterator tmp1(j6.begin());
json::const_iterator tmp2(j6.cbegin());
}
{
json::iterator tmp1(j7.begin());
json::const_iterator tmp2(j7.cbegin());
}
{
json j_array = {0, 1, 2, 3, 4, 5};
json::iterator i1 = j_array.begin();
++i1;
json::iterator i2(i1);
json::iterator i3;
i3 = i2;
CHECK(i1 == i1);
json::const_iterator i4 = j_array.begin();
++i4;
json::const_iterator i5(i4);
json::const_iterator i6;
i6 = i5;
CHECK(i4 == i4);
}
{
json j_object = {{"1", 1}, {"2", 2}, {"3", 3}};
json::iterator i1 = j_object.begin();
++i1;
json::iterator i11 = j_object.begin();
CHECK((i1 == i11) == false);
json::iterator i2(i1);
json::iterator i3;
i3 = i2;
CHECK(i1 == i1);
json::const_iterator i4 = j_object.begin();
++i4;
json::iterator i41 = j_object.begin();
CHECK((i4 == i41) == false);
json::const_iterator i5(i4);
json::const_iterator i6;
i6 = i5;
CHECK(i4 == i4);
}
// iterator copy assignment
{
json::iterator i1 = j2.begin();
json::const_iterator i2 = j2.cbegin();
json::iterator i3 = i1;
json::const_iterator i4 = i2;
}
// operator++
{
json j;
const json j_const = j;
{
json::iterator i = j.begin();
++i;
CHECK(i == j.end());
++i;
CHECK(i == j.end());
}
{
json::const_iterator i = j.begin();
++i;
CHECK(i == j.end());
++i;
CHECK(i == j.end());
}
{
json::const_iterator i = j_const.begin();
++i;
CHECK(i == j_const.end());
++i;
CHECK(i == j_const.end());
}
{
json::const_iterator i = j.cbegin();
++i;
CHECK(i == j.cend());
++i;
CHECK(i == j.cend());
}
{
json::const_iterator i = j_const.cbegin();
++i;
CHECK(i == j_const.cend());
++i;
CHECK(i == j_const.cend());
}
}
}
TEST_CASE("Comparisons")
{
json j1 = {0, 1, 2, 3, 4};
json j2 = {{"foo", "bar"}, {"baz", "bam"}};
json j3 = true;
json j4 = nullptr;
json j5 = 42;
json j6 = 23.42;
json j7 = "hello";
CHECK((j1 == j1) == true);
CHECK((j1 == j2) == false);
CHECK((j1 == j3) == false);
CHECK((j1 == j4) == false);
CHECK((j1 == j5) == false);
CHECK((j1 == j6) == false);
CHECK((j1 == j7) == false);
CHECK((j2 == j1) == false);
CHECK((j2 == j2) == true);
CHECK((j2 == j3) == false);
CHECK((j2 == j4) == false);
CHECK((j2 == j5) == false);
CHECK((j2 == j6) == false);
CHECK((j2 == j7) == false);
CHECK((j3 == j1) == false);
CHECK((j3 == j2) == false);
CHECK((j3 == j3) == true);
CHECK((j3 == j4) == false);
CHECK((j3 == j5) == false);
CHECK((j3 == j6) == false);
CHECK((j3 == j7) == false);
CHECK((j4 == j1) == false);
CHECK((j4 == j2) == false);
CHECK((j4 == j3) == false);
CHECK((j4 == j4) == true);
CHECK((j4 == j5) == false);
CHECK((j4 == j6) == false);
CHECK((j4 == j7) == false);
CHECK((j5 == j1) == false);
CHECK((j5 == j2) == false);
CHECK((j5 == j3) == false);
CHECK((j5 == j4) == false);
CHECK((j5 == j5) == true);
CHECK((j5 == j6) == false);
CHECK((j5 == j7) == false);
CHECK((j6 == j1) == false);
CHECK((j6 == j2) == false);
CHECK((j6 == j3) == false);
CHECK((j6 == j4) == false);
CHECK((j6 == j5) == false);
CHECK((j6 == j6) == true);
CHECK((j6 == j7) == false);
CHECK((j7 == j1) == false);
CHECK((j7 == j2) == false);
CHECK((j7 == j3) == false);
CHECK((j7 == j4) == false);
CHECK((j7 == j5) == false);
CHECK((j7 == j6) == false);
CHECK((j7 == j7) == true);
CHECK((j1 != j1) == false);
CHECK((j1 != j2) == true);
CHECK((j1 != j3) == true);
CHECK((j1 != j4) == true);
CHECK((j1 != j5) == true);
CHECK((j1 != j6) == true);
CHECK((j1 != j7) == true);
CHECK((j2 != j1) == true);
CHECK((j2 != j2) == false);
CHECK((j2 != j3) == true);
CHECK((j2 != j4) == true);
CHECK((j2 != j5) == true);
CHECK((j2 != j6) == true);
CHECK((j2 != j7) == true);
CHECK((j3 != j1) == true);
CHECK((j3 != j2) == true);
CHECK((j3 != j3) == false);
CHECK((j3 != j4) == true);
CHECK((j3 != j5) == true);
CHECK((j3 != j6) == true);
CHECK((j3 != j7) == true);
CHECK((j4 != j1) == true);
CHECK((j4 != j2) == true);
CHECK((j4 != j3) == true);
CHECK((j4 != j4) == false);
CHECK((j4 != j5) == true);
CHECK((j4 != j6) == true);
CHECK((j4 != j7) == true);
CHECK((j5 != j1) == true);
CHECK((j5 != j2) == true);
CHECK((j5 != j3) == true);
CHECK((j5 != j4) == true);
CHECK((j5 != j5) == false);
CHECK((j5 != j6) == true);
CHECK((j5 != j7) == true);
CHECK((j6 != j1) == true);
CHECK((j6 != j2) == true);
CHECK((j6 != j3) == true);
CHECK((j6 != j4) == true);
CHECK((j6 != j5) == true);
CHECK((j6 != j6) == false);
CHECK((j6 != j7) == true);
CHECK((j7 != j1) == true);
CHECK((j7 != j2) == true);
CHECK((j7 != j3) == true);
CHECK((j7 != j4) == true);
CHECK((j7 != j5) == true);
CHECK((j7 != j6) == true);
CHECK((j7 != j7) == false);
}
TEST_CASE("Parser")
{
SECTION("null")
{
// accept the exact values
CHECK(json::parse("null") == json(nullptr));
// ignore whitespace
CHECK(json::parse(" null ") == json(nullptr));
CHECK(json::parse("\tnull\n") == json(nullptr));
// respect capitalization
CHECK_THROWS_AS(json::parse("Null"), std::invalid_argument);
CHECK_THROWS_AS(json::parse("NULL"), std::invalid_argument);
// do not accept prefixes
CHECK_THROWS_AS(json::parse("n"), std::invalid_argument);
CHECK_THROWS_AS(json::parse("nu"), std::invalid_argument);
CHECK_THROWS_AS(json::parse("nul"), std::invalid_argument);
}
SECTION("string")
{
// accept some values
CHECK(json::parse("\"\"") == json(""));
CHECK(json::parse("\"foo\"") == json("foo"));
// escaping quotes
CHECK_THROWS_AS(json::parse("\"\\\""), std::invalid_argument);
CHECK_NOTHROW(json::parse("\"\\\"\""));
// escaping backslashes
CHECK(json::parse("\"a\\\\z\"") == json("a\\z"));
CHECK(json::parse("\"\\\\\"") == json("\\"));
CHECK(json::parse("\"\\\\a\\\\\"") == json("\\a\\"));
CHECK(json::parse("\"\\\\\\\\\"") == json("\\\\"));
// escaping slash
CHECK(json::parse("\"a\\/z\"") == json("a/z"));
CHECK(json::parse("\"\\/\"") == json("/"));
// escaping tabs
CHECK(json::parse("\"a\\tz\"") == json("a\tz"));
CHECK(json::parse("\"\\t\"") == json("\t"));
// escaping formfeed
CHECK(json::parse("\"a\\fz\"") == json("a\fz"));
CHECK(json::parse("\"\\f\"") == json("\f"));
// escaping carriage return
CHECK(json::parse("\"a\\rz\"") == json("a\rz"));
CHECK(json::parse("\"\\r\"") == json("\r"));
// escaping backspace
CHECK(json::parse("\"a\\bz\"") == json("a\bz"));
CHECK(json::parse("\"\\b\"") == json("\b"));
// escaping newline
CHECK(json::parse("\"a\\nz\"") == json("a\nz"));
CHECK(json::parse("\"\\n\"") == json("\n"));
// escaping senseless stuff
CHECK_THROWS_AS(json::parse("\"\\z\""), std::invalid_argument);
CHECK_THROWS_AS(json::parse("\"\\ \""), std::invalid_argument);
CHECK_THROWS_AS(json::parse("\"\\9\""), std::invalid_argument);
// quotes must be closed
CHECK_THROWS_AS(json::parse("\""), std::invalid_argument);
}
SECTION("unicode_escaping")
{
// two tests for uppercase and lowercase hex
// normal forward slash in ASCII range
CHECK(json::parse("\"\\u002F\"") == json("/"));
CHECK(json::parse("\"\\u002f\"") == json("/"));
// german a umlaut
CHECK(json::parse("\"\\u00E4\"") == json(u8"\u00E4"));
CHECK(json::parse("\"\\u00e4\"") == json(u8"\u00E4"));
// weird d
CHECK(json::parse("\"\\u0111\"") == json(u8"\u0111"));
// unicode arrow left
CHECK(json::parse("\"\\u2190\"") == json(u8"\u2190"));
// pleasing osiris by testing hieroglyph support
CHECK(json::parse("\"\\uD80C\\uDC60\"") == json(u8"\U00013060"));
CHECK(json::parse("\"\\ud80C\\udc60\"") == json(u8"\U00013060"));
// no hex numbers behind the \u
CHECK_THROWS_AS(json::parse("\"\\uD80v\""), std::invalid_argument);
CHECK_THROWS_AS(json::parse("\"\\uD80 A\""), std::invalid_argument);
CHECK_THROWS_AS(json::parse("\"\\uD8v\""), std::invalid_argument);
CHECK_THROWS_AS(json::parse("\"\\uDv\""), std::invalid_argument);
CHECK_THROWS_AS(json::parse("\"\\uv\""), std::invalid_argument);
CHECK_THROWS_AS(json::parse("\"\\u\""), std::invalid_argument);
CHECK_THROWS_AS(json::parse("\"\\u\\u\""), std::invalid_argument);
CHECK_THROWS_AS(json::parse("\"a\\uD80vAz\""), std::invalid_argument);
// missing part of a surrogate pair
CHECK_THROWS_AS(json::parse("\"bla \\uD80C bla\""), std::invalid_argument);
CHECK_THROWS_AS(json::parse("\"\\uD80C bla bla\""), std::invalid_argument);
CHECK_THROWS_AS(json::parse("\"bla bla \\uD80C bla bla\""), std::invalid_argument);
// senseless 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);
// test private code point converter function
CHECK_NOTHROW(json::parser("").codePointToUTF8(0x10FFFE));
CHECK_NOTHROW(json::parser("").codePointToUTF8(0x10FFFF));
CHECK_THROWS_AS(json::parser("").codePointToUTF8(0x110000), std::invalid_argument);
CHECK_THROWS_AS(json::parser("").codePointToUTF8(0x110001), std::invalid_argument);
}
SECTION("boolean")
{
// accept the exact values
CHECK(json::parse("true") == json(true));
CHECK(json::parse("false") == json(false));
// ignore whitespace
CHECK(json::parse(" true ") == json(true));
CHECK(json::parse("\tfalse\n") == json(false));
// respect capitalization
CHECK_THROWS_AS(json::parse("True"), std::invalid_argument);
CHECK_THROWS_AS(json::parse("False"), std::invalid_argument);
// do not accept prefixes
CHECK_THROWS_AS(json::parse("t"), std::invalid_argument);
CHECK_THROWS_AS(json::parse("tr"), std::invalid_argument);
CHECK_THROWS_AS(json::parse("tru"), std::invalid_argument);
CHECK_THROWS_AS(json::parse("f"), std::invalid_argument);
CHECK_THROWS_AS(json::parse("fa"), std::invalid_argument);
CHECK_THROWS_AS(json::parse("fal"), std::invalid_argument);
CHECK_THROWS_AS(json::parse("fals"), std::invalid_argument);
}
SECTION("number (int)")
{
// accept the exact values
CHECK(json::parse("0") == json(0));
CHECK(json::parse("-0") == json(0));
CHECK(json::parse("1") == json(1));
CHECK(json::parse("-1") == json(-1));
CHECK(json::parse("12345678") == json(12345678));
CHECK(json::parse("-12345678") == json(-12345678));
CHECK(json::parse("0.0") == json(0));
CHECK(json::parse("-0.0") == json(0));
CHECK(json::parse("1.0") == json(1));
CHECK(json::parse("-1.0") == json(-1));
CHECK(json::parse("12345678.0") == json(12345678));
CHECK(json::parse("-12345678.0") == json(-12345678));
CHECK(json::parse("17e0") == json(17));
CHECK(json::parse("17e1") == json(170));
CHECK(json::parse("17e3") == json(17000));
CHECK(json::parse("17e+0") == json(17));
CHECK(json::parse("17e+1") == json(170));
CHECK(json::parse("17e+3") == json(17000));
CHECK(json::parse("17E0") == json(17));
CHECK(json::parse("17E1") == json(170));
CHECK(json::parse("17E3") == json(17000));
CHECK(json::parse("17E+0") == json(17));
CHECK(json::parse("17E+1") == json(170));
CHECK(json::parse("17E+3") == json(17000));
CHECK(json::parse("10000e-0") == json(10000));
CHECK(json::parse("10000e-1") == json(1000));
CHECK(json::parse("10000e-4") == json(1));
CHECK(json::parse("10000E-0") == json(10000));
CHECK(json::parse("10000E-1") == json(1000));
CHECK(json::parse("10000E-4") == json(1));
CHECK(json::parse("17.0e0") == json(17));
CHECK(json::parse("17.0e1") == json(170));
CHECK(json::parse("17.0e3") == json(17000));
CHECK(json::parse("17.0e+0") == json(17));
CHECK(json::parse("17.0e+1") == json(170));
CHECK(json::parse("17.0e+3") == json(17000));
CHECK(json::parse("17.0E0") == json(17));
CHECK(json::parse("17.0E1") == json(170));
CHECK(json::parse("17.0E3") == json(17000));
CHECK(json::parse("17.0E+0") == json(17));
CHECK(json::parse("17.0E+1") == json(170));
CHECK(json::parse("17.0E+3") == json(17000));
CHECK(json::parse("10000.0e-0") == json(10000));
CHECK(json::parse("10000.0e-1") == json(1000));
CHECK(json::parse("10000.0e-4") == json(1));
CHECK(json::parse("10000.0E-0") == json(10000));
CHECK(json::parse("10000.0E-1") == json(1000));
CHECK(json::parse("10000.0E-4") == json(1));
// 64 bit integers
CHECK(json::parse("9000000000000000000") == json(9000000000000000000));
CHECK(json::parse("-9000000000000000000") == json(-9000000000000000000));
// trailing zero is not allowed
//CHECK_THROWS_AS(json::parse("01"), std::invalid_argument);
// whitespace inbetween is an error
//CHECK_THROWS_AS(json::parse("1 0"), std::invalid_argument);
// only one minus is allowd
CHECK_THROWS_AS(json::parse("--1"), std::invalid_argument);
// string representations are not allowed
CHECK_THROWS_AS(json::parse("NAN"), std::invalid_argument);
CHECK_THROWS_AS(json::parse("nan"), std::invalid_argument);
CHECK_THROWS_AS(json::parse("INF"), std::invalid_argument);
CHECK_THROWS_AS(json::parse("inf"), std::invalid_argument);
CHECK_THROWS_AS(json::parse("INFINITY"), std::invalid_argument);
CHECK_THROWS_AS(json::parse("infinity"), std::invalid_argument);
}
SECTION("number (float)")
{
// accept the exact values
CHECK(json::parse("0.5") == json(0.5));
CHECK(json::parse("-0.5") == json(-0.5));
CHECK(json::parse("1.5") == json(1.5));
CHECK(json::parse("-1.5") == json(-1.5));
CHECK(json::parse("12345678.5") == json(12345678.5));
CHECK(json::parse("-12345678.5") == json(-12345678.5));
CHECK(json::parse("17.5e0") == json(17.5));
CHECK(json::parse("17.5e1") == json(175));
CHECK(json::parse("17.5e3") == json(17500));
CHECK(json::parse("17.5e+0") == json(17.5));
CHECK(json::parse("17.5e+1") == json(175));
CHECK(json::parse("17.5e+3") == json(17500));
CHECK(json::parse("17.5E0") == json(17.5));
CHECK(json::parse("17.5E1") == json(175));
CHECK(json::parse("17.5E3") == json(17500));
CHECK(json::parse("17.5E+0") == json(17.5));
CHECK(json::parse("17.5E+1") == json(175));
CHECK(json::parse("17.5E+3") == json(17500));
CHECK(json::parse("10000.5e-0") == json(10000.5));
CHECK(json::parse("10000.5e-1") == json(1000.05));
CHECK(json::parse("10000.5e-4") == json(1.00005));
CHECK(json::parse("10000.5E-0") == json(10000.5));
CHECK(json::parse("10000.5E-1") == json(1000.05));
CHECK(json::parse("10000.5E-4") == json(1.00005));
}
SECTION("parse from C++ string")
{
std::string s = "{ \"foo\": [1,2,true] }";
json j = json::parse(s);
CHECK(j["foo"].size() == 3);
}
SECTION("parse from stream")
{
std::stringstream s;
s << "{ \"foo\": [1,2,true] }";
json j;
j << s;
CHECK(j["foo"].size() == 3);
}
SECTION("user-defined string literal operator")
{
auto j1 = "[1,2,3]"_json;
json j2 = {1, 2, 3};
CHECK(j1 == j2);
auto j3 = "{\"key\": \"value\"}"_json;
CHECK(j3["key"] == "value");
auto j22 = R"({
"pi": 3.141,
"happy": true
})"_json;
auto j23 = "{ \"pi\": 3.141, \"happy\": true }"_json;
CHECK(j22 == j23);
}
SECTION("serialization")
{
auto j23 = "{ \"a\": null, \"b\": true, \"c\": [1,2,3], \"d\": {\"a\": 0} }"_json;
CHECK(j23.dump() == "{\"a\":null,\"b\":true,\"c\":[1,2,3],\"d\":{\"a\":0}}");
CHECK(j23.dump(-1) == "{\"a\":null,\"b\":true,\"c\":[1,2,3],\"d\":{\"a\":0}}");
CHECK(j23.dump(0) ==
"{\n\"a\": null,\n\"b\": true,\n\"c\": [\n1,\n2,\n3\n],\n\"d\": {\n\"a\": 0\n}\n}");
CHECK(j23.dump(4) ==
"{\n \"a\": null,\n \"b\": true,\n \"c\": [\n 1,\n 2,\n 3\n ],\n \"d\": {\n \"a\": 0\n }\n}");
}
SECTION("Errors")
{
CHECK_THROWS_AS(json::parse(""), std::invalid_argument);
CHECK_THROWS_AS(json::parse(std::string("")), std::invalid_argument);
CHECK_THROWS_AS(json::parse("[1,2"), std::invalid_argument);
}
}