#pragma once #define TOML_UNDEF_MACROS 0 #include "../include/toml++/toml.h" TOML_PUSH_WARNINGS TOML_DISABLE_ALL_WARNINGS #include "catch2.h" #include using namespace toml; using namespace Catch::literals; TOML_POP_WARNINGS #define S(str) TOML_STRING_PREFIX(str) template void parsing_should_succeed(std::basic_string_view toml_str, FUNC&& func, std::string_view source_path = {}) noexcept { constexpr auto validate_table = [](table&& tabl, std::string_view path) noexcept -> table&& { CHECK(tabl.source().begin != source_position{}); CHECK(tabl.source().end != source_position{}); if (path.empty()) CHECK(tabl.source().path == nullptr); else { REQUIRE(tabl.source().path != nullptr); CHECK(*tabl.source().path == path); } return std::move(tabl); }; #if TOML_EXCEPTIONS try { std::forward(func)(validate_table(toml::parse(toml_str, source_path), source_path)); { std::basic_stringstream, std::allocator> ss; ss.write(toml_str.data(), toml_str.length()); std::forward(func)(validate_table(toml::parse(ss, source_path), source_path)); } } catch (const toml::parse_error& err) { FAIL( "Parse error on line "sv << err.source().begin.line << ", column "sv << err.source().begin.column << ":\n"sv << err.description() ); } #else { toml::parse_result result = toml::parse(toml_str, source_path); if (result) std::forward(func)(validate_table(std::move(result), source_path)); else { FAIL( "Parse error on line "sv << result.error().source().begin.line << ", column "sv << result.error().source().begin.column << ":\n"sv << result.error().description() ); return; } } { std::basic_stringstream, std::allocator> ss; ss.write(toml_str.data(), toml_str.length()); toml::parse_result result = toml::parse(ss, source_path); if (result) std::forward(func)(validate_table(std::move(result), source_path)); else { FAIL( "Parse error on line "sv << result.error().source().begin.line << ", column "sv << result.error().source().begin.column << ":\n"sv << result.error().description() ); return; } } #endif } template void parsing_should_fail(std::basic_string_view toml_str) noexcept { #if TOML_EXCEPTIONS static constexpr auto run_tests = [](auto&& fn) noexcept { try { fn(); } catch (const toml::parse_error&) { SUCCEED("toml::parse_error thrown OK"sv); return true; } catch (const std::exception& exc) { FAIL("Expected parsing failure, saw exception: "sv << exc.what()); } catch (...) { FAIL("Expected parsing failure, saw unspecified error"sv); } return false; }; if (run_tests([=]() { (void)toml::parse(toml_str); })) run_tests([=]() { std::basic_stringstream, std::allocator> ss; ss.write(toml_str.data(), toml_str.length()); (void)toml::parse(ss); }); #else static constexpr auto run_tests = [](auto&& fn) noexcept { toml::parse_result result = fn(); if (result) { FAIL("Expected parsing failure"sv); return false; } else { SUCCEED("toml::parse_error returned OK"sv); return true; } }; if (run_tests([=]() noexcept { return toml::parse(toml_str); })) run_tests([=]() noexcept { std::basic_stringstream, std::allocator> ss; ss.write(toml_str.data(), toml_str.length()); return toml::parse(ss); }); #endif } template void parse_expected_value(std::string_view value_str, const T& expected) noexcept { std::string value; static constexpr auto value_key = "val = "sv; value.reserve(value_key.length() + value_str.length()); value.append(value_key); value.append(value_str); static constexpr auto is_val = [](char32_t codepoint) noexcept { if constexpr (std::is_same_v>) return codepoint == U'"' || codepoint == U'\''; else return !impl::is_whitespace(codepoint); }; source_position pos{ 1, static_cast(value_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_line_break(decoder.codepoint)) { if (decoder.codepoint != U'\r') { 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++; parsing_should_succeed(std::string_view{ value }, [&](table&& tbl) noexcept { CHECK(tbl.size() == 1); const auto nv = tbl[S("val"sv)]; REQUIRE(nv); REQUIRE(nv.as>()); REQUIRE(nv.get()->type() == impl::node_type_of); //check the raw value CHECK(nv.as>()->get() == expected); //check the value relops CHECK(*nv.as>() == expected); CHECK(expected == *nv.as>()); CHECK(!(*nv.as>() != expected)); CHECK(!(expected != *nv.as>())); //check the node_view relops CHECK(nv == expected); CHECK(expected == nv); CHECK(!(nv != expected)); CHECK(!(expected != nv)); //make sure source info is correct CHECK(nv.get()->source().begin == begin); CHECK(nv.get()->source().end == end); }); }