// This file is a part of toml++ and is subject to the the terms of the MIT license. // Copyright (c) Mark Gillard // See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. // SPDX-License-Identifier: MIT #pragma once #include "settings.h" #if USE_TARTANLLAMA_OPTIONAL #include "lib_tloptional.h" #endif #if USE_SINGLE_HEADER #include "../toml.hpp" #else #include "../include/toml++/toml.h" #endif #if defined(TOML_FP16) ^ SHOULD_HAVE_FP16 #error TOML_FP16 was not deduced correctly #endif #if defined(TOML_FLOAT16) ^ SHOULD_HAVE_FLOAT16 #error TOML_FLOAT16 was not deduced correctly #endif #if defined(TOML_FLOAT128) ^ SHOULD_HAVE_FLOAT128 #error TOML_FLOAT128 was not deduced correctly #endif #if defined(TOML_INT128) ^ SHOULD_HAVE_INT128 #error TOML_INT128 was not deduced correctly #endif #if defined(TOML_INT128) ^ defined(TOML_UINT128) #error TOML_INT128 and TOML_UINT128 must both be defined, or neither be defined #endif #if TOML_COMPILER_EXCEPTIONS ^ SHOULD_HAVE_EXCEPTIONS #error TOML_COMPILER_EXCEPTIONS was not deduced correctly #endif #if TOML_COMPILER_EXCEPTIONS ^ TOML_EXCEPTIONS #error TOML_EXCEPTIONS does not match TOML_COMPILER_EXCEPTIONS (default behaviour should be to match) #endif #if defined(_WIN32) ^ TOML_ENABLE_WINDOWS_COMPAT #error TOML_ENABLE_WINDOWS_COMPAT does not match _WIN32 (default behaviour should be to match) #endif #if TOML_LIB_SINGLE_HEADER ^ USE_SINGLE_HEADER #error TOML_LIB_SINGLE_HEADER was not set correctly #endif #if TOML_ICC #define UNICODE_LITERALS_OK 0 #else #define UNICODE_LITERALS_OK 1 #endif TOML_DISABLE_SPAM_WARNINGS; TOML_DISABLE_ARITHMETIC_WARNINGS; #if TOML_CLANG == 13 #pragma clang diagnostic ignored "-Wreserved-identifier" // false-positive #endif TOML_DISABLE_WARNINGS; #include "lib_catch2.h" #include namespace toml { } using namespace Catch::literals; using namespace toml; TOML_ENABLE_WARNINGS; TOML_NODISCARD TOML_ATTR(const) TOML_ALWAYS_INLINE constexpr size_t operator"" _sz(unsigned long long n) noexcept { return static_cast(n); } #define FILE_LINE_ARGS trim_file_path(std::string_view{ __FILE__ }), __LINE__ #define BOM_PREFIX "\xEF\xBB\xBF" #if TOML_EXCEPTIONS #define FORCE_FAIL(...) FAIL(__VA_ARGS__) #else #define FORCE_FAIL(...) \ do \ { \ FAIL(__VA_ARGS__); \ std::exit(-1); \ TOML_UNREACHABLE; \ } \ while (false) #endif #define CHECK_SYMMETRIC_RELOP(lhs, op, rhs, result) \ CHECK(((lhs)op(rhs)) == (result)); \ CHECK(((rhs)op(lhs)) == (result)) #define CHECK_SYMMETRIC_EQUAL(lhs, rhs) \ CHECK_SYMMETRIC_RELOP(lhs, ==, rhs, true); \ CHECK_SYMMETRIC_RELOP(lhs, !=, rhs, false) #define CHECK_SYMMETRIC_INEQUAL(lhs, rhs) \ CHECK_SYMMETRIC_RELOP(lhs, ==, rhs, false); \ CHECK_SYMMETRIC_RELOP(lhs, !=, rhs, true) template struct type_tag { using type = T; }; // function_view - adapted from here: https://vittorioromeo.info/index/blog/passing_functions_to_functions.html template class function_view; template class function_view final { private: using func_type = R(P...); using eraser_func_type = R(void*, P&&...); mutable void* ptr_ = {}; mutable eraser_func_type* eraser = {}; public: function_view() noexcept = default; template function_view(T&& x) noexcept : ptr_{ reinterpret_cast(std::addressof(x)) } { eraser = [](void* ptr, P&&... xs) -> R { return (*reinterpret_cast>>(ptr))(std::forward

