2016-08-05 03:55:47 +08:00
|
|
|
/*
|
|
|
|
__ _____ _____ _____
|
|
|
|
__| | __| | | | JSON for Modern C++ (test suite)
|
2016-09-15 05:06:39 +08:00
|
|
|
| | |__ | | | | | | version 2.0.5
|
2016-08-05 03:55:47 +08:00
|
|
|
|_____|_____|_____|_|___| 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "catch.hpp"
|
|
|
|
|
|
|
|
#include "json.hpp"
|
|
|
|
using nlohmann::json;
|
|
|
|
|
2016-09-12 04:30:08 +08:00
|
|
|
#include <fstream>
|
|
|
|
|
2016-08-05 03:55:47 +08:00
|
|
|
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);
|
|
|
|
}
|
2016-09-12 04:30:08 +08:00
|
|
|
|
2016-09-12 04:40:51 +08:00
|
|
|
SECTION("issue #304 - Unused variable warning")
|
|
|
|
{
|
|
|
|
// code triggered a "warning: unused variable" warning and is left
|
|
|
|
// here to avoid the warning in the future
|
|
|
|
json object;
|
|
|
|
json patch = json::array();
|
|
|
|
object = object.patch(patch);
|
|
|
|
}
|
|
|
|
|
2016-09-12 04:30:08 +08:00
|
|
|
SECTION("issue #306 - Parsing fails without space at end of file")
|
|
|
|
{
|
|
|
|
for (auto filename :
|
|
|
|
{
|
|
|
|
"test/data/regression/broken_file.json",
|
|
|
|
"test/data/regression/working_file.json"
|
|
|
|
})
|
|
|
|
{
|
|
|
|
CAPTURE(filename);
|
|
|
|
json j;
|
|
|
|
std::ifstream f(filename);
|
|
|
|
CHECK_NOTHROW(j << f);
|
|
|
|
}
|
|
|
|
}
|
2016-09-15 04:48:12 +08:00
|
|
|
|
|
|
|
SECTION("issue #310 - make json_benchmarks no longer working in 2.0.4")
|
|
|
|
{
|
|
|
|
for (auto filename :
|
|
|
|
{
|
|
|
|
"test/data/regression/floats.json",
|
|
|
|
"test/data/regression/signed_ints.json",
|
|
|
|
"test/data/regression/unsigned_ints.json"
|
|
|
|
})
|
|
|
|
{
|
|
|
|
CAPTURE(filename);
|
|
|
|
json j;
|
|
|
|
std::ifstream f(filename);
|
|
|
|
CHECK_NOTHROW(j << f);
|
|
|
|
}
|
|
|
|
}
|
2016-10-08 20:27:28 +08:00
|
|
|
|
|
|
|
SECTION("issue #323 - add nested object capabilities to pointers")
|
|
|
|
{
|
|
|
|
json j;
|
|
|
|
j["/this/that"_json_pointer] = 27;
|
|
|
|
CHECK(j == json({{"this", {{"that", 27}}}}));
|
|
|
|
}
|
2016-08-05 03:55:47 +08:00
|
|
|
}
|