Skip to content

Commit 8182c63

Browse files
authored
Merge pull request #594 from biojppm/save_all_test_yaml
Add option to dump the YAML from all the rapidyaml test cases.
2 parents d15f2de + 96323b4 commit 8182c63

9 files changed

Lines changed: 236 additions & 40 deletions

File tree

CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,20 @@ endif()
132132
#-------------------------------------------------------
133133
# developer targets
134134

135+
option(RYML_SAVE_TEST_YAML "" OFF)
136+
if(RYML_SAVE_TEST_YAML)
137+
target_compile_definitions(ryml PUBLIC RYML_SAVE_TEST_YAML)
138+
set(RYML_TEST_FUZZ OFF CACHE BOOL "" FORCE)
139+
set(RYML_TEST_SUITE OFF CACHE BOOL "" FORCE)
140+
endif()
141+
function(ryml_maybe_dump_test_yaml target)
142+
if(RYML_SAVE_TEST_YAML)
143+
target_compile_definitions(${target} PRIVATE RYML_SAVE_TEST_YAML)
144+
target_sources(${target} PRIVATE ${PROJECT_SOURCE_DIR}/test/test_lib/test_save.cpp)
145+
target_link_libraries(${target} PRIVATE c4fs gtest)
146+
endif()
147+
endfunction()
148+
135149
# extern libraries, used only for testing/benchmarking
136150
if(RYML_BUILD_TESTS OR RYML_BUILD_BENCHMARKS OR RYML_BUILD_TOOLS)
137151
include(ext/testbm.cmake)

src/c4/yml/parse_engine.def.hpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,21 @@
4848
#define _RYML_WITH_OR_WITHOUT_TAB_TOKENS(with, without) without
4949
#endif
5050

51+
// helper to export cases to the YAML test suite
52+
#ifndef RYML_SAVE_TEST_YAML
53+
#define _RYML_SAVE_TEST_YAML(filename, src)
54+
#define _RYML_SAVE_TEST_JSON(filename, src)
55+
#else
56+
#define _RYML_SAVE_TEST_YAML(filename, src) c4::yml::ryml_save_test_yaml(filename, src)
57+
#define _RYML_SAVE_TEST_JSON(filename, src) c4::yml::ryml_save_test_json(filename, src)
58+
namespace c4 {
59+
namespace yml {
60+
void ryml_save_test_yaml(csubstr filename, csubstr src);
61+
void ryml_save_test_json(csubstr filename, csubstr src);
62+
} // namespace yml
63+
} // namespace c4
64+
#endif
65+
5166

