mirror of
https://github.com/red0124/ssp.git
synced 2025-12-14 21:59:55 +01:00
Updated and added new functions related to headers, resolved ODR issues, resolved clang-tidy warnings (#50)
* Bugfix/odr violations (#47) * Make common non-member functions inline, remove unreachable line from get_line_buffer * [skip ci] Fix namespace comments * Resolve clang-tidy warnings (#48) * Resolve clang-tidy warnings, update single_header_generator.py * Update single header test, resolve additional clang-tidy warnings * Add header and raw_header methods, update header usage methods error handling, write new and update existing unit tests * Update parser error messages, fix parser tests * Add [[nodiscard]] where fitting, update unit tests (#49) * Add const where fitting, make splitter class members private, add #pragma once to ssp.hpp * Modify header parsing for empty headers, update old and add new tests for header parsing * Enable the parser to accept a header with one empty field, update unit tests * Fix test CMakeLists.txt typo
This commit is contained in:
@@ -33,10 +33,11 @@ set(DOCTEST "${FETCHCONTENT_BASE_DIR}/doctest-src")
|
||||
enable_testing()
|
||||
|
||||
foreach(name IN ITEMS test_splitter test_parser1_1 test_parser1_2
|
||||
test_parser1_3 test_parser1_4 test_converter
|
||||
test_extractions test_parser2_1 test_parser2_2
|
||||
test_parser2_3 test_parser2_4 test_parser2_5
|
||||
test_parser2_6 test_extractions_without_fast_float)
|
||||
test_parser1_3 test_parser1_4 test_parser1_5
|
||||
test_converter test_extractions test_parser2_1
|
||||
test_parser2_2 test_parser2_3 test_parser2_4
|
||||
test_parser2_5 test_parser2_6
|
||||
test_extractions_without_fast_float)
|
||||
add_executable("${name}" "${name}.cpp")
|
||||
target_link_libraries("${name}" PRIVATE ssp::ssp fast_float
|
||||
doctest::doctest)
|
||||
|
||||
@@ -6,6 +6,7 @@ tests = [
|
||||
'parser1_2',
|
||||
'parser1_3',
|
||||
'parser1_4',
|
||||
'parser1_5',
|
||||
'splitter',
|
||||
'converter',
|
||||
'extractions',
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
#include "test_helpers.hpp"
|
||||
#include <algorithm>
|
||||
#include <ss/converter.hpp>
|
||||
|
||||
TEST_CASE("converter test split") {
|
||||
ss::converter c;
|
||||
for (const auto& [s, expected, delim] :
|
||||
// clang-format off
|
||||
{std::make_tuple("a,b,c,d", std::vector{"a", "b", "c", "d"}, ","),
|
||||
{"", {}, " "},
|
||||
{" x x x x | x ", {" x x x x ", " x "}, "|"},
|
||||
{"a::b::c::d", {"a", "b", "c", "d"}, "::"},
|
||||
{"x\t-\ty", {"x", "y"}, "\t-\t"},
|
||||
{"x", {"x"}, ","}} // clang-format on
|
||||
{std::make_tuple("a,b,c,d", std::vector{"a", "b", "c", "d"}, ","),
|
||||
{"", {}, " "},
|
||||
{" x x x x | x ", {" x x x x ", " x "}, "|"},
|
||||
{"a::b::c::d", {"a", "b", "c", "d"}, "::"},
|
||||
{"x\t-\ty", {"x", "y"}, "\t-\t"},
|
||||
{"x", {"x"}, ","}}
|
||||
// clang-format on
|
||||
) {
|
||||
auto split = c.split(s, delim);
|
||||
CHECK_EQ(split.size(), expected.size());
|
||||
@@ -278,37 +278,38 @@ TEST_CASE_TEMPLATE("converter test valid conversions with exceptions", T, int,
|
||||
TEST_CASE_TEMPLATE("converter test invalid conversions", T, int, ss::uint8) {
|
||||
ss::converter c;
|
||||
|
||||
c.convert<T>("");
|
||||
std::ignore = c.convert<T>("");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<T>("1", "");
|
||||
std::ignore = c.convert<T>("1", "");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<T>("10", "");
|
||||
std::ignore = c.convert<T>("10", "");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<T, void>("");
|
||||
std::ignore = c.convert<T, void>("");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<T, void>(",junk");
|
||||
std::ignore = c.convert<T, void>(",junk");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<void, T>("junk,");
|
||||
std::ignore = c.convert<void, T>("junk,");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<T>("x");
|
||||
std::ignore = c.convert<T>("x");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<T, void>("x");
|
||||
std::ignore = c.convert<T, void>("x");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<T, void>("x,junk");
|
||||
std::ignore = c.convert<T, void>("x,junk");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<void, T>("junk,x");
|
||||
std::ignore = c.convert<void, T>("junk,x");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<void, std::variant<T, double>, double>("junk;.5.5;6", ";");
|
||||
std::ignore =
|
||||
c.convert<void, std::variant<T, double>, double>("junk;.5.5;6", ";");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
}
|
||||
|
||||
@@ -316,34 +317,36 @@ TEST_CASE_TEMPLATE("converter test invalid conversions with exceptions", T, int,
|
||||
ss::uint8) {
|
||||
ss::converter<ss::throw_on_error> c;
|
||||
|
||||
REQUIRE_EXCEPTION(c.convert<T>(""));
|
||||
REQUIRE_EXCEPTION(c.convert<T>("1", ""));
|
||||
REQUIRE_EXCEPTION(c.convert<T>("10", ""));
|
||||
REQUIRE_EXCEPTION(c.convert<T, void>(""));
|
||||
REQUIRE_EXCEPTION(c.convert<T, void>(",junk"));
|
||||
REQUIRE_EXCEPTION(c.convert<void, T>("junk,"));
|
||||
REQUIRE_EXCEPTION(c.convert<T>("x"));
|
||||
REQUIRE_EXCEPTION(c.convert<T, void>("x"));
|
||||
REQUIRE_EXCEPTION(c.convert<T, void>("x,junk"));
|
||||
REQUIRE_EXCEPTION(c.convert<void, T>("junk,x"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<T>(""));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<T>("1", ""));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<T>("10", ""));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<T, void>(""));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<T, void>(",junk"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<void, T>("junk,"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<T>("x"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<T, void>("x"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<T, void>("x,junk"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<void, T>("junk,x"));
|
||||
REQUIRE_EXCEPTION(
|
||||
c.convert<void, std::variant<T, double>, double>("junk;.5.5;6", ";"));
|
||||
std::ignore =
|
||||
c.convert<void, std::variant<T, double>, double>("junk;.5.5;6",
|
||||
";"));
|
||||
}
|
||||
|
||||
TEST_CASE_TEMPLATE("converter test ss:ax restriction (all except)", T, int,
|
||||
ss::uint8) {
|
||||
ss::converter c;
|
||||
|
||||
c.convert<ss::ax<T, 0>>("0");
|
||||
std::ignore = c.convert<ss::ax<T, 0>>("0");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<ss::ax<T, 0, 1, 2>>("1");
|
||||
std::ignore = c.convert<ss::ax<T, 0, 1, 2>>("1");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<void, char, ss::ax<T, 0, 1, 2>>("junk,c,1");
|
||||
std::ignore = c.convert<void, char, ss::ax<T, 0, 1, 2>>("junk,c,1");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<ss::ax<T, 1>, char>("1,c");
|
||||
std::ignore = c.convert<ss::ax<T, 1>, char>("1,c");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
{
|
||||
T tup = c.convert<ss::ax<T, 1>>("3");
|
||||
@@ -367,10 +370,11 @@ TEST_CASE_TEMPLATE(
|
||||
ss::uint8) {
|
||||
ss::converter<ss::throw_on_error> c;
|
||||
|
||||
REQUIRE_EXCEPTION(c.convert<ss::ax<T, 0>>("0"));
|
||||
REQUIRE_EXCEPTION(c.convert<ss::ax<T, 0, 1, 2>>("1"));
|
||||
REQUIRE_EXCEPTION(c.convert<void, char, ss::ax<T, 0, 1, 2>>("junk,c,1"));
|
||||
REQUIRE_EXCEPTION(c.convert<ss::ax<T, 1>, char>("1,c"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<ss::ax<T, 0>>("0"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<ss::ax<T, 0, 1, 2>>("1"));
|
||||
REQUIRE_EXCEPTION(
|
||||
std::ignore = c.convert<void, char, ss::ax<T, 0, 1, 2>>("junk,c,1"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<ss::ax<T, 1>, char>("1,c"));
|
||||
|
||||
try {
|
||||
{
|
||||
@@ -393,13 +397,13 @@ TEST_CASE_TEMPLATE(
|
||||
TEST_CASE("converter test ss:nx restriction (none except)") {
|
||||
ss::converter c;
|
||||
|
||||
c.convert<ss::nx<int, 1>>("3");
|
||||
std::ignore = c.convert<ss::nx<int, 1>>("3");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<char, ss::nx<int, 1, 2, 69>>("c,3");
|
||||
std::ignore = c.convert<char, ss::nx<int, 1, 2, 69>>("c,3");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<ss::nx<int, 1>, char>("3,c");
|
||||
std::ignore = c.convert<ss::nx<int, 1>, char>("3,c");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
{
|
||||
@@ -427,9 +431,10 @@ TEST_CASE("converter test ss:nx restriction (none except)") {
|
||||
TEST_CASE("converter test ss:nx restriction (none except) with exceptions") {
|
||||
ss::converter<ss::throw_on_error> c;
|
||||
|
||||
REQUIRE_EXCEPTION(c.convert<ss::nx<int, 1>>("3"));
|
||||
REQUIRE_EXCEPTION(c.convert<char, ss::nx<int, 1, 2, 69>>("c,3"));
|
||||
REQUIRE_EXCEPTION(c.convert<ss::nx<int, 1>, char>("3,c"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<ss::nx<int, 1>>("3"));
|
||||
REQUIRE_EXCEPTION(std::ignore =
|
||||
c.convert<char, ss::nx<int, 1, 2, 69>>("c,3"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<ss::nx<int, 1>, char>("3,c"));
|
||||
|
||||
try {
|
||||
{
|
||||
@@ -461,13 +466,13 @@ TEST_CASE_TEMPLATE("converter test ss:ir restriction (in range)", T, int,
|
||||
ss::uint8) {
|
||||
ss::converter c;
|
||||
|
||||
c.convert<ss::ir<T, 0, 2>>("3");
|
||||
std::ignore = c.convert<ss::ir<T, 0, 2>>("3");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<char, ss::ir<T, 4, 69>>("c,3");
|
||||
std::ignore = c.convert<char, ss::ir<T, 4, 69>>("c,3");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<ss::ir<T, 1, 2>, char>("3,c");
|
||||
std::ignore = c.convert<ss::ir<T, 1, 2>, char>("3,c");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
{
|
||||
@@ -497,9 +502,9 @@ TEST_CASE_TEMPLATE(
|
||||
ss::uint8) {
|
||||
ss::converter<ss::throw_on_error> c;
|
||||
|
||||
REQUIRE_EXCEPTION(c.convert<ss::ir<T, 0, 2>>("3"));
|
||||
REQUIRE_EXCEPTION(c.convert<char, ss::ir<T, 4, 69>>("c,3"));
|
||||
REQUIRE_EXCEPTION(c.convert<ss::ir<T, 1, 2>, char>("3,c"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<ss::ir<T, 0, 2>>("3"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<char, ss::ir<T, 4, 69>>("c,3"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<ss::ir<T, 1, 2>, char>("3,c"));
|
||||
|
||||
try {
|
||||
{
|
||||
@@ -530,16 +535,16 @@ TEST_CASE_TEMPLATE(
|
||||
TEST_CASE("converter test ss:oor restriction (out of range)") {
|
||||
ss::converter c;
|
||||
|
||||
c.convert<ss::oor<int, 1, 5>>("3");
|
||||
std::ignore = c.convert<ss::oor<int, 1, 5>>("3");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<ss::oor<int, 0, 2>>("2");
|
||||
std::ignore = c.convert<ss::oor<int, 0, 2>>("2");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<char, ss::oor<int, 0, 1>, void>("c,1,junk");
|
||||
std::ignore = c.convert<char, ss::oor<int, 0, 1>, void>("c,1,junk");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<ss::oor<int, 1, 20>, char>("1,c");
|
||||
std::ignore = c.convert<ss::oor<int, 1, 20>, char>("1,c");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
{
|
||||
@@ -564,10 +569,12 @@ TEST_CASE("converter test ss:oor restriction (out of range)") {
|
||||
TEST_CASE("converter test ss:oor restriction (out of range) with exceptions") {
|
||||
ss::converter<ss::throw_on_error> c;
|
||||
|
||||
REQUIRE_EXCEPTION(c.convert<ss::oor<int, 1, 5>>("3"));
|
||||
REQUIRE_EXCEPTION(c.convert<ss::oor<int, 0, 2>>("2"));
|
||||
REQUIRE_EXCEPTION(c.convert<char, ss::oor<int, 0, 1>, void>("c,1,junk"));
|
||||
REQUIRE_EXCEPTION(c.convert<ss::oor<int, 1, 20>, char>("1,c"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<ss::oor<int, 1, 5>>("3"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<ss::oor<int, 0, 2>>("2"));
|
||||
REQUIRE_EXCEPTION(
|
||||
std::ignore = c.convert<char, ss::oor<int, 0, 1>, void>("c,1,junk"));
|
||||
REQUIRE_EXCEPTION(std::ignore =
|
||||
c.convert<ss::oor<int, 1, 20>, char>("1,c"));
|
||||
|
||||
try {
|
||||
{
|
||||
@@ -608,19 +615,19 @@ inline bool ss::extract(const char* begin, const char* end,
|
||||
TEST_CASE("converter test ss:ne restriction (not empty)") {
|
||||
ss::converter c;
|
||||
|
||||
c.convert<ss::ne<std::string>>("");
|
||||
std::ignore = c.convert<ss::ne<std::string>>("");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<int, ss::ne<std::string>>("3,");
|
||||
std::ignore = c.convert<int, ss::ne<std::string>>("3,");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<ss::ne<std::string>, int>(",3");
|
||||
std::ignore = c.convert<ss::ne<std::string>, int>(",3");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<void, ss::ne<std::string>, int>("junk,,3");
|
||||
std::ignore = c.convert<void, ss::ne<std::string>, int>("junk,,3");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<ss::ne<std::vector<int>>>("");
|
||||
std::ignore = c.convert<ss::ne<std::vector<int>>>("");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
{
|
||||
@@ -643,11 +650,12 @@ TEST_CASE("converter test ss:ne restriction (not empty)") {
|
||||
TEST_CASE("converter test ss:ne restriction (not empty) with exceptions") {
|
||||
ss::converter<ss::throw_on_error> c;
|
||||
|
||||
REQUIRE_EXCEPTION(c.convert<ss::ne<std::string>>(""));
|
||||
REQUIRE_EXCEPTION(c.convert<int, ss::ne<std::string>>("3,"));
|
||||
REQUIRE_EXCEPTION(c.convert<ss::ne<std::string>, int>(",3"));
|
||||
REQUIRE_EXCEPTION(c.convert<void, ss::ne<std::string>, int>("junk,,3"));
|
||||
REQUIRE_EXCEPTION(c.convert<ss::ne<std::vector<int>>>(""));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<ss::ne<std::string>>(""));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<int, ss::ne<std::string>>("3,"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<ss::ne<std::string>, int>(",3"));
|
||||
REQUIRE_EXCEPTION(std::ignore =
|
||||
c.convert<void, ss::ne<std::string>, int>("junk,,3"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<ss::ne<std::vector<int>>>(""));
|
||||
|
||||
try {
|
||||
{
|
||||
@@ -675,22 +683,22 @@ TEST_CASE(
|
||||
"converter test ss:lt ss::lte ss::gt ss::gte restriction (in range)") {
|
||||
ss::converter c;
|
||||
|
||||
c.convert<ss::lt<int, 3>>("3");
|
||||
std::ignore = c.convert<ss::lt<int, 3>>("3");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<ss::lt<int, 2>>("3");
|
||||
std::ignore = c.convert<ss::lt<int, 2>>("3");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<ss::gt<int, 3>>("3");
|
||||
std::ignore = c.convert<ss::gt<int, 3>>("3");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<ss::gt<int, 4>>("3");
|
||||
std::ignore = c.convert<ss::gt<int, 4>>("3");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<ss::lte<int, 2>>("3");
|
||||
std::ignore = c.convert<ss::lte<int, 2>>("3");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
c.convert<ss::gte<int, 4>>("3");
|
||||
std::ignore = c.convert<ss::gte<int, 4>>("3");
|
||||
REQUIRE_FALSE(c.valid());
|
||||
|
||||
{
|
||||
@@ -734,12 +742,12 @@ TEST_CASE("converter test ss:lt ss::lte ss::gt ss::gte restriction (in range) "
|
||||
"with exception") {
|
||||
ss::converter<ss::throw_on_error> c;
|
||||
|
||||
REQUIRE_EXCEPTION(c.convert<ss::lt<int, 3>>("3"));
|
||||
REQUIRE_EXCEPTION(c.convert<ss::lt<int, 2>>("3"));
|
||||
REQUIRE_EXCEPTION(c.convert<ss::gt<int, 3>>("3"));
|
||||
REQUIRE_EXCEPTION(c.convert<ss::gt<int, 4>>("3"));
|
||||
REQUIRE_EXCEPTION(c.convert<ss::lte<int, 2>>("3"));
|
||||
REQUIRE_EXCEPTION(c.convert<ss::gte<int, 4>>("3"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<ss::lt<int, 3>>("3"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<ss::lt<int, 2>>("3"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<ss::gt<int, 3>>("3"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<ss::gt<int, 4>>("3"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<ss::lte<int, 2>>("3"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<ss::gte<int, 4>>("3"));
|
||||
|
||||
try {
|
||||
{
|
||||
@@ -784,14 +792,14 @@ TEST_CASE("converter test ss:lt ss::lte ss::gt ss::gte restriction (in range) "
|
||||
|
||||
TEST_CASE("converter test error mode") {
|
||||
ss::converter<ss::string_error> c;
|
||||
c.convert<int>("junk");
|
||||
std::ignore = c.convert<int>("junk");
|
||||
CHECK_FALSE(c.valid());
|
||||
CHECK_FALSE(c.error_msg().empty());
|
||||
}
|
||||
|
||||
TEST_CASE("converter test throw on error mode") {
|
||||
ss::converter<ss::throw_on_error> c;
|
||||
REQUIRE_EXCEPTION(c.convert<int>("junk"));
|
||||
REQUIRE_EXCEPTION(std::ignore = c.convert<int>("junk"));
|
||||
}
|
||||
|
||||
TEST_CASE("converter test converter with quotes spacing and escaping") {
|
||||
@@ -908,7 +916,7 @@ TEST_CASE("converter test invalid split conversions") {
|
||||
|
||||
{
|
||||
// mismatched quote
|
||||
c.convert<std::string, std::string, double, char>(
|
||||
std::ignore = c.convert<std::string, std::string, double, char>(
|
||||
buff(R"( "just , some , "12.3","a" )"));
|
||||
CHECK_FALSE(c.valid());
|
||||
CHECK_FALSE(c.unterminated_quote());
|
||||
@@ -917,7 +925,7 @@ TEST_CASE("converter test invalid split conversions") {
|
||||
|
||||
{
|
||||
// unterminated quote
|
||||
c.convert<std::string, std::string, double, std::string>(
|
||||
std::ignore = c.convert<std::string, std::string, double, std::string>(
|
||||
buff(R"( ju\,st , "so,me" , 12.34 , "str""ings)"));
|
||||
CHECK_FALSE(c.valid());
|
||||
CHECK(c.unterminated_quote());
|
||||
@@ -926,7 +934,7 @@ TEST_CASE("converter test invalid split conversions") {
|
||||
|
||||
{
|
||||
// unterminated escape
|
||||
c.convert<std::string, std::string, double, std::string>(
|
||||
std::ignore = c.convert<std::string, std::string, double, std::string>(
|
||||
buff(R"(just,some,2,strings\)"));
|
||||
CHECK_FALSE(c.valid());
|
||||
CHECK_FALSE(c.unterminated_quote());
|
||||
@@ -935,7 +943,7 @@ TEST_CASE("converter test invalid split conversions") {
|
||||
|
||||
{
|
||||
// unterminated escape while quoting
|
||||
c.convert<std::string, std::string, double, std::string>(
|
||||
std::ignore = c.convert<std::string, std::string, double, std::string>(
|
||||
buff(R"(just,some,2,"strings\)"));
|
||||
CHECK_FALSE(c.valid());
|
||||
CHECK_FALSE(c.unterminated_quote());
|
||||
@@ -944,7 +952,7 @@ TEST_CASE("converter test invalid split conversions") {
|
||||
|
||||
{
|
||||
// unterminated escaped quote
|
||||
c.convert<std::string, std::string, double, std::string>(
|
||||
std::ignore = c.convert<std::string, std::string, double, std::string>(
|
||||
buff(R"(just,some,2,"strings\")"));
|
||||
CHECK_FALSE(c.valid());
|
||||
CHECK(c.unterminated_quote());
|
||||
@@ -958,27 +966,32 @@ TEST_CASE("converter test invalid split conversions with exceptions") {
|
||||
c;
|
||||
|
||||
// mismatched quote
|
||||
REQUIRE_EXCEPTION(c.convert<std::string, std::string, double, char>(
|
||||
buff(R"( "just , some , "12.3","a" )")));
|
||||
REQUIRE_EXCEPTION(std::ignore =
|
||||
c.convert<std::string, std::string, double, char>(
|
||||
buff(R"( "just , some , "12.3","a" )")));
|
||||
CHECK_FALSE(c.unterminated_quote());
|
||||
|
||||
// unterminated quote
|
||||
REQUIRE_EXCEPTION(c.convert<std::string, std::string, double, std::string>(
|
||||
buff(R"( ju\,st , "so,me" , 12.34 , "str""ings)")));
|
||||
REQUIRE_EXCEPTION(
|
||||
std::ignore = c.convert<std::string, std::string, double, std::string>(
|
||||
buff(R"( ju\,st , "so,me" , 12.34 , "str""ings)")));
|
||||
CHECK(c.unterminated_quote());
|
||||
|
||||
// unterminated escape
|
||||
REQUIRE_EXCEPTION(c.convert<std::string, std::string, double, std::string>(
|
||||
buff(R"(just,some,2,strings\)")));
|
||||
REQUIRE_EXCEPTION(
|
||||
std::ignore = c.convert<std::string, std::string, double, std::string>(
|
||||
buff(R"(just,some,2,strings\)")));
|
||||
CHECK_FALSE(c.unterminated_quote());
|
||||
|
||||
// unterminated escape while quoting
|
||||
REQUIRE_EXCEPTION(c.convert<std::string, std::string, double, std::string>(
|
||||
buff(R"(just,some,2,"strings\)")));
|
||||
REQUIRE_EXCEPTION(
|
||||
std::ignore = c.convert<std::string, std::string, double, std::string>(
|
||||
buff(R"(just,some,2,"strings\)")));
|
||||
CHECK_FALSE(c.unterminated_quote());
|
||||
|
||||
// unterminated escaped quote
|
||||
REQUIRE_EXCEPTION(c.convert<std::string, std::string, double, std::string>(
|
||||
buff(R"(just,some,2,"strings\")")));
|
||||
REQUIRE_EXCEPTION(
|
||||
std::ignore = c.convert<std::string, std::string, double, std::string>(
|
||||
buff(R"(just,some,2,"strings\")")));
|
||||
CHECK(c.unterminated_quote());
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ struct is_unsigned : public std::is_unsigned<T> {};
|
||||
template <>
|
||||
struct is_unsigned<ss::uint8> : public std::true_type {};
|
||||
|
||||
} /* namespace */
|
||||
} /* anonymous namespace */
|
||||
|
||||
static_assert(is_signed<ss::int8>::value);
|
||||
static_assert(is_unsigned<ss::uint8>::value);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "test_helpers.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
#define SSP_DISABLE_FAST_FLOAT
|
||||
#include <ss/extract.hpp>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
namespace ss {
|
||||
template <typename... Ts>
|
||||
class parser;
|
||||
} /* ss */
|
||||
} /* namespace ss */
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -145,6 +145,17 @@ struct unique_file_name {
|
||||
CHECK_FALSE(std::string{e.what()}.empty()); \
|
||||
}
|
||||
|
||||
#define CHECK_EQ_ARRAY(first, second) \
|
||||
{ \
|
||||
const auto& first_ = (first); \
|
||||
const auto& second_ = (second); \
|
||||
CHECK_EQ(first_.size(), second_.size()); \
|
||||
for (size_t i_ = 0; i_ < std::min(first_.size(), second_.size()); \
|
||||
++i_) { \
|
||||
CHECK_EQ(first_[i_], second_[i_]); \
|
||||
} \
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[maybe_unused]] std::vector<std::vector<T>> vector_combinations(
|
||||
const std::vector<T>& v, size_t n) {
|
||||
@@ -166,6 +177,22 @@ template <typename T>
|
||||
return ret;
|
||||
}
|
||||
|
||||
[[maybe_unused]] std::string merge_header(
|
||||
const std::vector<std::string>& header,
|
||||
const std::string& delimiter = ss::default_delimiter) {
|
||||
std::string s;
|
||||
if (!header.empty()) {
|
||||
for (const auto& i : header) {
|
||||
s.append(i);
|
||||
s.append(delimiter);
|
||||
}
|
||||
for (size_t i = 0; i < delimiter.size(); ++i) {
|
||||
s.pop_back();
|
||||
}
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
||||
[[maybe_unused]] std::string make_buffer(const std::string& file_name) {
|
||||
std::ifstream in{file_name, std::ios::binary};
|
||||
std::string tmp;
|
||||
@@ -185,6 +212,7 @@ template <typename T>
|
||||
}
|
||||
};
|
||||
|
||||
// Evade small string optimization
|
||||
out.reserve(sizeof(out) + 1);
|
||||
|
||||
copy_if_whitespaces();
|
||||
@@ -224,4 +252,4 @@ make_parser(const std::string& file_name,
|
||||
return make_parser_impl<buffer_mode, Ts...>(file_name, delim);
|
||||
}
|
||||
|
||||
} /* namespace */
|
||||
} /* anonymous namespace */
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
#include <unordered_set>
|
||||
|
||||
namespace {
|
||||
[[maybe_unused]] void replace_all(std::string& s, const std::string& from,
|
||||
const std::string& to) {
|
||||
#ifdef _WIN32
|
||||
void replace_all(std::string& s, const std::string& from,
|
||||
const std::string& to) {
|
||||
if (from.empty()) return;
|
||||
size_t start_pos = 0;
|
||||
while ((start_pos = s.find(from, start_pos)) != std::string::npos) {
|
||||
@@ -21,6 +22,7 @@ namespace {
|
||||
start_pos += to.length();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename... Ts>
|
||||
void expect_error_on_command(ss::parser<Ts...>& p,
|
||||
@@ -28,6 +30,7 @@ void expect_error_on_command(ss::parser<Ts...>& p,
|
||||
if (ss::setup<Ts...>::throw_on_error) {
|
||||
try {
|
||||
command();
|
||||
FAIL("expected exception");
|
||||
} catch (const std::exception& e) {
|
||||
CHECK_FALSE(std::string{e.what()}.empty());
|
||||
}
|
||||
@@ -55,7 +58,7 @@ struct X {
|
||||
double d;
|
||||
std::string s;
|
||||
|
||||
std::string to_string() const {
|
||||
[[nodiscard]] std::string to_string() const {
|
||||
if (s == empty) {
|
||||
return "";
|
||||
}
|
||||
@@ -66,14 +69,15 @@ struct X {
|
||||
.append(delim)
|
||||
.append(s);
|
||||
}
|
||||
auto tied() const {
|
||||
|
||||
[[nodiscard]] auto tied() const {
|
||||
return std::tie(i, d, s);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<ss::has_m_tied_t<T>, bool> operator==(const T& lhs,
|
||||
const T& rhs) {
|
||||
[[nodiscard]] std::enable_if_t<ss::has_m_tied_t<T>, bool> operator==(
|
||||
const T& lhs, const T& rhs) {
|
||||
return lhs.tied() == rhs.tied();
|
||||
}
|
||||
|
||||
@@ -109,4 +113,4 @@ static void make_and_write(const std::string& file_name,
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace */
|
||||
} /* anonymous namespace */
|
||||
|
||||
@@ -57,7 +57,7 @@ struct Y {
|
||||
.append(s3);
|
||||
}
|
||||
|
||||
auto tied() const {
|
||||
[[nodiscard]] auto tied() const {
|
||||
return std::tie(s1, s2, s3);
|
||||
}
|
||||
};
|
||||
@@ -115,7 +115,8 @@ TEST_CASE_TEMPLATE("test line method", T, ParserOptionCombinations) {
|
||||
CHECK_EQ(p.line(), expected_line);
|
||||
|
||||
while (!p.eof()) {
|
||||
auto _ = p.template get_next<std::string, std::string, std::string>();
|
||||
std::ignore =
|
||||
p.template get_next<std::string, std::string, std::string>();
|
||||
++expected_line;
|
||||
CHECK_EQ(p.line(), expected_line);
|
||||
}
|
||||
|
||||
@@ -51,14 +51,16 @@ TEST_CASE_TEMPLATE("test moving of parsed composite values", T,
|
||||
// to compile is enough
|
||||
return;
|
||||
auto [p, _] = make_parser<buffer_mode, ErrorMode>("", "");
|
||||
p.template try_next<my_string, my_string, my_string>()
|
||||
.template or_else<my_string, my_string, my_string, my_string>(
|
||||
[](auto&&) {})
|
||||
.template or_else<my_string>([](auto&) {})
|
||||
.template or_else<xyz>([](auto&&) {})
|
||||
.template or_object<xyz, my_string, my_string, my_string>([](auto&&) {})
|
||||
.template or_else<std::tuple<my_string, my_string, my_string>>(
|
||||
[](auto&, auto&, auto&) {});
|
||||
std::ignore =
|
||||
p.template try_next<my_string, my_string, my_string>()
|
||||
.template or_else<my_string, my_string, my_string, my_string>(
|
||||
[](auto&&) {})
|
||||
.template or_else<my_string>([](auto&) {})
|
||||
.template or_else<xyz>([](auto&&) {})
|
||||
.template or_object<xyz, my_string, my_string, my_string>(
|
||||
[](auto&&) {})
|
||||
.template or_else<std::tuple<my_string, my_string, my_string>>(
|
||||
[](auto&, auto&, auto&) {});
|
||||
}
|
||||
|
||||
TEST_CASE_TEMPLATE("parser test string error mode", BufferMode, std::true_type,
|
||||
@@ -73,7 +75,7 @@ TEST_CASE_TEMPLATE("parser test string error mode", BufferMode, std::true_type,
|
||||
auto [p, _] = make_parser<BufferMode::value, ss::string_error>(f.name, ",");
|
||||
|
||||
REQUIRE_FALSE(p.eof());
|
||||
p.template get_next<int>();
|
||||
std::ignore = p.template get_next<int>();
|
||||
CHECK_FALSE(p.valid());
|
||||
CHECK_FALSE(p.error_msg().empty());
|
||||
}
|
||||
@@ -92,7 +94,7 @@ TEST_CASE_TEMPLATE("parser throw on error mode", BufferMode, std::true_type,
|
||||
|
||||
REQUIRE_FALSE(p.eof());
|
||||
try {
|
||||
p.template get_next<int>();
|
||||
std::ignore = p.template get_next<int>();
|
||||
FAIL("Expected exception...");
|
||||
} catch (const std::exception& e) {
|
||||
CHECK_FALSE(std::string{e.what()}.empty());
|
||||
@@ -148,7 +150,8 @@ TEST_CASE_TEMPLATE("test quote multiline", T, ParserOptionCombinations) {
|
||||
make_parser<buffer_mode, ErrorMode, ss::quote<'"'>>(f.name, ",");
|
||||
while (!p.eof()) {
|
||||
auto command = [&p_no_multiline = p_no_multiline] {
|
||||
p_no_multiline.template get_next<int, double, std::string>();
|
||||
std::ignore =
|
||||
p_no_multiline.template get_next<int, double, std::string>();
|
||||
};
|
||||
expect_error_on_command(p_no_multiline, command);
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ void test_unterminated_line(const std::vector<std::string>& lines,
|
||||
size_t line = 0;
|
||||
while (!p.eof()) {
|
||||
auto command = [&p = p] {
|
||||
p.template get_next<int, double, std::string>();
|
||||
std::ignore = p.template get_next<int, double, std::string>();
|
||||
};
|
||||
|
||||
if (line == bad_line) {
|
||||
|
||||
@@ -9,6 +9,7 @@ struct has_type<T, std::tuple<Us...>>
|
||||
|
||||
template <typename T, typename... Ts>
|
||||
static void test_fields(const std::string file_name, const std::vector<X>& data,
|
||||
const std::vector<std::string>& header,
|
||||
const std::vector<std::string>& fields) {
|
||||
constexpr auto buffer_mode = T::BufferMode::value;
|
||||
using ErrorMode = typename T::ErrorMode;
|
||||
@@ -17,9 +18,14 @@ static void test_fields(const std::string file_name, const std::vector<X>& data,
|
||||
auto [p, _] = make_parser<buffer_mode, ErrorMode>(file_name, ",");
|
||||
CHECK_FALSE(p.field_exists("Unknown"));
|
||||
p.use_fields(fields);
|
||||
|
||||
CHECK_EQ_ARRAY(header, p.header());
|
||||
CHECK_EQ(merge_header(p.header(), ","), p.raw_header());
|
||||
std::vector<CaseType> i;
|
||||
|
||||
for (const auto& a : p.template iterate<CaseType>()) {
|
||||
CHECK_EQ_ARRAY(header, p.header());
|
||||
CHECK_EQ(merge_header(p.header(), ","), p.raw_header());
|
||||
i.push_back(a);
|
||||
}
|
||||
|
||||
@@ -40,12 +46,12 @@ static void test_fields(const std::string file_name, const std::vector<X>& data,
|
||||
TEST_CASE_TEMPLATE("test various cases with header", T,
|
||||
ParserOptionCombinations) {
|
||||
unique_file_name f{"various_cases_with_header"};
|
||||
using str = std::string;
|
||||
|
||||
constexpr static auto Int = "Int";
|
||||
constexpr static auto Dbl = "Double";
|
||||
constexpr static auto Str = "String";
|
||||
using str = std::string;
|
||||
|
||||
std::vector<std::string> header{Int, Dbl, Str};
|
||||
const std::vector<std::string> header{Int, Dbl, Str};
|
||||
|
||||
std::vector<X> data = {{1, 2, "x"}, {3, 4, "y"}, {5, 6, "z"},
|
||||
{7, 8, "u"}, {9, 10, "v"}, {11, 12, "w"}};
|
||||
@@ -59,6 +65,8 @@ TEST_CASE_TEMPLATE("test various cases with header", T,
|
||||
std::vector<X> i;
|
||||
|
||||
for (const auto& a : p.iterate<int, double, std::string>()) {
|
||||
CHECK_EQ(header, p.header());
|
||||
CHECK_EQ(merge_header(p.header(), ","), p.raw_header());
|
||||
i.emplace_back(ss::to_object<X>(a));
|
||||
}
|
||||
|
||||
@@ -71,46 +79,22 @@ TEST_CASE_TEMPLATE("test various cases with header", T,
|
||||
|
||||
p.ignore_next();
|
||||
for (const auto& a : p.iterate<int, double, std::string>()) {
|
||||
CHECK_EQ(header, p.header());
|
||||
CHECK_EQ(merge_header(p.header(), ","), p.raw_header());
|
||||
i.emplace_back(ss::to_object<X>(a));
|
||||
}
|
||||
|
||||
CHECK_EQ(i, data);
|
||||
}
|
||||
|
||||
{
|
||||
ss::parser<ss::ignore_header> p{f.name, ","};
|
||||
std::vector<X> i;
|
||||
|
||||
for (const auto& a : p.iterate<int, double, std::string>()) {
|
||||
i.emplace_back(ss::to_object<X>(a));
|
||||
}
|
||||
|
||||
CHECK_EQ(i, data);
|
||||
}
|
||||
|
||||
{
|
||||
ss::parser<ss::ignore_header, ss::string_error> p{f.name, ","};
|
||||
p.use_fields(Int, Dbl, Str);
|
||||
CHECK_FALSE(p.valid());
|
||||
}
|
||||
|
||||
{
|
||||
ss::parser<ss::ignore_header, ss::string_error> p{f.name, ","};
|
||||
CHECK_FALSE(p.field_exists("Unknown"));
|
||||
|
||||
p.use_fields(Int, "Unknown");
|
||||
CHECK_FALSE(p.valid());
|
||||
}
|
||||
|
||||
{
|
||||
ss::parser<ss::ignore_header, ss::string_error> p{f.name, ","};
|
||||
p.use_fields(Int, Int);
|
||||
CHECK_FALSE(p.valid());
|
||||
}
|
||||
|
||||
{
|
||||
ss::parser<ss::string_error> p{f.name, ","};
|
||||
CHECK_EQ(header, p.header());
|
||||
CHECK_EQ(merge_header(p.header(), ","), p.raw_header());
|
||||
|
||||
p.use_fields(Int, Dbl);
|
||||
CHECK_EQ(header, p.header());
|
||||
CHECK_EQ(merge_header(p.header(), ","), p.raw_header());
|
||||
|
||||
{
|
||||
auto [int_, double_] = p.get_next<int, double>();
|
||||
@@ -119,6 +103,8 @@ TEST_CASE_TEMPLATE("test various cases with header", T,
|
||||
}
|
||||
|
||||
p.use_fields(Dbl, Int);
|
||||
CHECK_EQ(header, p.header());
|
||||
CHECK_EQ(merge_header(p.header(), ","), p.raw_header());
|
||||
|
||||
{
|
||||
auto [double_, int_] = p.get_next<double, int>();
|
||||
@@ -163,25 +149,25 @@ TEST_CASE_TEMPLATE("test various cases with header", T,
|
||||
template_params.append(type)
|
||||
arg_params.append(header[type])
|
||||
call = 'testFields<' + ', '.join(template_params) + \
|
||||
'>(o, d, {' + ', '.join(arg_params) + '});'
|
||||
'>(o, d, header, {' + ', '.join(arg_params) + '});'
|
||||
print(call)
|
||||
*/
|
||||
|
||||
test_fields<T, str>(o, d, {Str});
|
||||
test_fields<T, int>(o, d, {Int});
|
||||
test_fields<T, double>(o, d, {Dbl});
|
||||
test_fields<T, str, int>(o, d, {Str, Int});
|
||||
test_fields<T, str, double>(o, d, {Str, Dbl});
|
||||
test_fields<T, int, str>(o, d, {Int, Str});
|
||||
test_fields<T, int, double>(o, d, {Int, Dbl});
|
||||
test_fields<T, double, str>(o, d, {Dbl, Str});
|
||||
test_fields<T, double, int>(o, d, {Dbl, Int});
|
||||
test_fields<T, str, int, double>(o, d, {Str, Int, Dbl});
|
||||
test_fields<T, str, double, int>(o, d, {Str, Dbl, Int});
|
||||
test_fields<T, int, str, double>(o, d, {Int, Str, Dbl});
|
||||
test_fields<T, int, double, str>(o, d, {Int, Dbl, Str});
|
||||
test_fields<T, double, str, int>(o, d, {Dbl, Str, Int});
|
||||
test_fields<T, double, int, str>(o, d, {Dbl, Int, Str});
|
||||
test_fields<T, str>(o, d, header, {Str});
|
||||
test_fields<T, int>(o, d, header, {Int});
|
||||
test_fields<T, double>(o, d, header, {Dbl});
|
||||
test_fields<T, str, int>(o, d, header, {Str, Int});
|
||||
test_fields<T, str, double>(o, d, header, {Str, Dbl});
|
||||
test_fields<T, int, str>(o, d, header, {Int, Str});
|
||||
test_fields<T, int, double>(o, d, header, {Int, Dbl});
|
||||
test_fields<T, double, str>(o, d, header, {Dbl, Str});
|
||||
test_fields<T, double, int>(o, d, header, {Dbl, Int});
|
||||
test_fields<T, str, int, double>(o, d, header, {Str, Int, Dbl});
|
||||
test_fields<T, str, double, int>(o, d, header, {Str, Dbl, Int});
|
||||
test_fields<T, int, str, double>(o, d, header, {Int, Str, Dbl});
|
||||
test_fields<T, int, double, str>(o, d, header, {Int, Dbl, Str});
|
||||
test_fields<T, double, str, int>(o, d, header, {Dbl, Str, Int});
|
||||
test_fields<T, double, int, str>(o, d, header, {Dbl, Int, Str});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -190,6 +176,18 @@ void test_invalid_fields(const std::vector<std::string>& lines,
|
||||
constexpr auto buffer_mode = T::BufferMode::value;
|
||||
using ErrorMode = typename T::ErrorMode;
|
||||
|
||||
auto check_header = [&lines](auto& p) {
|
||||
if (lines.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()));
|
||||
CHECK_EQ(merge_header(p.header(), ","), p.raw_header());
|
||||
}
|
||||
CHECK(p.valid());
|
||||
};
|
||||
|
||||
unique_file_name f{"invalid_fields"};
|
||||
{
|
||||
std::ofstream out{f.name};
|
||||
@@ -203,6 +201,7 @@ void test_invalid_fields(const std::vector<std::string>& lines,
|
||||
auto [p, _] = make_parser<buffer_mode, ErrorMode>(f.name, ",");
|
||||
auto command = [&p = p] { p.use_fields(); };
|
||||
expect_error_on_command(p, command);
|
||||
check_header(p);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -210,6 +209,7 @@ void test_invalid_fields(const std::vector<std::string>& lines,
|
||||
auto [p, _] = make_parser<buffer_mode, ErrorMode>(f.name, ",");
|
||||
auto command = [&p = p] { p.use_fields("Unknown"); };
|
||||
expect_error_on_command(p, command);
|
||||
check_header(p);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -221,6 +221,7 @@ void test_invalid_fields(const std::vector<std::string>& lines,
|
||||
if (!fields.empty()) {
|
||||
expect_error_on_command(p, command);
|
||||
}
|
||||
check_header(p);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -228,17 +229,21 @@ void test_invalid_fields(const std::vector<std::string>& lines,
|
||||
auto [p, _] = make_parser<buffer_mode, ErrorMode>(f.name, ",");
|
||||
auto command = [&p = p, &fields = fields] {
|
||||
p.use_fields(fields.at(0));
|
||||
p.template get_next<std::string, std::string>();
|
||||
std::ignore = p.template get_next<std::string, std::string>();
|
||||
};
|
||||
check_header(p);
|
||||
|
||||
if (!fields.empty()) {
|
||||
expect_error_on_command(p, command);
|
||||
}
|
||||
check_header(p);
|
||||
}
|
||||
|
||||
{
|
||||
// Invalid header
|
||||
auto [p, _] = make_parser<buffer_mode, ErrorMode>(f.name, ",");
|
||||
auto command = [&p = p, &fields = fields] { p.use_fields(fields); };
|
||||
check_header(p);
|
||||
|
||||
if (!fields.empty()) {
|
||||
// Pass if there are no duplicates, fail otherwise
|
||||
@@ -255,10 +260,11 @@ void test_invalid_fields(const std::vector<std::string>& lines,
|
||||
}
|
||||
}
|
||||
}
|
||||
check_header(p);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_TEMPLATE("test invalid fheader fields usage", T,
|
||||
TEST_CASE_TEMPLATE("test invalid header fields usage", T,
|
||||
ParserOptionCombinations) {
|
||||
test_invalid_fields<T>({}, {});
|
||||
|
||||
@@ -289,7 +295,7 @@ TEST_CASE_TEMPLATE("test invalid rows with header", T,
|
||||
constexpr auto buffer_mode = T::BufferMode::value;
|
||||
using ErrorMode = typename T::ErrorMode;
|
||||
|
||||
unique_file_name f{"invalid rows with header"};
|
||||
unique_file_name f{"invalid_rows_with_header"};
|
||||
{
|
||||
std::ofstream out{f.name};
|
||||
out << "Int,String,Double" << std::endl;
|
||||
@@ -301,8 +307,12 @@ TEST_CASE_TEMPLATE("test invalid rows with header", T,
|
||||
out << "six,line6,10.11" << std::endl;
|
||||
}
|
||||
|
||||
std::vector<std::string> header = {"Int", "String", "Double"};
|
||||
|
||||
{
|
||||
auto [p, _] = make_parser<buffer_mode, ErrorMode>(f.name);
|
||||
CHECK_EQ_ARRAY(header, p.header());
|
||||
CHECK_EQ(merge_header(p.header()), p.raw_header());
|
||||
|
||||
p.use_fields("Int", "String", "Double");
|
||||
using data = std::tuple<int, std::string, double>;
|
||||
@@ -325,10 +335,14 @@ TEST_CASE_TEMPLATE("test invalid rows with header", T,
|
||||
{3, "line3", 67.8},
|
||||
{5, "line5", 9.10}};
|
||||
CHECK_EQ(i, expected);
|
||||
CHECK_EQ_ARRAY(header, p.header());
|
||||
CHECK_EQ(merge_header(p.header()), p.raw_header());
|
||||
}
|
||||
|
||||
{
|
||||
auto [p, _] = make_parser<buffer_mode, ErrorMode>(f.name);
|
||||
CHECK_EQ_ARRAY(header, p.header());
|
||||
CHECK_EQ(merge_header(p.header()), p.raw_header());
|
||||
|
||||
p.use_fields("Double", "Int");
|
||||
using data = std::tuple<double, int>;
|
||||
@@ -349,10 +363,14 @@ TEST_CASE_TEMPLATE("test invalid rows with header", T,
|
||||
|
||||
std::vector<data> expected = {{2.34, 1}, {67.8, 3}, {9.10, 5}};
|
||||
CHECK_EQ(i, expected);
|
||||
CHECK_EQ_ARRAY(header, p.header());
|
||||
CHECK_EQ(merge_header(p.header()), p.raw_header());
|
||||
}
|
||||
|
||||
{
|
||||
auto [p, _] = make_parser<buffer_mode, ErrorMode>(f.name);
|
||||
CHECK_EQ_ARRAY(header, p.header());
|
||||
CHECK_EQ(merge_header(p.header()), p.raw_header());
|
||||
|
||||
p.use_fields("String", "Double");
|
||||
using data = std::tuple<std::string, double>;
|
||||
@@ -376,96 +394,7 @@ TEST_CASE_TEMPLATE("test invalid rows with header", T,
|
||||
{"line5", 9.10},
|
||||
{"line6", 10.11}};
|
||||
CHECK_EQ(i, expected);
|
||||
CHECK_EQ_ARRAY(header, p.header());
|
||||
CHECK_EQ(merge_header(p.header()), p.raw_header());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void test_ignore_empty(const std::vector<X>& data) {
|
||||
constexpr auto buffer_mode = T::BufferMode::value;
|
||||
using ErrorMode = typename T::ErrorMode;
|
||||
|
||||
unique_file_name f{"ignore_empty"};
|
||||
make_and_write(f.name, data);
|
||||
|
||||
std::vector<X> expected;
|
||||
for (const auto& d : data) {
|
||||
if (d.s != X::empty) {
|
||||
expected.push_back(d);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto [p, _] =
|
||||
make_parser<buffer_mode, ErrorMode, ss::ignore_empty>(f.name, ",");
|
||||
|
||||
std::vector<X> i;
|
||||
for (const auto& a : p.template iterate<X>()) {
|
||||
i.push_back(a);
|
||||
}
|
||||
|
||||
CHECK_EQ(i, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto [p, _] = make_parser<buffer_mode, ErrorMode>(f.name, ",");
|
||||
std::vector<X> i;
|
||||
size_t n = 0;
|
||||
while (!p.eof()) {
|
||||
try {
|
||||
++n;
|
||||
const auto& a = p.template get_next<X>();
|
||||
if (data.at(n - 1).s == X::empty) {
|
||||
CHECK_FALSE(p.valid());
|
||||
continue;
|
||||
}
|
||||
i.push_back(a);
|
||||
} catch (...) {
|
||||
CHECK_EQ(data.at(n - 1).s, X::empty);
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_EQ(i, expected);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_TEMPLATE("test various cases with empty lines", T,
|
||||
ParserOptionCombinations) {
|
||||
test_ignore_empty<T>(
|
||||
{{1, 2, "x"}, {3, 4, "y"}, {9, 10, "v"}, {11, 12, "w"}});
|
||||
|
||||
test_ignore_empty<T>(
|
||||
{{1, 2, X::empty}, {3, 4, "y"}, {9, 10, "v"}, {11, 12, "w"}});
|
||||
|
||||
test_ignore_empty<T>(
|
||||
{{1, 2, "x"}, {3, 4, "y"}, {9, 10, "v"}, {11, 12, X::empty}});
|
||||
|
||||
test_ignore_empty<T>(
|
||||
{{1, 2, "x"}, {5, 6, X::empty}, {9, 10, "v"}, {11, 12, "w"}});
|
||||
|
||||
test_ignore_empty<T>(
|
||||
{{1, 2, X::empty}, {5, 6, X::empty}, {9, 10, "v"}, {11, 12, "w"}});
|
||||
|
||||
test_ignore_empty<T>(
|
||||
{{1, 2, X::empty}, {3, 4, "y"}, {9, 10, "v"}, {11, 12, X::empty}});
|
||||
|
||||
test_ignore_empty<T>(
|
||||
{{1, 2, "x"}, {3, 4, "y"}, {9, 10, X::empty}, {11, 12, X::empty}});
|
||||
|
||||
test_ignore_empty<T>(
|
||||
{{1, 2, X::empty}, {3, 4, "y"}, {9, 10, X::empty}, {11, 12, X::empty}});
|
||||
|
||||
test_ignore_empty<T>({{1, 2, X::empty},
|
||||
{3, 4, X::empty},
|
||||
{9, 10, X::empty},
|
||||
{11, 12, X::empty}});
|
||||
|
||||
test_ignore_empty<T>(
|
||||
{{1, 2, "x"}, {3, 4, X::empty}, {9, 10, X::empty}, {11, 12, X::empty}});
|
||||
|
||||
test_ignore_empty<T>(
|
||||
{{1, 2, X::empty}, {3, 4, X::empty}, {9, 10, X::empty}, {11, 12, "w"}});
|
||||
|
||||
test_ignore_empty<T>({{11, 12, X::empty}});
|
||||
|
||||
test_ignore_empty<T>({});
|
||||
}
|
||||
|
||||
301
test/test_parser1_5.cpp
Normal file
301
test/test_parser1_5.cpp
Normal file
@@ -0,0 +1,301 @@
|
||||
#include "test_parser1.hpp"
|
||||
|
||||
TEST_CASE_TEMPLATE("test empty fields header", T, ParserOptionCombinations) {
|
||||
constexpr auto buffer_mode = T::BufferMode::value;
|
||||
using ErrorMode = typename T::ErrorMode;
|
||||
|
||||
unique_file_name f{"empty_fields_header"};
|
||||
|
||||
// Empty header
|
||||
{
|
||||
std::ofstream out{f.name};
|
||||
out << "" << std::endl;
|
||||
out << "1" << std::endl;
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> expected_header = {""};
|
||||
auto [p, _] = make_parser<buffer_mode, ErrorMode>(f.name);
|
||||
CHECK_EQ_ARRAY(expected_header, p.header());
|
||||
CHECK_EQ("", p.raw_header());
|
||||
CHECK(p.valid());
|
||||
}
|
||||
|
||||
// All empty header fields
|
||||
{
|
||||
std::ofstream out{f.name};
|
||||
out << ",," << std::endl;
|
||||
out << "1,2,3" << std::endl;
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> expected_header = {"", "", ""};
|
||||
auto [p, _] = make_parser<buffer_mode, ErrorMode>(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);
|
||||
}
|
||||
|
||||
// One empty field
|
||||
const std::vector<std::string> valid_fields = {"Int0", "Int1", ""};
|
||||
|
||||
using svec = std::vector<std::string>;
|
||||
const std::vector<std::vector<std::string>> valid_field_combinations =
|
||||
{svec{"Int0"},
|
||||
svec{"Int1"},
|
||||
svec{""},
|
||||
svec{"", "Int0"},
|
||||
svec{"Int0", "Int1"},
|
||||
svec{"Int1", ""},
|
||||
svec{"Int0", "", "Int1"},
|
||||
svec{"", "Int1", "Int0"}};
|
||||
|
||||
// Last header field empty
|
||||
{
|
||||
std::ofstream out{f.name};
|
||||
out << "Int0,Int1," << std::endl;
|
||||
out << "1,2,3" << std::endl;
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> expected_header = {"Int0", "Int1", ""};
|
||||
auto [p, _] = make_parser<buffer_mode, ErrorMode>(f.name);
|
||||
CHECK_EQ_ARRAY(expected_header, p.header());
|
||||
CHECK_EQ("Int0,Int1,", p.raw_header());
|
||||
CHECK(p.valid());
|
||||
|
||||
for (const auto& field : valid_fields) {
|
||||
CHECK(p.field_exists(field));
|
||||
CHECK(p.valid());
|
||||
}
|
||||
|
||||
for (const auto& fields : valid_field_combinations) {
|
||||
p.use_fields(fields);
|
||||
CHECK(p.valid());
|
||||
}
|
||||
}
|
||||
|
||||
// First header field empty
|
||||
{
|
||||
std::ofstream out{f.name};
|
||||
out << ",Int0,Int1" << std::endl;
|
||||
out << "1,2,3" << std::endl;
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> expected_header = {"", "Int0", "Int1"};
|
||||
auto [p, _] = make_parser<buffer_mode, ErrorMode>(f.name);
|
||||
CHECK_EQ_ARRAY(expected_header, p.header());
|
||||
CHECK_EQ(",Int0,Int1", p.raw_header());
|
||||
CHECK(p.valid());
|
||||
|
||||
for (const auto& field : valid_fields) {
|
||||
CHECK(p.field_exists(field));
|
||||
CHECK(p.valid());
|
||||
}
|
||||
|
||||
for (const auto& fields : valid_field_combinations) {
|
||||
p.use_fields(fields);
|
||||
CHECK(p.valid());
|
||||
}
|
||||
}
|
||||
|
||||
// Middle header field empty
|
||||
{
|
||||
std::ofstream out{f.name};
|
||||
out << "Int0,,Int1" << std::endl;
|
||||
out << "1,2,3" << std::endl;
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> expected_header = {"Int0", "", "Int1"};
|
||||
auto [p, _] = make_parser<buffer_mode, ErrorMode>(f.name);
|
||||
CHECK_EQ_ARRAY(expected_header, p.header());
|
||||
CHECK_EQ("Int0,,Int1", p.raw_header());
|
||||
CHECK(p.valid());
|
||||
|
||||
for (const auto& field : valid_fields) {
|
||||
CHECK(p.field_exists(field));
|
||||
CHECK(p.valid());
|
||||
}
|
||||
|
||||
for (const auto& fields : valid_field_combinations) {
|
||||
p.use_fields(fields);
|
||||
CHECK(p.valid());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename... Ts>
|
||||
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;
|
||||
out << "1" << std::endl;
|
||||
}
|
||||
|
||||
{
|
||||
auto [p, _] = make_parser<buffer_mode, ErrorMode, Ts...>(f.name);
|
||||
|
||||
auto command0 = [&p = p] { std::ignore = p.header(); };
|
||||
expect_error_on_command(p, command0);
|
||||
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<T, quote>();
|
||||
test_unterminated_quote_header<T, quote, ss::multiline>();
|
||||
test_unterminated_quote_header<T, quote, escape>();
|
||||
test_unterminated_quote_header<T, quote, escape, ss::multiline>();
|
||||
}
|
||||
|
||||
template <typename T, typename... Ts>
|
||||
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
|
||||
{
|
||||
std::ofstream out{f.name};
|
||||
out << "Int\\" << std::endl;
|
||||
out << "1" << std::endl;
|
||||
}
|
||||
|
||||
{
|
||||
auto [p, _] = make_parser<buffer_mode, ErrorMode, Ts...>(f.name);
|
||||
|
||||
auto command0 = [&p = p] { std::ignore = p.header(); };
|
||||
expect_error_on_command(p, command0);
|
||||
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<T, escape>();
|
||||
test_unterminated_escape_header<T, escape, ss::multiline>();
|
||||
test_unterminated_escape_header<T, escape, quote>();
|
||||
test_unterminated_escape_header<T, escape, quote, ss::multiline>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void test_ignore_empty(const std::vector<X>& data) {
|
||||
constexpr auto buffer_mode = T::BufferMode::value;
|
||||
using ErrorMode = typename T::ErrorMode;
|
||||
|
||||
unique_file_name f{"ignore_empty"};
|
||||
make_and_write(f.name, data);
|
||||
|
||||
std::vector<X> expected;
|
||||
for (const auto& d : data) {
|
||||
if (d.s != X::empty) {
|
||||
expected.push_back(d);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto [p, _] =
|
||||
make_parser<buffer_mode, ErrorMode, ss::ignore_empty>(f.name, ",");
|
||||
|
||||
std::vector<X> i;
|
||||
for (const auto& a : p.template iterate<X>()) {
|
||||
i.push_back(a);
|
||||
}
|
||||
|
||||
CHECK_EQ(i, expected);
|
||||
}
|
||||
|
||||
{
|
||||
auto [p, _] = make_parser<buffer_mode, ErrorMode>(f.name, ",");
|
||||
std::vector<X> i;
|
||||
size_t n = 0;
|
||||
while (!p.eof()) {
|
||||
try {
|
||||
++n;
|
||||
const auto& a = p.template get_next<X>();
|
||||
if (data.at(n - 1).s == X::empty) {
|
||||
CHECK_FALSE(p.valid());
|
||||
continue;
|
||||
}
|
||||
i.push_back(a);
|
||||
} catch (...) {
|
||||
CHECK_EQ(data.at(n - 1).s, X::empty);
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_EQ(i, expected);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_TEMPLATE("test various cases with empty lines", T,
|
||||
ParserOptionCombinations) {
|
||||
test_ignore_empty<T>(
|
||||
{{1, 2, "x"}, {3, 4, "y"}, {9, 10, "v"}, {11, 12, "w"}});
|
||||
|
||||
test_ignore_empty<T>(
|
||||
{{1, 2, X::empty}, {3, 4, "y"}, {9, 10, "v"}, {11, 12, "w"}});
|
||||
|
||||
test_ignore_empty<T>(
|
||||
{{1, 2, "x"}, {3, 4, "y"}, {9, 10, "v"}, {11, 12, X::empty}});
|
||||
|
||||
test_ignore_empty<T>(
|
||||
{{1, 2, "x"}, {5, 6, X::empty}, {9, 10, "v"}, {11, 12, "w"}});
|
||||
|
||||
test_ignore_empty<T>(
|
||||
{{1, 2, X::empty}, {5, 6, X::empty}, {9, 10, "v"}, {11, 12, "w"}});
|
||||
|
||||
test_ignore_empty<T>(
|
||||
{{1, 2, X::empty}, {3, 4, "y"}, {9, 10, "v"}, {11, 12, X::empty}});
|
||||
|
||||
test_ignore_empty<T>(
|
||||
{{1, 2, "x"}, {3, 4, "y"}, {9, 10, X::empty}, {11, 12, X::empty}});
|
||||
|
||||
test_ignore_empty<T>(
|
||||
{{1, 2, X::empty}, {3, 4, "y"}, {9, 10, X::empty}, {11, 12, X::empty}});
|
||||
|
||||
test_ignore_empty<T>({{1, 2, X::empty},
|
||||
{3, 4, X::empty},
|
||||
{9, 10, X::empty},
|
||||
{11, 12, X::empty}});
|
||||
|
||||
test_ignore_empty<T>(
|
||||
{{1, 2, "x"}, {3, 4, X::empty}, {9, 10, X::empty}, {11, 12, X::empty}});
|
||||
|
||||
test_ignore_empty<T>(
|
||||
{{1, 2, X::empty}, {3, 4, X::empty}, {9, 10, X::empty}, {11, 12, "w"}});
|
||||
|
||||
test_ignore_empty<T>({{11, 12, X::empty}});
|
||||
|
||||
test_ignore_empty<T>({});
|
||||
}
|
||||
@@ -1,13 +1,7 @@
|
||||
#include "test_helpers.hpp"
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <ss/parser.hpp>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#ifndef SEGMENT_NAME
|
||||
@@ -91,8 +85,8 @@ struct column {
|
||||
};
|
||||
|
||||
template <typename... Ts>
|
||||
column make_column(const std::string& input_header,
|
||||
const std::vector<field>& input_fields) {
|
||||
[[nodiscard]] column make_column(const std::string& input_header,
|
||||
const std::vector<field>& input_fields) {
|
||||
using setup = ss::setup<Ts...>;
|
||||
std::vector<field> filtered_fields;
|
||||
|
||||
@@ -133,8 +127,8 @@ column make_column(const std::string& input_header,
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
std::vector<std::string> generate_csv_data(const std::vector<field>& data,
|
||||
const std::string& delim) {
|
||||
[[nodiscard]] std::vector<std::string> generate_csv_data(
|
||||
const std::vector<field>& data, const std::string& delim) {
|
||||
(void)delim;
|
||||
using setup = ss::setup<Ts...>;
|
||||
constexpr static auto escape = '\\';
|
||||
@@ -333,8 +327,10 @@ void test_data_combinations(const std::vector<column>& input_data,
|
||||
field_header.push_back(field{el.header});
|
||||
}
|
||||
|
||||
std::string header_line;
|
||||
if (include_header) {
|
||||
auto header_data = generate_csv_data<Ts...>(field_header, delim);
|
||||
header_line = merge_header(header_data, delim);
|
||||
if (input_data.size() == 0 && rand() % 10 == 0) {
|
||||
write_to_file(header_data, delim, f.name, false);
|
||||
} else {
|
||||
@@ -403,7 +399,9 @@ void test_data_combinations(const std::vector<column>& input_data,
|
||||
fields.push_back(header[index]);
|
||||
}
|
||||
|
||||
p.use_fields(fields);
|
||||
if constexpr (!setup::ignore_header) {
|
||||
p.use_fields(fields);
|
||||
}
|
||||
|
||||
if (!p.valid()) {
|
||||
if constexpr (setup::string_error) {
|
||||
@@ -425,8 +423,19 @@ void test_data_combinations(const std::vector<column>& input_data,
|
||||
}
|
||||
};
|
||||
|
||||
auto check_header = [&p = p, &header = header, include_header,
|
||||
header_line] {
|
||||
if (include_header) {
|
||||
if constexpr (!setup::ignore_header) {
|
||||
CHECK_EQ_ARRAY(header, p.header());
|
||||
CHECK_EQ(header_line, p.raw_header());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int num_columns = layout.size();
|
||||
for (size_t i = 0; i < n + 1; ++i) {
|
||||
check_header();
|
||||
try {
|
||||
switch (num_columns) {
|
||||
case 1: {
|
||||
@@ -616,7 +625,7 @@ void test_option_combinations3() {
|
||||
test_option_combinations2<Ts..., trim>();
|
||||
}
|
||||
|
||||
} /* namespace */
|
||||
} /* anonymous namespace */
|
||||
|
||||
// Tests split into multiple compilation units
|
||||
#if 0
|
||||
|
||||
@@ -9,4 +9,3 @@ TEST_CASE("parser test various cases version 2 segment 1") {
|
||||
test_option_combinations3<escape>();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -10,4 +10,3 @@ TEST_CASE("parser test various cases version 2 segment 2") {
|
||||
test_option_combinations3<escape, quote>();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -11,4 +11,3 @@ TEST_CASE("parser test various cases version 2 segment 3") {
|
||||
test_option_combinations3<quote, multiline>();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -12,4 +12,3 @@ TEST_CASE("parser test various cases version 2 segment 4") {
|
||||
test_option_combinations3<escape, quote, multiline_r>();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -13,4 +13,3 @@ TEST_CASE("parser test various cases version 2 segment 5") {
|
||||
test_option_combinations<escape, quote, multiline, trimr>();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -8,4 +8,3 @@ TEST_CASE("parser test various cases version 2 segment 6") {
|
||||
|
||||
test_option_combinations3<escape, quote, multiline>();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
set -x
|
||||
set -e
|
||||
|
||||
python3 script/single_header_generator.py > ssp.cpp
|
||||
TMP_HDR=test_single_header.hpp
|
||||
TMP_SRC=test_single_header.cpp
|
||||
TMP_BIN=test_single_header
|
||||
|
||||
echo 'int main(){ ss::parser p{""}; p.get_next<int, float>(); return 0; }' \
|
||||
>> ssp.cpp
|
||||
python3 script/single_header_generator.py > ${TMP_HDR}
|
||||
cat ${TMP_HDR} test/test_single_header_main.txt > ${TMP_SRC}
|
||||
|
||||
g++ -std=c++17 ssp.cpp -o ssp.bin -Wall -Wextra
|
||||
./ssp.bin
|
||||
g++ -std=c++17 ${TMP_SRC} -o ${TMP_BIN} -Wall -Wextra
|
||||
./${TMP_BIN}
|
||||
|
||||
rm ssp.cpp ssp.bin
|
||||
rm ${TMP_HDR} ${TMP_SRC} ${TMP_BIN}
|
||||
|
||||
12
test/test_single_header_main.txt
Normal file
12
test/test_single_header_main.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
int main() {
|
||||
using quote = ss::quote<'"'>;
|
||||
using escape = ss::escape<'\\'>;
|
||||
using trim = ss::trim<' '>;
|
||||
|
||||
std::string data = "1,string,2.34,c";
|
||||
|
||||
ss::parser<quote, escape, trim, ss::multiline> p{data.c_str(), data.size()};
|
||||
auto tup = p.get_next<int, std::string, float, std::optional<char>>();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -145,7 +145,7 @@ make_combinations(const std::vector<std::string>& input,
|
||||
|
||||
return {std::move(lines), std::move(expectations)};
|
||||
}
|
||||
} /* namespace */
|
||||
} /* anonymous namespace */
|
||||
|
||||
/* ********************************** */
|
||||
/* ********************************** */
|
||||
@@ -548,7 +548,7 @@ public:
|
||||
return splitter.size_shifted();
|
||||
}
|
||||
};
|
||||
} /* ss */
|
||||
} /* namespace ss */
|
||||
|
||||
TEST_CASE("splitter test resplit unterminated quote") {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user