(xs)...); }; } decltype(auto) operator()(P&&... xs) const { return eraser(ptr_, std::forward

(xs)...); } TOML_NODISCARD operator bool() const noexcept { return !!ptr_; } }; using pss_func = function_view; bool parsing_should_succeed(std::string_view test_file, uint32_t test_line, std::string_view toml_str, pss_func&& func = {}, std::string_view source_path = {}); bool parsing_should_fail(std::string_view test_file, uint32_t test_line, std::string_view toml_str, source_index expected_failure_line = static_cast(-1), source_index expected_failure_column = static_cast(-1)); TOML_PURE_GETTER constexpr std::string_view trim_file_path(std::string_view sv) noexcept { const auto src = std::min(sv.rfind("\\"sv), sv.rfind("/"sv)); if (src != std::string_view::npos) sv = sv.substr(src + 1_sz); return sv; } template inline bool parse_expected_value(std::string_view test_file, uint32_t test_line, std::string_view value_str, const T& expected) { INFO("["sv << test_file << ", line "sv << test_line << "] "sv << "parse_expected_value(\""sv << value_str << "\")"sv) std::string val; static constexpr auto key = "val = "sv; val.reserve(key.length() + value_str.length()); val.append(key); val.append(value_str); static constexpr auto is_val = [](char32_t codepoint) { if constexpr (impl::node_type_of == node_type::string) return codepoint == U'"' || codepoint == U'\''; else return !impl::is_whitespace(codepoint); }; source_position pos{ 1, static_cast(key.length()) }; source_position begin{}, end{}; { impl::utf8_decoder decoder; for (auto c : value_str) { decoder(static_cast(c)); if (!decoder.has_code_point()) continue; if (impl::is_ascii_vertical_whitespace(decoder.codepoint)) { if (decoder.codepoint == U'\n') { pos.line++; pos.column = source_index{ 1 }; } continue; } pos.column++; if (is_val(decoder.codepoint)) { if (!begin) begin = pos; else end = pos; } } if (!end) end = begin; end.column++; } using value_type = impl::native_type_of>; value val_parsed; { INFO("["sv << test_file << ", line "sv << test_line << "] "sv << "parse_expected_value: Checking initial parse"sv) bool stolen_value = false; // parsing_should_succeed invokes the functor more than once const auto result = parsing_should_succeed( test_file, test_line, std::string_view{ val }, [&](table&& tbl) { REQUIRE(tbl.size() == 1); auto nv = tbl["val"sv]; REQUIRE(nv); REQUIRE(nv.is()); REQUIRE(nv.as()); REQUIRE(nv.type() == impl::node_type_of); REQUIRE(nv.node()); REQUIRE(nv.node()->is()); REQUIRE(nv.node()->as()); REQUIRE(nv.node()->type() == impl::node_type_of); // check homogeneity REQUIRE(nv.is_homogeneous()); REQUIRE(nv.is_homogeneous(node_type::none)); REQUIRE(nv.is_homogeneous(impl::node_type_of)); REQUIRE(nv.is_homogeneous()); REQUIRE(nv.node()->is_homogeneous()); REQUIRE(nv.node()->is_homogeneous(node_type::none)); REQUIRE(nv.node()->is_homogeneous(impl::node_type_of)); REQUIRE(nv.node()->is_homogeneous()); for (auto nt = impl::unwrap_enum(node_type::table); nt <= impl::unwrap_enum(node_type::date_time); nt++) { if (node_type{ nt } == impl::node_type_of) continue; node* first_nonmatch{}; REQUIRE(!nv.is_homogeneous(node_type{ nt })); REQUIRE(!nv.is_homogeneous(node_type{ nt }, first_nonmatch)); REQUIRE(first_nonmatch == nv.node()); REQUIRE(!nv.node()->is_homogeneous(node_type{ nt })); REQUIRE(!nv.node()->is_homogeneous(node_type{ nt }, first_nonmatch)); REQUIRE(first_nonmatch == nv.node()); } // check the raw value REQUIRE(nv.node()->value() == expected); REQUIRE(nv.node()->value_or(T{}) == expected); REQUIRE(nv.as()->get() == expected); REQUIRE(nv.value() == expected); REQUIRE(nv.value_or(T{}) == expected); REQUIRE(nv.ref() == expected); REQUIRE(nv.node()->ref() == expected); // check the table relops REQUIRE(tbl == table{ { { "val"sv, expected } } }); REQUIRE(!(tbl != table{ { { "val"sv, expected } } })); // check value/node relops CHECK_SYMMETRIC_EQUAL(*nv.as(), *nv.as()); CHECK_SYMMETRIC_EQUAL(*nv.as(), expected); CHECK_SYMMETRIC_EQUAL(nv, expected); // make sure source info is correct CHECK_SYMMETRIC_EQUAL(nv.node()->source().begin, begin); CHECK_SYMMETRIC_EQUAL(nv.node()->source().end, end); // check float identities etc if constexpr (std::is_same_v) { auto& float_node = *nv.as(); const auto fpcls = impl::fpclassify(*float_node); if (fpcls == impl::fp_class::nan) { CHECK_SYMMETRIC_EQUAL(float_node, std::numeric_limits::quiet_NaN()); CHECK_SYMMETRIC_INEQUAL(float_node, std::numeric_limits::infinity()); CHECK_SYMMETRIC_INEQUAL(float_node, -std::numeric_limits::infinity()); CHECK_SYMMETRIC_INEQUAL(float_node, 1.0); CHECK_SYMMETRIC_INEQUAL(float_node, 0.0); CHECK_SYMMETRIC_INEQUAL(float_node, -1.0); } else if (fpcls == impl::fp_class::neg_inf || fpcls == impl::fp_class::pos_inf) { CHECK_SYMMETRIC_INEQUAL(float_node, std::numeric_limits::quiet_NaN()); if (fpcls == impl::fp_class::neg_inf) { CHECK_SYMMETRIC_EQUAL(float_node, -std::numeric_limits::infinity()); CHECK_SYMMETRIC_INEQUAL(float_node, std::numeric_limits::infinity()); } else { CHECK_SYMMETRIC_EQUAL(float_node, std::numeric_limits::infinity()); CHECK_SYMMETRIC_INEQUAL(float_node, -std::numeric_limits::infinity()); } CHECK_SYMMETRIC_INEQUAL(float_node, 1.0); CHECK_SYMMETRIC_INEQUAL(float_node, 0.0); CHECK_SYMMETRIC_INEQUAL(float_node, -1.0); } else { CHECK_SYMMETRIC_INEQUAL(float_node, std::numeric_limits::quiet_NaN()); CHECK_SYMMETRIC_INEQUAL(float_node, std::numeric_limits::infinity()); CHECK_SYMMETRIC_INEQUAL(float_node, -std::numeric_limits::infinity()); CHECK_SYMMETRIC_EQUAL(float_node, *float_node); if (std::abs(*float_node) <= 1e10) { CHECK_SYMMETRIC_INEQUAL(float_node, *float_node + 100.0); CHECK_SYMMETRIC_INEQUAL(float_node, *float_node - 100.0); } CHECK(float_node < std::numeric_limits::infinity()); CHECK(float_node > -std::numeric_limits::infinity()); } } // steal the val for round-trip tests if (!stolen_value) { val_parsed = std::move(*nv.as()); stolen_value = true; } }); if (!result) return false; } // check round-tripping { INFO("["sv << test_file << ", line "sv << test_line << "] "sv << "parse_expected_value: Checking round-trip"sv) { std::string str; { auto tbl = table{ { { "val"sv, *val_parsed } } }; std::ostringstream ss; ss << tbl; str = std::move(ss).str(); } bool value_ok = true; const auto parse_ok = parsing_should_succeed(test_file, test_line, std::string_view{ str }, [&](table&& tbl) { REQUIRE(tbl.size() == 1); auto nv = tbl["val"sv]; REQUIRE(nv); REQUIRE(nv.as()); REQUIRE(nv.node()->type() == impl::node_type_of); if (value_ok && nv.ref() != expected) { value_ok = false; FORCE_FAIL("Value was not the same after round-tripping"sv); } }); if (!parse_ok || value_ok) return false; } } return true; } // manually instantiate some templates to reduce obj bloat and test compilation time extern template bool parse_expected_value(std::string_view, uint32_t, std::string_view, const int&); extern template bool parse_expected_value(std::string_view, uint32_t, std::string_view, const unsigned int&); extern template bool parse_expected_value(std::string_view, uint32_t, std::string_view, const bool&); extern template bool parse_expected_value(std::string_view, uint32_t, std::string_view, const float&); extern template bool parse_expected_value(std::string_view, uint32_t, std::string_view, const double&); extern template bool parse_expected_value(std::string_view, uint32_t, std::string_view, const std::string_view&); namespace std { extern template class unique_ptr; } namespace Catch { extern template struct StringMaker>; extern template struct StringMaker>; extern template ReusableStringStream& ReusableStringStream::operator<<(node_view const&); extern template ReusableStringStream& ReusableStringStream::operator<<(node_view const&); namespace Detail { extern template std::string stringify(const node_view&); extern template std::string stringify(const node_view&); } }