#include "test_helpers.hpp" #include #include #include #include // TODO make ss::quote accept only one argument namespace { constexpr static auto combinations_size_default = 4; size_t combinations_size = combinations_size_default; 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); } } return ret; } 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; } 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, {","}); } }