From 1af0e75788e8d86219122a1616c260530f44a63d Mon Sep 17 00:00:00 2001 From: ado Date: Sun, 21 Feb 2021 02:49:23 +0100 Subject: [PATCH] update all unit tests, fix bug with unterminated escape, updated multiline parsing, refactored some code, removed unused code --- include/ss/converter.hpp | 4 + include/ss/parser.hpp | 46 ++++++--- include/ss/splitter.hpp | 144 ++++++++++++++++----------- test/meson.build | 6 +- test/test_converter.cpp | 201 +++++++++++++++++++++----------------- test/test_extractions.cpp | 38 +++---- test/test_helpers.hpp | 5 + test/test_parser.cpp | 64 ++++++------ test/test_splitter.cpp | 195 +++++++++++++++++++++++------------- 9 files changed, 422 insertions(+), 281 deletions(-) diff --git a/include/ss/converter.hpp b/include/ss/converter.hpp index d774040..b66002b 100644 --- a/include/ss/converter.hpp +++ b/include/ss/converter.hpp @@ -198,6 +198,10 @@ private: return splitter_.resplit(new_line, new_size, delim); } + size_t size_shifted() { + return splitter_.size_shifted(); + } + //////////////// // error //////////////// diff --git a/include/ss/parser.hpp b/include/ss/parser.hpp index eebeee9..bf0fad6 100644 --- a/include/ss/parser.hpp +++ b/include/ss/parser.hpp @@ -9,9 +9,6 @@ #include #include -// TODO remove -#include - namespace ss { template @@ -21,6 +18,12 @@ class parser { using multiline = typename setup::multiline; using error_type = ss::ternary_t; + constexpr static bool escaped_multiline_enabled = + multiline::enabled && setup::escape::enabled; + + constexpr static bool quoted_multiline_enabled = + multiline::enabled && setup::quote::enabled; + public: parser(const std::string& file_name, const std::string& delim = ss::default_delimiter) @@ -385,13 +388,12 @@ private: size_t size = remove_eol(next_line_buffer_, ssize); size_t limit = 0; - if constexpr (multiline::enabled && - setup::escape::enabled) { + if constexpr (escaped_multiline_enabled) { while (escaped_eol(size)) { if (multiline_limit_reached(limit)) { return true; } - if (!append_line(next_line_buffer_, size)) { + if (!append_next_line_to_buffer(next_line_buffer_, size)) { return false; } } @@ -399,15 +401,27 @@ private: next_line_converter_.split(next_line_buffer_, delim_); - if constexpr (multiline::enabled && - setup::quote::enabled) { + if constexpr (quoted_multiline_enabled) { while (unterminated_quote()) { if (multiline_limit_reached(limit)) { return true; } - if (!append_line(next_line_buffer_, size)) { + if (!append_next_line_to_buffer(next_line_buffer_, size)) { return false; } + + if constexpr (escaped_multiline_enabled) { + while (escaped_eol(size)) { + if (multiline_limit_reached(limit)) { + return true; + } + if (!append_next_line_to_buffer(next_line_buffer_, + size)) { + return false; + } + } + } + next_line_converter_.resplit(next_line_buffer_, size); } } @@ -450,7 +464,7 @@ private: void undo_remove_eol(char* buffer, size_t& string_end) { if (next_line_converter_.unterminated_quote()) { - string_end -= next_line_converter_.splitter_.escaped_; + string_end -= next_line_converter_.size_shifted(); } if (crlf_) { std::copy_n("\r\n\0", 3, buffer + string_end); @@ -483,16 +497,16 @@ private: first_size += second_size; } - bool append_line(char*& dst_buffer, size_t& dst_size) { - undo_remove_eol(dst_buffer, dst_size); + bool append_next_line_to_buffer(char*& buffer, size_t& size) { + undo_remove_eol(buffer, size); - ssize_t ssize = getline(&helper_buffer_, &helper_size_, file_); - if (ssize == -1) { + ssize_t next_ssize = getline(&helper_buffer_, &helper_size_, file_); + if (next_ssize == -1) { return false; } - size_t size = remove_eol(helper_buffer_, ssize); - realloc_concat(dst_buffer, dst_size, helper_buffer_, size); + size_t next_size = remove_eol(helper_buffer_, next_ssize); + realloc_concat(buffer, size, helper_buffer_, next_size); return true; } diff --git a/include/ss/splitter.hpp b/include/ss/splitter.hpp index b11c22e..96ca082 100644 --- a/include/ss/splitter.hpp +++ b/include/ss/splitter.hpp @@ -9,9 +9,6 @@ #include #include -// TODO remove -#include - namespace ss { template @@ -21,6 +18,7 @@ private: using trim_left = typename setup::trim_left; using trim_right = typename setup::trim_right; using escape = typename setup::escape; + using multiline = typename setup::multiline; constexpr static auto string_error = setup::string_error; constexpr static auto is_const_line = !quote::enabled && !escape::enabled; @@ -50,7 +48,9 @@ public: const split_data& split(line_ptr_type new_line, const std::string& delimiter = default_delimiter) { split_data_.clear(); - return resplit(new_line, -1, delimiter); + line_ = new_line; + begin_ = line_; + return split_impl_select_delim(delimiter); } private: @@ -58,6 +58,11 @@ private: // resplit //////////////// + // number of characters the end of line is shifted backwards + size_t size_shifted() const { + return escaped_; + } + void adjust_ranges(const char* old_line) { for (auto& [begin, end] : split_data_) { begin = begin - old_line + line_; @@ -68,33 +73,35 @@ private: const split_data& resplit( line_ptr_type new_line, ssize_t new_size, const std::string& delimiter = default_delimiter) { - line_ = new_line; // resplitting, continue from last slice - if constexpr (quote::enabled) { - if (!split_data_.empty() && unterminated_quote()) { - const auto& last = std::prev(split_data_.end()); - const auto [old_line, old_begin] = *last; - size_t begin = old_begin - old_line - 1; - split_data_.pop_back(); - adjust_ranges(old_line); - - // safety measure - if (new_size != -1 && static_cast(new_size) < begin) { - set_error_invalid_resplit(); - return split_data_; - } - - std::cout << "======================" << std::endl; - std::cout << "resplitting" << std::endl; - resplitting_ = true; - begin_ = line_ + begin; - size_t end = end_ - old_line - escaped_; - end_ = line_ + end; - curr_ = end_; - } + if (!quote::enabled || !multiline::enabled || split_data_.empty() || + !unterminated_quote()) { + set_error_invalid_resplit(); + return split_data_; } + const auto [old_line, old_begin] = *std::prev(split_data_.end()); + size_t begin = old_begin - old_line - 1; + + // safety measure + if (new_size != -1 && static_cast(new_size) < begin) { + set_error_invalid_resplit(); + return split_data_; + } + + // if unterminated quote, the last element is junk + split_data_.pop_back(); + + line_ = new_line; + adjust_ranges(old_line); + + begin_ = line_ + begin; + end_ = line_ - old_line + end_ - escaped_; + curr_ = end_; + + resplitting_ = true; + return split_impl_select_delim(delimiter); } @@ -129,6 +136,15 @@ private: } } + void set_error_unterminated_escape() { + if constexpr (string_error) { + error_.clear(); + error_.append("unterminated escape at the end of the line"); + } else { + error_ = true; + } + } + void set_error_unterminated_quote() { unterminated_quote_ = true; if constexpr (string_error) { @@ -215,25 +231,14 @@ private: // shifting //////////////// - void shift_and_set_current() { - if constexpr (!is_const_line) { - if (escaped_ > 0) { - std::copy_n(curr_ + escaped_, end_ - curr_ - escaped_, curr_); - curr_ = end_ - escaped_; - return; - } - } - curr_ = end_; - } - - void shift_and_push() { - shift_and_set_current(); - split_data_.emplace_back(begin_, curr_); - } - void shift_if_escaped(line_ptr_type& curr) { if constexpr (escape::enabled) { if (escape::match(*curr)) { + if (curr[1] == '\0') { + set_error_unterminated_escape(); + done_ = true; + return; + } shift_and_jump_escape(); } } @@ -252,6 +257,22 @@ private: begin_ = end_ + n; } + void shift_and_push() { + shift_and_set_current(); + split_data_.emplace_back(begin_, curr_); + } + + void shift_and_set_current() { + if constexpr (!is_const_line) { + if (escaped_ > 0) { + std::copy_n(curr_ + escaped_, end_ - curr_ - escaped_, curr_); + curr_ = end_ - escaped_; + return; + } + } + curr_ = end_; + } + //////////////// // split impl //////////////// @@ -273,10 +294,6 @@ private: template const split_data& split_impl(const Delim& delim) { - if (split_data_.empty()) { - begin_ = line_; - } - trim_left_if_enabled(begin_); for (done_ = false; !done_; read(delim)) @@ -293,11 +310,13 @@ private: void read(const Delim& delim) { escaped_ = 0; if constexpr (quote::enabled) { - if (resplitting_) { - resplitting_ = false; - ++begin_; - read_quoted(delim); - return; + if constexpr (multiline::enabled) { + if (resplitting_) { + resplitting_ = false; + ++begin_; + read_quoted(delim); + return; + } } if (quote::match(*begin_)) { curr_ = end_ = ++begin_; @@ -336,19 +355,27 @@ private: template void read_quoted(const Delim& delim) { if constexpr (quote::enabled) { - std::cout << "start loop: " << std::endl; while (true) { - std::cout << "- " << *end_ << std::endl; if (!quote::match(*end_)) { if constexpr (escape::enabled) { if (escape::match(*end_)) { + if (end_[1] == '\0') { + // eol, unterminated escape + // eg: ... "hel\\0 + set_error_unterminated_escape(); + done_ = true; + break; + } + // not eol + shift_and_jump_escape(); ++end_; continue; } } + // not escaped - // unterminated quote error + // eol, unterminated quote error // eg: ..."hell\0 -> quote not terminated if (*end_ == '\0') { shift_and_set_current(); @@ -357,9 +384,13 @@ private: done_ = true; break; } + // not eol + ++end_; continue; } + // quote found + // ... auto [width, valid] = match_delimiter(end_ + 1, delim); @@ -368,6 +399,7 @@ private: shift_push_and_start_next(width + 1); break; } + // not delimiter // double quote // eg: ...,"hel""lo",... -> hel"lo @@ -376,8 +408,8 @@ private: ++end_; continue; } + // not double quote - // not a delimiter if (width == 0) { // eol // eg: ...,"hello" \0 -> hello diff --git a/test/meson.build b/test/meson.build index 708f9ad..a8596eb 100644 --- a/test/meson.build +++ b/test/meson.build @@ -1,9 +1,9 @@ test_sources = files([ 'test_main.cpp', - #'test_splitter.cpp', - #'test_converter.cpp', + 'test_splitter.cpp', + 'test_converter.cpp', 'test_parser.cpp', - #'test_extractions.cpp', + 'test_extractions.cpp', ]) doctest_proj = subproject('doctest') diff --git a/test/test_converter.cpp b/test/test_converter.cpp index 044c85d..434eeb8 100644 --- a/test/test_converter.cpp +++ b/test/test_converter.cpp @@ -14,10 +14,10 @@ TEST_CASE("converter test split") { {"x", {"x"}, ","}} // clang-format on ) { auto split = c.split(s, delim); - CHECK(split.size() == expected.size()); + CHECK_EQ(split.size(), expected.size()); for (size_t i = 0; i < split.size(); ++i) { auto s = std::string(split[i].first, split[i].second); - CHECK(s == expected[i]); + CHECK_EQ(s, expected[i]); } } } @@ -28,68 +28,68 @@ TEST_CASE("converter test valid conversions") { { auto tup = c.convert("5"); REQUIRE(c.valid()); - CHECK(tup == 5); + CHECK_EQ(tup, 5); } { auto tup = c.convert("5,junk"); REQUIRE(c.valid()); - CHECK(tup == 5); + CHECK_EQ(tup, 5); } { auto tup = c.convert("junk,5"); REQUIRE(c.valid()); - CHECK(tup == 5); + CHECK_EQ(tup, 5); } { auto tup = c.convert("5\njunk\njunk", "\n"); REQUIRE(c.valid()); - CHECK(tup == 5); + CHECK_EQ(tup, 5); } { auto tup = c.convert("junk 5 junk", " "); REQUIRE(c.valid()); - CHECK(tup == 5); + CHECK_EQ(tup, 5); } { auto tup = c.convert("junk\tjunk\t5", "\t"); REQUIRE(c.valid()); - CHECK(tup == 5); + CHECK_EQ(tup, 5); } { auto tup = c.convert>("junk\tjunk\t5", "\t"); REQUIRE(c.valid()); REQUIRE(tup.has_value()); - CHECK(tup == 5); + CHECK_EQ(tup, 5); } { auto tup = c.convert("5,6.6,junk"); REQUIRE(c.valid()); - CHECK(tup == std::make_tuple(5, 6.6)); + CHECK_EQ(tup, std::make_tuple(5, 6.6)); } { auto tup = c.convert("5,junk,6.6"); REQUIRE(c.valid()); - CHECK(tup == std::make_tuple(5, 6.6)); + CHECK_EQ(tup, std::make_tuple(5, 6.6)); } { auto tup = c.convert("junk;5;6.6", ";"); REQUIRE(c.valid()); - CHECK(tup == std::make_tuple(5, 6.6)); + CHECK_EQ(tup, std::make_tuple(5, 6.6)); } { auto tup = c.convert, double>("junk;5;6.6", ";"); REQUIRE(c.valid()); REQUIRE(std::get<0>(tup).has_value()); - CHECK(tup == std::make_tuple(5, 6.6)); + CHECK_EQ(tup, std::make_tuple(5, 6.6)); } { auto tup = c.convert, double>("junk;5.4;6.6", ";"); REQUIRE(c.valid()); - REQUIRE(!std::get<0>(tup).has_value()); - CHECK(tup == std::make_tuple(std::optional{}, 6.6)); + REQUIRE_FALSE(std::get<0>(tup).has_value()); + CHECK_EQ(tup, std::make_tuple(std::optional{}, 6.6)); } { auto tup = @@ -97,7 +97,7 @@ TEST_CASE("converter test valid conversions") { ";"); REQUIRE(c.valid()); REQUIRE(std::holds_alternative(std::get<0>(tup))); - CHECK(tup == std::make_tuple(std::variant{5}, 6.6)); + CHECK_EQ(tup, std::make_tuple(std::variant{5}, 6.6)); } { auto tup = @@ -105,7 +105,7 @@ TEST_CASE("converter test valid conversions") { ";"); REQUIRE(c.valid()); REQUIRE(std::holds_alternative(std::get<0>(tup))); - CHECK(tup == std::make_tuple(std::variant{5.5}, 6.6)); + CHECK_EQ(tup, std::make_tuple(std::variant{5.5}, 6.6)); } } @@ -113,64 +113,64 @@ TEST_CASE("converter test invalid conversions") { ss::converter c; c.convert(""); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert("10", ""); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert(""); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert(",junk"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert("junk,"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert("x"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert("x"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert("x,junk"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert("junk,x"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert, double>("junk;.5.5;6", ";"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); } TEST_CASE("converter test ss:ax restriction (all except)") { ss::converter c; c.convert>("0"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert>("1"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert>("junk,c,1"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert, char>("1,c"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); { int tup = c.convert>("3"); REQUIRE(c.valid()); - CHECK(tup == 3); + CHECK_EQ(tup, 3); } { std::tuple tup = c.convert>("c,3"); REQUIRE(c.valid()); - CHECK(tup == std::make_tuple('c', 3)); + CHECK_EQ(tup, std::make_tuple('c', 3)); } { std::tuple tup = c.convert, char>("3,c"); REQUIRE(c.valid()); - CHECK(tup == std::make_tuple(3, 'c')); + CHECK_EQ(tup, std::make_tuple(3, 'c')); } } @@ -178,33 +178,33 @@ TEST_CASE("converter test ss:nx restriction (none except)") { ss::converter c; c.convert>("3"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert>("c,3"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert, char>("3,c"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); { auto tup = c.convert>("3"); REQUIRE(c.valid()); - CHECK(tup == 3); + CHECK_EQ(tup, 3); } { auto tup = c.convert>("2"); REQUIRE(c.valid()); - CHECK(tup == 2); + CHECK_EQ(tup, 2); } { auto tup = c.convert>("c,junk,1"); REQUIRE(c.valid()); - CHECK(tup == std::make_tuple('c', 1)); + CHECK_EQ(tup, std::make_tuple('c', 1)); } { auto tup = c.convert, char>("1,c"); REQUIRE(c.valid()); - CHECK(tup == std::make_tuple(1, 'c')); + CHECK_EQ(tup, std::make_tuple(1, 'c')); } } @@ -212,33 +212,33 @@ TEST_CASE("converter test ss:ir restriction (in range)") { ss::converter c; c.convert>("3"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert>("c,3"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert, char>("3,c"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); { auto tup = c.convert>("3"); REQUIRE(c.valid()); - CHECK(tup == 3); + CHECK_EQ(tup, 3); } { auto tup = c.convert>("2"); REQUIRE(c.valid()); - CHECK(tup == 2); + CHECK_EQ(tup, 2); } { auto tup = c.convert>("c,junk,1"); REQUIRE(c.valid()); - CHECK(tup == std::make_tuple('c', 1)); + CHECK_EQ(tup, std::make_tuple('c', 1)); } { auto tup = c.convert, char>("1,c"); REQUIRE(c.valid()); - CHECK(tup == std::make_tuple(1, 'c')); + CHECK_EQ(tup, std::make_tuple(1, 'c')); } } @@ -246,33 +246,33 @@ TEST_CASE("converter test ss:oor restriction (out of range)") { ss::converter c; c.convert>("3"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert>("2"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert, void>("c,1,junk"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert, char>("1,c"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); { auto tup = c.convert>("3"); REQUIRE(c.valid()); - CHECK(tup == 3); + CHECK_EQ(tup, 3); } { auto tup = c.convert>("c,junk,3"); REQUIRE(c.valid()); - CHECK(tup == std::make_tuple('c', 3)); + CHECK_EQ(tup, std::make_tuple('c', 3)); } { auto tup = c.convert, char>("3,c"); REQUIRE(c.valid()); - CHECK(tup == std::make_tuple(3, 'c')); + CHECK_EQ(tup, std::make_tuple(3, 'c')); } } @@ -293,34 +293,34 @@ TEST_CASE("converter test ss:ne restriction (not empty)") { ss::converter c; c.convert>(""); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert>("3,"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert, int>(",3"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert, int>("junk,,3"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert>>(""); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); { auto tup = c.convert>("s"); REQUIRE(c.valid()); - CHECK(tup == "s"); + CHECK_EQ(tup, "s"); } { auto tup = c.convert, ss::ne>("1,s"); REQUIRE(c.valid()); - CHECK(tup == std::make_tuple(1, "s")); + CHECK_EQ(tup, std::make_tuple(1, "s")); } { auto tup = c.convert>>("{1 2 3}"); REQUIRE(c.valid()); - CHECK(tup == extracted_vector); + CHECK_EQ(tup, extracted_vector); } } @@ -329,65 +329,65 @@ TEST_CASE( ss::converter c; c.convert>("3"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert>("3"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert>("3"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert>("3"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert>("3"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); c.convert>("3"); - REQUIRE(!c.valid()); + REQUIRE_FALSE(c.valid()); { auto tup = c.convert>("3"); REQUIRE(c.valid()); - CHECK(tup == 3); + CHECK_EQ(tup, 3); } { auto tup = c.convert>("3"); REQUIRE(c.valid()); - CHECK(tup == 3); + CHECK_EQ(tup, 3); } { auto tup = c.convert>("3"); REQUIRE(c.valid()); - CHECK(tup == 3); + CHECK_EQ(tup, 3); } { auto tup = c.convert>("3"); REQUIRE(c.valid()); - CHECK(tup == 3); + CHECK_EQ(tup, 3); } { auto tup = c.convert>("3"); REQUIRE(c.valid()); - CHECK(tup == 3); + CHECK_EQ(tup, 3); } { auto tup = c.convert>("3"); REQUIRE(c.valid()); - CHECK(tup == 3); + CHECK_EQ(tup, 3); } } TEST_CASE("converter test error mode") { ss::converter c; c.convert("junk"); - CHECK(!c.valid()); - CHECK(!c.error_msg().empty()); + CHECK_FALSE(c.valid()); + CHECK_FALSE(c.error_msg().empty()); } TEST_CASE("converter test converter with quotes spacing and escaping") { @@ -397,7 +397,7 @@ TEST_CASE("converter test converter with quotes spacing and escaping") { auto tup = c.convert( R"("just","some","strings")"); REQUIRE(c.valid()); - CHECK(tup == std::make_tuple("\"just\"", "\"some\"", "\"strings\"")); + CHECK_EQ(tup, std::make_tuple("\"just\"", "\"some\"", "\"strings\"")); } { @@ -406,7 +406,7 @@ TEST_CASE("converter test converter with quotes spacing and escaping") { auto tup = c.convert( buff(R"("just",some,"12.3","a")")); REQUIRE(c.valid()); - CHECK(tup == std::make_tuple("just", "some", 12.3, 'a')); + CHECK_EQ(tup, std::make_tuple("just", "some", 12.3, 'a')); } { @@ -415,7 +415,7 @@ TEST_CASE("converter test converter with quotes spacing and escaping") { auto tup = c.convert( buff(R"( just , some , 12.3 ,a )")); REQUIRE(c.valid()); - CHECK(tup == std::make_tuple("just", "some", 12.3, 'a')); + CHECK_EQ(tup, std::make_tuple("just", "some", 12.3, 'a')); } { @@ -424,7 +424,7 @@ TEST_CASE("converter test converter with quotes spacing and escaping") { auto tup = c.convert(buff(R"(ju\,st,strings)")); REQUIRE(c.valid()); - CHECK(tup == std::make_tuple("ju,st", "strings")); + CHECK_EQ(tup, std::make_tuple("ju,st", "strings")); } { @@ -433,7 +433,7 @@ TEST_CASE("converter test converter with quotes spacing and escaping") { auto tup = c.convert( buff(R"( ju\,st , "so,me" , 12.34 , "str""ings")")); REQUIRE(c.valid()); - CHECK(tup == std::make_tuple("ju,st", "so,me", 12.34, "str\"ings")); + CHECK_EQ(tup, std::make_tuple("ju,st", "so,me", 12.34, "str\"ings")); } } @@ -446,17 +446,44 @@ TEST_CASE("converter test invalid split conversions") { // mismatched quote auto tup = c.convert( buff(R"( "just , some , "12.3","a" )")); - CHECK(!c.valid()); - CHECK(!c.unterminated_quote()); - CHECK(!c.error_msg().empty()); + CHECK_FALSE(c.valid()); + CHECK_FALSE(c.unterminated_quote()); + CHECK_FALSE(c.error_msg().empty()); } { // unterminated quote auto tup = c.convert( buff(R"( ju\,st , "so,me" , 12.34 , "str""ings)")); - CHECK(!c.valid()); + CHECK_FALSE(c.valid()); CHECK(c.unterminated_quote()); - CHECK(!c.error_msg().empty()); + CHECK_FALSE(c.error_msg().empty()); + } + + { + // unterminated escape + auto tup = c.convert( + buff(R"(just,some,2,strings\)")); + CHECK_FALSE(c.valid()); + CHECK_FALSE(c.unterminated_quote()); + CHECK_FALSE(c.error_msg().empty()); + } + + { + // unterminated escape while quoting + auto tup = c.convert( + buff(R"(just,some,2,"strings\)")); + CHECK_FALSE(c.valid()); + CHECK_FALSE(c.unterminated_quote()); + CHECK_FALSE(c.error_msg().empty()); + } + + { + // unterminated escaped quote + auto tup = c.convert( + buff(R"(just,some,2,"strings\")")); + CHECK_FALSE(c.valid()); + CHECK(c.unterminated_quote()); + CHECK_FALSE(c.error_msg().empty()); } } diff --git a/test/test_extractions.cpp b/test/test_extractions.cpp index 118a1a7..cb092d0 100644 --- a/test/test_extractions.cpp +++ b/test/test_extractions.cpp @@ -8,7 +8,7 @@ std::string s = #input; \ auto t = ss::to_num(s.c_str(), s.c_str() + s.size()); \ REQUIRE(t.has_value()); \ - CHECK(std::abs(t.value() - type(input)) < eps); \ + CHECK_LT(std::abs(t.value() - type(input)), eps); \ } \ { \ /* check negative too */ \ @@ -16,7 +16,7 @@ auto s = std::string("-") + #input; \ auto t = ss::to_num(s.c_str(), s.c_str() + s.size()); \ REQUIRE(t.has_value()); \ - CHECK(std::abs(t.value() - type(-input)) < eps); \ + CHECK_LT(std::abs(t.value() - type(-input)), eps); \ } TEST_CASE("testing extract functions for floating point values") { @@ -41,7 +41,7 @@ TEST_CASE("testing extract functions for floating point values") { std::string s = #input; \ auto t = ss::to_num(s.c_str(), s.c_str() + s.size()); \ REQUIRE(t.has_value()); \ - CHECK(t.value() == type(input)); \ + CHECK_EQ(t.value(), type(input)); \ } \ { \ /* check negative too */ \ @@ -49,7 +49,7 @@ TEST_CASE("testing extract functions for floating point values") { auto s = std::string("-") + #input; \ auto t = ss::to_num(s.c_str(), s.c_str() + s.size()); \ REQUIRE(t.has_value()); \ - CHECK(t.value() == type(-input)); \ + CHECK_EQ(t.value(), type(-input)); \ } \ } @@ -74,7 +74,7 @@ TEST_CASE("extract test functions for decimal values") { { \ std::string s = input; \ auto t = ss::to_num(s.c_str(), s.c_str() + s.size()); \ - CHECK(!t.has_value()); \ + CHECK_FALSE(t.has_value()); \ } TEST_CASE("extract test functions for numbers with invalid inputs") { @@ -106,7 +106,7 @@ TEST_CASE("extract test functions for numbers with invalid inputs") { } \ } \ t = ss::to_num(s.c_str(), s.c_str() + s.size()); \ - CHECK(!t.has_value()); \ + CHECK_FALSE(t.has_value()); \ } \ { \ std::string s = std::to_string(std::numeric_limits::min()); \ @@ -122,7 +122,7 @@ TEST_CASE("extract test functions for numbers with invalid inputs") { } \ } \ t = ss::to_num(s.c_str(), s.c_str() + s.size()); \ - CHECK(!t.has_value()); \ + CHECK_FALSE(t.has_value()); \ } TEST_CASE("extract test functions for numbers with out of range inputs") { @@ -143,12 +143,12 @@ TEST_CASE("extract test functions for boolean values") { {false, "false"}}) { bool v; REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), v)); - CHECK(v == b); + CHECK_EQ(v, b); } for (const std::string& s : {"2", "tru", "truee", "xxx", ""}) { bool v; - REQUIRE(!ss::extract(s.c_str(), s.c_str() + s.size(), v)); + CHECK_FALSE(ss::extract(s.c_str(), s.c_str() + s.size(), v)); } } @@ -157,12 +157,12 @@ TEST_CASE("extract test functions for char values") { {std::pair{'a', "a"}, {'x', "x"}, {' ', " "}}) { char v; REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), v)); - CHECK(v == c); + CHECK_EQ(v, c); } for (const std::string& s : {"aa", "xxx", ""}) { char v; - REQUIRE(!ss::extract(s.c_str(), s.c_str() + s.size(), v)); + CHECK_FALSE(ss::extract(s.c_str(), s.c_str() + s.size(), v)); } } @@ -174,7 +174,7 @@ TEST_CASE("extract test functions for std::optional") { std::optional v; REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), v)); REQUIRE(v.has_value()); - CHECK(*v == i); + CHECK_EQ(*v, i); } for (const auto& [c, s] : @@ -184,19 +184,19 @@ TEST_CASE("extract test functions for std::optional") { std::optional v; REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), v)); REQUIRE(v.has_value()); - CHECK(*v == c); + CHECK_EQ(*v, c); } for (const std::string& s : {"aa", "xxx", ""}) { std::optional v; REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), v)); - REQUIRE(!v.has_value()); + CHECK_FALSE(v.has_value()); } for (const std::string& s : {"aa", "xxx", ""}) { std::optional v; REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), v)); - REQUIRE(!v.has_value()); + CHECK_FALSE(v.has_value()); } } @@ -204,7 +204,7 @@ TEST_CASE("extract test functions for std::optional") { { \ auto ptr = std::get_if(&var); \ REQUIRE(ptr); \ - REQUIRE(el == *ptr); \ + REQUIRE_EQ(el, *ptr); \ } #define CHECK_NOT_VARIANT(var, type) CHECK(!std::holds_alternative(var)); @@ -288,21 +288,21 @@ TEST_CASE("extract test functions for std::variant") { } { std::variant var; - REQUIRE(!ss::extract(s.c_str(), s.c_str() + s.size(), var)); + REQUIRE_FALSE(ss::extract(s.c_str(), s.c_str() + s.size(), var)); REQUIRE_VARIANT(var, int{}, int); CHECK_NOT_VARIANT(var, double); } { std::variant var; - REQUIRE(!ss::extract(s.c_str(), s.c_str() + s.size(), var)); + REQUIRE_FALSE(ss::extract(s.c_str(), s.c_str() + s.size(), var)); REQUIRE_VARIANT(var, double{}, double); CHECK_NOT_VARIANT(var, int); } { std::variant var; - REQUIRE(!ss::extract(s.c_str(), s.c_str() + s.size(), var)); + REQUIRE_FALSE(ss::extract(s.c_str(), s.c_str() + s.size(), var)); REQUIRE_VARIANT(var, int{}, int); } diff --git a/test/test_helpers.hpp b/test/test_helpers.hpp index a635bb8..03f2750 100644 --- a/test/test_helpers.hpp +++ b/test/test_helpers.hpp @@ -33,6 +33,11 @@ struct buffer { } } + char* append_overwrite_last(const char* data, size_t size) { + data_[strlen(data_) - size] = '\0'; + return append(data); + } + ~buffer() { if (data_) { delete[] data_; diff --git a/test/test_parser.cpp b/test/test_parser.cpp index 10355ec..af8b183 100644 --- a/test/test_parser.cpp +++ b/test/test_parser.cpp @@ -72,6 +72,7 @@ TEST_CASE("parser test various cases") { { ss::parser p{f.name, ","}; std::vector i; + std::vector expected = {std::begin(data) + 1, std::end(data)}; p.ignore_next(); while (!p.eof()) { @@ -80,7 +81,7 @@ TEST_CASE("parser test various cases") { i.emplace_back(ss::to_object(a)); } - CHECK_EQ(i, data); + CHECK_EQ(i, expected); } { @@ -129,10 +130,17 @@ TEST_CASE("parser test various cases") { i.push_back(a); } } - std::vector expected = data; - std::remove_if(expected.begin(), expected.end(), - [](const X& x) { return x.i == excluded; }); - CHECK_EQ(i, data); + + std::vector expected; + for (auto& x : data) { + if (x.i != excluded) { + expected.push_back(x); + } + } + + std::copy_if(data.begin(), data.end(), expected.begin(), + [](const X& x) { return x.i != excluded; }); + CHECK_EQ(i, expected); } { @@ -146,7 +154,7 @@ TEST_CASE("parser test various cases") { } } std::vector expected = {{3, 4, "y"}}; - CHECK_EQ(i, data); + CHECK_EQ(i, expected); } { @@ -203,7 +211,7 @@ TEST_CASE("parser test composite conversion") { p.try_next(fail) .or_else(fail) .or_else( - [](auto&& data) { CHECK(data == expectedData); }) + [](auto&& data) { CHECK_EQ(data, expectedData); }) .on_error(fail) .or_else(fail) .values(); @@ -222,7 +230,7 @@ TEST_CASE("parser test composite conversion") { auto [d1, d2, d3, d4] = p.try_next([](auto& i1, auto i2, double d) { - CHECK(std::tie(i1, i2, d) == expectedData); + CHECK_EQ(std::tie(i1, i2, d), expectedData); }) .on_error(fail) .or_object(fail) @@ -237,7 +245,7 @@ TEST_CASE("parser test composite conversion") { REQUIRE_FALSE(d2); REQUIRE_FALSE(d3); REQUIRE_FALSE(d4); - CHECK_EQ(*d1 ,expectedData); + CHECK_EQ(*d1, expectedData); } { @@ -265,7 +273,7 @@ TEST_CASE("parser test composite conversion") { auto [d1, d2] = p.try_next([](auto& i, auto& d) { - REQUIRE(std::tie(i, d) == std::tuple{10, 11.1}); + REQUIRE_EQ(std::tie(i, d), std::tuple{10, 11.1}); }) .or_else([](auto&, auto&) { FAIL(""); }) .values(); @@ -551,7 +559,7 @@ std::string no_escape(std::string& s) { return s; } -TEST_CASE("parser test csv on multiple lines with escapes xxx") { +TEST_CASE("parser test csv on multiple lines with escapes") { unique_file_name f; std::vector data = {{1, 2, "x\\\nx\\\nx"}, {5, 6, "z\\\nz\\\nz"}, {7, 8, "u"}, {3, 4, "y\\\ny\\\ny"}, @@ -571,7 +579,6 @@ TEST_CASE("parser test csv on multiple lines with escapes xxx") { while (!p.eof()) { auto a = p.get_next(); i.emplace_back(ss::to_object(a)); - // std::cout << i.back().s << std::endl; } CHECK_EQ(i, data); @@ -583,15 +590,15 @@ TEST_CASE("parser test csv on multiple lines with escapes xxx") { } } -TEST_CASE("parser test csv on multiple lines with quotes and escapes yyy") { +TEST_CASE("parser test csv on multiple lines with quotes and escapes") { unique_file_name f; { std::ofstream out{f.name}; - // out << "1,2,\"just\\\n\nstrings\"" << std::endl; - out << "3,4,\"j\\\ns\n\\\nx\\\ny\ng\"" << std::endl; - // out << "5,6,\"just\\\n\\\n\n\nstrings" << std::endl; - // out << "7,8,\"just strings\"" << std::endl; - // out << "9,10,just strings" << std::endl; + out << "1,2,\"just\\\n\nstrings\"" << std::endl; + out << "3,4,\"just\nsome\\\n\n\\\nstrings\"" << std::endl; + out << "5,6,\"just\\\n\\\n\n\nstrings" << std::endl; + out << "7,8,\"just strings\"" << std::endl; + out << "9,10,just strings" << std::endl; } ss::parser, ss::quote<'"'>> p{f.name}; @@ -611,28 +618,26 @@ TEST_CASE("parser test csv on multiple lines with quotes and escapes yyy") { CHECK_EQ(i, data); } -/* TEST_CASE("parser test multiline restricted") { unique_file_name f; { std::ofstream out{f.name}; - //out << "1,2,\"just\n\nstrings\"" << std::endl; - //out << "3,4,\"ju\n\n\nnk\"" << std::endl; - //out << "5,6,just\\\n\\\nstrings" << std::endl; - //out << "7,8,ju\\\n\\\n\\\nnk" << std::endl; - //out << "9,10,\"just\\\n\nstrings\"" << std::endl; + out << "1,2,\"just\n\nstrings\"" << std::endl; + out << "3,4,\"ju\n\n\nnk\"" << std::endl; + out << "5,6,just\\\n\\\nstrings" << std::endl; + out << "7,8,ju\\\n\\\n\\\nnk" << std::endl; + out << "9,10,\"just\\\n\nstrings\"" << std::endl; out << "11,12,\"ju\\\n|\n\n\n\n\nk\"" << std::endl; - // out << "13,14,\"ju\\\n\\\n15,16\"\\\n\\\\\n\nnk\"" << std::endl; - // out << "17,18,\"ju\\\n\\\n\\\n\\\\\n\nnk\"" << std::endl; + out << "13,14,\"ju\\\n\\\n15,16\"\\\n\\\\\n\nnk\"" << std::endl; + out << "17,18,\"ju\\\n\\\n\\\n\\\\\n\nnk\"" << std::endl; out << "19,20,just strings" << std::endl; } - ss::parser, ss::quote<'"'>, ss::escape<'\\'>> + ss::parser, ss::quote<'"'>, ss::escape<'\\'>> p{f.name, ","}; std::vector i; while (!p.eof()) { - std::cout << "==========================" << std::endl; auto a = p.get_next(); if (p.valid()) { i.emplace_back(ss::to_object(a)); @@ -643,6 +648,5 @@ TEST_CASE("parser test multiline restricted") { {5, 6, "just\n\nstrings"}, {9, 10, "just\n\nstrings"}, {19, 20, "just strings"}}; - CHECK(std::equal(i.begin(), i.end(), data.begin())); + CHECK_EQ(i, data); } -*/ diff --git a/test/test_splitter.cpp b/test/test_splitter.cpp index 3ec2cfe..14caded 100644 --- a/test/test_splitter.cpp +++ b/test/test_splitter.cpp @@ -193,7 +193,7 @@ void test_combinations(matches_type& matches, std::vector delims) { 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]); + CHECK_EQ(words(vec), expectations[i]); } } } @@ -506,25 +506,25 @@ TEST_CASE("splitter test error mode") { // empty delimiter ss::splitter s; s.split(buff("just,some,strings"), ""); - CHECK(!s.valid()); - CHECK(!s.unterminated_quote()); - CHECK(!s.error_msg().empty()); + CHECK_FALSE(s.valid()); + CHECK_FALSE(s.unterminated_quote()); + CHECK_FALSE(s.error_msg().empty()); } { // unterminated quote ss::splitter> s; s.split(buff("\"just")); - CHECK(!s.valid()); + CHECK_FALSE(s.valid()); CHECK(s.unterminated_quote()); - CHECK(!s.error_msg().empty()); + CHECK_FALSE(s.error_msg().empty()); } } template auto expect_unterminated_quote(Splitter& s, const std::string& line) { auto vec = s.split(buff(line.c_str())); - CHECK(!s.valid()); + CHECK_FALSE(s.valid()); CHECK(s.unterminated_quote()); return vec; } @@ -538,100 +538,135 @@ public: auto resplit(char* new_line, size_t new_line_size) { return splitter.resplit(new_line, new_line_size); } + + size_t size_shifted() { + return splitter.size_shifted(); + } }; } /* ss */ TEST_CASE("splitter test resplit unterminated quote") { + { - ss::converter> c; + ss::converter, ss::multiline, ss::escape<'\\'>> c; + auto& s = c.splitter; + auto vec = expect_unterminated_quote(s, R"("x)"); + CHECK_EQ(vec.size(), 1); + REQUIRE(s.unterminated_quote()); + + { + auto new_line = + buff.append_overwrite_last(R"(a\x)", c.size_shifted()); + + vec = c.resplit(new_line, strlen(new_line)); + CHECK(s.unterminated_quote()); + CHECK_EQ(vec.size(), 1); + } + + { + auto new_line = + buff.append_overwrite_last(R"(")", c.size_shifted()); + + vec = c.resplit(new_line, strlen(new_line)); + REQUIRE(s.valid()); + CHECK_FALSE(s.unterminated_quote()); + REQUIRE_EQ(vec.size(), 1); + CHECK_EQ(words(vec)[0], "xax"); + } + } + + { + ss::converter, ss::multiline> c; auto& s = c.splitter; auto vec = expect_unterminated_quote(s, "\"just"); - CHECK(vec.size() == 1); + CHECK_EQ(vec.size(), 1); auto new_line = buff.append(R"(",strings)"); vec = c.resplit(new_line, strlen(new_line)); CHECK(s.valid()); - CHECK(!s.unterminated_quote()); + CHECK_FALSE(s.unterminated_quote()); std::vector expected{"just", "strings"}; - CHECK(words(vec) == expected); + CHECK_EQ(words(vec), expected); } { - ss::converter> c; + ss::converter, ss::multiline> c; auto& s = c.splitter; auto vec = expect_unterminated_quote(s, "just,some,\"random"); std::vector expected{"just", "some", "just,some,\""}; - CHECK(words(vec) == expected); + CHECK_EQ(words(vec), expected); auto new_line = buff.append(R"(",strings)"); vec = c.resplit(new_line, strlen(new_line)); CHECK(s.valid()); - CHECK(!s.unterminated_quote()); + CHECK_FALSE(s.unterminated_quote()); expected = {"just", "some", "random", "strings"}; - CHECK(words(vec) == expected); + CHECK_EQ(words(vec), expected); } { - ss::converter> c; + ss::converter, ss::multiline> c; auto& s = c.splitter; auto vec = expect_unterminated_quote(s, R"("just","some","ran"")"); std::vector expected{"just", "some", R"("just","some",")"}; - CHECK(words(vec) == expected); + CHECK_EQ(words(vec), expected); - buff.data_[strlen(buff.data_) - c.splitter.escaped_] = '\0'; - auto new_line = buff.append(R"(,dom","strings")"); + auto new_line = + buff.append_overwrite_last(R"(,dom","strings")", c.size_shifted()); vec = c.resplit(new_line, strlen(new_line)); CHECK(s.valid()); - CHECK(!s.unterminated_quote()); + CHECK_FALSE(s.unterminated_quote()); expected = {"just", "some", "ran\",dom", "strings"}; - CHECK(words(vec) == expected); + CHECK_EQ(words(vec), expected); } { - ss::converter> c; + ss::converter, ss::multiline> c; auto& s = c.splitter; auto vec = expect_unterminated_quote(s, R"("just","some","ran)"); std::vector expected{"just", "some", R"("just","some",")"}; - CHECK(words(vec) == expected); + CHECK_EQ(words(vec), expected); + REQUIRE(s.unterminated_quote()); { auto new_line = buff.append(R"(,dom)"); vec = c.resplit(new_line, strlen(new_line)); - CHECK(!s.valid()); + CHECK_FALSE(s.valid()); CHECK(s.unterminated_quote()); - CHECK(words(vec) == expected); + CHECK_EQ(words(vec), expected); } { auto new_line = buff.append(R"(",strings)"); vec = c.resplit(new_line, strlen(new_line)); CHECK(s.valid()); - CHECK(!s.unterminated_quote()); + CHECK_FALSE(s.unterminated_quote()); expected = {"just", "some", "ran,dom", "strings"}; - CHECK(words(vec) == expected); + CHECK_EQ(words(vec), expected); } } { - ss::converter, ss::escape<'\\'>> c; + ss::converter, ss::escape<'\\'>, ss::multiline> c; auto& s = c.splitter; auto vec = expect_unterminated_quote(s, R"("just\"some","ra)"); std::vector expected{"just\"some"}; auto w = words(vec); w.pop_back(); - CHECK(w == expected); + CHECK_EQ(w, expected); + REQUIRE(s.unterminated_quote()); { auto new_line = buff.append(R"(n,dom",str\"ings)"); vec = c.resplit(new_line, strlen(new_line)); CHECK(s.valid()); - CHECK(!s.unterminated_quote()); + CHECK_FALSE(s.unterminated_quote()); expected = {"just\"some", "ran,dom", "str\"ings"}; - CHECK(words(vec) == expected); + CHECK_EQ(words(vec), expected); } } { - ss::converter, ss::escape<'\\'>> c; + ss::converter, ss::escape<'\\'>, ss::multiline> c; auto& s = c.splitter; auto vec = expect_unterminated_quote(s, "3,4," @@ -640,89 +675,97 @@ TEST_CASE("splitter test resplit unterminated quote") { std::vector expected{"3", "4"}; auto w = words(vec); w.pop_back(); - CHECK(w == expected); + CHECK_EQ(w, expected); + REQUIRE(s.unterminated_quote()); { - buff.data_[strlen(buff.data_) - c.splitter.escaped_] = '\0'; - auto new_line = buff.append("\nx5strings\""); + auto new_line = + buff.append_overwrite_last("\nx5strings\"", c.size_shifted()); vec = c.resplit(new_line, strlen(new_line)); CHECK(s.valid()); - CHECK(!s.unterminated_quote()); + CHECK_FALSE(s.unterminated_quote()); expected = {"3", "4", "just0\n1\n22\n33333x\n4\nx5strings"}; - CHECK(words(vec) == expected); + CHECK_EQ(words(vec), expected); } } { - ss::converter, ss::escape<'\\'>> c; + ss::converter, ss::escape<'\\'>, ss::multiline> c; auto& s = c.splitter; auto vec = expect_unterminated_quote(s, R"("just\"some","ra"")"); std::vector expected{"just\"some"}; auto w = words(vec); w.pop_back(); - CHECK(w == expected); + CHECK_EQ(w, expected); + REQUIRE(s.unterminated_quote()); { - buff.data_[strlen(buff.data_) - c.splitter.escaped_] = '\0'; - auto new_line = buff.append(R"(n,dom",str\"ings)"); + auto new_line = buff.append_overwrite_last(R"(n,dom",str\"ings)", + c.size_shifted()); vec = c.resplit(new_line, strlen(new_line)); CHECK(s.valid()); - CHECK(!s.unterminated_quote()); + CHECK_FALSE(s.unterminated_quote()); expected = {"just\"some", "ra\"n,dom", "str\"ings"}; - CHECK(words(vec) == expected); + CHECK_EQ(words(vec), expected); } } { - ss::converter, ss::escape<'\\'>> c; + ss::converter, ss::escape<'\\'>, ss::multiline> c; auto& s = c.splitter; auto vec = expect_unterminated_quote(s, R"("just\"some","r\a\a\\\a\")"); std::vector expected{"just\"some"}; auto w = words(vec); w.pop_back(); - CHECK(w == expected); + CHECK_EQ(w, expected); + REQUIRE(s.unterminated_quote()); { - buff.data_[strlen(buff.data_) - c.splitter.escaped_] = '\0'; - auto new_line = buff.append(R"(n,dom",str\"ings)"); + auto new_line = buff.append_overwrite_last(R"(n,dom",str\"ings)", + c.size_shifted()); vec = c.resplit(new_line, strlen(new_line)); CHECK(s.valid()); - CHECK(!s.unterminated_quote()); + CHECK_FALSE(s.unterminated_quote()); expected = {"just\"some", "raa\\a\"n,dom", "str\"ings"}; - CHECK(words(vec) == expected); + CHECK_EQ(words(vec), expected); } } { - ss::converter, ss::trim<' '>> c; + ss::converter, ss::trim<' '>, ss::multiline> c; auto& s = c.splitter; auto vec = expect_unterminated_quote(s, R"( "just" ,some, "ra )"); std::vector expected{"just", "some"}; auto w = words(vec); w.pop_back(); - CHECK(w == expected); + CHECK_EQ(w, expected); + REQUIRE(s.unterminated_quote()); { auto new_line = buff.append(R"( n,dom" , strings )"); vec = c.resplit(new_line, strlen(new_line)); CHECK(s.valid()); - CHECK(!s.unterminated_quote()); + CHECK_FALSE(s.unterminated_quote()); expected = {"just", "some", "ra n,dom", "strings"}; - CHECK(words(vec) == expected); + CHECK_EQ(words(vec), expected); } } { - ss::converter, ss::trim<' '>, ss::escape<'\\'>> c; + ss::converter, ss::trim<' '>, ss::escape<'\\'>, + ss::multiline> + c; auto& s = c.splitter; auto vec = expect_unterminated_quote(s, R"( "ju\"st" ,some, "ra \")"); std::vector expected{"ju\"st", "some"}; auto w = words(vec); w.pop_back(); - CHECK(w == expected); + CHECK_EQ(w, expected); + REQUIRE(s.unterminated_quote()); { - buff.data_[strlen(buff.data_) - c.splitter.escaped_] = '\0'; - auto new_line = buff.append(R"( n,dom" , strings )"); + auto new_line = + buff.append_overwrite_last(R"( n,dom" , strings )", + c.size_shifted()); vec = c.resplit(new_line, strlen(new_line)); CHECK(s.valid()); - CHECK(!s.unterminated_quote()); + CHECK_FALSE(s.unterminated_quote()); expected = {"ju\"st", "some", "ra \" n,dom", "strings"}; - CHECK(words(vec) == expected); + CHECK_EQ(words(vec), expected); } } } @@ -735,28 +778,40 @@ TEST_CASE("splitter test invalid splits") { // empty delimiter s.split(buff("some,random,strings"), ""); - CHECK(!s.valid()); - CHECK(!s.unterminated_quote()); - CHECK(!s.error_msg().empty()); + CHECK_FALSE(s.valid()); + CHECK_FALSE(s.unterminated_quote()); + CHECK_FALSE(s.error_msg().empty()); // mismatched delimiter s.split(buff(R"(some,"random,"strings")")); - CHECK(!s.valid()); - CHECK(!s.unterminated_quote()); - CHECK(!s.error_msg().empty()); + CHECK_FALSE(s.valid()); + CHECK_FALSE(s.unterminated_quote()); + CHECK_FALSE(s.error_msg().empty()); + + // unterminated escape + s.split(buff(R"(some,random,strings\)")); + CHECK_FALSE(s.valid()); + CHECK_FALSE(s.unterminated_quote()); + CHECK_FALSE(s.error_msg().empty()); + + // unterminated escape + s.split(buff(R"(some,random,"strings\)")); + CHECK_FALSE(s.valid()); + CHECK_FALSE(s.unterminated_quote()); + CHECK_FALSE(s.error_msg().empty()); // unterminated quote s.split(buff("some,random,\"strings")); - CHECK(!s.valid()); + CHECK_FALSE(s.valid()); CHECK(s.unterminated_quote()); - CHECK(!s.error_msg().empty()); + CHECK_FALSE(s.error_msg().empty()); // invalid resplit char new_line[] = "some"; auto a = c.resplit(new_line, strlen(new_line)); - CHECK(!s.valid()); - CHECK(!s.unterminated_quote()); - CHECK(!s.error_msg().empty()); + CHECK_FALSE(s.valid()); + CHECK_FALSE(s.unterminated_quote()); + CHECK_FALSE(s.error_msg().empty()); } TEST_CASE("splitter test with trim_left") {