221 lines
6.7 KiB
C++
221 lines
6.7 KiB
C++
// __ _____ _____ _____
|
|
// __| | __| | | | JSON for Modern C++ (supporting code)
|
|
// | | |__ | | | | | | version 3.12.0
|
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
//
|
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#include "doctest_compatibility.h"
|
|
|
|
// This file tests the opt-in JSON_BRACE_INIT_COPY_SEMANTICS, so it defines the
|
|
// macro itself rather than relying on a -D flag, and runs in every build.
|
|
#ifdef JSON_BRACE_INIT_COPY_SEMANTICS
|
|
#undef JSON_BRACE_INIT_COPY_SEMANTICS
|
|
#endif
|
|
|
|
#define JSON_BRACE_INIT_COPY_SEMANTICS 1
|
|
|
|
#include <nlohmann/json.hpp>
|
|
using nlohmann::json;
|
|
|
|
#include <array>
|
|
#include <list>
|
|
#include <map>
|
|
#include <string>
|
|
#include <tuple>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#define STRINGIZE_EX(x) #x
|
|
#define STRINGIZE(x) STRINGIZE_EX(x)
|
|
|
|
TEST_CASE("JSON_BRACE_INIT_COPY_SEMANTICS")
|
|
{
|
|
SECTION("the macro is part of the ABI tag")
|
|
{
|
|
const std::string ns = STRINGIZE(NLOHMANN_JSON_NAMESPACE);
|
|
// other tags may come before it, e.g. json_abi_ldvcmp_bics
|
|
CHECK(ns.find("_bics") != std::string::npos);
|
|
}
|
|
|
|
SECTION("single-element brace initialization copies the element (#5074)")
|
|
{
|
|
json const j_obj = {{"key", "value"}, {"num", 42}};
|
|
json const j_arr = {1, 2, 3};
|
|
|
|
// object: brace init copies instead of wrapping
|
|
json const j1{j_obj};
|
|
CHECK(j1.is_object());
|
|
CHECK(j1 == j_obj);
|
|
|
|
// array: brace init copies instead of wrapping
|
|
json const j2{j_arr};
|
|
CHECK(j2.is_array());
|
|
CHECK(j2.size() == 3);
|
|
CHECK(j2 == j_arr);
|
|
|
|
// this applies to any single element, not only to JSON values
|
|
json const j3{true};
|
|
CHECK(j3.is_boolean());
|
|
|
|
json const j4{42};
|
|
CHECK(j4.is_number_integer());
|
|
|
|
json const j5 = {1};
|
|
CHECK(j5 == 1);
|
|
|
|
json const j6 = {"text"};
|
|
CHECK(j6 == "text");
|
|
|
|
json const j7 = {{1, 2}};
|
|
CHECK(j7 == json::array({1, 2}));
|
|
}
|
|
|
|
SECTION("single-element brace initialization copies a pair-shaped array value (#5662)")
|
|
{
|
|
// a JSON value that happens to be a 2-element array whose first
|
|
// element is a string must still be copied, not turned into an
|
|
// object; only a braced list written in the source, such as the
|
|
// inner {"key", "value"} of {{"key", "value"}}, describes an object
|
|
json const pair_shaped = json::array({"key", 42});
|
|
|
|
json const j1{pair_shaped};
|
|
CHECK(j1.is_array());
|
|
CHECK(j1 == pair_shaped);
|
|
|
|
json const j2 = {pair_shaped};
|
|
CHECK(j2.is_array());
|
|
CHECK(j2 == pair_shaped);
|
|
|
|
// the same holds for an rvalue of the same shape
|
|
json const j3{json::array({"key", 42})};
|
|
CHECK(j3.is_array());
|
|
CHECK(j3 == pair_shaped);
|
|
}
|
|
|
|
SECTION("push_back and operator+= with a single braced JSON value")
|
|
{
|
|
// with the macro, the result does not depend on the compiler;
|
|
// without it, GCC wraps a JSON value and Clang does not (a literal
|
|
// such as {1} is wrapped by both)
|
|
json const obj = {{"key", "value"}};
|
|
json const arr = {1, 2};
|
|
|
|
json a1 = json::array();
|
|
a1.push_back({obj});
|
|
CHECK(a1 == json::array({obj}));
|
|
|
|
json a2 = json::array();
|
|
a2 += {obj};
|
|
CHECK(a2 == json::array({obj}));
|
|
|
|
json b1 = json::array();
|
|
b1.push_back({arr});
|
|
CHECK(b1 == json::array({arr}));
|
|
|
|
json b2 = json::array();
|
|
b2 += {arr};
|
|
CHECK(b2 == json::array({arr}));
|
|
|
|
// a literal is copied as well
|
|
json c = json::array();
|
|
c.push_back({1});
|
|
c += {2};
|
|
CHECK(c == json::array({1, 2}));
|
|
}
|
|
|
|
SECTION("what the macro does not change")
|
|
{
|
|
// lists with more than one element are unaffected
|
|
json const j1 = {1, 2};
|
|
CHECK(j1.is_array());
|
|
CHECK(j1.size() == 2);
|
|
|
|
// a single [string, value] pair still describes an object
|
|
json const j2 = {{"key", "value"}};
|
|
CHECK(j2.is_object());
|
|
CHECK(j2["key"] == "value");
|
|
|
|
// json::array() always creates an array
|
|
json const j3 = json::array({1});
|
|
CHECK(j3.is_array());
|
|
CHECK(j3.size() == 1);
|
|
CHECK(j3[0] == 1);
|
|
|
|
json const j_obj = {{"key", "value"}};
|
|
json const j4 = json::array({j_obj});
|
|
CHECK(j4.is_array());
|
|
CHECK(j4.size() == 1);
|
|
CHECK(j4[0] == j_obj);
|
|
}
|
|
|
|
SECTION("conversions build the same values as without the macro")
|
|
{
|
|
SECTION("one-element std::tuple")
|
|
{
|
|
json const j1 = std::tuple<int> {5};
|
|
CHECK(j1.dump() == "[5]");
|
|
CHECK(std::get<0>(j1.get<std::tuple<int>>()) == 5);
|
|
|
|
json const j2 = std::tuple<std::string> {"text"};
|
|
CHECK(j2.dump() == "[\"text\"]");
|
|
CHECK(std::get<0>(j2.get<std::tuple<std::string>>()) == "text");
|
|
|
|
json const j3 = std::tuple<json> {json::array({1, 2})};
|
|
CHECK(j3.dump() == "[[1,2]]");
|
|
|
|
// as without the macro, a [string, value] pair becomes an object
|
|
// member (see the known limitation documented for std::pair)
|
|
json const j4 = std::tuple<std::pair<std::string, int>> {{"a", 1}};
|
|
CHECK(j4.dump() == "{\"a\":1}");
|
|
}
|
|
|
|
SECTION("tuples with more elements")
|
|
{
|
|
json const j1 = std::tuple<int, std::string> {1, "a"};
|
|
CHECK(j1.dump() == "[1,\"a\"]");
|
|
|
|
json const j2 = std::tuple<> {};
|
|
CHECK(j2.dump() == "[]");
|
|
}
|
|
|
|
SECTION("one-element containers")
|
|
{
|
|
json const j1 = std::vector<int> {1};
|
|
CHECK(j1.dump() == "[1]");
|
|
CHECK(j1.get<std::vector<int>>() == std::vector<int> {1});
|
|
|
|
std::array<int, 1> const arr = {{1}};
|
|
json const j2 = arr;
|
|
CHECK(j2.dump() == "[1]");
|
|
|
|
json const j3 = std::list<std::string> {"a"};
|
|
CHECK(j3.dump() == "[\"a\"]");
|
|
|
|
json const j4 = std::map<std::string, int> {{"a", 1}};
|
|
CHECK(j4.dump() == "{\"a\":1}");
|
|
|
|
json const j5 = std::map<int, int> {{1, 2}};
|
|
CHECK(j5.dump() == "[[1,2]]");
|
|
}
|
|
|
|
SECTION("std::pair")
|
|
{
|
|
json const j = std::pair<int, int> {1, 2};
|
|
CHECK(j.dump() == "[1,2]");
|
|
CHECK((j.get<std::pair<int, int>>() == std::pair<int, int> {1, 2}));
|
|
}
|
|
|
|
SECTION("items()")
|
|
{
|
|
json j_obj = {{"key", 1}};
|
|
for (const auto& el : j_obj.items())
|
|
{
|
|
json const j = el;
|
|
CHECK(j.dump() == "{\"key\":1}");
|
|
}
|
|
}
|
|
}
|
|
}
|