diff options
| author | Stefan Boberg <[email protected]> | 2026-02-23 11:19:52 +0100 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2026-02-23 11:19:52 +0100 |
| commit | 9aac0fd369b87e965fb34b5168646387de7ea1cd (patch) | |
| tree | 367a820685a829adbab31cd1374b1af2cece4b7e /thirdparty/ryml/test/test_suite.cpp | |
| parent | changed command names and descriptions to use class members instead of string... (diff) | |
| download | zen-9aac0fd369b87e965fb34b5168646387de7ea1cd.tar.xz zen-9aac0fd369b87e965fb34b5168646387de7ea1cd.zip | |
implement yaml generation (#774)
this implements a yaml generation strategy similar to the JSON generation where we just build a string instead of building a ryml tree.
This also removes the dependency on ryml for reduced binary/build times.
Diffstat (limited to 'thirdparty/ryml/test/test_suite.cpp')
| -rw-r--r-- | thirdparty/ryml/test/test_suite.cpp | 612 |
1 files changed, 0 insertions, 612 deletions
diff --git a/thirdparty/ryml/test/test_suite.cpp b/thirdparty/ryml/test/test_suite.cpp deleted file mode 100644 index 22dabf8c4..000000000 --- a/thirdparty/ryml/test/test_suite.cpp +++ /dev/null @@ -1,612 +0,0 @@ -#ifndef RYML_SINGLE_HEADER -#include <c4/yml/std/std.hpp> -#include <c4/yml/tree.hpp> -#include <c4/yml/parse.hpp> -#include <c4/yml/emit.hpp> -#include <c4/yml/detail/print.hpp> -#include <c4/yml/detail/checks.hpp> -#endif -#include "test_case.hpp" -#include "test_suite/test_suite_common.hpp" -#include "test_suite/test_suite_parts.hpp" -#include "test_suite/test_suite_events.hpp" -#include <c4/fs/fs.hpp> -#include <c4/log/log.hpp> -#include <gtest/gtest.h> - - -/* Each case from the test suite contains: - * - * - (awkward) input yaml (in_yaml) - * - (somewhat standard) output equivalent (out_yaml) - * - (when meaningful/possible) json equivalent (in_json) - * - yaml parsing events (events) - * - * Running a test consists of parsing the contents above into a data - * structure, and then repeatedly parsing and emitting yaml in a sort - * of pipe. Ie, (eg for in_yaml) parse in_yaml, emit corresponding - * yaml, then parse this emitted yaml, and so on. Each parse/emit pair - * is named a processing level in this test. */ - - -C4_SUPPRESS_WARNING_MSVC_PUSH -C4_SUPPRESS_WARNING_MSVC(4702) // unreachable code - -#define NLEVELS 4 - -namespace c4 { -namespace yml { - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - -struct Events -{ - csubstr filename = {}; - std::string src = {}; - std::string emitted_events = {}; - Tree tree = {}; - mutable Tree adjusted_tree = {}; - mutable Tree tree_from_emitted_events = {}; - bool was_parsed = false; - bool enabled = false; - - void init(csubstr filename_, csubstr src_) - { - filename = filename_; - src.assign(src_.begin(), src_.end()); - tree.clear(); - tree.clear_arena(); - tree.reserve(10); - was_parsed = false; - enabled = true; - } - - void compare_trees(csubstr actual_src, Tree const& actual_tree) const - { - if(actual_src.empty()) - GTEST_SKIP(); - _nfo_logf("SRC:\n{}", actual_src); - _nfo_print_tree("EXPECTED", tree); - _nfo_print_tree("ACTUAL", actual_tree); - test_compare(actual_tree, tree); - } - - void compare_emitted_events(csubstr actual_src, Tree const& tree_from_actual_src) - { - C4_UNUSED(actual_src); - emit_events(&emitted_events, tree_from_actual_src); - _nfo_logf("EXPECTED_EVENTS:\n{}", src); - _nfo_logf("ACTUAL_EVENTS:\n{}", emitted_events); - // we cannot directly compare the event strings, - // so we create a tree from the emitted events, - // and then compare the trees: - tree_from_emitted_events.clear(); - tree_from_emitted_events.reserve(16); - parser.parse(c4::to_csubstr(emitted_events), &tree_from_emitted_events); - _nfo_logf("SRC:\n{}", actual_src); - _nfo_print_tree("ACTUAL_FROM_SOURCE", tree_from_actual_src); - _nfo_print_tree("ACTUAL_FROM_EMITTED_EVENTS", tree_from_emitted_events); - _nfo_print_tree("EXPECTED_FROM_EVENTS", tree); - test_compare(tree_from_emitted_events, tree); - } - - EventsParser parser; - void parse_events(csubstr actual_src) - { - if(was_parsed) - return; - if(actual_src.empty()) - GTEST_SKIP(); - parser.parse(c4::to_csubstr(src), &tree); - if(tree.empty()) - tree.reserve(10); - _nfo_print_tree("EXPECTED", tree); - was_parsed = true; - } -}; - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -/** a processing level */ -struct ProcLevel -{ - size_t level; - ProcLevel *prev; - csubstr filename; - std::string src; - c4::yml::Parser parser; - c4::yml::Tree tree; - std::string emitted; - - bool immutable = false; - bool reuse = false; - bool was_parsed = false; - bool was_emitted = false; - - void init(size_t level_, ProcLevel *prev_, csubstr filename_, csubstr src_, bool immutable_, bool reuse_) - { - level = level_; - prev = prev_; - filename = filename_; - src.assign(src_.begin(), src_.end()); - immutable = immutable_; - reuse = reuse_; - was_parsed = false; - was_emitted = false; - } - - void receive_src(ProcLevel & prev_) - { - RYML_ASSERT(&prev_ == prev); - if(!prev_.was_emitted) - { - _nfo_logf("level[{}] not emitted. emit!", prev_.level); - prev_.emit(); - } - if(src != prev_.emitted) - { - was_parsed = false; - was_emitted = false; - src = prev_.emitted; - } - } - - template<class T> - void log(const char* context, T const& v) - { - C4_UNUSED(context); - C4_UNUSED(v); - #if RYML_NFO - constexpr const char sep[] = "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n"; - c4::log("{}:\n{}{}{}", context, sep, v, sep); - #endif - } - - void parse() - { - if(was_parsed) - return; - if(prev) - { - receive_src(*prev); - } - _nfo_logf("level[{}]: parsing source:\n{}", level, src); - if(reuse) - { - tree.clear(); - if(immutable) - parser.parse_in_arena(filename, c4::to_csubstr(src), &tree); - else - parser.parse_in_place(filename, c4::to_substr(src), &tree); - } - else - { - if(immutable) - tree = parse_in_arena(filename, c4::to_csubstr(src)); - else - tree = parse_in_place(filename, c4::to_substr(src)); - } - _nfo_print_tree("PARSED", tree); - tree.resolve_tags(); - _nfo_print_tree("RESOLVED TAGS", tree); - was_parsed = true; - //_resolve_if_needed(); - } - - void _resolve_if_needed() - { - ConstNodeRef root = tree.rootref(); - bool has_anchors_or_refs = root.visit([](ConstNodeRef const& node, size_t /*level*/){ - return (node.is_anchor() || node.is_ref()); - }); - if(has_anchors_or_refs) - { - tree.resolve(); - _nfo_print_tree("RESOLVED", tree); - } - } - - void emit() - { - if(was_emitted) - return; - if(!was_parsed) - { - _nfo_logf("level[{}] not parsed. parse!", level); - parse(); - } - emitrs_yaml(tree, &emitted); - csubstr ss = to_csubstr(emitted); - if(ss.ends_with("\n...\n")) - emitted.resize(emitted.size() - 4); - was_emitted = true; - _nfo_logf("EMITTED:\n{}", emitted); - } - - void compare_trees(ProcLevel & prev_) - { - RYML_ASSERT(&prev_ == prev); - if(!prev_.was_parsed) - { - _nfo_logf("level[{}] not parsed. parse!", prev_.level); - prev_.parse(); - } - if(!was_parsed) - { - _nfo_logf("level[{}] not parsed. parse!", level); - parse(); - } - _nfo_print_tree("PREV_", prev_.tree); - _nfo_print_tree("CURR", tree); - test_compare(prev_.tree, tree); - } - - void compare_emitted(ProcLevel & prev_) - { - RYML_ASSERT(&prev_ == prev); - if(!prev_.was_emitted) - { - _nfo_logf("level[{}] not emitted. emit!", prev_.level); - prev_.emit(); - } - if(!was_emitted) - { - _nfo_logf("level[{}] not emitted. emit!", level); - emit(); - } - _nfo_logf("level[{}]: EMITTED:\n{}", prev_.level, prev_.emitted); - _nfo_logf("level[{}]: EMITTED:\n{}", level, emitted); - if(emitted != prev_.emitted) - { - // workaround for lack of idempotency in tag normalization. - Tree from_prev = parse_in_arena(to_csubstr(prev_.emitted)); - Tree from_this = parse_in_arena(to_csubstr(emitted)); - from_prev.resolve_tags(); - from_this.resolve_tags(); - test_compare(from_prev, from_this); - } - } -}; - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -/** holds data for one particular test suite approach. */ -struct Approach -{ - csubstr casename; - csubstr filename; - ProcLevel levels[NLEVELS] = {}; - AllowedFailure allowed_failure = {}; - bool enabled = false; - bool expect_error = false; - - void init(csubstr casename_, csubstr filename_, csubstr src_, bool immutable_, bool reuse_, bool expect_error_) - { - casename = casename_; - filename = filename_; - allowed_failure = is_failure_expected(casename); - size_t level_index = 0; - ProcLevel *prev = nullptr; - for(ProcLevel &l : levels) - { - l.init(level_index++, prev, filename, src_, immutable_, reuse_); - prev = &l; - } - expect_error = expect_error_; - } - - csubstr src() const { return c4::to_csubstr(levels[0].src); } - bool skip() const { return allowed_failure; } - - void parse(size_t num, bool emit) - { - if(allowed_failure) - GTEST_SKIP(); - for(size_t i = 0; i < num; ++i) - { - levels[i].parse(); - if(emit) - levels[i].emit(); - if(i + 1 < num) - levels[i+1].receive_src(levels[i]); - } - } - - void compare_trees(size_t num) - { - if(allowed_failure) - GTEST_SKIP(); - for(size_t i = 1; i < num; ++i) - levels[i].compare_trees(levels[i-1]); - } - void compare_trees(size_t num, Approach & other) - { - if(allowed_failure) - GTEST_SKIP(); - for(size_t i = 0; i < num; ++i) - levels[i].compare_trees(other.levels[i]); - } - - void compare_emitted(size_t num) - { - if(allowed_failure) - GTEST_SKIP(); - for(size_t i = 1; i < num; ++i) - levels[i].compare_emitted(levels[i-1]); - } - void compare_emitted(size_t num, Approach & other) - { - if(allowed_failure) - GTEST_SKIP(); - for(size_t i = 0; i < num; ++i) - levels[i].compare_emitted(other.levels[i]); - } - - void compare_events(Events *events) - { - if(allowed_failure || filename.ends_with(".json")) - GTEST_SKIP(); - events->parse_events(src()); - parse(1, /*emit*/false); - events->compare_trees(src(), levels[0].tree); - } - - void compare_emitted_events(Events *events) - { - if(allowed_failure || filename.ends_with(".json")) - GTEST_SKIP(); - events->parse_events(src()); - parse(1, /*emit*/false); - events->compare_emitted_events(src(), levels[0].tree); - } - - void check_expected_error() - { - if(allowed_failure) - GTEST_SKIP(); - ExpectError::do_check(&levels[0].tree, [this]{ - levels[0].parse(); - }); - } - -}; - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -/** Each approach can be read from mutable/immutable yaml source and - * with/without reuse. */ -struct Subject -{ - Approach unix_arena; - Approach unix_arena_reuse; - Approach unix_inplace; - Approach unix_inplace_reuse; - - Approach windows_arena; - Approach windows_arena_reuse; - Approach windows_inplace; - Approach windows_inplace_reuse; - - std::string unix_src; - std::string windows_src; - - void init(csubstr casename, csubstr filename, csubstr src, bool expect_error) - { - src = replace_all("\r", "", src, &unix_src); - - unix_arena .init(casename, filename, src, /*immutable*/true , /*reuse*/false, expect_error); - unix_arena_reuse.init(casename, filename, src, /*immutable*/true , /*reuse*/true , expect_error); - unix_inplace .init(casename, filename, src, /*immutable*/false, /*reuse*/false, expect_error); - unix_inplace_reuse.init(casename, filename, src, /*immutable*/false, /*reuse*/true , expect_error); - - src = replace_all("\n", "\r\n", src, &windows_src); - - windows_arena .init(casename, filename, src, /*immutable*/true , /*reuse*/false, expect_error); - windows_arena_reuse.init(casename, filename, src, /*immutable*/true , /*reuse*/true , expect_error); - windows_inplace .init(casename, filename, src, /*immutable*/false, /*reuse*/false, expect_error); - windows_inplace_reuse.init(casename, filename, src, /*immutable*/false, /*reuse*/true , expect_error); - } -}; - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - -// some utility functions, used below - -/** all the ways to process a test case are available through this - * class. Tests are defined below and use only one of these. */ -struct SuiteCase -{ - csubstr case_title; - csubstr case_dir; - std::string filename; - std::string file_contents; - std::string events_filename; - std::string events_file_contents; - - Subject input; - Events events; - bool expect_error; - - /** loads the several types of tests from an input test suite file */ - SuiteCase(const char *case_title_, const char* case_dir_, const char *input_file) - { - using namespace c4; - using c4::to_csubstr; - - if(to_csubstr(input_file) == "error") - input_file = "in.yaml"; - - case_title = to_csubstr(case_title_); - - case_dir = to_csubstr(case_dir_); - RYML_CHECK(case_dir.find('\\') == yml::npos); - C4_CHECK_MSG(fs::dir_exists(case_dir.str), "dir not found: '%s'", case_dir); - - filename = catrs<std::string>(case_dir, '/', to_csubstr(input_file)); - C4_CHECK_MSG(fs::file_exists(filename.c_str()), "file not found: '%s'", filename.c_str()); - log("testing suite case: {} {} ({})", case_title, filename, case_dir); - - std::string errfile = catrs<std::string>(to_csubstr(case_dir_), "/error"); - expect_error = fs::file_exists(errfile.c_str()); - - fs::file_get_contents(filename.c_str(), &file_contents); - input.init(case_title, to_csubstr(filename), to_csubstr(file_contents), expect_error); - - events_filename = catrs<std::string>(case_dir, "/test.event"); - C4_CHECK(fs::file_exists(events_filename.c_str())); - fs::file_get_contents(events_filename.c_str(), &events_file_contents); - events.init(to_csubstr(events_filename), to_csubstr(events_file_contents)); - - dump("~~~ case: " , case_title , "~~~\n", - "~~~ file: " , filename , "~~~\n", - "~~~ input:\n" , to_csubstr(input.unix_arena.levels[0].src), "~~~\n", - "~~~ events:\n" , events.src , "~~~\n"); - } - -}; - - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - - -// a global holding the test case data -SuiteCase* g_suite_case = nullptr; -bool g_do_subcases = true; - - - -#define DEFINE_TESTS(which) \ - \ - \ -struct which : public ::testing::TestWithParam<size_t> \ -{ \ -}; \ - \ - \ -TEST_P(which, parse) \ -{ \ - RYML_CHECK(GetParam() < NLEVELS); \ - if(g_suite_case->expect_error) \ - GTEST_SKIP(); \ - g_suite_case->input.which.parse(1 + GetParam(), false); \ -} \ - \ - \ -TEST_P(which, compare_trees) \ -{ \ - RYML_CHECK(GetParam() < NLEVELS); \ - if(g_suite_case->expect_error) \ - GTEST_SKIP(); \ - g_suite_case->input.which.compare_trees(1 + GetParam()); \ -} \ - \ - \ -TEST_P(which, emit) \ -{ \ - RYML_CHECK(GetParam() < NLEVELS); \ - if(g_suite_case->expect_error) \ - GTEST_SKIP(); \ - g_suite_case->input.which.parse(1 + GetParam(), true); \ -} \ - \ - \ -TEST_P(which, compare_emitted) \ -{ \ - RYML_CHECK(GetParam() < NLEVELS); \ - if(g_suite_case->expect_error) \ - GTEST_SKIP(); \ - g_suite_case->input.which.compare_emitted(1 + GetParam()); \ -} \ - \ -/*-----------------------------------------------*/ \ - \ -TEST(which##_events, compare) \ -{ \ - if(g_suite_case->expect_error) \ - GTEST_SKIP(); \ - g_suite_case->input.which.compare_events(&g_suite_case->events); \ -} \ - \ -TEST(which##_events, emit_events) \ -{ \ - if(g_suite_case->expect_error) \ - GTEST_SKIP(); \ - g_suite_case->input.which.compare_emitted_events(&g_suite_case->events); \ -} \ - \ -/*-----------------------------------------------*/ \ - \ -TEST(which##_errors, check_expected_error) \ -{ \ - if(!g_suite_case->expect_error) \ - GTEST_SKIP(); \ - g_suite_case->input.which.check_expected_error(); \ -} \ - \ - \ -INSTANTIATE_TEST_SUITE_P(_, which, testing::Range<size_t>(0, NLEVELS)) - - -DEFINE_TESTS(unix_arena); -DEFINE_TESTS(unix_inplace); -DEFINE_TESTS(unix_arena_reuse); -DEFINE_TESTS(unix_inplace_reuse); -DEFINE_TESTS(windows_arena); -DEFINE_TESTS(windows_inplace); -DEFINE_TESTS(windows_arena_reuse); -DEFINE_TESTS(windows_inplace_reuse); - - -//------------------------------------------- -// this is needed to use the test case library -Case const* get_case(csubstr /*name*/) { return nullptr; } - -} // namespace yml -} // namespace c4 - - - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - -int main(int argc, char* argv[]) -{ - c4::dump("$"); - for(int i = 0; i < argc; ++i) - c4::dump(' ', c4::to_csubstr(argv[i])); - c4::dump("\n"); - - // make gtest parse its args - testing::InitGoogleTest(&argc, argv); - - // now we have only our args to consider - if(argc != 4) - { - log("usage:\n{} <test_name> <test-dir> <input-file>", c4::to_csubstr(argv[0])); - return 1; - } - - // load the test case from the suite file - c4::yml::SuiteCase suite_case(argv[1], argv[2], argv[3]); - c4::yml::g_suite_case = &suite_case; - - return RUN_ALL_TESTS(); -} - -C4_SUPPRESS_WARNING_MSVC_PUSH |