diff --git a/include/ss/parser.hpp b/include/ss/parser.hpp index c19b834..50f61b0 100644 --- a/include/ss/parser.hpp +++ b/include/ss/parser.hpp @@ -106,6 +106,10 @@ public: : reader_.line_number_; } + size_t position() const { + return reader_.chars_read_; + } + template no_void_validator_tup_t get_next() { std::optional error; @@ -694,6 +698,7 @@ private: csv_data_size_{other.csv_data_size_}, curr_char_{other.curr_char_}, crlf_{other.crlf_}, line_number_{other.line_number_}, + chars_read_{other.chars_read_}, next_line_size_{other.next_line_size_} { other.buffer_ = nullptr; other.next_line_buffer_ = nullptr; @@ -718,12 +723,14 @@ private: curr_char_ = other.curr_char_; crlf_ = other.crlf_; line_number_ = other.line_number_; + chars_read_ = other.chars_read_; next_line_size_ = other.next_line_size_; other.buffer_ = nullptr; other.next_line_buffer_ = nullptr; other.helper_buffer_ = nullptr; other.file_ = nullptr; + other.csv_data_buffer_ = nullptr; } return *this; @@ -803,9 +810,11 @@ private: next_line_buffer_[0] = '\0'; } + chars_read_ = curr_char_; if (file_) { ssize = get_line_file(&next_line_buffer_, &next_line_buffer_size_, file_); + curr_char_ = ftell(file_); } else { ssize = get_line_buffer(&next_line_buffer_, &next_line_buffer_size_, @@ -1009,6 +1018,7 @@ private: bool crlf_{false}; size_t line_number_{0}; + size_t chars_read_{0}; size_t next_line_size_{0}; }; diff --git a/include/ss/setup.hpp b/include/ss/setup.hpp index 80cf4d5..2f298fc 100644 --- a/include/ss/setup.hpp +++ b/include/ss/setup.hpp @@ -165,25 +165,25 @@ using get_multiline_t = typename get_multiline::type; // string_error //////////////// -class string_error; +class string_error {}; //////////////// // ignore_header //////////////// -class ignore_header; +class ignore_header {}; //////////////// // ignore_empty //////////////// -class ignore_empty; +class ignore_empty {}; //////////////// // throw_on_error //////////////// -class throw_on_error; +class throw_on_error {}; //////////////// // setup implementation diff --git a/ssp.hpp b/ssp.hpp index c6e6620..4e7bd02 100644 --- a/ssp.hpp +++ b/ssp.hpp @@ -646,7 +646,7 @@ inline ssize_t get_line_file(char** lineptr, size_t* n, FILE* stream) { } #else -using ssize_t = int64_t; +using ssize_t = intptr_t; ssize_t get_line_file(char** lineptr, size_t* n, FILE* fp) { if (lineptr == nullptr || n == nullptr || fp == nullptr) { @@ -670,14 +670,15 @@ ssize_t get_line_file(char** lineptr, size_t* n, FILE* fp) { (*lineptr)[0] = '\0'; + size_t line_used = 0; while (fgets(buff, sizeof(buff), fp) != nullptr) { - size_t line_used = strlen(*lineptr); + line_used = strlen(*lineptr); size_t buff_used = strlen(buff); - if (*n < buff_used + line_used) { + if (*n <= buff_used + line_used) { size_t new_n = *n * 2; - auto new_lineptr = static_cast(realloc(*lineptr, *n)); + auto new_lineptr = static_cast(realloc(*lineptr, new_n)); if (new_lineptr == nullptr) { errno = ENOMEM; return -1; @@ -696,7 +697,7 @@ ssize_t get_line_file(char** lineptr, size_t* n, FILE* fp) { } } - return -1; + return (line_used != 0) ? line_used : -1; } #endif @@ -866,25 +867,25 @@ using get_multiline_t = typename get_multiline::type; // string_error //////////////// -class string_error; +class string_error {}; //////////////// // ignore_header //////////////// -class ignore_header; +class ignore_header {}; //////////////// // ignore_empty //////////////// -class ignore_empty; +class ignore_empty {}; //////////////// // throw_on_error //////////////// -class throw_on_error; +class throw_on_error {}; //////////////// // setup implementation @@ -2238,6 +2239,10 @@ public: : reader_.line_number_; } + size_t position() const { + return reader_.chars_read_; + } + template no_void_validator_tup_t get_next() { std::optional error; @@ -2826,6 +2831,7 @@ private: csv_data_size_{other.csv_data_size_}, curr_char_{other.curr_char_}, crlf_{other.crlf_}, line_number_{other.line_number_}, + chars_read_{other.chars_read_}, next_line_size_{other.next_line_size_} { other.buffer_ = nullptr; other.next_line_buffer_ = nullptr; @@ -2850,12 +2856,14 @@ private: curr_char_ = other.curr_char_; crlf_ = other.crlf_; line_number_ = other.line_number_; + chars_read_ = other.chars_read_; next_line_size_ = other.next_line_size_; other.buffer_ = nullptr; other.next_line_buffer_ = nullptr; other.helper_buffer_ = nullptr; other.file_ = nullptr; + other.csv_data_buffer_ = nullptr; } return *this; @@ -2876,50 +2884,52 @@ private: reader& operator=(const reader& other) = delete; ssize_t get_line_buffer(char** lineptr, size_t* n, - const char* const buffer, size_t csv_data_size, - size_t& curr_char) { - size_t pos; - int c; + const char* const csv_data_buffer, + size_t csv_data_size, size_t& curr_char) { + if (lineptr == nullptr || n == nullptr || + csv_data_buffer == nullptr) { + errno = EINVAL; + return -1; + } if (curr_char >= csv_data_size) { return -1; } - c = buffer[curr_char++]; - if (*lineptr == nullptr) { - *lineptr = - static_cast(malloc(get_line_initial_buffer_size)); - if (*lineptr == nullptr) { + if (*lineptr == nullptr || *n < get_line_initial_buffer_size) { + auto new_lineptr = static_cast( + realloc(*lineptr, get_line_initial_buffer_size)); + if (new_lineptr == nullptr) { return -1; } - *n = 128; + *lineptr = new_lineptr; + *n = get_line_initial_buffer_size; } - pos = 0; + size_t line_used = 0; while (curr_char <= csv_data_size) { - if (pos + 1 >= *n) { - size_t new_size = *n + (*n >> 2); - if (new_size < get_line_initial_buffer_size) { - new_size = get_line_initial_buffer_size; - } - char* new_ptr = static_cast( - realloc(static_cast(*lineptr), new_size)); - if (new_ptr == nullptr) { + if (line_used + 1 >= *n) { + size_t new_n = *n * 2; + + char* new_lineptr = + static_cast(realloc(*lineptr, new_n)); + if (new_lineptr == nullptr) { + errno = ENOMEM; return -1; } - *n = new_size; - *lineptr = new_ptr; + *n = new_n; + *lineptr = new_lineptr; } - (*lineptr)[pos++] = c; + auto c = csv_data_buffer[curr_char++]; + (*lineptr)[line_used++] = c; if (c == '\n') { - break; + (*lineptr)[line_used] = '\0'; + return line_used; } - c = buffer[curr_char++]; } - (*lineptr)[pos] = '\0'; - return pos; + return (line_used != 0) ? line_used : -1; } // read next line each time in order to set eof_ @@ -2933,9 +2943,11 @@ private: next_line_buffer_[0] = '\0'; } + chars_read_ = curr_char_; if (file_) { ssize = get_line_file(&next_line_buffer_, &next_line_buffer_size_, file_); + curr_char_ = ftell(file_); } else { ssize = get_line_buffer(&next_line_buffer_, &next_line_buffer_size_, @@ -3139,6 +3151,7 @@ private: bool crlf_{false}; size_t line_number_{0}; + size_t chars_read_{0}; size_t next_line_size_{0}; }; diff --git a/test/test_helpers.hpp b/test/test_helpers.hpp index 8df69d9..89477c8 100644 --- a/test/test_helpers.hpp +++ b/test/test_helpers.hpp @@ -1,12 +1,14 @@ #pragma once +#include #include #include +#include #include +#include +#include #include #include #include -#include -#include #ifdef CMAKE_GITHUB_CI #include @@ -20,6 +22,24 @@ class parser; } /* ss */ namespace { + +struct bool_error {}; + +template +struct config { + using BufferMode = T; + using ErrorMode = U; + + constexpr static auto ThrowOnError = std::is_same_v; + constexpr static auto StringError = std::is_same_v; +}; + +#define ParserOptionCombinations \ + config, config, \ + config, config, \ + config, \ + config + struct buffer { std::string data_; @@ -172,23 +192,32 @@ template } template -[[maybe_unused]] std::tuple, std::string> make_parser( - const std::string& file_name, const std::string& delim = "") { +std::tuple, std::string> make_parser_impl( + const std::string& file_name, std::string delim = ss::default_delimiter) { if (buffer_mode) { auto buffer = make_buffer(file_name); - if (delim.empty()) { - return {ss::parser{buffer.data(), buffer.size()}, - std::move(buffer)}; - } else { - return {ss::parser{buffer.data(), buffer.size(), delim}, - std::move(buffer)}; - } + return {ss::parser{buffer.data(), buffer.size(), delim}, + std::move(buffer)}; } else { - if (delim.empty()) { - return {ss::parser{file_name}, std::string{}}; - } else { - return {ss::parser{file_name, delim}, std::string{}}; - } + return {ss::parser{file_name, delim}, std::string{}}; } } + +template +[[maybe_unused]] std::enable_if_t< + !std::is_same_v, + std::tuple, std::string>> +make_parser(const std::string& file_name, + std::string delim = ss::default_delimiter) { + return make_parser_impl(file_name, delim); +} + +template +[[maybe_unused]] std::enable_if_t, + std::tuple, std::string>> +make_parser(const std::string& file_name, + std::string delim = ss::default_delimiter) { + return make_parser_impl(file_name, delim); +} + } /* namespace */ diff --git a/test/test_parser1_1.cpp b/test/test_parser1_1.cpp index 401d171..681877f 100644 --- a/test/test_parser1_1.cpp +++ b/test/test_parser1_1.cpp @@ -1,7 +1,7 @@ #include "test_parser1.hpp" TEST_CASE("test file not found") { - unique_file_name f{"test_parser"}; + unique_file_name f{"file_not_found"}; { ss::parser p{f.name, ","}; @@ -11,6 +11,7 @@ TEST_CASE("test file not found") { { ss::parser p{f.name, ","}; CHECK_FALSE(p.valid()); + CHECK_FALSE(p.error_msg().empty()); } try { @@ -30,6 +31,7 @@ TEST_CASE("test null buffer") { { ss::parser p{nullptr, 10, ","}; CHECK_FALSE(p.valid()); + CHECK_FALSE(p.error_msg().empty()); } try { @@ -40,20 +42,105 @@ TEST_CASE("test null buffer") { } } -template -void test_various_cases() { +struct Y { + constexpr static auto delim = ","; + std::string s1; + std::string s2; + std::string s3; + + std::string to_string() const { + return std::string{} + .append(s1) + .append(delim) + .append(s2) + .append(delim) + .append(s3); + } + + auto tied() const { + return std::tie(s1, s2, s3); + } +}; + +TEST_CASE_TEMPLATE("test position method", T, ParserOptionCombinations) { + constexpr auto buffer_mode = T::BufferMode::value; + using ErrorMode = typename T::ErrorMode; + + unique_file_name f{"position_method"}; + std::vector data = {{"1", "21", "x"}, {"321", "4", "y"}, + {"54", "6", "zz"}, {"7", "876", "uuuu"}, + {"910", "10", "v"}, {"10", "321", "ww"}}; + make_and_write(f.name, data); + + auto [p, buff] = make_parser(f.name); + auto data_at = [&buff = buff, &f = f](auto n) { + if (!buff.empty()) { + return buff[n]; + } else { + auto file = fopen(f.name.c_str(), "r"); + fseek(file, n, SEEK_SET); + return static_cast(fgetc(file)); + } + }; + + while (!p.eof()) { + auto curr_char = p.position(); + const auto& [s1, s2, s3] = + p.template get_next(); + + auto s = s1 + "," + s2 + "," + s3; + + for (size_t i = 0; i < s1.size(); ++i) { + CHECK_EQ(data_at(curr_char + i), s[i]); + } + + auto last_char = data_at(curr_char + s.size()); + CHECK((last_char == '\n' || last_char == '\r')); + } +} + +// TODO uncomment +/* +TEST_CASE_TEMPLATE("test line method", BufferMode, std::true_type, + std::false_type) { unique_file_name f{"test_parser"}; + std::vector data = {{"1", "21", "x"}, {"321", "4", "y"}, + {"54", "6", "zz"}, {"7", "876", "uuuu"}, + {"910", "10", "v"}, {"10", "321", "ww"}}; + make_and_write(f.name, data); + + auto [p, buff] = make_parser(f.name); + + auto expected_line = 0; + CHECK_EQ(p.line(), expected_line); + + while (!p.eof()) { + auto _ = p.template get_next(); + ++expected_line; + CHECK_EQ(p.line(), expected_line); + } + + CHECK_EQ(p.line(), data.size()); +} +*/ + +TEST_CASE_TEMPLATE("parser test various valid cases", T, + ParserOptionCombinations) { + constexpr auto buffer_mode = T::BufferMode::value; + using ErrorMode = typename T::ErrorMode; + + unique_file_name f{"various_valid_cases"}; std::vector data = {{1, 2, "x"}, {3, 4, "y"}, {5, 6, "z"}, {7, 8, "u"}, {9, 10, "v"}, {11, 12, "w"}}; make_and_write(f.name, data); auto csv_data_buffer = make_buffer(f.name); { - auto [p, _] = make_parser(f.name, ","); + auto [p, _] = make_parser(f.name, ","); ss::parser p0{std::move(p)}; p = std::move(p0); std::vector i; - auto [p2, __] = make_parser(f.name, ","); + auto [p2, __] = make_parser(f.name, ","); std::vector i2; auto move_rotate = [&p = p, &p0 = p0] { @@ -77,13 +164,13 @@ void test_various_cases() { } { - auto [p, _] = make_parser(f.name, ","); + auto [p, _] = make_parser(f.name, ","); std::vector i; - auto [p2, __] = make_parser(f.name, ","); + auto [p2, __] = make_parser(f.name, ","); std::vector i2; - auto [p3, ___] = make_parser(f.name, ","); + auto [p3, ___] = make_parser(f.name, ","); std::vector i3; std::vector expected = {std::begin(data) + 1, std::end(data)}; @@ -112,9 +199,9 @@ void test_various_cases() { } { - auto [p, _] = make_parser(f.name, ","); + auto [p, _] = make_parser(f.name, ","); std::vector i; - auto [p2, __] = make_parser(f.name, ","); + auto [p2, __] = make_parser(f.name, ","); std::vector i2; while (!p.eof()) { @@ -131,7 +218,7 @@ void test_various_cases() { } { - auto [p, _] = make_parser(f.name, ","); + auto [p, _] = make_parser(f.name, ","); std::vector i; for (auto&& a : @@ -143,10 +230,10 @@ void test_various_cases() { } { - auto [p, _] = make_parser(f.name, ","); + auto [p, _] = make_parser(f.name, ","); std::vector i; - auto [p2, __] = make_parser(f.name, ","); + auto [p2, __] = make_parser(f.name, ","); std::vector i2; using tup = std::tuple; @@ -164,7 +251,7 @@ void test_various_cases() { } { - auto [p, _] = make_parser(f.name, ","); + auto [p, _] = make_parser(f.name, ","); std::vector i; using tup = std::tuple; @@ -176,7 +263,7 @@ void test_various_cases() { } { - auto [p, _] = make_parser(f.name, ","); + auto [p, _] = make_parser(f.name, ","); std::vector i; while (!p.eof()) { @@ -187,7 +274,7 @@ void test_various_cases() { } { - auto [p, _] = make_parser(f.name, ","); + auto [p, _] = make_parser(f.name, ","); std::vector i; for (auto&& a : p.template iterate()) { @@ -199,10 +286,10 @@ void test_various_cases() { { constexpr int excluded = 3; - auto [p, _] = make_parser(f.name, ","); + auto [p, _] = make_parser(f.name, ","); std::vector i; - auto [p2, __] = make_parser(f.name, ","); + auto [p2, __] = make_parser(f.name, ","); std::vector i2; while (!p.eof()) { @@ -217,7 +304,7 @@ void test_various_cases() { }; } - if (!ss::setup::throw_on_error) { + if (!T::ThrowOnError) { for (auto&& a : p2.template iterate_object, double, std::string>()) { if (p2.valid()) { @@ -237,16 +324,16 @@ void test_various_cases() { [&](const X& x) { return x.i != excluded; }); CHECK_EQ(i, expected); - if (!ss::setup::throw_on_error) { + if (!T::ThrowOnError) { CHECK_EQ(i2, expected); } } { - auto [p, _] = make_parser(f.name, ","); + auto [p, _] = make_parser(f.name, ","); std::vector i; - auto [p2, __] = make_parser(f.name, ","); + auto [p2, __] = make_parser(f.name, ","); std::vector i2; while (!p.eof()) { @@ -261,7 +348,7 @@ void test_various_cases() { } } - if (!ss::setup::throw_on_error) { + if (!T::ThrowOnError) { for (auto&& a : p2.template iterate_object, double, std::string>()) { if (p2.valid()) { @@ -272,21 +359,21 @@ void test_various_cases() { std::vector expected = {{3, 4, "y"}}; CHECK_EQ(i, expected); - if (!ss::setup::throw_on_error) { + if (!T::ThrowOnError) { CHECK_EQ(i2, expected); } } { - unique_file_name empty_f{"test_parser"}; + unique_file_name empty_f{"various_valid_cases"}; std::vector empty_data = {}; make_and_write(empty_f.name, empty_data); - auto [p, _] = make_parser(empty_f.name, ","); + auto [p, _] = make_parser(empty_f.name, ","); std::vector i; - auto [p2, __] = make_parser(empty_f.name, ","); + auto [p2, __] = make_parser(empty_f.name, ","); std::vector i2; while (!p.eof()) { @@ -302,15 +389,6 @@ void test_various_cases() { } } -TEST_CASE("parser test various cases") { - test_various_cases(); - test_various_cases(); - test_various_cases(); - test_various_cases(); - test_various_cases(); - test_various_cases(); -} - using test_tuple = std::tuple; struct test_struct { int i; @@ -324,9 +402,10 @@ struct test_struct { static inline void expect_test_struct(const test_struct&) { } -template -void test_composite_conversion() { - unique_file_name f{"test_parser"}; +TEST_CASE_TEMPLATE("parser test composite conversion", BufferMode, + std::true_type, std::false_type) { + constexpr auto buffer_mode = BufferMode::value; + unique_file_name f{"composite_conversion"}; { std::ofstream out{f.name}; for (auto& i : @@ -336,7 +415,7 @@ void test_composite_conversion() { } } - auto [p, _] = make_parser(f.name, ","); + auto [p, _] = make_parser(f.name, ","); auto fail = [] { FAIL(""); }; auto expect_error = [](auto error) { CHECK(!error.empty()); }; auto ignore_error = [] {}; @@ -546,18 +625,12 @@ void test_composite_conversion() { CHECK(p.eof()); } -// various scenarios -TEST_CASE("parser test composite conversion") { - test_composite_conversion(); - test_composite_conversion(); -} - -template +template void test_no_new_line_at_eof_impl(const std::vector& data) { - unique_file_name f{"test_parser"}; + unique_file_name f{"no_new_line_at_eof"}; make_and_write(f.name, data, {}, false); - auto [p, _] = make_parser(f.name); + auto [p, _] = make_parser(f.name); std::vector parsed_data; for (const auto& el : p.template iterate()) { @@ -567,32 +640,36 @@ void test_no_new_line_at_eof_impl(const std::vector& data) { CHECK_EQ(data, parsed_data); } -template +template void test_no_new_line_at_eof() { - test_no_new_line_at_eof_impl({}); - test_no_new_line_at_eof_impl({{1, 2, "X"}}); - test_no_new_line_at_eof_impl({{1, 2, "X"}, {}}); - test_no_new_line_at_eof_impl({{1, 2, "X"}, {3, 4, "YY"}}); - test_no_new_line_at_eof_impl({{1, 2, "X"}, {3, 4, "YY"}, {}}); - test_no_new_line_at_eof_impl( + test_no_new_line_at_eof_impl({}); + test_no_new_line_at_eof_impl({{1, 2, "X"}}); + test_no_new_line_at_eof_impl({{1, 2, "X"}, {}}); + test_no_new_line_at_eof_impl( + {{1, 2, "X"}, {3, 4, "YY"}}); + test_no_new_line_at_eof_impl( + {{1, 2, "X"}, {3, 4, "YY"}, {}}); + test_no_new_line_at_eof_impl( {{1, 2, "X"}, {3, 4, "YY"}, {5, 6, "ZZZ"}, {7, 8, "UUU"}}); for (size_t i = 0; i < 2 * ss::get_line_initial_buffer_size; ++i) { - test_no_new_line_at_eof_impl( + test_no_new_line_at_eof_impl( {{1, 2, std::string(i, 'X')}}); for (size_t j = 0; j < 2 * ss::get_line_initial_buffer_size; j += 13) { - test_no_new_line_at_eof_impl( + test_no_new_line_at_eof_impl( {{1, 2, std::string(i, 'X')}, {3, 4, std::string(j, 'Y')}}); - test_no_new_line_at_eof_impl( + test_no_new_line_at_eof_impl( {{1, 2, std::string(j, 'X')}, {3, 4, std::string(i, 'Y')}}); } } } -TEST_CASE("test no new line at end of data") { - test_no_new_line_at_eof(); - test_no_new_line_at_eof(); +TEST_CASE_TEMPLATE("test no new line at end of data", T, + ParserOptionCombinations) { + constexpr auto buffer_mode = T::BufferMode::value; + using ErrorMode = typename T::ErrorMode; + test_no_new_line_at_eof(); } diff --git a/test/test_parser1_2.cpp b/test/test_parser1_2.cpp index 6ca7a0b..eaf9516 100644 --- a/test/test_parser1_2.cpp +++ b/test/test_parser1_2.cpp @@ -41,11 +41,16 @@ struct xyz { } }; -template -void test_moving_of_parsed_composite_values() { +TEST_CASE_TEMPLATE("test moving of parsed composite values", T, + config, config, + config, + config) { + constexpr auto buffer_mode = T::BufferMode::value; + using ErrorMode = typename T::ErrorMode; + // to compile is enough return; - auto [p, _] = make_parser("", ""); + auto [p, _] = make_parser("", ""); p.template try_next() .template or_else( [](auto&&) {}) @@ -56,70 +61,41 @@ void test_moving_of_parsed_composite_values() { [](auto&, auto&, auto&) {}); } -TEST_CASE("parser test the moving of parsed composite values") { - test_moving_of_parsed_composite_values(); - test_moving_of_parsed_composite_values(); - test_moving_of_parsed_composite_values(); - test_moving_of_parsed_composite_values(); -} - -TEST_CASE("parser test error mode") { - unique_file_name f{"test_parser"}; +TEST_CASE_TEMPLATE("parser test string error mode", BufferMode, std::true_type, + std::false_type) { + unique_file_name f{"string_error"}; { std::ofstream out{f.name}; out << "junk" << std::endl; out << "junk" << std::endl; } - { - auto [p, _] = make_parser(f.name, ","); + auto [p, _] = make_parser(f.name, ","); - REQUIRE_FALSE(p.eof()); - p.get_next(); - CHECK_FALSE(p.valid()); - CHECK_FALSE(p.error_msg().empty()); - } - - { - auto [p, _] = make_parser(f.name, ","); - - REQUIRE_FALSE(p.eof()); - p.get_next(); - CHECK_FALSE(p.valid()); - CHECK_FALSE(p.error_msg().empty()); - } + REQUIRE_FALSE(p.eof()); + p.template get_next(); + CHECK_FALSE(p.valid()); + CHECK_FALSE(p.error_msg().empty()); } -TEST_CASE("parser throw on error mode") { - unique_file_name f{"test_parser"}; +TEST_CASE_TEMPLATE("parser throw on error mode", BufferMode, std::true_type, + std::false_type) { + unique_file_name f{"throw_on_error"}; { std::ofstream out{f.name}; out << "junk" << std::endl; out << "junk" << std::endl; } - { - auto [p, _] = make_parser(f.name, ","); + auto [p, _] = + make_parser(f.name, ","); - REQUIRE_FALSE(p.eof()); - try { - p.get_next(); - FAIL("Expected exception..."); - } catch (const std::exception& e) { - CHECK_FALSE(std::string{e.what()}.empty()); - } - } - - { - auto [p, _] = make_parser(f.name, ","); - - REQUIRE_FALSE(p.eof()); - try { - p.get_next(); - FAIL("Expected exception..."); - } catch (const std::exception& e) { - CHECK_FALSE(std::string{e.what()}.empty()); - } + REQUIRE_FALSE(p.eof()); + try { + p.template get_next(); + FAIL("Expected exception..."); + } catch (const std::exception& e) { + CHECK_FALSE(std::string{e.what()}.empty()); } } @@ -130,9 +106,11 @@ static inline std::string no_quote(const std::string& s) { return s; } -template -void test_quote_multiline() { - unique_file_name f{"test_parser"}; +TEST_CASE_TEMPLATE("test quote multiline", T, ParserOptionCombinations) { + constexpr auto buffer_mode = T::BufferMode::value; + using ErrorMode = typename T::ErrorMode; + + unique_file_name f{"quote_multiline"}; std::vector data = {{1, 2, "\"x\r\nx\nx\""}, {3, 4, "\"y\ny\r\ny\""}, {5, 6, "\"z\nz\""}, @@ -151,9 +129,8 @@ void test_quote_multiline() { } } - auto [p, _] = - make_parser, Ts...>(f.name, - ","); + auto [p, _] = make_parser>(f.name, ","); std::vector i; @@ -168,7 +145,7 @@ void test_quote_multiline() { CHECK_EQ(i, data); auto [p_no_multiline, __] = - make_parser, Ts...>(f.name, ","); + make_parser>(f.name, ","); while (!p.eof()) { auto command = [&p_no_multiline = p_no_multiline] { p_no_multiline.template get_next(); @@ -177,23 +154,16 @@ void test_quote_multiline() { } } -TEST_CASE("parser test csv on multiple lines with quotes") { - test_quote_multiline(); - test_quote_multiline(); - test_quote_multiline(); - test_quote_multiline(); - test_quote_multiline(); - test_quote_multiline(); -} - static inline std::string no_escape(std::string& s) { s.erase(std::remove(begin(s), end(s), '\\'), end(s)); return s; } -template -void test_escape_multiline() { - unique_file_name f{"test_parser"}; +TEST_CASE_TEMPLATE("test escape multiline", T, ParserOptionCombinations) { + constexpr auto buffer_mode = T::BufferMode::value; + using ErrorMode = typename T::ErrorMode; + + unique_file_name f{"escape_multiline"}; std::vector data = {{1, 2, "x\\\nx\\\r\nx"}, {5, 6, "z\\\nz\\\nz"}, {7, 8, "u"}, @@ -212,9 +182,8 @@ void test_escape_multiline() { } } - auto [p, _] = - make_parser, Ts...>(f.name, - ","); + auto [p, _] = make_parser>(f.name, ","); std::vector i; while (!p.eof()) { @@ -228,7 +197,7 @@ void test_escape_multiline() { CHECK_EQ(i, data); auto [p_no_multiline, __] = - make_parser, Ts...>(f.name, ","); + make_parser>(f.name, ","); while (!p.eof()) { auto command = [&p_no_multiline = p_no_multiline] { auto a = @@ -238,18 +207,11 @@ void test_escape_multiline() { } } -TEST_CASE("parser test csv on multiple lines with escapes") { - test_escape_multiline(); - test_escape_multiline(); - test_escape_multiline(); - test_escape_multiline(); - test_escape_multiline(); - test_escape_multiline(); -} +TEST_CASE_TEMPLATE("test quote escape multiline", T, ParserOptionCombinations) { + constexpr auto buffer_mode = T::BufferMode::value; + using ErrorMode = typename T::ErrorMode; -template -void test_quote_escape_multiline() { - unique_file_name f{"test_parser"}; + unique_file_name f{"quote_escape_multiline"}; { std::ofstream out{f.name}; out << "1,2,\"just\\\n\nstrings\"" << std::endl; @@ -266,8 +228,8 @@ void test_quote_escape_multiline() { size_t bad_lines = 1; auto num_errors = 0; - auto [p, _] = make_parser, - ss::quote<'"'>, Ts...>(f.name); + auto [p, _] = make_parser, ss::quote<'"'>>(f.name); std::vector i; while (!p.eof()) { @@ -298,12 +260,3 @@ void test_quote_escape_multiline() { } CHECK_EQ(i, data); } - -TEST_CASE("parser test csv on multiple lines with quotes and escapes") { - test_quote_escape_multiline(); - test_quote_escape_multiline(); - test_quote_escape_multiline(); - test_quote_escape_multiline(); - test_quote_escape_multiline(); - test_quote_escape_multiline(); -} diff --git a/test/test_parser1_3.cpp b/test/test_parser1_3.cpp index 612d17d..07147b6 100644 --- a/test/test_parser1_3.cpp +++ b/test/test_parser1_3.cpp @@ -1,8 +1,10 @@ #include "test_parser1.hpp" -template -void test_multiline_restricted() { - unique_file_name f{"test_parser"}; +TEST_CASE_TEMPLATE("test multiline restricted", T, ParserOptionCombinations) { + constexpr auto buffer_mode = T::BufferMode::value; + using ErrorMode = typename T::ErrorMode; + + unique_file_name f{"multiline_restricted"}; { std::ofstream out{f.name}; out << "1,2,\"just\n\nstrings\"" << std::endl; @@ -24,8 +26,8 @@ void test_multiline_restricted() { auto num_errors = 0; auto [p, _] = - make_parser, ss::quote<'"'>, - ss::escape<'\\'>, Ts...>(f.name, ","); + make_parser, + ss::quote<'"'>, ss::escape<'\\'>>(f.name, ","); std::vector i; while (!p.eof()) { @@ -63,26 +65,20 @@ void test_multiline_restricted() { CHECK_EQ(i, data); } -TEST_CASE("parser test multiline restricted") { - test_multiline_restricted(); - test_multiline_restricted(); - test_multiline_restricted(); - test_multiline_restricted(); - test_multiline_restricted(); - test_multiline_restricted(); -} +template +void test_unterminated_line(const std::vector& lines, + size_t bad_line) { + constexpr auto buffer_mode = T::BufferMode::value; + using ErrorMode = typename T::ErrorMode; -template -void test_unterminated_line_impl(const std::vector& lines, - size_t bad_line) { - unique_file_name f{"test_parser"}; + unique_file_name f{"unterminated_line"}; std::ofstream out{f.name}; for (const auto& line : lines) { out << line << std::endl; } out.close(); - auto [p, _] = make_parser(f.name); + auto [p, _] = make_parser(f.name); size_t line = 0; while (!p.eof()) { auto command = [&p = p] { @@ -100,21 +96,8 @@ void test_unterminated_line_impl(const std::vector& lines, } } -template -void test_unterminated_line(const std::vector& lines, - size_t bad_line) { - test_unterminated_line_impl(lines, bad_line); - test_unterminated_line_impl(lines, - bad_line); - test_unterminated_line_impl(lines, - bad_line); - test_unterminated_line_impl(lines, bad_line); - test_unterminated_line_impl(lines, bad_line); - test_unterminated_line_impl(lines, - bad_line); -} - -TEST_CASE("parser test csv on multiline with errors") { +TEST_CASE_TEMPLATE("parser test csv on multiline with errors", T, + ParserOptionCombinations) { using multiline = ss::multiline_restricted<3>; using escape = ss::escape<'\\'>; using quote = ss::quote<'"'>; @@ -122,209 +105,209 @@ TEST_CASE("parser test csv on multiline with errors") { // unterminated escape { const std::vector lines{"1,2,just\\"}; - test_unterminated_line(lines, 0); - test_unterminated_line(lines, 0); + test_unterminated_line(lines, 0); + test_unterminated_line(lines, 0); } { const std::vector lines{"1,2,just\\", "9,8,second"}; - test_unterminated_line(lines, 0); - test_unterminated_line(lines, 0); + test_unterminated_line(lines, 0); + test_unterminated_line(lines, 0); } { const std::vector lines{"9,8,first", "1,2,just\\"}; - test_unterminated_line(lines, 1); - test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); } { const std::vector lines{"9,8,first", "1,2,just\\", "3,4,third"}; - test_unterminated_line(lines, 1); - test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); } { const std::vector lines{"9,8,first", "1,2,just\\\nstrings\\", "3,4,th\\\nird"}; - test_unterminated_line(lines, 1); - test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); } { const std::vector lines{"9,8,first", "3,4,second", "1,2,just\\"}; - test_unterminated_line(lines, 2); - test_unterminated_line(lines, 2); + test_unterminated_line(lines, 2); + test_unterminated_line(lines, 2); } { const std::vector lines{"9,8,\\first", "3,4,second", "1,2,jus\\t\\"}; - test_unterminated_line(lines, 2); - test_unterminated_line(lines, 2); + test_unterminated_line(lines, 2); + test_unterminated_line(lines, 2); } // unterminated quote { const std::vector lines{"1,2,\"just"}; - test_unterminated_line(lines, 0); - test_unterminated_line(lines, 0); + test_unterminated_line(lines, 0); + test_unterminated_line(lines, 0); } { const std::vector lines{"1,2,\"just", "9,8,second"}; - test_unterminated_line(lines, 0); - test_unterminated_line(lines, 0); + test_unterminated_line(lines, 0); + test_unterminated_line(lines, 0); } { const std::vector lines{"9,8,first", "1,2,\"just"}; - test_unterminated_line(lines, 1); - test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); } { const std::vector lines{"9,8,first", "1,2,\"just", "3,4,th\\,ird"}; - test_unterminated_line(lines, 1); - test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); } { const std::vector lines{"9,8,first", "3,4,second", "1,2,\"just"}; - test_unterminated_line(lines, 2); - test_unterminated_line(lines, 2); + test_unterminated_line(lines, 2); + test_unterminated_line(lines, 2); } { const std::vector lines{"9,8,\"first\"", "\"3\",4,\"sec,ond\"", "1,2,\"ju\"\"st"}; - test_unterminated_line(lines, 2); - test_unterminated_line(lines, 2); + test_unterminated_line(lines, 2); + test_unterminated_line(lines, 2); } // unterminated quote and escape { const std::vector lines{"1,2,\"just\\"}; - test_unterminated_line(lines, 0); - test_unterminated_line(lines, 0); + test_unterminated_line(lines, 0); + test_unterminated_line(lines, 0); } { const std::vector lines{"1,2,\"just\\\n\\"}; - test_unterminated_line(lines, 0); + test_unterminated_line(lines, 0); } { const std::vector lines{"1,2,\"just\n\\"}; - test_unterminated_line(lines, 0); + test_unterminated_line(lines, 0); } { const std::vector lines{"9,8,first", "1,2,\"just\n\\"}; - test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); } { const std::vector lines{"9,8,first", "1,2,\"just\n\\", "4,3,thrid"}; - test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); } { const std::vector lines{"9,8,f\\\nirst", "1,2,\"just\n\\", "4,3,thrid"}; - test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); } { const std::vector lines{"9,8,\"f\ni\nrst\"", "1,2,\"just\n\\", "4,3,thrid"}; - test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); } // multiline limmit reached escape { const std::vector lines{"1,2,\\\n\\\n\\\n\\\njust"}; - test_unterminated_line(lines, 0); - test_unterminated_line(lines, 0); + test_unterminated_line(lines, 0); + test_unterminated_line(lines, 0); } { const std::vector lines{"9,8,first", "1,2,\\\n\\\n\\\n\\\njust"}; - test_unterminated_line(lines, 1); - test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); } { const std::vector lines{"9,8,fi\\\nrs\\\nt", "1,2,\\\n\\\n\\\n\\\njust"}; - test_unterminated_line(lines, 1); - test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); } { const std::vector lines{"9,8,first", "1,2,\\\n\\\n\\\n\\\njust", "4,3,third"}; - test_unterminated_line(lines, 1); - test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); } // multiline limmit reached quote { const std::vector lines{"1,2,\"\n\n\n\n\njust\""}; - test_unterminated_line(lines, 0); - test_unterminated_line(lines, 0); + test_unterminated_line(lines, 0); + test_unterminated_line(lines, 0); } { const std::vector lines{"9,8,first", "1,2,\"\n\n\n\n\njust\""}; - test_unterminated_line(lines, 1); - test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); } { const std::vector lines{"9,8,\"fir\nst\"", "1,2,\"\n\n\n\n\njust\""}; - test_unterminated_line(lines, 1); - test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); } // multiline limmit reached quote and escape { const std::vector lines{"1,2,\"\\\n\n\\\n\\\n\\\njust"}; - test_unterminated_line(lines, 0); + test_unterminated_line(lines, 0); } { const std::vector lines{"9,8,first", "1,2,\"\\\n\n\\\n\\\n\\\njust"}; - test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); } { const std::vector lines{"9,8,fi\\\nrst", "1,2,\"\\\n\n\\\n\\\n\\\njust"}; - test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); } { const std::vector lines{"9,8,\"fi\nrst\"", "1,2,\"\\\n\n\\\n\\\n\\\njust"}; - test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); } { const std::vector lines{"9,8,\"fi\nr\\\nst\"", "1,2,\"\\\n\n\\\n\\\n\\\njust"}; - test_unterminated_line(lines, 1); + test_unterminated_line(lines, 1); } } diff --git a/test/test_parser1_4.cpp b/test/test_parser1_4.cpp index 82233c6..993e74b 100644 --- a/test/test_parser1_4.cpp +++ b/test/test_parser1_4.cpp @@ -7,13 +7,14 @@ template struct has_type> : std::disjunction...> {}; -template -static void test_fields_impl(const std::string file_name, - const std::vector& data, - const std::vector& fields) { +template +static void test_fields(const std::string file_name, const std::vector& data, + const std::vector& fields) { + constexpr auto buffer_mode = T::BufferMode::value; + using ErrorMode = typename T::ErrorMode; using CaseType = std::tuple; - auto [p, _] = make_parser(file_name, ","); + auto [p, _] = make_parser(file_name, ","); CHECK_FALSE(p.field_exists("Unknown")); p.use_fields(fields); std::vector i; @@ -36,23 +37,9 @@ static void test_fields_impl(const std::string file_name, } } -template -static void test_fields(const std::string file_name, const std::vector& data, - const std::vector& fields) { - test_fields_impl, Ts...>(file_name, data, fields); - test_fields_impl, Ts...>(file_name, data, - fields); - test_fields_impl, Ts...>(file_name, - data, fields); - test_fields_impl, Ts...>(file_name, data, fields); - test_fields_impl, Ts...>(file_name, data, - fields); - test_fields_impl, Ts...>(file_name, - data, fields); -} - -TEST_CASE("parser test various cases with header") { - unique_file_name f{"test_parser"}; +TEST_CASE_TEMPLATE("test various cases with header", T, + ParserOptionCombinations) { + unique_file_name f{"various_cases_with_header"}; constexpr static auto Int = "Int"; constexpr static auto Dbl = "Double"; constexpr static auto Str = "String"; @@ -180,27 +167,30 @@ TEST_CASE("parser test various cases with header") { print(call) */ - test_fields(o, d, {Str}); - test_fields(o, d, {Int}); - test_fields(o, d, {Dbl}); - test_fields(o, d, {Str, Int}); - test_fields(o, d, {Str, Dbl}); - test_fields(o, d, {Int, Str}); - test_fields(o, d, {Int, Dbl}); - test_fields(o, d, {Dbl, Str}); - test_fields(o, d, {Dbl, Int}); - test_fields(o, d, {Str, Int, Dbl}); - test_fields(o, d, {Str, Dbl, Int}); - test_fields(o, d, {Int, Str, Dbl}); - test_fields(o, d, {Int, Dbl, Str}); - test_fields(o, d, {Dbl, Str, Int}); - test_fields(o, d, {Dbl, Int, Str}); + test_fields(o, d, {Str}); + test_fields(o, d, {Int}); + test_fields(o, d, {Dbl}); + test_fields(o, d, {Str, Int}); + test_fields(o, d, {Str, Dbl}); + test_fields(o, d, {Int, Str}); + test_fields(o, d, {Int, Dbl}); + test_fields(o, d, {Dbl, Str}); + test_fields(o, d, {Dbl, Int}); + test_fields(o, d, {Str, Int, Dbl}); + test_fields(o, d, {Str, Dbl, Int}); + test_fields(o, d, {Int, Str, Dbl}); + test_fields(o, d, {Int, Dbl, Str}); + test_fields(o, d, {Dbl, Str, Int}); + test_fields(o, d, {Dbl, Int, Str}); } -template -void test_invalid_fields_impl(const std::vector& lines, - const std::vector& fields) { - unique_file_name f{"test_parser"}; +template +void test_invalid_fields(const std::vector& lines, + const std::vector& fields) { + constexpr auto buffer_mode = T::BufferMode::value; + using ErrorMode = typename T::ErrorMode; + + unique_file_name f{"invalid_fields"}; { std::ofstream out{f.name}; for (const auto& line : lines) { @@ -210,21 +200,21 @@ void test_invalid_fields_impl(const std::vector& lines, { // No fields specified - auto [p, _] = make_parser(f.name, ","); + auto [p, _] = make_parser(f.name, ","); auto command = [&p = p] { p.use_fields(); }; expect_error_on_command(p, command); } { // Unknown field - auto [p, _] = make_parser(f.name, ","); + auto [p, _] = make_parser(f.name, ","); auto command = [&p = p] { p.use_fields("Unknown"); }; expect_error_on_command(p, command); } { // Field used multiple times - auto [p, _] = make_parser(f.name, ","); + auto [p, _] = make_parser(f.name, ","); auto command = [&p = p, &fields = fields] { p.use_fields(fields.at(0), fields.at(0)); }; @@ -235,7 +225,7 @@ void test_invalid_fields_impl(const std::vector& lines, { // Mapping out of range - auto [p, _] = make_parser(f.name, ","); + auto [p, _] = make_parser(f.name, ","); auto command = [&p = p, &fields = fields] { p.use_fields(fields.at(0)); p.template get_next(); @@ -247,7 +237,7 @@ void test_invalid_fields_impl(const std::vector& lines, { // Invalid header - auto [p, _] = make_parser(f.name, ","); + auto [p, _] = make_parser(f.name, ","); auto command = [&p = p, &fields = fields] { p.use_fields(fields); }; if (!fields.empty()) { @@ -259,7 +249,7 @@ void test_invalid_fields_impl(const std::vector& lines, command(); CHECK(p.valid()); if (!p.valid()) { - if constexpr (ss::setup::string_error) { + if constexpr (T::StringError) { std::cout << p.error_msg() << std::endl; } } @@ -268,44 +258,38 @@ void test_invalid_fields_impl(const std::vector& lines, } } -template -void test_invalid_fields(const std::vector& lines, - const std::vector& fields) { - test_invalid_fields_impl(lines, fields); - test_invalid_fields_impl(lines, fields); - test_invalid_fields_impl(lines, fields); - test_invalid_fields_impl(lines, fields); - test_invalid_fields_impl(lines, fields); - test_invalid_fields_impl(lines, fields); +TEST_CASE_TEMPLATE("test invalid fheader fields usage", T, + ParserOptionCombinations) { + test_invalid_fields({}, {}); + + test_invalid_fields({"Int"}, {"Int"}); + test_invalid_fields({"Int", "1"}, {"Int"}); + test_invalid_fields({"Int", "1", "2"}, {"Int"}); + + test_invalid_fields({"Int,String"}, {"Int", "String"}); + test_invalid_fields({"Int,String", "1,hi"}, {"Int", "String"}); + test_invalid_fields({"Int,String", "2,hello"}, {"Int", "String"}); + + test_invalid_fields({"Int,String,Double"}, {"Int", "String", "Double"}); + test_invalid_fields({"Int,String,Double", "1,hi,2.34"}, + {"Int", "String", "Double"}); + test_invalid_fields({"Int,String,Double", "1,hi,2.34", "2,hello,3.45"}, + {"Int", "String", "Double"}); + + test_invalid_fields({"Int,Int,Int"}, {"Int", "Int", "Int"}); + test_invalid_fields({"Int,Int,Int", "1,2,3"}, {"Int", "Int", "Int"}); + + test_invalid_fields({"Int,String,Int"}, {"Int", "String", "Int"}); + test_invalid_fields({"Int,String,Int", "1,hi,3"}, + {"Int", "String", "Int"}); } -TEST_CASE("parser test invalid header fields usage") { - test_invalid_fields({}, {}); +TEST_CASE_TEMPLATE("test invalid rows with header", T, + ParserOptionCombinations) { + constexpr auto buffer_mode = T::BufferMode::value; + using ErrorMode = typename T::ErrorMode; - test_invalid_fields({"Int"}, {"Int"}); - test_invalid_fields({"Int", "1"}, {"Int"}); - test_invalid_fields({"Int", "1", "2"}, {"Int"}); - - test_invalid_fields({"Int,String"}, {"Int", "String"}); - test_invalid_fields({"Int,String", "1,hi"}, {"Int", "String"}); - test_invalid_fields({"Int,String", "2,hello"}, {"Int", "String"}); - - test_invalid_fields({"Int,String,Double"}, {"Int", "String", "Double"}); - test_invalid_fields({"Int,String,Double", "1,hi,2.34"}, - {"Int", "String", "Double"}); - test_invalid_fields({"Int,String,Double", "1,hi,2.34", "2,hello,3.45"}, - {"Int", "String", "Double"}); - - test_invalid_fields({"Int,Int,Int"}, {"Int", "Int", "Int"}); - test_invalid_fields({"Int,Int,Int", "1,2,3"}, {"Int", "Int", "Int"}); - - test_invalid_fields({"Int,String,Int"}, {"Int", "String", "Int"}); - test_invalid_fields({"Int,String,Int", "1,hi,3"}, {"Int", "String", "Int"}); -} - -template -void test_invalid_rows_with_header() { - unique_file_name f{"test_parser"}; + unique_file_name f{"invalid rows with header"}; { std::ofstream out{f.name}; out << "Int,String,Double" << std::endl; @@ -318,7 +302,7 @@ void test_invalid_rows_with_header() { } { - auto [p, _] = make_parser(f.name); + auto [p, _] = make_parser(f.name); p.use_fields("Int", "String", "Double"); using data = std::tuple; @@ -344,7 +328,7 @@ void test_invalid_rows_with_header() { } { - auto [p, _] = make_parser(f.name); + auto [p, _] = make_parser(f.name); p.use_fields("Double", "Int"); using data = std::tuple; @@ -368,7 +352,7 @@ void test_invalid_rows_with_header() { } { - auto [p, _] = make_parser(f.name); + auto [p, _] = make_parser(f.name); p.use_fields("String", "Double"); using data = std::tuple; @@ -395,18 +379,12 @@ void test_invalid_rows_with_header() { } } -TEST_CASE("parser test invalid rows with header") { - test_invalid_rows_with_header(); - test_invalid_rows_with_header(); - test_invalid_rows_with_header(); - test_invalid_rows_with_header(); - test_invalid_rows_with_header(); - test_invalid_rows_with_header(); -} +template +void test_ignore_empty(const std::vector& data) { + constexpr auto buffer_mode = T::BufferMode::value; + using ErrorMode = typename T::ErrorMode; -template -void test_ignore_empty_impl(const std::vector& data) { - unique_file_name f{"test_parser"}; + unique_file_name f{"ignore_empty"}; make_and_write(f.name, data); std::vector expected; @@ -418,7 +396,7 @@ void test_ignore_empty_impl(const std::vector& data) { { auto [p, _] = - make_parser(f.name, ","); + make_parser(f.name, ","); std::vector i; for (const auto& a : p.template iterate()) { @@ -429,7 +407,7 @@ void test_ignore_empty_impl(const std::vector& data) { } { - auto [p, _] = make_parser(f.name, ","); + auto [p, _] = make_parser(f.name, ","); std::vector i; size_t n = 0; while (!p.eof()) { @@ -450,52 +428,44 @@ void test_ignore_empty_impl(const std::vector& data) { } } -template -void test_ignore_empty(const std::vector& data) { - test_ignore_empty_impl(data); - test_ignore_empty_impl(data); - test_ignore_empty_impl(data); - test_ignore_empty_impl(data); - test_ignore_empty_impl(data); - test_ignore_empty_impl(data); -} +TEST_CASE_TEMPLATE("test various cases with empty lines", T, + ParserOptionCombinations) { + test_ignore_empty( + {{1, 2, "x"}, {3, 4, "y"}, {9, 10, "v"}, {11, 12, "w"}}); -TEST_CASE("parser test various cases with empty lines") { - test_ignore_empty({{1, 2, "x"}, {3, 4, "y"}, {9, 10, "v"}, {11, 12, "w"}}); - - test_ignore_empty( + test_ignore_empty( {{1, 2, X::empty}, {3, 4, "y"}, {9, 10, "v"}, {11, 12, "w"}}); - test_ignore_empty( + test_ignore_empty( {{1, 2, "x"}, {3, 4, "y"}, {9, 10, "v"}, {11, 12, X::empty}}); - test_ignore_empty( + test_ignore_empty( {{1, 2, "x"}, {5, 6, X::empty}, {9, 10, "v"}, {11, 12, "w"}}); - test_ignore_empty( + test_ignore_empty( {{1, 2, X::empty}, {5, 6, X::empty}, {9, 10, "v"}, {11, 12, "w"}}); - test_ignore_empty( + test_ignore_empty( {{1, 2, X::empty}, {3, 4, "y"}, {9, 10, "v"}, {11, 12, X::empty}}); - test_ignore_empty( + test_ignore_empty( {{1, 2, "x"}, {3, 4, "y"}, {9, 10, X::empty}, {11, 12, X::empty}}); - test_ignore_empty( + test_ignore_empty( {{1, 2, X::empty}, {3, 4, "y"}, {9, 10, X::empty}, {11, 12, X::empty}}); - test_ignore_empty({{1, 2, X::empty}, - {3, 4, X::empty}, - {9, 10, X::empty}, - {11, 12, X::empty}}); + test_ignore_empty({{1, 2, X::empty}, + {3, 4, X::empty}, + {9, 10, X::empty}, + {11, 12, X::empty}}); - test_ignore_empty( + test_ignore_empty( {{1, 2, "x"}, {3, 4, X::empty}, {9, 10, X::empty}, {11, 12, X::empty}}); - test_ignore_empty( + test_ignore_empty( {{1, 2, X::empty}, {3, 4, X::empty}, {9, 10, X::empty}, {11, 12, "w"}}); - test_ignore_empty({{11, 12, X::empty}}); + test_ignore_empty({{11, 12, X::empty}}); - test_ignore_empty({}); + test_ignore_empty({}); } diff --git a/test/test_parser2.hpp b/test/test_parser2.hpp index 9d29f2a..6048a33 100644 --- a/test/test_parser2.hpp +++ b/test/test_parser2.hpp @@ -314,7 +314,7 @@ void test_data_combinations(const std::vector& input_data, return; } - unique_file_name f{"test_parser2" + std::string{SEGMENT_NAME}}; + unique_file_name f{"parser_data_combinations" + std::string{SEGMENT_NAME}}; std::vector> expected_data; std::vector header; std::vector field_header;