From f232c7d99592017d55e12f86e7eca00a2cd04396 Mon Sep 17 00:00:00 2001 From: ado Date: Tue, 8 Aug 2023 00:43:02 +0200 Subject: [PATCH 01/19] Add coverage ci --- .github/workflows/coverage.yml | 69 ++++++++++++++++++++++++++++++++++ test/CMakeLists.txt | 2 + 2 files changed, 71 insertions(+) create mode 100644 .github/workflows/coverage.yml diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000..df70a9a --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,69 @@ +name: ubuntu-latest-gcc-ci + +on: + workflow_dispatch: + + push: + branches: + - master + - feature/** + - improvement/** + - bugfix/** + + pull_request: + branches: + - master + - feature/** + - improvement/** + - bugfix/** + +jobs: + gcc_tests: + if: >- + ! contains(toJSON(github.event.commits.*.message), '[skip ci]') && + ! contains(toJSON(github.event.commits.*.message), '[skip github]') + + runs-on: ubuntu-latest + + name: "Coverage" + + container: + image: gcc:latest + options: -v /usr/local:/host_usr_local + + steps: + - uses: actions/checkout@v1 + + - uses: friendlyanon/fetch-core-count@v1 + id: cores + + - name: CMake + run: echo "/host_usr_local/bin" >> $GITHUB_PATH + + - name: Install dependencies + run: script/ci_install_deps.sh + + - name: Configure + run: cmake -S test -B build -D CMAKE_BUILD_TYPE=Debug + + - name: Build + run: cmake --build build -j ${{steps.cores.outputs.count}} + + - name: Run + working-directory: build + run: ctest --output-on-failure -j ${{steps.cores.outputs.count}} + + - name: Generate coverage report + run: | + lcov -d . -c -o out.info --rc lcov_branch_coverage=1 --no-external + lcov -e out.info '*include/ss*hpp' -o filtered_out.info + genhtml --t "SSP Coverage" --legend --demangle-cpp -o html -s --branch-coverage -p $PWD filtered_out.info + + - name: Archive coverage report + uses: actions/upload-artifact@v3 + with: + name: code-coverage-report + path: ${{github.workspace}}/html + + - name: Coveralls GitHub Action + uses: coverallsapp/github-action@v2 diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index de6b612..94c3327 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,6 +12,8 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") target_compile_options(ssp INTERFACE -Wall -Wextra) endif() +set(CMAKE_CXX_FLAGS "-Wall -fprofile-arcs -ftest-coverage --coverage") + if (MSVC) add_compile_options(/bigobj) elseif (MINGW) From d4c92278301c0ff631b1bb7b7dbc0f0af811f901 Mon Sep 17 00:00:00 2001 From: ado Date: Tue, 8 Aug 2023 01:06:27 +0200 Subject: [PATCH 02/19] Update coverage ci --- .github/workflows/coverage.yml | 9 +++++++-- test/CMakeLists.txt | 2 +- test/test_helpers.hpp | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index df70a9a..f752f8e 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -1,4 +1,4 @@ -name: ubuntu-latest-gcc-ci +name: coverage-ci on: workflow_dispatch: @@ -18,7 +18,7 @@ on: - bugfix/** jobs: - gcc_tests: + test_coverage: if: >- ! contains(toJSON(github.event.commits.*.message), '[skip ci]') && ! contains(toJSON(github.event.commits.*.message), '[skip github]') @@ -43,6 +43,11 @@ jobs: - name: Install dependencies run: script/ci_install_deps.sh + - name: Install test coverage tools + run: | + apt update + apt install -y gcovr lcov + - name: Configure run: cmake -S test -B build -D CMAKE_BUILD_TYPE=Debug diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 94c3327..e757397 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -41,6 +41,6 @@ foreach(name IN ITEMS test_splitter test_parser test_converter test_extractions target_link_libraries("${name}" PRIVATE ssp::ssp fast_float doctest::doctest) target_compile_definitions( - "${name}" PRIVATE DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN CMAKE_GITHUB_CI) + "${name}" PRIVATE DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN _CMAKE_GITHUB_CI) add_test(NAME "${name}" COMMAND "${name}") endforeach() diff --git a/test/test_helpers.hpp b/test/test_helpers.hpp index b7f362f..0437935 100644 --- a/test/test_helpers.hpp +++ b/test/test_helpers.hpp @@ -6,7 +6,7 @@ #include #include -#ifdef CMAKE_GITHUB_CI +#ifdef _CMAKE_GITHUB_CI #include #else #include From ed8f4e3147999385e2c99eb23e2e96bbd0004650 Mon Sep 17 00:00:00 2001 From: ado Date: Tue, 8 Aug 2023 01:12:05 +0200 Subject: [PATCH 03/19] Update coverage ci --- .github/workflows/coverage.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index f752f8e..76fb882 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -61,8 +61,8 @@ jobs: - name: Generate coverage report run: | lcov -d . -c -o out.info --rc lcov_branch_coverage=1 --no-external - lcov -e out.info '*include/ss*hpp' -o filtered_out.info - genhtml --t "SSP Coverage" --legend --demangle-cpp -o html -s --branch-coverage -p $PWD filtered_out.info + lcov -e out.info '*include/ss*hpp' -o filtered.info + genhtml --t "SSP Coverage" --legend --demangle-cpp -o html -s --branch-coverage -p $PWD filtered.info - name: Archive coverage report uses: actions/upload-artifact@v3 @@ -72,3 +72,6 @@ jobs: - name: Coveralls GitHub Action uses: coverallsapp/github-action@v2 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + path-to-lcov: ${{github.workspace}}.filtered.info From 0bd78120f3c9c80a84a6085c563f3f3be97e9912 Mon Sep 17 00:00:00 2001 From: ado Date: Tue, 8 Aug 2023 01:15:57 +0200 Subject: [PATCH 04/19] Update coverage ci --- .github/workflows/coverage.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 76fb882..64f58d1 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -70,8 +70,8 @@ jobs: name: code-coverage-report path: ${{github.workspace}}/html - - name: Coveralls GitHub Action + - name: Invoke coveralls uses: coverallsapp/github-action@v2 with: github-token: ${{secrets.GITHUB_TOKEN}} - path-to-lcov: ${{github.workspace}}.filtered.info + path-to-lcov: ${{github.workspace}}/filtered.info From 2504c3574d02f29107d82db163060e6b44a98fe2 Mon Sep 17 00:00:00 2001 From: ado Date: Tue, 8 Aug 2023 01:19:21 +0200 Subject: [PATCH 05/19] Update coverage ci --- .github/workflows/coverage.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 64f58d1..29a92a5 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -63,6 +63,9 @@ jobs: lcov -d . -c -o out.info --rc lcov_branch_coverage=1 --no-external lcov -e out.info '*include/ss*hpp' -o filtered.info genhtml --t "SSP Coverage" --legend --demangle-cpp -o html -s --branch-coverage -p $PWD filtered.info + pwd + ls + cat $${github.workspace}} - name: Archive coverage report uses: actions/upload-artifact@v3 From 17c21e260fb69b0b3713aaca6cbff0817c674934 Mon Sep 17 00:00:00 2001 From: ado Date: Tue, 8 Aug 2023 01:25:43 +0200 Subject: [PATCH 06/19] Update coverage ci --- .github/workflows/coverage.yml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 29a92a5..165650a 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -63,18 +63,9 @@ jobs: lcov -d . -c -o out.info --rc lcov_branch_coverage=1 --no-external lcov -e out.info '*include/ss*hpp' -o filtered.info genhtml --t "SSP Coverage" --legend --demangle-cpp -o html -s --branch-coverage -p $PWD filtered.info - pwd - ls - cat $${github.workspace}} - - - name: Archive coverage report - uses: actions/upload-artifact@v3 - with: - name: code-coverage-report - path: ${{github.workspace}}/html - name: Invoke coveralls uses: coverallsapp/github-action@v2 with: github-token: ${{secrets.GITHUB_TOKEN}} - path-to-lcov: ${{github.workspace}}/filtered.info + path-to-lcov: filtered.info From 7d3d02f11dc179bfe8eb3601e700b1a2a4a554ad Mon Sep 17 00:00:00 2001 From: ado Date: Tue, 8 Aug 2023 01:33:59 +0200 Subject: [PATCH 07/19] Update coverage ci --- .github/workflows/coverage.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 165650a..c311f12 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -68,4 +68,3 @@ jobs: uses: coverallsapp/github-action@v2 with: github-token: ${{secrets.GITHUB_TOKEN}} - path-to-lcov: filtered.info From 1d0911ab3ca7748ab0785c4da070e25ade0f0a7a Mon Sep 17 00:00:00 2001 From: ado Date: Tue, 8 Aug 2023 01:46:20 +0200 Subject: [PATCH 08/19] Update coverage ci --- .github/workflows/coverage.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index c311f12..4e1976f 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -68,3 +68,4 @@ jobs: uses: coverallsapp/github-action@v2 with: github-token: ${{secrets.GITHUB_TOKEN}} + file: filtered.info From 49a3a20e681f95589fa8dd6ae8d7b7e44bfa87f0 Mon Sep 17 00:00:00 2001 From: ado Date: Tue, 8 Aug 2023 02:03:42 +0200 Subject: [PATCH 09/19] Update coverage ci --- .github/workflows/coverage.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 4e1976f..e8eb86e 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -69,3 +69,4 @@ jobs: with: github-token: ${{secrets.GITHUB_TOKEN}} file: filtered.info + format: lcov From 6e27f35209e586ac0d6f08dc2fed5b0f34c9b5ec Mon Sep 17 00:00:00 2001 From: ado Date: Tue, 8 Aug 2023 02:08:31 +0200 Subject: [PATCH 10/19] Update coverage ci --- test/CMakeLists.txt | 2 +- test/test_helpers.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e757397..94c3327 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -41,6 +41,6 @@ foreach(name IN ITEMS test_splitter test_parser test_converter test_extractions target_link_libraries("${name}" PRIVATE ssp::ssp fast_float doctest::doctest) target_compile_definitions( - "${name}" PRIVATE DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN _CMAKE_GITHUB_CI) + "${name}" PRIVATE DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN CMAKE_GITHUB_CI) add_test(NAME "${name}" COMMAND "${name}") endforeach() diff --git a/test/test_helpers.hpp b/test/test_helpers.hpp index 0437935..b7f362f 100644 --- a/test/test_helpers.hpp +++ b/test/test_helpers.hpp @@ -6,7 +6,7 @@ #include #include -#ifdef _CMAKE_GITHUB_CI +#ifdef CMAKE_GITHUB_CI #include #else #include From 5ac506d8f0fa71bca6efcac7da2a90d4d4f19969 Mon Sep 17 00:00:00 2001 From: ado Date: Tue, 8 Aug 2023 10:01:47 +0200 Subject: [PATCH 11/19] Add coverage-ci badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 85b5877..ced5f3e 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ [![windows-msys2-clang](https://github.com/red0124/ssp/workflows/win-msys2-clang-ci/badge.svg)](https://github.com/red0124/ssp/actions/workflows/win-msys2-clang.yml) [![win-msvc-ci](https://github.com/red0124/ssp/workflows/win-msvc-ci/badge.svg)](https://github.com/red0124/ssp/actions/workflows/win-msvc.yml) [![single-header-ci](https://github.com/red0124/ssp/workflows/single-header-ci/badge.svg)](https://github.com/red0124/ssp/actions/workflows/single-header.yml) +[![coverage](https://coveralls.io/repos/github/red0124/ssp/badge.svg)](https://coveralls.io/github/red0124/ssp) A header only "csv" parser which is fast and versatile with modern C++ api. Requires compiler with C++17 support. [Can also be used to convert strings to specific types.](#the-converter) From 1c6eacad30d6e510a875d619af6f6026bdb1e5d1 Mon Sep 17 00:00:00 2001 From: ado Date: Tue, 8 Aug 2023 10:05:07 +0200 Subject: [PATCH 12/19] Update coverage-ci badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ced5f3e..230cd61 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ [![windows-msys2-clang](https://github.com/red0124/ssp/workflows/win-msys2-clang-ci/badge.svg)](https://github.com/red0124/ssp/actions/workflows/win-msys2-clang.yml) [![win-msvc-ci](https://github.com/red0124/ssp/workflows/win-msvc-ci/badge.svg)](https://github.com/red0124/ssp/actions/workflows/win-msvc.yml) [![single-header-ci](https://github.com/red0124/ssp/workflows/single-header-ci/badge.svg)](https://github.com/red0124/ssp/actions/workflows/single-header.yml) -[![coverage](https://coveralls.io/repos/github/red0124/ssp/badge.svg)](https://coveralls.io/github/red0124/ssp) +[![coverage](https://coveralls.io/repos/github/red0124/ssp/badge.svg?branch=feature/coverage_ci)](https://coveralls.io/github/red0124/ssp?branch=feature/coverage_ci) A header only "csv" parser which is fast and versatile with modern C++ api. Requires compiler with C++17 support. [Can also be used to convert strings to specific types.](#the-converter) From e316558a7b41cd9df7d6e8c9091f29b110602e45 Mon Sep 17 00:00:00 2001 From: ado Date: Tue, 8 Aug 2023 11:07:26 +0200 Subject: [PATCH 13/19] Add parser test for invalid rows with header --- test/test_parser.cpp | 98 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/test/test_parser.cpp b/test/test_parser.cpp index 94e6c4c..e7089ff 100644 --- a/test/test_parser.cpp +++ b/test/test_parser.cpp @@ -1491,6 +1491,104 @@ TEST_CASE("parser test invalid header fields usage") { 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"}; + { + std::ofstream out{f.name}; + out << "Int,String,Double" << std::endl; + out << "1,line1,2.34" << std::endl; + out << "2,line2" << std::endl; + out << "3,line3,67.8" << std::endl; + out << "4,line4,67.8,9" << std::endl; + out << "5,line5,9.10" << std::endl; + out << "six,line6,10.11" << std::endl; + } + + { + ss::parser p{f.name}; + + p.use_fields("Int", "String", "Double"); + using data = std::tuple; + std::vector i; + + CHECK(p.valid()); + + while (!p.eof()) { + try { + const auto& t = p.template get_next(); + if (p.valid()) { + i.push_back(t); + } + } catch (const ss::exception&) { + continue; + } + } + + std::vector expected = {{1, "line1", 2.34}, + {3, "line3", 67.8}, + {5, "line5", 9.10}}; + CHECK_EQ(i, expected); + } + + { + ss::parser p{f.name}; + + p.use_fields("Double", "Int"); + using data = std::tuple; + std::vector i; + + CHECK(p.valid()); + + while (!p.eof()) { + try { + const auto& t = p.template get_next(); + if (p.valid()) { + i.push_back(t); + } + } catch (const ss::exception&) { + continue; + } + } + + std::vector expected = {{2.34, 1}, {67.8, 3}, {9.10, 5}}; + CHECK_EQ(i, expected); + } + + { + ss::parser p{f.name}; + + p.use_fields("String", "Double"); + using data = std::tuple; + std::vector i; + + CHECK(p.valid()); + + while (!p.eof()) { + try { + const auto& t = p.template get_next(); + if (p.valid()) { + i.push_back(t); + } + } catch (const ss::exception&) { + continue; + } + } + + std::vector expected = {{"line1", 2.34}, + {"line3", 67.8}, + {"line5", 9.10}, + {"line6", 10.11}}; + CHECK_EQ(i, expected); + } +} + +TEST_CASE("parser 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_impl(const std::vector& data) { unique_file_name f{"test_parser"}; From b7e5dd28b895409783f7734521bc90d37c8f1119 Mon Sep 17 00:00:00 2001 From: ado Date: Tue, 8 Aug 2023 12:10:20 +0200 Subject: [PATCH 14/19] Update splitter tests --- include/ss/converter.hpp | 1 - test/test_splitter.cpp | 47 +++++++++++++++++++++++++++++++--------- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/include/ss/converter.hpp b/include/ss/converter.hpp index 8fce18d..c91fee7 100644 --- a/include/ss/converter.hpp +++ b/include/ss/converter.hpp @@ -379,7 +379,6 @@ private: return extract_tuple(elems); } - // do not know how to specialize by return type :( template no_void_validator_tup_t> convert_impl( const split_data& elems, const std::tuple*) { diff --git a/test/test_splitter.cpp b/test/test_splitter.cpp index bf3ba16..201de9d 100644 --- a/test/test_splitter.cpp +++ b/test/test_splitter.cpp @@ -775,6 +775,23 @@ TEST_CASE("splitter test resplit unterminated quote") { CHECK_EQ(words(vec), expected); } } + + { + ss::converter, ss::escape<'\\'>, ss::multiline> c; + auto& s = c.splitter; + auto vec = expect_unterminated_quote(s, R"("just\"some","ra)"); + std::vector expected{"just\"some"}; + auto w = words(vec); + w.pop_back(); + CHECK_EQ(w, expected); + REQUIRE(s.unterminated_quote()); + { + auto new_line = buff.append(R"(n,dom",str\"ings)"); + // invalid resplit size + vec = c.resplit(new_line, 4); + CHECK(!s.valid()); + } + } } TEST_CASE("splitter test resplit unterminated quote with exceptions") { @@ -1040,47 +1057,57 @@ TEST_CASE("splitter test resplit unterminated quote with exceptions") { } } -TEST_CASE("splitter test invalid splits") { - ss::converter, ss::trim<' '>, - ss::escape<'\\'>> - c; +template +void test_invalid_splits() { + ss::converter, ss::trim<' '>, ss::escape<'\\'>, Ts...> c; auto& s = c.splitter; + auto check_error_msg = [&] { + if constexpr (ss::setup::string_error) { + CHECK_FALSE(s.error_msg().empty()); + } + }; + // empty delimiter s.split(buff("some,random,strings"), ""); CHECK_FALSE(s.valid()); CHECK_FALSE(s.unterminated_quote()); - CHECK_FALSE(s.error_msg().empty()); + check_error_msg(); // mismatched delimiter s.split(buff(R"(some,"random,"strings")")); CHECK_FALSE(s.valid()); CHECK_FALSE(s.unterminated_quote()); - CHECK_FALSE(s.error_msg().empty()); + check_error_msg(); // unterminated escape s.split(buff(R"(some,random,strings\)")); CHECK_FALSE(s.valid()); CHECK_FALSE(s.unterminated_quote()); - CHECK_FALSE(s.error_msg().empty()); + check_error_msg(); // unterminated escape s.split(buff(R"(some,random,"strings\)")); CHECK_FALSE(s.valid()); CHECK_FALSE(s.unterminated_quote()); - CHECK_FALSE(s.error_msg().empty()); + check_error_msg(); // unterminated quote s.split(buff("some,random,\"strings")); CHECK_FALSE(s.valid()); CHECK(s.unterminated_quote()); - CHECK_FALSE(s.error_msg().empty()); + check_error_msg(); // invalid resplit char new_line[] = "some"; c.resplit(new_line, strlen(new_line)); CHECK_FALSE(s.valid()); - CHECK_FALSE(s.error_msg().empty()); + check_error_msg(); +} + +TEST_CASE("splitter test invalid splits") { + test_invalid_splits(); + test_invalid_splits(); } TEST_CASE("splitter test invalid splits with exceptions") { From 848689451cbf871ce0e422fdcf13d3d994443646 Mon Sep 17 00:00:00 2001 From: ado Date: Tue, 8 Aug 2023 12:29:04 +0200 Subject: [PATCH 15/19] Add big float extraction test --- test/test_extractions.cpp | 5 +++++ test/test_extractions_without_fast_float.cpp | 13 +++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/test/test_extractions.cpp b/test/test_extractions.cpp index b8dd858..f1ba009 100644 --- a/test/test_extractions.cpp +++ b/test/test_extractions.cpp @@ -63,6 +63,11 @@ TEST_CASE("extract test functions for numbers with invalid inputs") { // random input for float CHECK_INVALID_CONVERSION("xxx1", float); + // number too big + CHECK_INVALID_CONVERSION((std::string{20, '1'} + "." + + std::string{20, '2'}), + double); + // random input for int CHECK_INVALID_CONVERSION("xxx1", int); diff --git a/test/test_extractions_without_fast_float.cpp b/test/test_extractions_without_fast_float.cpp index 3f5cd40..fbb9558 100644 --- a/test/test_extractions_without_fast_float.cpp +++ b/test/test_extractions_without_fast_float.cpp @@ -9,11 +9,11 @@ TEST_CASE( CHECK_FLOATING_CONVERSION(123.456, float); CHECK_FLOATING_CONVERSION(123.456, double); - CHECK_FLOATING_CONVERSION(69, float); - CHECK_FLOATING_CONVERSION(69, double); + CHECK_FLOATING_CONVERSION(59, float); + CHECK_FLOATING_CONVERSION(59, double); - CHECK_FLOATING_CONVERSION(420., float); - CHECK_FLOATING_CONVERSION(420., double); + CHECK_FLOATING_CONVERSION(4210., float); + CHECK_FLOATING_CONVERSION(4210., double); CHECK_FLOATING_CONVERSION(0.123, float); CHECK_FLOATING_CONVERSION(0.123, double); @@ -29,6 +29,11 @@ TEST_CASE("extract test functions for numbers with invalid inputs without fast " // random input for float CHECK_INVALID_CONVERSION("xxx1", float); + + // number too big + CHECK_INVALID_CONVERSION((std::string{20, '1'} + "." + + std::string{20, '2'}), + double); } TEST_CASE("extract test functions for std::variant without fast float") { From 32cbfe1d17159bbe0646a66021607dd314c7ceda Mon Sep 17 00:00:00 2001 From: ado Date: Tue, 8 Aug 2023 12:43:56 +0200 Subject: [PATCH 16/19] Update extraction tests, update coverage-ci, update ssp.hpp --- .github/workflows/coverage.yml | 3 +-- ssp.hpp | 1 - test/CMakeLists.txt | 2 -- test/test_extractions.cpp | 4 ++-- test/test_extractions_without_fast_float.cpp | 4 ++-- 5 files changed, 5 insertions(+), 9 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index e8eb86e..b0a8210 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -49,7 +49,7 @@ jobs: apt install -y gcovr lcov - name: Configure - run: cmake -S test -B build -D CMAKE_BUILD_TYPE=Debug + run: cmake -S test -B build -D CMAKE_BUILD_TYPE=Debug -D CMAKE_CXX_FLAGS="-Wall -fprofile-arcs -ftest-coverage --coverage" - name: Build run: cmake --build build -j ${{steps.cores.outputs.count}} @@ -62,7 +62,6 @@ jobs: run: | lcov -d . -c -o out.info --rc lcov_branch_coverage=1 --no-external lcov -e out.info '*include/ss*hpp' -o filtered.info - genhtml --t "SSP Coverage" --legend --demangle-cpp -o html -s --branch-coverage -p $PWD filtered.info - name: Invoke coveralls uses: coverallsapp/github-action@v2 diff --git a/ssp.hpp b/ssp.hpp index e6fbdba..d50d3c3 100644 --- a/ssp.hpp +++ b/ssp.hpp @@ -2017,7 +2017,6 @@ private: return extract_tuple(elems); } - // do not know how to specialize by return type :( template no_void_validator_tup_t> convert_impl( const split_data& elems, const std::tuple*) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 94c3327..de6b612 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,8 +12,6 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") target_compile_options(ssp INTERFACE -Wall -Wextra) endif() -set(CMAKE_CXX_FLAGS "-Wall -fprofile-arcs -ftest-coverage --coverage") - if (MSVC) add_compile_options(/bigobj) elseif (MINGW) diff --git a/test/test_extractions.cpp b/test/test_extractions.cpp index f1ba009..8b2b64b 100644 --- a/test/test_extractions.cpp +++ b/test/test_extractions.cpp @@ -64,8 +64,8 @@ TEST_CASE("extract test functions for numbers with invalid inputs") { CHECK_INVALID_CONVERSION("xxx1", float); // number too big - CHECK_INVALID_CONVERSION((std::string{20, '1'} + "." + - std::string{20, '2'}), + CHECK_INVALID_CONVERSION((std::string{40, '1'} + "." + + std::string{40, '2'}), double); // random input for int diff --git a/test/test_extractions_without_fast_float.cpp b/test/test_extractions_without_fast_float.cpp index fbb9558..7e6660c 100644 --- a/test/test_extractions_without_fast_float.cpp +++ b/test/test_extractions_without_fast_float.cpp @@ -31,8 +31,8 @@ TEST_CASE("extract test functions for numbers with invalid inputs without fast " CHECK_INVALID_CONVERSION("xxx1", float); // number too big - CHECK_INVALID_CONVERSION((std::string{20, '1'} + "." + - std::string{20, '2'}), + CHECK_INVALID_CONVERSION((std::string{40, '1'} + "." + + std::string{40, '2'}), double); } From b9d2c2aad9e4c40286d01aa601d7e0e7cb6f40f8 Mon Sep 17 00:00:00 2001 From: ado Date: Tue, 8 Aug 2023 12:57:15 +0200 Subject: [PATCH 17/19] Fix big number extraction test --- test/test_extractions.cpp | 4 ++-- test/test_extractions_without_fast_float.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_extractions.cpp b/test/test_extractions.cpp index 8b2b64b..2ed550d 100644 --- a/test/test_extractions.cpp +++ b/test/test_extractions.cpp @@ -64,8 +64,8 @@ TEST_CASE("extract test functions for numbers with invalid inputs") { CHECK_INVALID_CONVERSION("xxx1", float); // number too big - CHECK_INVALID_CONVERSION((std::string{40, '1'} + "." + - std::string{40, '2'}), + CHECK_INVALID_CONVERSION((std::string(40, '1') + "." + + std::string(40, '2')), double); // random input for int diff --git a/test/test_extractions_without_fast_float.cpp b/test/test_extractions_without_fast_float.cpp index 7e6660c..e3871a6 100644 --- a/test/test_extractions_without_fast_float.cpp +++ b/test/test_extractions_without_fast_float.cpp @@ -31,8 +31,8 @@ TEST_CASE("extract test functions for numbers with invalid inputs without fast " CHECK_INVALID_CONVERSION("xxx1", float); // number too big - CHECK_INVALID_CONVERSION((std::string{40, '1'} + "." + - std::string{40, '2'}), + CHECK_INVALID_CONVERSION((std::string(40, '1') + "." + + std::string(40, '2')), double); } From 672b89b213d5179d73c03e536e8938e5821ba1b0 Mon Sep 17 00:00:00 2001 From: ado Date: Tue, 8 Aug 2023 14:11:51 +0200 Subject: [PATCH 18/19] Add ability to convert larger numbers without fast_float, write unit tests --- include/ss/extract.hpp | 21 ++++++++++++------- test/test_extractions.cpp | 22 +++++++++++++++----- test/test_extractions_without_fast_float.cpp | 22 +++++++++++++++----- test/test_helpers.hpp | 14 ++++++++++++- 4 files changed, 60 insertions(+), 19 deletions(-) diff --git a/include/ss/extract.hpp b/include/ss/extract.hpp index 246ea9a..4862f3f 100644 --- a/include/ss/extract.hpp +++ b/include/ss/extract.hpp @@ -1,13 +1,13 @@ #pragma once #include "type_traits.hpp" +#include #include #include #include #include #include #include -#include #ifndef SSP_DISABLE_FAST_FLOAT #include @@ -42,17 +42,24 @@ std::enable_if_t, std::optional> to_num( template std::enable_if_t, std::optional> to_num( const char* const begin, const char* const end) { + static_assert(!std::is_same_v, + "Conversion to long double is disabled"); + constexpr static auto buff_max = 64; - char buff[buff_max]; + char short_buff[buff_max]; size_t string_range = std::distance(begin, end); + std::string long_buff; + char* buff; if (string_range > buff_max) { - return std::nullopt; + long_buff = std::string{begin, end}; + buff = long_buff.data(); + } else { + buff = short_buff; + buff[string_range] = '\0'; + std::copy_n(begin, string_range, buff); } - std::copy_n(begin, string_range, buff); - buff[string_range] = '\0'; - T ret; char* parse_end = nullptr; @@ -60,8 +67,6 @@ std::enable_if_t, std::optional> to_num( ret = std::strtof(buff, &parse_end); } else if constexpr (std::is_same_v) { ret = std::strtod(buff, &parse_end); - } else if constexpr (std::is_same_v) { - ret = std::strtold(buff, &parse_end); } if (parse_end != buff + string_range) { diff --git a/test/test_extractions.cpp b/test/test_extractions.cpp index 2ed550d..4cab853 100644 --- a/test/test_extractions.cpp +++ b/test/test_extractions.cpp @@ -63,11 +63,6 @@ TEST_CASE("extract test functions for numbers with invalid inputs") { // random input for float CHECK_INVALID_CONVERSION("xxx1", float); - // number too big - CHECK_INVALID_CONVERSION((std::string(40, '1') + "." + - std::string(40, '2')), - double); - // random input for int CHECK_INVALID_CONVERSION("xxx1", int); @@ -280,3 +275,20 @@ TEST_CASE("extract test functions for std::variant") { } } } + +TEST_CASE("extract test with long number string") { + { + std::string string_num = + std::string(20, '1') + "." + std::string(20, '2'); + + CHECK_FLOATING_CONVERSION_LONG_NUMBER(string_num, float, stof); + CHECK_FLOATING_CONVERSION_LONG_NUMBER(string_num, double, stod); + } + + { + std::string string_num = + std::string(50, '1') + "." + std::string(50, '2'); + + CHECK_FLOATING_CONVERSION_LONG_NUMBER(string_num, double, stod); + } +} diff --git a/test/test_extractions_without_fast_float.cpp b/test/test_extractions_without_fast_float.cpp index e3871a6..2c9a984 100644 --- a/test/test_extractions_without_fast_float.cpp +++ b/test/test_extractions_without_fast_float.cpp @@ -29,11 +29,6 @@ TEST_CASE("extract test functions for numbers with invalid inputs without fast " // random input for float CHECK_INVALID_CONVERSION("xxx1", float); - - // number too big - CHECK_INVALID_CONVERSION((std::string(40, '1') + "." + - std::string(40, '2')), - double); } TEST_CASE("extract test functions for std::variant without fast float") { @@ -135,3 +130,20 @@ TEST_CASE("extract test functions for std::variant without fast float") { } } } + +TEST_CASE("extract test with long number string without fast float") { + { + std::string string_num = + std::string(20, '1') + "." + std::string(20, '2'); + + CHECK_FLOATING_CONVERSION_LONG_NUMBER(string_num, float, stof); + CHECK_FLOATING_CONVERSION_LONG_NUMBER(string_num, double, stod); + } + + { + std::string string_num = + std::string(50, '1') + "." + std::string(50, '2'); + + CHECK_FLOATING_CONVERSION_LONG_NUMBER(string_num, double, stod); + } +} diff --git a/test/test_helpers.hpp b/test/test_helpers.hpp index b7f362f..15d20f6 100644 --- a/test/test_helpers.hpp +++ b/test/test_helpers.hpp @@ -1,10 +1,10 @@ #pragma once #include +#include #include #include #include #include -#include #ifdef CMAKE_GITHUB_CI #include @@ -75,6 +75,18 @@ struct unique_file_name { CHECK_LT(std::abs(t.value() - type(-input)), eps); \ } +#define CHECK_FLOATING_CONVERSION_LONG_NUMBER(STRING_NUMBER, TYPE, CONVERTER) \ + { \ + auto begin = STRING_NUMBER.c_str(); \ + auto end = begin + STRING_NUMBER.size(); \ + \ + auto number = ss::to_num(begin, end); \ + REQUIRE(number.has_value()); \ + \ + auto expected_number = CONVERTER(STRING_NUMBER); \ + CHECK_EQ(number.value(), expected_number); \ + } + #define CHECK_INVALID_CONVERSION(input, type) \ { \ std::string s = input; \ From 8b72deb1ed573b249040bd109ccb91d4b4da63da Mon Sep 17 00:00:00 2001 From: ado Date: Tue, 8 Aug 2023 14:38:49 +0200 Subject: [PATCH 19/19] Remove some comments, update README, update ssp.hpp --- README.md | 2 +- include/ss/extract.hpp | 1 - include/ss/parser.hpp | 1 - ssp.hpp | 21 ++++++++++++--------- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 230cd61..7a4c2de 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ [![windows-msys2-clang](https://github.com/red0124/ssp/workflows/win-msys2-clang-ci/badge.svg)](https://github.com/red0124/ssp/actions/workflows/win-msys2-clang.yml) [![win-msvc-ci](https://github.com/red0124/ssp/workflows/win-msvc-ci/badge.svg)](https://github.com/red0124/ssp/actions/workflows/win-msvc.yml) [![single-header-ci](https://github.com/red0124/ssp/workflows/single-header-ci/badge.svg)](https://github.com/red0124/ssp/actions/workflows/single-header.yml) -[![coverage](https://coveralls.io/repos/github/red0124/ssp/badge.svg?branch=feature/coverage_ci)](https://coveralls.io/github/red0124/ssp?branch=feature/coverage_ci) +[![coverage](https://coveralls.io/repos/github/red0124/ssp/badge.svg?branch=master/coverage_ci)](https://coveralls.io/github/red0124/ssp?branch=master/coverage_ci) A header only "csv" parser which is fast and versatile with modern C++ api. Requires compiler with C++17 support. [Can also be used to convert strings to specific types.](#the-converter) diff --git a/include/ss/extract.hpp b/include/ss/extract.hpp index 4862f3f..a26828d 100644 --- a/include/ss/extract.hpp +++ b/include/ss/extract.hpp @@ -16,7 +16,6 @@ #include #endif -// TODO try from_chars for integer conversions namespace ss { //////////////// diff --git a/include/ss/parser.hpp b/include/ss/parser.hpp index 54126bb..db80dd3 100644 --- a/include/ss/parser.hpp +++ b/include/ss/parser.hpp @@ -1,6 +1,5 @@ #pragma once -// TODO add single header tests #include "common.hpp" #include "converter.hpp" #include "exception.hpp" diff --git a/ssp.hpp b/ssp.hpp index d50d3c3..cb0bc2e 100644 --- a/ssp.hpp +++ b/ssp.hpp @@ -1465,7 +1465,6 @@ public: #else #endif -// TODO try from_chars for integer conversions namespace ss { //////////////// @@ -1491,17 +1490,24 @@ std::enable_if_t, std::optional> to_num( template std::enable_if_t, std::optional> to_num( const char* const begin, const char* const end) { + static_assert(!std::is_same_v, + "Conversion to long double is disabled"); + constexpr static auto buff_max = 64; - char buff[buff_max]; + char short_buff[buff_max]; size_t string_range = std::distance(begin, end); + std::string long_buff; + char* buff; if (string_range > buff_max) { - return std::nullopt; + long_buff = std::string{begin, end}; + buff = long_buff.data(); + } else { + buff = short_buff; + buff[string_range] = '\0'; + std::copy_n(begin, string_range, buff); } - std::copy_n(begin, string_range, buff); - buff[string_range] = '\0'; - T ret; char* parse_end = nullptr; @@ -1509,8 +1515,6 @@ std::enable_if_t, std::optional> to_num( ret = std::strtof(buff, &parse_end); } else if constexpr (std::is_same_v) { ret = std::strtod(buff, &parse_end); - } else if constexpr (std::is_same_v) { - ret = std::strtold(buff, &parse_end); } if (parse_end != buff + string_range) { @@ -2132,7 +2136,6 @@ private: } /* ss */ -// TODO add single header tests namespace ss {