diff --git a/meson.build b/meson.build index c59bd09..16fff8f 100644 --- a/meson.build +++ b/meson.build @@ -2,7 +2,7 @@ project('ssp', 'cpp', default_options : ['warning_level=3', 'cpp_std=c++17', - 'buildtype=debug']) + 'buildtype=debugoptimized']) includes = include_directories('include') subdir('test') diff --git a/test/test_converter.cpp b/test/test_converter.cpp index b6b3115..5bcab32 100644 --- a/test/test_converter.cpp +++ b/test/test_converter.cpp @@ -1,26 +1,7 @@ +#include "test_helpers.hpp" #include #include -#ifdef CMAKE_GITHUB_CI -#include -#else -#include -#endif - -class buffer { - constexpr static auto buff_size = 1024; - char data_[buff_size]; - -public: - char* operator()(const char* data) { - memset(data_, '\0', buff_size); - strcpy(data_, data); - return data_; - } -}; - -static buffer buff; - TEST_CASE("testing split") { ss::converter c; for (const auto& [s, expected, delim] : diff --git a/test/test_extractions.cpp b/test/test_extractions.cpp index d645b61..c0866e9 100644 --- a/test/test_extractions.cpp +++ b/test/test_extractions.cpp @@ -1,11 +1,6 @@ -#include +#include "test_helpers.hpp" #include - -#ifdef CMAKE_GITHUB_CI -#include -#else -#include -#endif +#include constexpr auto eps = 0.000001; using ld = long double; diff --git a/test/test_helpers.hpp b/test/test_helpers.hpp new file mode 100644 index 0000000..87abd8a --- /dev/null +++ b/test/test_helpers.hpp @@ -0,0 +1,22 @@ +#pragma once +#include + +#ifdef CMAKE_GITHUB_CI +#include +#else +#include +#endif + +class buffer { + constexpr static auto buff_size = 1024; + char data_[buff_size]; + +public: + char* operator()(const char* data) { + memset(data_, '\0', buff_size); + strcpy(data_, data); + return data_; + } +}; + +[[maybe_unused]] inline buffer buff; diff --git a/test/test_parser.cpp b/test/test_parser.cpp index 19dd8de..f9518d0 100644 --- a/test/test_parser.cpp +++ b/test/test_parser.cpp @@ -1,20 +1,18 @@ +#include "test_helpers.hpp" #include #include #include #include -#ifdef CMAKE_GITHUB_CI -#include -#else -#include -#endif - struct unique_file_name { const std::string name; - unique_file_name() : name{std::tmpnam(nullptr)} {} + unique_file_name() : name{std::tmpnam(nullptr)} { + } - ~unique_file_name() { std::filesystem::remove(name); } + ~unique_file_name() { + std::filesystem::remove(name); + } }; struct X { @@ -30,7 +28,9 @@ struct X { .append(delim) .append(s); } - auto tied() const { return std::tie(i, d, s); } + auto tied() const { + return std::tie(i, d, s); + } }; template @@ -167,10 +167,13 @@ struct test_struct { int i; double d; char c; - auto tied() { return std::tie(i, d, c); } + auto tied() { + return std::tie(i, d, c); + } }; -void expect_test_struct(const test_struct&) {} +void expect_test_struct(const test_struct&) { +} // various scenarios TEST_CASE("testing composite conversion") { @@ -392,7 +395,9 @@ struct my_string { my_string() = default; - ~my_string() { delete[] data; } + ~my_string() { + delete[] data; + } // make sure no object is copied my_string(const my_string&) = delete; @@ -423,7 +428,9 @@ struct xyz { my_string x; my_string y; my_string z; - auto tied() { return std::tie(x, y, z); } + auto tied() { + return std::tie(x, y, z); + } }; TEST_CASE("testing the moving of parsed values") { diff --git a/test/test_splitter.cpp b/test/test_splitter.cpp index e1651fa..d910cfe 100644 --- a/test/test_splitter.cpp +++ b/test/test_splitter.cpp @@ -1,48 +1,369 @@ +#include "test_helpers.hpp" #include #include #include #include -#ifdef CMAKE_GITHUB_CI -#include -#else -#include -#endif +// TODO make ss::quote accept only one argument -TEST_CASE("testing splitter with escaping") { - std::vector values{"10", "he\\\"llo", - "\\\"", "\\\"a\\,a\\\"", - "3.33", "a\\\""}; +namespace { +constexpr static auto combinations_size_default = 4; +size_t combinations_size = combinations_size_default; - char buff[128]; - // with quote - ss::splitter, ss::escape<'\\'>> s; - std::string delim = ","; - for (size_t i = 0; i < values.size() * values.size(); ++i) { - std::string input1; - std::string input2; - for (size_t j = 0; j < values.size(); ++j) { - if (i & (1 << j) && j != 2 && j != 3) { - input1.append(values[j]); - input2.append(values.at(values.size() - 1 - j)); - } else { - input1.append("\"" + values[j] + "\""); - input2.append("\"" + values.at(values.size() - 1 - j) + "\""); - } - input1.append(delim); - input2.append(delim); +struct set_combinations_size { + set_combinations_size(size_t size) { + combinations_size = size; + } + ~set_combinations_size() { + combinations_size = combinations_size_default; + } +}; + +std::vector words(const ss::split_input& input) { + std::vector ret; + for (const auto& [begin, end] : input) { + ret.emplace_back(begin, end); + } + return ret; +} + +[[maybe_unused]] std::string concat(const std::vector& v) { + std::string ret = "{"; + for (const auto& i : v) { + ret.append(i).append(" "); + } + ret.back() = ('}'); + return ret; +} + +std::vector combinations(const std::vector& v, + const std::string& delim, size_t n) { + if (n <= 1) { + return v; + } + std::vector ret; + auto inner_combinations = combinations(v, delim, n - 1); + for (auto& i : v) { + for (auto& j : inner_combinations) { + ret.push_back(i + delim + j); } - input1.pop_back(); - input2.pop_back(); - input1.append("\0\""); - input2.append("\0\""); + } + return ret; +} - memcpy(buff, input1.c_str(), input1.size() + 1); - auto tup1 = s.split(buff, delim); - CHECK(tup1.size() == 6); +std::vector> vector_combinations( + const std::vector& v, size_t n) { + std::vector> ret; + if (n <= 1) { + for (auto& i : v) { + ret.push_back({i}); + } + return ret; + } - memcpy(buff, input2.c_str(), input2.size() + 1); - auto tup2 = s.split(buff, delim); - CHECK(tup2.size() == 6); + auto inner_combinations = vector_combinations(v, n - 1); + for (auto& i : v) { + for (auto j : inner_combinations) { + j.insert(j.begin(), i); + ret.push_back(std::move(j)); + } + } + return ret; +} + +using case_type = std::vector; +std::pair, std::vector>> +make_combinations(const std::vector& input, + const std::vector& output, + const std::string& delim) { + std::vector lines; + std::vector> expectations; + for (size_t i = 0; i < combinations_size; ++i) { + auto l = combinations(input, delim, i); + lines.reserve(lines.size() + l.size()); + lines.insert(lines.end(), l.begin(), l.end()); + + auto e = vector_combinations(output, i); + expectations.reserve(expectations.size() + e.size()); + expectations.insert(expectations.end(), e.begin(), e.end()); + } + + return {std::move(lines), std::move(expectations)}; +} + +auto spaced(const case_type& input, std::string s) { + case_type ret = input; + for (auto& i : input) { + ret.push_back(s + i + s); + ret.push_back(i + s); + ret.push_back(s + i); + ret.push_back(s + s + i); + ret.push_back(s + s + i + s); + ret.push_back(s + s + i + s + s); + ret.push_back(s + i + s + s); + ret.push_back(i + s + s); + } + + return ret; +} + +auto spaced(const case_type& input, std::string s1, std::string s2) { + case_type ret = input; + for (auto& i : input) { + ret.push_back(s1 + i + s2); + ret.push_back(s2 + i + s1); + ret.push_back(s2 + s2 + s1 + s1 + i); + ret.push_back(i + s1 + s2 + s1 + s2); + ret.push_back(s1 + s1 + s1 + i + s2 + s2 + s2); + ret.push_back(s2 + s2 + s2 + i + s1 + s1 + s1); + } + + return ret; +} +} /* namespace */ + +/* ********************************** */ +/* ********************************** */ + +using matches_type = std::vector>; + +template +void test_combinations(matches_type& matches, std::vector delims) { + + ss::splitter s; + std::vector inputs; + std::vector outputs; + for (const auto& [cases, e] : matches) { + for (const auto& c : cases) { + inputs.emplace_back(c); + outputs.emplace_back(e); + } + } + + for (const auto& delim : delims) { + auto [lines, expectations] = make_combinations(inputs, outputs, delim); + + REQUIRE(lines.size() == expectations.size()); + + for (size_t i = 0; i < lines.size(); ++i) { + auto vec = s.split(buff(lines[i].c_str()), delim); + CHECK(s.valid()); + CHECK(words(vec) == expectations[i]); + } + } +} + +TEST_CASE("testing splitter no setup") { + { + matches_type p{{{"x"}, "x"}, {{"\""}, "\""}, + {{""}, ""}, {{"\n"}, "\n"}, + {{"\"\""}, "\"\""}, {{"\" \\ \""}, "\" \\ \""}, + {{" "}, " "}}; + test_combinations(p, {",", ";", "\t", "::"}); + } +} + +TEST_CASE("testing splitter quote") { + case_type case1 = {R"("""")"}; + case_type case2 = {R"("x""x")", R"(x"x)"}; + case_type case3 = {R"("")", R"()"}; + case_type case4 = {R"("x")", R"(x)"}; + case_type case5 = {R"("""""")"}; + case_type case6 = {R"("\")", R"(\)"}; + case_type case7 = {R"("xxxxxxxxxx")", R"(xxxxxxxxxx)"}; + + std::vector delims = {",", "::", " ", "\t", "\n"}; + + { + matches_type p{{case1, "\""}, {case2, "x\"x"}, {case3, ""}, + {case4, "x"}, {case5, "\"\""}, {case6, "\\"}, + {case7, "xxxxxxxxxx"}}; + test_combinations>(p, delims); + } + + case_type case8 = {R"(",")"}; + case_type case9 = {R"("x,")"}; + case_type case10 = {R"(",x")"}; + case_type case11 = {R"("x,x")"}; + case_type case12 = {R"(",,")"}; + { + matches_type p{{case1, "\""}, {case3, ""}, {case8, ","}, + {case9, "x,"}, {case10, ",x"}, {case11, "x,x"}, + {case12, ",,"}}; + test_combinations>(p, {","}); + } + + case_type case13 = {R"("::")"}; + case_type case14 = {R"("x::")"}; + case_type case15 = {R"("::x")"}; + case_type case16 = {R"("x::x")"}; + case_type case17 = {R"("::::")"}; + + { + matches_type p{{case1, "\""}, {case3, ""}, {case13, "::"}, + {case14, "x::"}, {case15, "::x"}, {case16, "x::x"}, + {case17, "::::"}}; + test_combinations>(p, {"::"}); + } +} + +TEST_CASE("testing splitter trim") { + auto guard = set_combinations_size(3); + case_type case1 = spaced({R"(x)"}, " "); + case_type case2 = spaced({R"(yy)"}, " "); + case_type case3 = spaced({R"(y y)"}, " "); + case_type case4 = spaced({R"()"}, " "); + + std::vector delims = {",", "::", "\t", "\n"}; + + { + matches_type p{{case1, "x"}, + {case2, "yy"}, + {case3, "y y"}, + {case4, ""}}; + test_combinations>(p, delims); + } + + case_type case5 = spaced({"z"}, "\t"); + case_type case6 = spaced({"ab"}, " ", "\t"); + case_type case7 = spaced({"a\tb"}, " ", "\t"); + case_type case8 = spaced({"a \t b"}, " ", "\t"); + + { + matches_type p{{case1, "x"}, {case2, "yy"}, {case3, "y y"}, + {case4, ""}, {case5, "z"}, {case6, "ab"}, + {case7, "a\tb"}, {case8, "a \t b"}}; + test_combinations>(p, {",", "::", "\n"}); + } +} + +TEST_CASE("testing splitter escape") { + case_type case1 = {R"(x)", R"(\x)"}; + case_type case2 = {R"(xx)", R"(\xx)", R"(x\x)", R"(\x\x)"}; + case_type case3 = {R"(\\)"}; + + std::vector delims = {",", "::", " ", "\t", "\n"}; + + { + matches_type p{{case1, "x"}, {case2, "xx"}, {case3, "\\"}}; + test_combinations>(p, delims); + } + + case_type case4 = {R"(\,)"}; + case_type case5 = {R"(x#,)"}; + case_type case6 = {R"(#,x)"}; + case_type case7 = {R"(x\,x)"}; + + { + matches_type p{{case1, "x"}, {case2, "xx"}, {case3, "\\"}, + {case4, ","}, {case5, "x,"}, {case6, ",x"}, + {case7, "x,x"}}; + test_combinations>(p, {","}); + } + + case_type case8 = {R"(\:\:)"}; + case_type case9 = {R"(x\::x)"}; + + { + matches_type p{{case1, "x"}, + {case2, "xx"}, + {case3, "\\"}, + {case8, "::"}, + {case9, "x::x"}}; + test_combinations>(p, {"::"}); + } +} + +TEST_CASE("testing splitter quote and trim") { + auto guard = set_combinations_size(3); + case_type case1 = spaced({R"("""")"}, " "); + case_type case2 = spaced({R"("x""x")", R"(x"x)"}, " "); + case_type case3 = spaced({R"("")", R"()"}, " "); + case_type case4 = spaced({R"("x")", R"(x)"}, " "); + case_type case5 = spaced({R"("""""")"}, " "); + case_type case6 = spaced({R"("\")", R"(\)"}, " "); + case_type case7 = spaced({R"("xxxxxxxxxx")", R"(xxxxxxxxxx)"}, " "); + + std::vector delims = {",", "::", "\t", "\n"}; + + { + matches_type p{{case1, "\""}, {case2, "x\"x"}, {case3, ""}, + {case4, "x"}, {case5, "\"\""}, {case6, "\\"}, + {case7, "xxxxxxxxxx"}}; + test_combinations, ss::trim<' '>>(p, delims); + } + + return; + case_type case8 = {R"(",")"}; + case_type case9 = {R"("x,")"}; + case_type case10 = {R"(",x")"}; + case_type case11 = {R"("x,x")"}; + case_type case12 = {R"(",,")"}; + { + matches_type p{{case1, "\""}, {case3, ""}, {case8, ","}, + {case9, "x,"}, {case10, ",x"}, {case11, "x,x"}, + {case12, ",,"}}; + test_combinations>(p, {","}); + } + + case_type case13 = {R"("::")"}; + case_type case14 = {R"("x::")"}; + case_type case15 = {R"("::x")"}; + case_type case16 = {R"("x::x")"}; + case_type case17 = {R"("::::")"}; + + { + matches_type p{{case1, "\""}, {case3, ""}, {case13, "::"}, + {case14, "x::"}, {case15, "::x"}, {case16, "x::x"}, + {case17, "::::"}}; + test_combinations>(p, {"::"}); + } +} + +TEST_CASE("testing splitter quote and escape") { + case_type case1 = {R"("\"")", R"(\")", R"("""")"}; + case_type case2 = {R"("x\"x")", R"(x\"x)", R"(x"x)", R"("x""x")"}; + case_type case3 = {R"("")", R"()"}; + case_type case4 = {R"("x")", R"(x)"}; + case_type case5 = {R"("\"\"")", R"("""""")", R"("\"""")", R"("""\"")"}; + case_type case6 = {R"("\\")", R"(\\)"}; + case_type case7 = {R"("xxxxxxxxxx")", R"(xxxxxxxxxx)"}; + + std::vector delims = {",", "::", " ", "\t", "\n"}; + + ss::splitter, ss::escape<'\\'>> s; + + { + matches_type p{{case1, "\""}, {case2, "x\"x"}, {case3, ""}, + {case4, "x"}, {case5, "\"\""}, {case6, "\\"}, + {case7, "xxxxxxxxxx"}}; + test_combinations, ss::escape<'\\'>>(p, delims); + } + + case_type case8 = {R"('xxxxxxxxxx')", R"(xxxxxxxxxx)"}; + case_type case9 = {R"('')", R"()"}; + case_type case10 = {R"('#\')", R"(#\)"}; + case_type case11 = {R"('#'')", R"(#')", R"('''')"}; + case_type case12 = {R"('##')", R"(##)"}; + { + matches_type p{{case8, "xxxxxxxxxx"}, + {case9, ""}, + {case10, "\\"}, + {case11, "'"}, + {case12, "#"}}; + test_combinations, ss::escape<'#'>>(p, delims); + } + + case_type case13 = {R"("x,x")", R"(x\,x)", R"(x#,x)", + R"("x\,x")", R"("x#,x")", R"("x#,x")"}; + case_type case14 = {R"("#\\#")", R"(#\\#)", R"(\\##)", R"("\\##")"}; + + { + matches_type p{{case1, "\""}, + {case2, "x\"x"}, + {case3, ""}, + {case13, "x,x"}, + {case14, "\\#"}}; + test_combinations, ss::escape<'\\', '#'>>(p, {","}); } }