104 lines
3.9 KiB
C++
104 lines
3.9 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
|
|
|
|
#pragma once
|
|
|
|
#include <cstdint> // uint8_t
|
|
#include <vector> // vector
|
|
|
|
#include "doctest_compatibility.h"
|
|
|
|
#include <nlohmann/json.hpp>
|
|
|
|
#include "round_trip_corpus.hpp"
|
|
|
|
// The BJData round-trip invariants, split by the BJData version that turns a
|
|
// corpus value into j1 (unit-bjdata_round_trip_draft2.cpp and
|
|
// unit-bjdata_round_trip_draft3.cpp). They were part of unit-bjdata.cpp, but
|
|
// took 80 % of its time; as separate tests, both halves run in parallel and
|
|
// stay below the ctest timeout under Valgrind.
|
|
namespace utils
|
|
{
|
|
|
|
// This checks what the parse_bjdata_fuzzer driver checks (see
|
|
// tests/src/fuzzer-parse_bjdata.cpp), so that a regression shows up in CI
|
|
// rather than as an OSS-Fuzz report: every value from_bjdata() returns
|
|
// (j1) can be serialized with any combination of options, the result can
|
|
// be parsed back (j2), and serializing j2 again with the same options
|
|
// yields a value-equal result.
|
|
//
|
|
// Beyond the driver, this also checks that j2 equals j1 and that
|
|
// serializing j2 reproduces the exact bytes, both except for values that
|
|
// contain a binary value: a binary value is only written as a binary
|
|
// value with Draft 3's optimized binary array, and otherwise read back as
|
|
// an array of integers, for which the writer may choose different (but
|
|
// equally valid) type markers when it is serialized again (see #5494).
|
|
//
|
|
// Values are compared with dump() rather than operator==, because a NaN
|
|
// never compares equal to itself.
|
|
//
|
|
// Only the corpus values written with @a initial_version become j1; each j1
|
|
// is then serialized with every combination of options, both versions
|
|
// included.
|
|
inline void check_bjdata_round_trip(const nlohmann::json::bjdata_version_t initial_version)
|
|
{
|
|
using nlohmann::json;
|
|
|
|
struct options
|
|
{
|
|
bool use_size;
|
|
bool use_type;
|
|
json::bjdata_version_t version;
|
|
};
|
|
const std::vector<options> all_options =
|
|
{
|
|
{false, false, json::bjdata_version_t::draft2},
|
|
{true, false, json::bjdata_version_t::draft2},
|
|
{true, true, json::bjdata_version_t::draft2},
|
|
{false, false, json::bjdata_version_t::draft3},
|
|
{true, false, json::bjdata_version_t::draft3},
|
|
{true, true, json::bjdata_version_t::draft3},
|
|
};
|
|
|
|
for (const auto& j0 : utils::round_trip_corpus::values())
|
|
{
|
|
// turn the corpus value into a value as from_bjdata() returns it
|
|
for (const auto& initial : all_options)
|
|
{
|
|
if (initial.version != initial_version)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const json j1 = json::from_bjdata(json::to_bjdata(j0, initial.use_size, initial.use_type, initial.version));
|
|
const bool has_binary = utils::round_trip_corpus::contains_binary(j1);
|
|
|
|
for (const auto& o : all_options)
|
|
{
|
|
INFO("j1 = " << j1.dump() << ", use_size = " << o.use_size << ", use_type = " << o.use_type
|
|
<< ", draft3 = " << (o.version == json::bjdata_version_t::draft3));
|
|
|
|
const std::vector<std::uint8_t> vec = json::to_bjdata(j1, o.use_size, o.use_type, o.version);
|
|
json j2;
|
|
// anything the library writes must be parsable by the library
|
|
REQUIRE_NOTHROW(j2 = json::from_bjdata(vec));
|
|
const std::vector<std::uint8_t> vec2 = json::to_bjdata(j2, o.use_size, o.use_type, o.version);
|
|
CHECK(json::from_bjdata(vec2).dump() == j2.dump());
|
|
|
|
if (!has_binary)
|
|
{
|
|
CHECK(j2.dump() == j1.dump());
|
|
CHECK(vec2 == vec);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace utils
|