5267
// scaffold:
5368
#define _c4dbgnextline() \
@@ -8726,6 +8741,7 @@ template<class EventHandler>
87268741
void ParseEngine<EventHandler>::parse_json_in_place_ev(csubstr filename, substr src)
87278742
{
87288743
_RYML_ASSERT_BASIC_(m_evt_handler->m_stack.m_callbacks, m_evt_handler->m_stack.size() >= 1);
8744+
_RYML_SAVE_TEST_JSON(filename, src);
87298745
m_evt_handler->start_parse(filename.str, src);
87308746
m_evt_handler->begin_stream();
87318747
_reset();
@@ -8768,6 +8784,7 @@ template<class EventHandler>
87688784
void ParseEngine<EventHandler>::parse_in_place_ev(csubstr filename, substr src)
87698785
{
87708786
_RYML_ASSERT_BASIC_(m_evt_handler->m_stack.m_callbacks, m_evt_handler->m_stack.size() >= 1);
8787+
_RYML_SAVE_TEST_YAML(filename, src);
87718788
m_evt_handler->start_parse(filename.str, src);
87728789
m_evt_handler->begin_stream();
87738790
_reset();

test/CMakeLists.txt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ function(ryml_add_test_no_lib test_name)
77
target_compile_definitions(${test_name} PRIVATE RYML_DBG)
88
c4_target_compile_flags(${test_name} PUBLIC GCC -Wno-useless-cast)
99
endif()
10+
ryml_maybe_dump_test_yaml(${test_name})
1011
c4_add_test(${test_name})
1112
endfunction()
1213
ryml_add_test_no_lib(ryml-test-quickstart
@@ -32,6 +33,7 @@ c4_add_library(ryml-_testlib LIBRARY_TYPE STATIC
3233
test_lib/test_engine.cpp
3334
test_lib/test_events_ints_helpers.hpp
3435
test_lib/test_events_ints_helpers.cpp
36+
test_lib/test_save.cpp
3537
../src_extra/c4/yml/extra/event_handler_ints.cpp
3638
../src_extra/c4/yml/extra/event_handler_ints.hpp
3739
../src_extra/c4/yml/extra/event_handler_testsuite.cpp
@@ -53,6 +55,9 @@ endif()
5355
if(RYML_DBG)
5456
target_compile_definitions(ryml-_testlib PUBLIC RYML_DBG)
5557
endif()
58+
if(RYML_SAVE_TEST_YAML)
59+
target_compile_definitions(ryml-_testlib PUBLIC RYML_SAVE_TEST_YAML)
60+
endif()
5661

5762
c4_add_library(ryml-_testmain LIBRARY_TYPE OBJECT
5863
SOURCES test_lib/test_main.cpp test_lib/test_main.hpp
@@ -72,9 +77,11 @@ function(ryml_add_test test_name)
7277
SOURCES test_${test_name}.cpp
7378
LIBS ryml-_testlib ryml-_testmain
7479
FOLDER test)
80+
if(RYML_SAVE_TEST_YAML)
81+
target_sources(${t} PRIVATE test_lib/test_save.cpp)
82+
endif()
7583
c4_add_test(${t})
76-
# MSVC bigobj
77-
if(MSVC AND _BIG)
84+
if(MSVC AND _BIG) # MSVC bigobj
7885
target_compile_options(${t} PRIVATE /bigobj)
7986
endif()
8087
endfunction()
@@ -250,6 +257,7 @@ if(RYML_TEST_TOOLS)
250257
ryml_add_event_tool_test(fail_badcmd FALSE "barbar" "foo")
251258
ryml_add_event_tool_test_all(success TRUE "" "{foo: bar, baz: [exactly]}")
252259
ryml_add_event_tool_test_all(success_timing TRUE " --timing" "{foo: bar, baz: [exactly]}")
260+
ryml_add_event_tool_test_all(success_quiet TRUE " --quiet" "{foo: bar, baz: [exactly]}")
253261
ryml_add_event_tool_test_tsonly(success_contkeys TRUE "" "{{this: is, a: keymap}: [seq,val]}")
254262
ryml_add_event_tool_test(success_evts_ints_resize TRUE "tsi --ints-size 1" "{{this: is, a: keymap}: [seq,val]}")
255263
ryml_add_event_tool_test(fail_evts_ints_resize FALSE "tsi --ints-size" "{{this: is, a: keymap}: [seq,val]}")

test/test_install/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ include(../../ext/c4core/cmake/c4Project.cmake)
77
c4_project(VERSION 0.11.1
88
AUTHOR "Joao Paulo Magalhaes <dev@jpmag.me>")
99

10-
1110
if(RYML_TEST_INSTALL_PACKAGE_MODE)
1211
find_package(ryml REQUIRED)
1312
add_library(ryml ALIAS ryml::ryml)
@@ -22,4 +21,7 @@ endif()
2221
enable_testing()
2322
set(RYML_INSTALL_TEST ON)
2423
include(../../ext/testbm.cmake)
24+
function(ryml_maybe_dump_test_yaml target)
25+
# nothing to do
26+
endfunction()
2527
add_subdirectory(../../test test)

test/test_lib/test_save.cpp

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
#ifdef RYML_SAVE_TEST_YAML
2+
3+
#include <c4/yml/common.hpp>
4+
#include <c4/yml/error.hpp>
5+
#include <c4/error.hpp>
6+
#include <c4/format.hpp>
7+
#include <c4/std/vector.hpp>
8+
#include <c4/std/string.hpp>
9+
#include <c4/fs/fs.hpp>
10+
#include <gtest/gtest.h>
11+
#include <map>
12+
#include <string>
13+
14+
C4_SUPPRESS_WARNING_GCC_CLANG_PUSH
15+
C4_SUPPRESS_WARNING_GCC_CLANG("-Wold-style-cast")
16+
17+
namespace c4 {
18+
namespace yml {
19+
20+
static char savedir[] = "./yamldump\0";
21+
22+
struct savehelper
23+
{
24+
std::vector<std::string> sources;
25+
std::vector<char> basename;
26+
std::vector<char> fullname;
27+
size_t indexpos;
28+
void reset_from_notest(csubstr filename)
29+
{
30+
char buf[1024];
31+
ssize_t ret = readlink("/proc/self/exe", buf, sizeof(buf)-1);
32+
_RYML_CHECK_BASIC(ret > 0);
33+
_RYML_CHECK_BASIC(ret < (int)sizeof(buf));
34+
csubstr exe = {buf, (size_t)ret};
35+
exe = exe.basename();
36+
static size_t count = 0;
37+
if(filename.empty())
38+
filename = "nofilename";
39+
filename = filename.basename();
40+
c4::formatrs(&basename, "{}_{}_{}", exe, filename, c4::fmt::zpad(count, 3));
41+
to_substr(basename).replace('/', '_');
42+
++count;
43+
prepare_fullname();
44+
}
45+
void reset_from_gtest(testing::TestInfo const* curr)
46+
{
47+
csubstr filename = to_csubstr(curr->file());
48+
csubstr suitename = to_csubstr(curr->test_suite_name());
49+
csubstr testname = to_csubstr(curr->name());
50+
// handle group tests differently:
51+
if(filename.ends_with("test_group.def.hpp"))
52+
{
53+
filename = suitename;
54+
size_t pos = filename.find('/');
55+
_RYML_ASSERT_BASIC(pos != npos);
56+
filename = filename.first(pos);
57+
suitename = suitename.sub(pos + 1);
58+
if(suitename.begins_with("YmlTestCase"))
59+
{
60+
pos = testname.last_of('/');
61+
_RYML_ASSERT_BASIC(pos != npos);
62+
suitename = testname.sub(pos+1);
63+
}
64+
}
65+
size_t pos = filename.find("test/");
66+
if(pos != npos)
67+
filename = filename.sub(pos + 5);
68+
if(filename.ends_with(".cpp") || filename.ends_with(".hpp"))
69+
filename = filename.offs(0, 4);
70+
c4::formatrs(&basename, "{}_{}-{}_{}", filename, fmt::zpad(curr->line(), 3), suitename, testname);
71+
to_substr(basename).replace('/', '_');
72+
prepare_fullname();
73+
}
74+
void prepare_fullname()
75+
{
76+
printf("new test! %.*s\n", (int)basename.size(), &basename.front());
77+
c4::formatrs(&fullname, "{}/{}--", savedir, basename);
78+
indexpos = fullname.size();
79+
fullname.resize(fullname.size() + 32);
80+
sources.clear();
81+
}
82+
};
83+
84+
static void save_impl(csubstr filename, csubstr extension, csubstr src)
85+
{
86+
static savehelper h = {};
87+
static testing::TestInfo const* prev = nullptr;
88+
testing::TestInfo const* curr = testing::UnitTest::GetInstance()->current_test_info();
89+
if(prev == nullptr)
90+
c4::fs::mkdirs(savedir);
91+
if(curr != prev || !curr)
92+
{
93+
prev = curr;
94+
if(curr)
95+
h.reset_from_gtest(curr);
96+
else
97+
h.reset_from_notest(filename);
98+
}
99+
if(to_csubstr(h.basename).find("FilterTest_filter") != npos)
100+
return; // this case is not interesting, and very noisy
101+
// have we already saved this source?
102+
for(std::string const& s : h.sources)
103+
if(to_csubstr(s) == src)
104+
return;
105+
// we'll place the source in this position:
106+
size_t index = h.sources.size(); // it is also the index we'll use for the savename
107+
h.sources.emplace_back(src.begin(), src.end());
108+
// now form the savename
109+
substr buf = to_substr(h.fullname).sub(h.indexpos);
110+
_RYML_ASSERT_BASIC(buf.len > 0);
111+
size_t len = c4::cat(buf, c4::fmt::zpad(index, 3), extension, '\0');
112+
_RYML_ASSERT_BASIC(len < buf.len);
113+
csubstr savename = to_substr(h.fullname).first(h.indexpos + len);
114+
// done! dump the file
115+
printf("saving %.*s\n", (int)savename.len, savename.str);
116+
c4::fs::file_put_contents(savename.str, src);
117+
}
118+
119+
void ryml_save_test_yaml(csubstr filename, csubstr src)
120+
{
121+
save_impl(filename, ".yaml", src);
122+
}
123+
void ryml_save_test_json(csubstr filename, csubstr src)
124+
{
125+
save_impl(filename, ".json", src);
126+
}
127+
} // namespace yml
128+
} // namespace c4
129+
130+
C4_SUPPRESS_WARNING_GCC_CLANG_POP
131+
132+
#endif // RYML_SAVE_TEST_YAML

test/test_singleheader/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,7 @@ include(../../ext/testbm.cmake)
3838

3939
enable_testing()
4040
set(RYML_DEFINED_FROM_SINGLEHEADER ON)
41+
function(ryml_maybe_dump_test_yaml target)
42+
# nothing to do
43+
endfunction()
4144
add_subdirectory(../../test test)

tools/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ c4_add_executable(ryml-parse-emit
33
SOURCES parse_emit.cpp
44
LIBS ryml c4fs
55
FOLDER tools)
6+
ryml_maybe_dump_test_yaml(ryml-parse-emit)
67

78
c4_add_executable(ryml-yaml-events
89
SOURCES yaml_events.cpp
@@ -20,3 +21,4 @@ c4_add_executable(ryml-yaml-events
2021
INC_DIRS ../test ../src_extra
2122
LIBS ryml c4fs
2223
FOLDER tools)
24+
ryml_maybe_dump_test_yaml(ryml-yaml-events)

tools/parse_emit.cpp

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,20 @@ Parse yaml from file (or stdin when file is `-`) and emit to stdout.
8686
);
8787
}
8888
bool timing_enabled = false;
89+
bool is_arg0(c4::csubstr arg, c4::csubstr argshort, c4::csubstr arglong) noexcept
90+
{
91+
return (arg == argshort) || (arg == arglong);
92+
}
93+
bool is_arg1(c4::csubstr arg, c4::csubstr argshort, c4::csubstr arglong, int i, int argc)
94+
{
95+
if(is_arg0(arg, argshort, arglong))
96+
{
97+
if(i + 1 >= argc)
98+
_RYML_ERR_BASIC("missing argument value: {}", arg);
99+
return true;
100+
}
101+
return false;
102+
}
89103
bool parse_args(int argc, const char *argv[], Args &args)
90104
{
91105
args = {};
@@ -98,28 +112,16 @@ bool parse_args(int argc, const char *argv[], Args &args)
98112
for(int i = 1; i+1 < argc; ++i)
99113
{
100114
c4::csubstr arg = c4::to_csubstr(argv[i]);
101-
auto arg0_is = [&](c4::csubstr argshort, c4::csubstr arglong){
102-
return (arg == argshort) || (arg == arglong);
103-
};
104-
auto arg1_is = [&](c4::csubstr argshort, c4::csubstr arglong){
105-
if(arg0_is(argshort, arglong))
106-
{
107-
if(i + 1 >= argc)
108-
_RYML_ERR_BASIC("missing argument value: {}", arg);
109-
return true;
110-
}
111-
return false;
112-
};
113-
if /**/(arg1_is("-e", "--reserve")) C4_CHECK(c4::from_chars(c4::to_csubstr(argv[++i]), &args.reserve_size));
114-
else if(arg1_is("-r", "--resolve")) args.resolve_refs = true;
115-
else if(arg1_is("-o", "--output")) args.output = c4::to_csubstr(argv[++i]);
116-
else if(arg0_is("-k", "--keep-refs")) args.keep_refs = true;
117-
else if(arg0_is("-p", "--print-tree")) args.print_tree = true;
118-
else if(arg0_is("-q", "--quiet")) args.quiet = true;
119-
else if(arg0_is("-j", "--json")) args.emit_as_json = true;
120-
else if(arg0_is("-s", "--string")) args.emit_to_string = true;
121-
else if(arg0_is("-t", "--timed")) args.timed_sections = true;
122-
else if(arg0_is("-h", "--help"))
115+
if /**/(is_arg1(arg, "-e", "--reserve" , i, argc)) C4_CHECK(c4::from_chars(c4::to_csubstr(argv[++i]), &args.reserve_size));
116+
else if(is_arg1(arg, "-o", "--output" , i, argc)) args.output = c4::to_csubstr(argv[++i]);
117+
else if(is_arg0(arg, "-r", "--resolve" )) args.resolve_refs = true;
118+
else if(is_arg0(arg, "-k", "--keep-refs" )) args.keep_refs = true;
119+
else if(is_arg0(arg, "-p", "--print-tree" )) args.print_tree = true;
120+
else if(is_arg0(arg, "-q", "--quiet" )) args.quiet = true;
121+
else if(is_arg0(arg, "-j", "--json" )) args.emit_as_json = true;
122+
else if(is_arg0(arg, "-s", "--string" )) args.emit_to_string = true;
123+
else if(is_arg0(arg, "-t", "--timed" )) args.timed_sections = true;
124+
else if(is_arg0(arg, "-h", "--help" ))
123125
{
124126
print_usage(argv[0]);
125127
return false;
@@ -335,7 +337,7 @@ void process_file(Args const& args, std::string *contents)
335337
else
336338
{
337339
C4_SUPPRESS_WARNING_CLANG_WITH_PUSH("-Wdeprecated-declarations") // fopen is deprecated
338-
FILE *output = fopen(args.output.str, "wb");
340+
FILE *output = fopen(args.output.str, "wb"); // NOLINT
339341
if (!output)
340342
_RYML_ERR_BASIC("could not open file: {}", args.output.str);
341343
{

0 commit comments

Comments
 (0)