mirror of
https://github.com/nlohmann/json.git
synced 2026-08-15 13:43:17 +00:00
Reorganize directories (#3462)
* 🚚 move files * 🚚 rename doc folder to docs * 🚚 rename test folder to tests
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (fuzz test support)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
This file implements a driver for American Fuzzy Lop (afl-fuzz). It relies on
|
||||
an implementation of the `LLVMFuzzerTestOneInput` function which processes a
|
||||
passed byte array.
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
*/
|
||||
|
||||
#include <vector> // for vector
|
||||
#include <cstdint> // for uint8_t
|
||||
#include <iostream> // for cin
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
|
||||
|
||||
int main()
|
||||
{
|
||||
#ifdef __AFL_HAVE_MANUAL_CONTROL
|
||||
while (__AFL_LOOP(1000))
|
||||
{
|
||||
#endif
|
||||
// copy stdin to byte vector
|
||||
std::vector<uint8_t> vec;
|
||||
char c;
|
||||
while (std::cin.get(c))
|
||||
{
|
||||
vec.push_back(static_cast<uint8_t>(c));
|
||||
}
|
||||
|
||||
LLVMFuzzerTestOneInput(vec.data(), vec.size());
|
||||
#ifdef __AFL_HAVE_MANUAL_CONTROL
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (fuzz test support)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
This file implements a parser test suitable for fuzz testing. Given a byte
|
||||
array data, it performs the following steps:
|
||||
|
||||
- j1 = from_bjdata(data)
|
||||
- vec = to_bjdata(j1)
|
||||
- j2 = from_bjdata(vec)
|
||||
- assert(j1 == j2)
|
||||
- vec2 = to_bjdata(j1, use_size = true, use_type = false)
|
||||
- j3 = from_bjdata(vec2)
|
||||
- assert(j1 == j3)
|
||||
- vec3 = to_bjdata(j1, use_size = true, use_type = true)
|
||||
- j4 = from_bjdata(vec3)
|
||||
- assert(j1 == j4)
|
||||
|
||||
The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
||||
drivers.
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
// see http://llvm.org/docs/LibFuzzer.html
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||
{
|
||||
try
|
||||
{
|
||||
// step 1: parse input
|
||||
std::vector<uint8_t> vec1(data, data + size);
|
||||
json j1 = json::from_bjdata(vec1);
|
||||
|
||||
try
|
||||
{
|
||||
// step 2.1: round trip without adding size annotations to container types
|
||||
std::vector<uint8_t> vec2 = json::to_bjdata(j1, false, false);
|
||||
|
||||
// step 2.2: round trip with adding size annotations but without adding type annonations to container types
|
||||
std::vector<uint8_t> vec3 = json::to_bjdata(j1, true, false);
|
||||
|
||||
// step 2.3: round trip with adding size as well as type annotations to container types
|
||||
std::vector<uint8_t> vec4 = json::to_bjdata(j1, true, true);
|
||||
|
||||
// parse serialization
|
||||
json j2 = json::from_bjdata(vec2);
|
||||
json j3 = json::from_bjdata(vec3);
|
||||
json j4 = json::from_bjdata(vec4);
|
||||
|
||||
// serializations must match
|
||||
assert(json::to_bjdata(j2, false, false) == vec2);
|
||||
assert(json::to_bjdata(j3, true, false) == vec3);
|
||||
assert(json::to_bjdata(j4, true, true) == vec4);
|
||||
}
|
||||
catch (const json::parse_error&)
|
||||
{
|
||||
// parsing a BJData serialization must not fail
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
catch (const json::parse_error&)
|
||||
{
|
||||
// parse errors are ok, because input may be random bytes
|
||||
}
|
||||
catch (const json::type_error&)
|
||||
{
|
||||
// type errors can occur during parsing, too
|
||||
}
|
||||
catch (const json::out_of_range&)
|
||||
{
|
||||
// out of range errors may happen if provided sizes are excessive
|
||||
}
|
||||
|
||||
// return 0 - non-zero return values are reserved for future use
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (fuzz test support)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
This file implements a parser test suitable for fuzz testing. Given a byte
|
||||
array data, it performs the following steps:
|
||||
|
||||
- j1 = from_bson(data)
|
||||
- vec = to_bson(j1)
|
||||
- j2 = from_bson(vec)
|
||||
- assert(j1 == j2)
|
||||
|
||||
The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
||||
drivers.
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
// see http://llvm.org/docs/LibFuzzer.html
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||
{
|
||||
try
|
||||
{
|
||||
// step 1: parse input
|
||||
std::vector<uint8_t> vec1(data, data + size);
|
||||
json j1 = json::from_bson(vec1);
|
||||
|
||||
if (j1.is_discarded())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// step 2: round trip
|
||||
std::vector<uint8_t> vec2 = json::to_bson(j1);
|
||||
|
||||
// parse serialization
|
||||
json j2 = json::from_bson(vec2);
|
||||
|
||||
// serializations must match
|
||||
assert(json::to_bson(j2) == vec2);
|
||||
}
|
||||
catch (const json::parse_error&)
|
||||
{
|
||||
// parsing a BSON serialization must not fail
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
catch (const json::parse_error&)
|
||||
{
|
||||
// parse errors are ok, because input may be random bytes
|
||||
}
|
||||
catch (const json::type_error&)
|
||||
{
|
||||
// type errors can occur during parsing, too
|
||||
}
|
||||
catch (const json::out_of_range&)
|
||||
{
|
||||
// out of range errors can occur during parsing, too
|
||||
}
|
||||
|
||||
// return 0 - non-zero return values are reserved for future use
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (fuzz test support)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
This file implements a parser test suitable for fuzz testing. Given a byte
|
||||
array data, it performs the following steps:
|
||||
|
||||
- j1 = from_cbor(data)
|
||||
- vec = to_cbor(j1)
|
||||
- j2 = from_cbor(vec)
|
||||
- assert(j1 == j2)
|
||||
|
||||
The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
||||
drivers.
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
// see http://llvm.org/docs/LibFuzzer.html
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||
{
|
||||
try
|
||||
{
|
||||
// step 1: parse input
|
||||
std::vector<uint8_t> vec1(data, data + size);
|
||||
json j1 = json::from_cbor(vec1);
|
||||
|
||||
try
|
||||
{
|
||||
// step 2: round trip
|
||||
std::vector<uint8_t> vec2 = json::to_cbor(j1);
|
||||
|
||||
// parse serialization
|
||||
json j2 = json::from_cbor(vec2);
|
||||
|
||||
// serializations must match
|
||||
assert(json::to_cbor(j2) == vec2);
|
||||
}
|
||||
catch (const json::parse_error&)
|
||||
{
|
||||
// parsing a CBOR serialization must not fail
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
catch (const json::parse_error&)
|
||||
{
|
||||
// parse errors are ok, because input may be random bytes
|
||||
}
|
||||
catch (const json::type_error&)
|
||||
{
|
||||
// type errors can occur during parsing, too
|
||||
}
|
||||
catch (const json::out_of_range&)
|
||||
{
|
||||
// out of range errors can occur during parsing, too
|
||||
}
|
||||
|
||||
// return 0 - non-zero return values are reserved for future use
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (fuzz test support)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
This file implements a parser test suitable for fuzz testing. Given a byte
|
||||
array data, it performs the following steps:
|
||||
|
||||
- j1 = parse(data)
|
||||
- s1 = serialize(j1)
|
||||
- j2 = parse(s1)
|
||||
- s2 = serialize(j2)
|
||||
- assert(s1 == s2)
|
||||
|
||||
The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
||||
drivers.
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
// see http://llvm.org/docs/LibFuzzer.html
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||
{
|
||||
try
|
||||
{
|
||||
// step 1: parse input
|
||||
json j1 = json::parse(data, data + size);
|
||||
|
||||
try
|
||||
{
|
||||
// step 2: round trip
|
||||
|
||||
// first serialization
|
||||
std::string s1 = j1.dump();
|
||||
|
||||
// parse serialization
|
||||
json j2 = json::parse(s1);
|
||||
|
||||
// second serialization
|
||||
std::string s2 = j2.dump();
|
||||
|
||||
// serializations must match
|
||||
assert(s1 == s2);
|
||||
}
|
||||
catch (const json::parse_error&)
|
||||
{
|
||||
// parsing a JSON serialization must not fail
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
catch (const json::parse_error&)
|
||||
{
|
||||
// parse errors are ok, because input may be random bytes
|
||||
}
|
||||
catch (const json::out_of_range&)
|
||||
{
|
||||
// out of range errors may happen if provided sizes are excessive
|
||||
}
|
||||
|
||||
// return 0 - non-zero return values are reserved for future use
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (fuzz test support)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
This file implements a parser test suitable for fuzz testing. Given a byte
|
||||
array data, it performs the following steps:
|
||||
|
||||
- j1 = from_msgpack(data)
|
||||
- vec = to_msgpack(j1)
|
||||
- j2 = from_msgpack(vec)
|
||||
- assert(j1 == j2)
|
||||
|
||||
The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
||||
drivers.
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
// see http://llvm.org/docs/LibFuzzer.html
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||
{
|
||||
try
|
||||
{
|
||||
// step 1: parse input
|
||||
std::vector<uint8_t> vec1(data, data + size);
|
||||
json j1 = json::from_msgpack(vec1);
|
||||
|
||||
try
|
||||
{
|
||||
// step 2: round trip
|
||||
std::vector<uint8_t> vec2 = json::to_msgpack(j1);
|
||||
|
||||
// parse serialization
|
||||
json j2 = json::from_msgpack(vec2);
|
||||
|
||||
// serializations must match
|
||||
assert(json::to_msgpack(j2) == vec2);
|
||||
}
|
||||
catch (const json::parse_error&)
|
||||
{
|
||||
// parsing a MessagePack serialization must not fail
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
catch (const json::parse_error&)
|
||||
{
|
||||
// parse errors are ok, because input may be random bytes
|
||||
}
|
||||
catch (const json::type_error&)
|
||||
{
|
||||
// type errors can occur during parsing, too
|
||||
}
|
||||
catch (const json::out_of_range&)
|
||||
{
|
||||
// out of range errors may happen if provided sizes are excessive
|
||||
}
|
||||
|
||||
// return 0 - non-zero return values are reserved for future use
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (fuzz test support)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
This file implements a parser test suitable for fuzz testing. Given a byte
|
||||
array data, it performs the following steps:
|
||||
|
||||
- j1 = from_ubjson(data)
|
||||
- vec = to_ubjson(j1)
|
||||
- j2 = from_ubjson(vec)
|
||||
- assert(j1 == j2)
|
||||
- vec2 = to_ubjson(j1, use_size = true, use_type = false)
|
||||
- j3 = from_ubjson(vec2)
|
||||
- assert(j1 == j3)
|
||||
- vec3 = to_ubjson(j1, use_size = true, use_type = true)
|
||||
- j4 = from_ubjson(vec3)
|
||||
- assert(j1 == j4)
|
||||
|
||||
The provided function `LLVMFuzzerTestOneInput` can be used in different fuzzer
|
||||
drivers.
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
// see http://llvm.org/docs/LibFuzzer.html
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||
{
|
||||
try
|
||||
{
|
||||
// step 1: parse input
|
||||
std::vector<uint8_t> vec1(data, data + size);
|
||||
json j1 = json::from_ubjson(vec1);
|
||||
|
||||
try
|
||||
{
|
||||
// step 2.1: round trip without adding size annotations to container types
|
||||
std::vector<uint8_t> vec2 = json::to_ubjson(j1, false, false);
|
||||
|
||||
// step 2.2: round trip with adding size annotations but without adding type annonations to container types
|
||||
std::vector<uint8_t> vec3 = json::to_ubjson(j1, true, false);
|
||||
|
||||
// step 2.3: round trip with adding size as well as type annotations to container types
|
||||
std::vector<uint8_t> vec4 = json::to_ubjson(j1, true, true);
|
||||
|
||||
// parse serialization
|
||||
json j2 = json::from_ubjson(vec2);
|
||||
json j3 = json::from_ubjson(vec3);
|
||||
json j4 = json::from_ubjson(vec4);
|
||||
|
||||
// serializations must match
|
||||
assert(json::to_ubjson(j2, false, false) == vec2);
|
||||
assert(json::to_ubjson(j3, true, false) == vec3);
|
||||
assert(json::to_ubjson(j4, true, true) == vec4);
|
||||
}
|
||||
catch (const json::parse_error&)
|
||||
{
|
||||
// parsing a UBJSON serialization must not fail
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
catch (const json::parse_error&)
|
||||
{
|
||||
// parse errors are ok, because input may be random bytes
|
||||
}
|
||||
catch (const json::type_error&)
|
||||
{
|
||||
// type errors can occur during parsing, too
|
||||
}
|
||||
catch (const json::out_of_range&)
|
||||
{
|
||||
// out of range errors may happen if provided sizes are excessive
|
||||
}
|
||||
|
||||
// return 0 - non-zero return values are reserved for future use
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint> // uint8_t
|
||||
#include <fstream> // ifstream, istreambuf_iterator, ios
|
||||
#include <vector> // vector
|
||||
|
||||
namespace utils
|
||||
{
|
||||
|
||||
inline std::vector<std::uint8_t> read_binary_file(const std::string& filename)
|
||||
{
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
file.unsetf(std::ios::skipws);
|
||||
|
||||
file.seekg(0, std::ios::end);
|
||||
const auto size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<std::uint8_t> byte_vector;
|
||||
byte_vector.reserve(static_cast<std::size_t>(size));
|
||||
byte_vector.insert(byte_vector.begin(), std::istream_iterator<std::uint8_t>(file), std::istream_iterator<std::uint8_t>());
|
||||
return byte_vector;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
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.empty();
|
||||
}));
|
||||
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() && 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.empty();
|
||||
}));
|
||||
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(!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(!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(!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_WITH_AS(std::sort(j.begin(), j.end()), "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
|
||||
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"}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#define JSON_TESTS_PRIVATE
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
namespace
|
||||
{
|
||||
// special test case to check if memory is leaked if constructor throws
|
||||
template<class T>
|
||||
struct bad_allocator : std::allocator<T>
|
||||
{
|
||||
using std::allocator<T>::allocator;
|
||||
|
||||
template<class... Args>
|
||||
void construct(T* /*unused*/, Args&& ... /*unused*/)
|
||||
{
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("bad_alloc")
|
||||
{
|
||||
SECTION("bad_alloc")
|
||||
{
|
||||
// create JSON type using the throwing allocator
|
||||
using bad_json = nlohmann::basic_json<std::map,
|
||||
std::vector,
|
||||
std::string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
bad_allocator>;
|
||||
|
||||
// creating an object should throw
|
||||
CHECK_THROWS_AS(bad_json(bad_json::value_t::object), std::bad_alloc&);
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
bool next_construct_fails = false;
|
||||
bool next_destroy_fails = false;
|
||||
bool next_deallocate_fails = false;
|
||||
|
||||
template<class T>
|
||||
struct my_allocator : std::allocator<T>
|
||||
{
|
||||
using std::allocator<T>::allocator;
|
||||
|
||||
template<class... Args>
|
||||
void construct(T* p, Args&& ... args)
|
||||
{
|
||||
if (next_construct_fails)
|
||||
{
|
||||
next_construct_fails = false;
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
::new (reinterpret_cast<void*>(p)) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void deallocate(T* p, std::size_t n)
|
||||
{
|
||||
if (next_deallocate_fails)
|
||||
{
|
||||
next_deallocate_fails = false;
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
std::allocator<T>::deallocate(p, n);
|
||||
}
|
||||
|
||||
void destroy(T* p)
|
||||
{
|
||||
if (next_destroy_fails)
|
||||
{
|
||||
next_destroy_fails = false;
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
static_cast<void>(p); // fix MSVC's C4100 warning
|
||||
p->~T();
|
||||
}
|
||||
|
||||
template <class U>
|
||||
struct rebind
|
||||
{
|
||||
using other = my_allocator<U>;
|
||||
};
|
||||
};
|
||||
|
||||
// allows deletion of raw pointer, usually hold by json_value
|
||||
template<class T>
|
||||
void my_allocator_clean_up(T* p)
|
||||
{
|
||||
assert(p != nullptr);
|
||||
my_allocator<T> alloc;
|
||||
alloc.destroy(p);
|
||||
alloc.deallocate(p, 1);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("controlled 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>;
|
||||
|
||||
SECTION("class json_value")
|
||||
{
|
||||
SECTION("json_value(value_t)")
|
||||
{
|
||||
SECTION("object")
|
||||
{
|
||||
next_construct_fails = false;
|
||||
auto t = my_json::value_t::object;
|
||||
CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).object));
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc&);
|
||||
next_construct_fails = false;
|
||||
}
|
||||
SECTION("array")
|
||||
{
|
||||
next_construct_fails = false;
|
||||
auto t = my_json::value_t::array;
|
||||
CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).array));
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc&);
|
||||
next_construct_fails = false;
|
||||
}
|
||||
SECTION("string")
|
||||
{
|
||||
next_construct_fails = false;
|
||||
auto t = my_json::value_t::string;
|
||||
CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).string));
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc&);
|
||||
next_construct_fails = false;
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("json_value(const string_t&)")
|
||||
{
|
||||
next_construct_fails = false;
|
||||
my_json::string_t v("foo");
|
||||
CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(v).string));
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(my_json::json_value(v), std::bad_alloc&);
|
||||
next_construct_fails = false;
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("class basic_json")
|
||||
{
|
||||
SECTION("basic_json(const CompatibleObjectType&)")
|
||||
{
|
||||
next_construct_fails = false;
|
||||
std::map<std::string, std::string> v {{"foo", "bar"}};
|
||||
CHECK_NOTHROW(my_json(v));
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(my_json(v), std::bad_alloc&);
|
||||
next_construct_fails = false;
|
||||
}
|
||||
|
||||
SECTION("basic_json(const CompatibleArrayType&)")
|
||||
{
|
||||
next_construct_fails = false;
|
||||
std::vector<std::string> v {"foo", "bar", "baz"};
|
||||
CHECK_NOTHROW(my_json(v));
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(my_json(v), std::bad_alloc&);
|
||||
next_construct_fails = false;
|
||||
}
|
||||
|
||||
SECTION("basic_json(const typename string_t::value_type*)")
|
||||
{
|
||||
next_construct_fails = false;
|
||||
CHECK_NOTHROW(my_json("foo"));
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(my_json("foo"), std::bad_alloc&);
|
||||
next_construct_fails = false;
|
||||
}
|
||||
|
||||
SECTION("basic_json(const typename string_t::value_type*)")
|
||||
{
|
||||
next_construct_fails = false;
|
||||
std::string s("foo");
|
||||
CHECK_NOTHROW(my_json(s));
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(my_json(s), std::bad_alloc&);
|
||||
next_construct_fails = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
template<class T>
|
||||
struct allocator_no_forward : std::allocator<T>
|
||||
{
|
||||
allocator_no_forward() = default;
|
||||
template <class U>
|
||||
allocator_no_forward(allocator_no_forward<U> /*unused*/) {}
|
||||
|
||||
template <class U>
|
||||
struct rebind
|
||||
{
|
||||
using other = allocator_no_forward<U>;
|
||||
};
|
||||
|
||||
template <class... Args>
|
||||
void construct(T* p, const Args& ... args) noexcept(noexcept(::new (static_cast<void*>(p)) T(args...)))
|
||||
{
|
||||
// force copy even if move is available
|
||||
::new (static_cast<void*>(p)) T(args...);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("bad my_allocator::construct")
|
||||
{
|
||||
SECTION("my_allocator::construct doesn't forward")
|
||||
{
|
||||
using bad_alloc_json = nlohmann::basic_json<std::map,
|
||||
std::vector,
|
||||
std::string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
allocator_no_forward>;
|
||||
|
||||
bad_alloc_json j;
|
||||
j["test"] = bad_alloc_json::array_t();
|
||||
j["test"].push_back("should not leak");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2018 Vitaliy Manushkin <agri@akamo.info>.
|
||||
|
||||
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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
|
||||
/* forward declarations */
|
||||
class alt_string;
|
||||
bool operator<(const char* op1, const alt_string& op2) noexcept;
|
||||
void int_to_string(alt_string& target, std::size_t value);
|
||||
|
||||
/*
|
||||
* This is virtually a string class.
|
||||
* It covers std::string under the hood.
|
||||
*/
|
||||
class alt_string
|
||||
{
|
||||
public:
|
||||
using value_type = std::string::value_type;
|
||||
|
||||
static constexpr auto npos = static_cast<std::size_t>(-1);
|
||||
|
||||
alt_string(const char* str): str_impl(str) {}
|
||||
alt_string(const char* str, std::size_t count): str_impl(str, count) {}
|
||||
alt_string(size_t count, char chr): str_impl(count, chr) {}
|
||||
alt_string() = default;
|
||||
|
||||
template <typename...TParams>
|
||||
alt_string& append(TParams&& ...params)
|
||||
{
|
||||
str_impl.append(std::forward<TParams>(params)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void push_back(char c)
|
||||
{
|
||||
str_impl.push_back(c);
|
||||
}
|
||||
|
||||
template <typename op_type>
|
||||
bool operator==(const op_type& op) const
|
||||
{
|
||||
return str_impl == op;
|
||||
}
|
||||
|
||||
bool operator==(const alt_string& op) const
|
||||
{
|
||||
return str_impl == op.str_impl;
|
||||
}
|
||||
|
||||
template <typename op_type>
|
||||
bool operator!=(const op_type& op) const
|
||||
{
|
||||
return str_impl != op;
|
||||
}
|
||||
|
||||
bool operator!=(const alt_string& op) const
|
||||
{
|
||||
return str_impl != op.str_impl;
|
||||
}
|
||||
|
||||
std::size_t size() const noexcept
|
||||
{
|
||||
return str_impl.size();
|
||||
}
|
||||
|
||||
void resize (std::size_t n)
|
||||
{
|
||||
str_impl.resize(n);
|
||||
}
|
||||
|
||||
void resize (std::size_t n, char c)
|
||||
{
|
||||
str_impl.resize(n, c);
|
||||
}
|
||||
|
||||
template <typename op_type>
|
||||
bool operator<(const op_type& op) const noexcept
|
||||
{
|
||||
return str_impl < op;
|
||||
}
|
||||
|
||||
bool operator<(const alt_string& op) const noexcept
|
||||
{
|
||||
return str_impl < op.str_impl;
|
||||
}
|
||||
|
||||
const char* c_str() const
|
||||
{
|
||||
return str_impl.c_str();
|
||||
}
|
||||
|
||||
char& operator[](std::size_t index)
|
||||
{
|
||||
return str_impl[index];
|
||||
}
|
||||
|
||||
const char& operator[](std::size_t index) const
|
||||
{
|
||||
return str_impl[index];
|
||||
}
|
||||
|
||||
char& back()
|
||||
{
|
||||
return str_impl.back();
|
||||
}
|
||||
|
||||
const char& back() const
|
||||
{
|
||||
return str_impl.back();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
str_impl.clear();
|
||||
}
|
||||
|
||||
const value_type* data() const
|
||||
{
|
||||
return str_impl.data();
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return str_impl.empty();
|
||||
}
|
||||
|
||||
std::size_t find(const alt_string& str, std::size_t pos = 0) const
|
||||
{
|
||||
return str_impl.find(str.str_impl, pos);
|
||||
}
|
||||
|
||||
std::size_t find_first_of(char c, std::size_t pos = 0) const
|
||||
{
|
||||
return str_impl.find_first_of(c, pos);
|
||||
}
|
||||
|
||||
alt_string substr(std::size_t pos = 0, std::size_t count = npos) const
|
||||
{
|
||||
std::string s = str_impl.substr(pos, count);
|
||||
return {s.data(), s.size()};
|
||||
}
|
||||
|
||||
alt_string& replace(std::size_t pos, std::size_t count, const alt_string& str)
|
||||
{
|
||||
str_impl.replace(pos, count, str.str_impl);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string str_impl {};
|
||||
|
||||
friend bool ::operator<(const char* /*op1*/, const alt_string& /*op2*/) noexcept;
|
||||
};
|
||||
|
||||
void int_to_string(alt_string& target, std::size_t value)
|
||||
{
|
||||
target = std::to_string(value).c_str();
|
||||
}
|
||||
|
||||
using alt_json = nlohmann::basic_json <
|
||||
std::map,
|
||||
std::vector,
|
||||
alt_string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
std::allocator,
|
||||
nlohmann::adl_serializer >;
|
||||
|
||||
|
||||
bool operator<(const char* op1, const alt_string& op2) noexcept
|
||||
{
|
||||
return op1 < op2.str_impl;
|
||||
}
|
||||
|
||||
TEST_CASE("alternative string type")
|
||||
{
|
||||
SECTION("dump")
|
||||
{
|
||||
{
|
||||
alt_json doc;
|
||||
doc["pi"] = 3.141;
|
||||
alt_string dump = doc.dump();
|
||||
CHECK(dump == R"({"pi":3.141})");
|
||||
}
|
||||
|
||||
{
|
||||
alt_json doc;
|
||||
doc["happy"] = true;
|
||||
alt_string dump = doc.dump();
|
||||
CHECK(dump == R"({"happy":true})");
|
||||
}
|
||||
|
||||
{
|
||||
alt_json doc;
|
||||
doc["name"] = "I'm Batman";
|
||||
alt_string dump = doc.dump();
|
||||
CHECK(dump == R"({"name":"I'm Batman"})");
|
||||
}
|
||||
|
||||
{
|
||||
alt_json doc;
|
||||
doc["nothing"] = nullptr;
|
||||
alt_string dump = doc.dump();
|
||||
CHECK(dump == R"({"nothing":null})");
|
||||
}
|
||||
|
||||
{
|
||||
alt_json doc;
|
||||
doc["answer"]["everything"] = 42;
|
||||
alt_string dump = doc.dump();
|
||||
CHECK(dump == R"({"answer":{"everything":42}})");
|
||||
}
|
||||
|
||||
{
|
||||
alt_json doc;
|
||||
doc["list"] = { 1, 0, 2 };
|
||||
alt_string dump = doc.dump();
|
||||
CHECK(dump == R"({"list":[1,0,2]})");
|
||||
}
|
||||
|
||||
{
|
||||
alt_json doc;
|
||||
doc["object"] = { {"currency", "USD"}, {"value", 42.99} };
|
||||
alt_string dump = doc.dump();
|
||||
CHECK(dump == R"({"object":{"currency":"USD","value":42.99}})");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("parse")
|
||||
{
|
||||
auto doc = alt_json::parse(R"({"foo": "bar"})");
|
||||
alt_string dump = doc.dump();
|
||||
CHECK(dump == R"({"foo":"bar"})");
|
||||
}
|
||||
|
||||
SECTION("items")
|
||||
{
|
||||
auto doc = alt_json::parse(R"({"foo": "bar"})");
|
||||
|
||||
for (const auto& item : doc.items())
|
||||
{
|
||||
CHECK(item.key() == "foo");
|
||||
CHECK(item.value() == "bar");
|
||||
}
|
||||
|
||||
auto doc_array = alt_json::parse(R"(["foo", "bar"])");
|
||||
|
||||
for (const auto& item : doc_array.items())
|
||||
{
|
||||
if (item.key() == "0" )
|
||||
{
|
||||
CHECK( item.value() == "foo" );
|
||||
}
|
||||
else if (item.key() == "1" )
|
||||
{
|
||||
CHECK(item.value() == "bar");
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("equality")
|
||||
{
|
||||
alt_json doc;
|
||||
doc["Who are you?"] = "I'm Batman";
|
||||
|
||||
CHECK("I'm Batman" == doc["Who are you?"]);
|
||||
CHECK(doc["Who are you?"] == "I'm Batman");
|
||||
CHECK_FALSE("I'm Batman" != doc["Who are you?"]);
|
||||
CHECK_FALSE(doc["Who are you?"] != "I'm Batman");
|
||||
|
||||
CHECK("I'm Bruce Wayne" != doc["Who are you?"]);
|
||||
CHECK(doc["Who are you?"] != "I'm Bruce Wayne");
|
||||
CHECK_FALSE("I'm Bruce Wayne" == doc["Who are you?"]);
|
||||
CHECK_FALSE(doc["Who are you?"] == "I'm Bruce Wayne");
|
||||
|
||||
{
|
||||
const alt_json& const_doc = doc;
|
||||
|
||||
CHECK("I'm Batman" == const_doc["Who are you?"]);
|
||||
CHECK(const_doc["Who are you?"] == "I'm Batman");
|
||||
CHECK_FALSE("I'm Batman" != const_doc["Who are you?"]);
|
||||
CHECK_FALSE(const_doc["Who are you?"] != "I'm Batman");
|
||||
|
||||
CHECK("I'm Bruce Wayne" != const_doc["Who are you?"]);
|
||||
CHECK(const_doc["Who are you?"] != "I'm Bruce Wayne");
|
||||
CHECK_FALSE("I'm Bruce Wayne" == const_doc["Who are you?"]);
|
||||
CHECK_FALSE(const_doc["Who are you?"] == "I'm Bruce Wayne");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("JSON pointer")
|
||||
{
|
||||
// conversion from json to alt_json fails to compile (see #3425);
|
||||
// attempted fix(*) produces: [[['b','a','r'],['b','a','z']]] (with each char being an integer)
|
||||
// (*) disable implicit conversion for json_refs of any basic_json type
|
||||
// alt_json j = R"(
|
||||
// {
|
||||
// "foo": ["bar", "baz"]
|
||||
// }
|
||||
// )"_json;
|
||||
auto j = alt_json::parse(R"({"foo": ["bar", "baz"]})");
|
||||
|
||||
CHECK(j.at(alt_json::json_pointer("/foo/0")) == j["foo"][0]);
|
||||
CHECK(j.at(alt_json::json_pointer("/foo/1")) == j["foo"][1]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
// avoid warning when assert does not abort
|
||||
DOCTEST_GCC_SUPPRESS_WARNING_PUSH
|
||||
DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow")
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING("-Wstrict-overflow")
|
||||
|
||||
/// global variable to record side effect of assert calls
|
||||
static int assert_counter;
|
||||
|
||||
/// set failure variable to true instead of calling assert(x)
|
||||
#define JSON_ASSERT(x) {if (!(x)) ++assert_counter; }
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
// the test assumes exceptions to work
|
||||
#if !defined(JSON_NOEXCEPTION)
|
||||
TEST_CASE("JSON_ASSERT(x)")
|
||||
{
|
||||
SECTION("basic_json(first, second)")
|
||||
{
|
||||
assert_counter = 0;
|
||||
CHECK(assert_counter == 0);
|
||||
|
||||
json::iterator it;
|
||||
json j;
|
||||
|
||||
// in case assertions do not abort execution, an exception is thrown
|
||||
CHECK_THROWS_WITH_AS(json(it, j.end()), "[json.exception.invalid_iterator.201] iterators are not compatible", json::invalid_iterator);
|
||||
|
||||
// check that assertion actually happened
|
||||
CHECK(assert_counter == 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
DOCTEST_GCC_SUPPRESS_WARNING_POP
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_POP
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
TEST_CASE("byte_container_with_subtype")
|
||||
{
|
||||
using subtype_type = nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>>::subtype_type;
|
||||
|
||||
SECTION("empty container")
|
||||
{
|
||||
nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>> container;
|
||||
|
||||
CHECK(!container.has_subtype());
|
||||
CHECK(container.subtype() == static_cast<subtype_type>(-1));
|
||||
|
||||
container.clear_subtype();
|
||||
CHECK(!container.has_subtype());
|
||||
CHECK(container.subtype() == static_cast<subtype_type>(-1));
|
||||
|
||||
container.set_subtype(42);
|
||||
CHECK(container.has_subtype());
|
||||
CHECK(container.subtype() == 42);
|
||||
}
|
||||
|
||||
SECTION("subtyped container")
|
||||
{
|
||||
nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>> container({}, 42);
|
||||
CHECK(container.has_subtype());
|
||||
CHECK(container.subtype() == 42);
|
||||
|
||||
container.clear_subtype();
|
||||
CHECK(!container.has_subtype());
|
||||
CHECK(container.subtype() == static_cast<subtype_type>(-1));
|
||||
}
|
||||
|
||||
SECTION("comparisons")
|
||||
{
|
||||
std::vector<std::uint8_t> bytes = {{0xCA, 0xFE, 0xBA, 0xBE}};
|
||||
nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>> container1;
|
||||
nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>> container2({}, 42);
|
||||
nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>> container3(bytes);
|
||||
nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>> container4(bytes, 42);
|
||||
|
||||
CHECK(container1 == container1);
|
||||
CHECK(container1 != container2);
|
||||
CHECK(container1 != container3);
|
||||
CHECK(container1 != container4);
|
||||
CHECK(container2 != container1);
|
||||
CHECK(container2 == container2);
|
||||
CHECK(container2 != container3);
|
||||
CHECK(container2 != container4);
|
||||
CHECK(container3 != container1);
|
||||
CHECK(container3 != container2);
|
||||
CHECK(container3 == container3);
|
||||
CHECK(container3 != container4);
|
||||
CHECK(container4 != container1);
|
||||
CHECK(container4 != container2);
|
||||
CHECK(container4 != container3);
|
||||
CHECK(container4 == container4);
|
||||
|
||||
container3.clear();
|
||||
container4.clear();
|
||||
|
||||
CHECK(container1 == container3);
|
||||
CHECK(container2 == container4);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,563 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
TEST_CASE("capacity")
|
||||
{
|
||||
SECTION("empty()")
|
||||
{
|
||||
SECTION("boolean")
|
||||
{
|
||||
json j = true;
|
||||
const json j_const(j);
|
||||
|
||||
SECTION("result of empty")
|
||||
{
|
||||
CHECK(j.empty() == false);
|
||||
CHECK(j_const.empty() == false);
|
||||
}
|
||||
|
||||
SECTION("definition of empty")
|
||||
{
|
||||
CHECK(j.empty() == (j.begin() == j.end()));
|
||||
CHECK(j_const.empty() == (j_const.begin() == j_const.end()));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("string")
|
||||
{
|
||||
json j = "hello world";
|
||||
const json j_const(j);
|
||||
|
||||
SECTION("result of empty")
|
||||
{
|
||||
CHECK(j.empty() == false);
|
||||
CHECK(j_const.empty() == false);
|
||||
}
|
||||
|
||||
SECTION("definition of empty")
|
||||
{
|
||||
CHECK(j.empty() == (j.begin() == j.end()));
|
||||
CHECK(j_const.empty() == (j_const.begin() == j_const.end()));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("array")
|
||||
{
|
||||
SECTION("empty array")
|
||||
{
|
||||
json j = json::array();
|
||||
const json j_const(j);
|
||||
|
||||
SECTION("result of empty")
|
||||
{
|
||||
CHECK(j.empty() == true);
|
||||
CHECK(j_const.empty() == true);
|
||||
}
|
||||
|
||||
SECTION("definition of empty")
|
||||
{
|
||||
CHECK(j.empty() == (j.begin() == j.end()));
|
||||
CHECK(j_const.empty() == (j_const.begin() == j_const.end()));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("filled array")
|
||||
{
|
||||
json j = {1, 2, 3};
|
||||
const json j_const(j);
|
||||
|
||||
SECTION("result of empty")
|
||||
{
|
||||
CHECK(j.empty() == false);
|
||||
CHECK(j_const.empty() == false);
|
||||
}
|
||||
|
||||
SECTION("definition of empty")
|
||||
{
|
||||
CHECK(j.empty() == (j.begin() == j.end()));
|
||||
CHECK(j_const.empty() == (j_const.begin() == j_const.end()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("object")
|
||||
{
|
||||
SECTION("empty object")
|
||||
{
|
||||
json j = json::object();
|
||||
const json j_const(j);
|
||||
|
||||
SECTION("result of empty")
|
||||
{
|
||||
CHECK(j.empty() == true);
|
||||
CHECK(j_const.empty() == true);
|
||||
}
|
||||
|
||||
SECTION("definition of empty")
|
||||
{
|
||||
CHECK(j.empty() == (j.begin() == j.end()));
|
||||
CHECK(j_const.empty() == (j_const.begin() == j_const.end()));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("filled object")
|
||||
{
|
||||
json j = {{"one", 1}, {"two", 2}, {"three", 3}};
|
||||
const json j_const(j);
|
||||
|
||||
SECTION("result of empty")
|
||||
{
|
||||
CHECK(j.empty() == false);
|
||||
CHECK(j_const.empty() == false);
|
||||
}
|
||||
|
||||
SECTION("definition of empty")
|
||||
{
|
||||
CHECK(j.empty() == (j.begin() == j.end()));
|
||||
CHECK(j_const.empty() == (j_const.begin() == j_const.end()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("number (integer)")
|
||||
{
|
||||
json j = -23;
|
||||
const json j_const(j);
|
||||
|
||||
SECTION("result of empty")
|
||||
{
|
||||
CHECK(j.empty() == false);
|
||||
CHECK(j_const.empty() == false);
|
||||
}
|
||||
|
||||
SECTION("definition of empty")
|
||||
{
|
||||
CHECK(j.empty() == (j.begin() == j.end()));
|
||||
CHECK(j_const.empty() == (j_const.begin() == j_const.end()));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("number (unsigned)")
|
||||
{
|
||||
json j = 23u;
|
||||
const json j_const(j);
|
||||
|
||||
SECTION("result of empty")
|
||||
{
|
||||
CHECK(j.empty() == false);
|
||||
CHECK(j_const.empty() == false);
|
||||
}
|
||||
|
||||
SECTION("definition of empty")
|
||||
{
|
||||
CHECK(j.empty() == (j.begin() == j.end()));
|
||||
CHECK(j_const.empty() == (j_const.begin() == j_const.end()));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("number (float)")
|
||||
{
|
||||
json j = 23.42;
|
||||
const json j_const(j);
|
||||
|
||||
SECTION("result of empty")
|
||||
{
|
||||
CHECK(j.empty() == false);
|
||||
CHECK(j_const.empty() == false);
|
||||
}
|
||||
|
||||
SECTION("definition of empty")
|
||||
{
|
||||
CHECK(j.empty() == (j.begin() == j.end()));
|
||||
CHECK(j_const.empty() == (j_const.begin() == j_const.end()));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("null")
|
||||
{
|
||||
json j = nullptr;
|
||||
const json j_const(j);
|
||||
|
||||
SECTION("result of empty")
|
||||
{
|
||||
CHECK(j.empty() == true);
|
||||
CHECK(j_const.empty() == true);
|
||||
}
|
||||
|
||||
SECTION("definition of empty")
|
||||
{
|
||||
CHECK(j.empty() == (j.begin() == j.end()));
|
||||
CHECK(j_const.empty() == (j_const.begin() == j_const.end()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("size()")
|
||||
{
|
||||
SECTION("boolean")
|
||||
{
|
||||
json j = true;
|
||||
const 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";
|
||||
const 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();
|
||||
const 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};
|
||||
const 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();
|
||||
const 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}};
|
||||
const 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;
|
||||
const 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;
|
||||
const 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;
|
||||
const 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;
|
||||
const 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;
|
||||
const json j_const = true;
|
||||
|
||||
SECTION("result of max_size")
|
||||
{
|
||||
CHECK(j.max_size() == 1);
|
||||
CHECK(j_const.max_size() == 1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("string")
|
||||
{
|
||||
json j = "hello world";
|
||||
const json j_const = "hello world";
|
||||
|
||||
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();
|
||||
const json j_const = json::array();
|
||||
|
||||
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};
|
||||
const json j_const = {1, 2, 3};
|
||||
|
||||
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();
|
||||
const json j_const = json::object();
|
||||
|
||||
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}};
|
||||
const json j_const = {{"one", 1}, {"two", 2}, {"three", 3}};
|
||||
|
||||
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;
|
||||
const json j_const = -23;
|
||||
|
||||
SECTION("result of max_size")
|
||||
{
|
||||
CHECK(j.max_size() == 1);
|
||||
CHECK(j_const.max_size() == 1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("number (unsigned)")
|
||||
{
|
||||
json j = 23u;
|
||||
const json j_const = 23u;
|
||||
|
||||
SECTION("result of max_size")
|
||||
{
|
||||
CHECK(j.max_size() == 1);
|
||||
CHECK(j_const.max_size() == 1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("number (float)")
|
||||
{
|
||||
json j = 23.42;
|
||||
const json j_const = 23.42;
|
||||
|
||||
SECTION("result of max_size")
|
||||
{
|
||||
CHECK(j.max_size() == 1);
|
||||
CHECK(j_const.max_size() == 1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("null")
|
||||
{
|
||||
json j = nullptr;
|
||||
const json j_const = nullptr;
|
||||
|
||||
SECTION("result of max_size")
|
||||
{
|
||||
CHECK(j.max_size() == 0);
|
||||
CHECK(j_const.max_size() == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#define JSON_TESTS_PRIVATE
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
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("copy constructor from non-const iterator")
|
||||
{
|
||||
SECTION("create from uninitialized iterator")
|
||||
{
|
||||
const json::iterator it {};
|
||||
json::const_iterator cit(it);
|
||||
}
|
||||
|
||||
SECTION("create from initialized iterator")
|
||||
{
|
||||
json j;
|
||||
const json::iterator it = j.begin();
|
||||
json::const_iterator cit(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_WITH_AS(*it, "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
}
|
||||
|
||||
SECTION("number")
|
||||
{
|
||||
json j(17);
|
||||
json::const_iterator it = j.cbegin();
|
||||
CHECK(*it == json(17));
|
||||
it = j.cend();
|
||||
CHECK_THROWS_WITH_AS(*it, "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
}
|
||||
|
||||
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_WITH_AS(std::string(it->type_name()), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
}
|
||||
|
||||
SECTION("number")
|
||||
{
|
||||
json j(17);
|
||||
json::const_iterator it = j.cbegin();
|
||||
CHECK(std::string(it->type_name()) == "number");
|
||||
it = j.cend();
|
||||
CHECK_THROWS_WITH_AS(std::string(it->type_name()), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
}
|
||||
|
||||
SECTION("object")
|
||||
{
|
||||
json j({{"foo", "bar"}});
|
||||
json::const_iterator it = j.cbegin();
|
||||
CHECK(std::string(it->type_name()) == "string");
|
||||
}
|
||||
|
||||
SECTION("array")
|
||||
{
|
||||
json j({1, 2, 3, 4});
|
||||
json::const_iterator it = j.cbegin();
|
||||
CHECK(std::string(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.m_it == 1));
|
||||
it++;
|
||||
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 1));
|
||||
}
|
||||
|
||||
SECTION("number")
|
||||
{
|
||||
json j(17);
|
||||
json::const_iterator it = j.cbegin();
|
||||
CHECK((it.m_it.primitive_iterator.m_it == 0));
|
||||
it++;
|
||||
CHECK((it.m_it.primitive_iterator.m_it == 1));
|
||||
it++;
|
||||
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 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.m_it == 1));
|
||||
++it;
|
||||
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 1));
|
||||
}
|
||||
|
||||
SECTION("number")
|
||||
{
|
||||
json j(17);
|
||||
json::const_iterator it = j.cbegin();
|
||||
CHECK((it.m_it.primitive_iterator.m_it == 0));
|
||||
++it;
|
||||
CHECK((it.m_it.primitive_iterator.m_it == 1));
|
||||
++it;
|
||||
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 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.m_it == 1));
|
||||
}
|
||||
|
||||
SECTION("number")
|
||||
{
|
||||
json j(17);
|
||||
json::const_iterator it = j.cend();
|
||||
CHECK((it.m_it.primitive_iterator.m_it == 1));
|
||||
it--;
|
||||
CHECK((it.m_it.primitive_iterator.m_it == 0));
|
||||
it--;
|
||||
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 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.m_it == 1));
|
||||
}
|
||||
|
||||
SECTION("number")
|
||||
{
|
||||
json j(17);
|
||||
json::const_iterator it = j.cend();
|
||||
CHECK((it.m_it.primitive_iterator.m_it == 1));
|
||||
--it;
|
||||
CHECK((it.m_it.primitive_iterator.m_it == 0));
|
||||
--it;
|
||||
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,489 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#define JSON_TESTS_PRIVATE
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
template<typename Iter>
|
||||
using can_post_increment_temporary = decltype((std::declval<Iter>()++)++);
|
||||
|
||||
template<typename Iter>
|
||||
using can_post_decrement_temporary = decltype((std::declval<Iter>()--)--);
|
||||
|
||||
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_WITH_AS(*it, "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
}
|
||||
|
||||
SECTION("number")
|
||||
{
|
||||
json j(17);
|
||||
json::iterator it = j.begin();
|
||||
CHECK(*it == json(17));
|
||||
it = j.end();
|
||||
CHECK_THROWS_WITH_AS(*it, "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
}
|
||||
|
||||
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_WITH_AS(std::string(it->type_name()), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
}
|
||||
|
||||
SECTION("number")
|
||||
{
|
||||
json j(17);
|
||||
json::iterator it = j.begin();
|
||||
CHECK(std::string(it->type_name()) == "number");
|
||||
it = j.end();
|
||||
CHECK_THROWS_WITH_AS(std::string(it->type_name()), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
}
|
||||
|
||||
SECTION("object")
|
||||
{
|
||||
json j({{"foo", "bar"}});
|
||||
json::iterator it = j.begin();
|
||||
CHECK(std::string(it->type_name()) == "string");
|
||||
}
|
||||
|
||||
SECTION("array")
|
||||
{
|
||||
json j({1, 2, 3, 4});
|
||||
json::iterator it = j.begin();
|
||||
CHECK(std::string(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.m_it == 1));
|
||||
it++;
|
||||
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 1));
|
||||
}
|
||||
|
||||
SECTION("number")
|
||||
{
|
||||
json j(17);
|
||||
json::iterator it = j.begin();
|
||||
CHECK((it.m_it.primitive_iterator.m_it == 0));
|
||||
it++;
|
||||
CHECK((it.m_it.primitive_iterator.m_it == 1));
|
||||
it++;
|
||||
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 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.m_it == 1));
|
||||
++it;
|
||||
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 1));
|
||||
}
|
||||
|
||||
SECTION("number")
|
||||
{
|
||||
json j(17);
|
||||
json::iterator it = j.begin();
|
||||
CHECK((it.m_it.primitive_iterator.m_it == 0));
|
||||
++it;
|
||||
CHECK((it.m_it.primitive_iterator.m_it == 1));
|
||||
++it;
|
||||
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 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.m_it == 1));
|
||||
}
|
||||
|
||||
SECTION("number")
|
||||
{
|
||||
json j(17);
|
||||
json::iterator it = j.end();
|
||||
CHECK((it.m_it.primitive_iterator.m_it == 1));
|
||||
it--;
|
||||
CHECK((it.m_it.primitive_iterator.m_it == 0));
|
||||
it--;
|
||||
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 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.m_it == 1));
|
||||
}
|
||||
|
||||
SECTION("number")
|
||||
{
|
||||
json j(17);
|
||||
json::iterator it = j.end();
|
||||
CHECK((it.m_it.primitive_iterator.m_it == 1));
|
||||
--it;
|
||||
CHECK((it.m_it.primitive_iterator.m_it == 0));
|
||||
--it;
|
||||
CHECK((it.m_it.primitive_iterator.m_it != 0 && it.m_it.primitive_iterator.m_it != 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("equality-preserving")
|
||||
{
|
||||
SECTION("post-increment")
|
||||
{
|
||||
SECTION("primitive_iterator_t")
|
||||
{
|
||||
using Iter = nlohmann::detail::primitive_iterator_t;
|
||||
CHECK(std::is_same < decltype(std::declval<Iter&>()++), Iter >::value);
|
||||
}
|
||||
SECTION("iter_impl")
|
||||
{
|
||||
using Iter = nlohmann::detail::iter_impl<json>;
|
||||
CHECK(std::is_same < decltype(std::declval<Iter&>()++), Iter >::value);
|
||||
}
|
||||
SECTION("json_reverse_iterator")
|
||||
{
|
||||
using Base = nlohmann::detail::iter_impl<json>;
|
||||
using Iter = nlohmann::detail::json_reverse_iterator<Base>;
|
||||
CHECK(std::is_same < decltype(std::declval<Iter&>()++), Iter >::value);
|
||||
}
|
||||
}
|
||||
SECTION("post-decrement")
|
||||
{
|
||||
SECTION("primitive_iterator_t")
|
||||
{
|
||||
using Iter = nlohmann::detail::primitive_iterator_t;
|
||||
CHECK(std::is_same < decltype(std::declval<Iter&>()--), Iter >::value);
|
||||
}
|
||||
SECTION("iter_impl")
|
||||
{
|
||||
using Iter = nlohmann::detail::iter_impl<json>;
|
||||
CHECK(std::is_same < decltype(std::declval<Iter&>()--), Iter >::value );
|
||||
}
|
||||
SECTION("json_reverse_iterator")
|
||||
{
|
||||
using Base = nlohmann::detail::iter_impl<json>;
|
||||
using Iter = nlohmann::detail::json_reverse_iterator<Base>;
|
||||
CHECK(std::is_same < decltype(std::declval<Iter&>()--), Iter >::value );
|
||||
}
|
||||
}
|
||||
}
|
||||
// prevent "accidental mutation of a temporary object"
|
||||
SECTION("cert-dcl21-cpp")
|
||||
{
|
||||
using nlohmann::detail::is_detected;
|
||||
SECTION("post-increment")
|
||||
{
|
||||
SECTION("primitive_iterator_t")
|
||||
{
|
||||
using Iter = nlohmann::detail::primitive_iterator_t;
|
||||
CHECK_FALSE(is_detected<can_post_increment_temporary, Iter&>::value);
|
||||
}
|
||||
SECTION("iter_impl")
|
||||
{
|
||||
using Iter = nlohmann::detail::iter_impl<json>;
|
||||
CHECK_FALSE(is_detected<can_post_increment_temporary, Iter&>::value);
|
||||
}
|
||||
SECTION("json_reverse_iterator")
|
||||
{
|
||||
using Base = nlohmann::detail::iter_impl<json>;
|
||||
using Iter = nlohmann::detail::json_reverse_iterator<Base>;
|
||||
CHECK_FALSE(is_detected<can_post_increment_temporary, Iter&>::value);
|
||||
}
|
||||
}
|
||||
SECTION("post-decrement")
|
||||
{
|
||||
SECTION("primitive_iterator_t")
|
||||
{
|
||||
using Iter = nlohmann::detail::primitive_iterator_t;
|
||||
CHECK_FALSE(is_detected<can_post_decrement_temporary, Iter&>::value);
|
||||
}
|
||||
SECTION("iter_impl")
|
||||
{
|
||||
using Iter = nlohmann::detail::iter_impl<json>;
|
||||
CHECK_FALSE(is_detected<can_post_decrement_temporary, Iter&>::value);
|
||||
}
|
||||
SECTION("json_reverse_iterator")
|
||||
{
|
||||
using Base = nlohmann::detail::iter_impl<json>;
|
||||
using Iter = nlohmann::detail::json_reverse_iterator<Base>;
|
||||
CHECK_FALSE(is_detected<can_post_decrement_temporary, Iter&>::value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#define JSON_TESTS_PRIVATE
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
namespace
|
||||
{
|
||||
// shortcut to scan a string literal
|
||||
json::lexer::token_type scan_string(const char* s, bool ignore_comments = false);
|
||||
json::lexer::token_type scan_string(const char* s, const bool ignore_comments)
|
||||
{
|
||||
auto ia = nlohmann::detail::input_adapter(s);
|
||||
return nlohmann::detail::lexer<json, decltype(ia)>(std::move(ia), ignore_comments).scan(); // NOLINT(hicpp-move-const-arg,performance-move-const-arg)
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::string get_error_message(const char* s, bool ignore_comments = false);
|
||||
std::string get_error_message(const char* s, const bool ignore_comments)
|
||||
{
|
||||
auto ia = nlohmann::detail::input_adapter(s);
|
||||
auto lexer = nlohmann::detail::lexer<json, decltype(ia)>(std::move(ia), ignore_comments); // NOLINT(hicpp-move-const-arg,performance-move-const-arg)
|
||||
lexer.scan();
|
||||
return lexer.get_error_message();
|
||||
}
|
||||
|
||||
TEST_CASE("lexer class")
|
||||
{
|
||||
SECTION("scan")
|
||||
{
|
||||
SECTION("structural characters")
|
||||
{
|
||||
CHECK((scan_string("[") == json::lexer::token_type::begin_array));
|
||||
CHECK((scan_string("]") == json::lexer::token_type::end_array));
|
||||
CHECK((scan_string("{") == json::lexer::token_type::begin_object));
|
||||
CHECK((scan_string("}") == json::lexer::token_type::end_object));
|
||||
CHECK((scan_string(",") == json::lexer::token_type::value_separator));
|
||||
CHECK((scan_string(":") == json::lexer::token_type::name_separator));
|
||||
}
|
||||
|
||||
SECTION("literal names")
|
||||
{
|
||||
CHECK((scan_string("null") == json::lexer::token_type::literal_null));
|
||||
CHECK((scan_string("true") == json::lexer::token_type::literal_true));
|
||||
CHECK((scan_string("false") == json::lexer::token_type::literal_false));
|
||||
}
|
||||
|
||||
SECTION("numbers")
|
||||
{
|
||||
CHECK((scan_string("0") == json::lexer::token_type::value_unsigned));
|
||||
CHECK((scan_string("1") == json::lexer::token_type::value_unsigned));
|
||||
CHECK((scan_string("2") == json::lexer::token_type::value_unsigned));
|
||||
CHECK((scan_string("3") == json::lexer::token_type::value_unsigned));
|
||||
CHECK((scan_string("4") == json::lexer::token_type::value_unsigned));
|
||||
CHECK((scan_string("5") == json::lexer::token_type::value_unsigned));
|
||||
CHECK((scan_string("6") == json::lexer::token_type::value_unsigned));
|
||||
CHECK((scan_string("7") == json::lexer::token_type::value_unsigned));
|
||||
CHECK((scan_string("8") == json::lexer::token_type::value_unsigned));
|
||||
CHECK((scan_string("9") == json::lexer::token_type::value_unsigned));
|
||||
|
||||
CHECK((scan_string("-0") == json::lexer::token_type::value_integer));
|
||||
CHECK((scan_string("-1") == json::lexer::token_type::value_integer));
|
||||
|
||||
CHECK((scan_string("1.1") == json::lexer::token_type::value_float));
|
||||
CHECK((scan_string("-1.1") == json::lexer::token_type::value_float));
|
||||
CHECK((scan_string("1E10") == json::lexer::token_type::value_float));
|
||||
}
|
||||
|
||||
SECTION("whitespace")
|
||||
{
|
||||
// result is end_of_input, because not token is following
|
||||
CHECK((scan_string(" ") == json::lexer::token_type::end_of_input));
|
||||
CHECK((scan_string("\t") == json::lexer::token_type::end_of_input));
|
||||
CHECK((scan_string("\n") == json::lexer::token_type::end_of_input));
|
||||
CHECK((scan_string("\r") == json::lexer::token_type::end_of_input));
|
||||
CHECK((scan_string(" \t\n\r\n\t ") == json::lexer::token_type::end_of_input));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("token_type_name")
|
||||
{
|
||||
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::uninitialized)) == "<uninitialized>"));
|
||||
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::literal_true)) == "true literal"));
|
||||
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::literal_false)) == "false literal"));
|
||||
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::literal_null)) == "null literal"));
|
||||
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_string)) == "string literal"));
|
||||
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_unsigned)) == "number literal"));
|
||||
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_integer)) == "number literal"));
|
||||
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_float)) == "number literal"));
|
||||
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::begin_array)) == "'['"));
|
||||
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::begin_object)) == "'{'"));
|
||||
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::end_array)) == "']'"));
|
||||
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::end_object)) == "'}'"));
|
||||
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::name_separator)) == "':'"));
|
||||
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::value_separator)) == "','"));
|
||||
CHECK((std::string(json::lexer::token_type_name(json::lexer::token_type::parse_error)) == "<parse error>"));
|
||||
CHECK((std::string(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)
|
||||
{
|
||||
// create string from the ASCII code
|
||||
const auto s = std::string(1, static_cast<char>(c));
|
||||
// store scan() result
|
||||
const auto res = scan_string(s.c_str());
|
||||
|
||||
CAPTURE(s)
|
||||
|
||||
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((res != json::lexer::token_type::parse_error));
|
||||
break;
|
||||
}
|
||||
|
||||
// whitespace
|
||||
case (' '):
|
||||
case ('\t'):
|
||||
case ('\n'):
|
||||
case ('\r'):
|
||||
{
|
||||
CHECK((res == json::lexer::token_type::end_of_input));
|
||||
break;
|
||||
}
|
||||
|
||||
// anything else is not expected
|
||||
default:
|
||||
{
|
||||
CHECK((res == json::lexer::token_type::parse_error));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("very large string")
|
||||
{
|
||||
// strings larger than 1024 bytes yield a resize of the lexer's yytext buffer
|
||||
std::string s("\"");
|
||||
s += std::string(2048, 'x');
|
||||
s += "\"";
|
||||
CHECK((scan_string(s.c_str()) == json::lexer::token_type::value_string));
|
||||
}
|
||||
|
||||
SECTION("fail on comments")
|
||||
{
|
||||
CHECK((scan_string("/", false) == json::lexer::token_type::parse_error));
|
||||
CHECK(get_error_message("/", false) == "invalid literal");
|
||||
|
||||
CHECK((scan_string("/!", false) == json::lexer::token_type::parse_error));
|
||||
CHECK(get_error_message("/!", false) == "invalid literal");
|
||||
CHECK((scan_string("/*", false) == json::lexer::token_type::parse_error));
|
||||
CHECK(get_error_message("/*", false) == "invalid literal");
|
||||
CHECK((scan_string("/**", false) == json::lexer::token_type::parse_error));
|
||||
CHECK(get_error_message("/**", false) == "invalid literal");
|
||||
|
||||
CHECK((scan_string("//", false) == json::lexer::token_type::parse_error));
|
||||
CHECK(get_error_message("//", false) == "invalid literal");
|
||||
CHECK((scan_string("/**/", false) == json::lexer::token_type::parse_error));
|
||||
CHECK(get_error_message("/**/", false) == "invalid literal");
|
||||
CHECK((scan_string("/** /", false) == json::lexer::token_type::parse_error));
|
||||
CHECK(get_error_message("/** /", false) == "invalid literal");
|
||||
|
||||
CHECK((scan_string("/***/", false) == json::lexer::token_type::parse_error));
|
||||
CHECK(get_error_message("/***/", false) == "invalid literal");
|
||||
CHECK((scan_string("/* true */", false) == json::lexer::token_type::parse_error));
|
||||
CHECK(get_error_message("/* true */", false) == "invalid literal");
|
||||
CHECK((scan_string("/*/**/", false) == json::lexer::token_type::parse_error));
|
||||
CHECK(get_error_message("/*/**/", false) == "invalid literal");
|
||||
CHECK((scan_string("/*/* */", false) == json::lexer::token_type::parse_error));
|
||||
CHECK(get_error_message("/*/* */", false) == "invalid literal");
|
||||
}
|
||||
|
||||
SECTION("ignore comments")
|
||||
{
|
||||
CHECK((scan_string("/", true) == json::lexer::token_type::parse_error));
|
||||
CHECK(get_error_message("/", true) == "invalid comment; expecting '/' or '*' after '/'");
|
||||
|
||||
CHECK((scan_string("/!", true) == json::lexer::token_type::parse_error));
|
||||
CHECK(get_error_message("/!", true) == "invalid comment; expecting '/' or '*' after '/'");
|
||||
CHECK((scan_string("/*", true) == json::lexer::token_type::parse_error));
|
||||
CHECK(get_error_message("/*", true) == "invalid comment; missing closing '*/'");
|
||||
CHECK((scan_string("/**", true) == json::lexer::token_type::parse_error));
|
||||
CHECK(get_error_message("/**", true) == "invalid comment; missing closing '*/'");
|
||||
|
||||
CHECK((scan_string("//", true) == json::lexer::token_type::end_of_input));
|
||||
CHECK((scan_string("/**/", true) == json::lexer::token_type::end_of_input));
|
||||
CHECK((scan_string("/** /", true) == json::lexer::token_type::parse_error));
|
||||
CHECK(get_error_message("/** /", true) == "invalid comment; missing closing '*/'");
|
||||
|
||||
CHECK((scan_string("/***/", true) == json::lexer::token_type::end_of_input));
|
||||
CHECK((scan_string("/* true */", true) == json::lexer::token_type::end_of_input));
|
||||
CHECK((scan_string("/*/**/", true) == json::lexer::token_type::end_of_input));
|
||||
CHECK((scan_string("/*/* */", true) == json::lexer::token_type::end_of_input));
|
||||
|
||||
CHECK((scan_string("//\n//\n", true) == json::lexer::token_type::end_of_input));
|
||||
CHECK((scan_string("/**//**//**/", true) == json::lexer::token_type::end_of_input));
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
namespace
|
||||
{
|
||||
// helper function to check std::less<json::value_t>
|
||||
// see https://en.cppreference.com/w/cpp/utility/functional/less
|
||||
template <typename A, typename B, typename U = std::less<json::value_t>>
|
||||
bool f(A a, B b, U u = U())
|
||||
{
|
||||
return u(a, b);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
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,
|
||||
json::value_t::binary
|
||||
};
|
||||
|
||||
SECTION("comparison: less")
|
||||
{
|
||||
std::vector<std::vector<bool>> expected =
|
||||
{
|
||||
{false, true, true, true, true, true, true, true, true},
|
||||
{false, false, true, true, true, true, true, true, true},
|
||||
{false, false, false, false, false, true, true, true, true},
|
||||
{false, false, false, false, false, true, true, true, true},
|
||||
{false, false, false, false, false, true, true, true, true},
|
||||
{false, false, false, false, false, false, true, true, true},
|
||||
{false, false, false, false, false, false, false, true, true},
|
||||
{false, false, false, false, false, false, false, false, true},
|
||||
{false, 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]);
|
||||
CHECK(f(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"}}},
|
||||
json::binary({1, 2, 3}), json::binary({1, 2, 4})
|
||||
};
|
||||
|
||||
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, false, false},
|
||||
{true, true, 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, 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, false, false, true, 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, 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, false, false, true, 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},
|
||||
{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, false, false, true, 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, 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, false, false, true, 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, 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, false, false, true, false},
|
||||
{false, 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)
|
||||
CAPTURE(j_values[i])
|
||||
CAPTURE(j_values[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 (const auto& v : j_values)
|
||||
{
|
||||
CHECK( (v == j_discarded) == false);
|
||||
CHECK( (j_discarded == v) == 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]) == !(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) == !(j_null == nullptr));
|
||||
CHECK( (nullptr != j_null) == !(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, true, true},
|
||||
{false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true},
|
||||
{false, false, false, true, true, true, true, true, true, true, false, false, true, true, true, true, true, true},
|
||||
{false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, true, true, true},
|
||||
{false, false, false, true, false, true, false, true, true, true, false, false, true, true, true, true, true, true},
|
||||
{false, false, false, true, false, false, false, true, true, true, false, false, true, true, true, true, true, true},
|
||||
{false, false, false, true, true, true, false, true, true, true, false, false, true, true, true, true, true, true},
|
||||
{false, false, false, true, false, false, false, false, true, true, false, false, true, true, true, true, true, true},
|
||||
{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true},
|
||||
{false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, true, true},
|
||||
{false, false, true, true, true, true, true, true, true, true, false, false, true, true, true, true, true, true},
|
||||
{false, false, true, true, true, true, true, true, true, true, true, false, true, true, true, true, true, true},
|
||||
{false, false, false, false, false, false, false, false, true, true, false, false, false, true, false, false, true, true},
|
||||
{false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, true, true},
|
||||
{false, false, false, false, false, false, false, false, true, true, false, false, true, true, false, false, true, true},
|
||||
{false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, false, true, true},
|
||||
{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, false, false, false, false, false, false, false, false, false}
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < j_values.size(); ++i)
|
||||
{
|
||||
for (size_t j = 0; j < j_values.size(); ++j)
|
||||
{
|
||||
// Skip comparing indicies 12 and 13, and 13 and 12 in C++20 pending fix
|
||||
// See issue #3207
|
||||
#if defined(JSON_HAS_CPP_20) || JSON_HAS_THREE_WAY_COMPARISON
|
||||
if ((i == 12 && j == 13) || (i == 13 && j == 12))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
CAPTURE(i)
|
||||
CAPTURE(j)
|
||||
CAPTURE(j_values[i])
|
||||
CAPTURE(j_values[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]) == !(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]) == !(j_values[i] < j_values[j]) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
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(static_cast<json::size_type>((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_move_constructible<json>::value);
|
||||
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();
|
||||
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();
|
||||
swap(it1, it2);
|
||||
CHECK(it1 == j.end());
|
||||
CHECK(it2 == j.begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
TEST_CASE("other constructors and destructor")
|
||||
{
|
||||
SECTION("copy constructor")
|
||||
{
|
||||
SECTION("object")
|
||||
{
|
||||
json j {{"foo", 1}, {"bar", false}};
|
||||
json k(j); // NOLINT(performance-unnecessary-copy-initialization)
|
||||
CHECK(j == k);
|
||||
}
|
||||
|
||||
SECTION("array")
|
||||
{
|
||||
json j {"foo", 1, 42.23, false};
|
||||
json k(j); // NOLINT(performance-unnecessary-copy-initialization)
|
||||
CHECK(j == k);
|
||||
}
|
||||
|
||||
SECTION("null")
|
||||
{
|
||||
json j(nullptr);
|
||||
json k(j); // NOLINT(performance-unnecessary-copy-initialization)
|
||||
CHECK(j == k);
|
||||
}
|
||||
|
||||
SECTION("boolean")
|
||||
{
|
||||
json j(true);
|
||||
json k(j); // NOLINT(performance-unnecessary-copy-initialization)
|
||||
CHECK(j == k);
|
||||
}
|
||||
|
||||
SECTION("string")
|
||||
{
|
||||
json j("Hello world");
|
||||
json k(j); // NOLINT(performance-unnecessary-copy-initialization)
|
||||
CHECK(j == k);
|
||||
}
|
||||
|
||||
SECTION("number (integer)")
|
||||
{
|
||||
json j(42);
|
||||
json k(j); // NOLINT(performance-unnecessary-copy-initialization)
|
||||
CHECK(j == k);
|
||||
}
|
||||
|
||||
SECTION("number (unsigned)")
|
||||
{
|
||||
json j(42u);
|
||||
json k(j); // NOLINT(performance-unnecessary-copy-initialization)
|
||||
CHECK(j == k);
|
||||
}
|
||||
|
||||
SECTION("number (floating-point)")
|
||||
{
|
||||
json j(42.23);
|
||||
json k(j); // NOLINT(performance-unnecessary-copy-initialization)
|
||||
CHECK(j == k);
|
||||
}
|
||||
|
||||
SECTION("binary")
|
||||
{
|
||||
json j = json::binary({1, 2, 3});
|
||||
json k(j); // NOLINT(performance-unnecessary-copy-initialization)
|
||||
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); // NOLINT: access after move is OK here
|
||||
}
|
||||
|
||||
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("binary")
|
||||
{
|
||||
json j = json::binary({1, 2, 3});
|
||||
json k;
|
||||
k = j;
|
||||
CHECK(j == k);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("destructor")
|
||||
{
|
||||
SECTION("object")
|
||||
{
|
||||
auto* j = new json {{"foo", 1}, {"bar", false}}; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
delete j; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
}
|
||||
|
||||
SECTION("array")
|
||||
{
|
||||
auto* j = new json {"foo", 1, 1u, false, 23.42}; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
delete j; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
}
|
||||
|
||||
SECTION("string")
|
||||
{
|
||||
auto* j = new json("Hello world"); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
delete j; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#define JSON_TESTS_PRIVATE
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace
|
||||
{
|
||||
struct alt_string_iter
|
||||
{
|
||||
alt_string_iter() = default;
|
||||
alt_string_iter(const char* cstr)
|
||||
: impl(cstr)
|
||||
{}
|
||||
|
||||
void reserve(std::size_t s)
|
||||
{
|
||||
impl.reserve(s);
|
||||
}
|
||||
|
||||
template<typename Iter>
|
||||
void append(Iter first, Iter last)
|
||||
{
|
||||
impl.append(first, last);
|
||||
}
|
||||
|
||||
std::string::const_iterator begin() const
|
||||
{
|
||||
return impl.begin();
|
||||
}
|
||||
|
||||
std::string::const_iterator end() const
|
||||
{
|
||||
return impl.end();
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
return impl.size();
|
||||
}
|
||||
|
||||
alt_string_iter& operator+=(const char c)
|
||||
{
|
||||
impl += c;
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string impl{};
|
||||
};
|
||||
|
||||
struct alt_string_data
|
||||
{
|
||||
alt_string_data() = default;
|
||||
alt_string_data(const char* cstr)
|
||||
: impl(cstr)
|
||||
{}
|
||||
|
||||
void reserve(std::size_t s)
|
||||
{
|
||||
impl.reserve(s);
|
||||
}
|
||||
|
||||
void append(const char* p, std::size_t s)
|
||||
{
|
||||
impl.append(p, s);
|
||||
}
|
||||
|
||||
const char* data() const
|
||||
{
|
||||
return impl.data();
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
return impl.size();
|
||||
}
|
||||
|
||||
alt_string_data& operator+=(const char c)
|
||||
{
|
||||
impl += c;
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string impl{};
|
||||
};
|
||||
|
||||
void check_escaped(const char* original, const char* escaped = "", bool ensure_ascii = false);
|
||||
void check_escaped(const char* original, const char* escaped, const bool ensure_ascii)
|
||||
{
|
||||
std::stringstream ss;
|
||||
json::serializer s(nlohmann::detail::output_adapter<char>(ss), ' ');
|
||||
s.dump_escaped(original, ensure_ascii);
|
||||
CHECK(ss.str() == escaped);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("convenience functions")
|
||||
{
|
||||
SECTION("type name as string")
|
||||
{
|
||||
CHECK(std::string(json(json::value_t::null).type_name()) == "null");
|
||||
CHECK(std::string(json(json::value_t::object).type_name()) == "object");
|
||||
CHECK(std::string(json(json::value_t::array).type_name()) == "array");
|
||||
CHECK(std::string(json(json::value_t::number_integer).type_name()) == "number");
|
||||
CHECK(std::string(json(json::value_t::number_unsigned).type_name()) == "number");
|
||||
CHECK(std::string(json(json::value_t::number_float).type_name()) == "number");
|
||||
CHECK(std::string(json(json::value_t::binary).type_name()) == "binary");
|
||||
CHECK(std::string(json(json::value_t::boolean).type_name()) == "boolean");
|
||||
CHECK(std::string(json(json::value_t::string).type_name()) == "string");
|
||||
CHECK(std::string(json(json::value_t::discarded).type_name()) == "discarded");
|
||||
}
|
||||
|
||||
SECTION("string escape")
|
||||
{
|
||||
check_escaped("\"", "\\\"");
|
||||
check_escaped("\\", "\\\\");
|
||||
check_escaped("\b", "\\b");
|
||||
check_escaped("\f", "\\f");
|
||||
check_escaped("\n", "\\n");
|
||||
check_escaped("\r", "\\r");
|
||||
check_escaped("\t", "\\t");
|
||||
|
||||
check_escaped("\x01", "\\u0001");
|
||||
check_escaped("\x02", "\\u0002");
|
||||
check_escaped("\x03", "\\u0003");
|
||||
check_escaped("\x04", "\\u0004");
|
||||
check_escaped("\x05", "\\u0005");
|
||||
check_escaped("\x06", "\\u0006");
|
||||
check_escaped("\x07", "\\u0007");
|
||||
check_escaped("\x08", "\\b");
|
||||
check_escaped("\x09", "\\t");
|
||||
check_escaped("\x0a", "\\n");
|
||||
check_escaped("\x0b", "\\u000b");
|
||||
check_escaped("\x0c", "\\f");
|
||||
check_escaped("\x0d", "\\r");
|
||||
check_escaped("\x0e", "\\u000e");
|
||||
check_escaped("\x0f", "\\u000f");
|
||||
check_escaped("\x10", "\\u0010");
|
||||
check_escaped("\x11", "\\u0011");
|
||||
check_escaped("\x12", "\\u0012");
|
||||
check_escaped("\x13", "\\u0013");
|
||||
check_escaped("\x14", "\\u0014");
|
||||
check_escaped("\x15", "\\u0015");
|
||||
check_escaped("\x16", "\\u0016");
|
||||
check_escaped("\x17", "\\u0017");
|
||||
check_escaped("\x18", "\\u0018");
|
||||
check_escaped("\x19", "\\u0019");
|
||||
check_escaped("\x1a", "\\u001a");
|
||||
check_escaped("\x1b", "\\u001b");
|
||||
check_escaped("\x1c", "\\u001c");
|
||||
check_escaped("\x1d", "\\u001d");
|
||||
check_escaped("\x1e", "\\u001e");
|
||||
check_escaped("\x1f", "\\u001f");
|
||||
|
||||
// invalid UTF-8 characters
|
||||
CHECK_THROWS_WITH_AS(check_escaped("ä\xA9ü"), "[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9", json::type_error&);
|
||||
|
||||
CHECK_THROWS_WITH_AS(check_escaped("\xC2"), "[json.exception.type_error.316] incomplete UTF-8 string; last byte: 0xC2", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("string concat")
|
||||
{
|
||||
using nlohmann::detail::concat;
|
||||
|
||||
const char* expected = "Hello, world!";
|
||||
alt_string_iter hello_iter{"Hello, "};
|
||||
alt_string_data hello_data{"Hello, "};
|
||||
std::string world = "world";
|
||||
|
||||
SECTION("std::string")
|
||||
{
|
||||
std::string str1 = concat(hello_iter, world, '!');
|
||||
std::string str2 = concat(hello_data, world, '!');
|
||||
std::string str3 = concat("Hello, ", world, '!');
|
||||
|
||||
CHECK(str1 == expected);
|
||||
CHECK(str2 == expected);
|
||||
CHECK(str3 == expected);
|
||||
}
|
||||
|
||||
SECTION("alt_string_iter")
|
||||
{
|
||||
alt_string_iter str = concat<alt_string_iter>(hello_iter, world, '!');
|
||||
|
||||
CHECK(str.impl == expected);
|
||||
}
|
||||
|
||||
SECTION("alt_string_data")
|
||||
{
|
||||
alt_string_data str = concat<alt_string_data>(hello_data, world, '!');
|
||||
|
||||
CHECK(str.impl == expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,266 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#ifdef JSON_DIAGNOSTICS
|
||||
#undef JSON_DIAGNOSTICS
|
||||
#endif
|
||||
|
||||
#define JSON_DIAGNOSTICS 1
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
TEST_CASE("Better diagnostics")
|
||||
{
|
||||
SECTION("empty JSON Pointer")
|
||||
{
|
||||
json j = 1;
|
||||
std::string s;
|
||||
CHECK_THROWS_WITH_AS(s = j.get<std::string>(), "[json.exception.type_error.302] type must be string, but is number", json::type_error);
|
||||
}
|
||||
|
||||
SECTION("invalid type")
|
||||
{
|
||||
json j;
|
||||
j["a"]["b"]["c"] = 1;
|
||||
std::string s;
|
||||
CHECK_THROWS_WITH_AS(s = j["a"]["b"]["c"].get<std::string>(), "[json.exception.type_error.302] (/a/b/c) type must be string, but is number", json::type_error);
|
||||
}
|
||||
|
||||
SECTION("missing key")
|
||||
{
|
||||
json j;
|
||||
j["object"]["object"] = true;
|
||||
CHECK_THROWS_WITH_AS(j["object"].at("not_found"), "[json.exception.out_of_range.403] (/object) key 'not_found' not found", json::out_of_range);
|
||||
}
|
||||
|
||||
SECTION("array index out of range")
|
||||
{
|
||||
json j;
|
||||
j["array"][4] = true;
|
||||
CHECK_THROWS_WITH_AS(j["array"].at(5), "[json.exception.out_of_range.401] (/array) array index 5 is out of range", json::out_of_range);
|
||||
}
|
||||
|
||||
SECTION("array index at wrong type")
|
||||
{
|
||||
json j;
|
||||
j["array"][4] = true;
|
||||
CHECK_THROWS_WITH_AS(j["array"][4][5], "[json.exception.type_error.305] (/array/4) cannot use operator[] with a numeric argument with boolean", json::type_error);
|
||||
}
|
||||
|
||||
SECTION("wrong iterator")
|
||||
{
|
||||
json j;
|
||||
j["array"] = json::array();
|
||||
CHECK_THROWS_WITH_AS(j["array"].erase(j.begin()), "[json.exception.invalid_iterator.202] (/array) iterator does not fit current value", json::invalid_iterator);
|
||||
}
|
||||
|
||||
SECTION("JSON Pointer escaping")
|
||||
{
|
||||
json j;
|
||||
j["a/b"]["m~n"] = 1;
|
||||
std::string s;
|
||||
CHECK_THROWS_WITH_AS(s = j["a/b"]["m~n"].get<std::string>(), "[json.exception.type_error.302] (/a~1b/m~0n) type must be string, but is number", json::type_error);
|
||||
}
|
||||
|
||||
SECTION("Parse error")
|
||||
{
|
||||
json _;
|
||||
CHECK_THROWS_WITH_AS(_ = json::parse(""), "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error);
|
||||
}
|
||||
|
||||
SECTION("Wrong type in update()")
|
||||
{
|
||||
json j = {{"foo", "bar"}};
|
||||
json k = {{"bla", 1}};
|
||||
|
||||
CHECK_THROWS_WITH_AS(j.update(k["bla"].begin(), k["bla"].end()), "[json.exception.type_error.312] (/bla) cannot use update() with number", json::type_error);
|
||||
CHECK_THROWS_WITH_AS(j.update(k["bla"]), "[json.exception.type_error.312] (/bla) cannot use update() with number", json::type_error);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Regression tests for extended diagnostics")
|
||||
{
|
||||
SECTION("Regression test for https://github.com/nlohmann/json/pull/2562#pullrequestreview-574858448")
|
||||
{
|
||||
CHECK_THROWS_WITH_AS(json({"0", "0"})[1].get<int>(), "[json.exception.type_error.302] (/1) type must be number, but is string", json::type_error);
|
||||
CHECK_THROWS_WITH_AS(json({"0", "1"})[1].get<int>(), "[json.exception.type_error.302] (/1) type must be number, but is string", json::type_error);
|
||||
}
|
||||
|
||||
SECTION("Regression test for https://github.com/nlohmann/json/pull/2562/files/380a613f2b5d32425021129cd1f371ddcfd54ddf#r563259793")
|
||||
{
|
||||
json j;
|
||||
j["/foo"] = {1, 2, 3};
|
||||
CHECK_THROWS_WITH_AS(j.unflatten(), "[json.exception.type_error.315] (/~1foo) values in object must be primitive", json::type_error);
|
||||
}
|
||||
|
||||
SECTION("Regression test for issue #2838 - Assertion failure when inserting into arrays with JSON_DIAGNOSTICS set")
|
||||
{
|
||||
// void push_back(basic_json&& val)
|
||||
{
|
||||
json j_arr = json::array();
|
||||
j_arr.push_back(json::object());
|
||||
j_arr.push_back(json::object());
|
||||
j_arr.push_back(json::object());
|
||||
j_arr.push_back(json::object());
|
||||
json j_obj = json::object();
|
||||
j_obj["key"] = j_arr;
|
||||
}
|
||||
|
||||
// void push_back(const basic_json& val)
|
||||
{
|
||||
json j_arr = json::array();
|
||||
auto object = json::object();
|
||||
j_arr.push_back(object);
|
||||
j_arr.push_back(object);
|
||||
j_arr.push_back(object);
|
||||
j_arr.push_back(object);
|
||||
json j_obj = json::object();
|
||||
j_obj["key"] = j_arr;
|
||||
}
|
||||
|
||||
// reference emplace_back(Args&& ... args)
|
||||
{
|
||||
json j_arr = json::array();
|
||||
j_arr.emplace_back(json::object());
|
||||
j_arr.emplace_back(json::object());
|
||||
j_arr.emplace_back(json::object());
|
||||
j_arr.emplace_back(json::object());
|
||||
json j_obj = json::object();
|
||||
j_obj["key"] = j_arr;
|
||||
}
|
||||
|
||||
// iterator insert(const_iterator pos, const basic_json& val)
|
||||
{
|
||||
json j_arr = json::array();
|
||||
j_arr.insert(j_arr.begin(), json::object());
|
||||
j_arr.insert(j_arr.begin(), json::object());
|
||||
j_arr.insert(j_arr.begin(), json::object());
|
||||
j_arr.insert(j_arr.begin(), json::object());
|
||||
json j_obj = json::object();
|
||||
j_obj["key"] = j_arr;
|
||||
}
|
||||
|
||||
// iterator insert(const_iterator pos, size_type cnt, const basic_json& val)
|
||||
{
|
||||
json j_arr = json::array();
|
||||
j_arr.insert(j_arr.begin(), 2, json::object());
|
||||
json j_obj = json::object();
|
||||
j_obj["key"] = j_arr;
|
||||
}
|
||||
|
||||
// iterator insert(const_iterator pos, const_iterator first, const_iterator last)
|
||||
{
|
||||
json j_arr = json::array();
|
||||
json j_objects = {json::object(), json::object()};
|
||||
j_arr.insert(j_arr.begin(), j_objects.begin(), j_objects.end());
|
||||
json j_obj = json::object();
|
||||
j_obj["key"] = j_arr;
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Regression test for issue #2962 - JSON_DIAGNOSTICS assertion for ordered_json")
|
||||
{
|
||||
nlohmann::ordered_json j;
|
||||
nlohmann::ordered_json j2;
|
||||
const std::string value;
|
||||
j["first"] = value;
|
||||
j["second"] = value;
|
||||
j2["something"] = j;
|
||||
}
|
||||
|
||||
SECTION("Regression test for issue #3007 - Parent pointers properly set when using update()")
|
||||
{
|
||||
// void update(const_reference j)
|
||||
{
|
||||
json j = json::object();
|
||||
|
||||
{
|
||||
json j2 = json::object();
|
||||
j2["one"] = 1;
|
||||
|
||||
j.update(j2);
|
||||
}
|
||||
|
||||
// Must call operator[] on const element, otherwise m_parent gets updated.
|
||||
auto const& constJ = j;
|
||||
CHECK_THROWS_WITH_AS(constJ["one"].at(0), "[json.exception.type_error.304] (/one) cannot use at() with number", json::type_error);
|
||||
}
|
||||
|
||||
// void update(const_iterator first, const_iterator last)
|
||||
{
|
||||
json j = json::object();
|
||||
|
||||
{
|
||||
json j2 = json::object();
|
||||
j2["one"] = 1;
|
||||
|
||||
j.update(j2.begin(), j2.end());
|
||||
}
|
||||
|
||||
// Must call operator[] on const element, otherwise m_parent gets updated.
|
||||
auto const& constJ = j;
|
||||
CHECK_THROWS_WITH_AS(constJ["one"].at(0), "[json.exception.type_error.304] (/one) cannot use at() with number", json::type_error);
|
||||
}
|
||||
|
||||
// Code from #3007 triggering unwanted assertion without fix to update().
|
||||
{
|
||||
json root = json::array();
|
||||
json lower = json::object();
|
||||
|
||||
{
|
||||
json lowest = json::object();
|
||||
lowest["one"] = 1;
|
||||
|
||||
lower.update(lowest);
|
||||
}
|
||||
|
||||
root.push_back(lower);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Regression test for issue #3032 - Yet another assertion failure when inserting into arrays with JSON_DIAGNOSTICS set")
|
||||
{
|
||||
// reference operator[](size_type idx)
|
||||
{
|
||||
json j_arr = json::array();
|
||||
j_arr[0] = 0;
|
||||
j_arr[1] = 1;
|
||||
j_arr[2] = 2;
|
||||
j_arr[3] = 3;
|
||||
j_arr[4] = 4;
|
||||
j_arr[5] = 5;
|
||||
j_arr[6] = 6;
|
||||
j_arr[7] = 7;
|
||||
json j_arr_copy = j_arr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
// disable -Wnoexcept as exceptions are switched off for this test suite
|
||||
DOCTEST_GCC_SUPPRESS_WARNING_PUSH
|
||||
DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept")
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using json = nlohmann::json;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// for #2824
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
class sax_no_exception : public nlohmann::detail::json_sax_dom_parser<json>
|
||||
{
|
||||
public:
|
||||
explicit sax_no_exception(json& j) : nlohmann::detail::json_sax_dom_parser<json>(j, false) {}
|
||||
|
||||
static bool parse_error(std::size_t /*position*/, const std::string& /*last_token*/, const json::exception& ex)
|
||||
{
|
||||
error_string = new std::string(ex.what()); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::string* error_string;
|
||||
};
|
||||
|
||||
std::string* sax_no_exception::error_string = nullptr;
|
||||
|
||||
TEST_CASE("Tests with disabled exceptions")
|
||||
{
|
||||
SECTION("issue #2824 - encoding of json::exception::what()")
|
||||
{
|
||||
json j;
|
||||
sax_no_exception sax(j);
|
||||
|
||||
CHECK (!json::sax_parse("xyz", &sax));
|
||||
CHECK(*sax_no_exception::error_string == "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - invalid literal; last read: 'x'");
|
||||
delete sax_no_exception::error_string; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
}
|
||||
}
|
||||
|
||||
DOCTEST_GCC_SUPPRESS_WARNING_POP
|
||||
@@ -0,0 +1,901 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
TEST_CASE("element access 1")
|
||||
{
|
||||
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::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::object());
|
||||
CHECK(j_const.at(7) == json({1, 2, 3}));
|
||||
}
|
||||
|
||||
SECTION("access outside bounds")
|
||||
{
|
||||
CHECK_THROWS_WITH_AS(j.at(8),
|
||||
"[json.exception.out_of_range.401] array index 8 is out of range", json::out_of_range&);
|
||||
CHECK_THROWS_WITH_AS(j_const.at(8),
|
||||
"[json.exception.out_of_range.401] array index 8 is out of range", json::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_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with null", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with null", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("boolean")
|
||||
{
|
||||
json j_nonarray(json::value_t::boolean);
|
||||
const json j_nonarray_const(j_nonarray);
|
||||
|
||||
CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with boolean", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with boolean", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("string")
|
||||
{
|
||||
json j_nonarray(json::value_t::string);
|
||||
const json j_nonarray_const(j_nonarray);
|
||||
|
||||
CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with string", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with string", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("object")
|
||||
{
|
||||
json j_nonarray(json::value_t::object);
|
||||
const json j_nonarray_const(j_nonarray);
|
||||
|
||||
CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with object", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with object", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("number (integer)")
|
||||
{
|
||||
json j_nonarray(json::value_t::number_integer);
|
||||
const json j_nonarray_const(j_nonarray);
|
||||
|
||||
CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("number (unsigned)")
|
||||
{
|
||||
json j_nonarray(json::value_t::number_unsigned);
|
||||
const json j_nonarray_const(j_nonarray);
|
||||
|
||||
CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("number (floating-point)")
|
||||
{
|
||||
json j_nonarray(json::value_t::number_float);
|
||||
const json j_nonarray_const(j_nonarray);
|
||||
|
||||
CHECK_THROWS_WITH_AS(j_nonarray.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j_nonarray_const.at(0), "[json.exception.type_error.304] cannot use at() with number", json::type_error&);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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::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::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_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with null", json::type_error&);
|
||||
}
|
||||
|
||||
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_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with boolean", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with boolean", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("string")
|
||||
{
|
||||
json j_nonarray(json::value_t::string);
|
||||
const json j_nonarray_const(j_nonarray);
|
||||
CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with string", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with string", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("object")
|
||||
{
|
||||
json j_nonarray(json::value_t::object);
|
||||
const json j_nonarray_const(j_nonarray);
|
||||
CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with object", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with object", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("number (integer)")
|
||||
{
|
||||
json j_nonarray(json::value_t::number_integer);
|
||||
const json j_nonarray_const(j_nonarray);
|
||||
CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("number (unsigned)")
|
||||
{
|
||||
json j_nonarray(json::value_t::number_unsigned);
|
||||
const json j_nonarray_const(j_nonarray);
|
||||
CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("number (floating-point)")
|
||||
{
|
||||
json j_nonarray(json::value_t::number_float);
|
||||
const json j_nonarray_const(j_nonarray);
|
||||
CHECK_THROWS_WITH_AS(j_nonarray[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j_nonarray_const[0], "[json.exception.type_error.305] cannot use operator[] with a numeric argument with number", json::type_error&);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_WITH_AS(jarray.erase(8), "[json.exception.out_of_range.401] array index 8 is out of range", json::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_WITH_AS(jarray.erase(jarray2.begin()),
|
||||
"[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(jarray.erase(jarray.begin(), jarray2.end()),
|
||||
"[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(jarray.erase(jarray2.begin(), jarray.end()),
|
||||
"[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(jarray.erase(jarray2.begin(), jarray2.end()),
|
||||
"[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
|
||||
json jarray2 = {"foo", "bar"};
|
||||
|
||||
CHECK_THROWS_WITH_AS(jarray.erase(jarray2.cbegin()),
|
||||
"[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(jarray.erase(jarray.cbegin(), jarray2.cend()),
|
||||
"[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(jarray.erase(jarray2.cbegin(), jarray.cend()),
|
||||
"[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(jarray.erase(jarray2.cbegin(), jarray2.cend()),
|
||||
"[json.exception.invalid_iterator.203] iterators do not fit current value", json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("remove element by index in non-array type")
|
||||
{
|
||||
SECTION("null")
|
||||
{
|
||||
json j_nonobject(json::value_t::null);
|
||||
CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with null", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("boolean")
|
||||
{
|
||||
json j_nonobject(json::value_t::boolean);
|
||||
CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with boolean", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("string")
|
||||
{
|
||||
json j_nonobject(json::value_t::string);
|
||||
CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with string", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("object")
|
||||
{
|
||||
json j_nonobject(json::value_t::object);
|
||||
CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with object", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("number (integer)")
|
||||
{
|
||||
json j_nonobject(json::value_t::number_integer);
|
||||
CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with number", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("number (unsigned)")
|
||||
{
|
||||
json j_nonobject(json::value_t::number_unsigned);
|
||||
CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with number", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("number (floating-point)")
|
||||
{
|
||||
json j_nonobject(json::value_t::number_float);
|
||||
CHECK_THROWS_WITH_AS(j_nonobject.erase(0), "[json.exception.type_error.307] cannot use erase() with number", json::type_error&);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("other values")
|
||||
{
|
||||
SECTION("front and back")
|
||||
{
|
||||
SECTION("null")
|
||||
{
|
||||
{
|
||||
json j;
|
||||
CHECK_THROWS_WITH_AS(j.front(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j.back(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
const json j{};
|
||||
CHECK_THROWS_WITH_AS(j.front(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j.back(), "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
|
||||
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_WITH_AS(j.erase(j.begin()), "[json.exception.type_error.307] cannot use erase() with null", json::type_error&);
|
||||
}
|
||||
{
|
||||
json j;
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.begin()),
|
||||
"[json.exception.type_error.307] cannot use erase() with null", json::type_error&);
|
||||
}
|
||||
}
|
||||
|
||||
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("binary")
|
||||
{
|
||||
{
|
||||
json j = json::binary({1, 2, 3});
|
||||
json::iterator it = j.erase(j.begin());
|
||||
CHECK(j.type() == json::value_t::null);
|
||||
CHECK(it == j.end());
|
||||
}
|
||||
{
|
||||
json j = json::binary({1, 2, 3});
|
||||
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_WITH_AS(j.erase(j.end()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
json j = "bar";
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.cend()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("number (boolean)")
|
||||
{
|
||||
{
|
||||
json j = false;
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.end()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
json j = true;
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.cend()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("number (integer)")
|
||||
{
|
||||
{
|
||||
json j = 17;
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.end()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
json j = 17;
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.cend()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("number (unsigned)")
|
||||
{
|
||||
{
|
||||
json j = 17u;
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.end()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
json j = 17u;
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.cend()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("number (floating point)")
|
||||
{
|
||||
{
|
||||
json j = 23.42;
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.end()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
json j = 23.42;
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.cend()), "[json.exception.invalid_iterator.205] iterator out of range", json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("erase with two valid iterators")
|
||||
{
|
||||
SECTION("null")
|
||||
{
|
||||
{
|
||||
json j;
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.end()), "[json.exception.type_error.307] cannot use erase() with null", json::type_error&);
|
||||
}
|
||||
{
|
||||
json j;
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cend()), "[json.exception.type_error.307] cannot use erase() with null", json::type_error&);
|
||||
}
|
||||
}
|
||||
|
||||
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("binary")
|
||||
{
|
||||
{
|
||||
json j = json::binary({1, 2, 3});
|
||||
json::iterator it = j.erase(j.begin(), j.end());
|
||||
CHECK(j.type() == json::value_t::null);
|
||||
CHECK(it == j.end());
|
||||
}
|
||||
{
|
||||
json j = json::binary({1, 2, 3});
|
||||
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_WITH_AS(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
json j = "bar";
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("number (boolean)")
|
||||
{
|
||||
{
|
||||
json j = false;
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
json j = true;
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("number (integer)")
|
||||
{
|
||||
{
|
||||
json j = 17;
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
json j = 17;
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("number (unsigned)")
|
||||
{
|
||||
{
|
||||
json j = 17u;
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
json j = 17u;
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("number (floating point)")
|
||||
{
|
||||
{
|
||||
json j = 23.42;
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.end(), j.end()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.begin(), j.begin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
json j = 23.42;
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.cend(), j.cend()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j.erase(j.cbegin(), j.cbegin()), "[json.exception.invalid_iterator.204] iterators out of range", json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using json = nlohmann::json;
|
||||
using ordered_json = nlohmann::ordered_json;
|
||||
|
||||
#include <set>
|
||||
|
||||
TEST_CASE("hash<nlohmann::json>")
|
||||
{
|
||||
// Collect hashes for different JSON values and make sure that they are distinct
|
||||
// We cannot compare against fixed values, because the implementation of
|
||||
// std::hash may differ between compilers.
|
||||
|
||||
std::set<std::size_t> hashes;
|
||||
|
||||
// null
|
||||
hashes.insert(std::hash<json> {}(json(nullptr)));
|
||||
|
||||
// boolean
|
||||
hashes.insert(std::hash<json> {}(json(true)));
|
||||
hashes.insert(std::hash<json> {}(json(false)));
|
||||
|
||||
// string
|
||||
hashes.insert(std::hash<json> {}(json("")));
|
||||
hashes.insert(std::hash<json> {}(json("foo")));
|
||||
|
||||
// number
|
||||
hashes.insert(std::hash<json> {}(json(0)));
|
||||
hashes.insert(std::hash<json> {}(json(static_cast<unsigned>(0))));
|
||||
|
||||
hashes.insert(std::hash<json> {}(json(-1)));
|
||||
hashes.insert(std::hash<json> {}(json(0.0)));
|
||||
hashes.insert(std::hash<json> {}(json(42.23)));
|
||||
|
||||
// array
|
||||
hashes.insert(std::hash<json> {}(json::array()));
|
||||
hashes.insert(std::hash<json> {}(json::array({1, 2, 3})));
|
||||
|
||||
// object
|
||||
hashes.insert(std::hash<json> {}(json::object()));
|
||||
hashes.insert(std::hash<json> {}(json::object({{"foo", "bar"}})));
|
||||
|
||||
// binary
|
||||
hashes.insert(std::hash<json> {}(json::binary({})));
|
||||
hashes.insert(std::hash<json> {}(json::binary({}, 0)));
|
||||
hashes.insert(std::hash<json> {}(json::binary({}, 42)));
|
||||
hashes.insert(std::hash<json> {}(json::binary({1, 2, 3})));
|
||||
hashes.insert(std::hash<json> {}(json::binary({1, 2, 3}, 0)));
|
||||
hashes.insert(std::hash<json> {}(json::binary({1, 2, 3}, 42)));
|
||||
|
||||
// discarded
|
||||
hashes.insert(std::hash<json> {}(json(json::value_t::discarded)));
|
||||
|
||||
CHECK(hashes.size() == 21);
|
||||
}
|
||||
|
||||
TEST_CASE("hash<nlohmann::ordered_json>")
|
||||
{
|
||||
// Collect hashes for different JSON values and make sure that they are distinct
|
||||
// We cannot compare against fixed values, because the implementation of
|
||||
// std::hash may differ between compilers.
|
||||
|
||||
std::set<std::size_t> hashes;
|
||||
|
||||
// null
|
||||
hashes.insert(std::hash<ordered_json> {}(ordered_json(nullptr)));
|
||||
|
||||
// boolean
|
||||
hashes.insert(std::hash<ordered_json> {}(ordered_json(true)));
|
||||
hashes.insert(std::hash<ordered_json> {}(ordered_json(false)));
|
||||
|
||||
// string
|
||||
hashes.insert(std::hash<ordered_json> {}(ordered_json("")));
|
||||
hashes.insert(std::hash<ordered_json> {}(ordered_json("foo")));
|
||||
|
||||
// number
|
||||
hashes.insert(std::hash<ordered_json> {}(ordered_json(0)));
|
||||
hashes.insert(std::hash<ordered_json> {}(ordered_json(static_cast<unsigned>(0))));
|
||||
|
||||
hashes.insert(std::hash<ordered_json> {}(ordered_json(-1)));
|
||||
hashes.insert(std::hash<ordered_json> {}(ordered_json(0.0)));
|
||||
hashes.insert(std::hash<ordered_json> {}(ordered_json(42.23)));
|
||||
|
||||
// array
|
||||
hashes.insert(std::hash<ordered_json> {}(ordered_json::array()));
|
||||
hashes.insert(std::hash<ordered_json> {}(ordered_json::array({1, 2, 3})));
|
||||
|
||||
// object
|
||||
hashes.insert(std::hash<ordered_json> {}(ordered_json::object()));
|
||||
hashes.insert(std::hash<ordered_json> {}(ordered_json::object({{"foo", "bar"}})));
|
||||
|
||||
// binary
|
||||
hashes.insert(std::hash<ordered_json> {}(ordered_json::binary({})));
|
||||
hashes.insert(std::hash<ordered_json> {}(ordered_json::binary({}, 0)));
|
||||
hashes.insert(std::hash<ordered_json> {}(ordered_json::binary({}, 42)));
|
||||
hashes.insert(std::hash<ordered_json> {}(ordered_json::binary({1, 2, 3})));
|
||||
hashes.insert(std::hash<ordered_json> {}(ordered_json::binary({1, 2, 3}, 0)));
|
||||
hashes.insert(std::hash<ordered_json> {}(ordered_json::binary({1, 2, 3}, 42)));
|
||||
|
||||
// discarded
|
||||
hashes.insert(std::hash<ordered_json> {}(ordered_json(ordered_json::value_t::discarded)));
|
||||
|
||||
CHECK(hashes.size() == 21);
|
||||
}
|
||||
@@ -0,0 +1,480 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <test_data.hpp>
|
||||
|
||||
TEST_CASE("object inspection")
|
||||
{
|
||||
SECTION("convenience type checker")
|
||||
{
|
||||
SECTION("object")
|
||||
{
|
||||
json j {{"foo", 1}, {"bar", false}};
|
||||
CHECK(!j.is_null());
|
||||
CHECK(!j.is_boolean());
|
||||
CHECK(!j.is_number());
|
||||
CHECK(!j.is_number_integer());
|
||||
CHECK(!j.is_number_unsigned());
|
||||
CHECK(!j.is_number_float());
|
||||
CHECK(!j.is_binary());
|
||||
CHECK(j.is_object());
|
||||
CHECK(!j.is_array());
|
||||
CHECK(!j.is_string());
|
||||
CHECK(!j.is_discarded());
|
||||
CHECK(!j.is_primitive());
|
||||
CHECK(j.is_structured());
|
||||
}
|
||||
|
||||
SECTION("array")
|
||||
{
|
||||
json j {"foo", 1, 1u, 42.23, false};
|
||||
CHECK(!j.is_null());
|
||||
CHECK(!j.is_boolean());
|
||||
CHECK(!j.is_number());
|
||||
CHECK(!j.is_number_integer());
|
||||
CHECK(!j.is_number_unsigned());
|
||||
CHECK(!j.is_number_float());
|
||||
CHECK(!j.is_binary());
|
||||
CHECK(!j.is_object());
|
||||
CHECK(j.is_array());
|
||||
CHECK(!j.is_string());
|
||||
CHECK(!j.is_discarded());
|
||||
CHECK(!j.is_primitive());
|
||||
CHECK(j.is_structured());
|
||||
}
|
||||
|
||||
SECTION("null")
|
||||
{
|
||||
json j(nullptr);
|
||||
CHECK(j.is_null());
|
||||
CHECK(!j.is_boolean());
|
||||
CHECK(!j.is_number());
|
||||
CHECK(!j.is_number_integer());
|
||||
CHECK(!j.is_number_unsigned());
|
||||
CHECK(!j.is_number_float());
|
||||
CHECK(!j.is_binary());
|
||||
CHECK(!j.is_object());
|
||||
CHECK(!j.is_array());
|
||||
CHECK(!j.is_string());
|
||||
CHECK(!j.is_discarded());
|
||||
CHECK(j.is_primitive());
|
||||
CHECK(!j.is_structured());
|
||||
}
|
||||
|
||||
SECTION("boolean")
|
||||
{
|
||||
json j(true);
|
||||
CHECK(!j.is_null());
|
||||
CHECK(j.is_boolean());
|
||||
CHECK(!j.is_number());
|
||||
CHECK(!j.is_number_integer());
|
||||
CHECK(!j.is_number_unsigned());
|
||||
CHECK(!j.is_number_float());
|
||||
CHECK(!j.is_binary());
|
||||
CHECK(!j.is_object());
|
||||
CHECK(!j.is_array());
|
||||
CHECK(!j.is_string());
|
||||
CHECK(!j.is_discarded());
|
||||
CHECK(j.is_primitive());
|
||||
CHECK(!j.is_structured());
|
||||
}
|
||||
|
||||
SECTION("string")
|
||||
{
|
||||
json j("Hello world");
|
||||
CHECK(!j.is_null());
|
||||
CHECK(!j.is_boolean());
|
||||
CHECK(!j.is_number());
|
||||
CHECK(!j.is_number_integer());
|
||||
CHECK(!j.is_number_unsigned());
|
||||
CHECK(!j.is_number_float());
|
||||
CHECK(!j.is_binary());
|
||||
CHECK(!j.is_object());
|
||||
CHECK(!j.is_array());
|
||||
CHECK(j.is_string());
|
||||
CHECK(!j.is_discarded());
|
||||
CHECK(j.is_primitive());
|
||||
CHECK(!j.is_structured());
|
||||
}
|
||||
|
||||
SECTION("number (integer)")
|
||||
{
|
||||
json j(42);
|
||||
CHECK(!j.is_null());
|
||||
CHECK(!j.is_boolean());
|
||||
CHECK(j.is_number());
|
||||
CHECK(j.is_number_integer());
|
||||
CHECK(!j.is_number_unsigned());
|
||||
CHECK(!j.is_number_float());
|
||||
CHECK(!j.is_binary());
|
||||
CHECK(!j.is_object());
|
||||
CHECK(!j.is_array());
|
||||
CHECK(!j.is_string());
|
||||
CHECK(!j.is_discarded());
|
||||
CHECK(j.is_primitive());
|
||||
CHECK(!j.is_structured());
|
||||
}
|
||||
|
||||
SECTION("number (unsigned)")
|
||||
{
|
||||
json j(42u);
|
||||
CHECK(!j.is_null());
|
||||
CHECK(!j.is_boolean());
|
||||
CHECK(j.is_number());
|
||||
CHECK(j.is_number_integer());
|
||||
CHECK(j.is_number_unsigned());
|
||||
CHECK(!j.is_number_float());
|
||||
CHECK(!j.is_binary());
|
||||
CHECK(!j.is_object());
|
||||
CHECK(!j.is_array());
|
||||
CHECK(!j.is_string());
|
||||
CHECK(!j.is_discarded());
|
||||
CHECK(j.is_primitive());
|
||||
CHECK(!j.is_structured());
|
||||
}
|
||||
|
||||
SECTION("number (floating-point)")
|
||||
{
|
||||
json j(42.23);
|
||||
CHECK(!j.is_null());
|
||||
CHECK(!j.is_boolean());
|
||||
CHECK(j.is_number());
|
||||
CHECK(!j.is_number_integer());
|
||||
CHECK(!j.is_number_unsigned());
|
||||
CHECK(j.is_number_float());
|
||||
CHECK(!j.is_binary());
|
||||
CHECK(!j.is_object());
|
||||
CHECK(!j.is_array());
|
||||
CHECK(!j.is_string());
|
||||
CHECK(!j.is_discarded());
|
||||
CHECK(j.is_primitive());
|
||||
CHECK(!j.is_structured());
|
||||
}
|
||||
|
||||
SECTION("binary")
|
||||
{
|
||||
json j(json::value_t::binary);
|
||||
CHECK(!j.is_null());
|
||||
CHECK(!j.is_boolean());
|
||||
CHECK(!j.is_number());
|
||||
CHECK(!j.is_number_integer());
|
||||
CHECK(!j.is_number_unsigned());
|
||||
CHECK(!j.is_number_float());
|
||||
CHECK(j.is_binary());
|
||||
CHECK(!j.is_object());
|
||||
CHECK(!j.is_array());
|
||||
CHECK(!j.is_string());
|
||||
CHECK(!j.is_discarded());
|
||||
CHECK(j.is_primitive());
|
||||
CHECK(!j.is_structured());
|
||||
}
|
||||
|
||||
SECTION("discarded")
|
||||
{
|
||||
json j(json::value_t::discarded);
|
||||
CHECK(!j.is_null());
|
||||
CHECK(!j.is_boolean());
|
||||
CHECK(!j.is_number());
|
||||
CHECK(!j.is_number_integer());
|
||||
CHECK(!j.is_number_unsigned());
|
||||
CHECK(!j.is_number_float());
|
||||
CHECK(!j.is_binary());
|
||||
CHECK(!j.is_object());
|
||||
CHECK(!j.is_array());
|
||||
CHECK(!j.is_string());
|
||||
CHECK(j.is_discarded());
|
||||
CHECK(!j.is_primitive());
|
||||
CHECK(!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=1, space='\t'")
|
||||
{
|
||||
CHECK(j.dump(1, '\t') ==
|
||||
"{\n\t\"array\": [\n\t\t1,\n\t\t2,\n\t\t3,\n\t\t4\n\t],\n\t\"boolean\": false,\n\t\"null\": null,\n\t\"number\": 42,\n\t\"object\": {},\n\t\"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("indent=x")
|
||||
{
|
||||
CHECK(j.dump().size() == 94);
|
||||
CHECK(j.dump(1).size() == 127);
|
||||
CHECK(j.dump(2).size() == 142);
|
||||
CHECK(j.dump(512).size() == 7792);
|
||||
|
||||
// important test, because it yields a resize of the indent_string
|
||||
// inside the dump() function
|
||||
CHECK(j.dump(1024).size() == 15472);
|
||||
|
||||
const auto binary = json::binary({1, 2, 3}, 128);
|
||||
CHECK(binary.dump(1024).size() == 2086);
|
||||
}
|
||||
|
||||
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("dump with ensure_ascii and non-ASCII characters")
|
||||
{
|
||||
CHECK(json("ä").dump(-1, ' ', true) == "\"\\u00e4\"");
|
||||
CHECK(json("Ö").dump(-1, ' ', true) == "\"\\u00d6\"");
|
||||
CHECK(json("❤️").dump(-1, ' ', true) == "\"\\u2764\\ufe0f\"");
|
||||
}
|
||||
|
||||
SECTION("full Unicode escaping to ASCII")
|
||||
{
|
||||
SECTION("parsing yields the same JSON value")
|
||||
{
|
||||
std::ifstream f_escaped(TEST_DATA_DIRECTORY "/json_nlohmann_tests/all_unicode_ascii.json");
|
||||
std::ifstream f_unescaped(TEST_DATA_DIRECTORY "/json_nlohmann_tests/all_unicode.json");
|
||||
|
||||
json j1 = json::parse(f_escaped);
|
||||
json j2 = json::parse(f_unescaped);
|
||||
CHECK(j1 == j2);
|
||||
}
|
||||
|
||||
SECTION("dumping yields the same JSON text")
|
||||
{
|
||||
std::ifstream f_escaped(TEST_DATA_DIRECTORY "/json_nlohmann_tests/all_unicode_ascii.json");
|
||||
std::ifstream f_unescaped(TEST_DATA_DIRECTORY "/json_nlohmann_tests/all_unicode.json");
|
||||
|
||||
json value = json::parse(f_unescaped);
|
||||
std::string text = value.dump(4, ' ', true);
|
||||
|
||||
std::string expected((std::istreambuf_iterator<char>(f_escaped)),
|
||||
std::istreambuf_iterator<char>());
|
||||
CHECK(text == expected);
|
||||
}
|
||||
}
|
||||
|
||||
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.14159265358979;
|
||||
ss << j_number;
|
||||
|
||||
// check that precision has been overridden during serialization
|
||||
CHECK(ss.str() == "3.14159265358979");
|
||||
|
||||
// check that precision has been restored
|
||||
CHECK(ss.precision() == 3);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("round trips")
|
||||
{
|
||||
for (const auto& s :
|
||||
{"3.141592653589793", "1000000000000000010E5"
|
||||
})
|
||||
{
|
||||
json j1 = json::parse(s);
|
||||
std::string s1 = j1.dump();
|
||||
json j2 = json::parse(s1);
|
||||
std::string s2 = j2.dump();
|
||||
CHECK(s1 == s2);
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
SECTION("binary")
|
||||
{
|
||||
json j = json::binary({});
|
||||
json::value_t t = j;
|
||||
CHECK(t == j.type());
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,884 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
TEST_CASE("iterators 2")
|
||||
{
|
||||
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(!(it1 == it2));
|
||||
CHECK(!(it1 == it3));
|
||||
CHECK(!(it2 == it3));
|
||||
CHECK(it1_c == it1_c);
|
||||
CHECK(!(it1_c == it2_c));
|
||||
CHECK(!(it1_c == it3_c));
|
||||
CHECK(!(it2_c == it3_c));
|
||||
}
|
||||
|
||||
// comparison: not equal
|
||||
{
|
||||
// check definition
|
||||
CHECK( (it1 != it1) == !(it1 == it1) );
|
||||
CHECK( (it1 != it2) == !(it1 == it2) );
|
||||
CHECK( (it1 != it3) == !(it1 == it3) );
|
||||
CHECK( (it2 != it3) == !(it2 == it3) );
|
||||
CHECK( (it1_c != it1_c) == !(it1_c == it1_c) );
|
||||
CHECK( (it1_c != it2_c) == !(it1_c == it2_c) );
|
||||
CHECK( (it1_c != it3_c) == !(it1_c == it3_c) );
|
||||
CHECK( (it2_c != it3_c) == !(it2_c == it3_c) );
|
||||
}
|
||||
|
||||
// comparison: smaller
|
||||
{
|
||||
if (j.type() == json::value_t::object)
|
||||
{
|
||||
#if JSON_DIAGNOSTICS
|
||||
CHECK_THROWS_WITH_AS(it1 < it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 < it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c < it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c < it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
#else
|
||||
CHECK_THROWS_WITH_AS(it1 < it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 < it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c < it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c < it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK(!(it1 < it1));
|
||||
CHECK(it1 < it2);
|
||||
CHECK(it1 < it3);
|
||||
CHECK(it2 < it3);
|
||||
CHECK(!(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)
|
||||
{
|
||||
#if JSON_DIAGNOSTICS
|
||||
CHECK_THROWS_WITH_AS(it1 <= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 <= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c <= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c <= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
#else
|
||||
CHECK_THROWS_WITH_AS(it1 <= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 <= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c <= it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c <= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
#endif
|
||||
}
|
||||
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
|
||||
{
|
||||
if (j.type() == json::value_t::object)
|
||||
{
|
||||
#if JSON_DIAGNOSTICS
|
||||
CHECK_THROWS_WITH_AS(it1 > it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 > it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c > it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c > it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
#else
|
||||
CHECK_THROWS_WITH_AS(it1 > it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 > it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c > it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c > it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
#endif
|
||||
}
|
||||
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)
|
||||
{
|
||||
#if JSON_DIAGNOSTICS
|
||||
CHECK_THROWS_WITH_AS(it1 >= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 >= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c >= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c >= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
#else
|
||||
CHECK_THROWS_WITH_AS(it1 >= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 >= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c >= it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c >= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// check definition
|
||||
CHECK( (it1 >= it1) == !(it1 < it1) );
|
||||
CHECK( (it1 >= it2) == !(it1 < it2) );
|
||||
CHECK( (it1 >= it3) == !(it1 < it3) );
|
||||
CHECK( (it2 >= it3) == !(it2 < it3) );
|
||||
CHECK( (it1_c >= it1_c) == !(it1_c < it1_c) );
|
||||
CHECK( (it1_c >= it2_c) == !(it1_c < it2_c) );
|
||||
CHECK( (it1_c >= it3_c) == !(it1_c < it3_c) );
|
||||
CHECK( (it2_c >= it3_c) == !(it2_c < it3_c) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check exceptions if different objects are compared
|
||||
for (auto j : j_values)
|
||||
{
|
||||
for (auto k : j_values)
|
||||
{
|
||||
if (j != k)
|
||||
{
|
||||
#if JSON_DIAGNOSTICS
|
||||
// the output differs in each loop, so we cannot fix a string for the expected exception
|
||||
#else
|
||||
CHECK_THROWS_WITH_AS(j.begin() == k.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j.cbegin() == k.cbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j.begin() < k.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j.cbegin() < k.cbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_WITH_AS(it += 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.cbegin();
|
||||
CHECK_THROWS_WITH_AS(it += 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.begin();
|
||||
CHECK_THROWS_WITH_AS(it + 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.cbegin();
|
||||
CHECK_THROWS_WITH_AS(it + 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.begin();
|
||||
CHECK_THROWS_WITH_AS(1 + it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.cbegin();
|
||||
CHECK_THROWS_WITH_AS(1 + it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.begin();
|
||||
CHECK_THROWS_WITH_AS(it -= 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.cbegin();
|
||||
CHECK_THROWS_WITH_AS(it -= 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.begin();
|
||||
CHECK_THROWS_WITH_AS(it - 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.cbegin();
|
||||
CHECK_THROWS_WITH_AS(it - 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.begin();
|
||||
CHECK_THROWS_WITH_AS(it - it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.cbegin();
|
||||
CHECK_THROWS_WITH_AS(it - it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("array")
|
||||
{
|
||||
{
|
||||
auto it = j_array.begin();
|
||||
it += 3;
|
||||
CHECK((j_array.begin() + 3) == it);
|
||||
CHECK((3 + j_array.begin()) == 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((3 + j_array.cbegin()) == 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((3 + j_null.begin()) == 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((3 + j_null.cbegin()) == 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((3 + j_value.begin()) == 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((3 + j_value.cbegin()) == 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_WITH_AS(it[0], "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.cbegin();
|
||||
CHECK_THROWS_WITH_AS(it[0], "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.208] cannot use operator[] for object iterators", json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
|
||||
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_WITH_AS(it[0], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_null.cbegin();
|
||||
CHECK_THROWS_WITH_AS(it[0], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("value")
|
||||
{
|
||||
{
|
||||
auto it = j_value.begin();
|
||||
CHECK(it[0] == json(42));
|
||||
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_value.cbegin();
|
||||
CHECK(it[0] == json(42));
|
||||
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(!(it1 == it2));
|
||||
CHECK(!(it1 == it3));
|
||||
CHECK(!(it2 == it3));
|
||||
CHECK(it1_c == it1_c);
|
||||
CHECK(!(it1_c == it2_c));
|
||||
CHECK(!(it1_c == it3_c));
|
||||
CHECK(!(it2_c == it3_c));
|
||||
}
|
||||
|
||||
// comparison: not equal
|
||||
{
|
||||
// check definition
|
||||
CHECK( (it1 != it1) == !(it1 == it1) );
|
||||
CHECK( (it1 != it2) == !(it1 == it2) );
|
||||
CHECK( (it1 != it3) == !(it1 == it3) );
|
||||
CHECK( (it2 != it3) == !(it2 == it3) );
|
||||
CHECK( (it1_c != it1_c) == !(it1_c == it1_c) );
|
||||
CHECK( (it1_c != it2_c) == !(it1_c == it2_c) );
|
||||
CHECK( (it1_c != it3_c) == !(it1_c == it3_c) );
|
||||
CHECK( (it2_c != it3_c) == !(it2_c == it3_c) );
|
||||
}
|
||||
|
||||
// comparison: smaller
|
||||
{
|
||||
if (j.type() == json::value_t::object)
|
||||
{
|
||||
#if JSON_DIAGNOSTICS
|
||||
CHECK_THROWS_WITH_AS(it1 < it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 < it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c < it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c < it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
#else
|
||||
CHECK_THROWS_WITH_AS(it1 < it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 < it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c < it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c < it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK(!(it1 < it1));
|
||||
CHECK(it1 < it2);
|
||||
CHECK(it1 < it3);
|
||||
CHECK(it2 < it3);
|
||||
CHECK(!(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)
|
||||
{
|
||||
#if JSON_DIAGNOSTICS
|
||||
CHECK_THROWS_WITH_AS(it1 <= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 <= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c <= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c <= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
#else
|
||||
CHECK_THROWS_WITH_AS(it1 <= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 <= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c <= it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c <= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
#endif
|
||||
}
|
||||
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
|
||||
{
|
||||
if (j.type() == json::value_t::object)
|
||||
{
|
||||
#if JSON_DIAGNOSTICS
|
||||
CHECK_THROWS_WITH_AS(it1 > it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 > it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c > it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c > it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
#else
|
||||
CHECK_THROWS_WITH_AS(it1 > it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 > it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c > it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c > it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
#endif
|
||||
}
|
||||
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)
|
||||
{
|
||||
#if JSON_DIAGNOSTICS
|
||||
CHECK_THROWS_WITH_AS(it1 >= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 >= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c >= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c >= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators", json::invalid_iterator&);
|
||||
#else
|
||||
CHECK_THROWS_WITH_AS(it1 >= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 >= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c >= it1_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c >= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it2_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it1_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators", json::invalid_iterator&);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// check definition
|
||||
CHECK( (it1 >= it1) == !(it1 < it1) );
|
||||
CHECK( (it1 >= it2) == !(it1 < it2) );
|
||||
CHECK( (it1 >= it3) == !(it1 < it3) );
|
||||
CHECK( (it2 >= it3) == !(it2 < it3) );
|
||||
CHECK( (it1_c >= it1_c) == !(it1_c < it1_c) );
|
||||
CHECK( (it1_c >= it2_c) == !(it1_c < it2_c) );
|
||||
CHECK( (it1_c >= it3_c) == !(it1_c < it3_c) );
|
||||
CHECK( (it2_c >= it3_c) == !(it2_c < it3_c) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check exceptions if different objects are compared
|
||||
for (auto j : j_values)
|
||||
{
|
||||
for (auto k : j_values)
|
||||
{
|
||||
if (j != k)
|
||||
{
|
||||
#if JSON_DIAGNOSTICS
|
||||
// the output differs in each loop, so we cannot fix a string for the expected exception
|
||||
#else
|
||||
CHECK_THROWS_WITH_AS(j.rbegin() == k.rbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j.crbegin() == k.crbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j.rbegin() < k.rbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j.crbegin() < k.crbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator&);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_WITH_AS(it += 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.crbegin();
|
||||
CHECK_THROWS_WITH_AS(it += 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.rbegin();
|
||||
CHECK_THROWS_WITH_AS(it + 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.crbegin();
|
||||
CHECK_THROWS_WITH_AS(it + 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.rbegin();
|
||||
CHECK_THROWS_WITH_AS(1 + it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.crbegin();
|
||||
CHECK_THROWS_WITH_AS(1 + it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.rbegin();
|
||||
CHECK_THROWS_WITH_AS(it -= 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.crbegin();
|
||||
CHECK_THROWS_WITH_AS(it -= 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.rbegin();
|
||||
CHECK_THROWS_WITH_AS(it - 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.crbegin();
|
||||
CHECK_THROWS_WITH_AS(it - 1, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.rbegin();
|
||||
CHECK_THROWS_WITH_AS(it - it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.crbegin();
|
||||
CHECK_THROWS_WITH_AS(it - it, "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("array")
|
||||
{
|
||||
{
|
||||
auto it = j_array.rbegin();
|
||||
it += 3;
|
||||
CHECK((j_array.rbegin() + 3) == it);
|
||||
CHECK(json::reverse_iterator(3 + j_array.rbegin()) == it);
|
||||
CHECK((it - 3) == j_array.rbegin());
|
||||
CHECK((it - j_array.rbegin()) == 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(json::const_reverse_iterator(3 + j_array.crbegin()) == it);
|
||||
CHECK((it - 3) == j_array.crbegin());
|
||||
CHECK((it - j_array.crbegin()) == 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(json::reverse_iterator(3 + j_null.rbegin()) == it);
|
||||
CHECK((it - 3) == j_null.rbegin());
|
||||
CHECK((it - j_null.rbegin()) == 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(json::const_reverse_iterator(3 + j_null.crbegin()) == it);
|
||||
CHECK((it - 3) == j_null.crbegin());
|
||||
CHECK((it - j_null.crbegin()) == 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(json::reverse_iterator(3 + j_value.rbegin()) == it);
|
||||
CHECK((it - 3) == j_value.rbegin());
|
||||
CHECK((it - j_value.rbegin()) == 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(json::const_reverse_iterator(3 + j_value.crbegin()) == it);
|
||||
CHECK((it - 3) == j_value.crbegin());
|
||||
CHECK((it - j_value.crbegin()) == 3);
|
||||
CHECK(it != j_value.crend());
|
||||
it -= 3;
|
||||
CHECK(*it == json(42));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("subscript operator")
|
||||
{
|
||||
SECTION("object")
|
||||
{
|
||||
{
|
||||
auto it = j_object.rbegin();
|
||||
CHECK_THROWS_WITH_AS(it[0], "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_object.crbegin();
|
||||
CHECK_THROWS_WITH_AS(it[0], "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
|
||||
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_WITH_AS(it[0], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_null.crbegin();
|
||||
CHECK_THROWS_WITH_AS(it[0], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("value")
|
||||
{
|
||||
{
|
||||
auto it = j_value.rbegin();
|
||||
CHECK(it[0] == json(42));
|
||||
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
}
|
||||
{
|
||||
auto it = j_value.crbegin();
|
||||
CHECK(it[0] == json(42));
|
||||
CHECK_THROWS_WITH_AS(it[1], "[json.exception.invalid_iterator.214] cannot get value", json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,713 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#define JSON_TESTS_PRIVATE
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
TEST_CASE("JSON pointers")
|
||||
{
|
||||
SECTION("errors")
|
||||
{
|
||||
CHECK_THROWS_WITH_AS(json::json_pointer("foo"),
|
||||
"[json.exception.parse_error.107] parse error at byte 1: JSON pointer must be empty or begin with '/' - was: 'foo'", json::parse_error&);
|
||||
|
||||
CHECK_THROWS_WITH_AS(json::json_pointer("/~~"),
|
||||
"[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'", json::parse_error&);
|
||||
|
||||
CHECK_THROWS_WITH_AS(json::json_pointer("/~"),
|
||||
"[json.exception.parse_error.108] parse error: escape character '~' must be followed with '0' or '1'", json::parse_error&);
|
||||
|
||||
json::json_pointer p;
|
||||
CHECK_THROWS_WITH_AS(p.top(),
|
||||
"[json.exception.out_of_range.405] JSON pointer has no parent", json::out_of_range&);
|
||||
CHECK_THROWS_WITH_AS(p.pop_back(),
|
||||
"[json.exception.out_of_range.405] JSON pointer has no parent", json::out_of_range&);
|
||||
|
||||
SECTION("array index error")
|
||||
{
|
||||
json v = {1, 2, 3, 4};
|
||||
json::json_pointer ptr("/10e");
|
||||
CHECK_THROWS_WITH_AS(v[ptr],
|
||||
"[json.exception.out_of_range.404] unresolved reference token '10e'", json::out_of_range&);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
CHECK(j.contains(json::json_pointer()));
|
||||
CHECK(j.contains(json::json_pointer("")));
|
||||
|
||||
// array access
|
||||
CHECK(j[json::json_pointer("/foo")] == j["foo"]);
|
||||
CHECK(j.contains(json::json_pointer("/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]);
|
||||
CHECK(j.contains(json::json_pointer("/foo/0")));
|
||||
CHECK(j.contains(json::json_pointer("/foo/1")));
|
||||
CHECK(!j.contains(json::json_pointer("/foo/3")));
|
||||
CHECK(!j.contains(json::json_pointer("/foo/+")));
|
||||
CHECK(!j.contains(json::json_pointer("/foo/1+2")));
|
||||
CHECK(!j.contains(json::json_pointer("/foo/-")));
|
||||
|
||||
// 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[""]);
|
||||
CHECK(j.contains(json::json_pointer("")));
|
||||
CHECK(j.contains(json::json_pointer("/")));
|
||||
|
||||
// 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"]);
|
||||
|
||||
// contains
|
||||
CHECK(j.contains(json::json_pointer("/ ")));
|
||||
CHECK(j.contains(json::json_pointer("/c%d")));
|
||||
CHECK(j.contains(json::json_pointer("/e^f")));
|
||||
CHECK(j.contains(json::json_pointer("/g|h")));
|
||||
CHECK(j.contains(json::json_pointer("/i\\j")));
|
||||
CHECK(j.contains(json::json_pointer("/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"]);
|
||||
CHECK(j.contains(json::json_pointer("/a~1b")));
|
||||
CHECK(j.contains(json::json_pointer("/m~0n")));
|
||||
|
||||
// unescaped access
|
||||
// access to nonexisting values yield object creation
|
||||
CHECK(!j.contains(json::json_pointer("/a/b")));
|
||||
CHECK_NOTHROW(j[json::json_pointer("/a/b")] = 42);
|
||||
CHECK(j.contains(json::json_pointer("/a/b")));
|
||||
CHECK(j["a"]["b"] == json(42));
|
||||
|
||||
CHECK(!j.contains(json::json_pointer("/a/c/1")));
|
||||
CHECK_NOTHROW(j[json::json_pointer("/a/c/1")] = 42);
|
||||
CHECK(j["a"]["c"] == json({nullptr, 42}));
|
||||
CHECK(j.contains(json::json_pointer("/a/c/1")));
|
||||
|
||||
CHECK(!j.contains(json::json_pointer("/a/d/-")));
|
||||
CHECK_NOTHROW(j[json::json_pointer("/a/d/-")] = 42);
|
||||
CHECK(!j.contains(json::json_pointer("/a/d/-")));
|
||||
CHECK(j["a"]["d"] == json::array({42}));
|
||||
// "/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_WITH_AS(j_primitive["/foo"_json_pointer],
|
||||
"[json.exception.out_of_range.404] unresolved reference token 'foo'", json::out_of_range&);
|
||||
CHECK_THROWS_WITH_AS(j_primitive.at("/foo"_json_pointer),
|
||||
"[json.exception.out_of_range.404] unresolved reference token 'foo'", json::out_of_range&);
|
||||
CHECK(!j_primitive.contains(json::json_pointer("/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_WITH_AS(j.at(json::json_pointer("/a/b")),
|
||||
"[json.exception.out_of_range.403] key 'a' not found", json::out_of_range&);
|
||||
|
||||
// unresolved access
|
||||
const json j_primitive = 1;
|
||||
CHECK_THROWS_WITH_AS(j_primitive["/foo"_json_pointer],
|
||||
"[json.exception.out_of_range.404] unresolved reference token 'foo'", json::out_of_range&);
|
||||
CHECK_THROWS_WITH_AS(j_primitive.at("/foo"_json_pointer),
|
||||
"[json.exception.out_of_range.404] unresolved reference token 'foo'", json::out_of_range&);
|
||||
}
|
||||
|
||||
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);
|
||||
CHECK(j.contains(""_json_pointer));
|
||||
|
||||
// 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]);
|
||||
CHECK(j.contains("/foo"_json_pointer));
|
||||
CHECK(j.contains("/foo/0"_json_pointer));
|
||||
CHECK(j.contains("/foo/1"_json_pointer));
|
||||
CHECK(!j.contains("/foo/-"_json_pointer));
|
||||
}
|
||||
}
|
||||
|
||||
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_WITH_AS(j["/01"_json_pointer],
|
||||
"[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'", json::parse_error&);
|
||||
CHECK_THROWS_WITH_AS(j_const["/01"_json_pointer],
|
||||
"[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'", json::parse_error&);
|
||||
CHECK_THROWS_WITH_AS(j.at("/01"_json_pointer),
|
||||
"[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'", json::parse_error&);
|
||||
CHECK_THROWS_WITH_AS(j_const.at("/01"_json_pointer),
|
||||
"[json.exception.parse_error.106] parse error: array index '01' must not begin with '0'", json::parse_error&);
|
||||
|
||||
CHECK(!j.contains("/01"_json_pointer));
|
||||
CHECK(!j.contains("/01"_json_pointer));
|
||||
CHECK(!j_const.contains("/01"_json_pointer));
|
||||
CHECK(!j_const.contains("/01"_json_pointer));
|
||||
|
||||
// error with incorrect numbers
|
||||
CHECK_THROWS_WITH_AS(j["/one"_json_pointer] = 1,
|
||||
"[json.exception.parse_error.109] parse error: array index 'one' is not a number", json::parse_error&);
|
||||
CHECK_THROWS_WITH_AS(j_const["/one"_json_pointer] == 1,
|
||||
"[json.exception.parse_error.109] parse error: array index 'one' is not a number", json::parse_error&);
|
||||
|
||||
CHECK_THROWS_WITH_AS(j.at("/one"_json_pointer) = 1,
|
||||
"[json.exception.parse_error.109] parse error: array index 'one' is not a number", json::parse_error&);
|
||||
CHECK_THROWS_WITH_AS(j_const.at("/one"_json_pointer) == 1,
|
||||
"[json.exception.parse_error.109] parse error: array index 'one' is not a number", json::parse_error&);
|
||||
|
||||
CHECK_THROWS_WITH_AS(j["/+1"_json_pointer] = 1,
|
||||
"[json.exception.parse_error.109] parse error: array index '+1' is not a number", json::parse_error&);
|
||||
CHECK_THROWS_WITH_AS(j_const["/+1"_json_pointer] == 1,
|
||||
"[json.exception.parse_error.109] parse error: array index '+1' is not a number", json::parse_error&);
|
||||
|
||||
CHECK_THROWS_WITH_AS(j["/1+1"_json_pointer] = 1,
|
||||
"[json.exception.out_of_range.404] unresolved reference token '1+1'", json::out_of_range&);
|
||||
CHECK_THROWS_WITH_AS(j_const["/1+1"_json_pointer] == 1,
|
||||
"[json.exception.out_of_range.404] unresolved reference token '1+1'", json::out_of_range&);
|
||||
|
||||
{
|
||||
auto too_large_index = std::to_string((std::numeric_limits<unsigned long long>::max)()) + "1";
|
||||
json::json_pointer jp(std::string("/") + too_large_index);
|
||||
std::string throw_msg = std::string("[json.exception.out_of_range.404] unresolved reference token '") + too_large_index + "'";
|
||||
|
||||
CHECK_THROWS_WITH_AS(j[jp] = 1, throw_msg.c_str(), json::out_of_range&);
|
||||
CHECK_THROWS_WITH_AS(j_const[jp] == 1, throw_msg.c_str(), json::out_of_range&);
|
||||
}
|
||||
|
||||
// on some machines, the check below is not constant
|
||||
DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
|
||||
DOCTEST_MSVC_SUPPRESS_WARNING(4127)
|
||||
|
||||
if (sizeof(typename json::size_type) < sizeof(unsigned long long))
|
||||
{
|
||||
auto size_type_max_uul = static_cast<unsigned long long>((std::numeric_limits<json::size_type>::max)());
|
||||
auto too_large_index = std::to_string(size_type_max_uul);
|
||||
json::json_pointer jp(std::string("/") + too_large_index);
|
||||
std::string throw_msg = std::string("[json.exception.out_of_range.410] array index ") + too_large_index + " exceeds size_type";
|
||||
|
||||
CHECK_THROWS_WITH_AS(j[jp] = 1, throw_msg.c_str(), json::out_of_range&);
|
||||
CHECK_THROWS_WITH_AS(j_const[jp] == 1, throw_msg.c_str(), json::out_of_range&);
|
||||
}
|
||||
|
||||
DOCTEST_MSVC_SUPPRESS_WARNING_POP
|
||||
|
||||
CHECK_THROWS_WITH_AS(j.at("/one"_json_pointer) = 1,
|
||||
"[json.exception.parse_error.109] parse error: array index 'one' is not a number", json::parse_error&);
|
||||
CHECK_THROWS_WITH_AS(j_const.at("/one"_json_pointer) == 1,
|
||||
"[json.exception.parse_error.109] parse error: array index 'one' is not a number", json::parse_error&);
|
||||
|
||||
CHECK(!j.contains("/one"_json_pointer));
|
||||
CHECK(!j.contains("/one"_json_pointer));
|
||||
CHECK(!j_const.contains("/one"_json_pointer));
|
||||
CHECK(!j_const.contains("/one"_json_pointer));
|
||||
|
||||
CHECK_THROWS_WITH_AS(json({{"/list/0", 1}, {"/list/1", 2}, {"/list/three", 3}}).unflatten(),
|
||||
"[json.exception.parse_error.109] parse error: array index 'three' is not a number", json::parse_error&);
|
||||
|
||||
// assign to "-"
|
||||
j["/-"_json_pointer] = 99;
|
||||
CHECK(j == json({1, 13, 3, 33, nullptr, 55, 99}));
|
||||
|
||||
// error when using "-" in const object
|
||||
CHECK_THROWS_WITH_AS(j_const["/-"_json_pointer],
|
||||
"[json.exception.out_of_range.402] array index '-' (3) is out of range", json::out_of_range&);
|
||||
CHECK(!j_const.contains("/-"_json_pointer));
|
||||
|
||||
// error when using "-" with at
|
||||
CHECK_THROWS_WITH_AS(j.at("/-"_json_pointer),
|
||||
"[json.exception.out_of_range.402] array index '-' (7) is out of range", json::out_of_range&);
|
||||
CHECK_THROWS_WITH_AS(j_const.at("/-"_json_pointer),
|
||||
"[json.exception.out_of_range.402] array index '-' (3) is out of range", json::out_of_range&);
|
||||
CHECK(!j_const.contains("/-"_json_pointer));
|
||||
}
|
||||
|
||||
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_WITH_AS(j.at("/3"_json_pointer),
|
||||
"[json.exception.out_of_range.401] array index 3 is out of range", json::out_of_range&);
|
||||
CHECK(!j.contains("/3"_json_pointer));
|
||||
|
||||
// assign to nonexisting index (with gap)
|
||||
CHECK_THROWS_WITH_AS(j.at("/5"_json_pointer),
|
||||
"[json.exception.out_of_range.401] array index 5 is out of range", json::out_of_range&);
|
||||
CHECK(!j.contains("/5"_json_pointer));
|
||||
|
||||
// assign to "-"
|
||||
CHECK_THROWS_WITH_AS(j["/-"_json_pointer],
|
||||
"[json.exception.out_of_range.402] array index '-' (3) is out of range", json::out_of_range&);
|
||||
CHECK_THROWS_WITH_AS(j.at("/-"_json_pointer),
|
||||
"[json.exception.out_of_range.402] array index '-' (3) is out of range", json::out_of_range&);
|
||||
CHECK(!j.contains("/-"_json_pointer));
|
||||
}
|
||||
}
|
||||
|
||||
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_WITH_AS(json(1).unflatten(),
|
||||
"[json.exception.type_error.314] only objects can be unflattened", json::type_error&);
|
||||
|
||||
// error for nonprimitve values
|
||||
#if JSON_DIAGNOSTICS
|
||||
CHECK_THROWS_WITH_AS(json({{"/1", {1, 2, 3}}}).unflatten(), "[json.exception.type_error.315] (/~11) values in object must be primitive", json::type_error&);
|
||||
#else
|
||||
CHECK_THROWS_WITH_AS(json({{"/1", {1, 2, 3}}}).unflatten(), "[json.exception.type_error.315] values in object must be primitive", json::type_error&);
|
||||
#endif
|
||||
|
||||
// error for conflicting values
|
||||
json j_error = {{"", 42}, {"/foo", 17}};
|
||||
CHECK_THROWS_WITH_AS(j_error.unflatten(),
|
||||
"[json.exception.type_error.313] invalid value to unflatten", json::type_error&);
|
||||
|
||||
// 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 (const 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);
|
||||
CHECK(std::string(json::json_pointer(ptr)) == ptr);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("conversion")
|
||||
{
|
||||
SECTION("array")
|
||||
{
|
||||
json j;
|
||||
// all numbers -> array
|
||||
j["/12"_json_pointer] = 0;
|
||||
CHECK(j.is_array());
|
||||
}
|
||||
|
||||
SECTION("object")
|
||||
{
|
||||
json j;
|
||||
// contains a number, but is not a number -> object
|
||||
j["/a12"_json_pointer] = 0;
|
||||
CHECK(j.is_object());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("empty, push, pop and parent")
|
||||
{
|
||||
const json j =
|
||||
{
|
||||
{"", "Hello"},
|
||||
{"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"}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// empty json_pointer returns the root JSON-object
|
||||
auto ptr = ""_json_pointer;
|
||||
CHECK(ptr.empty());
|
||||
CHECK(j[ptr] == j);
|
||||
|
||||
// simple field access
|
||||
ptr.push_back("pi");
|
||||
CHECK(!ptr.empty());
|
||||
CHECK(j[ptr] == j["pi"]);
|
||||
|
||||
ptr.pop_back();
|
||||
CHECK(ptr.empty());
|
||||
CHECK(j[ptr] == j);
|
||||
|
||||
// object and children access
|
||||
const std::string answer("answer");
|
||||
ptr.push_back(answer);
|
||||
ptr.push_back("everything");
|
||||
CHECK(!ptr.empty());
|
||||
CHECK(j[ptr] == j["answer"]["everything"]);
|
||||
|
||||
// check access via const pointer
|
||||
const auto cptr = ptr;
|
||||
CHECK(cptr.back() == "everything");
|
||||
|
||||
ptr.pop_back();
|
||||
ptr.pop_back();
|
||||
CHECK(ptr.empty());
|
||||
CHECK(j[ptr] == j);
|
||||
|
||||
// push key which has to be encoded
|
||||
ptr.push_back("object");
|
||||
ptr.push_back("/");
|
||||
CHECK(j[ptr] == j["object"]["/"]);
|
||||
CHECK(ptr.to_string() == "/object/~1");
|
||||
|
||||
CHECK(j[ptr.parent_pointer()] == j["object"]);
|
||||
ptr = ptr.parent_pointer().parent_pointer();
|
||||
CHECK(ptr.empty());
|
||||
CHECK(j[ptr] == j);
|
||||
// parent-pointer of the empty json_pointer is empty
|
||||
ptr = ptr.parent_pointer();
|
||||
CHECK(ptr.empty());
|
||||
CHECK(j[ptr] == j);
|
||||
|
||||
CHECK_THROWS_WITH(ptr.pop_back(),
|
||||
"[json.exception.out_of_range.405] JSON pointer has no parent");
|
||||
}
|
||||
|
||||
SECTION("operators")
|
||||
{
|
||||
const json j =
|
||||
{
|
||||
{"", "Hello"},
|
||||
{"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"}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// empty json_pointer returns the root JSON-object
|
||||
auto ptr = ""_json_pointer;
|
||||
CHECK(j[ptr] == j);
|
||||
|
||||
// simple field access
|
||||
ptr = ptr / "pi";
|
||||
CHECK(j[ptr] == j["pi"]);
|
||||
|
||||
ptr.pop_back();
|
||||
CHECK(j[ptr] == j);
|
||||
|
||||
// object and children access
|
||||
const std::string answer("answer");
|
||||
ptr /= answer;
|
||||
ptr = ptr / "everything";
|
||||
CHECK(j[ptr] == j["answer"]["everything"]);
|
||||
|
||||
ptr.pop_back();
|
||||
ptr.pop_back();
|
||||
CHECK(j[ptr] == j);
|
||||
|
||||
CHECK(ptr / ""_json_pointer == ptr);
|
||||
CHECK(j["/answer"_json_pointer / "/everything"_json_pointer] == j["answer"]["everything"]);
|
||||
|
||||
// list children access
|
||||
CHECK(j["/list"_json_pointer / 1] == j["list"][1]);
|
||||
|
||||
// push key which has to be encoded
|
||||
ptr /= "object";
|
||||
ptr = ptr / "/";
|
||||
CHECK(j[ptr] == j["object"]["/"]);
|
||||
CHECK(ptr.to_string() == "/object/~1");
|
||||
}
|
||||
|
||||
SECTION("equality comparison")
|
||||
{
|
||||
auto ptr1 = json::json_pointer("/foo/bar");
|
||||
auto ptr2 = json::json_pointer("/foo/bar");
|
||||
|
||||
CHECK(ptr1 == ptr2);
|
||||
CHECK_FALSE(ptr1 != ptr2);
|
||||
}
|
||||
|
||||
SECTION("backwards compatibility and mixing")
|
||||
{
|
||||
json j = R"(
|
||||
{
|
||||
"foo": ["bar", "baz"]
|
||||
}
|
||||
)"_json;
|
||||
|
||||
using nlohmann::ordered_json;
|
||||
using json_ptr_str = nlohmann::json_pointer<std::string>;
|
||||
using json_ptr_j = nlohmann::json_pointer<json>;
|
||||
using json_ptr_oj = nlohmann::json_pointer<ordered_json>;
|
||||
|
||||
CHECK(std::is_same<json_ptr_str::string_t, json::json_pointer::string_t>::value);
|
||||
CHECK(std::is_same<json_ptr_str::string_t, ordered_json::json_pointer::string_t>::value);
|
||||
CHECK(std::is_same<json_ptr_str::string_t, json_ptr_j::string_t>::value);
|
||||
CHECK(std::is_same<json_ptr_str::string_t, json_ptr_oj::string_t>::value);
|
||||
|
||||
json_ptr_str ptr{"/foo/0"};
|
||||
json_ptr_j ptr_j{"/foo/0"};
|
||||
json_ptr_oj ptr_oj{"/foo/0"};
|
||||
|
||||
CHECK(j.contains(ptr));
|
||||
CHECK(j.contains(ptr_j));
|
||||
CHECK(j.contains(ptr_oj));
|
||||
|
||||
CHECK(j.at(ptr) == j.at(ptr_j));
|
||||
CHECK(j.at(ptr) == j.at(ptr_oj));
|
||||
|
||||
CHECK(j[ptr] == j[ptr_j]);
|
||||
CHECK(j[ptr] == j[ptr_oj]);
|
||||
|
||||
CHECK(j.value(ptr, "x") == j.value(ptr_j, "x"));
|
||||
CHECK(j.value(ptr, "x") == j.value(ptr_oj, "x"));
|
||||
|
||||
CHECK(ptr == ptr_j);
|
||||
CHECK(ptr == ptr_oj);
|
||||
CHECK_FALSE(ptr != ptr_j);
|
||||
CHECK_FALSE(ptr != ptr_oj);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
TEST_CASE("tests on very large JSONs")
|
||||
{
|
||||
SECTION("issue #1419 - Segmentation fault (stack overflow) due to unbounded recursion")
|
||||
{
|
||||
const auto depth = 5000000;
|
||||
|
||||
std::string s(static_cast<std::size_t>(2 * depth), '[');
|
||||
std::fill(s.begin() + depth, s.end(), ']');
|
||||
|
||||
json _;
|
||||
CHECK_NOTHROW(_ = nlohmann::json::parse(s));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
TEST_CASE("JSON Merge Patch")
|
||||
{
|
||||
SECTION("examples from RFC 7396")
|
||||
{
|
||||
SECTION("Section 1")
|
||||
{
|
||||
json document = R"({
|
||||
"a": "b",
|
||||
"c": {
|
||||
"d": "e",
|
||||
"f": "g"
|
||||
}
|
||||
})"_json;
|
||||
|
||||
json patch = R"({
|
||||
"a": "z",
|
||||
"c": {
|
||||
"f": null
|
||||
}
|
||||
})"_json;
|
||||
|
||||
json expected = R"({
|
||||
"a": "z",
|
||||
"c": {
|
||||
"d": "e"
|
||||
}
|
||||
})"_json;
|
||||
|
||||
document.merge_patch(patch);
|
||||
CHECK(document == expected);
|
||||
}
|
||||
|
||||
SECTION("Section 3")
|
||||
{
|
||||
json document = R"({
|
||||
"title": "Goodbye!",
|
||||
"author": {
|
||||
"givenName": "John",
|
||||
"familyName": "Doe"
|
||||
},
|
||||
"tags": [
|
||||
"example",
|
||||
"sample"
|
||||
],
|
||||
"content": "This will be unchanged"
|
||||
})"_json;
|
||||
|
||||
json patch = R"({
|
||||
"title": "Hello!",
|
||||
"phoneNumber": "+01-123-456-7890",
|
||||
"author": {
|
||||
"familyName": null
|
||||
},
|
||||
"tags": [
|
||||
"example"
|
||||
]
|
||||
})"_json;
|
||||
|
||||
json expected = R"({
|
||||
"title": "Hello!",
|
||||
"author": {
|
||||
"givenName": "John"
|
||||
},
|
||||
"tags": [
|
||||
"example"
|
||||
],
|
||||
"content": "This will be unchanged",
|
||||
"phoneNumber": "+01-123-456-7890"
|
||||
})"_json;
|
||||
|
||||
document.merge_patch(patch);
|
||||
CHECK(document == expected);
|
||||
}
|
||||
|
||||
SECTION("Appendix A")
|
||||
{
|
||||
SECTION("Example 1")
|
||||
{
|
||||
json original = R"({"a":"b"})"_json;
|
||||
json patch = R"({"a":"c"})"_json;
|
||||
json result = R"({"a":"c"})"_json;
|
||||
|
||||
original.merge_patch(patch);
|
||||
CHECK(original == result);
|
||||
}
|
||||
|
||||
SECTION("Example 2")
|
||||
{
|
||||
json original = R"({"a":"b"})"_json;
|
||||
json patch = R"({"b":"c"})"_json;
|
||||
json result = R"({"a":"b", "b":"c"})"_json;
|
||||
|
||||
original.merge_patch(patch);
|
||||
CHECK(original == result);
|
||||
}
|
||||
|
||||
SECTION("Example 3")
|
||||
{
|
||||
json original = R"({"a":"b"})"_json;
|
||||
json patch = R"({"a":null})"_json;
|
||||
json result = R"({})"_json;
|
||||
|
||||
original.merge_patch(patch);
|
||||
CHECK(original == result);
|
||||
}
|
||||
|
||||
SECTION("Example 4")
|
||||
{
|
||||
json original = R"({"a":"b","b":"c"})"_json;
|
||||
json patch = R"({"a":null})"_json;
|
||||
json result = R"({"b":"c"})"_json;
|
||||
|
||||
original.merge_patch(patch);
|
||||
CHECK(original == result);
|
||||
}
|
||||
|
||||
SECTION("Example 5")
|
||||
{
|
||||
json original = R"({"a":["b"]})"_json;
|
||||
json patch = R"({"a":"c"})"_json;
|
||||
json result = R"({"a":"c"})"_json;
|
||||
|
||||
original.merge_patch(patch);
|
||||
CHECK(original == result);
|
||||
}
|
||||
|
||||
SECTION("Example 6")
|
||||
{
|
||||
json original = R"({"a":"c"})"_json;
|
||||
json patch = R"({"a":["b"]})"_json;
|
||||
json result = R"({"a":["b"]})"_json;
|
||||
|
||||
original.merge_patch(patch);
|
||||
CHECK(original == result);
|
||||
}
|
||||
|
||||
SECTION("Example 7")
|
||||
{
|
||||
json original = R"({"a":{"b": "c"}})"_json;
|
||||
json patch = R"({"a":{"b":"d","c":null}})"_json;
|
||||
json result = R"({"a": {"b": "d"}})"_json;
|
||||
|
||||
original.merge_patch(patch);
|
||||
CHECK(original == result);
|
||||
}
|
||||
|
||||
SECTION("Example 8")
|
||||
{
|
||||
json original = R"({"a":[{"b":"c"}]})"_json;
|
||||
json patch = R"({"a":[1]})"_json;
|
||||
json result = R"({"a":[1]})"_json;
|
||||
|
||||
original.merge_patch(patch);
|
||||
CHECK(original == result);
|
||||
}
|
||||
|
||||
SECTION("Example 9")
|
||||
{
|
||||
json original = R"(["a","b"])"_json;
|
||||
json patch = R"(["c","d"])"_json;
|
||||
json result = R"(["c","d"])"_json;
|
||||
|
||||
original.merge_patch(patch);
|
||||
CHECK(original == result);
|
||||
}
|
||||
|
||||
SECTION("Example 10")
|
||||
{
|
||||
json original = R"({"a":"b"})"_json;
|
||||
json patch = R"(["c"])"_json;
|
||||
json result = R"(["c"])"_json;
|
||||
|
||||
original.merge_patch(patch);
|
||||
CHECK(original == result);
|
||||
}
|
||||
|
||||
SECTION("Example 11")
|
||||
{
|
||||
json original = R"({"a":"foo"})"_json;
|
||||
json patch = R"(null)"_json;
|
||||
json result = R"(null)"_json;
|
||||
|
||||
original.merge_patch(patch);
|
||||
CHECK(original == result);
|
||||
}
|
||||
|
||||
SECTION("Example 12")
|
||||
{
|
||||
json original = R"({"a":"foo"})"_json;
|
||||
json patch = R"("bar")"_json;
|
||||
json result = R"("bar")"_json;
|
||||
|
||||
original.merge_patch(patch);
|
||||
CHECK(original == result);
|
||||
}
|
||||
|
||||
SECTION("Example 13")
|
||||
{
|
||||
json original = R"({"e":null})"_json;
|
||||
json patch = R"({"a":1})"_json;
|
||||
json result = R"({"e":null,"a":1})"_json;
|
||||
|
||||
original.merge_patch(patch);
|
||||
CHECK(original == result);
|
||||
}
|
||||
|
||||
SECTION("Example 14")
|
||||
{
|
||||
json original = R"([1,2])"_json;
|
||||
json patch = R"({"a":"b","c":null})"_json;
|
||||
json result = R"({"a":"b"})"_json;
|
||||
|
||||
original.merge_patch(patch);
|
||||
CHECK(original == result);
|
||||
}
|
||||
|
||||
SECTION("Example 15")
|
||||
{
|
||||
json original = R"({})"_json;
|
||||
json patch = R"({"a":{"bb":{"ccc":null}}})"_json;
|
||||
json result = R"({"a":{"bb":{}}})"_json;
|
||||
|
||||
original.merge_patch(patch);
|
||||
CHECK(original == result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
TEST_CASE("version information")
|
||||
{
|
||||
SECTION("meta()")
|
||||
{
|
||||
json j = json::meta();
|
||||
|
||||
CHECK(j["name"] == "JSON for Modern C++");
|
||||
CHECK(j["copyright"] == "(C) 2013-2022 Niels Lohmann");
|
||||
CHECK(j["url"] == "https://github.com/nlohmann/json");
|
||||
CHECK(j["version"] == json(
|
||||
{
|
||||
{"string", "3.10.5"},
|
||||
{"major", 3},
|
||||
{"minor", 10},
|
||||
{"patch", 5}
|
||||
}));
|
||||
|
||||
CHECK(j.find("platform") != j.end());
|
||||
CHECK(j.at("compiler").find("family") != j.at("compiler").end());
|
||||
CHECK(j.at("compiler").find("version") != j.at("compiler").end());
|
||||
CHECK(j.at("compiler").find("c++") != j.at("compiler").end());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,973 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
TEST_CASE("modifiers")
|
||||
{
|
||||
SECTION("clear()")
|
||||
{
|
||||
SECTION("boolean")
|
||||
{
|
||||
json j = true;
|
||||
json k = j;
|
||||
|
||||
j.clear();
|
||||
CHECK(j == json(json::value_t::boolean));
|
||||
CHECK(j == json(k.type()));
|
||||
}
|
||||
|
||||
SECTION("string")
|
||||
{
|
||||
json j = "hello world";
|
||||
json k = j;
|
||||
|
||||
j.clear();
|
||||
CHECK(j == json(json::value_t::string));
|
||||
CHECK(j == json(k.type()));
|
||||
}
|
||||
|
||||
SECTION("array")
|
||||
{
|
||||
SECTION("empty array")
|
||||
{
|
||||
json j = json::array();
|
||||
json k = j;
|
||||
|
||||
j.clear();
|
||||
CHECK(j.empty());
|
||||
CHECK(j == json(json::value_t::array));
|
||||
CHECK(j == json(k.type()));
|
||||
}
|
||||
|
||||
SECTION("filled array")
|
||||
{
|
||||
json j = {1, 2, 3};
|
||||
json k = j;
|
||||
|
||||
j.clear();
|
||||
CHECK(j.empty());
|
||||
CHECK(j == json(json::value_t::array));
|
||||
CHECK(j == json(k.type()));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("object")
|
||||
{
|
||||
SECTION("empty object")
|
||||
{
|
||||
json j = json::object();
|
||||
json k = j;
|
||||
|
||||
j.clear();
|
||||
CHECK(j.empty());
|
||||
CHECK(j == json(json::value_t::object));
|
||||
CHECK(j == json(k.type()));
|
||||
}
|
||||
|
||||
SECTION("filled object")
|
||||
{
|
||||
json j = {{"one", 1}, {"two", 2}, {"three", 3}};
|
||||
json k = j;
|
||||
|
||||
j.clear();
|
||||
CHECK(j.empty());
|
||||
CHECK(j == json(json::value_t::object));
|
||||
CHECK(j == json(k.type()));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("binary")
|
||||
{
|
||||
SECTION("empty binary")
|
||||
{
|
||||
json j = json::binary({});
|
||||
json k = j;
|
||||
|
||||
j.clear();
|
||||
CHECK(!j.empty());
|
||||
CHECK(j == json(json::value_t::binary));
|
||||
CHECK(j == json(k.type()));
|
||||
}
|
||||
|
||||
SECTION("filled binary")
|
||||
{
|
||||
json j = json::binary({1, 2, 3, 4, 5});
|
||||
json k = j;
|
||||
|
||||
j.clear();
|
||||
CHECK(!j.empty());
|
||||
CHECK(j == json(json::value_t::binary));
|
||||
CHECK(j == json(k.type()));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("number (integer)")
|
||||
{
|
||||
json j = 23;
|
||||
json k = j;
|
||||
|
||||
j.clear();
|
||||
CHECK(j == json(json::value_t::number_integer));
|
||||
CHECK(j == json(k.type()));
|
||||
}
|
||||
|
||||
SECTION("number (unsigned)")
|
||||
{
|
||||
json j = 23u;
|
||||
json k = j;
|
||||
|
||||
j.clear();
|
||||
CHECK(j == json(json::value_t::number_integer));
|
||||
CHECK(j == json(k.type()));
|
||||
}
|
||||
|
||||
SECTION("number (float)")
|
||||
{
|
||||
json j = 23.42;
|
||||
json k = j;
|
||||
|
||||
j.clear();
|
||||
CHECK(j == json(json::value_t::number_float));
|
||||
CHECK(j == json(k.type()));
|
||||
}
|
||||
|
||||
SECTION("null")
|
||||
{
|
||||
json j = nullptr;
|
||||
json k = j;
|
||||
|
||||
j.clear();
|
||||
CHECK(j == json(json::value_t::null));
|
||||
CHECK(j == json(k.type()));
|
||||
}
|
||||
}
|
||||
|
||||
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_WITH_AS(j.push_back("Hello"), "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&);
|
||||
}
|
||||
}
|
||||
|
||||
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_WITH_AS(j.push_back(k), "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_WITH_AS(j.push_back(json::object_t::value_type({"one", 1})), "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&);
|
||||
}
|
||||
}
|
||||
|
||||
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"}}));
|
||||
|
||||
// invalid values (no string/val pair)
|
||||
CHECK_THROWS_WITH_AS(j.push_back({1}), "[json.exception.type_error.308] cannot use push_back() with object", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j.push_back({1, 2}), "[json.exception.type_error.308] cannot use push_back() with object", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j.push_back({1, 2, 3, 4}), "[json.exception.type_error.308] cannot use push_back() with object", json::type_error&);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("emplace_back()")
|
||||
{
|
||||
SECTION("to array")
|
||||
{
|
||||
SECTION("null")
|
||||
{
|
||||
json j;
|
||||
auto& x1 = j.emplace_back(1);
|
||||
CHECK(x1 == 1);
|
||||
auto& x2 = j.emplace_back(2);
|
||||
CHECK(x2 == 2);
|
||||
CHECK(j.type() == json::value_t::array);
|
||||
CHECK(j == json({1, 2}));
|
||||
}
|
||||
|
||||
SECTION("array")
|
||||
{
|
||||
json j = {1, 2, 3};
|
||||
auto& x = j.emplace_back("Hello");
|
||||
CHECK(x == "Hello");
|
||||
CHECK(j.type() == json::value_t::array);
|
||||
CHECK(j == json({1, 2, 3, "Hello"}));
|
||||
}
|
||||
|
||||
SECTION("multiple values")
|
||||
{
|
||||
json j;
|
||||
auto& x = j.emplace_back(3, "foo");
|
||||
CHECK(x == json({"foo", "foo", "foo"}));
|
||||
CHECK(j.type() == json::value_t::array);
|
||||
CHECK(j == json({{"foo", "foo", "foo"}}));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("other type")
|
||||
{
|
||||
json j = 1;
|
||||
CHECK_THROWS_WITH_AS(j.emplace_back("Hello"), "[json.exception.type_error.311] cannot use emplace_back() with number", json::type_error&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("emplace()")
|
||||
{
|
||||
SECTION("to object")
|
||||
{
|
||||
SECTION("null")
|
||||
{
|
||||
// start with a null value
|
||||
json j;
|
||||
|
||||
// add a new key
|
||||
auto res1 = j.emplace("foo", "bar");
|
||||
CHECK(res1.second == true);
|
||||
CHECK(*res1.first == "bar");
|
||||
|
||||
// the null value is changed to an object
|
||||
CHECK(j.type() == json::value_t::object);
|
||||
|
||||
// add a new key
|
||||
auto res2 = j.emplace("baz", "bam");
|
||||
CHECK(res2.second == true);
|
||||
CHECK(*res2.first == "bam");
|
||||
|
||||
// we try to insert at given key - no change
|
||||
auto res3 = j.emplace("baz", "bad");
|
||||
CHECK(res3.second == false);
|
||||
CHECK(*res3.first == "bam");
|
||||
|
||||
// the final object
|
||||
CHECK(j == json({{"baz", "bam"}, {"foo", "bar"}}));
|
||||
}
|
||||
|
||||
SECTION("object")
|
||||
{
|
||||
// start with an object
|
||||
json j = {{"foo", "bar"}};
|
||||
|
||||
// add a new key
|
||||
auto res1 = j.emplace("baz", "bam");
|
||||
CHECK(res1.second == true);
|
||||
CHECK(*res1.first == "bam");
|
||||
|
||||
// add an existing key
|
||||
auto res2 = j.emplace("foo", "bad");
|
||||
CHECK(res2.second == false);
|
||||
CHECK(*res2.first == "bar");
|
||||
|
||||
// check final object
|
||||
CHECK(j == json({{"baz", "bam"}, {"foo", "bar"}}));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("other type")
|
||||
{
|
||||
json j = 1;
|
||||
CHECK_THROWS_WITH_AS(j.emplace("foo", "bar"), "[json.exception.type_error.311] cannot use emplace() with number", json::type_error&);
|
||||
}
|
||||
}
|
||||
|
||||
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_WITH_AS(j += "Hello", "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&);
|
||||
}
|
||||
}
|
||||
|
||||
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_WITH_AS(j += k, "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_WITH_AS(j += json::object_t::value_type({"one", 1}), "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&);
|
||||
}
|
||||
}
|
||||
|
||||
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_WITH_AS((k += {1, 2, 3, 4}), "[json.exception.type_error.308] cannot use push_back() with object", json::type_error&);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 it = j_array.insert(j_array.end(), 0, 5);
|
||||
CHECK(j_array.size() == 4);
|
||||
// the returned iterator points to the first inserted element;
|
||||
// there were 4 elements, so it should point to the 5th
|
||||
CHECK(it == j_array.begin() + 4);
|
||||
CHECK(j_array == json({1, 2, 3, 4}));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("range for array")
|
||||
{
|
||||
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_WITH_AS(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), "[json.exception.invalid_iterator.211] passed iterators may not belong to container",
|
||||
json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()), "[json.exception.invalid_iterator.210] iterators do not fit",
|
||||
json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("range for object")
|
||||
{
|
||||
json j_object1 = {{"one", "eins"}, {"two", "zwei"}};
|
||||
json j_object2 = {{"eleven", "elf"}, {"seventeen", "siebzehn"}};
|
||||
|
||||
SECTION("proper usage")
|
||||
{
|
||||
j_object1.insert(j_object2.begin(), j_object2.end());
|
||||
CHECK(j_object1.size() == 4);
|
||||
}
|
||||
|
||||
SECTION("empty range")
|
||||
{
|
||||
j_object1.insert(j_object2.begin(), j_object2.begin());
|
||||
CHECK(j_object1.size() == 2);
|
||||
}
|
||||
|
||||
SECTION("invalid iterators")
|
||||
{
|
||||
json j_other_array2 = {"first", "second"};
|
||||
|
||||
CHECK_THROWS_WITH_AS(j_array.insert(j_object2.begin(), j_object2.end()), "[json.exception.type_error.309] cannot use insert() with array", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j_object1.insert(j_object1.begin(), j_object2.end()), "[json.exception.invalid_iterator.210] iterators do not fit", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j_object1.insert(j_array.begin(), j_array.end()), "[json.exception.invalid_iterator.202] iterators first and last must point to objects", json::invalid_iterator&);
|
||||
}
|
||||
}
|
||||
|
||||
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_WITH_AS(j_array.insert(j_another_array.end(), 10), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), j_value), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), 10, 11), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), j_yet_another_array.end()), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&);
|
||||
}
|
||||
|
||||
SECTION("non-array type")
|
||||
{
|
||||
// call insert on a non-array type
|
||||
json j_nonarray = 3;
|
||||
json j_yet_another_array = {"first", "second"};
|
||||
CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), 10), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), j_value), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), 10, 11), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(), j_yet_another_array.end()), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("update()")
|
||||
{
|
||||
SECTION("non-recursive (default)")
|
||||
{
|
||||
json j_object1 = {{"one", "eins"}, {"two", "zwei"}};
|
||||
json j_object2 = {{"three", "drei"}, {"two", "zwo"}};
|
||||
json j_array = {1, 2, 3, 4};
|
||||
|
||||
SECTION("const reference")
|
||||
{
|
||||
SECTION("proper usage")
|
||||
{
|
||||
j_object1.update(j_object2);
|
||||
CHECK(j_object1 == json({{"one", "eins"}, {"two", "zwo"}, {"three", "drei"}}));
|
||||
|
||||
json j_null;
|
||||
j_null.update(j_object2);
|
||||
CHECK(j_null == j_object2);
|
||||
}
|
||||
|
||||
SECTION("wrong types")
|
||||
{
|
||||
CHECK_THROWS_WITH_AS(j_array.update(j_object1), "[json.exception.type_error.312] cannot use update() with array", json::type_error&);
|
||||
|
||||
CHECK_THROWS_WITH_AS(j_object1.update(j_array), "[json.exception.type_error.312] cannot use update() with array", json::type_error&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("iterator range")
|
||||
{
|
||||
SECTION("proper usage")
|
||||
{
|
||||
j_object1.update(j_object2.begin(), j_object2.end());
|
||||
CHECK(j_object1 == json({{"one", "eins"}, {"two", "zwo"}, {"three", "drei"}}));
|
||||
|
||||
json j_null;
|
||||
j_null.update(j_object2.begin(), j_object2.end());
|
||||
CHECK(j_null == j_object2);
|
||||
}
|
||||
|
||||
SECTION("empty range")
|
||||
{
|
||||
j_object1.update(j_object2.begin(), j_object2.begin());
|
||||
CHECK(j_object1 == json({{"one", "eins"}, {"two", "zwei"}}));
|
||||
}
|
||||
|
||||
SECTION("invalid iterators")
|
||||
{
|
||||
json j_other_array2 = {"first", "second"};
|
||||
|
||||
CHECK_THROWS_WITH_AS(j_array.update(j_object2.begin(), j_object2.end()), "[json.exception.type_error.312] cannot use update() with array", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j_object1.update(j_object1.begin(), j_object2.end()), "[json.exception.invalid_iterator.210] iterators do not fit", json::invalid_iterator&);
|
||||
CHECK_THROWS_WITH_AS(j_object1.update(j_array.begin(), j_array.end()), "[json.exception.type_error.312] cannot use update() with array", json::type_error&);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("recursive")
|
||||
{
|
||||
SECTION("const reference")
|
||||
{
|
||||
SECTION("extend object")
|
||||
{
|
||||
json j1 = {{"string", "s"}, {"numbers", {{"one", 1}}}};
|
||||
json j2 = {{"string", "t"}, {"numbers", {{"two", 2}}}};
|
||||
j1.update(j2, true);
|
||||
CHECK(j1 == json({{"string", "t"}, {"numbers", {{"one", 1}, {"two", 2}}}}));
|
||||
}
|
||||
|
||||
SECTION("replace object")
|
||||
{
|
||||
json j1 = {{"string", "s"}, {"numbers", {{"one", 1}}}};
|
||||
json j2 = {{"string", "t"}, {"numbers", 1}};
|
||||
j1.update(j2, true);
|
||||
CHECK(j1 == json({{"string", "t"}, {"numbers", 1}}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
using std::swap;
|
||||
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_WITH_AS(j.swap(a), "[json.exception.type_error.310] cannot use swap() with number", json::type_error&);
|
||||
}
|
||||
}
|
||||
|
||||
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_WITH_AS(j.swap(o), "[json.exception.type_error.310] cannot use swap() with number", json::type_error&);
|
||||
}
|
||||
}
|
||||
|
||||
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_WITH_AS(j.swap(s), "[json.exception.type_error.310] cannot use swap() with number", json::type_error&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("binary_t")
|
||||
{
|
||||
SECTION("binary_t type")
|
||||
{
|
||||
json j = json::binary({1, 2, 3, 4});
|
||||
json::binary_t s = {{5, 6, 7, 8}};
|
||||
|
||||
j.swap(s);
|
||||
|
||||
CHECK(j == json::binary({5, 6, 7, 8}));
|
||||
|
||||
j.swap(s);
|
||||
|
||||
CHECK(j == json::binary({1, 2, 3, 4}));
|
||||
}
|
||||
|
||||
SECTION("binary_t::container_type type")
|
||||
{
|
||||
json j = json::binary({1, 2, 3, 4});
|
||||
std::vector<std::uint8_t> s = {{5, 6, 7, 8}};
|
||||
|
||||
j.swap(s);
|
||||
|
||||
CHECK(j == json::binary({5, 6, 7, 8}));
|
||||
|
||||
j.swap(s);
|
||||
|
||||
CHECK(j == json::binary({1, 2, 3, 4}));
|
||||
}
|
||||
|
||||
SECTION("non-binary_t type")
|
||||
{
|
||||
json j = 17;
|
||||
json::binary_t s1 = {{1, 2, 3, 4}};
|
||||
std::vector<std::uint8_t> s2 = {{5, 6, 7, 8}};
|
||||
|
||||
CHECK_THROWS_WITH_AS(j.swap(s1), "[json.exception.type_error.310] cannot use swap() with number", json::type_error);
|
||||
CHECK_THROWS_WITH_AS(j.swap(s2), "[json.exception.type_error.310] cannot use swap() with number", json::type_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
// disable -Wnoexcept due to struct pod_bis
|
||||
DOCTEST_GCC_SUPPRESS_WARNING_PUSH
|
||||
DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept")
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using nlohmann::json;
|
||||
|
||||
namespace
|
||||
{
|
||||
enum test
|
||||
{
|
||||
};
|
||||
|
||||
struct pod {};
|
||||
struct pod_bis {};
|
||||
|
||||
void to_json(json& /*unused*/, pod /*unused*/) noexcept;
|
||||
void to_json(json& /*unused*/, pod_bis /*unused*/);
|
||||
void from_json(const json& /*unused*/, pod /*unused*/) noexcept;
|
||||
void from_json(const json& /*unused*/, pod_bis /*unused*/);
|
||||
void to_json(json& /*unused*/, pod /*unused*/) noexcept {}
|
||||
void to_json(json& /*unused*/, pod_bis /*unused*/) {}
|
||||
void from_json(const json& /*unused*/, pod /*unused*/) noexcept {}
|
||||
void from_json(const json& /*unused*/, pod_bis /*unused*/) {}
|
||||
|
||||
json* j = nullptr;
|
||||
|
||||
static_assert(noexcept(json{}), "");
|
||||
static_assert(noexcept(nlohmann::to_json(*j, 2)), "");
|
||||
static_assert(noexcept(nlohmann::to_json(*j, 2.5)), "");
|
||||
static_assert(noexcept(nlohmann::to_json(*j, true)), "");
|
||||
static_assert(noexcept(nlohmann::to_json(*j, test{})), "");
|
||||
static_assert(noexcept(nlohmann::to_json(*j, pod{})), "");
|
||||
static_assert(!noexcept(nlohmann::to_json(*j, pod_bis{})), "");
|
||||
static_assert(noexcept(json(2)), "");
|
||||
static_assert(noexcept(json(test{})), "");
|
||||
static_assert(noexcept(json(pod{})), "");
|
||||
static_assert(noexcept(j->get<pod>()), "");
|
||||
static_assert(!noexcept(j->get<pod_bis>()), "");
|
||||
static_assert(noexcept(json(pod{})), "");
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("runtime checks")
|
||||
{
|
||||
SECTION("nothrow-copy-constructible exceptions")
|
||||
{
|
||||
// for ERR60-CPP (https://github.com/nlohmann/json/issues/531):
|
||||
// Exceptions should be nothrow-copy-constructible. However, compilers
|
||||
// treat std::runtime_exception differently in this regard. Therefore,
|
||||
// we can only demand nothrow-copy-constructibility for our exceptions
|
||||
// if std::runtime_exception is.
|
||||
CHECK(std::is_nothrow_copy_constructible<json::exception>::value == std::is_nothrow_copy_constructible<std::runtime_error>::value);
|
||||
CHECK(std::is_nothrow_copy_constructible<json::parse_error>::value == std::is_nothrow_copy_constructible<std::runtime_error>::value);
|
||||
CHECK(std::is_nothrow_copy_constructible<json::invalid_iterator>::value == std::is_nothrow_copy_constructible<std::runtime_error>::value);
|
||||
CHECK(std::is_nothrow_copy_constructible<json::type_error>::value == std::is_nothrow_copy_constructible<std::runtime_error>::value);
|
||||
CHECK(std::is_nothrow_copy_constructible<json::out_of_range>::value == std::is_nothrow_copy_constructible<std::runtime_error>::value);
|
||||
CHECK(std::is_nothrow_copy_constructible<json::other_error>::value == std::is_nothrow_copy_constructible<std::runtime_error>::value);
|
||||
}
|
||||
|
||||
SECTION("silence -Wunneeded-internal-declaration errors")
|
||||
{
|
||||
j = nullptr;
|
||||
json j2;
|
||||
to_json(j2, pod());
|
||||
to_json(j2, pod_bis());
|
||||
from_json(j2, pod());
|
||||
from_json(j2, pod_bis());
|
||||
}
|
||||
}
|
||||
|
||||
DOCTEST_GCC_SUPPRESS_WARNING_POP
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
using nlohmann::ordered_json;
|
||||
|
||||
|
||||
TEST_CASE("ordered_json")
|
||||
{
|
||||
json j;
|
||||
ordered_json oj;
|
||||
|
||||
j["element3"] = 3;
|
||||
j["element1"] = 1;
|
||||
j["element2"] = 2;
|
||||
|
||||
oj["element3"] = 3;
|
||||
oj["element1"] = 1;
|
||||
oj["element2"] = 2;
|
||||
|
||||
CHECK(j.dump() == "{\"element1\":1,\"element2\":2,\"element3\":3}");
|
||||
CHECK(oj.dump() == "{\"element3\":3,\"element1\":1,\"element2\":2}");
|
||||
|
||||
CHECK(j == json(oj));
|
||||
CHECK(ordered_json(json(oj)) == ordered_json(j));
|
||||
|
||||
j.erase("element1");
|
||||
oj.erase("element1");
|
||||
|
||||
CHECK(j.dump() == "{\"element2\":2,\"element3\":3}");
|
||||
CHECK(oj.dump() == "{\"element3\":3,\"element2\":2}");
|
||||
|
||||
// remove again and nothing changes
|
||||
j.erase("element1");
|
||||
oj.erase("element1");
|
||||
|
||||
CHECK(j.dump() == "{\"element2\":2,\"element3\":3}");
|
||||
CHECK(oj.dump() == "{\"element3\":3,\"element2\":2}");
|
||||
|
||||
// There are no dup keys cause constructor calls emplace...
|
||||
json multi {{"z", 1}, {"m", 2}, {"m", 3}, {"y", 4}, {"m", 5}};
|
||||
CHECK(multi.size() == 3);
|
||||
CHECK(multi.dump() == "{\"m\":2,\"y\":4,\"z\":1}");
|
||||
|
||||
ordered_json multi_ordered {{"z", 1}, {"m", 2}, {"m", 3}, {"y", 4}, {"m", 5}};
|
||||
CHECK(multi_ordered.size() == 3);
|
||||
CHECK(multi_ordered.dump() == "{\"z\":1,\"m\":2,\"y\":4}");
|
||||
CHECK(multi_ordered.erase("m") == 1);
|
||||
CHECK(multi_ordered.dump() == "{\"z\":1,\"y\":4}");
|
||||
|
||||
// Ranged insert test.
|
||||
// It seems that values shouldn't be overwritten. Only new values are added
|
||||
json j1 {{"c", 1}, {"b", 2}, {"a", 3}};
|
||||
const json j2 {{"c", 77}, {"d", 42}, {"a", 4}};
|
||||
j1.insert( j2.cbegin(), j2.cend() );
|
||||
CHECK(j1.size() == 4);
|
||||
CHECK(j1.dump() == "{\"a\":3,\"b\":2,\"c\":1,\"d\":42}");
|
||||
|
||||
ordered_json oj1 {{"c", 1}, {"b", 2}, {"a", 3}};
|
||||
const ordered_json oj2 {{"c", 77}, {"d", 42}, {"a", 4}};
|
||||
oj1.insert( oj2.cbegin(), oj2.cend() );
|
||||
CHECK(oj1.size() == 4);
|
||||
CHECK(oj1.dump() == "{\"c\":1,\"b\":2,\"a\":3,\"d\":42}");
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::ordered_map;
|
||||
|
||||
|
||||
TEST_CASE("ordered_map")
|
||||
{
|
||||
SECTION("constructor")
|
||||
{
|
||||
SECTION("constructor from iterator range")
|
||||
{
|
||||
std::map<std::string, std::string> m {{"eins", "one"}, {"zwei", "two"}, {"drei", "three"}};
|
||||
ordered_map<std::string, std::string> om(m.begin(), m.end());
|
||||
CHECK(om.size() == 3);
|
||||
}
|
||||
|
||||
SECTION("copy assignment")
|
||||
{
|
||||
std::map<std::string, std::string> m {{"eins", "one"}, {"zwei", "two"}, {"drei", "three"}};
|
||||
ordered_map<std::string, std::string> om(m.begin(), m.end());
|
||||
const auto com = om;
|
||||
om.clear(); // silence a warning by forbidding having "const auto& com = om;"
|
||||
CHECK(com.size() == 3);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("at")
|
||||
{
|
||||
std::map<std::string, std::string> m {{"eins", "one"}, {"zwei", "two"}, {"drei", "three"}};
|
||||
ordered_map<std::string, std::string> om(m.begin(), m.end());
|
||||
const auto com = om;
|
||||
|
||||
SECTION("with Key&&")
|
||||
{
|
||||
CHECK(om.at(std::string("eins")) == std::string("one"));
|
||||
CHECK(com.at(std::string("eins")) == std::string("one"));
|
||||
CHECK_THROWS_AS(om.at(std::string("vier")), std::out_of_range);
|
||||
CHECK_THROWS_AS(com.at(std::string("vier")), std::out_of_range);
|
||||
}
|
||||
|
||||
SECTION("with const Key&&")
|
||||
{
|
||||
const std::string eins = "eins";
|
||||
const std::string vier = "vier";
|
||||
CHECK(om.at(eins) == std::string("one"));
|
||||
CHECK(com.at(eins) == std::string("one"));
|
||||
CHECK_THROWS_AS(om.at(vier), std::out_of_range);
|
||||
CHECK_THROWS_AS(com.at(vier), std::out_of_range);
|
||||
}
|
||||
|
||||
SECTION("with string literal")
|
||||
{
|
||||
CHECK(om.at("eins") == std::string("one"));
|
||||
CHECK(com.at("eins") == std::string("one"));
|
||||
CHECK_THROWS_AS(om.at("vier"), std::out_of_range);
|
||||
CHECK_THROWS_AS(com.at("vier"), std::out_of_range);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("operator[]")
|
||||
{
|
||||
std::map<std::string, std::string> m {{"eins", "one"}, {"zwei", "two"}, {"drei", "three"}};
|
||||
ordered_map<std::string, std::string> om(m.begin(), m.end());
|
||||
const auto com = om;
|
||||
|
||||
SECTION("with Key&&")
|
||||
{
|
||||
CHECK(om[std::string("eins")] == std::string("one"));
|
||||
CHECK(com[std::string("eins")] == std::string("one"));
|
||||
|
||||
CHECK(om[std::string("vier")] == std::string(""));
|
||||
CHECK(om.size() == 4);
|
||||
}
|
||||
|
||||
SECTION("with const Key&&")
|
||||
{
|
||||
const std::string eins = "eins";
|
||||
const std::string vier = "vier";
|
||||
|
||||
CHECK(om[eins] == std::string("one"));
|
||||
CHECK(com[eins] == std::string("one"));
|
||||
|
||||
CHECK(om[vier] == std::string(""));
|
||||
CHECK(om.size() == 4);
|
||||
}
|
||||
|
||||
SECTION("with string literal")
|
||||
{
|
||||
CHECK(om["eins"] == std::string("one"));
|
||||
CHECK(com["eins"] == std::string("one"));
|
||||
|
||||
CHECK(om["vier"] == std::string(""));
|
||||
CHECK(om.size() == 4);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("erase")
|
||||
{
|
||||
ordered_map<std::string, std::string> om;
|
||||
om["eins"] = "one";
|
||||
om["zwei"] = "two";
|
||||
om["drei"] = "three";
|
||||
|
||||
{
|
||||
auto it = om.begin();
|
||||
CHECK(it->first == "eins");
|
||||
++it;
|
||||
CHECK(it->first == "zwei");
|
||||
++it;
|
||||
CHECK(it->first == "drei");
|
||||
++it;
|
||||
CHECK(it == om.end());
|
||||
}
|
||||
|
||||
SECTION("with Key&&")
|
||||
{
|
||||
CHECK(om.size() == 3);
|
||||
CHECK(om.erase(std::string("eins")) == 1);
|
||||
CHECK(om.size() == 2);
|
||||
CHECK(om.erase(std::string("vier")) == 0);
|
||||
CHECK(om.size() == 2);
|
||||
|
||||
auto it = om.begin();
|
||||
CHECK(it->first == "zwei");
|
||||
++it;
|
||||
CHECK(it->first == "drei");
|
||||
++it;
|
||||
CHECK(it == om.end());
|
||||
}
|
||||
|
||||
SECTION("with const Key&&")
|
||||
{
|
||||
const std::string eins = "eins";
|
||||
const std::string vier = "vier";
|
||||
CHECK(om.size() == 3);
|
||||
CHECK(om.erase(eins) == 1);
|
||||
CHECK(om.size() == 2);
|
||||
CHECK(om.erase(vier) == 0);
|
||||
CHECK(om.size() == 2);
|
||||
|
||||
auto it = om.begin();
|
||||
CHECK(it->first == "zwei");
|
||||
++it;
|
||||
CHECK(it->first == "drei");
|
||||
++it;
|
||||
CHECK(it == om.end());
|
||||
}
|
||||
|
||||
SECTION("with string literal")
|
||||
{
|
||||
CHECK(om.size() == 3);
|
||||
CHECK(om.erase("eins") == 1);
|
||||
CHECK(om.size() == 2);
|
||||
CHECK(om.erase("vier") == 0);
|
||||
CHECK(om.size() == 2);
|
||||
|
||||
auto it = om.begin();
|
||||
CHECK(it->first == "zwei");
|
||||
++it;
|
||||
CHECK(it->first == "drei");
|
||||
++it;
|
||||
CHECK(it == om.end());
|
||||
}
|
||||
|
||||
SECTION("with iterator")
|
||||
{
|
||||
CHECK(om.size() == 3);
|
||||
CHECK(om.begin()->first == "eins");
|
||||
CHECK(std::next(om.begin(), 1)->first == "zwei");
|
||||
CHECK(std::next(om.begin(), 2)->first == "drei");
|
||||
|
||||
auto it = om.erase(om.begin());
|
||||
CHECK(it->first == "zwei");
|
||||
CHECK(om.size() == 2);
|
||||
|
||||
auto it2 = om.begin();
|
||||
CHECK(it2->first == "zwei");
|
||||
++it2;
|
||||
CHECK(it2->first == "drei");
|
||||
++it2;
|
||||
CHECK(it2 == om.end());
|
||||
}
|
||||
|
||||
SECTION("with iterator pair")
|
||||
{
|
||||
SECTION("range in the middle")
|
||||
{
|
||||
// need more elements
|
||||
om["vier"] = "four";
|
||||
om["fünf"] = "five";
|
||||
|
||||
// delete "zwei" and "drei"
|
||||
auto it = om.erase(om.begin() + 1, om.begin() + 3);
|
||||
CHECK(it->first == "vier");
|
||||
CHECK(om.size() == 3);
|
||||
}
|
||||
|
||||
SECTION("range at the beginning")
|
||||
{
|
||||
// need more elements
|
||||
om["vier"] = "four";
|
||||
om["fünf"] = "five";
|
||||
|
||||
// delete "eins" and "zwei"
|
||||
auto it = om.erase(om.begin(), om.begin() + 2);
|
||||
CHECK(it->first == "drei");
|
||||
CHECK(om.size() == 3);
|
||||
}
|
||||
|
||||
SECTION("range at the end")
|
||||
{
|
||||
// need more elements
|
||||
om["vier"] = "four";
|
||||
om["fünf"] = "five";
|
||||
|
||||
// delete "vier" and "fünf"
|
||||
auto it = om.erase(om.begin() + 3, om.end());
|
||||
CHECK(it == om.end());
|
||||
CHECK(om.size() == 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("count")
|
||||
{
|
||||
ordered_map<std::string, std::string> om;
|
||||
om["eins"] = "one";
|
||||
om["zwei"] = "two";
|
||||
om["drei"] = "three";
|
||||
|
||||
const std::string eins("eins");
|
||||
const std::string vier("vier");
|
||||
CHECK(om.count("eins") == 1);
|
||||
CHECK(om.count(std::string("eins")) == 1);
|
||||
CHECK(om.count(eins) == 1);
|
||||
CHECK(om.count("vier") == 0);
|
||||
CHECK(om.count(std::string("vier")) == 0);
|
||||
CHECK(om.count(vier) == 0);
|
||||
}
|
||||
|
||||
SECTION("find")
|
||||
{
|
||||
ordered_map<std::string, std::string> om;
|
||||
om["eins"] = "one";
|
||||
om["zwei"] = "two";
|
||||
om["drei"] = "three";
|
||||
const auto com = om;
|
||||
|
||||
const std::string eins("eins");
|
||||
const std::string vier("vier");
|
||||
CHECK(om.find("eins") == om.begin());
|
||||
CHECK(om.find(std::string("eins")) == om.begin());
|
||||
CHECK(om.find(eins) == om.begin());
|
||||
CHECK(om.find("vier") == om.end());
|
||||
CHECK(om.find(std::string("vier")) == om.end());
|
||||
CHECK(om.find(vier) == om.end());
|
||||
|
||||
CHECK(com.find("eins") == com.begin());
|
||||
CHECK(com.find(std::string("eins")) == com.begin());
|
||||
CHECK(com.find(eins) == com.begin());
|
||||
CHECK(com.find("vier") == com.end());
|
||||
CHECK(com.find(std::string("vier")) == com.end());
|
||||
CHECK(com.find(vier) == com.end());
|
||||
}
|
||||
|
||||
SECTION("insert")
|
||||
{
|
||||
ordered_map<std::string, std::string> om;
|
||||
om["eins"] = "one";
|
||||
om["zwei"] = "two";
|
||||
om["drei"] = "three";
|
||||
|
||||
SECTION("const value_type&")
|
||||
{
|
||||
ordered_map<std::string, std::string>::value_type vt1 {"eins", "1"};
|
||||
ordered_map<std::string, std::string>::value_type vt4 {"vier", "four"};
|
||||
|
||||
auto res1 = om.insert(vt1);
|
||||
CHECK(res1.first == om.begin());
|
||||
CHECK(res1.second == false);
|
||||
CHECK(om.size() == 3);
|
||||
|
||||
auto res4 = om.insert(vt4);
|
||||
CHECK(res4.first == om.begin() + 3);
|
||||
CHECK(res4.second == true);
|
||||
CHECK(om.size() == 4);
|
||||
}
|
||||
|
||||
SECTION("value_type&&")
|
||||
{
|
||||
auto res1 = om.insert({"eins", "1"});
|
||||
CHECK(res1.first == om.begin());
|
||||
CHECK(res1.second == false);
|
||||
CHECK(om.size() == 3);
|
||||
|
||||
auto res4 = om.insert({"vier", "four"});
|
||||
CHECK(res4.first == om.begin() + 3);
|
||||
CHECK(res4.second == true);
|
||||
CHECK(om.size() == 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,500 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
TEST_CASE("pointer access")
|
||||
{
|
||||
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(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(p3 == 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);
|
||||
CHECK(value.get_ptr<json::binary_t*>() == nullptr);
|
||||
}
|
||||
|
||||
SECTION("pointer access to const object_t")
|
||||
{
|
||||
using test_type = const json::object_t;
|
||||
const 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(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(p3 == value.get_ptr<const test_type* const>());
|
||||
CHECK(*p3 == value.get<test_type>());
|
||||
|
||||
// check if null pointers are returned correctly
|
||||
CHECK(value.get_ptr<const json::object_t*>() != nullptr);
|
||||
CHECK(value.get_ptr<const json::array_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::string_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::boolean_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_integer_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_unsigned_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_float_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::binary_t*>() == nullptr);
|
||||
}
|
||||
|
||||
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(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(p3 == 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);
|
||||
CHECK(value.get_ptr<json::binary_t*>() == nullptr);
|
||||
}
|
||||
|
||||
SECTION("pointer access to const array_t")
|
||||
{
|
||||
using test_type = const json::array_t;
|
||||
const 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(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(p3 == value.get_ptr<const test_type* const>());
|
||||
CHECK(*p3 == value.get<test_type>());
|
||||
|
||||
// check if null pointers are returned correctly
|
||||
CHECK(value.get_ptr<const json::object_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::array_t*>() != nullptr);
|
||||
CHECK(value.get_ptr<const json::string_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::boolean_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_integer_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_unsigned_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_float_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::binary_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(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(p3 == 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);
|
||||
CHECK(value.get_ptr<json::binary_t*>() == nullptr);
|
||||
}
|
||||
|
||||
SECTION("pointer access to const string_t")
|
||||
{
|
||||
using test_type = const json::string_t;
|
||||
const 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(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(p3 == value.get_ptr<const test_type* const>());
|
||||
CHECK(*p3 == value.get<test_type>());
|
||||
|
||||
// check if null pointers are returned correctly
|
||||
CHECK(value.get_ptr<const json::object_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::array_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::string_t*>() != nullptr);
|
||||
CHECK(value.get_ptr<const json::boolean_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_integer_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_unsigned_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_float_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::binary_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(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(p3 == 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);
|
||||
CHECK(value.get_ptr<json::binary_t*>() == nullptr);
|
||||
}
|
||||
|
||||
SECTION("pointer access to const boolean_t")
|
||||
{
|
||||
using test_type = const json::boolean_t;
|
||||
const 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(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(p3 == value.get_ptr<const test_type* const>());
|
||||
CHECK(*p3 == value.get<test_type>());
|
||||
|
||||
// check if null pointers are returned correctly
|
||||
CHECK(value.get_ptr<const json::object_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::array_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::string_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::boolean_t*>() != nullptr);
|
||||
CHECK(value.get_ptr<const json::number_integer_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_unsigned_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_float_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::binary_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(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(p3 == 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);
|
||||
CHECK(value.get_ptr<json::binary_t*>() == nullptr);
|
||||
}
|
||||
|
||||
SECTION("pointer access to const number_integer_t")
|
||||
{
|
||||
using test_type = const json::number_integer_t;
|
||||
const 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(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(p3 == value.get_ptr<const test_type* const>());
|
||||
CHECK(*p3 == value.get<test_type>());
|
||||
|
||||
// check if null pointers are returned correctly
|
||||
CHECK(value.get_ptr<const json::object_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::array_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::string_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::boolean_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_integer_t*>() != nullptr);
|
||||
CHECK(value.get_ptr<const json::number_unsigned_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_float_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::binary_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(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(p3 == 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);
|
||||
CHECK(value.get_ptr<json::binary_t*>() == nullptr);
|
||||
}
|
||||
|
||||
SECTION("pointer access to const number_unsigned_t")
|
||||
{
|
||||
using test_type = const json::number_unsigned_t;
|
||||
const 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(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(p3 == value.get_ptr<const test_type* const>());
|
||||
CHECK(*p3 == value.get<test_type>());
|
||||
|
||||
// check if null pointers are returned correctly
|
||||
CHECK(value.get_ptr<const json::object_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::array_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::string_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::boolean_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_integer_t*>() != nullptr);
|
||||
CHECK(value.get_ptr<const json::number_unsigned_t*>() != nullptr);
|
||||
CHECK(value.get_ptr<const json::number_float_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::binary_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(p2 == 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(p3 == 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);
|
||||
CHECK(value.get_ptr<json::binary_t*>() == nullptr);
|
||||
}
|
||||
|
||||
SECTION("pointer access to const number_float_t")
|
||||
{
|
||||
using test_type = const json::number_float_t;
|
||||
const 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(p2 == 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(p3 == 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<const json::object_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::array_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::string_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::boolean_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_integer_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_unsigned_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_float_t*>() != nullptr);
|
||||
CHECK(value.get_ptr<const json::binary_t*>() == nullptr);
|
||||
}
|
||||
|
||||
SECTION("pointer access to const binary_t")
|
||||
{
|
||||
using test_type = const json::binary_t;
|
||||
const json value = json::binary({1, 2, 3});
|
||||
|
||||
// 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(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(p3 == value.get_ptr<const test_type* const>());
|
||||
CHECK(*p3 == value.get<test_type>());
|
||||
|
||||
// check if null pointers are returned correctly
|
||||
CHECK(value.get_ptr<const json::object_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::array_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::string_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::boolean_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_integer_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_unsigned_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_float_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::binary_t*>() != nullptr);
|
||||
}
|
||||
|
||||
SECTION("pointer access to const binary_t")
|
||||
{
|
||||
using test_type = const json::binary_t;
|
||||
const json value = json::binary({});
|
||||
|
||||
// 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(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(p3 == value.get_ptr<const test_type* const>());
|
||||
CHECK(*p3 == value.get<test_type>());
|
||||
|
||||
// check if null pointers are returned correctly
|
||||
CHECK(value.get_ptr<const json::object_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::array_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::string_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::boolean_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_integer_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_unsigned_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::number_float_t*>() == nullptr);
|
||||
CHECK(value.get_ptr<const json::binary_t*>() != nullptr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <deque>
|
||||
#include <forward_list>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
// local variable is initialized but not referenced
|
||||
DOCTEST_MSVC_SUPPRESS_WARNING_PUSH
|
||||
DOCTEST_MSVC_SUPPRESS_WARNING(4189)
|
||||
|
||||
TEST_CASE("README" * doctest::skip())
|
||||
{
|
||||
{
|
||||
// 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 = {{}};
|
||||
CHECK(empty_array_implicit.is_array());
|
||||
json empty_array_explicit = json::array();
|
||||
CHECK(empty_array_explicit.is_array());
|
||||
|
||||
// a way to express the empty object {}
|
||||
json empty_object_explicit = json::object();
|
||||
CHECK(empty_object_explicit.is_object());
|
||||
|
||||
// a way to express an _array_ of key/value pairs [["currency", "USD"], ["value", 42.99]]
|
||||
json array_not_object = json::array({ {"currency", "USD"}, {"value", 42.99} });
|
||||
CHECK(array_not_object.is_array());
|
||||
CHECK(array_not_object.size() == 2);
|
||||
CHECK(array_not_object[0].is_array());
|
||||
CHECK(array_not_object[1].is_array());
|
||||
}
|
||||
|
||||
{
|
||||
// create object from string literal
|
||||
json j = "{ \"happy\": true, \"pi\": 3.141 }"_json; // NOLINT(modernize-raw-string-literal)
|
||||
|
||||
// or even nicer with a raw string literal
|
||||
auto j2 = R"(
|
||||
{
|
||||
"happy": true,
|
||||
"pi": 3.141
|
||||
}
|
||||
)"_json;
|
||||
|
||||
// or explicitly
|
||||
auto j3 = json::parse(R"({"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);
|
||||
|
||||
// comparison
|
||||
bool x = (j == R"(["foo", 1, true])"_json); // true
|
||||
CHECK(x == true);
|
||||
|
||||
// iterate the array
|
||||
for (json::iterator it = j.begin(); it != j.end(); ++it) // NOLINT(modernize-loop-convert)
|
||||
{
|
||||
std::cout << *it << '\n';
|
||||
}
|
||||
|
||||
// range-based for
|
||||
for (auto& element : j)
|
||||
{
|
||||
std::cout << element << '\n';
|
||||
}
|
||||
|
||||
// getter/setter
|
||||
const auto tmp = j[0].get<std::string>();
|
||||
j[1] = 42;
|
||||
bool foo{j.at(2)};
|
||||
CHECK(foo == true);
|
||||
|
||||
// other stuff
|
||||
j.size(); // 3 entries
|
||||
j.empty(); // false
|
||||
j.type(); // json::value_t::array
|
||||
j.clear(); // the array is empty again
|
||||
|
||||
// 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); // both entries for "one" are used
|
||||
// maybe ["one", "two", "one", "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;
|
||||
auto s2 = js.get<std::string>();
|
||||
|
||||
// Booleans
|
||||
bool b1 = true;
|
||||
json jb = b1;
|
||||
bool b2{jb};
|
||||
CHECK(b2 == true);
|
||||
|
||||
// numbers
|
||||
int i = 42;
|
||||
json jn = i;
|
||||
double f{jn};
|
||||
CHECK(f == 42);
|
||||
|
||||
// etc.
|
||||
|
||||
std::string vs = js.get<std::string>();
|
||||
bool vb = jb.get<bool>();
|
||||
CHECK(vb == true);
|
||||
int vi = jn.get<int>();
|
||||
CHECK(vi == 42);
|
||||
|
||||
// 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
|
||||
auto res = 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);
|
||||
}
|
||||
}
|
||||
|
||||
DOCTEST_MSVC_SUPPRESS_WARNING_POP
|
||||
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
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
|
||||
auto& p1 = value.get_ref<test_type&>();
|
||||
CHECK(&p1 == value.get_ptr<test_type*>());
|
||||
CHECK(p1 == value.get<test_type>());
|
||||
|
||||
const auto& 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_WITH_AS(value.get_ref<json::array_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::string_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::boolean_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::number_integer_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::number_unsigned_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::number_float_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is object", json::type_error&);
|
||||
}
|
||||
|
||||
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 auto& 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
|
||||
auto& p1 = value.get_ref<test_type&>();
|
||||
CHECK(&p1 == value.get_ptr<test_type*>());
|
||||
CHECK(p1 == value.get<test_type>());
|
||||
|
||||
const auto& 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_WITH_AS(value.get_ref<json::object_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array", json::type_error&);
|
||||
CHECK_NOTHROW(value.get_ref<json::array_t&>());
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::string_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::boolean_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::number_integer_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::number_unsigned_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::number_float_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is array", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("reference access to string_t")
|
||||
{
|
||||
using test_type = json::string_t;
|
||||
json value = "hello";
|
||||
|
||||
// check if references are returned correctly
|
||||
auto& p1 = value.get_ref<test_type&>();
|
||||
CHECK(&p1 == value.get_ptr<test_type*>());
|
||||
CHECK(p1 == value.get<test_type>());
|
||||
|
||||
const auto& 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_WITH_AS(value.get_ref<json::object_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::array_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string", json::type_error&);
|
||||
CHECK_NOTHROW(value.get_ref<json::string_t&>());
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::boolean_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::number_integer_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::number_unsigned_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::number_float_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is string", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("reference access to boolean_t")
|
||||
{
|
||||
using test_type = json::boolean_t;
|
||||
json value = false;
|
||||
|
||||
// check if references are returned correctly
|
||||
auto& p1 = value.get_ref<test_type&>();
|
||||
CHECK(&p1 == value.get_ptr<test_type*>());
|
||||
CHECK(p1 == value.get<test_type>());
|
||||
|
||||
const auto& 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_WITH_AS(value.get_ref<json::object_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::array_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::string_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean", json::type_error&);
|
||||
CHECK_NOTHROW(value.get_ref<json::boolean_t&>());
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::number_integer_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::number_unsigned_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::number_float_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is boolean", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("reference access to number_integer_t")
|
||||
{
|
||||
using test_type = json::number_integer_t;
|
||||
json value = -23;
|
||||
|
||||
// check if references are returned correctly
|
||||
auto& p1 = value.get_ref<test_type&>();
|
||||
CHECK(&p1 == value.get_ptr<test_type*>());
|
||||
CHECK(p1 == value.get<test_type>());
|
||||
|
||||
const auto& 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_WITH_AS(value.get_ref<json::object_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::array_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::string_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::boolean_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
|
||||
CHECK_NOTHROW(value.get_ref<json::number_integer_t&>());
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::number_unsigned_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::number_float_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("reference access to number_unsigned_t")
|
||||
{
|
||||
using test_type = json::number_unsigned_t;
|
||||
json value = 23u;
|
||||
|
||||
// check if references are returned correctly
|
||||
auto& p1 = value.get_ref<test_type&>();
|
||||
CHECK(&p1 == value.get_ptr<test_type*>());
|
||||
CHECK(p1 == value.get<test_type>());
|
||||
|
||||
const auto& 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_WITH_AS(value.get_ref<json::object_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::array_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::string_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::boolean_t&>(),
|
||||
"[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
|
||||
//CHECK_THROWS_WITH_AS(value.get_ref<json::number_integer_t&>(),
|
||||
// "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
|
||||
CHECK_NOTHROW(value.get_ref<json::number_unsigned_t&>());
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::number_float_t&>(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
|
||||
}
|
||||
|
||||
SECTION("reference access to number_float_t")
|
||||
{
|
||||
using test_type = json::number_float_t;
|
||||
json value = 42.23;
|
||||
|
||||
// check if references are returned correctly
|
||||
auto& p1 = value.get_ref<test_type&>();
|
||||
CHECK(&p1 == value.get_ptr<test_type*>());
|
||||
CHECK(p1 == value.get<test_type>());
|
||||
|
||||
const auto& 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_WITH_AS(value.get_ref<json::object_t&>(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::array_t&>(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::string_t&>(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::boolean_t&>(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::number_integer_t&>(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(value.get_ref<json::number_unsigned_t&>(), "[json.exception.type_error.303] incompatible ReferenceType for get_ref, actual type is number", json::type_error&);
|
||||
CHECK_NOTHROW(value.get_ref<json::number_float_t&>());
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,878 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
// for some reason including this after the json header leads to linker errors with VS 2017...
|
||||
#include <locale>
|
||||
|
||||
#define JSON_TESTS_PRIVATE
|
||||
#include <nlohmann/json.hpp>
|
||||
using json = nlohmann::json;
|
||||
using ordered_json = nlohmann::ordered_json;
|
||||
|
||||
#include <cstdio>
|
||||
#include <list>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#ifdef JSON_HAS_CPP_17
|
||||
#include <any>
|
||||
#include <variant>
|
||||
#endif
|
||||
|
||||
#if JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
||||
// JSON_HAS_CPP_17 (magic keyword; do not remove)
|
||||
#include <experimental/filesystem>
|
||||
namespace nlohmann::detail
|
||||
{
|
||||
namespace std_fs = std::experimental::filesystem;
|
||||
} // namespace nlohmann::detail
|
||||
#elif JSON_HAS_FILESYSTEM
|
||||
#include <filesystem>
|
||||
namespace nlohmann::detail
|
||||
{
|
||||
namespace std_fs = std::filesystem;
|
||||
} // namespace nlohmann::detail
|
||||
#endif
|
||||
|
||||
#ifdef JSON_HAS_CPP_20
|
||||
#include <span>
|
||||
#endif
|
||||
|
||||
// NLOHMANN_JSON_SERIALIZE_ENUM uses a static std::pair
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// for #1021
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
using float_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, float>;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// for #1647
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
namespace
|
||||
{
|
||||
struct NonDefaultFromJsonStruct
|
||||
{};
|
||||
|
||||
inline bool operator==(NonDefaultFromJsonStruct const& /*unused*/, NonDefaultFromJsonStruct const& /*unused*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
enum class for_1647
|
||||
{
|
||||
one,
|
||||
two
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays): this is a false positive
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(for_1647,
|
||||
{
|
||||
{for_1647::one, "one"},
|
||||
{for_1647::two, "two"},
|
||||
})
|
||||
} // namespace
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// for #1299
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct Data
|
||||
{
|
||||
Data() = default;
|
||||
Data(std::string a_, std::string b_)
|
||||
: a(std::move(a_))
|
||||
, b(std::move(b_))
|
||||
{}
|
||||
std::string a{};
|
||||
std::string b{};
|
||||
};
|
||||
|
||||
void from_json(const json& j, Data& data);
|
||||
void from_json(const json& j, Data& data)
|
||||
{
|
||||
j["a"].get_to(data.a);
|
||||
j["b"].get_to(data.b);
|
||||
}
|
||||
|
||||
bool operator==(Data const& lhs, Data const& rhs);
|
||||
bool operator==(Data const& lhs, Data const& rhs)
|
||||
{
|
||||
return lhs.a == rhs.a && lhs.b == rhs.b;
|
||||
}
|
||||
|
||||
//bool operator!=(Data const& lhs, Data const& rhs)
|
||||
//{
|
||||
// return !(lhs == rhs);
|
||||
//}
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
template<>
|
||||
struct adl_serializer<NonDefaultFromJsonStruct>
|
||||
{
|
||||
static NonDefaultFromJsonStruct from_json(json const& /*unused*/) noexcept
|
||||
{
|
||||
return {};
|
||||
}
|
||||
};
|
||||
} // namespace nlohmann
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// for #1805
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct NotSerializableData
|
||||
{
|
||||
int mydata;
|
||||
float myfloat;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// for #2574
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct NonDefaultConstructible
|
||||
{
|
||||
explicit NonDefaultConstructible(int a)
|
||||
: x(a)
|
||||
{}
|
||||
int x;
|
||||
};
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
template<>
|
||||
struct adl_serializer<NonDefaultConstructible>
|
||||
{
|
||||
static NonDefaultConstructible from_json(json const& j)
|
||||
{
|
||||
return NonDefaultConstructible(j.get<int>());
|
||||
}
|
||||
};
|
||||
} // namespace nlohmann
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// for #2824
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
class sax_no_exception : public nlohmann::detail::json_sax_dom_parser<json>
|
||||
{
|
||||
public:
|
||||
explicit sax_no_exception(json& j)
|
||||
: nlohmann::detail::json_sax_dom_parser<json>(j, false)
|
||||
{}
|
||||
|
||||
static bool parse_error(std::size_t /*position*/, const std::string& /*last_token*/, const json::exception& ex)
|
||||
{
|
||||
error_string = new std::string(ex.what()); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::string* error_string;
|
||||
};
|
||||
|
||||
std::string* sax_no_exception::error_string = nullptr;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// for #2982
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<class T>
|
||||
class my_allocator : public std::allocator<T>
|
||||
{
|
||||
public:
|
||||
using std::allocator<T>::allocator;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// for #3077
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
class FooAlloc
|
||||
{};
|
||||
|
||||
class Foo
|
||||
{
|
||||
public:
|
||||
explicit Foo(const FooAlloc& /* unused */ = FooAlloc()) {}
|
||||
|
||||
bool value = false;
|
||||
};
|
||||
|
||||
class FooBar
|
||||
{
|
||||
public:
|
||||
Foo foo{};
|
||||
};
|
||||
|
||||
inline void from_json(const nlohmann::json& j, FooBar& fb)
|
||||
{
|
||||
j.at("value").get_to(fb.foo.value);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// for #3171
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct for_3171_base // NOLINT(cppcoreguidelines-special-member-functions)
|
||||
{
|
||||
for_3171_base(const std::string& /*unused*/ = {}) {}
|
||||
virtual ~for_3171_base() = default;
|
||||
|
||||
virtual void _from_json(const json& j)
|
||||
{
|
||||
j.at("str").get_to(str);
|
||||
}
|
||||
|
||||
std::string str{};
|
||||
};
|
||||
|
||||
struct for_3171_derived : public for_3171_base
|
||||
{
|
||||
for_3171_derived() = default;
|
||||
explicit for_3171_derived(const std::string& /*unused*/) { }
|
||||
};
|
||||
|
||||
inline void from_json(const json& j, for_3171_base& tb)
|
||||
{
|
||||
tb._from_json(j);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// for #3312
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef JSON_HAS_CPP_20
|
||||
struct for_3312
|
||||
{
|
||||
std::string name;
|
||||
};
|
||||
|
||||
inline void from_json(const json& j, for_3312& obj)
|
||||
{
|
||||
j.at("name").get_to(obj.name);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("regression tests 2")
|
||||
{
|
||||
SECTION("issue #1001 - Fix memory leak during parser callback")
|
||||
{
|
||||
const auto* geojsonExample = R"(
|
||||
{ "type": "FeatureCollection",
|
||||
"features": [
|
||||
{ "type": "Feature",
|
||||
"geometry": {"type": "Point", "coordinates": [102.0, 0.5]},
|
||||
"properties": {"prop0": "value0"}
|
||||
},
|
||||
{ "type": "Feature",
|
||||
"geometry": {
|
||||
"type": "LineString",
|
||||
"coordinates": [
|
||||
[102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0]
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"prop0": "value0",
|
||||
"prop1": 0.0
|
||||
}
|
||||
},
|
||||
{ "type": "Feature",
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0],
|
||||
[100.0, 1.0], [100.0, 0.0] ]
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"prop0": "value0",
|
||||
"prop1": {"this": "that"}
|
||||
}
|
||||
}
|
||||
]
|
||||
})";
|
||||
|
||||
json::parser_callback_t cb = [&](int /*level*/, json::parse_event_t event, json & parsed) noexcept
|
||||
{
|
||||
// skip uninteresting events
|
||||
if (event == json::parse_event_t::value && !parsed.is_primitive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case json::parse_event_t::key:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
case json::parse_event_t::value:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case json::parse_event_t::object_start:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
case json::parse_event_t::object_end:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case json::parse_event_t::array_start:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
case json::parse_event_t::array_end:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
auto j = json::parse(geojsonExample, cb, true);
|
||||
CHECK(j == json());
|
||||
}
|
||||
|
||||
SECTION("issue #1021 - to/from_msgpack only works with standard typization")
|
||||
{
|
||||
float_json j = 1000.0;
|
||||
CHECK(float_json::from_cbor(float_json::to_cbor(j)) == j);
|
||||
CHECK(float_json::from_msgpack(float_json::to_msgpack(j)) == j);
|
||||
CHECK(float_json::from_ubjson(float_json::to_ubjson(j)) == j);
|
||||
|
||||
float_json j2 = {1000.0, 2000.0, 3000.0};
|
||||
CHECK(float_json::from_ubjson(float_json::to_ubjson(j2, true, true)) == j2);
|
||||
}
|
||||
|
||||
SECTION("issue #1045 - Using STL algorithms with JSON containers with expected results?")
|
||||
{
|
||||
json diffs = nlohmann::json::array();
|
||||
json m1{{"key1", 42}};
|
||||
json m2{{"key2", 42}};
|
||||
auto p1 = m1.items();
|
||||
auto p2 = m2.items();
|
||||
|
||||
using it_type = decltype(p1.begin());
|
||||
|
||||
std::set_difference(
|
||||
p1.begin(),
|
||||
p1.end(),
|
||||
p2.begin(),
|
||||
p2.end(),
|
||||
std::inserter(diffs, diffs.end()),
|
||||
[&](const it_type & e1, const it_type & e2) -> bool
|
||||
{
|
||||
using comper_pair = std::pair<std::string, decltype(e1.value())>; // Trying to avoid unneeded copy
|
||||
return comper_pair(e1.key(), e1.value()) < comper_pair(e2.key(), e2.value()); // Using pair comper
|
||||
});
|
||||
|
||||
CHECK(diffs.size() == 1); // Note the change here, was 2
|
||||
}
|
||||
|
||||
#ifdef JSON_HAS_CPP_17
|
||||
SECTION("issue #1292 - Serializing std::variant causes stack overflow")
|
||||
{
|
||||
static_assert(!std::is_constructible<json, std::variant<int, float>>::value, "unexpected value");
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("issue #1299 - compile error in from_json converting to container "
|
||||
"with std::pair")
|
||||
{
|
||||
json j =
|
||||
{
|
||||
{"1", {{"a", "testa_1"}, {"b", "testb_1"}}},
|
||||
{"2", {{"a", "testa_2"}, {"b", "testb_2"}}},
|
||||
{"3", {{"a", "testa_3"}, {"b", "testb_3"}}},
|
||||
};
|
||||
|
||||
std::map<std::string, Data> expected
|
||||
{
|
||||
{"1", {"testa_1", "testb_1"}},
|
||||
{"2", {"testa_2", "testb_2"}},
|
||||
{"3", {"testa_3", "testb_3"}},
|
||||
};
|
||||
const auto data = j.get<decltype(expected)>();
|
||||
CHECK(expected == data);
|
||||
}
|
||||
|
||||
SECTION("issue #1445 - buffer overflow in dumping invalid utf-8 strings")
|
||||
{
|
||||
SECTION("a bunch of -1, ensure_ascii=true")
|
||||
{
|
||||
const auto length = 300;
|
||||
|
||||
json dump_test;
|
||||
dump_test["1"] = std::string(length, -1);
|
||||
|
||||
std::string expected = R"({"1":")";
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
expected += "\\ufffd";
|
||||
}
|
||||
expected += "\"}";
|
||||
|
||||
auto s = dump_test.dump(-1, ' ', true, nlohmann::json::error_handler_t::replace);
|
||||
CHECK(s == expected);
|
||||
}
|
||||
SECTION("a bunch of -2, ensure_ascii=false")
|
||||
{
|
||||
const auto length = 500;
|
||||
|
||||
json dump_test;
|
||||
dump_test["1"] = std::string(length, -2);
|
||||
|
||||
std::string expected = R"({"1":")";
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
expected += "\xEF\xBF\xBD";
|
||||
}
|
||||
expected += "\"}";
|
||||
|
||||
auto s = dump_test.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace);
|
||||
CHECK(s == expected);
|
||||
}
|
||||
SECTION("test case in issue #1445")
|
||||
{
|
||||
nlohmann::json dump_test;
|
||||
const std::array<int, 108> data =
|
||||
{
|
||||
{109, 108, 103, 125, -122, -53, 115, 18, 3, 0, 102, 19, 1, 15, -110, 13, -3, -1, -81, 32, 2, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -80, 2, 0, 0, 96, -118, 46, -116, 46, 109, -84, -87, 108, 14, 109, -24, -83, 13, -18, -51, -83, -52, -115, 14, 6, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 3, 0, 0, 0, 35, -74, -73, 55, 57, -128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, -96, -54, -28, -26}
|
||||
};
|
||||
std::string s;
|
||||
for (int i : data)
|
||||
{
|
||||
s += static_cast<char>(i);
|
||||
}
|
||||
dump_test["1"] = s;
|
||||
dump_test.dump(-1, ' ', true, nlohmann::json::error_handler_t::replace);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("issue #1447 - Integer Overflow (OSS-Fuzz 12506)")
|
||||
{
|
||||
json j = json::parse("[-9223372036854775808]");
|
||||
CHECK(j.dump() == "[-9223372036854775808]");
|
||||
}
|
||||
|
||||
SECTION("issue #1708 - minimum value of int64_t can be outputted")
|
||||
{
|
||||
constexpr auto smallest = (std::numeric_limits<int64_t>::min)();
|
||||
json j = smallest;
|
||||
CHECK(j.dump() == std::to_string(smallest));
|
||||
}
|
||||
|
||||
SECTION("issue #1727 - Contains with non-const lvalue json_pointer picks the wrong overload")
|
||||
{
|
||||
json j = {{"root", {{"settings", {{"logging", true}}}}}};
|
||||
|
||||
auto jptr1 = "/root/settings/logging"_json_pointer;
|
||||
auto jptr2 = json::json_pointer{"/root/settings/logging"};
|
||||
|
||||
CHECK(j.contains(jptr1));
|
||||
CHECK(j.contains(jptr2));
|
||||
}
|
||||
|
||||
SECTION("issue #1647 - compile error when deserializing enum if both non-default from_json and non-member operator== exists for other type")
|
||||
{
|
||||
{
|
||||
json j;
|
||||
NonDefaultFromJsonStruct x(j);
|
||||
NonDefaultFromJsonStruct y;
|
||||
CHECK(x == y);
|
||||
}
|
||||
|
||||
auto val = nlohmann::json("one").get<for_1647>();
|
||||
CHECK(val == for_1647::one);
|
||||
json j = val;
|
||||
}
|
||||
|
||||
SECTION("issue #1715 - json::from_cbor does not respect allow_exceptions = false when input is string literal")
|
||||
{
|
||||
SECTION("string literal")
|
||||
{
|
||||
json cbor = json::from_cbor("B", true, false);
|
||||
CHECK(cbor.is_discarded());
|
||||
}
|
||||
|
||||
SECTION("string array")
|
||||
{
|
||||
const std::array<char, 2> input = {{'B', 0x00}};
|
||||
json cbor = json::from_cbor(input, true, false);
|
||||
CHECK(cbor.is_discarded());
|
||||
}
|
||||
|
||||
SECTION("std::string")
|
||||
{
|
||||
json cbor = json::from_cbor(std::string("B"), true, false);
|
||||
CHECK(cbor.is_discarded());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("issue #1805 - A pair<T1, T2> is json constructible only if T1 and T2 are json constructible")
|
||||
{
|
||||
static_assert(!std::is_constructible<json, std::pair<std::string, NotSerializableData>>::value, "unexpected result");
|
||||
static_assert(!std::is_constructible<json, std::pair<NotSerializableData, std::string>>::value, "unexpected result");
|
||||
static_assert(std::is_constructible<json, std::pair<int, std::string>>::value, "unexpected result");
|
||||
}
|
||||
SECTION("issue #1825 - A tuple<Args..> is json constructible only if all T in Args are json constructible")
|
||||
{
|
||||
static_assert(!std::is_constructible<json, std::tuple<std::string, NotSerializableData>>::value, "unexpected result");
|
||||
static_assert(!std::is_constructible<json, std::tuple<NotSerializableData, std::string>>::value, "unexpected result");
|
||||
static_assert(std::is_constructible<json, std::tuple<int, std::string>>::value, "unexpected result");
|
||||
}
|
||||
|
||||
SECTION("issue #1983 - JSON patch diff for op=add formation is not as per standard (RFC 6902)")
|
||||
{
|
||||
const auto source = R"({ "foo": [ "1", "2" ] })"_json;
|
||||
const auto target = R"({"foo": [ "1", "2", "3" ]})"_json;
|
||||
const auto result = json::diff(source, target);
|
||||
CHECK(result.dump() == R"([{"op":"add","path":"/foo/-","value":"3"}])");
|
||||
}
|
||||
|
||||
SECTION("issue #2067 - cannot serialize binary data to text JSON")
|
||||
{
|
||||
const std::array<unsigned char, 23> data = {{0x81, 0xA4, 0x64, 0x61, 0x74, 0x61, 0xC4, 0x0F, 0x33, 0x30, 0x30, 0x32, 0x33, 0x34, 0x30, 0x31, 0x30, 0x37, 0x30, 0x35, 0x30, 0x31, 0x30}};
|
||||
json j = json::from_msgpack(data.data(), data.size());
|
||||
CHECK_NOTHROW(
|
||||
j.dump(4, // Indent
|
||||
' ', // Indent char
|
||||
false, // Ensure ascii
|
||||
json::error_handler_t::strict // Error
|
||||
));
|
||||
}
|
||||
|
||||
SECTION("PR #2181 - regression bug with lvalue")
|
||||
{
|
||||
// see https://github.com/nlohmann/json/pull/2181#issuecomment-653326060
|
||||
json j{{"x", "test"}};
|
||||
std::string defval = "default value";
|
||||
auto val = j.value("x", defval);
|
||||
auto val2 = j.value("y", defval);
|
||||
}
|
||||
|
||||
SECTION("issue #2293 - eof doesn't cause parsing to stop")
|
||||
{
|
||||
std::vector<uint8_t> data =
|
||||
{
|
||||
0x7B,
|
||||
0x6F,
|
||||
0x62,
|
||||
0x6A,
|
||||
0x65,
|
||||
0x63,
|
||||
0x74,
|
||||
0x20,
|
||||
0x4F,
|
||||
0x42
|
||||
};
|
||||
json result = json::from_cbor(data, true, false);
|
||||
CHECK(result.is_discarded());
|
||||
}
|
||||
|
||||
SECTION("issue #2315 - json.update and vector<pair>does not work with ordered_json")
|
||||
{
|
||||
nlohmann::ordered_json jsonAnimals = {{"animal", "dog"}};
|
||||
nlohmann::ordered_json jsonCat = {{"animal", "cat"}};
|
||||
jsonAnimals.update(jsonCat);
|
||||
CHECK(jsonAnimals["animal"] == "cat");
|
||||
|
||||
auto jsonAnimals_parsed = nlohmann::ordered_json::parse(jsonAnimals.dump());
|
||||
CHECK(jsonAnimals == jsonAnimals_parsed);
|
||||
|
||||
std::vector<std::pair<std::string, int64_t>> intData = {std::make_pair("aaaa", 11),
|
||||
std::make_pair("bbb", 222)
|
||||
};
|
||||
nlohmann::ordered_json jsonObj;
|
||||
for (const auto& data : intData)
|
||||
{
|
||||
jsonObj[data.first] = data.second;
|
||||
}
|
||||
CHECK(jsonObj["aaaa"] == 11);
|
||||
CHECK(jsonObj["bbb"] == 222);
|
||||
}
|
||||
|
||||
SECTION("issue #2330 - ignore_comment=true fails on multiple consecutive lines starting with comments")
|
||||
{
|
||||
std::string ss = "//\n//\n{\n}\n";
|
||||
json j = json::parse(ss, nullptr, true, true);
|
||||
CHECK(j.dump() == "{}");
|
||||
}
|
||||
|
||||
#ifdef JSON_HAS_CPP_20
|
||||
SECTION("issue #2546 - parsing containers of std::byte")
|
||||
{
|
||||
const char DATA[] = R"("Hello, world!")"; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
const auto s = std::as_bytes(std::span(DATA));
|
||||
json j = json::parse(s);
|
||||
CHECK(j.dump() == "\"Hello, world!\"");
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("issue #2574 - Deserialization to std::array, std::pair, and std::tuple with non-default constructable types fails")
|
||||
{
|
||||
SECTION("std::array")
|
||||
{
|
||||
{
|
||||
json j = {7, 4};
|
||||
auto arr = j.get<std::array<NonDefaultConstructible, 2>>();
|
||||
CHECK(arr[0].x == 7);
|
||||
CHECK(arr[1].x == 4);
|
||||
}
|
||||
|
||||
{
|
||||
json j = 7;
|
||||
CHECK_THROWS_AS((j.get<std::array<NonDefaultConstructible, 1>>()), json::type_error);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("std::pair")
|
||||
{
|
||||
{
|
||||
json j = {3, 8};
|
||||
auto p = j.get<std::pair<NonDefaultConstructible, NonDefaultConstructible>>();
|
||||
CHECK(p.first.x == 3);
|
||||
CHECK(p.second.x == 8);
|
||||
}
|
||||
|
||||
{
|
||||
json j = {4, 1};
|
||||
auto p = j.get<std::pair<int, NonDefaultConstructible>>();
|
||||
CHECK(p.first == 4);
|
||||
CHECK(p.second.x == 1);
|
||||
}
|
||||
|
||||
{
|
||||
json j = {6, 7};
|
||||
auto p = j.get<std::pair<NonDefaultConstructible, int>>();
|
||||
CHECK(p.first.x == 6);
|
||||
CHECK(p.second == 7);
|
||||
}
|
||||
|
||||
{
|
||||
json j = 7;
|
||||
CHECK_THROWS_AS((j.get<std::pair<NonDefaultConstructible, int>>()), json::type_error);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("std::tuple")
|
||||
{
|
||||
{
|
||||
json j = {9};
|
||||
auto t = j.get<std::tuple<NonDefaultConstructible>>();
|
||||
CHECK(std::get<0>(t).x == 9);
|
||||
}
|
||||
|
||||
{
|
||||
json j = {9, 8, 7};
|
||||
auto t = j.get<std::tuple<NonDefaultConstructible, int, NonDefaultConstructible>>();
|
||||
CHECK(std::get<0>(t).x == 9);
|
||||
CHECK(std::get<1>(t) == 8);
|
||||
CHECK(std::get<2>(t).x == 7);
|
||||
}
|
||||
|
||||
{
|
||||
json j = 7;
|
||||
CHECK_THROWS_AS((j.get<std::tuple<NonDefaultConstructible>>()), json::type_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("issue #2865 - ASAN detects memory leaks")
|
||||
{
|
||||
// the code below is expected to not leak memory
|
||||
{
|
||||
nlohmann::json o;
|
||||
std::string s = "bar";
|
||||
|
||||
nlohmann::to_json(o["foo"], s);
|
||||
|
||||
nlohmann::json p = o;
|
||||
|
||||
// call to_json with a non-null JSON value
|
||||
nlohmann::to_json(p["foo"], s);
|
||||
}
|
||||
|
||||
{
|
||||
nlohmann::json o;
|
||||
std::string s = "bar";
|
||||
|
||||
nlohmann::to_json(o["foo"], s);
|
||||
|
||||
// call to_json with a non-null JSON value
|
||||
nlohmann::to_json(o["foo"], s);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("issue #2824 - encoding of json::exception::what()")
|
||||
{
|
||||
json j;
|
||||
sax_no_exception sax(j);
|
||||
|
||||
CHECK(!json::sax_parse("xyz", &sax));
|
||||
CHECK(*sax_no_exception::error_string == "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - invalid literal; last read: 'x'");
|
||||
delete sax_no_exception::error_string; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
}
|
||||
|
||||
SECTION("issue #2825 - Properly constrain the basic_json conversion operator")
|
||||
{
|
||||
static_assert(std::is_copy_assignable<nlohmann::ordered_json>::value, "ordered_json must be copy assignable");
|
||||
}
|
||||
|
||||
SECTION("issue #2958 - Inserting in unordered json using a pointer retains the leading slash")
|
||||
{
|
||||
std::string p = "/root";
|
||||
|
||||
json test1;
|
||||
test1[json::json_pointer(p)] = json::object();
|
||||
CHECK(test1.dump() == "{\"root\":{}}");
|
||||
|
||||
ordered_json test2;
|
||||
test2[ordered_json::json_pointer(p)] = json::object();
|
||||
CHECK(test2.dump() == "{\"root\":{}}");
|
||||
|
||||
// json::json_pointer and ordered_json::json_pointer are the same type; behave as above
|
||||
ordered_json test3;
|
||||
test3[json::json_pointer(p)] = json::object();
|
||||
CHECK(std::is_same<json::json_pointer::string_t, ordered_json::json_pointer::string_t>::value);
|
||||
CHECK(test3.dump() == "{\"root\":{}}");
|
||||
}
|
||||
|
||||
SECTION("issue #2982 - to_{binary format} does not provide a mechanism for specifying a custom allocator for the returned type")
|
||||
{
|
||||
std::vector<std::uint8_t, my_allocator<std::uint8_t>> my_vector;
|
||||
json j = {1, 2, 3, 4};
|
||||
json::to_cbor(j, my_vector);
|
||||
json k = json::from_cbor(my_vector);
|
||||
CHECK(j == k);
|
||||
}
|
||||
|
||||
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
||||
SECTION("issue #3070 - Version 3.10.3 breaks backward-compatibility with 3.10.2 ")
|
||||
{
|
||||
nlohmann::detail::std_fs::path text_path("/tmp/text.txt");
|
||||
json j(text_path);
|
||||
|
||||
const auto j_path = j.get<nlohmann::detail::std_fs::path>();
|
||||
CHECK(j_path == text_path);
|
||||
|
||||
#if !defined(_MSC_VER) && !(defined(__GNUC__) && __GNUC__ == 8 && __GNUC_MINOR__ < 4)
|
||||
// works everywhere but on MSVC and GCC <8.4
|
||||
CHECK_THROWS_WITH_AS(nlohmann::detail::std_fs::path(json(1)), "[json.exception.type_error.302] type must be string, but is number", json::type_error);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("issue #3077 - explicit constructor with default does not compile")
|
||||
{
|
||||
json j;
|
||||
j[0]["value"] = true;
|
||||
std::vector<FooBar> foo;
|
||||
j.get_to(foo);
|
||||
}
|
||||
|
||||
SECTION("issue #3108 - ordered_json doesn't support range based erase")
|
||||
{
|
||||
ordered_json j = {1, 2, 2, 4};
|
||||
|
||||
auto last = std::unique(j.begin(), j.end());
|
||||
j.erase(last, j.end());
|
||||
|
||||
CHECK(j.dump() == "[1,2,4]");
|
||||
|
||||
j.erase(std::remove_if(j.begin(), j.end(), [](const ordered_json & val)
|
||||
{
|
||||
return val == 2;
|
||||
}), j.end());
|
||||
|
||||
CHECK(j.dump() == "[1,4]");
|
||||
}
|
||||
|
||||
SECTION("issue #3343 - json and ordered_json are not interchangable")
|
||||
{
|
||||
json::object_t jobj({ { "product", "one" } });
|
||||
ordered_json::object_t ojobj({{"product", "one"}});
|
||||
|
||||
auto jit = jobj.begin();
|
||||
auto ojit = ojobj.begin();
|
||||
|
||||
CHECK(jit->first == ojit->first);
|
||||
CHECK(jit->second.get<std::string>() == ojit->second.get<std::string>());
|
||||
}
|
||||
|
||||
SECTION("issue #3171 - if class is_constructible from std::string wrong from_json overload is being selected, compilation failed")
|
||||
{
|
||||
json j{{ "str", "value"}};
|
||||
|
||||
// failed with: error: no match for ‘operator=’ (operand types are ‘for_3171_derived’ and ‘const nlohmann::basic_json<>::string_t’
|
||||
// {aka ‘const std::__cxx11::basic_string<char>’})
|
||||
// s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
|
||||
auto td = j.get<for_3171_derived>();
|
||||
|
||||
CHECK(td.str == "value");
|
||||
}
|
||||
|
||||
#ifdef JSON_HAS_CPP_20
|
||||
SECTION("issue #3312 - Parse to custom class from unordered_json breaks on G++11.2.0 with C++20")
|
||||
{
|
||||
// see test for #3171
|
||||
ordered_json j = {{"name", "class"}};
|
||||
for_3312 obj{};
|
||||
|
||||
j.get_to(obj);
|
||||
|
||||
CHECK(obj.name == "class");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(JSON_HAS_CPP_17) && JSON_USE_IMPLICIT_CONVERSIONS
|
||||
SECTION("issue #3428 - Error occurred when converting nlohmann::json to std::any")
|
||||
{
|
||||
json j;
|
||||
std::any a1 = j;
|
||||
std::any&& a2 = j;
|
||||
|
||||
CHECK(a1.type() == typeid(j));
|
||||
CHECK(a2.type() == typeid(j));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_POP
|
||||
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
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("given fill")
|
||||
{
|
||||
std::stringstream ss;
|
||||
json j = {"foo", 1, 2, 3, false, {{"one", 1}}};
|
||||
ss << std::setw(1) << std::setfill('\t') << j;
|
||||
CHECK(ss.str() ==
|
||||
"[\n\t\"foo\",\n\t1,\n\t2,\n\t3,\n\tfalse,\n\t{\n\t\t\"one\": 1\n\t}\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]");
|
||||
}
|
||||
|
||||
SECTION("given fill")
|
||||
{
|
||||
std::stringstream ss;
|
||||
json j = {"foo", 1, 2, 3, false, {{"one", 1}}};
|
||||
ss.width(1);
|
||||
ss.fill('\t');
|
||||
j >> ss;
|
||||
CHECK(ss.str() ==
|
||||
"[\n\t\"foo\",\n\t1,\n\t2,\n\t3,\n\tfalse,\n\t{\n\t\t\"one\": 1\n\t}\n]");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("dump")
|
||||
{
|
||||
SECTION("invalid character")
|
||||
{
|
||||
json j = "ä\xA9ü";
|
||||
|
||||
CHECK_THROWS_WITH_AS(j.dump(), "[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(j.dump(1, ' ', false, json::error_handler_t::strict), "[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9", json::type_error&);
|
||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"äü\"");
|
||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"ä\xEF\xBF\xBDü\"");
|
||||
CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"\\u00e4\\ufffd\\u00fc\"");
|
||||
}
|
||||
|
||||
SECTION("ending with incomplete character")
|
||||
{
|
||||
json j = "123\xC2";
|
||||
|
||||
CHECK_THROWS_WITH_AS(j.dump(), "[json.exception.type_error.316] incomplete UTF-8 string; last byte: 0xC2", json::type_error&);
|
||||
CHECK_THROWS_AS(j.dump(1, ' ', false, json::error_handler_t::strict), json::type_error&);
|
||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"123\"");
|
||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"123\xEF\xBF\xBD\"");
|
||||
CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"123\\ufffd\"");
|
||||
}
|
||||
|
||||
SECTION("unexpected character")
|
||||
{
|
||||
json j = "123\xF1\xB0\x34\x35\x36";
|
||||
|
||||
CHECK_THROWS_WITH_AS(j.dump(), "[json.exception.type_error.316] invalid UTF-8 byte at index 5: 0x34", json::type_error&);
|
||||
CHECK_THROWS_AS(j.dump(1, ' ', false, json::error_handler_t::strict), json::type_error&);
|
||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::ignore) == "\"123456\"");
|
||||
CHECK(j.dump(-1, ' ', false, json::error_handler_t::replace) == "\"123\xEF\xBF\xBD\x34\x35\x36\"");
|
||||
CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"123\\ufffd456\"");
|
||||
}
|
||||
|
||||
SECTION("U+FFFD Substitution of Maximal Subparts")
|
||||
{
|
||||
// Some tests (mostly) from
|
||||
// https://www.unicode.org/versions/Unicode11.0.0/ch03.pdf
|
||||
// Section 3.9 -- U+FFFD Substitution of Maximal Subparts
|
||||
|
||||
auto test = [&](std::string const & input, std::string const & expected)
|
||||
{
|
||||
json j = input;
|
||||
CHECK(j.dump(-1, ' ', true, json::error_handler_t::replace) == "\"" + expected + "\"");
|
||||
};
|
||||
|
||||
test("\xC2", "\\ufffd");
|
||||
test("\xC2\x41\x42", "\\ufffd" "\x41" "\x42");
|
||||
test("\xC2\xF4", "\\ufffd" "\\ufffd");
|
||||
|
||||
test("\xF0\x80\x80\x41", "\\ufffd" "\\ufffd" "\\ufffd" "\x41");
|
||||
test("\xF1\x80\x80\x41", "\\ufffd" "\x41");
|
||||
test("\xF2\x80\x80\x41", "\\ufffd" "\x41");
|
||||
test("\xF3\x80\x80\x41", "\\ufffd" "\x41");
|
||||
test("\xF4\x80\x80\x41", "\\ufffd" "\x41");
|
||||
test("\xF5\x80\x80\x41", "\\ufffd" "\\ufffd" "\\ufffd" "\x41");
|
||||
|
||||
test("\xF0\x90\x80\x41", "\\ufffd" "\x41");
|
||||
test("\xF1\x90\x80\x41", "\\ufffd" "\x41");
|
||||
test("\xF2\x90\x80\x41", "\\ufffd" "\x41");
|
||||
test("\xF3\x90\x80\x41", "\\ufffd" "\x41");
|
||||
test("\xF4\x90\x80\x41", "\\ufffd" "\\ufffd" "\\ufffd" "\x41");
|
||||
test("\xF5\x90\x80\x41", "\\ufffd" "\\ufffd" "\\ufffd" "\x41");
|
||||
|
||||
test("\xC0\xAF\xE0\x80\xBF\xF0\x81\x82\x41", "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\x41");
|
||||
test("\xED\xA0\x80\xED\xBF\xBF\xED\xAF\x41", "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\x41");
|
||||
test("\xF4\x91\x92\x93\xFF\x41\x80\xBF\x42", "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\x41" "\\ufffd""\\ufffd" "\x42");
|
||||
test("\xE1\x80\xE2\xF0\x91\x92\xF1\xBF\x41", "\\ufffd" "\\ufffd" "\\ufffd" "\\ufffd" "\x41");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("to_string")
|
||||
{
|
||||
auto test = [&](std::string const & input, std::string const & expected)
|
||||
{
|
||||
using std::to_string;
|
||||
json j = input;
|
||||
CHECK(to_string(j) == "\"" + expected + "\"");
|
||||
};
|
||||
|
||||
test(R"({"x":5,"y":6})", R"({\"x\":5,\"y\":6})");
|
||||
test("{\"x\":[10,null,null,null]}", R"({\"x\":[10,null,null,null]})");
|
||||
test("test", "test");
|
||||
test("[3,\"false\",false]", R"([3,\"false\",false])");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_TEMPLATE("serialization for extreme integer values", T, int32_t, uint32_t, int64_t, uint64_t)
|
||||
{
|
||||
SECTION("minimum")
|
||||
{
|
||||
constexpr auto minimum = (std::numeric_limits<T>::min)();
|
||||
json j = minimum;
|
||||
CHECK(j.dump() == std::to_string(minimum));
|
||||
}
|
||||
|
||||
SECTION("maximum")
|
||||
{
|
||||
constexpr auto maximum = (std::numeric_limits<T>::max)();
|
||||
json j = maximum;
|
||||
CHECK(j.dump() == std::to_string(maximum));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("dump with binary values")
|
||||
{
|
||||
auto binary = json::binary({1, 2, 3, 4});
|
||||
auto binary_empty = json::binary({});
|
||||
auto binary_with_subtype = json::binary({1, 2, 3, 4}, 128);
|
||||
auto binary_empty_with_subtype = json::binary({}, 128);
|
||||
|
||||
json object = {{"key", binary}};
|
||||
json object_empty = {{"key", binary_empty}};
|
||||
json object_with_subtype = {{"key", binary_with_subtype}};
|
||||
json object_empty_with_subtype = {{"key", binary_empty_with_subtype}};
|
||||
|
||||
json array = {"value", 1, binary};
|
||||
json array_empty = {"value", 1, binary_empty};
|
||||
json array_with_subtype = {"value", 1, binary_with_subtype};
|
||||
json array_empty_with_subtype = {"value", 1, binary_empty_with_subtype};
|
||||
|
||||
SECTION("normal")
|
||||
{
|
||||
CHECK(binary.dump() == "{\"bytes\":[1,2,3,4],\"subtype\":null}");
|
||||
CHECK(binary_empty.dump() == "{\"bytes\":[],\"subtype\":null}");
|
||||
CHECK(binary_with_subtype.dump() == "{\"bytes\":[1,2,3,4],\"subtype\":128}");
|
||||
CHECK(binary_empty_with_subtype.dump() == "{\"bytes\":[],\"subtype\":128}");
|
||||
|
||||
CHECK(object.dump() == "{\"key\":{\"bytes\":[1,2,3,4],\"subtype\":null}}");
|
||||
CHECK(object_empty.dump() == "{\"key\":{\"bytes\":[],\"subtype\":null}}");
|
||||
CHECK(object_with_subtype.dump() == "{\"key\":{\"bytes\":[1,2,3,4],\"subtype\":128}}");
|
||||
CHECK(object_empty_with_subtype.dump() == "{\"key\":{\"bytes\":[],\"subtype\":128}}");
|
||||
|
||||
CHECK(array.dump() == "[\"value\",1,{\"bytes\":[1,2,3,4],\"subtype\":null}]");
|
||||
CHECK(array_empty.dump() == "[\"value\",1,{\"bytes\":[],\"subtype\":null}]");
|
||||
CHECK(array_with_subtype.dump() == "[\"value\",1,{\"bytes\":[1,2,3,4],\"subtype\":128}]");
|
||||
CHECK(array_empty_with_subtype.dump() == "[\"value\",1,{\"bytes\":[],\"subtype\":128}]");
|
||||
}
|
||||
|
||||
SECTION("pretty-printed")
|
||||
{
|
||||
CHECK(binary.dump(4) == "{\n"
|
||||
" \"bytes\": [1, 2, 3, 4],\n"
|
||||
" \"subtype\": null\n"
|
||||
"}");
|
||||
CHECK(binary_empty.dump(4) == "{\n"
|
||||
" \"bytes\": [],\n"
|
||||
" \"subtype\": null\n"
|
||||
"}");
|
||||
CHECK(binary_with_subtype.dump(4) == "{\n"
|
||||
" \"bytes\": [1, 2, 3, 4],\n"
|
||||
" \"subtype\": 128\n"
|
||||
"}");
|
||||
CHECK(binary_empty_with_subtype.dump(4) == "{\n"
|
||||
" \"bytes\": [],\n"
|
||||
" \"subtype\": 128\n"
|
||||
"}");
|
||||
|
||||
CHECK(object.dump(4) == "{\n"
|
||||
" \"key\": {\n"
|
||||
" \"bytes\": [1, 2, 3, 4],\n"
|
||||
" \"subtype\": null\n"
|
||||
" }\n"
|
||||
"}");
|
||||
CHECK(object_empty.dump(4) == "{\n"
|
||||
" \"key\": {\n"
|
||||
" \"bytes\": [],\n"
|
||||
" \"subtype\": null\n"
|
||||
" }\n"
|
||||
"}");
|
||||
CHECK(object_with_subtype.dump(4) == "{\n"
|
||||
" \"key\": {\n"
|
||||
" \"bytes\": [1, 2, 3, 4],\n"
|
||||
" \"subtype\": 128\n"
|
||||
" }\n"
|
||||
"}");
|
||||
CHECK(object_empty_with_subtype.dump(4) == "{\n"
|
||||
" \"key\": {\n"
|
||||
" \"bytes\": [],\n"
|
||||
" \"subtype\": 128\n"
|
||||
" }\n"
|
||||
"}");
|
||||
|
||||
CHECK(array.dump(4) == "[\n"
|
||||
" \"value\",\n"
|
||||
" 1,\n"
|
||||
" {\n"
|
||||
" \"bytes\": [1, 2, 3, 4],\n"
|
||||
" \"subtype\": null\n"
|
||||
" }\n"
|
||||
"]");
|
||||
CHECK(array_empty.dump(4) == "[\n"
|
||||
" \"value\",\n"
|
||||
" 1,\n"
|
||||
" {\n"
|
||||
" \"bytes\": [],\n"
|
||||
" \"subtype\": null\n"
|
||||
" }\n"
|
||||
"]");
|
||||
CHECK(array_with_subtype.dump(4) == "[\n"
|
||||
" \"value\",\n"
|
||||
" 1,\n"
|
||||
" {\n"
|
||||
" \"bytes\": [1, 2, 3, 4],\n"
|
||||
" \"subtype\": 128\n"
|
||||
" }\n"
|
||||
"]");
|
||||
CHECK(array_empty_with_subtype.dump(4) == "[\n"
|
||||
" \"value\",\n"
|
||||
" 1,\n"
|
||||
" {\n"
|
||||
" \"bytes\": [],\n"
|
||||
" \"subtype\": 128\n"
|
||||
" }\n"
|
||||
"]");
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,537 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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.
|
||||
*/
|
||||
|
||||
// XXX:
|
||||
// Only compile these tests if 'float' and 'double' are IEEE-754 single- and
|
||||
// double-precision numbers, resp.
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::detail::dtoa_impl::reinterpret_bits;
|
||||
|
||||
namespace
|
||||
{
|
||||
float make_float(uint32_t sign_bit, uint32_t biased_exponent, uint32_t significand)
|
||||
{
|
||||
assert(sign_bit == 0 || sign_bit == 1);
|
||||
assert(biased_exponent <= 0xFF);
|
||||
assert(significand <= 0x007FFFFF);
|
||||
|
||||
uint32_t bits = 0;
|
||||
|
||||
bits |= sign_bit << 31;
|
||||
bits |= biased_exponent << 23;
|
||||
bits |= significand;
|
||||
|
||||
return reinterpret_bits<float>(bits);
|
||||
}
|
||||
|
||||
// ldexp -- convert f * 2^e to IEEE single precision
|
||||
float make_float(uint64_t f, int e)
|
||||
{
|
||||
constexpr uint64_t kHiddenBit = 0x00800000;
|
||||
constexpr uint64_t kSignificandMask = 0x007FFFFF;
|
||||
constexpr int kPhysicalSignificandSize = 23; // Excludes the hidden bit.
|
||||
constexpr int kExponentBias = 0x7F + kPhysicalSignificandSize;
|
||||
constexpr int kDenormalExponent = 1 - kExponentBias;
|
||||
constexpr int kMaxExponent = 0xFF - kExponentBias;
|
||||
|
||||
while (f > kHiddenBit + kSignificandMask)
|
||||
{
|
||||
f >>= 1;
|
||||
e++;
|
||||
}
|
||||
if (e >= kMaxExponent)
|
||||
{
|
||||
return std::numeric_limits<float>::infinity();
|
||||
}
|
||||
if (e < kDenormalExponent)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
while (e > kDenormalExponent && (f & kHiddenBit) == 0)
|
||||
{
|
||||
f <<= 1;
|
||||
e--;
|
||||
}
|
||||
|
||||
uint64_t biased_exponent = (e == kDenormalExponent && (f & kHiddenBit) == 0)
|
||||
? 0
|
||||
: static_cast<uint64_t>(e + kExponentBias);
|
||||
|
||||
uint64_t bits = (f & kSignificandMask) | (biased_exponent << kPhysicalSignificandSize);
|
||||
return reinterpret_bits<float>(static_cast<uint32_t>(bits));
|
||||
}
|
||||
|
||||
double make_double(uint64_t sign_bit, uint64_t biased_exponent, uint64_t significand)
|
||||
{
|
||||
assert(sign_bit == 0 || sign_bit == 1);
|
||||
assert(biased_exponent <= 0x7FF);
|
||||
assert(significand <= 0x000FFFFFFFFFFFFF);
|
||||
|
||||
uint64_t bits = 0;
|
||||
|
||||
bits |= sign_bit << 63;
|
||||
bits |= biased_exponent << 52;
|
||||
bits |= significand;
|
||||
|
||||
return reinterpret_bits<double>(bits);
|
||||
}
|
||||
|
||||
// ldexp -- convert f * 2^e to IEEE double precision
|
||||
double make_double(uint64_t f, int e)
|
||||
{
|
||||
constexpr uint64_t kHiddenBit = 0x0010000000000000;
|
||||
constexpr uint64_t kSignificandMask = 0x000FFFFFFFFFFFFF;
|
||||
constexpr int kPhysicalSignificandSize = 52; // Excludes the hidden bit.
|
||||
constexpr int kExponentBias = 0x3FF + kPhysicalSignificandSize;
|
||||
constexpr int kDenormalExponent = 1 - kExponentBias;
|
||||
constexpr int kMaxExponent = 0x7FF - kExponentBias;
|
||||
|
||||
while (f > kHiddenBit + kSignificandMask)
|
||||
{
|
||||
f >>= 1;
|
||||
e++;
|
||||
}
|
||||
if (e >= kMaxExponent)
|
||||
{
|
||||
return std::numeric_limits<double>::infinity();
|
||||
}
|
||||
if (e < kDenormalExponent)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
while (e > kDenormalExponent && (f & kHiddenBit) == 0)
|
||||
{
|
||||
f <<= 1;
|
||||
e--;
|
||||
}
|
||||
|
||||
uint64_t biased_exponent = (e == kDenormalExponent && (f & kHiddenBit) == 0)
|
||||
? 0
|
||||
: static_cast<uint64_t>(e + kExponentBias);
|
||||
|
||||
uint64_t bits = (f & kSignificandMask) | (biased_exponent << kPhysicalSignificandSize);
|
||||
return reinterpret_bits<double>(bits);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("digit gen")
|
||||
{
|
||||
SECTION("single precision")
|
||||
{
|
||||
auto check_float = [](float number, const std::string & digits, int expected_exponent)
|
||||
{
|
||||
CAPTURE(number)
|
||||
CAPTURE(digits)
|
||||
CAPTURE(expected_exponent)
|
||||
|
||||
std::array<char, 32> buf{};
|
||||
int len = 0;
|
||||
int exponent = 0;
|
||||
nlohmann::detail::dtoa_impl::grisu2(buf.data(), len, exponent, number);
|
||||
|
||||
CHECK(digits == std::string(buf.data(), buf.data() + len));
|
||||
CHECK(expected_exponent == exponent);
|
||||
};
|
||||
|
||||
check_float(make_float(0, 0, 0x00000001), "1", -45); // min denormal
|
||||
check_float(make_float(0, 0, 0x007FFFFF), "11754942", -45); // max denormal
|
||||
check_float(make_float(0, 1, 0x00000000), "11754944", -45); // min normal
|
||||
check_float(make_float(0, 1, 0x00000001), "11754945", -45);
|
||||
check_float(make_float(0, 1, 0x007FFFFF), "23509886", -45);
|
||||
check_float(make_float(0, 2, 0x00000000), "23509887", -45);
|
||||
check_float(make_float(0, 2, 0x00000001), "2350989", -44);
|
||||
check_float(make_float(0, 24, 0x00000000), "98607613", -39); // fail if no special case in normalized boundaries
|
||||
check_float(make_float(0, 30, 0x00000000), "63108872", -37); // fail if no special case in normalized boundaries
|
||||
check_float(make_float(0, 31, 0x00000000), "12621775", -36); // fail if no special case in normalized boundaries
|
||||
check_float(make_float(0, 57, 0x00000000), "84703295", -29); // fail if no special case in normalized boundaries
|
||||
check_float(make_float(0, 254, 0x007FFFFE), "34028233", 31);
|
||||
check_float(make_float(0, 254, 0x007FFFFF), "34028235", 31); // max normal
|
||||
|
||||
// V. Paxson and W. Kahan, "A Program for Testing IEEE Binary-Decimal Conversion", manuscript, May 1991,
|
||||
// ftp://ftp.ee.lbl.gov/testbase-report.ps.Z (report)
|
||||
// ftp://ftp.ee.lbl.gov/testbase.tar.Z (program)
|
||||
|
||||
// Table 16: Stress Inputs for Converting 24-bit Binary to Decimal, < 1/2 ULP
|
||||
check_float(make_float(12676506, -102), "25", -25);
|
||||
check_float(make_float(12676506, -103), "125", -26);
|
||||
check_float(make_float(15445013, 86), "1195", 30);
|
||||
check_float(make_float(13734123, -138), "39415", -39);
|
||||
check_float(make_float(12428269, -130), "913085", -38);
|
||||
check_float(make_float(15334037, -146), "1719005", -43);
|
||||
check_float(make_float(11518287, -41), "52379105", -13);
|
||||
check_float(make_float(12584953, -145), "2821644", -43);
|
||||
check_float(make_float(15961084, -125), "37524328", -38);
|
||||
check_float(make_float(14915817, -146), "16721209", -44);
|
||||
check_float(make_float(10845484, -102), "21388946", -31);
|
||||
check_float(make_float(16431059, -61), "7125836", -18);
|
||||
|
||||
// Table 17: Stress Inputs for Converting 24-bit Binary to Decimal, > 1/2 ULP
|
||||
check_float(make_float(16093626, 69), "95", 26);
|
||||
check_float(make_float( 9983778, 25), "335", 12);
|
||||
check_float(make_float(12745034, 104), "2585", 35);
|
||||
check_float(make_float(12706553, 72), "60005", 24);
|
||||
check_float(make_float(11005028, 45), "387205", 15);
|
||||
check_float(make_float(15059547, 71), "3555835", 22);
|
||||
check_float(make_float(16015691, -99), "25268305", -30);
|
||||
check_float(make_float( 8667859, 56), "6245851", 17);
|
||||
check_float(make_float(14855922, -82), "30721327", -25);
|
||||
check_float(make_float(14855922, -83), "15360663", -25);
|
||||
check_float(make_float(10144164, -110), "781478", -32);
|
||||
check_float(make_float(13248074, 95), "52481028", 28);
|
||||
}
|
||||
|
||||
SECTION("double precision")
|
||||
{
|
||||
auto check_double = [](double number, const std::string & digits, int expected_exponent)
|
||||
{
|
||||
CAPTURE(number)
|
||||
CAPTURE(digits)
|
||||
CAPTURE(expected_exponent)
|
||||
|
||||
std::array<char, 32> buf{};
|
||||
int len = 0;
|
||||
int exponent = 0;
|
||||
nlohmann::detail::dtoa_impl::grisu2(buf.data(), len, exponent, number);
|
||||
|
||||
CHECK(digits == std::string(buf.data(), buf.data() + len));
|
||||
CHECK(expected_exponent == exponent);
|
||||
};
|
||||
|
||||
check_double(make_double(0, 0, 0x0000000000000001), "5", -324); // min denormal
|
||||
check_double(make_double(0, 0, 0x000FFFFFFFFFFFFF), "2225073858507201", -323); // max denormal
|
||||
check_double(make_double(0, 1, 0x0000000000000000), "22250738585072014", -324); // min normal
|
||||
check_double(make_double(0, 1, 0x0000000000000001), "2225073858507202", -323);
|
||||
check_double(make_double(0, 1, 0x000FFFFFFFFFFFFF), "44501477170144023", -324);
|
||||
check_double(make_double(0, 2, 0x0000000000000000), "4450147717014403", -323);
|
||||
check_double(make_double(0, 2, 0x0000000000000001), "4450147717014404", -323);
|
||||
check_double(make_double(0, 4, 0x0000000000000000), "17800590868057611", -323); // fail if no special case in normalized boundaries
|
||||
check_double(make_double(0, 5, 0x0000000000000000), "35601181736115222", -323); // fail if no special case in normalized boundaries
|
||||
check_double(make_double(0, 6, 0x0000000000000000), "7120236347223045", -322); // fail if no special case in normalized boundaries
|
||||
check_double(make_double(0, 10, 0x0000000000000000), "11392378155556871", -321); // fail if no special case in normalized boundaries
|
||||
check_double(make_double(0, 2046, 0x000FFFFFFFFFFFFE), "17976931348623155", 292);
|
||||
check_double(make_double(0, 2046, 0x000FFFFFFFFFFFFF), "17976931348623157", 292); // max normal
|
||||
|
||||
// Test different paths in DigitGen
|
||||
check_double( 10000, "1", 4);
|
||||
check_double( 1200000, "12", 5);
|
||||
check_double(4.9406564584124654e-324, "5", -324); // exit integral loop
|
||||
check_double(2.2250738585072009e-308, "2225073858507201", -323); // exit fractional loop
|
||||
check_double( 1.82877982605164e-99, "182877982605164", -113);
|
||||
check_double( 1.1505466208671903e-09, "11505466208671903", -25);
|
||||
check_double( 5.5645893133766722e+20, "5564589313376672", 5);
|
||||
check_double( 53.034830388866226, "53034830388866226", -15);
|
||||
check_double( 0.0021066531670178605, "21066531670178605", -19);
|
||||
|
||||
// V. Paxson and W. Kahan, "A Program for Testing IEEE Binary-Decimal Conversion", manuscript, May 1991,
|
||||
// ftp://ftp.ee.lbl.gov/testbase-report.ps.Z (report)
|
||||
// ftp://ftp.ee.lbl.gov/testbase.tar.Z (program)
|
||||
|
||||
// Table 3: Stress Inputs for Converting 53-bit Binary to Decimal, < 1/2 ULP
|
||||
check_double(make_double(8511030020275656, -342) /* 9.5e-088 */, "95", -89);
|
||||
check_double(make_double(5201988407066741, -824) /* 4.65e-233 */, "465", -235);
|
||||
check_double(make_double(6406892948269899, +237) /* 1.415e+087 */, "1415", 84);
|
||||
check_double(make_double(8431154198732492, +72) /* 3.9815e+037 */, "39815", 33);
|
||||
check_double(make_double(6475049196144587, +99) /* 4.10405e+045 */, "410405", 40);
|
||||
check_double(make_double(8274307542972842, +726) /* 2.920845e+234 */, "2920845", 228);
|
||||
check_double(make_double(5381065484265332, -456) /* 2.8919465e-122 */, "28919465", -129);
|
||||
check_double(make_double(6761728585499734, -1057) /* 4.37877185e-303 */, "437877185", -311);
|
||||
check_double(make_double(7976538478610756, +376) /* 1.227701635e+129 */, "1227701635", 120);
|
||||
check_double(make_double(5982403858958067, +377) /* 1.8415524525e+129 */, "18415524525", 119);
|
||||
check_double(make_double(5536995190630837, +93) /* 5.48357443505e+043 */, "548357443505", 32);
|
||||
check_double(make_double(7225450889282194, +710) /* 3.891901811465e+229 */, "3891901811465", 217);
|
||||
check_double(make_double(7225450889282194, +709) /* 1.9459509057325e+229 */, "19459509057325", 216);
|
||||
check_double(make_double(8703372741147379, +117) /* 1.44609583816055e+051 */, "144609583816055", 37);
|
||||
check_double(make_double(8944262675275217, -1001) /* 4.173677474585315e-286 */, "4173677474585315", -301);
|
||||
check_double(make_double(7459803696087692, -707) /* 1.1079507728788885e-197 */, "11079507728788885", -213);
|
||||
check_double(make_double(6080469016670379, -381) /* 1.234550136632744e-099 */, "1234550136632744", -114);
|
||||
check_double(make_double(8385515147034757, +721) /* 9.2503171196036502e+232 */, "925031711960365", 218);
|
||||
check_double(make_double(7514216811389786, -828) /* 4.1980471502848898e-234 */, "419804715028489", -248);
|
||||
check_double(make_double(8397297803260511, -345) /* 1.1716315319786511e-088 */, "11716315319786511", -104);
|
||||
check_double(make_double(6733459239310543, +202) /* 4.3281007284461249e+076 */, "4328100728446125", 61);
|
||||
check_double(make_double(8091450587292794, -473) /* 3.3177101181600311e-127 */, "3317710118160031", -142);
|
||||
|
||||
// Table 4: Stress Inputs for Converting 53-bit Binary to Decimal, > 1/2 ULP
|
||||
check_double(make_double(6567258882077402, +952) /* 2.5e+302 */, "25", 301);
|
||||
check_double(make_double(6712731423444934, +535) /* 7.55e+176 */, "755", 174);
|
||||
check_double(make_double(6712731423444934, +534) /* 3.775e+176 */, "3775", 173);
|
||||
check_double(make_double(5298405411573037, -957) /* 4.3495e-273 */, "43495", -277);
|
||||
check_double(make_double(5137311167659507, -144) /* 2.30365e-028 */, "230365", -33);
|
||||
check_double(make_double(6722280709661868, +363) /* 1.263005e+125 */, "1263005", 119);
|
||||
check_double(make_double(5344436398034927, -169) /* 7.1422105e-036 */, "71422105", -43);
|
||||
check_double(make_double(8369123604277281, -853) /* 1.39345735e-241 */, "139345735", -249);
|
||||
check_double(make_double(8995822108487663, -780) /* 1.414634485e-219 */, "1414634485", -228);
|
||||
check_double(make_double(8942832835564782, -383) /* 4.5392779195e-100 */, "45392779195", -110);
|
||||
check_double(make_double(8942832835564782, -384) /* 2.26963895975e-100 */, "226963895975", -111);
|
||||
check_double(make_double(8942832835564782, -385) /* 1.134819479875e-100 */, "1134819479875", -112);
|
||||
check_double(make_double(6965949469487146, -249) /* 7.7003665618895e-060 */, "77003665618895", -73);
|
||||
check_double(make_double(6965949469487146, -250) /* 3.85018328094475e-060 */, "385018328094475", -74);
|
||||
check_double(make_double(6965949469487146, -251) /* 1.925091640472375e-060 */, "1925091640472375", -75);
|
||||
check_double(make_double(7487252720986826, +548) /* 6.8985865317742005e+180 */, "68985865317742005", 164);
|
||||
check_double(make_double(5592117679628511, +164) /* 1.3076622631878654e+065 */, "13076622631878654", 49);
|
||||
check_double(make_double(8887055249355788, +665) /* 1.3605202075612124e+216 */, "13605202075612124", 200);
|
||||
check_double(make_double(6994187472632449, +690) /* 3.5928102174759597e+223 */, "35928102174759597", 207);
|
||||
check_double(make_double(8797576579012143, +588) /* 8.9125197712484552e+192 */, "8912519771248455", 177);
|
||||
check_double(make_double(7363326733505337, +272) /* 5.5876975736230114e+097 */, "55876975736230114", 81);
|
||||
check_double(make_double(8549497411294502, -448) /* 1.1762578307285404e-119 */, "11762578307285404", -135);
|
||||
|
||||
// Table 20: Stress Inputs for Converting 56-bit Binary to Decimal, < 1/2 ULP
|
||||
check_double(make_double(50883641005312716, -172) /* 8.4999999999999993e-036 */, "8499999999999999", -51);
|
||||
check_double(make_double(38162730753984537, -170) /* 2.5499999999999999e-035 */, "255", -37);
|
||||
check_double(make_double(50832789069151999, -101) /* 2.0049999999999997e-014 */, "20049999999999997", -30);
|
||||
check_double(make_double(51822367833714164, -109) /* 7.9844999999999994e-017 */, "7984499999999999", -32);
|
||||
check_double(make_double(66840152193508133, -172) /* 1.1165499999999999e-035 */, "11165499999999999", -51);
|
||||
check_double(make_double(55111239245584393, -138) /* 1.581615e-025 */, "1581615", -31);
|
||||
check_double(make_double(71704866733321482, -112) /* 1.3809855e-017 */, "13809855", -24);
|
||||
check_double(make_double(67160949328233173, -142) /* 1.2046404499999999e-026 */, "12046404499999999", -42);
|
||||
check_double(make_double(53237141308040189, -152) /* 9.3251405449999991e-030 */, "9325140544999999", -45);
|
||||
check_double(make_double(62785329394975786, -112) /* 1.2092014595e-017 */, "12092014595", -27);
|
||||
check_double(make_double(48367680154689523, -77) /* 3.2007045838499998e-007 */, "320070458385", -18);
|
||||
check_double(make_double(42552223180606797, -102) /* 8.391946324354999e-015 */, "8391946324354999", -30);
|
||||
check_double(make_double(63626356173011241, -112) /* 1.2253990460585e-017 */, "12253990460585", -30);
|
||||
check_double(make_double(43566388595783643, -99) /* 6.8735641489760495e-014 */, "687356414897605", -28);
|
||||
check_double(make_double(54512669636675272, -159) /* 7.459816430480385e-032 */, "7459816430480385", -47);
|
||||
check_double(make_double(52306490527514614, -167) /* 2.7960588398142552e-034 */, "2796058839814255", -49);
|
||||
check_double(make_double(52306490527514614, -168) /* 1.3980294199071276e-034 */, "13980294199071276", -50);
|
||||
check_double(make_double(41024721590449423, -89) /* 6.6279012373057359e-011 */, "6627901237305736", -26);
|
||||
check_double(make_double(37664020415894738, -132) /* 6.9177880043968072e-024 */, "6917788004396807", -39);
|
||||
check_double(make_double(37549883692866294, -93) /* 3.7915693108349708e-012 */, "3791569310834971", -27);
|
||||
check_double(make_double(69124110374399839, -104) /* 3.4080817676591365e-015 */, "34080817676591365", -31);
|
||||
check_double(make_double(69124110374399839, -105) /* 1.7040408838295683e-015 */, "17040408838295683", -31);
|
||||
|
||||
// Table 21: Stress Inputs for Converting 56-bit Binary to Decimal, > 1/2 ULP
|
||||
check_double(make_double(49517601571415211, -94) /* 2.4999999999999998e-012 */, "25", -13);
|
||||
check_double(make_double(49517601571415211, -95) /* 1.2499999999999999e-012 */, "125", -14);
|
||||
check_double(make_double(54390733528642804, -133) /* 4.9949999999999996e-024 */, "49949999999999996", -40); // shortest: 4995e-27
|
||||
check_double(make_double(71805402319113924, -157) /* 3.9304999999999998e-031 */, "39304999999999998", -47); // shortest: 39305e-35
|
||||
check_double(make_double(40435277969631694, -179) /* 5.2770499999999992e-038 */, "5277049999999999", -53);
|
||||
check_double(make_double(57241991568619049, -165) /* 1.223955e-033 */, "1223955", -39);
|
||||
check_double(make_double(65224162876242886, +58) /* 1.8799584999999998e+034 */, "18799584999999998", 18);
|
||||
check_double(make_double(70173376848895368, -138) /* 2.01387715e-025 */, "201387715", -33);
|
||||
check_double(make_double(37072848117383207, -99) /* 5.8490641049999989e-014 */, "5849064104999999", -29);
|
||||
check_double(make_double(56845051585389697, -176) /* 5.9349003054999999e-037 */, "59349003055", -47);
|
||||
check_double(make_double(54791673366936431, -145) /* 1.2284718039499998e-027 */, "12284718039499998", -43);
|
||||
check_double(make_double(66800318669106231, -169) /* 8.9270767180849991e-035 */, "8927076718084999", -50);
|
||||
check_double(make_double(66800318669106231, -170) /* 4.4635383590424995e-035 */, "44635383590424995", -51);
|
||||
check_double(make_double(66574323440112438, -119) /* 1.0016990862549499e-019 */, "10016990862549499", -35);
|
||||
check_double(make_double(65645179969330963, -173) /* 5.4829412628024647e-036 */, "5482941262802465", -51);
|
||||
check_double(make_double(61847254334681076, -109) /* 9.5290783281036439e-017 */, "9529078328103644", -32);
|
||||
check_double(make_double(39990712921393606, -145) /* 8.9662279366405553e-028 */, "8966227936640555", -43);
|
||||
check_double(make_double(59292318184400283, -149) /* 8.3086234418058538e-029 */, "8308623441805854", -44);
|
||||
check_double(make_double(69116558615326153, -143) /* 6.1985873566126555e-027 */, "61985873566126555", -43);
|
||||
check_double(make_double(69116558615326153, -144) /* 3.0992936783063277e-027 */, "30992936783063277", -43);
|
||||
check_double(make_double(39462549494468513, -152) /* 6.9123512506176015e-030 */, "6912351250617602", -45);
|
||||
check_double(make_double(39462549494468513, -153) /* 3.4561756253088008e-030 */, "3456175625308801", -45);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("formatting")
|
||||
{
|
||||
SECTION("single precision")
|
||||
{
|
||||
auto check_float = [](float number, const std::string & expected)
|
||||
{
|
||||
std::array<char, 33> buf{};
|
||||
char* end = nlohmann::detail::to_chars(buf.data(), buf.data() + 32, number); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||
std::string actual(buf.data(), end);
|
||||
|
||||
CHECK(actual == expected);
|
||||
};
|
||||
// %.9g
|
||||
check_float( -1.2345e-22f, "-1.2345e-22" ); // -1.23450004e-22
|
||||
check_float( -1.2345e-21f, "-1.2345e-21" ); // -1.23450002e-21
|
||||
check_float( -1.2345e-20f, "-1.2345e-20" ); // -1.23450002e-20
|
||||
check_float( -1.2345e-19f, "-1.2345e-19" ); // -1.23449999e-19
|
||||
check_float( -1.2345e-18f, "-1.2345e-18" ); // -1.23449996e-18
|
||||
check_float( -1.2345e-17f, "-1.2345e-17" ); // -1.23449998e-17
|
||||
check_float( -1.2345e-16f, "-1.2345e-16" ); // -1.23449996e-16
|
||||
check_float( -1.2345e-15f, "-1.2345e-15" ); // -1.23450002e-15
|
||||
check_float( -1.2345e-14f, "-1.2345e-14" ); // -1.23450004e-14
|
||||
check_float( -1.2345e-13f, "-1.2345e-13" ); // -1.23449997e-13
|
||||
check_float( -1.2345e-12f, "-1.2345e-12" ); // -1.23450002e-12
|
||||
check_float( -1.2345e-11f, "-1.2345e-11" ); // -1.2345e-11
|
||||
check_float( -1.2345e-10f, "-1.2345e-10" ); // -1.2345e-10
|
||||
check_float( -1.2345e-9f, "-1.2345e-09" ); // -1.23449995e-09
|
||||
check_float( -1.2345e-8f, "-1.2345e-08" ); // -1.23449997e-08
|
||||
check_float( -1.2345e-7f, "-1.2345e-07" ); // -1.23449993e-07
|
||||
check_float( -1.2345e-6f, "-1.2345e-06" ); // -1.23450002e-06
|
||||
check_float( -1.2345e-5f, "-1.2345e-05" ); // -1.2345e-05
|
||||
check_float( -1.2345e-4f, "-0.00012345" ); // -0.000123449994
|
||||
check_float( -1.2345e-3f, "-0.0012345" ); // -0.00123449997
|
||||
check_float( -1.2345e-2f, "-0.012345" ); // -0.0123450002
|
||||
check_float( -1.2345e-1f, "-0.12345" ); // -0.123450004
|
||||
check_float( -0.0f, "-0.0" ); // -0
|
||||
check_float( 0.0f, "0.0" ); // 0
|
||||
check_float( 1.2345e+0f, "1.2345" ); // 1.23450005
|
||||
check_float( 1.2345e+1f, "12.345" ); // 12.3450003
|
||||
check_float( 1.2345e+2f, "123.45" ); // 123.449997
|
||||
check_float( 1.2345e+3f, "1234.5" ); // 1234.5
|
||||
check_float( 1.2345e+4f, "12345.0" ); // 12345
|
||||
check_float( 1.2345e+5f, "123450.0" ); // 123450
|
||||
check_float( 1.2345e+6f, "1.2345e+06" ); // 1234500
|
||||
check_float( 1.2345e+7f, "1.2345e+07" ); // 12345000
|
||||
check_float( 1.2345e+8f, "1.2345e+08" ); // 123450000
|
||||
check_float( 1.2345e+9f, "1.2345e+09" ); // 1.23449997e+09
|
||||
check_float( 1.2345e+10f, "1.2345e+10" ); // 1.23449999e+10
|
||||
check_float( 1.2345e+11f, "1.2345e+11" ); // 1.23449999e+11
|
||||
check_float( 1.2345e+12f, "1.2345e+12" ); // 1.23450006e+12
|
||||
check_float( 1.2345e+13f, "1.2345e+13" ); // 1.23449995e+13
|
||||
check_float( 1.2345e+14f, "1.2345e+14" ); // 1.23450002e+14
|
||||
check_float( 1.2345e+15f, "1.2345e+15" ); // 1.23450003e+15
|
||||
check_float( 1.2345e+16f, "1.2345e+16" ); // 1.23449998e+16
|
||||
check_float( 1.2345e+17f, "1.2345e+17" ); // 1.23449996e+17
|
||||
check_float( 1.2345e+18f, "1.2345e+18" ); // 1.23450004e+18
|
||||
check_float( 1.2345e+19f, "1.2345e+19" ); // 1.23449999e+19
|
||||
check_float( 1.2345e+20f, "1.2345e+20" ); // 1.23449999e+20
|
||||
check_float( 1.2345e+21f, "1.2345e+21" ); // 1.23449999e+21
|
||||
check_float( 1.2345e+22f, "1.2345e+22" ); // 1.23450005e+22
|
||||
}
|
||||
|
||||
SECTION("double precision")
|
||||
{
|
||||
auto check_double = [](double number, const std::string & expected)
|
||||
{
|
||||
std::array<char, 33> buf{};
|
||||
char* end = nlohmann::detail::to_chars(buf.data(), buf.data() + 32, number); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||
std::string actual(buf.data(), end);
|
||||
|
||||
CHECK(actual == expected);
|
||||
};
|
||||
// dtoa %.15g %.17g shortest
|
||||
check_double( -1.2345e-22, "-1.2345e-22" ); // -1.2345e-22 -1.2345000000000001e-22 -1.2345e-22
|
||||
check_double( -1.2345e-21, "-1.2345e-21" ); // -1.2345e-21 -1.2345000000000001e-21 -1.2345e-21
|
||||
check_double( -1.2345e-20, "-1.2345e-20" ); // -1.2345e-20 -1.2345e-20 -1.2345e-20
|
||||
check_double( -1.2345e-19, "-1.2345e-19" ); // -1.2345e-19 -1.2345000000000001e-19 -1.2345e-19
|
||||
check_double( -1.2345e-18, "-1.2345e-18" ); // -1.2345e-18 -1.2345000000000001e-18 -1.2345e-18
|
||||
check_double( -1.2345e-17, "-1.2345e-17" ); // -1.2345e-17 -1.2345e-17 -1.2345e-17
|
||||
check_double( -1.2345e-16, "-1.2345e-16" ); // -1.2345e-16 -1.2344999999999999e-16 -1.2345e-16
|
||||
check_double( -1.2345e-15, "-1.2345e-15" ); // -1.2345e-15 -1.2345e-15 -1.2345e-15
|
||||
check_double( -1.2345e-14, "-1.2345e-14" ); // -1.2345e-14 -1.2345e-14 -1.2345e-14
|
||||
check_double( -1.2345e-13, "-1.2345e-13" ); // -1.2345e-13 -1.2344999999999999e-13 -1.2345e-13
|
||||
check_double( -1.2345e-12, "-1.2345e-12" ); // -1.2345e-12 -1.2345e-12 -1.2345e-12
|
||||
check_double( -1.2345e-11, "-1.2345e-11" ); // -1.2345e-11 -1.2345e-11 -1.2345e-11
|
||||
check_double( -1.2345e-10, "-1.2345e-10" ); // -1.2345e-10 -1.2345e-10 -1.2345e-10
|
||||
check_double( -1.2345e-9, "-1.2345e-09" ); // -1.2345e-09 -1.2345e-09 -1.2345e-9
|
||||
check_double( -1.2345e-8, "-1.2345e-08" ); // -1.2345e-08 -1.2345000000000001e-08 -1.2345e-8
|
||||
check_double( -1.2345e-7, "-1.2345e-07" ); // -1.2345e-07 -1.2345000000000001e-07 -1.2345e-7
|
||||
check_double( -1.2345e-6, "-1.2345e-06" ); // -1.2345e-06 -1.2345e-06 -1.2345e-6
|
||||
check_double( -1.2345e-5, "-1.2345e-05" ); // -1.2345e-05 -1.2345e-05 -1.2345e-5
|
||||
check_double( -1.2345e-4, "-0.00012345" ); // -0.00012345 -0.00012344999999999999 -0.00012345
|
||||
check_double( -1.2345e-3, "-0.0012345" ); // -0.0012345 -0.0012344999999999999 -0.0012345
|
||||
check_double( -1.2345e-2, "-0.012345" ); // -0.012345 -0.012345 -0.012345
|
||||
check_double( -1.2345e-1, "-0.12345" ); // -0.12345 -0.12345 -0.12345
|
||||
check_double( -0.0, "-0.0" ); // -0 -0 -0
|
||||
check_double( 0.0, "0.0" ); // 0 0 0
|
||||
check_double( 1.2345e+0, "1.2345" ); // 1.2345 1.2344999999999999 1.2345
|
||||
check_double( 1.2345e+1, "12.345" ); // 12.345 12.345000000000001 12.345
|
||||
check_double( 1.2345e+2, "123.45" ); // 123.45 123.45 123.45
|
||||
check_double( 1.2345e+3, "1234.5" ); // 1234.5 1234.5 1234.5
|
||||
check_double( 1.2345e+4, "12345.0" ); // 12345 12345 12345
|
||||
check_double( 1.2345e+5, "123450.0" ); // 123450 123450 123450
|
||||
check_double( 1.2345e+6, "1234500.0" ); // 1234500 1234500 1234500
|
||||
check_double( 1.2345e+7, "12345000.0" ); // 12345000 12345000 12345000
|
||||
check_double( 1.2345e+8, "123450000.0" ); // 123450000 123450000 123450000
|
||||
check_double( 1.2345e+9, "1234500000.0" ); // 1234500000 1234500000 1234500000
|
||||
check_double( 1.2345e+10, "12345000000.0" ); // 12345000000 12345000000 12345000000
|
||||
check_double( 1.2345e+11, "123450000000.0" ); // 123450000000 123450000000 123450000000
|
||||
check_double( 1.2345e+12, "1234500000000.0" ); // 1234500000000 1234500000000 1234500000000
|
||||
check_double( 1.2345e+13, "12345000000000.0" ); // 12345000000000 12345000000000 12345000000000
|
||||
check_double( 1.2345e+14, "123450000000000.0" ); // 123450000000000 123450000000000 123450000000000
|
||||
check_double( 1.2345e+15, "1.2345e+15" ); // 1.2345e+15 1234500000000000 1.2345e15
|
||||
check_double( 1.2345e+16, "1.2345e+16" ); // 1.2345e+16 12345000000000000 1.2345e16
|
||||
check_double( 1.2345e+17, "1.2345e+17" ); // 1.2345e+17 1.2345e+17 1.2345e17
|
||||
check_double( 1.2345e+18, "1.2345e+18" ); // 1.2345e+18 1.2345e+18 1.2345e18
|
||||
check_double( 1.2345e+19, "1.2345e+19" ); // 1.2345e+19 1.2345e+19 1.2345e19
|
||||
check_double( 1.2345e+20, "1.2345e+20" ); // 1.2345e+20 1.2345e+20 1.2345e20
|
||||
check_double( 1.2345e+21, "1.2344999999999999e+21" ); // 1.2345e+21 1.2344999999999999e+21 1.2345e21
|
||||
check_double( 1.2345e+22, "1.2345e+22" ); // 1.2345e+22 1.2345e+22 1.2345e22
|
||||
}
|
||||
|
||||
SECTION("integer")
|
||||
{
|
||||
auto check_integer = [](std::int64_t number, const std::string & expected)
|
||||
{
|
||||
nlohmann::json j = number;
|
||||
CHECK(j.dump() == expected);
|
||||
};
|
||||
|
||||
// edge cases
|
||||
check_integer(INT64_MIN, "-9223372036854775808");
|
||||
check_integer(INT64_MAX, "9223372036854775807");
|
||||
|
||||
// few random big integers
|
||||
check_integer(-3456789012345678901LL, "-3456789012345678901");
|
||||
check_integer(3456789012345678901LL, "3456789012345678901");
|
||||
check_integer(-5678901234567890123LL, "-5678901234567890123");
|
||||
check_integer(5678901234567890123LL, "5678901234567890123");
|
||||
|
||||
// integers with various digit counts
|
||||
check_integer(-1000000000000000000LL, "-1000000000000000000");
|
||||
check_integer(-100000000000000000LL, "-100000000000000000");
|
||||
check_integer(-10000000000000000LL, "-10000000000000000");
|
||||
check_integer(-1000000000000000LL, "-1000000000000000");
|
||||
check_integer(-100000000000000LL, "-100000000000000");
|
||||
check_integer(-10000000000000LL, "-10000000000000");
|
||||
check_integer(-1000000000000LL, "-1000000000000");
|
||||
check_integer(-100000000000LL, "-100000000000");
|
||||
check_integer(-10000000000LL, "-10000000000");
|
||||
check_integer(-1000000000LL, "-1000000000");
|
||||
check_integer(-100000000LL, "-100000000");
|
||||
check_integer(-10000000LL, "-10000000");
|
||||
check_integer(-1000000LL, "-1000000");
|
||||
check_integer(-100000LL, "-100000");
|
||||
check_integer(-10000LL, "-10000");
|
||||
check_integer(-1000LL, "-1000");
|
||||
check_integer(-100LL, "-100");
|
||||
check_integer(-10LL, "-10");
|
||||
check_integer(-1LL, "-1");
|
||||
check_integer(0, "0");
|
||||
check_integer(1LL, "1");
|
||||
check_integer(10LL, "10");
|
||||
check_integer(100LL, "100");
|
||||
check_integer(1000LL, "1000");
|
||||
check_integer(10000LL, "10000");
|
||||
check_integer(100000LL, "100000");
|
||||
check_integer(1000000LL, "1000000");
|
||||
check_integer(10000000LL, "10000000");
|
||||
check_integer(100000000LL, "100000000");
|
||||
check_integer(1000000000LL, "1000000000");
|
||||
check_integer(10000000000LL, "10000000000");
|
||||
check_integer(100000000000LL, "100000000000");
|
||||
check_integer(1000000000000LL, "1000000000000");
|
||||
check_integer(10000000000000LL, "10000000000000");
|
||||
check_integer(100000000000000LL, "100000000000000");
|
||||
check_integer(1000000000000000LL, "1000000000000000");
|
||||
check_integer(10000000000000000LL, "10000000000000000");
|
||||
check_integer(100000000000000000LL, "100000000000000000");
|
||||
check_integer(1000000000000000000LL, "1000000000000000000");
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,882 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
// disable -Wnoexcept due to class Evil
|
||||
DOCTEST_GCC_SUPPRESS_WARNING_PUSH
|
||||
DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept")
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace udt
|
||||
{
|
||||
enum class country
|
||||
{
|
||||
china,
|
||||
france,
|
||||
russia
|
||||
};
|
||||
|
||||
struct age
|
||||
{
|
||||
int m_val;
|
||||
age(int rhs = 0) : m_val(rhs) {}
|
||||
};
|
||||
|
||||
struct name
|
||||
{
|
||||
std::string m_val;
|
||||
name(std::string rhs = "") : m_val(std::move(rhs)) {}
|
||||
};
|
||||
|
||||
struct address
|
||||
{
|
||||
std::string m_val;
|
||||
address(std::string rhs = "") : m_val(std::move(rhs)) {}
|
||||
};
|
||||
|
||||
struct person
|
||||
{
|
||||
age m_age{};
|
||||
name m_name{};
|
||||
country m_country{};
|
||||
person() = default;
|
||||
person(const age& a, name n, const country& c) : m_age(a), m_name(std::move(n)), m_country(c) {}
|
||||
};
|
||||
|
||||
struct contact
|
||||
{
|
||||
person m_person{};
|
||||
address m_address{};
|
||||
contact() = default;
|
||||
contact(person p, address a) : m_person(std::move(p)), m_address(std::move(a)) {}
|
||||
};
|
||||
|
||||
struct contact_book
|
||||
{
|
||||
name m_book_name{};
|
||||
std::vector<contact> m_contacts{};
|
||||
contact_book() = default;
|
||||
contact_book(name n, std::vector<contact> c) : m_book_name(std::move(n)), m_contacts(std::move(c)) {}
|
||||
};
|
||||
} // namespace udt
|
||||
|
||||
// to_json methods
|
||||
namespace udt
|
||||
{
|
||||
// templates because of the custom_json tests (see below)
|
||||
template <typename BasicJsonType>
|
||||
static void to_json(BasicJsonType& j, age a)
|
||||
{
|
||||
j = a.m_val;
|
||||
}
|
||||
|
||||
template <typename BasicJsonType>
|
||||
static void to_json(BasicJsonType& j, const name& n)
|
||||
{
|
||||
j = n.m_val;
|
||||
}
|
||||
|
||||
template <typename BasicJsonType>
|
||||
static void to_json(BasicJsonType& j, country c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case country::china:
|
||||
j = "中华人民共和国";
|
||||
return;
|
||||
case country::france:
|
||||
j = "France";
|
||||
return;
|
||||
case country::russia:
|
||||
j = "Российская Федерация";
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename BasicJsonType>
|
||||
static void to_json(BasicJsonType& j, const person& p)
|
||||
{
|
||||
j = BasicJsonType{{"age", p.m_age}, {"name", p.m_name}, {"country", p.m_country}};
|
||||
}
|
||||
|
||||
static void to_json(nlohmann::json& j, const address& a)
|
||||
{
|
||||
j = a.m_val;
|
||||
}
|
||||
|
||||
static void to_json(nlohmann::json& j, const contact& c)
|
||||
{
|
||||
j = json{{"person", c.m_person}, {"address", c.m_address}};
|
||||
}
|
||||
|
||||
static void to_json(nlohmann::json& j, const contact_book& cb)
|
||||
{
|
||||
j = json{{"name", cb.m_book_name}, {"contacts", cb.m_contacts}};
|
||||
}
|
||||
|
||||
// operators
|
||||
static bool operator==(age lhs, age rhs)
|
||||
{
|
||||
return lhs.m_val == rhs.m_val;
|
||||
}
|
||||
|
||||
static bool operator==(const address& lhs, const address& rhs)
|
||||
{
|
||||
return lhs.m_val == rhs.m_val;
|
||||
}
|
||||
|
||||
static bool operator==(const name& lhs, const name& rhs)
|
||||
{
|
||||
return lhs.m_val == rhs.m_val;
|
||||
}
|
||||
|
||||
static bool operator==(const person& lhs, const person& rhs)
|
||||
{
|
||||
return std::tie(lhs.m_name, lhs.m_age) == std::tie(rhs.m_name, rhs.m_age);
|
||||
}
|
||||
|
||||
static bool operator==(const contact& lhs, const contact& rhs)
|
||||
{
|
||||
return std::tie(lhs.m_person, lhs.m_address) ==
|
||||
std::tie(rhs.m_person, rhs.m_address);
|
||||
}
|
||||
|
||||
static bool operator==(const contact_book& lhs, const contact_book& rhs)
|
||||
{
|
||||
return std::tie(lhs.m_book_name, lhs.m_contacts) ==
|
||||
std::tie(rhs.m_book_name, rhs.m_contacts);
|
||||
}
|
||||
} // namespace udt
|
||||
|
||||
// from_json methods
|
||||
namespace udt
|
||||
{
|
||||
template <typename BasicJsonType>
|
||||
static void from_json(const BasicJsonType& j, age& a)
|
||||
{
|
||||
a.m_val = j.template get<int>();
|
||||
}
|
||||
|
||||
template <typename BasicJsonType>
|
||||
static void from_json(const BasicJsonType& j, name& n)
|
||||
{
|
||||
n.m_val = j.template get<std::string>();
|
||||
}
|
||||
|
||||
template <typename BasicJsonType>
|
||||
static void from_json(const BasicJsonType& j, country& c)
|
||||
{
|
||||
const auto str = j.template get<std::string>();
|
||||
const std::map<std::string, country> m =
|
||||
{
|
||||
{"中华人民共和国", country::china},
|
||||
{"France", country::france},
|
||||
{"Российская Федерация", country::russia}
|
||||
};
|
||||
|
||||
const auto it = m.find(str);
|
||||
// TODO(nlohmann) test exceptions
|
||||
c = it->second;
|
||||
}
|
||||
|
||||
template <typename BasicJsonType>
|
||||
static void from_json(const BasicJsonType& j, person& p)
|
||||
{
|
||||
p.m_age = j["age"].template get<age>();
|
||||
p.m_name = j["name"].template get<name>();
|
||||
p.m_country = j["country"].template get<country>();
|
||||
}
|
||||
|
||||
static void from_json(const nlohmann::json& j, address& a)
|
||||
{
|
||||
a.m_val = j.get<std::string>();
|
||||
}
|
||||
|
||||
static void from_json(const nlohmann::json& j, contact& c)
|
||||
{
|
||||
c.m_person = j["person"].get<person>();
|
||||
c.m_address = j["address"].get<address>();
|
||||
}
|
||||
|
||||
static void from_json(const nlohmann::json& j, contact_book& cb)
|
||||
{
|
||||
cb.m_book_name = j["name"].get<name>();
|
||||
cb.m_contacts = j["contacts"].get<std::vector<contact>>();
|
||||
}
|
||||
} // namespace udt
|
||||
|
||||
TEST_CASE("basic usage" * doctest::test_suite("udt"))
|
||||
{
|
||||
|
||||
// a bit narcissistic maybe :) ?
|
||||
const udt::age a
|
||||
{
|
||||
23
|
||||
};
|
||||
const udt::name n{"theo"};
|
||||
const udt::country c{udt::country::france};
|
||||
const udt::person sfinae_addict{a, n, c};
|
||||
const udt::person senior_programmer{{42}, {"王芳"}, udt::country::china};
|
||||
const udt::address addr{"Paris"};
|
||||
const udt::contact cpp_programmer{sfinae_addict, addr};
|
||||
const udt::contact_book book{{"C++"}, {cpp_programmer, {senior_programmer, addr}}};
|
||||
|
||||
SECTION("conversion to json via free-functions")
|
||||
{
|
||||
CHECK(json(a) == json(23));
|
||||
CHECK(json(n) == json("theo"));
|
||||
CHECK(json(c) == json("France"));
|
||||
CHECK(json(sfinae_addict) == R"({"name":"theo", "age":23, "country":"France"})"_json);
|
||||
CHECK(json("Paris") == json(addr));
|
||||
CHECK(json(cpp_programmer) ==
|
||||
R"({"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"})"_json);
|
||||
|
||||
CHECK(
|
||||
json(book) ==
|
||||
R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"中华人民共和国", "name":"王芳"}, "address":"Paris"}]})"_json);
|
||||
|
||||
}
|
||||
|
||||
SECTION("conversion from json via free-functions")
|
||||
{
|
||||
const auto big_json =
|
||||
R"({"name":"C++", "contacts" : [{"person" : {"age":23, "name":"theo", "country":"France"}, "address":"Paris"}, {"person" : {"age":42, "country":"中华人民共和国", "name":"王芳"}, "address":"Paris"}]})"_json;
|
||||
SECTION("via explicit calls to get")
|
||||
{
|
||||
const auto parsed_book = big_json.get<udt::contact_book>();
|
||||
const auto book_name = big_json["name"].get<udt::name>();
|
||||
const auto contacts =
|
||||
big_json["contacts"].get<std::vector<udt::contact>>();
|
||||
const auto contact_json = big_json["contacts"].at(0);
|
||||
const auto contact = contact_json.get<udt::contact>();
|
||||
const auto person = contact_json["person"].get<udt::person>();
|
||||
const auto address = contact_json["address"].get<udt::address>();
|
||||
const auto age = contact_json["person"]["age"].get<udt::age>();
|
||||
const auto country =
|
||||
contact_json["person"]["country"].get<udt::country>();
|
||||
const auto name = contact_json["person"]["name"].get<udt::name>();
|
||||
|
||||
CHECK(age == a);
|
||||
CHECK(name == n);
|
||||
CHECK(country == c);
|
||||
CHECK(address == addr);
|
||||
CHECK(person == sfinae_addict);
|
||||
CHECK(contact == cpp_programmer);
|
||||
CHECK(contacts == book.m_contacts);
|
||||
CHECK(book_name == udt::name{"C++"});
|
||||
CHECK(book == parsed_book);
|
||||
}
|
||||
|
||||
SECTION("via explicit calls to get_to")
|
||||
{
|
||||
udt::person person;
|
||||
udt::name name;
|
||||
|
||||
json person_json = big_json["contacts"][0]["person"];
|
||||
CHECK(person_json.get_to(person) == sfinae_addict);
|
||||
|
||||
// correct reference gets returned
|
||||
person_json["name"].get_to(name).m_val = "new name";
|
||||
CHECK(name.m_val == "new name");
|
||||
}
|
||||
|
||||
#if JSON_USE_IMPLICIT_CONVERSIONS
|
||||
SECTION("implicit conversions")
|
||||
{
|
||||
const udt::contact_book parsed_book = big_json;
|
||||
const udt::name book_name = big_json["name"];
|
||||
const std::vector<udt::contact> contacts = big_json["contacts"];
|
||||
const auto contact_json = big_json["contacts"].at(0);
|
||||
const udt::contact contact = contact_json;
|
||||
const udt::person person = contact_json["person"];
|
||||
const udt::address address = contact_json["address"];
|
||||
const udt::age age = contact_json["person"]["age"];
|
||||
const udt::country country = contact_json["person"]["country"];
|
||||
const udt::name name = contact_json["person"]["name"];
|
||||
|
||||
CHECK(age == a);
|
||||
CHECK(name == n);
|
||||
CHECK(country == c);
|
||||
CHECK(address == addr);
|
||||
CHECK(person == sfinae_addict);
|
||||
CHECK(contact == cpp_programmer);
|
||||
CHECK(contacts == book.m_contacts);
|
||||
CHECK(book_name == udt::name{"C++"});
|
||||
CHECK(book == parsed_book);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
namespace udt
|
||||
{
|
||||
struct legacy_type
|
||||
{
|
||||
std::string number{};
|
||||
legacy_type() = default;
|
||||
legacy_type(std::string n) : number(std::move(n)) {}
|
||||
};
|
||||
} // namespace udt
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
template <typename T>
|
||||
struct adl_serializer<std::shared_ptr<T>>
|
||||
{
|
||||
static void to_json(json& j, const std::shared_ptr<T>& opt)
|
||||
{
|
||||
if (opt)
|
||||
{
|
||||
j = *opt;
|
||||
}
|
||||
else
|
||||
{
|
||||
j = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void from_json(const json& j, std::shared_ptr<T>& opt)
|
||||
{
|
||||
if (j.is_null())
|
||||
{
|
||||
opt = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
opt.reset(new T(j.get<T>())); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct adl_serializer<udt::legacy_type>
|
||||
{
|
||||
static void to_json(json& j, const udt::legacy_type& l)
|
||||
{
|
||||
j = std::stoi(l.number);
|
||||
}
|
||||
|
||||
static void from_json(const json& j, udt::legacy_type& l)
|
||||
{
|
||||
l.number = std::to_string(j.get<int>());
|
||||
}
|
||||
};
|
||||
} // namespace nlohmann
|
||||
|
||||
TEST_CASE("adl_serializer specialization" * doctest::test_suite("udt"))
|
||||
{
|
||||
SECTION("partial specialization")
|
||||
{
|
||||
SECTION("to_json")
|
||||
{
|
||||
std::shared_ptr<udt::person> optPerson;
|
||||
|
||||
json j = optPerson;
|
||||
CHECK(j.is_null());
|
||||
|
||||
optPerson.reset(new udt::person{{42}, {"John Doe"}, udt::country::russia}); // NOLINT(cppcoreguidelines-owning-memory,modernize-make-shared)
|
||||
j = optPerson;
|
||||
CHECK_FALSE(j.is_null());
|
||||
|
||||
CHECK(j.get<udt::person>() == *optPerson);
|
||||
}
|
||||
|
||||
SECTION("from_json")
|
||||
{
|
||||
auto person = udt::person{{42}, {"John Doe"}, udt::country::russia};
|
||||
json j = person;
|
||||
|
||||
auto optPerson = j.get<std::shared_ptr<udt::person>>();
|
||||
REQUIRE(optPerson);
|
||||
CHECK(*optPerson == person);
|
||||
|
||||
j = nullptr;
|
||||
optPerson = j.get<std::shared_ptr<udt::person>>();
|
||||
CHECK(!optPerson);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("total specialization")
|
||||
{
|
||||
SECTION("to_json")
|
||||
{
|
||||
udt::legacy_type lt{"4242"};
|
||||
|
||||
json j = lt;
|
||||
CHECK(j.get<int>() == 4242);
|
||||
}
|
||||
|
||||
SECTION("from_json")
|
||||
{
|
||||
json j = 4242;
|
||||
auto lt = j.get<udt::legacy_type>();
|
||||
CHECK(lt.number == "4242");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
template <>
|
||||
struct adl_serializer<std::vector<float>>
|
||||
{
|
||||
using type = std::vector<float>;
|
||||
static void to_json(json& j, const type& /*type*/)
|
||||
{
|
||||
j = "hijacked!";
|
||||
}
|
||||
|
||||
static void from_json(const json& /*unnamed*/, type& opt)
|
||||
{
|
||||
opt = {42.0, 42.0, 42.0};
|
||||
}
|
||||
|
||||
// preferred version
|
||||
static type from_json(const json& /*unnamed*/)
|
||||
{
|
||||
return {4.0, 5.0, 6.0};
|
||||
}
|
||||
};
|
||||
} // namespace nlohmann
|
||||
|
||||
TEST_CASE("even supported types can be specialized" * doctest::test_suite("udt"))
|
||||
{
|
||||
json j = std::vector<float> {1.0, 2.0, 3.0};
|
||||
CHECK(j.dump() == R"("hijacked!")");
|
||||
auto f = j.get<std::vector<float>>();
|
||||
// the single argument from_json method is preferred
|
||||
CHECK((f == std::vector<float> {4.0, 5.0, 6.0}));
|
||||
}
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
template <typename T>
|
||||
struct adl_serializer<std::unique_ptr<T>>
|
||||
{
|
||||
static void to_json(json& j, const std::unique_ptr<T>& opt)
|
||||
{
|
||||
if (opt)
|
||||
{
|
||||
j = *opt;
|
||||
}
|
||||
else
|
||||
{
|
||||
j = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// this is the overload needed for non-copyable types,
|
||||
static std::unique_ptr<T> from_json(const json& j)
|
||||
{
|
||||
if (j.is_null())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::unique_ptr<T>(new T(j.get<T>()));
|
||||
}
|
||||
};
|
||||
} // namespace nlohmann
|
||||
|
||||
TEST_CASE("Non-copyable types" * doctest::test_suite("udt"))
|
||||
{
|
||||
SECTION("to_json")
|
||||
{
|
||||
std::unique_ptr<udt::person> optPerson;
|
||||
|
||||
json j = optPerson;
|
||||
CHECK(j.is_null());
|
||||
|
||||
optPerson.reset(new udt::person{{42}, {"John Doe"}, udt::country::russia}); // NOLINT(cppcoreguidelines-owning-memory,modernize-make-unique)
|
||||
j = optPerson;
|
||||
CHECK_FALSE(j.is_null());
|
||||
|
||||
CHECK(j.get<udt::person>() == *optPerson);
|
||||
}
|
||||
|
||||
SECTION("from_json")
|
||||
{
|
||||
auto person = udt::person{{42}, {"John Doe"}, udt::country::russia};
|
||||
json j = person;
|
||||
|
||||
auto optPerson = j.get<std::unique_ptr<udt::person>>();
|
||||
REQUIRE(optPerson);
|
||||
CHECK(*optPerson == person);
|
||||
|
||||
j = nullptr;
|
||||
optPerson = j.get<std::unique_ptr<udt::person>>();
|
||||
CHECK(!optPerson);
|
||||
}
|
||||
}
|
||||
|
||||
// custom serializer - advanced usage
|
||||
// pack structs that are pod-types (but not scalar types)
|
||||
// relies on adl for any other type
|
||||
template <typename T, typename = void>
|
||||
struct pod_serializer
|
||||
{
|
||||
// use adl for non-pods, or scalar types
|
||||
template <
|
||||
typename BasicJsonType, typename U = T,
|
||||
typename std::enable_if <
|
||||
!(std::is_pod<U>::value && std::is_class<U>::value), int >::type = 0 >
|
||||
static void from_json(const BasicJsonType& j, U& t)
|
||||
{
|
||||
using nlohmann::from_json;
|
||||
from_json(j, t);
|
||||
}
|
||||
|
||||
// special behaviour for pods
|
||||
template < typename BasicJsonType, typename U = T,
|
||||
typename std::enable_if <
|
||||
std::is_pod<U>::value && std::is_class<U>::value, int >::type = 0 >
|
||||
static void from_json(const BasicJsonType& j, U& t)
|
||||
{
|
||||
std::uint64_t value = 0;
|
||||
// The following block is no longer relevant in this serializer, make another one that shows the issue
|
||||
// the problem arises only when one from_json method is defined without any constraint
|
||||
//
|
||||
// Why cannot we simply use: j.get<std::uint64_t>() ?
|
||||
// Well, with the current experiment, the get method looks for a from_json
|
||||
// function, which we are currently defining!
|
||||
// This would end up in a stack overflow. Calling nlohmann::from_json is a
|
||||
// workaround (is it?).
|
||||
// I shall find a good way to avoid this once all constructors are converted
|
||||
// to free methods
|
||||
//
|
||||
// In short, constructing a json by constructor calls to_json
|
||||
// calling get calls from_json, for now, we cannot do this in custom
|
||||
// serializers
|
||||
nlohmann::from_json(j, value);
|
||||
auto* bytes = static_cast<char*>(static_cast<void*>(&value));
|
||||
std::memcpy(&t, bytes, sizeof(value));
|
||||
}
|
||||
|
||||
template <
|
||||
typename BasicJsonType, typename U = T,
|
||||
typename std::enable_if <
|
||||
!(std::is_pod<U>::value && std::is_class<U>::value), int >::type = 0 >
|
||||
static void to_json(BasicJsonType& j, const T& t)
|
||||
{
|
||||
using nlohmann::to_json;
|
||||
to_json(j, t);
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename U = T,
|
||||
typename std::enable_if <
|
||||
std::is_pod<U>::value && std::is_class<U>::value, int >::type = 0 >
|
||||
static void to_json(BasicJsonType& j, const T& t) noexcept
|
||||
{
|
||||
const auto* bytes = static_cast< const unsigned char*>(static_cast<const void*>(&t));
|
||||
std::uint64_t value = 0;
|
||||
std::memcpy(&value, bytes, sizeof(value));
|
||||
nlohmann::to_json(j, value);
|
||||
}
|
||||
};
|
||||
|
||||
namespace udt
|
||||
{
|
||||
struct small_pod
|
||||
{
|
||||
int begin;
|
||||
char middle;
|
||||
short end;
|
||||
};
|
||||
|
||||
struct non_pod
|
||||
{
|
||||
std::string s{};
|
||||
non_pod() = default;
|
||||
non_pod(std::string S) : s(std::move(S)) {}
|
||||
};
|
||||
|
||||
template <typename BasicJsonType>
|
||||
static void to_json(BasicJsonType& j, const non_pod& np)
|
||||
{
|
||||
j = np.s;
|
||||
}
|
||||
|
||||
template <typename BasicJsonType>
|
||||
static void from_json(const BasicJsonType& j, non_pod& np)
|
||||
{
|
||||
np.s = j.template get<std::string>();
|
||||
}
|
||||
|
||||
static bool operator==(small_pod lhs, small_pod rhs) noexcept
|
||||
{
|
||||
return std::tie(lhs.begin, lhs.middle, lhs.end) ==
|
||||
std::tie(rhs.begin, rhs.middle, rhs.end);
|
||||
}
|
||||
|
||||
static bool operator==(const non_pod& lhs, const non_pod& rhs) noexcept
|
||||
{
|
||||
return lhs.s == rhs.s;
|
||||
}
|
||||
|
||||
static std::ostream& operator<<(std::ostream& os, small_pod l)
|
||||
{
|
||||
return os << "begin: " << l.begin << ", middle: " << l.middle << ", end: " << l.end;
|
||||
}
|
||||
} // namespace udt
|
||||
|
||||
TEST_CASE("custom serializer for pods" * doctest::test_suite("udt"))
|
||||
{
|
||||
using custom_json =
|
||||
nlohmann::basic_json<std::map, std::vector, std::string, bool,
|
||||
std::int64_t, std::uint64_t, double, std::allocator, pod_serializer>;
|
||||
|
||||
auto p = udt::small_pod{42, '/', 42};
|
||||
custom_json j = p;
|
||||
|
||||
auto p2 = j.get<udt::small_pod>();
|
||||
|
||||
CHECK(p == p2);
|
||||
|
||||
auto np = udt::non_pod{{"non-pod"}};
|
||||
custom_json j2 = np;
|
||||
auto np2 = j2.get<udt::non_pod>();
|
||||
CHECK(np == np2);
|
||||
}
|
||||
|
||||
template <typename T, typename>
|
||||
struct another_adl_serializer;
|
||||
|
||||
using custom_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, double, std::allocator, another_adl_serializer>;
|
||||
|
||||
template <typename T, typename>
|
||||
struct another_adl_serializer
|
||||
{
|
||||
static void from_json(const custom_json& j, T& t)
|
||||
{
|
||||
using nlohmann::from_json;
|
||||
from_json(j, t);
|
||||
}
|
||||
|
||||
static void to_json(custom_json& j, const T& t)
|
||||
{
|
||||
using nlohmann::to_json;
|
||||
to_json(j, t);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("custom serializer that does adl by default" * doctest::test_suite("udt"))
|
||||
{
|
||||
auto me = udt::person{{23}, {"theo"}, udt::country::france};
|
||||
|
||||
json j = me;
|
||||
custom_json cj = me;
|
||||
|
||||
CHECK(j.dump() == cj.dump());
|
||||
|
||||
CHECK(me == j.get<udt::person>());
|
||||
CHECK(me == cj.get<udt::person>());
|
||||
}
|
||||
|
||||
TEST_CASE("different basic_json types conversions")
|
||||
{
|
||||
SECTION("null")
|
||||
{
|
||||
json j;
|
||||
custom_json cj = j;
|
||||
CHECK(cj == nullptr);
|
||||
}
|
||||
|
||||
SECTION("boolean")
|
||||
{
|
||||
json j = true;
|
||||
custom_json cj = j;
|
||||
CHECK(cj == true);
|
||||
}
|
||||
|
||||
SECTION("discarded")
|
||||
{
|
||||
json j(json::value_t::discarded);
|
||||
custom_json cj;
|
||||
CHECK_NOTHROW(cj = j);
|
||||
CHECK(cj.type() == custom_json::value_t::discarded);
|
||||
}
|
||||
|
||||
SECTION("array")
|
||||
{
|
||||
json j = {1, 2, 3};
|
||||
custom_json cj = j;
|
||||
CHECK((cj == std::vector<int> {1, 2, 3}));
|
||||
}
|
||||
|
||||
SECTION("integer")
|
||||
{
|
||||
json j = 42;
|
||||
custom_json cj = j;
|
||||
CHECK(cj == 42);
|
||||
}
|
||||
|
||||
SECTION("float")
|
||||
{
|
||||
json j = 42.0;
|
||||
custom_json cj = j;
|
||||
CHECK(cj == 42.0);
|
||||
}
|
||||
|
||||
SECTION("unsigned")
|
||||
{
|
||||
json j = 42u;
|
||||
custom_json cj = j;
|
||||
CHECK(cj == 42u);
|
||||
}
|
||||
|
||||
SECTION("string")
|
||||
{
|
||||
json j = "forty-two";
|
||||
custom_json cj = j;
|
||||
CHECK(cj == "forty-two");
|
||||
}
|
||||
|
||||
SECTION("binary")
|
||||
{
|
||||
json j = json::binary({1, 2, 3}, 42);
|
||||
custom_json cj = j;
|
||||
CHECK(cj.get_binary().subtype() == 42);
|
||||
std::vector<std::uint8_t> cv = cj.get_binary();
|
||||
std::vector<std::uint8_t> v = j.get_binary();
|
||||
CHECK(cv == v);
|
||||
}
|
||||
|
||||
SECTION("object")
|
||||
{
|
||||
json j = {{"forty", "two"}};
|
||||
custom_json cj = j;
|
||||
auto m = j.get<std::map<std::string, std::string>>();
|
||||
CHECK(cj == m);
|
||||
}
|
||||
|
||||
SECTION("get<custom_json>")
|
||||
{
|
||||
json j = 42;
|
||||
custom_json cj = j.get<custom_json>();
|
||||
CHECK(cj == 42);
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
struct incomplete;
|
||||
|
||||
// std::is_constructible is broken on macOS' libc++
|
||||
// use the cppreference implementation
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct is_constructible_patched : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_constructible_patched<T, decltype(void(json(std::declval<T>())))> : std::true_type {};
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("an incomplete type does not trigger a compiler error in non-evaluated context" * doctest::test_suite("udt"))
|
||||
{
|
||||
static_assert(!is_constructible_patched<json, incomplete>::value, "");
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class Evil
|
||||
{
|
||||
public:
|
||||
Evil() = default;
|
||||
template <typename T>
|
||||
Evil(T t) : m_i(sizeof(t))
|
||||
{
|
||||
static_cast<void>(t); // fix MSVC's C4100 warning
|
||||
}
|
||||
|
||||
int m_i = 0;
|
||||
};
|
||||
|
||||
void from_json(const json& /*unused*/, Evil& /*unused*/) {}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Issue #924")
|
||||
{
|
||||
// Prevent get<std::vector<Evil>>() to throw
|
||||
auto j = json::array();
|
||||
|
||||
CHECK_NOTHROW(j.get<Evil>());
|
||||
CHECK_NOTHROW(j.get<std::vector<Evil>>());
|
||||
|
||||
// silence Wunused-template warnings
|
||||
Evil e(1);
|
||||
CHECK(e.m_i >= 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Issue #1237")
|
||||
{
|
||||
struct non_convertible_type {};
|
||||
static_assert(!std::is_convertible<json, non_convertible_type>::value, "");
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class no_iterator_type
|
||||
{
|
||||
public:
|
||||
no_iterator_type(std::initializer_list<int> l)
|
||||
: _v(l)
|
||||
{}
|
||||
|
||||
std::vector<int>::const_iterator begin() const
|
||||
{
|
||||
return _v.begin();
|
||||
}
|
||||
|
||||
std::vector<int>::const_iterator end() const
|
||||
{
|
||||
return _v.end();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<int> _v;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("compatible array type, without iterator type alias")
|
||||
{
|
||||
no_iterator_type vec{1, 2, 3};
|
||||
json j = vec;
|
||||
}
|
||||
|
||||
DOCTEST_GCC_SUPPRESS_WARNING_POP
|
||||
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 <string>
|
||||
#include <vector>
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
namespace persons
|
||||
{
|
||||
class person_with_private_data
|
||||
{
|
||||
private:
|
||||
std::string name{};
|
||||
int age = 0;
|
||||
json metadata = nullptr;
|
||||
|
||||
public:
|
||||
bool operator==(const person_with_private_data& rhs) const
|
||||
{
|
||||
return name == rhs.name && age == rhs.age && metadata == rhs.metadata;
|
||||
}
|
||||
|
||||
person_with_private_data() = default;
|
||||
person_with_private_data(std::string name_, int age_, json metadata_)
|
||||
: name(std::move(name_))
|
||||
, age(age_)
|
||||
, metadata(std::move(metadata_))
|
||||
{}
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_with_private_data, age, name, metadata)
|
||||
};
|
||||
|
||||
class person_with_private_data_2
|
||||
{
|
||||
private:
|
||||
std::string name{};
|
||||
int age = 0;
|
||||
json metadata = nullptr;
|
||||
|
||||
public:
|
||||
bool operator==(const person_with_private_data_2& rhs) const
|
||||
{
|
||||
return name == rhs.name && age == rhs.age && metadata == rhs.metadata;
|
||||
}
|
||||
|
||||
person_with_private_data_2() = default;
|
||||
person_with_private_data_2(std::string name_, int age_, json metadata_)
|
||||
: name(std::move(name_))
|
||||
, age(age_)
|
||||
, metadata(std::move(metadata_))
|
||||
{}
|
||||
|
||||
std::string getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
int getAge() const
|
||||
{
|
||||
return age;
|
||||
}
|
||||
json getMetadata() const
|
||||
{
|
||||
return metadata;
|
||||
}
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(person_with_private_data_2, age, name, metadata)
|
||||
};
|
||||
|
||||
class person_without_private_data_1
|
||||
{
|
||||
public:
|
||||
std::string name{};
|
||||
int age = 0;
|
||||
json metadata = nullptr;
|
||||
|
||||
bool operator==(const person_without_private_data_1& rhs) const
|
||||
{
|
||||
return name == rhs.name && age == rhs.age && metadata == rhs.metadata;
|
||||
}
|
||||
|
||||
person_without_private_data_1() = default;
|
||||
person_without_private_data_1(std::string name_, int age_, json metadata_)
|
||||
: name(std::move(name_))
|
||||
, age(age_)
|
||||
, metadata(std::move(metadata_))
|
||||
{}
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_without_private_data_1, age, name, metadata)
|
||||
};
|
||||
|
||||
class person_without_private_data_2
|
||||
{
|
||||
public:
|
||||
std::string name{};
|
||||
int age = 0;
|
||||
json metadata = nullptr;
|
||||
|
||||
bool operator==(const person_without_private_data_2& rhs) const
|
||||
{
|
||||
return name == rhs.name && age == rhs.age && metadata == rhs.metadata;
|
||||
}
|
||||
|
||||
person_without_private_data_2() = default;
|
||||
person_without_private_data_2(std::string name_, int age_, json metadata_)
|
||||
: name(std::move(name_))
|
||||
, age(age_)
|
||||
, metadata(std::move(metadata_))
|
||||
{}
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_without_private_data_2, age, name, metadata)
|
||||
|
||||
class person_without_private_data_3
|
||||
{
|
||||
public:
|
||||
std::string name{};
|
||||
int age = 0;
|
||||
json metadata = nullptr;
|
||||
|
||||
bool operator==(const person_without_private_data_3& rhs) const
|
||||
{
|
||||
return name == rhs.name && age == rhs.age && metadata == rhs.metadata;
|
||||
}
|
||||
|
||||
person_without_private_data_3() = default;
|
||||
person_without_private_data_3(std::string name_, int age_, json metadata_)
|
||||
: name(std::move(name_))
|
||||
, age(age_)
|
||||
, metadata(std::move(metadata_))
|
||||
{}
|
||||
|
||||
std::string getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
int getAge() const
|
||||
{
|
||||
return age;
|
||||
}
|
||||
json getMetadata() const
|
||||
{
|
||||
return metadata;
|
||||
}
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(person_without_private_data_3, age, name, metadata)
|
||||
|
||||
class person_with_private_alphabet
|
||||
{
|
||||
public:
|
||||
bool operator==(const person_with_private_alphabet& other) const
|
||||
{
|
||||
return a == other.a &&
|
||||
b == other.b &&
|
||||
c == other.c &&
|
||||
d == other.d &&
|
||||
e == other.e &&
|
||||
f == other.f &&
|
||||
g == other.g &&
|
||||
h == other.h &&
|
||||
i == other.i &&
|
||||
j == other.j &&
|
||||
k == other.k &&
|
||||
l == other.l &&
|
||||
m == other.m &&
|
||||
n == other.n &&
|
||||
o == other.o &&
|
||||
p == other.p &&
|
||||
q == other.q &&
|
||||
r == other.r &&
|
||||
s == other.s &&
|
||||
t == other.t &&
|
||||
u == other.u &&
|
||||
v == other.v &&
|
||||
w == other.w &&
|
||||
x == other.x &&
|
||||
y == other.y &&
|
||||
z == other.z;
|
||||
}
|
||||
|
||||
private:
|
||||
int a = 0;
|
||||
int b = 0;
|
||||
int c = 0;
|
||||
int d = 0;
|
||||
int e = 0;
|
||||
int f = 0;
|
||||
int g = 0;
|
||||
int h = 0;
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int k = 0;
|
||||
int l = 0;
|
||||
int m = 0;
|
||||
int n = 0;
|
||||
int o = 0;
|
||||
int p = 0;
|
||||
int q = 0;
|
||||
int r = 0;
|
||||
int s = 0;
|
||||
int t = 0;
|
||||
int u = 0;
|
||||
int v = 0;
|
||||
int w = 0;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int z = 0;
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(person_with_private_alphabet, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z)
|
||||
};
|
||||
|
||||
class person_with_public_alphabet
|
||||
{
|
||||
public:
|
||||
bool operator==(const person_with_public_alphabet& other) const
|
||||
{
|
||||
return a == other.a &&
|
||||
b == other.b &&
|
||||
c == other.c &&
|
||||
d == other.d &&
|
||||
e == other.e &&
|
||||
f == other.f &&
|
||||
g == other.g &&
|
||||
h == other.h &&
|
||||
i == other.i &&
|
||||
j == other.j &&
|
||||
k == other.k &&
|
||||
l == other.l &&
|
||||
m == other.m &&
|
||||
n == other.n &&
|
||||
o == other.o &&
|
||||
p == other.p &&
|
||||
q == other.q &&
|
||||
r == other.r &&
|
||||
s == other.s &&
|
||||
t == other.t &&
|
||||
u == other.u &&
|
||||
v == other.v &&
|
||||
w == other.w &&
|
||||
x == other.x &&
|
||||
y == other.y &&
|
||||
z == other.z;
|
||||
}
|
||||
|
||||
int a = 0;
|
||||
int b = 0;
|
||||
int c = 0;
|
||||
int d = 0;
|
||||
int e = 0;
|
||||
int f = 0;
|
||||
int g = 0;
|
||||
int h = 0;
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int k = 0;
|
||||
int l = 0;
|
||||
int m = 0;
|
||||
int n = 0;
|
||||
int o = 0;
|
||||
int p = 0;
|
||||
int q = 0;
|
||||
int r = 0;
|
||||
int s = 0;
|
||||
int t = 0;
|
||||
int u = 0;
|
||||
int v = 0;
|
||||
int w = 0;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int z = 0;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person_with_public_alphabet, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z)
|
||||
|
||||
} // namespace persons
|
||||
|
||||
TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE", T,
|
||||
persons::person_with_private_data,
|
||||
persons::person_without_private_data_1,
|
||||
persons::person_without_private_data_2)
|
||||
{
|
||||
SECTION("person")
|
||||
{
|
||||
// serialization
|
||||
T p1("Erik", 1, {{"haircuts", 2}});
|
||||
CHECK(json(p1).dump() == "{\"age\":1,\"metadata\":{\"haircuts\":2},\"name\":\"Erik\"}");
|
||||
|
||||
// deserialization
|
||||
auto p2 = json(p1).get<T>();
|
||||
CHECK(p2 == p1);
|
||||
|
||||
// roundtrip
|
||||
CHECK(T(json(p1)) == p1);
|
||||
CHECK(json(T(json(p1))) == json(p1));
|
||||
|
||||
// check exception in case of missing field
|
||||
json j = json(p1);
|
||||
j.erase("age");
|
||||
CHECK_THROWS_WITH_AS(j.get<T>(), "[json.exception.out_of_range.403] key 'age' not found", json::out_of_range);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT", T,
|
||||
persons::person_with_private_data_2,
|
||||
persons::person_without_private_data_3)
|
||||
{
|
||||
SECTION("person with default values")
|
||||
{
|
||||
// serialization of default constructed object
|
||||
T p0;
|
||||
CHECK(json(p0).dump() == "{\"age\":0,\"metadata\":null,\"name\":\"\"}");
|
||||
|
||||
// serialization
|
||||
T p1("Erik", 1, {{"haircuts", 2}});
|
||||
CHECK(json(p1).dump() == "{\"age\":1,\"metadata\":{\"haircuts\":2},\"name\":\"Erik\"}");
|
||||
|
||||
// deserialization
|
||||
auto p2 = json(p1).get<T>();
|
||||
CHECK(p2 == p1);
|
||||
|
||||
// roundtrip
|
||||
CHECK(T(json(p1)) == p1);
|
||||
CHECK(json(T(json(p1))) == json(p1));
|
||||
|
||||
// check default value in case of missing field
|
||||
json j = json(p1);
|
||||
j.erase("name");
|
||||
j.erase("age");
|
||||
j.erase("metadata");
|
||||
T p3 = j.get<T>();
|
||||
CHECK(p3.getName() == "");
|
||||
CHECK(p3.getAge() == 0);
|
||||
CHECK(p3.getMetadata() == nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_TEMPLATE("Serialization/deserialization of classes with 26 public/private member variables via NLOHMANN_DEFINE_TYPE_INTRUSIVE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE", T,
|
||||
persons::person_with_private_alphabet,
|
||||
persons::person_with_public_alphabet)
|
||||
{
|
||||
SECTION("alphabet")
|
||||
{
|
||||
{
|
||||
T obj1;
|
||||
nlohmann::json j = obj1; //via json object
|
||||
T obj2;
|
||||
j.get_to(obj2);
|
||||
bool ok = (obj1 == obj2);
|
||||
CHECK(ok);
|
||||
}
|
||||
|
||||
{
|
||||
T obj1;
|
||||
nlohmann::json j1 = obj1; //via json string
|
||||
std::string s = j1.dump();
|
||||
nlohmann::json j2 = nlohmann::json::parse(s);
|
||||
T obj2;
|
||||
j2.get_to(obj2);
|
||||
bool ok = (obj1 == obj2);
|
||||
CHECK(ok);
|
||||
}
|
||||
|
||||
{
|
||||
T obj1;
|
||||
nlohmann::json j1 = obj1; //via msgpack
|
||||
std::vector<uint8_t> buf = nlohmann::json::to_msgpack(j1);
|
||||
nlohmann::json j2 = nlohmann::json::from_msgpack(buf);
|
||||
T obj2;
|
||||
j2.get_to(obj2);
|
||||
bool ok = (obj1 == obj2);
|
||||
CHECK(ok);
|
||||
}
|
||||
|
||||
{
|
||||
T obj1;
|
||||
nlohmann::json j1 = obj1; //via bson
|
||||
std::vector<uint8_t> buf = nlohmann::json::to_bson(j1);
|
||||
nlohmann::json j2 = nlohmann::json::from_bson(buf);
|
||||
T obj2;
|
||||
j2.get_to(obj2);
|
||||
bool ok = (obj1 == obj2);
|
||||
CHECK(ok);
|
||||
}
|
||||
|
||||
{
|
||||
T obj1;
|
||||
nlohmann::json j1 = obj1; //via cbor
|
||||
std::vector<uint8_t> buf = nlohmann::json::to_cbor(j1);
|
||||
nlohmann::json j2 = nlohmann::json::from_cbor(buf);
|
||||
T obj2;
|
||||
j2.get_to(obj2);
|
||||
bool ok = (obj1 == obj2);
|
||||
CHECK(ok);
|
||||
}
|
||||
|
||||
{
|
||||
T obj1;
|
||||
nlohmann::json j1 = obj1; //via ubjson
|
||||
std::vector<uint8_t> buf = nlohmann::json::to_ubjson(j1);
|
||||
nlohmann::json j2 = nlohmann::json::from_ubjson(buf);
|
||||
T obj2;
|
||||
j2.get_to(obj2);
|
||||
bool ok = (obj1 == obj2);
|
||||
CHECK(ok);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,641 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
// for some reason including this after the json header leads to linker errors with VS 2017...
|
||||
#include <locale>
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <test_data.hpp>
|
||||
|
||||
TEST_CASE("Unicode (1/5)" * doctest::skip())
|
||||
{
|
||||
SECTION("\\uxxxx sequences")
|
||||
{
|
||||
// create an escaped string from a code point
|
||||
const auto codepoint_to_unicode = [](std::size_t cp)
|
||||
{
|
||||
// code 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();
|
||||
};
|
||||
|
||||
SECTION("correct sequences")
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
// string to store the code point as in \uxxxx format
|
||||
std::string json_text = "\"";
|
||||
|
||||
// decide whether to use one or two \uxxxx sequences
|
||||
if (cp < 0x10000u)
|
||||
{
|
||||
// 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 && cp <= 0xDFFFu)
|
||||
{
|
||||
// if we would not skip these code points, we would get a
|
||||
// "missing low surrogate" exception
|
||||
continue;
|
||||
}
|
||||
|
||||
// code points in the Basic Multilingual Plane can be
|
||||
// represented with one \uxxxx sequence
|
||||
json_text += codepoint_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);
|
||||
json_text += codepoint_to_unicode(codepoint1) + codepoint_to_unicode(codepoint2);
|
||||
}
|
||||
|
||||
json_text += "\"";
|
||||
CAPTURE(json_text)
|
||||
json _;
|
||||
CHECK_NOTHROW(_ = json::parse(json_text));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("incorrect sequences")
|
||||
{
|
||||
SECTION("incorrect surrogate values")
|
||||
{
|
||||
json _;
|
||||
|
||||
CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uDC00\\uDC00\""), "[json.exception.parse_error.101] parse error at line 1, column 7: syntax error while parsing value - invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF; last read: '\"\\uDC00'", json::parse_error&);
|
||||
|
||||
CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD7FF\\uDC00\""), "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF; last read: '\"\\uD7FF\\uDC00'", json::parse_error&);
|
||||
|
||||
CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD800]\""), "[json.exception.parse_error.101] parse error at line 1, column 8: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD800]'", json::parse_error&);
|
||||
|
||||
CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD800\\v\""), "[json.exception.parse_error.101] parse error at line 1, column 9: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD800\\v'", json::parse_error&);
|
||||
|
||||
CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD800\\u123\""), "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: '\\u' must be followed by 4 hex digits; last read: '\"\\uD800\\u123\"'", json::parse_error&);
|
||||
|
||||
CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD800\\uDBFF\""), "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD800\\uDBFF'", json::parse_error&);
|
||||
|
||||
CHECK_THROWS_WITH_AS(_ = json::parse("\"\\uD800\\uE000\""), "[json.exception.parse_error.101] parse error at line 1, column 13: syntax error while parsing value - invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF; last read: '\"\\uD800\\uE000'", json::parse_error&);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
SECTION("incorrect sequences")
|
||||
{
|
||||
SECTION("high surrogate without low surrogate")
|
||||
{
|
||||
// D800..DBFF are high surrogates and must be followed by low
|
||||
// surrogates DC00..DFFF; here, nothing follows
|
||||
for (std::size_t cp = 0xD800u; cp <= 0xDBFFu; ++cp)
|
||||
{
|
||||
std::string json_text = "\"" + codepoint_to_unicode(cp) + "\"";
|
||||
CAPTURE(json_text)
|
||||
CHECK_THROWS_AS(json::parse(json_text), json::parse_error&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("high surrogate with wrong low surrogate")
|
||||
{
|
||||
// D800..DBFF are high surrogates and must be followed by low
|
||||
// surrogates DC00..DFFF; here a different sequence follows
|
||||
for (std::size_t cp1 = 0xD800u; cp1 <= 0xDBFFu; ++cp1)
|
||||
{
|
||||
for (std::size_t cp2 = 0x0000u; cp2 <= 0xFFFFu; ++cp2)
|
||||
{
|
||||
if (0xDC00u <= cp2 && cp2 <= 0xDFFFu)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string json_text = "\"" + codepoint_to_unicode(cp1) + codepoint_to_unicode(cp2) + "\"";
|
||||
CAPTURE(json_text)
|
||||
CHECK_THROWS_AS(json::parse(json_text), json::parse_error&);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("low surrogate without high surrogate")
|
||||
{
|
||||
// low surrogates DC00..DFFF must follow high surrogates; here,
|
||||
// they occur alone
|
||||
for (std::size_t cp = 0xDC00u; cp <= 0xDFFFu; ++cp)
|
||||
{
|
||||
std::string json_text = "\"" + codepoint_to_unicode(cp) + "\"";
|
||||
CAPTURE(json_text)
|
||||
CHECK_THROWS_AS(json::parse(json_text), json::parse_error&);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
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_DIRECTORY "/json_nlohmann_tests/all_unicode.json");
|
||||
json j;
|
||||
CHECK_NOTHROW(f >> j);
|
||||
|
||||
// the array has 1112064 + 1 elements (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 (const auto& s : j)
|
||||
{
|
||||
// skip non-string JSON values
|
||||
if (!s.is_string())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto ptr = s.get<std::string>();
|
||||
|
||||
// tilde must be followed by 0 or 1
|
||||
if (ptr == "~")
|
||||
{
|
||||
ptr += "0";
|
||||
}
|
||||
|
||||
// JSON Pointers must begin with "/"
|
||||
ptr.insert(0, "/");
|
||||
|
||||
CHECK_NOTHROW(json::json_pointer("/" + ptr));
|
||||
|
||||
// check escape/unescape roundtrip
|
||||
auto escaped = nlohmann::detail::escape(ptr);
|
||||
nlohmann::detail::unescape(escaped);
|
||||
CHECK(escaped == ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ignore byte-order-mark")
|
||||
{
|
||||
SECTION("in a stream")
|
||||
{
|
||||
// read a file with a UTF-8 BOM
|
||||
std::ifstream f(TEST_DATA_DIRECTORY "/json_nlohmann_tests/bom.json");
|
||||
json j;
|
||||
CHECK_NOTHROW(f >> j);
|
||||
}
|
||||
|
||||
SECTION("with an iterator")
|
||||
{
|
||||
std::string i = "\xef\xbb\xbf{\n \"foo\": true\n}";
|
||||
json _;
|
||||
CHECK_NOTHROW(_ = json::parse(i.begin(), i.end()));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("error for incomplete/wrong BOM")
|
||||
{
|
||||
json _;
|
||||
CHECK_THROWS_AS(_ = json::parse("\xef\xbb"), json::parse_error&);
|
||||
CHECK_THROWS_AS(_ = json::parse("\xef\xbb\xbb"), json::parse_error&);
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
void roundtrip(bool success_expected, const std::string& s);
|
||||
|
||||
void roundtrip(bool success_expected, const std::string& s)
|
||||
{
|
||||
CAPTURE(s)
|
||||
json _;
|
||||
|
||||
// create JSON string value
|
||||
json j = s;
|
||||
// create JSON text
|
||||
std::string ps = std::string("\"") + s + "\"";
|
||||
|
||||
if (success_expected)
|
||||
{
|
||||
// serialization succeeds
|
||||
CHECK_NOTHROW(j.dump());
|
||||
|
||||
// exclude parse test for U+0000
|
||||
if (s[0] != '\0')
|
||||
{
|
||||
// parsing JSON text succeeds
|
||||
CHECK_NOTHROW(_ = json::parse(ps));
|
||||
}
|
||||
|
||||
// roundtrip succeeds
|
||||
CHECK_NOTHROW(_ = json::parse(j.dump()));
|
||||
|
||||
// after roundtrip, the same string is stored
|
||||
json jr = json::parse(j.dump());
|
||||
CHECK(jr.get<std::string>() == s);
|
||||
}
|
||||
else
|
||||
{
|
||||
// serialization fails
|
||||
CHECK_THROWS_AS(j.dump(), json::type_error&);
|
||||
|
||||
// parsing JSON text fails
|
||||
CHECK_THROWS_AS(_ = json::parse(ps), json::parse_error&);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Markus Kuhn's UTF-8 decoder capability and stress test")
|
||||
{
|
||||
// Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/> - 2015-08-28 - CC BY 4.0
|
||||
// http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
|
||||
|
||||
SECTION("1 Some correct UTF-8 text")
|
||||
{
|
||||
roundtrip(true, "κόσμε");
|
||||
}
|
||||
|
||||
SECTION("2 Boundary condition test cases")
|
||||
{
|
||||
SECTION("2.1 First possible sequence of a certain length")
|
||||
{
|
||||
// 2.1.1 1 byte (U-00000000)
|
||||
roundtrip(true, std::string("\0", 1));
|
||||
// 2.1.2 2 bytes (U-00000080)
|
||||
roundtrip(true, "\xc2\x80");
|
||||
// 2.1.3 3 bytes (U-00000800)
|
||||
roundtrip(true, "\xe0\xa0\x80");
|
||||
// 2.1.4 4 bytes (U-00010000)
|
||||
roundtrip(true, "\xf0\x90\x80\x80");
|
||||
|
||||
// 2.1.5 5 bytes (U-00200000)
|
||||
roundtrip(false, "\xF8\x88\x80\x80\x80");
|
||||
// 2.1.6 6 bytes (U-04000000)
|
||||
roundtrip(false, "\xFC\x84\x80\x80\x80\x80");
|
||||
}
|
||||
|
||||
SECTION("2.2 Last possible sequence of a certain length")
|
||||
{
|
||||
// 2.2.1 1 byte (U-0000007F)
|
||||
roundtrip(true, "\x7f");
|
||||
// 2.2.2 2 bytes (U-000007FF)
|
||||
roundtrip(true, "\xdf\xbf");
|
||||
// 2.2.3 3 bytes (U-0000FFFF)
|
||||
roundtrip(true, "\xef\xbf\xbf");
|
||||
|
||||
// 2.2.4 4 bytes (U-001FFFFF)
|
||||
roundtrip(false, "\xF7\xBF\xBF\xBF");
|
||||
// 2.2.5 5 bytes (U-03FFFFFF)
|
||||
roundtrip(false, "\xFB\xBF\xBF\xBF\xBF");
|
||||
// 2.2.6 6 bytes (U-7FFFFFFF)
|
||||
roundtrip(false, "\xFD\xBF\xBF\xBF\xBF\xBF");
|
||||
}
|
||||
|
||||
SECTION("2.3 Other boundary conditions")
|
||||
{
|
||||
// 2.3.1 U-0000D7FF = ed 9f bf
|
||||
roundtrip(true, "\xed\x9f\xbf");
|
||||
// 2.3.2 U-0000E000 = ee 80 80
|
||||
roundtrip(true, "\xee\x80\x80");
|
||||
// 2.3.3 U-0000FFFD = ef bf bd
|
||||
roundtrip(true, "\xef\xbf\xbd");
|
||||
// 2.3.4 U-0010FFFF = f4 8f bf bf
|
||||
roundtrip(true, "\xf4\x8f\xbf\xbf");
|
||||
|
||||
// 2.3.5 U-00110000 = f4 90 80 80
|
||||
roundtrip(false, "\xf4\x90\x80\x80");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("3 Malformed sequences")
|
||||
{
|
||||
SECTION("3.1 Unexpected continuation bytes")
|
||||
{
|
||||
// Each unexpected continuation byte should be separately signalled as a
|
||||
// malformed sequence of its own.
|
||||
|
||||
// 3.1.1 First continuation byte 0x80
|
||||
roundtrip(false, "\x80");
|
||||
// 3.1.2 Last continuation byte 0xbf
|
||||
roundtrip(false, "\xbf");
|
||||
|
||||
// 3.1.3 2 continuation bytes
|
||||
roundtrip(false, "\x80\xbf");
|
||||
// 3.1.4 3 continuation bytes
|
||||
roundtrip(false, "\x80\xbf\x80");
|
||||
// 3.1.5 4 continuation bytes
|
||||
roundtrip(false, "\x80\xbf\x80\xbf");
|
||||
// 3.1.6 5 continuation bytes
|
||||
roundtrip(false, "\x80\xbf\x80\xbf\x80");
|
||||
// 3.1.7 6 continuation bytes
|
||||
roundtrip(false, "\x80\xbf\x80\xbf\x80\xbf");
|
||||
// 3.1.8 7 continuation bytes
|
||||
roundtrip(false, "\x80\xbf\x80\xbf\x80\xbf\x80");
|
||||
|
||||
// 3.1.9 Sequence of all 64 possible continuation bytes (0x80-0xbf)
|
||||
roundtrip(false, "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf");
|
||||
}
|
||||
|
||||
SECTION("3.2 Lonely start characters")
|
||||
{
|
||||
// 3.2.1 All 32 first bytes of 2-byte sequences (0xc0-0xdf)
|
||||
roundtrip(false, "\xc0 \xc1 \xc2 \xc3 \xc4 \xc5 \xc6 \xc7 \xc8 \xc9 \xca \xcb \xcc \xcd \xce \xcf \xd0 \xd1 \xd2 \xd3 \xd4 \xd5 \xd6 \xd7 \xd8 \xd9 \xda \xdb \xdc \xdd \xde \xdf");
|
||||
// 3.2.2 All 16 first bytes of 3-byte sequences (0xe0-0xef)
|
||||
roundtrip(false, "\xe0 \xe1 \xe2 \xe3 \xe4 \xe5 \xe6 \xe7 \xe8 \xe9 \xea \xeb \xec \xed \xee \xef");
|
||||
// 3.2.3 All 8 first bytes of 4-byte sequences (0xf0-0xf7)
|
||||
roundtrip(false, "\xf0 \xf1 \xf2 \xf3 \xf4 \xf5 \xf6 \xf7");
|
||||
// 3.2.4 All 4 first bytes of 5-byte sequences (0xf8-0xfb)
|
||||
roundtrip(false, "\xf8 \xf9 \xfa \xfb");
|
||||
// 3.2.5 All 2 first bytes of 6-byte sequences (0xfc-0xfd)
|
||||
roundtrip(false, "\xfc \xfd");
|
||||
}
|
||||
|
||||
SECTION("3.3 Sequences with last continuation byte missing")
|
||||
{
|
||||
// All bytes of an incomplete sequence should be signalled as a single
|
||||
// malformed sequence, i.e., you should see only a single replacement
|
||||
// character in each of the next 10 tests. (Characters as in section 2)
|
||||
|
||||
// 3.3.1 2-byte sequence with last byte missing (U+0000)
|
||||
roundtrip(false, "\xc0");
|
||||
// 3.3.2 3-byte sequence with last byte missing (U+0000)
|
||||
roundtrip(false, "\xe0\x80");
|
||||
// 3.3.3 4-byte sequence with last byte missing (U+0000)
|
||||
roundtrip(false, "\xf0\x80\x80");
|
||||
// 3.3.4 5-byte sequence with last byte missing (U+0000)
|
||||
roundtrip(false, "\xf8\x80\x80\x80");
|
||||
// 3.3.5 6-byte sequence with last byte missing (U+0000)
|
||||
roundtrip(false, "\xfc\x80\x80\x80\x80");
|
||||
// 3.3.6 2-byte sequence with last byte missing (U-000007FF)
|
||||
roundtrip(false, "\xdf");
|
||||
// 3.3.7 3-byte sequence with last byte missing (U-0000FFFF)
|
||||
roundtrip(false, "\xef\xbf");
|
||||
// 3.3.8 4-byte sequence with last byte missing (U-001FFFFF)
|
||||
roundtrip(false, "\xf7\xbf\xbf");
|
||||
// 3.3.9 5-byte sequence with last byte missing (U-03FFFFFF)
|
||||
roundtrip(false, "\xfb\xbf\xbf\xbf");
|
||||
// 3.3.10 6-byte sequence with last byte missing (U-7FFFFFFF)
|
||||
roundtrip(false, "\xfd\xbf\xbf\xbf\xbf");
|
||||
}
|
||||
|
||||
SECTION("3.4 Concatenation of incomplete sequences")
|
||||
{
|
||||
// All the 10 sequences of 3.3 concatenated, you should see 10 malformed
|
||||
// sequences being signalled:
|
||||
roundtrip(false, "\xc0\xe0\x80\xf0\x80\x80\xf8\x80\x80\x80\xfc\x80\x80\x80\x80\xdf\xef\xbf\xf7\xbf\xbf\xfb\xbf\xbf\xbf\xfd\xbf\xbf\xbf\xbf");
|
||||
}
|
||||
|
||||
SECTION("3.5 Impossible bytes")
|
||||
{
|
||||
// The following two bytes cannot appear in a correct UTF-8 string
|
||||
|
||||
// 3.5.1 fe
|
||||
roundtrip(false, "\xfe");
|
||||
// 3.5.2 ff
|
||||
roundtrip(false, "\xff");
|
||||
// 3.5.3 fe fe ff ff
|
||||
roundtrip(false, "\xfe\xfe\xff\xff");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("4 Overlong sequences")
|
||||
{
|
||||
// The following sequences are not malformed according to the letter of
|
||||
// the Unicode 2.0 standard. However, they are longer then necessary and
|
||||
// a correct UTF-8 encoder is not allowed to produce them. A "safe UTF-8
|
||||
// decoder" should reject them just like malformed sequences for two
|
||||
// reasons: (1) It helps to debug applications if overlong sequences are
|
||||
// not treated as valid representations of characters, because this helps
|
||||
// to spot problems more quickly. (2) Overlong sequences provide
|
||||
// alternative representations of characters, that could maliciously be
|
||||
// used to bypass filters that check only for ASCII characters. For
|
||||
// instance, a 2-byte encoded line feed (LF) would not be caught by a
|
||||
// line counter that counts only 0x0a bytes, but it would still be
|
||||
// processed as a line feed by an unsafe UTF-8 decoder later in the
|
||||
// pipeline. From a security point of view, ASCII compatibility of UTF-8
|
||||
// sequences means also, that ASCII characters are *only* allowed to be
|
||||
// represented by ASCII bytes in the range 0x00-0x7f. To ensure this
|
||||
// aspect of ASCII compatibility, use only "safe UTF-8 decoders" that
|
||||
// reject overlong UTF-8 sequences for which a shorter encoding exists.
|
||||
|
||||
SECTION("4.1 Examples of an overlong ASCII character")
|
||||
{
|
||||
// With a safe UTF-8 decoder, all of the following five overlong
|
||||
// representations of the ASCII character slash ("/") should be rejected
|
||||
// like a malformed UTF-8 sequence, for instance by substituting it with
|
||||
// a replacement character. If you see a slash below, you do not have a
|
||||
// safe UTF-8 decoder!
|
||||
|
||||
// 4.1.1 U+002F = c0 af
|
||||
roundtrip(false, "\xc0\xaf");
|
||||
// 4.1.2 U+002F = e0 80 af
|
||||
roundtrip(false, "\xe0\x80\xaf");
|
||||
// 4.1.3 U+002F = f0 80 80 af
|
||||
roundtrip(false, "\xf0\x80\x80\xaf");
|
||||
// 4.1.4 U+002F = f8 80 80 80 af
|
||||
roundtrip(false, "\xf8\x80\x80\x80\xaf");
|
||||
// 4.1.5 U+002F = fc 80 80 80 80 af
|
||||
roundtrip(false, "\xfc\x80\x80\x80\x80\xaf");
|
||||
}
|
||||
|
||||
SECTION("4.2 Maximum overlong sequences")
|
||||
{
|
||||
// Below you see the highest Unicode value that is still resulting in an
|
||||
// overlong sequence if represented with the given number of bytes. This
|
||||
// is a boundary test for safe UTF-8 decoders. All five characters should
|
||||
// be rejected like malformed UTF-8 sequences.
|
||||
|
||||
// 4.2.1 U-0000007F = c1 bf
|
||||
roundtrip(false, "\xc1\xbf");
|
||||
// 4.2.2 U-000007FF = e0 9f bf
|
||||
roundtrip(false, "\xe0\x9f\xbf");
|
||||
// 4.2.3 U-0000FFFF = f0 8f bf bf
|
||||
roundtrip(false, "\xf0\x8f\xbf\xbf");
|
||||
// 4.2.4 U-001FFFFF = f8 87 bf bf bf
|
||||
roundtrip(false, "\xf8\x87\xbf\xbf\xbf");
|
||||
// 4.2.5 U-03FFFFFF = fc 83 bf bf bf bf
|
||||
roundtrip(false, "\xfc\x83\xbf\xbf\xbf\xbf");
|
||||
}
|
||||
|
||||
SECTION("4.3 Overlong representation of the NUL character")
|
||||
{
|
||||
// The following five sequences should also be rejected like malformed
|
||||
// UTF-8 sequences and should not be treated like the ASCII NUL
|
||||
// character.
|
||||
|
||||
// 4.3.1 U+0000 = c0 80
|
||||
roundtrip(false, "\xc0\x80");
|
||||
// 4.3.2 U+0000 = e0 80 80
|
||||
roundtrip(false, "\xe0\x80\x80");
|
||||
// 4.3.3 U+0000 = f0 80 80 80
|
||||
roundtrip(false, "\xf0\x80\x80\x80");
|
||||
// 4.3.4 U+0000 = f8 80 80 80 80
|
||||
roundtrip(false, "\xf8\x80\x80\x80\x80");
|
||||
// 4.3.5 U+0000 = fc 80 80 80 80 80
|
||||
roundtrip(false, "\xfc\x80\x80\x80\x80\x80");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("5 Illegal code positions")
|
||||
{
|
||||
// The following UTF-8 sequences should be rejected like malformed
|
||||
// sequences, because they never represent valid ISO 10646 characters and
|
||||
// a UTF-8 decoder that accepts them might introduce security problems
|
||||
// comparable to overlong UTF-8 sequences.
|
||||
|
||||
SECTION("5.1 Single UTF-16 surrogates")
|
||||
{
|
||||
// 5.1.1 U+D800 = ed a0 80
|
||||
roundtrip(false, "\xed\xa0\x80");
|
||||
// 5.1.2 U+DB7F = ed ad bf
|
||||
roundtrip(false, "\xed\xad\xbf");
|
||||
// 5.1.3 U+DB80 = ed ae 80
|
||||
roundtrip(false, "\xed\xae\x80");
|
||||
// 5.1.4 U+DBFF = ed af bf
|
||||
roundtrip(false, "\xed\xaf\xbf");
|
||||
// 5.1.5 U+DC00 = ed b0 80
|
||||
roundtrip(false, "\xed\xb0\x80");
|
||||
// 5.1.6 U+DF80 = ed be 80
|
||||
roundtrip(false, "\xed\xbe\x80");
|
||||
// 5.1.7 U+DFFF = ed bf bf
|
||||
roundtrip(false, "\xed\xbf\xbf");
|
||||
}
|
||||
|
||||
SECTION("5.2 Paired UTF-16 surrogates")
|
||||
{
|
||||
// 5.2.1 U+D800 U+DC00 = ed a0 80 ed b0 80
|
||||
roundtrip(false, "\xed\xa0\x80\xed\xb0\x80");
|
||||
// 5.2.2 U+D800 U+DFFF = ed a0 80 ed bf bf
|
||||
roundtrip(false, "\xed\xa0\x80\xed\xbf\xbf");
|
||||
// 5.2.3 U+DB7F U+DC00 = ed ad bf ed b0 80
|
||||
roundtrip(false, "\xed\xad\xbf\xed\xb0\x80");
|
||||
// 5.2.4 U+DB7F U+DFFF = ed ad bf ed bf bf
|
||||
roundtrip(false, "\xed\xad\xbf\xed\xbf\xbf");
|
||||
// 5.2.5 U+DB80 U+DC00 = ed ae 80 ed b0 80
|
||||
roundtrip(false, "\xed\xae\x80\xed\xb0\x80");
|
||||
// 5.2.6 U+DB80 U+DFFF = ed ae 80 ed bf bf
|
||||
roundtrip(false, "\xed\xae\x80\xed\xbf\xbf");
|
||||
// 5.2.7 U+DBFF U+DC00 = ed af bf ed b0 80
|
||||
roundtrip(false, "\xed\xaf\xbf\xed\xb0\x80");
|
||||
// 5.2.8 U+DBFF U+DFFF = ed af bf ed bf bf
|
||||
roundtrip(false, "\xed\xaf\xbf\xed\xbf\xbf");
|
||||
}
|
||||
|
||||
SECTION("5.3 Noncharacter code positions")
|
||||
{
|
||||
// The following "noncharacters" are "reserved for internal use" by
|
||||
// applications, and according to older versions of the Unicode Standard
|
||||
// "should never be interchanged". Unicode Corrigendum #9 dropped the
|
||||
// latter restriction. Nevertheless, their presence in incoming UTF-8 data
|
||||
// can remain a potential security risk, depending on what use is made of
|
||||
// these codes subsequently. Examples of such internal use:
|
||||
//
|
||||
// - Some file APIs with 16-bit characters may use the integer value -1
|
||||
// = U+FFFF to signal an end-of-file (EOF) or error condition.
|
||||
//
|
||||
// - In some UTF-16 receivers, code point U+FFFE might trigger a
|
||||
// byte-swap operation (to convert between UTF-16LE and UTF-16BE).
|
||||
//
|
||||
// With such internal use of noncharacters, it may be desirable and safer
|
||||
// to block those code points in UTF-8 decoders, as they should never
|
||||
// occur legitimately in incoming UTF-8 data, and could trigger unsafe
|
||||
// behaviour in subsequent processing.
|
||||
|
||||
// Particularly problematic noncharacters in 16-bit applications:
|
||||
|
||||
// 5.3.1 U+FFFE = ef bf be
|
||||
roundtrip(true, "\xef\xbf\xbe");
|
||||
// 5.3.2 U+FFFF = ef bf bf
|
||||
roundtrip(true, "\xef\xbf\xbf");
|
||||
|
||||
// 5.3.3 U+FDD0 .. U+FDEF
|
||||
roundtrip(true, "\xEF\xB7\x90");
|
||||
roundtrip(true, "\xEF\xB7\x91");
|
||||
roundtrip(true, "\xEF\xB7\x92");
|
||||
roundtrip(true, "\xEF\xB7\x93");
|
||||
roundtrip(true, "\xEF\xB7\x94");
|
||||
roundtrip(true, "\xEF\xB7\x95");
|
||||
roundtrip(true, "\xEF\xB7\x96");
|
||||
roundtrip(true, "\xEF\xB7\x97");
|
||||
roundtrip(true, "\xEF\xB7\x98");
|
||||
roundtrip(true, "\xEF\xB7\x99");
|
||||
roundtrip(true, "\xEF\xB7\x9A");
|
||||
roundtrip(true, "\xEF\xB7\x9B");
|
||||
roundtrip(true, "\xEF\xB7\x9C");
|
||||
roundtrip(true, "\xEF\xB7\x9D");
|
||||
roundtrip(true, "\xEF\xB7\x9E");
|
||||
roundtrip(true, "\xEF\xB7\x9F");
|
||||
roundtrip(true, "\xEF\xB7\xA0");
|
||||
roundtrip(true, "\xEF\xB7\xA1");
|
||||
roundtrip(true, "\xEF\xB7\xA2");
|
||||
roundtrip(true, "\xEF\xB7\xA3");
|
||||
roundtrip(true, "\xEF\xB7\xA4");
|
||||
roundtrip(true, "\xEF\xB7\xA5");
|
||||
roundtrip(true, "\xEF\xB7\xA6");
|
||||
roundtrip(true, "\xEF\xB7\xA7");
|
||||
roundtrip(true, "\xEF\xB7\xA8");
|
||||
roundtrip(true, "\xEF\xB7\xA9");
|
||||
roundtrip(true, "\xEF\xB7\xAA");
|
||||
roundtrip(true, "\xEF\xB7\xAB");
|
||||
roundtrip(true, "\xEF\xB7\xAC");
|
||||
roundtrip(true, "\xEF\xB7\xAD");
|
||||
roundtrip(true, "\xEF\xB7\xAE");
|
||||
roundtrip(true, "\xEF\xB7\xAF");
|
||||
|
||||
// 5.3.4 U+nFFFE U+nFFFF (for n = 1..10)
|
||||
roundtrip(true, "\xF0\x9F\xBF\xBF");
|
||||
roundtrip(true, "\xF0\xAF\xBF\xBF");
|
||||
roundtrip(true, "\xF0\xBF\xBF\xBF");
|
||||
roundtrip(true, "\xF1\x8F\xBF\xBF");
|
||||
roundtrip(true, "\xF1\x9F\xBF\xBF");
|
||||
roundtrip(true, "\xF1\xAF\xBF\xBF");
|
||||
roundtrip(true, "\xF1\xBF\xBF\xBF");
|
||||
roundtrip(true, "\xF2\x8F\xBF\xBF");
|
||||
roundtrip(true, "\xF2\x9F\xBF\xBF");
|
||||
roundtrip(true, "\xF2\xAF\xBF\xBF");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,631 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
// for some reason including this after the json header leads to linker errors with VS 2017...
|
||||
#include <locale>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <test_data.hpp>
|
||||
|
||||
// this test suite uses static variables with non-trivial destructors
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
|
||||
|
||||
namespace
|
||||
{
|
||||
extern size_t calls;
|
||||
size_t calls = 0;
|
||||
|
||||
void check_utf8dump(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||
|
||||
void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
|
||||
{
|
||||
static std::string json_string;
|
||||
json_string.clear();
|
||||
|
||||
CAPTURE(byte1)
|
||||
CAPTURE(byte2)
|
||||
CAPTURE(byte3)
|
||||
CAPTURE(byte4)
|
||||
|
||||
json_string += std::string(1, static_cast<char>(byte1));
|
||||
|
||||
if (byte2 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte2));
|
||||
}
|
||||
|
||||
if (byte3 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte3));
|
||||
}
|
||||
|
||||
if (byte4 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte4));
|
||||
}
|
||||
|
||||
CAPTURE(json_string)
|
||||
|
||||
// store the string in a JSON value
|
||||
static json j;
|
||||
static json j2;
|
||||
j = json_string;
|
||||
j2 = "abc" + json_string + "xyz";
|
||||
|
||||
static std::string s_ignored;
|
||||
static std::string s_ignored2;
|
||||
static std::string s_ignored_ascii;
|
||||
static std::string s_ignored2_ascii;
|
||||
static std::string s_replaced;
|
||||
static std::string s_replaced2;
|
||||
static std::string s_replaced_ascii;
|
||||
static std::string s_replaced2_ascii;
|
||||
|
||||
// dumping with ignore/replace must not throw in any case
|
||||
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||
s_ignored2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||
s_replaced = j.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||
|
||||
if (success_expected)
|
||||
{
|
||||
static std::string s_strict;
|
||||
// strict mode must not throw if success is expected
|
||||
s_strict = j.dump();
|
||||
// all dumps should agree on the string
|
||||
CHECK(s_strict == s_ignored);
|
||||
CHECK(s_strict == s_replaced);
|
||||
}
|
||||
else
|
||||
{
|
||||
// strict mode must throw if success is not expected
|
||||
CHECK_THROWS_AS(j.dump(), json::type_error&);
|
||||
// ignore and replace must create different dumps
|
||||
CHECK(s_ignored != s_replaced);
|
||||
|
||||
// check that replace string contains a replacement character
|
||||
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
||||
}
|
||||
|
||||
// check that prefix and suffix are preserved
|
||||
CHECK(s_ignored2.substr(1, 3) == "abc");
|
||||
CHECK(s_ignored2.substr(s_ignored2.size() - 4, 3) == "xyz");
|
||||
CHECK(s_ignored2_ascii.substr(1, 3) == "abc");
|
||||
CHECK(s_ignored2_ascii.substr(s_ignored2_ascii.size() - 4, 3) == "xyz");
|
||||
CHECK(s_replaced2.substr(1, 3) == "abc");
|
||||
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
||||
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
||||
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
||||
}
|
||||
|
||||
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||
|
||||
// create and check a JSON string with up to four UTF-8 bytes
|
||||
void check_utf8string(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
|
||||
{
|
||||
if (++calls % 100000 == 0)
|
||||
{
|
||||
std::cout << calls << " of 455355 UTF-8 strings checked" << std::endl;
|
||||
}
|
||||
|
||||
static std::string json_string;
|
||||
json_string = "\"";
|
||||
|
||||
CAPTURE(byte1)
|
||||
json_string += std::string(1, static_cast<char>(byte1));
|
||||
|
||||
if (byte2 != -1)
|
||||
{
|
||||
CAPTURE(byte2)
|
||||
json_string += std::string(1, static_cast<char>(byte2));
|
||||
}
|
||||
|
||||
if (byte3 != -1)
|
||||
{
|
||||
CAPTURE(byte3)
|
||||
json_string += std::string(1, static_cast<char>(byte3));
|
||||
}
|
||||
|
||||
if (byte4 != -1)
|
||||
{
|
||||
CAPTURE(byte4)
|
||||
json_string += std::string(1, static_cast<char>(byte4));
|
||||
}
|
||||
|
||||
json_string += "\"";
|
||||
|
||||
CAPTURE(json_string)
|
||||
|
||||
json _;
|
||||
if (success_expected)
|
||||
{
|
||||
CHECK_NOTHROW(_ = json::parse(json_string));
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_THROWS_AS(_ = json::parse(json_string), json::parse_error&);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Unicode (2/5)" * doctest::skip())
|
||||
{
|
||||
SECTION("RFC 3629")
|
||||
{
|
||||
/*
|
||||
RFC 3629 describes in Sect. 4 the syntax of UTF-8 byte sequences as
|
||||
follows:
|
||||
|
||||
A UTF-8 string is a sequence of octets representing a sequence of UCS
|
||||
characters. An octet sequence is valid UTF-8 only if it matches the
|
||||
following syntax, which is derived from the rules for encoding UTF-8
|
||||
and is expressed in the ABNF of [RFC2234].
|
||||
|
||||
UTF8-octets = *( UTF8-char )
|
||||
UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
|
||||
UTF8-1 = %x00-7F
|
||||
UTF8-2 = %xC2-DF UTF8-tail
|
||||
UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
|
||||
%xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
|
||||
UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
|
||||
%xF4 %x80-8F 2( UTF8-tail )
|
||||
UTF8-tail = %x80-BF
|
||||
*/
|
||||
|
||||
SECTION("ill-formed first byte")
|
||||
{
|
||||
for (int byte1 = 0x80; byte1 <= 0xC1; ++byte1)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
check_utf8dump(false, byte1);
|
||||
}
|
||||
|
||||
for (int byte1 = 0xF5; byte1 <= 0xFF; ++byte1)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
check_utf8dump(false, byte1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("UTF8-1 (x00-x7F)")
|
||||
{
|
||||
SECTION("well-formed")
|
||||
{
|
||||
for (int byte1 = 0x00; byte1 <= 0x7F; ++byte1)
|
||||
{
|
||||
// unescaped control characters are parse errors in JSON
|
||||
if (0x00 <= byte1 && byte1 <= 0x1F)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// a single quote is a parse error in JSON
|
||||
if (byte1 == 0x22)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// a single backslash is a parse error in JSON
|
||||
if (byte1 == 0x5C)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// all other characters are OK
|
||||
check_utf8string(true, byte1);
|
||||
check_utf8dump(true, byte1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("UTF8-2 (xC2-xDF UTF8-tail)")
|
||||
{
|
||||
SECTION("well-formed")
|
||||
{
|
||||
for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
check_utf8string(true, byte1, byte2);
|
||||
check_utf8dump(true, byte1, byte2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing second byte")
|
||||
{
|
||||
for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
check_utf8dump(false, byte1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong second byte")
|
||||
{
|
||||
for (int byte1 = 0xC2; byte1 <= 0xDF; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
|
||||
{
|
||||
// skip correct second byte
|
||||
if (0x80 <= byte2 && byte2 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
check_utf8string(false, byte1, byte2);
|
||||
check_utf8dump(false, byte1, byte2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("UTF8-3 (xE0 xA0-BF UTF8-tail)")
|
||||
{
|
||||
SECTION("well-formed")
|
||||
{
|
||||
for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
check_utf8string(true, byte1, byte2, byte3);
|
||||
check_utf8dump(true, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing second byte")
|
||||
{
|
||||
for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
check_utf8dump(false, byte1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing third byte")
|
||||
{
|
||||
for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2);
|
||||
check_utf8dump(false, byte1, byte2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong second byte")
|
||||
{
|
||||
for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
|
||||
{
|
||||
// skip correct second byte
|
||||
if (0xA0 <= byte2 && byte2 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3);
|
||||
check_utf8dump(false, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong third byte")
|
||||
{
|
||||
for (int byte1 = 0xE0; byte1 <= 0xE0; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0xA0; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
|
||||
{
|
||||
// skip correct third byte
|
||||
if (0x80 <= byte3 && byte3 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
check_utf8string(false, byte1, byte2, byte3);
|
||||
check_utf8dump(false, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("UTF8-3 (xE1-xEC UTF8-tail UTF8-tail)")
|
||||
{
|
||||
SECTION("well-formed")
|
||||
{
|
||||
for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
check_utf8string(true, byte1, byte2, byte3);
|
||||
check_utf8dump(true, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing second byte")
|
||||
{
|
||||
for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
check_utf8dump(false, byte1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing third byte")
|
||||
{
|
||||
for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2);
|
||||
check_utf8dump(false, byte1, byte2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong second byte")
|
||||
{
|
||||
for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
|
||||
{
|
||||
// skip correct second byte
|
||||
if (0x80 <= byte2 && byte2 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3);
|
||||
check_utf8dump(false, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong third byte")
|
||||
{
|
||||
for (int byte1 = 0xE1; byte1 <= 0xEC; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
|
||||
{
|
||||
// skip correct third byte
|
||||
if (0x80 <= byte3 && byte3 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
check_utf8string(false, byte1, byte2, byte3);
|
||||
check_utf8dump(false, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("UTF8-3 (xED x80-9F UTF8-tail)")
|
||||
{
|
||||
SECTION("well-formed")
|
||||
{
|
||||
for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
check_utf8string(true, byte1, byte2, byte3);
|
||||
check_utf8dump(true, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing second byte")
|
||||
{
|
||||
for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
check_utf8dump(false, byte1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing third byte")
|
||||
{
|
||||
for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2);
|
||||
check_utf8dump(false, byte1, byte2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong second byte")
|
||||
{
|
||||
for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
|
||||
{
|
||||
// skip correct second byte
|
||||
if (0x80 <= byte2 && byte2 <= 0x9F)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3);
|
||||
check_utf8dump(false, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong third byte")
|
||||
{
|
||||
for (int byte1 = 0xED; byte1 <= 0xED; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0x9F; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
|
||||
{
|
||||
// skip correct third byte
|
||||
if (0x80 <= byte3 && byte3 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
check_utf8string(false, byte1, byte2, byte3);
|
||||
check_utf8dump(false, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("UTF8-3 (xEE-xEF UTF8-tail UTF8-tail)")
|
||||
{
|
||||
SECTION("well-formed")
|
||||
{
|
||||
for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
check_utf8string(true, byte1, byte2, byte3);
|
||||
check_utf8dump(true, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing second byte")
|
||||
{
|
||||
for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
check_utf8dump(false, byte1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing third byte")
|
||||
{
|
||||
for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2);
|
||||
check_utf8dump(false, byte1, byte2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong second byte")
|
||||
{
|
||||
for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
|
||||
{
|
||||
// skip correct second byte
|
||||
if (0x80 <= byte2 && byte2 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3);
|
||||
check_utf8dump(false, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong third byte")
|
||||
{
|
||||
for (int byte1 = 0xEE; byte1 <= 0xEF; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
|
||||
{
|
||||
// skip correct third byte
|
||||
if (0x80 <= byte3 && byte3 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
check_utf8string(false, byte1, byte2, byte3);
|
||||
check_utf8dump(false, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_POP
|
||||
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
// for some reason including this after the json header leads to linker errors with VS 2017...
|
||||
#include <locale>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <test_data.hpp>
|
||||
|
||||
// this test suite uses static variables with non-trivial destructors
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
|
||||
|
||||
namespace
|
||||
{
|
||||
extern size_t calls;
|
||||
size_t calls = 0;
|
||||
|
||||
void check_utf8dump(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||
|
||||
void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
|
||||
{
|
||||
static std::string json_string;
|
||||
json_string.clear();
|
||||
|
||||
CAPTURE(byte1)
|
||||
CAPTURE(byte2)
|
||||
CAPTURE(byte3)
|
||||
CAPTURE(byte4)
|
||||
|
||||
json_string += std::string(1, static_cast<char>(byte1));
|
||||
|
||||
if (byte2 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte2));
|
||||
}
|
||||
|
||||
if (byte3 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte3));
|
||||
}
|
||||
|
||||
if (byte4 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte4));
|
||||
}
|
||||
|
||||
CAPTURE(json_string)
|
||||
|
||||
// store the string in a JSON value
|
||||
static json j;
|
||||
static json j2;
|
||||
j = json_string;
|
||||
j2 = "abc" + json_string + "xyz";
|
||||
|
||||
static std::string s_ignored;
|
||||
static std::string s_ignored2;
|
||||
static std::string s_ignored_ascii;
|
||||
static std::string s_ignored2_ascii;
|
||||
static std::string s_replaced;
|
||||
static std::string s_replaced2;
|
||||
static std::string s_replaced_ascii;
|
||||
static std::string s_replaced2_ascii;
|
||||
|
||||
// dumping with ignore/replace must not throw in any case
|
||||
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||
s_ignored2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||
s_replaced = j.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||
|
||||
if (success_expected)
|
||||
{
|
||||
static std::string s_strict;
|
||||
// strict mode must not throw if success is expected
|
||||
s_strict = j.dump();
|
||||
// all dumps should agree on the string
|
||||
CHECK(s_strict == s_ignored);
|
||||
CHECK(s_strict == s_replaced);
|
||||
}
|
||||
else
|
||||
{
|
||||
// strict mode must throw if success is not expected
|
||||
CHECK_THROWS_AS(j.dump(), json::type_error&);
|
||||
// ignore and replace must create different dumps
|
||||
CHECK(s_ignored != s_replaced);
|
||||
|
||||
// check that replace string contains a replacement character
|
||||
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
||||
}
|
||||
|
||||
// check that prefix and suffix are preserved
|
||||
CHECK(s_ignored2.substr(1, 3) == "abc");
|
||||
CHECK(s_ignored2.substr(s_ignored2.size() - 4, 3) == "xyz");
|
||||
CHECK(s_ignored2_ascii.substr(1, 3) == "abc");
|
||||
CHECK(s_ignored2_ascii.substr(s_ignored2_ascii.size() - 4, 3) == "xyz");
|
||||
CHECK(s_replaced2.substr(1, 3) == "abc");
|
||||
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
||||
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
||||
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
||||
}
|
||||
|
||||
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||
|
||||
// create and check a JSON string with up to four UTF-8 bytes
|
||||
void check_utf8string(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
|
||||
{
|
||||
if (++calls % 100000 == 0)
|
||||
{
|
||||
std::cout << calls << " of 1641521 UTF-8 strings checked" << std::endl;
|
||||
}
|
||||
|
||||
static std::string json_string;
|
||||
json_string = "\"";
|
||||
|
||||
CAPTURE(byte1)
|
||||
json_string += std::string(1, static_cast<char>(byte1));
|
||||
|
||||
if (byte2 != -1)
|
||||
{
|
||||
CAPTURE(byte2)
|
||||
json_string += std::string(1, static_cast<char>(byte2));
|
||||
}
|
||||
|
||||
if (byte3 != -1)
|
||||
{
|
||||
CAPTURE(byte3)
|
||||
json_string += std::string(1, static_cast<char>(byte3));
|
||||
}
|
||||
|
||||
if (byte4 != -1)
|
||||
{
|
||||
CAPTURE(byte4)
|
||||
json_string += std::string(1, static_cast<char>(byte4));
|
||||
}
|
||||
|
||||
json_string += "\"";
|
||||
|
||||
CAPTURE(json_string)
|
||||
|
||||
json _;
|
||||
if (success_expected)
|
||||
{
|
||||
CHECK_NOTHROW(_ = json::parse(json_string));
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_THROWS_AS(_ = json::parse(json_string), json::parse_error&);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Unicode (3/5)" * doctest::skip())
|
||||
{
|
||||
SECTION("RFC 3629")
|
||||
{
|
||||
/*
|
||||
RFC 3629 describes in Sect. 4 the syntax of UTF-8 byte sequences as
|
||||
follows:
|
||||
|
||||
A UTF-8 string is a sequence of octets representing a sequence of UCS
|
||||
characters. An octet sequence is valid UTF-8 only if it matches the
|
||||
following syntax, which is derived from the rules for encoding UTF-8
|
||||
and is expressed in the ABNF of [RFC2234].
|
||||
|
||||
UTF8-octets = *( UTF8-char )
|
||||
UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
|
||||
UTF8-1 = %x00-7F
|
||||
UTF8-2 = %xC2-DF UTF8-tail
|
||||
UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
|
||||
%xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
|
||||
UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
|
||||
%xF4 %x80-8F 2( UTF8-tail )
|
||||
UTF8-tail = %x80-BF
|
||||
*/
|
||||
|
||||
SECTION("UTF8-4 (xF0 x90-BF UTF8-tail UTF8-tail)")
|
||||
{
|
||||
SECTION("well-formed")
|
||||
{
|
||||
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
|
||||
{
|
||||
check_utf8string(true, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(true, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing second byte")
|
||||
{
|
||||
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
check_utf8dump(false, byte1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing third byte")
|
||||
{
|
||||
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2);
|
||||
check_utf8dump(false, byte1, byte2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing fourth byte")
|
||||
{
|
||||
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3);
|
||||
check_utf8dump(false, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong second byte")
|
||||
{
|
||||
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
|
||||
{
|
||||
// skip correct second byte
|
||||
if (0x90 <= byte2 && byte2 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(false, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong third byte")
|
||||
{
|
||||
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
|
||||
{
|
||||
// skip correct third byte
|
||||
if (0x80 <= byte3 && byte3 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(false, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong fourth byte")
|
||||
{
|
||||
for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
for (int byte4 = 0x00; byte4 <= 0xFF; ++byte4)
|
||||
{
|
||||
// skip fourth second byte
|
||||
if (0x80 <= byte3 && byte3 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
check_utf8string(false, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(false, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_POP
|
||||
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
// for some reason including this after the json header leads to linker errors with VS 2017...
|
||||
#include <locale>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <test_data.hpp>
|
||||
|
||||
// this test suite uses static variables with non-trivial destructors
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
|
||||
|
||||
namespace
|
||||
{
|
||||
extern size_t calls;
|
||||
size_t calls = 0;
|
||||
|
||||
void check_utf8dump(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||
|
||||
void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
|
||||
{
|
||||
static std::string json_string;
|
||||
json_string.clear();
|
||||
|
||||
CAPTURE(byte1)
|
||||
CAPTURE(byte2)
|
||||
CAPTURE(byte3)
|
||||
CAPTURE(byte4)
|
||||
|
||||
json_string += std::string(1, static_cast<char>(byte1));
|
||||
|
||||
if (byte2 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte2));
|
||||
}
|
||||
|
||||
if (byte3 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte3));
|
||||
}
|
||||
|
||||
if (byte4 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte4));
|
||||
}
|
||||
|
||||
CAPTURE(json_string)
|
||||
|
||||
// store the string in a JSON value
|
||||
static json j;
|
||||
static json j2;
|
||||
j = json_string;
|
||||
j2 = "abc" + json_string + "xyz";
|
||||
|
||||
static std::string s_ignored;
|
||||
static std::string s_ignored2;
|
||||
static std::string s_ignored_ascii;
|
||||
static std::string s_ignored2_ascii;
|
||||
static std::string s_replaced;
|
||||
static std::string s_replaced2;
|
||||
static std::string s_replaced_ascii;
|
||||
static std::string s_replaced2_ascii;
|
||||
|
||||
// dumping with ignore/replace must not throw in any case
|
||||
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||
s_ignored2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||
s_replaced = j.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||
|
||||
if (success_expected)
|
||||
{
|
||||
static std::string s_strict;
|
||||
// strict mode must not throw if success is expected
|
||||
s_strict = j.dump();
|
||||
// all dumps should agree on the string
|
||||
CHECK(s_strict == s_ignored);
|
||||
CHECK(s_strict == s_replaced);
|
||||
}
|
||||
else
|
||||
{
|
||||
// strict mode must throw if success is not expected
|
||||
CHECK_THROWS_AS(j.dump(), json::type_error&);
|
||||
// ignore and replace must create different dumps
|
||||
CHECK(s_ignored != s_replaced);
|
||||
|
||||
// check that replace string contains a replacement character
|
||||
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
||||
}
|
||||
|
||||
// check that prefix and suffix are preserved
|
||||
CHECK(s_ignored2.substr(1, 3) == "abc");
|
||||
CHECK(s_ignored2.substr(s_ignored2.size() - 4, 3) == "xyz");
|
||||
CHECK(s_ignored2_ascii.substr(1, 3) == "abc");
|
||||
CHECK(s_ignored2_ascii.substr(s_ignored2_ascii.size() - 4, 3) == "xyz");
|
||||
CHECK(s_replaced2.substr(1, 3) == "abc");
|
||||
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
||||
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
||||
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
||||
}
|
||||
|
||||
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||
|
||||
// create and check a JSON string with up to four UTF-8 bytes
|
||||
void check_utf8string(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
|
||||
{
|
||||
if (++calls % 100000 == 0)
|
||||
{
|
||||
std::cout << calls << " of 5517507 UTF-8 strings checked" << std::endl;
|
||||
}
|
||||
|
||||
static std::string json_string;
|
||||
json_string = "\"";
|
||||
|
||||
CAPTURE(byte1)
|
||||
json_string += std::string(1, static_cast<char>(byte1));
|
||||
|
||||
if (byte2 != -1)
|
||||
{
|
||||
CAPTURE(byte2)
|
||||
json_string += std::string(1, static_cast<char>(byte2));
|
||||
}
|
||||
|
||||
if (byte3 != -1)
|
||||
{
|
||||
CAPTURE(byte3)
|
||||
json_string += std::string(1, static_cast<char>(byte3));
|
||||
}
|
||||
|
||||
if (byte4 != -1)
|
||||
{
|
||||
CAPTURE(byte4)
|
||||
json_string += std::string(1, static_cast<char>(byte4));
|
||||
}
|
||||
|
||||
json_string += "\"";
|
||||
|
||||
CAPTURE(json_string)
|
||||
|
||||
json _;
|
||||
if (success_expected)
|
||||
{
|
||||
CHECK_NOTHROW(_ = json::parse(json_string));
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_THROWS_AS(_ = json::parse(json_string), json::parse_error&);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Unicode (4/5)" * doctest::skip())
|
||||
{
|
||||
SECTION("RFC 3629")
|
||||
{
|
||||
/*
|
||||
RFC 3629 describes in Sect. 4 the syntax of UTF-8 byte sequences as
|
||||
follows:
|
||||
|
||||
A UTF-8 string is a sequence of octets representing a sequence of UCS
|
||||
characters. An octet sequence is valid UTF-8 only if it matches the
|
||||
following syntax, which is derived from the rules for encoding UTF-8
|
||||
and is expressed in the ABNF of [RFC2234].
|
||||
|
||||
UTF8-octets = *( UTF8-char )
|
||||
UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
|
||||
UTF8-1 = %x00-7F
|
||||
UTF8-2 = %xC2-DF UTF8-tail
|
||||
UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
|
||||
%xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
|
||||
UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
|
||||
%xF4 %x80-8F 2( UTF8-tail )
|
||||
UTF8-tail = %x80-BF
|
||||
*/
|
||||
|
||||
SECTION("UTF8-4 (xF1-F3 UTF8-tail UTF8-tail UTF8-tail)")
|
||||
{
|
||||
SECTION("well-formed")
|
||||
{
|
||||
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
|
||||
{
|
||||
check_utf8string(true, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(true, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing second byte")
|
||||
{
|
||||
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
check_utf8dump(false, byte1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing third byte")
|
||||
{
|
||||
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2);
|
||||
check_utf8dump(false, byte1, byte2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing fourth byte")
|
||||
{
|
||||
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3);
|
||||
check_utf8dump(false, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong second byte")
|
||||
{
|
||||
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
|
||||
{
|
||||
// skip correct second byte
|
||||
if (0x80 <= byte2 && byte2 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(false, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong third byte")
|
||||
{
|
||||
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
|
||||
{
|
||||
// skip correct third byte
|
||||
if (0x80 <= byte3 && byte3 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(false, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong fourth byte")
|
||||
{
|
||||
for (int byte1 = 0xF1; byte1 <= 0xF3; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0xBF; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
for (int byte4 = 0x00; byte4 <= 0xFF; ++byte4)
|
||||
{
|
||||
// skip correct fourth byte
|
||||
if (0x80 <= byte3 && byte3 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
check_utf8string(false, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(false, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_POP
|
||||
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
// for some reason including this after the json header leads to linker errors with VS 2017...
|
||||
#include <locale>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <test_data.hpp>
|
||||
|
||||
// this test suite uses static variables with non-trivial destructors
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
|
||||
|
||||
namespace
|
||||
{
|
||||
extern size_t calls;
|
||||
size_t calls = 0;
|
||||
|
||||
void check_utf8dump(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||
|
||||
void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
|
||||
{
|
||||
static std::string json_string;
|
||||
json_string.clear();
|
||||
|
||||
CAPTURE(byte1)
|
||||
CAPTURE(byte2)
|
||||
CAPTURE(byte3)
|
||||
CAPTURE(byte4)
|
||||
|
||||
json_string += std::string(1, static_cast<char>(byte1));
|
||||
|
||||
if (byte2 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte2));
|
||||
}
|
||||
|
||||
if (byte3 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte3));
|
||||
}
|
||||
|
||||
if (byte4 != -1)
|
||||
{
|
||||
json_string += std::string(1, static_cast<char>(byte4));
|
||||
}
|
||||
|
||||
CAPTURE(json_string)
|
||||
|
||||
// store the string in a JSON value
|
||||
static json j;
|
||||
static json j2;
|
||||
j = json_string;
|
||||
j2 = "abc" + json_string + "xyz";
|
||||
|
||||
static std::string s_ignored;
|
||||
static std::string s_ignored2;
|
||||
static std::string s_ignored_ascii;
|
||||
static std::string s_ignored2_ascii;
|
||||
static std::string s_replaced;
|
||||
static std::string s_replaced2;
|
||||
static std::string s_replaced_ascii;
|
||||
static std::string s_replaced2_ascii;
|
||||
|
||||
// dumping with ignore/replace must not throw in any case
|
||||
s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||
s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
|
||||
s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||
s_ignored2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::ignore);
|
||||
s_replaced = j.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||
s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||
s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||
s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);
|
||||
|
||||
if (success_expected)
|
||||
{
|
||||
static std::string s_strict;
|
||||
// strict mode must not throw if success is expected
|
||||
s_strict = j.dump();
|
||||
// all dumps should agree on the string
|
||||
CHECK(s_strict == s_ignored);
|
||||
CHECK(s_strict == s_replaced);
|
||||
}
|
||||
else
|
||||
{
|
||||
// strict mode must throw if success is not expected
|
||||
CHECK_THROWS_AS(j.dump(), json::type_error&);
|
||||
// ignore and replace must create different dumps
|
||||
CHECK(s_ignored != s_replaced);
|
||||
|
||||
// check that replace string contains a replacement character
|
||||
CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
|
||||
}
|
||||
|
||||
// check that prefix and suffix are preserved
|
||||
CHECK(s_ignored2.substr(1, 3) == "abc");
|
||||
CHECK(s_ignored2.substr(s_ignored2.size() - 4, 3) == "xyz");
|
||||
CHECK(s_ignored2_ascii.substr(1, 3) == "abc");
|
||||
CHECK(s_ignored2_ascii.substr(s_ignored2_ascii.size() - 4, 3) == "xyz");
|
||||
CHECK(s_replaced2.substr(1, 3) == "abc");
|
||||
CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
|
||||
CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
|
||||
CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
|
||||
}
|
||||
|
||||
void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);
|
||||
|
||||
// create and check a JSON string with up to four UTF-8 bytes
|
||||
void check_utf8string(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
|
||||
{
|
||||
if (++calls % 100000 == 0)
|
||||
{
|
||||
std::cout << calls << " of 1246225 UTF-8 strings checked" << std::endl;
|
||||
}
|
||||
|
||||
static std::string json_string;
|
||||
json_string = "\"";
|
||||
|
||||
CAPTURE(byte1)
|
||||
json_string += std::string(1, static_cast<char>(byte1));
|
||||
|
||||
if (byte2 != -1)
|
||||
{
|
||||
CAPTURE(byte2)
|
||||
json_string += std::string(1, static_cast<char>(byte2));
|
||||
}
|
||||
|
||||
if (byte3 != -1)
|
||||
{
|
||||
CAPTURE(byte3)
|
||||
json_string += std::string(1, static_cast<char>(byte3));
|
||||
}
|
||||
|
||||
if (byte4 != -1)
|
||||
{
|
||||
CAPTURE(byte4)
|
||||
json_string += std::string(1, static_cast<char>(byte4));
|
||||
}
|
||||
|
||||
json_string += "\"";
|
||||
|
||||
CAPTURE(json_string)
|
||||
|
||||
json _;
|
||||
if (success_expected)
|
||||
{
|
||||
CHECK_NOTHROW(_ = json::parse(json_string));
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_THROWS_AS(_ = json::parse(json_string), json::parse_error&);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Unicode (5/5)" * doctest::skip())
|
||||
{
|
||||
SECTION("RFC 3629")
|
||||
{
|
||||
/*
|
||||
RFC 3629 describes in Sect. 4 the syntax of UTF-8 byte sequences as
|
||||
follows:
|
||||
|
||||
A UTF-8 string is a sequence of octets representing a sequence of UCS
|
||||
characters. An octet sequence is valid UTF-8 only if it matches the
|
||||
following syntax, which is derived from the rules for encoding UTF-8
|
||||
and is expressed in the ABNF of [RFC2234].
|
||||
|
||||
UTF8-octets = *( UTF8-char )
|
||||
UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
|
||||
UTF8-1 = %x00-7F
|
||||
UTF8-2 = %xC2-DF UTF8-tail
|
||||
UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
|
||||
%xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
|
||||
UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
|
||||
%xF4 %x80-8F 2( UTF8-tail )
|
||||
UTF8-tail = %x80-BF
|
||||
*/
|
||||
|
||||
SECTION("UTF8-4 (xF4 x80-8F UTF8-tail UTF8-tail)")
|
||||
{
|
||||
SECTION("well-formed")
|
||||
{
|
||||
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
|
||||
{
|
||||
check_utf8string(true, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(true, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing second byte")
|
||||
{
|
||||
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
|
||||
{
|
||||
check_utf8string(false, byte1);
|
||||
check_utf8dump(false, byte1);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing third byte")
|
||||
{
|
||||
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2);
|
||||
check_utf8dump(false, byte1, byte2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: missing fourth byte")
|
||||
{
|
||||
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3);
|
||||
check_utf8dump(false, byte1, byte2, byte3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong second byte")
|
||||
{
|
||||
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
|
||||
{
|
||||
// skip correct second byte
|
||||
if (0x80 <= byte2 && byte2 <= 0x8F)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(false, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong third byte")
|
||||
{
|
||||
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
|
||||
{
|
||||
// skip correct third byte
|
||||
if (0x80 <= byte3 && byte3 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
|
||||
{
|
||||
check_utf8string(false, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(false, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("ill-formed: wrong fourth byte")
|
||||
{
|
||||
for (int byte1 = 0xF4; byte1 <= 0xF4; ++byte1)
|
||||
{
|
||||
for (int byte2 = 0x80; byte2 <= 0x8F; ++byte2)
|
||||
{
|
||||
for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
|
||||
{
|
||||
for (int byte4 = 0x00; byte4 <= 0xFF; ++byte4)
|
||||
{
|
||||
// skip correct fourth byte
|
||||
if (0x80 <= byte3 && byte3 <= 0xBF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
check_utf8string(false, byte1, byte2, byte3, byte4);
|
||||
check_utf8dump(false, byte1, byte2, byte3, byte4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_POP
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <list>
|
||||
|
||||
namespace
|
||||
{
|
||||
TEST_CASE("Use arbitrary stdlib container")
|
||||
{
|
||||
std::string raw_data = "[1,2,3,4]";
|
||||
std::list<char> data(raw_data.begin(), raw_data.end());
|
||||
|
||||
json as_json = json::parse(data.begin(), data.end());
|
||||
CHECK(as_json.at(0) == 1);
|
||||
CHECK(as_json.at(1) == 2);
|
||||
CHECK(as_json.at(2) == 3);
|
||||
CHECK(as_json.at(3) == 4);
|
||||
}
|
||||
|
||||
struct MyContainer
|
||||
{
|
||||
const char* data;
|
||||
};
|
||||
|
||||
const char* begin(const MyContainer& c)
|
||||
{
|
||||
return c.data;
|
||||
}
|
||||
|
||||
const char* end(const MyContainer& c)
|
||||
{
|
||||
return c.data + strlen(c.data); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
}
|
||||
|
||||
TEST_CASE("Custom container non-member begin/end")
|
||||
{
|
||||
|
||||
MyContainer data{"[1,2,3,4]"};
|
||||
json as_json = json::parse(data);
|
||||
CHECK(as_json.at(0) == 1);
|
||||
CHECK(as_json.at(1) == 2);
|
||||
CHECK(as_json.at(2) == 3);
|
||||
CHECK(as_json.at(3) == 4);
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("Custom container member begin/end")
|
||||
{
|
||||
struct MyContainer2
|
||||
{
|
||||
const char* data;
|
||||
|
||||
const char* begin() const
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
const char* end() const
|
||||
{
|
||||
return data + strlen(data); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
}
|
||||
};
|
||||
|
||||
MyContainer2 data{"[1,2,3,4]"};
|
||||
json as_json = json::parse(data);
|
||||
CHECK(as_json.at(0) == 1);
|
||||
CHECK(as_json.at(1) == 2);
|
||||
CHECK(as_json.at(2) == 3);
|
||||
CHECK(as_json.at(3) == 4);
|
||||
}
|
||||
|
||||
TEST_CASE("Custom iterator")
|
||||
{
|
||||
const char* raw_data = "[1,2,3,4]";
|
||||
|
||||
struct MyIterator
|
||||
{
|
||||
using difference_type = std::size_t;
|
||||
using value_type = char;
|
||||
using pointer = const char*;
|
||||
using reference = const char&;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
MyIterator& operator++()
|
||||
{
|
||||
++ptr; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
return *this;
|
||||
}
|
||||
|
||||
reference operator*() const
|
||||
{
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
bool operator!=(const MyIterator& rhs) const
|
||||
{
|
||||
return ptr != rhs.ptr;
|
||||
}
|
||||
|
||||
const char* ptr;
|
||||
};
|
||||
|
||||
// avoid -Wunused-local-typedefs
|
||||
CHECK(std::is_same<MyIterator::difference_type, std::size_t>::value);
|
||||
CHECK(std::is_same<MyIterator::value_type, char>::value);
|
||||
CHECK(std::is_same<MyIterator::pointer, const char*>::value);
|
||||
CHECK(std::is_same<MyIterator::reference, const char&>::value);
|
||||
CHECK(std::is_same<MyIterator::iterator_category, std::input_iterator_tag>::value);
|
||||
|
||||
MyIterator begin{raw_data};
|
||||
MyIterator end{raw_data + strlen(raw_data)}; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
|
||||
json as_json = json::parse(begin, end);
|
||||
CHECK(as_json.at(0) == 1);
|
||||
CHECK(as_json.at(1) == 2);
|
||||
CHECK(as_json.at(2) == 3);
|
||||
CHECK(as_json.at(3) == 4);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool wstring_is_utf16();
|
||||
bool wstring_is_utf16()
|
||||
{
|
||||
return (std::wstring(L"💩") == std::wstring(L"\U0001F4A9"));
|
||||
}
|
||||
|
||||
bool u16string_is_utf16();
|
||||
bool u16string_is_utf16()
|
||||
{
|
||||
return (std::u16string(u"💩") == std::u16string(u"\U0001F4A9"));
|
||||
}
|
||||
|
||||
bool u32string_is_utf32();
|
||||
bool u32string_is_utf32()
|
||||
{
|
||||
return (std::u32string(U"💩") == std::u32string(U"\U0001F4A9"));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("wide strings")
|
||||
{
|
||||
SECTION("std::wstring")
|
||||
{
|
||||
if (wstring_is_utf16())
|
||||
{
|
||||
std::wstring w = L"[12.2,\"Ⴥaäö💤🧢\"]";
|
||||
json j = json::parse(w);
|
||||
CHECK(j.dump() == "[12.2,\"Ⴥaäö💤🧢\"]");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("invalid std::wstring")
|
||||
{
|
||||
if (wstring_is_utf16())
|
||||
{
|
||||
std::wstring w = L"\"\xDBFF";
|
||||
json _;
|
||||
CHECK_THROWS_AS(_ = json::parse(w), json::parse_error&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("std::u16string")
|
||||
{
|
||||
if (u16string_is_utf16())
|
||||
{
|
||||
std::u16string w = u"[12.2,\"Ⴥaäö💤🧢\"]";
|
||||
json j = json::parse(w);
|
||||
CHECK(j.dump() == "[12.2,\"Ⴥaäö💤🧢\"]");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("invalid std::u16string")
|
||||
{
|
||||
if (wstring_is_utf16())
|
||||
{
|
||||
std::u16string w = u"\"\xDBFF";
|
||||
json _;
|
||||
CHECK_THROWS_AS(_ = json::parse(w), json::parse_error&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("std::u32string")
|
||||
{
|
||||
if (u32string_is_utf32())
|
||||
{
|
||||
std::u32string w = U"[12.2,\"Ⴥaäö💤🧢\"]";
|
||||
json j = json::parse(w);
|
||||
CHECK(j.dump() == "[12.2,\"Ⴥaäö💤🧢\"]");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("invalid std::u32string")
|
||||
{
|
||||
if (u32string_is_utf32())
|
||||
{
|
||||
std::u32string w = U"\"\x110000";
|
||||
json _;
|
||||
CHECK_THROWS_AS(_ = json::parse(w), json::parse_error&);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++ (test suite)
|
||||
| | |__ | | | | | | version 3.10.5
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2013-2022 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 DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#include "doctest_compatibility.h"
|
||||
Reference in New Issue
Block a user