Skip to content

Commit 029a299

Browse files
committed
More unit tests
1 parent ea043de commit 029a299

6 files changed

Lines changed: 64 additions & 28 deletions

File tree

.github/workflows/codeql.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ jobs:
4747
cmake .. \
4848
-DCMAKE_BUILD_TYPE=Release \
4949
-DCSV_CXX_STANDARD=20 \
50+
-DCSV_BUILD_PROGRAMS=OFF \
51+
-DCSV_BUILD_TESTS=OFF \
52+
-DBUILD_PYTHON=OFF \
5053
-DCMAKE_CXX_COMPILER=g++ \
5154
-DCMAKE_C_COMPILER=gcc
5255

CMakeLists.txt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ if (CSV_BUILD_PROGRAMS)
104104
add_subdirectory("programs")
105105
endif()
106106

107+
## Tests
108+
option(CSV_BUILD_TESTS "Allow to disable building of tests" ON)
109+
107110
## Developer settings
108111
if (CSV_DEVELOPER)
109112
# Allow for performance profiling
@@ -180,6 +183,8 @@ if (CSV_DEVELOPER)
180183
endif()
181184

182185
## Tests
183-
enable_testing()
184-
add_subdirectory("tests")
186+
if (CSV_BUILD_TESTS)
187+
enable_testing()
188+
add_subdirectory("tests")
189+
endif()
185190
endif()

include/internal/basic_csv_parser.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace csv {
1313
namespace internals {
14+
#if defined(__EMSCRIPTEN__)
1415
// Opens the file and delegates to the template overload to avoid duplicating the read/resize logic.
1516
CSV_INLINE std::string get_csv_head_stream(csv::string_view filename) {
1617
std::ifstream infile(std::string(filename), std::ios::binary);
@@ -19,6 +20,7 @@ namespace csv {
1920
}
2021
return get_csv_head_stream(infile);
2122
}
23+
#endif
2224

2325
#if !defined(__EMSCRIPTEN__)
2426
CSV_INLINE std::pair<std::string, size_t> get_csv_head_mmap(csv::string_view filename) {

include/internal/basic_csv_parser.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,11 @@ namespace csv {
123123
return buf;
124124
}
125125

126+
#if defined(__EMSCRIPTEN__)
127+
/** Open a file-backed source and read the first 500KB through the stream path. */
128+
CSV_INLINE std::string get_csv_head_stream(csv::string_view filename);
129+
#endif
130+
126131
#if !defined(__EMSCRIPTEN__)
127132
/** Read the first 500KB from a filename using mmap.
128133
* Also returns the total file size so callers avoid a second mmap open.

tests/test_error_handling.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,53 @@ TEST_CASE("Worker thread exceptions propagate to main thread", "[error_handling]
117117
}
118118
}
119119

120+
/* Ensure reading empty CSVs does not cause errors.
121+
*
122+
* Reported in:
123+
* - https://github.com/vincentlaucsb/csv-parser/issues/116
124+
* - https://github.com/vincentlaucsb/csv-parser/issues/121
125+
* - empty mmap EOF guard in issue #267
126+
*/
127+
TEST_CASE("Empty CSV does not crash parser entry points", "[error_handling][empty_csv]") {
128+
SECTION("Stream path handles empty inputs and header-only inputs") {
129+
auto csv_string = GENERATE(as<std::string>{},
130+
"A,B,C,D\r\n", // Header row only
131+
"" // No content
132+
);
133+
134+
std::stringstream source(csv_string);
135+
CSVReader reader(source);
136+
REQUIRE(reader.empty());
137+
138+
for (auto& row : reader) {
139+
(void)row;
140+
}
141+
142+
REQUIRE(reader.n_rows() == 0);
143+
}
144+
145+
#ifndef __EMSCRIPTEN__
146+
SECTION("Filename path reports a zero-byte file as an open failure") {
147+
bool caught = false;
148+
std::string error_message;
149+
150+
try {
151+
CSVReader reader("./tests/data/fake_data/empty.csv");
152+
for (auto& row : reader) {
153+
(void)row;
154+
}
155+
}
156+
catch (const std::exception& e) {
157+
caught = true;
158+
error_message = e.what();
159+
}
160+
161+
REQUIRE(caught);
162+
REQUIRE(error_message == "Cannot open file ./tests/data/fake_data/empty.csv");
163+
}
164+
#endif
165+
}
166+
120167
#ifndef __EMSCRIPTEN__
121168
TEST_CASE("Fields at chunk boundaries are not corrupted", "[chunking][data_integrity]") {
122169
SECTION("Large file with known values around chunk boundary") {

tests/test_read_csv.cpp

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -681,32 +681,6 @@ TEST_CASE("Single Column CSV", "[read_single_col_direct]") {
681681
}
682682
}
683683

684-
/* Ensure reading empty CSVs does not cause errors
685-
*
686-
* Reported in:
687-
* - https://github.com/vincentlaucsb/csv-parser/issues/116
688-
* - https://github.com/vincentlaucsb/csv-parser/issues/121
689-
*/
690-
TEST_CASE("Empty CSV", "[read_empty_csv]") {
691-
auto csv_string = GENERATE(as<std::string>{},
692-
"A,B,C,D\r\n", // Header row only
693-
"" // No content
694-
);
695-
696-
SECTION("Read Empty CSV") {
697-
std::stringstream source(csv_string);
698-
CSVReader reader(source);
699-
REQUIRE(reader.empty());
700-
701-
for (auto& row : reader) {
702-
(void)row;
703-
}
704-
705-
// We want to make sure that no exceptions are thrown
706-
REQUIRE(reader.n_rows() == 0);
707-
}
708-
}
709-
710684
// Regression test for issue #149: trailing newline at EOF must not produce a spurious empty row
711685
TEST_CASE("Trailing newline at EOF (stringstream)", "[trailing_newline_stringstream]") {
712686
auto check = [](const std::string& csv_text) {

0 commit comments

Comments
 (0)