diff --git a/include/ss/parser.hpp b/include/ss/parser.hpp index 856182b..690d3d2 100644 --- a/include/ss/parser.hpp +++ b/include/ss/parser.hpp @@ -531,10 +531,6 @@ private: [[nodiscard]] bool strict_split(header_splitter& splitter, std::string& header) { - if (header.empty()) { - return false; - } - if constexpr (throw_on_error) { try { splitter.split(header.data(), reader_.delim_); diff --git a/ssp.hpp b/ssp.hpp index 94227ff..ebe031c 100644 --- a/ssp.hpp +++ b/ssp.hpp @@ -2805,10 +2805,6 @@ private: [[nodiscard]] bool strict_split(header_splitter& splitter, std::string& header) { - if (header.empty()) { - return false; - } - if constexpr (throw_on_error) { try { splitter.split(header.data(), reader_.delim_); diff --git a/test/test_parser1_4.cpp b/test/test_parser1_4.cpp index ca13d2b..9b5709a 100644 --- a/test/test_parser1_4.cpp +++ b/test/test_parser1_4.cpp @@ -178,7 +178,8 @@ void test_invalid_fields(const std::vector& lines, auto check_header = [&lines](auto& p) { if (lines.empty()) { - CHECK(p.header().empty()); + CHECK_EQ(p.header().size(), 1); + CHECK_EQ(p.header().at(0), ""); CHECK_EQ(merge_header(p.header(), ","), p.raw_header()); } else { CHECK_EQ(lines[0], merge_header(p.header())); @@ -263,7 +264,7 @@ void test_invalid_fields(const std::vector& lines, } } -TEST_CASE_TEMPLATE("test invalid fheader fields usage", T, +TEST_CASE_TEMPLATE("test invalid header fields usage", T, ParserOptionCombinations) { test_invalid_fields({}, {}); @@ -412,13 +413,42 @@ TEST_CASE_TEMPLATE("test invalid header", T, ParserOptionCombinations) { } { + std::vector expected_header = {""}; auto [p, _] = make_parser(f.name); - CHECK(p.header().empty()); - CHECK_EQ(merge_header(p.header()), p.raw_header()); + CHECK_EQ_ARRAY(expected_header, p.header()); + CHECK_EQ("", p.raw_header()); CHECK(p.valid()); } - // Unterminated quote in header + // Empty header fields + { + std::ofstream out{f.name}; + out << ",," << std::endl; + out << "1,2,3" << std::endl; + } + + { + std::vector expected_header = {"", "", ""}; + auto [p, _] = make_parser(f.name); + CHECK_EQ_ARRAY(expected_header, p.header()); + CHECK_EQ(",,", p.raw_header()); + CHECK(p.valid()); + + auto command1 = [&p = p] { std::ignore = p.field_exists("Int"); }; + expect_error_on_command(p, command1); + + auto command2 = [&p = p] { p.use_fields("Int"); }; + expect_error_on_command(p, command2); + } +} + +template +void test_unterminated_quote_header() { + constexpr auto buffer_mode = T::BufferMode::value; + using ErrorMode = typename T::ErrorMode; + + unique_file_name f{"unterminated_quote_header"}; + { std::ofstream out{f.name}; out << "\"Int" << std::endl; @@ -426,29 +456,36 @@ TEST_CASE_TEMPLATE("test invalid header", T, ParserOptionCombinations) { } { - auto [p, _] = - make_parser>(f.name); - auto command = [&p = p] { std::ignore = p.header(); }; - expect_error_on_command(p, command); - CHECK_EQ(p.raw_header(), "\"Int"); - } + auto [p, _] = make_parser(f.name); - { - auto [p, _] = - make_parser, ss::multiline>( - f.name); - auto command = [&p = p] { std::ignore = p.header(); }; - expect_error_on_command(p, command); + auto command0 = [&p = p] { std::ignore = p.header(); }; + expect_error_on_command(p, command0); CHECK_EQ(p.raw_header(), "\"Int"); - } - { - auto [p, _] = make_parser, - ss::escape<'\\'>, ss::multiline>(f.name); - auto command = [&p = p] { std::ignore = p.header(); }; - expect_error_on_command(p, command); - CHECK_EQ(p.raw_header(), "\"Int"); + auto command1 = [&p = p] { std::ignore = p.field_exists("Int"); }; + expect_error_on_command(p, command1); + + auto command2 = [&p = p] { p.use_fields("Int"); }; + expect_error_on_command(p, command2); } +} + +TEST_CASE_TEMPLATE("test unterminated quote header", T, + ParserOptionCombinations) { + using quote = ss::quote<'"'>; + using escape = ss::escape<'\\'>; + test_unterminated_quote_header(); + test_unterminated_quote_header(); + test_unterminated_quote_header(); + test_unterminated_quote_header(); +} + +template +void test_unterminated_escape_header() { + constexpr auto buffer_mode = T::BufferMode::value; + using ErrorMode = typename T::ErrorMode; + + unique_file_name f{"unterminated_escape_header"}; // Unterminated escape in header { @@ -458,30 +495,30 @@ TEST_CASE_TEMPLATE("test invalid header", T, ParserOptionCombinations) { } { - auto [p, _] = - make_parser>(f.name); - auto command = [&p = p] { std::ignore = p.header(); }; - expect_error_on_command(p, command); - CHECK_EQ(p.raw_header(), "Int\\"); - } + auto [p, _] = make_parser(f.name); - { - auto [p, _] = make_parser, - ss::multiline>(f.name); - auto command = [&p = p] { std::ignore = p.header(); }; - expect_error_on_command(p, command); + auto command0 = [&p = p] { std::ignore = p.header(); }; + expect_error_on_command(p, command0); CHECK_EQ(p.raw_header(), "Int\\"); - } - { - auto [p, _] = make_parser, - ss::quote<'"'>, ss::multiline>(f.name); - auto command = [&p = p] { std::ignore = p.header(); }; - expect_error_on_command(p, command); - CHECK_EQ(p.raw_header(), "Int\\"); + auto command1 = [&p = p] { std::ignore = p.field_exists("Int"); }; + expect_error_on_command(p, command1); + + auto command2 = [&p = p] { p.use_fields("Int"); }; + expect_error_on_command(p, command2); } } +TEST_CASE_TEMPLATE("test unterminated escape header", T, + ParserOptionCombinations) { + using quote = ss::quote<'"'>; + using escape = ss::escape<'\\'>; + test_unterminated_escape_header(); + test_unterminated_escape_header(); + test_unterminated_escape_header(); + test_unterminated_escape_header(); +} + template void test_ignore_empty(const std::vector& data) { constexpr auto buffer_mode = T::BufferMode::value;