From 08a5f3441675c446482b473777cbba53d5191f20 Mon Sep 17 00:00:00 2001 From: Thomas Schmucker Date: Sun, 21 Jun 2026 17:20:13 +0200 Subject: rework: add testing framework --- tests/src/tester.cpp | 476 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 476 insertions(+) create mode 100644 tests/src/tester.cpp (limited to 'tests/src/tester.cpp') diff --git a/tests/src/tester.cpp b/tests/src/tester.cpp new file mode 100644 index 0000000..75f1366 --- /dev/null +++ b/tests/src/tester.cpp @@ -0,0 +1,476 @@ +#include +#include +#include + +#include "csv.h" + +using namespace std; +using namespace testing; + +TEST(TestEmptyObject, StandardInitialization) +{ + csv_t csv{}; + ASSERT_THAT(csv_nfields(&csv), Eq(0)); +} + +TEST(TestEmptyObject, ExplicitInitialization) +{ + csv_t csv; + csv_init(&csv); + + ASSERT_THAT(csv_nfields(&csv), Eq(0)); +} + +class TestLineEndingsBase : public Test { + using file_ptr = unique_ptr; + +public: + explicit TestLineEndingsBase(string_view data) + : data_{ data } + , file_{ fmemopen(data_.data(), data_.size(), "r"), &fclose } + { + csv_init(&csv_); + } + + TestLineEndingsBase(string_view data, csv_options_t &csv_options) + : data_{ data } + , file_{ fmemopen(data_.data(), data_.size(), "r"), &fclose } + { + csv_init_opt(&csv_, &csv_options); + } + + ~TestLineEndingsBase() override + { + csv_cleanup(&csv_); + } + + TestLineEndingsBase(const TestLineEndingsBase &) = delete; + TestLineEndingsBase(TestLineEndingsBase &&) = delete; + void operator=(const TestLineEndingsBase &) = delete; + void operator=(TestLineEndingsBase &&) = delete; + + csv_t csv_{}; + string data_; + file_ptr file_; +}; + +class TestLineEndings : public TestLineEndingsBase { +protected: + TestLineEndings() + : TestLineEndingsBase("A,B,C\r\nD,E,F\n") + { + } +}; + +TEST_F(TestLineEndings, Part1) +{ + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(3)); + EXPECT_THAT(csv_nfields(&csv_), Eq(3)); + EXPECT_STREQ(csv_field(&csv_, 0), "A"); + EXPECT_STREQ(csv_field(&csv_, 1), "B"); + EXPECT_STREQ(csv_field(&csv_, 2), "C"); + + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(3)); + EXPECT_THAT(csv_nfields(&csv_), Eq(3)); + EXPECT_STREQ(csv_field(&csv_, 0), "D"); + EXPECT_STREQ(csv_field(&csv_, 1), "E"); + EXPECT_STREQ(csv_field(&csv_, 2), "F"); + + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(0)); +} + +class TestEmptyLinesWithDifferentLineEndings : public TestLineEndingsBase { +protected: + TestEmptyLinesWithDifferentLineEndings() + : TestLineEndingsBase("\r\n\n") + { + } +}; + +TEST_F(TestEmptyLinesWithDifferentLineEndings, Part1) +{ + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(1)); + EXPECT_THAT(csv_nfields(&csv_), Eq(1)); + EXPECT_STREQ(csv_field(&csv_, 0), ""); + + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(1)); + EXPECT_THAT(csv_nfields(&csv_), Eq(1)); + EXPECT_STREQ(csv_field(&csv_, 0), ""); + + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(0)); +} + +class TestEmptyFieldsWithDifferentLineEndings : public TestLineEndingsBase { + // NOLINTBEGIN + constexpr static const char *data = "\n" // L1 + "\r" // L2 + "\r\n" // L3 + "\"\"\n" // L4 + "\"\"\r" // L5 + "\"\"\r\n" // L6 + ",\n" // L7 + ",\r" // L8 + ",\r\n" // L9 + "\"\",\"\"\n" // L10 + "\"\",\"\"\r" // L11 + "\"\",\"\"\r\n" // L12 + ",,\n" // L13 + ",,\r" // L14 + ",,\r\n" // L15 + "\"\",\"\",\n" // L16 + "\"\",\"\",\r" // L17 + "\"\",\"\",\r\n" // L18 + ",,"; // L19 + + // NOLINTEND +protected: + TestEmptyFieldsWithDifferentLineEndings() + : TestLineEndingsBase(data) + { + } +}; + +TEST_F(TestEmptyFieldsWithDifferentLineEndings, Part1) +{ + // Line 1 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(1)); + EXPECT_THAT(csv_nfields(&csv_), Eq(1)); + EXPECT_STREQ(csv_field(&csv_, 0), ""); + + // Line 2 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(1)); + EXPECT_THAT(csv_nfields(&csv_), Eq(1)); + EXPECT_STREQ(csv_field(&csv_, 0), ""); + + // Line 3 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(1)); + EXPECT_THAT(csv_nfields(&csv_), Eq(1)); + EXPECT_STREQ(csv_field(&csv_, 0), ""); + + // Line 4 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(1)); + EXPECT_THAT(csv_nfields(&csv_), Eq(1)); + EXPECT_STREQ(csv_field(&csv_, 0), ""); + + // Line 5 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(1)); + EXPECT_THAT(csv_nfields(&csv_), Eq(1)); + EXPECT_STREQ(csv_field(&csv_, 0), ""); + + // Line 6 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(1)); + EXPECT_THAT(csv_nfields(&csv_), Eq(1)); + EXPECT_STREQ(csv_field(&csv_, 0), ""); + + // Line 7 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(2)); + EXPECT_THAT(csv_nfields(&csv_), Eq(2)); + EXPECT_STREQ(csv_field(&csv_, 0), ""); + EXPECT_STREQ(csv_field(&csv_, 1), ""); + + // Line 8 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(2)); + EXPECT_THAT(csv_nfields(&csv_), Eq(2)); + EXPECT_STREQ(csv_field(&csv_, 0), ""); + EXPECT_STREQ(csv_field(&csv_, 1), ""); + + // Line 9 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(2)); + EXPECT_THAT(csv_nfields(&csv_), Eq(2)); + EXPECT_STREQ(csv_field(&csv_, 0), ""); + EXPECT_STREQ(csv_field(&csv_, 1), ""); + + // Line 10 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(2)); + EXPECT_THAT(csv_nfields(&csv_), Eq(2)); + EXPECT_STREQ(csv_field(&csv_, 0), ""); + EXPECT_STREQ(csv_field(&csv_, 1), ""); + + // Line 11 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(2)); + EXPECT_THAT(csv_nfields(&csv_), Eq(2)); + EXPECT_STREQ(csv_field(&csv_, 0), ""); + EXPECT_STREQ(csv_field(&csv_, 1), ""); + + // Line 12 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(2)); + EXPECT_THAT(csv_nfields(&csv_), Eq(2)); + EXPECT_STREQ(csv_field(&csv_, 0), ""); + EXPECT_STREQ(csv_field(&csv_, 1), ""); + + // Line 13 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(3)); + EXPECT_THAT(csv_nfields(&csv_), Eq(3)); + EXPECT_STREQ(csv_field(&csv_, 0), ""); + EXPECT_STREQ(csv_field(&csv_, 1), ""); + EXPECT_STREQ(csv_field(&csv_, 2), ""); + + // Line 14 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(3)); + EXPECT_THAT(csv_nfields(&csv_), Eq(3)); + EXPECT_STREQ(csv_field(&csv_, 0), ""); + EXPECT_STREQ(csv_field(&csv_, 1), ""); + EXPECT_STREQ(csv_field(&csv_, 2), ""); + + // Line 15 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(3)); + EXPECT_THAT(csv_nfields(&csv_), Eq(3)); + EXPECT_STREQ(csv_field(&csv_, 0), ""); + EXPECT_STREQ(csv_field(&csv_, 1), ""); + EXPECT_STREQ(csv_field(&csv_, 2), ""); + + // Line 16 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(3)); + EXPECT_THAT(csv_nfields(&csv_), Eq(3)); + EXPECT_STREQ(csv_field(&csv_, 0), ""); + EXPECT_STREQ(csv_field(&csv_, 1), ""); + EXPECT_STREQ(csv_field(&csv_, 2), ""); + + // Line 17 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(3)); + EXPECT_THAT(csv_nfields(&csv_), Eq(3)); + EXPECT_STREQ(csv_field(&csv_, 0), ""); + EXPECT_STREQ(csv_field(&csv_, 1), ""); + EXPECT_STREQ(csv_field(&csv_, 2), ""); + + // Line 18 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(3)); + EXPECT_THAT(csv_nfields(&csv_), Eq(3)); + EXPECT_STREQ(csv_field(&csv_, 0), ""); + EXPECT_STREQ(csv_field(&csv_, 1), ""); + EXPECT_STREQ(csv_field(&csv_, 2), ""); + + // Line 19 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(3)); + EXPECT_THAT(csv_nfields(&csv_), Eq(3)); + EXPECT_STREQ(csv_field(&csv_, 0), ""); + EXPECT_STREQ(csv_field(&csv_, 1), ""); + EXPECT_STREQ(csv_field(&csv_, 2), ""); + + // EOF + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(0)); + EXPECT_THAT(csv_nfields(&csv_), Eq(0)); +} + +class TestFieldsWithDifferentLineEndings : public TestLineEndingsBase { + // NOLINTBEGIN + static constexpr const char *data = "A\n" // L1 + "A\r" // L2 + "A\r\n" // L3 + "\"A\"\n" // L4 + "\"A\"\r" // L5 + "\"A\"\r\n" // L6 + "A,B\n" // L7 + "A,B\r" // L8 + "A,B\r\n" // L9 + "\"A\",\"B\"\n" // L10 + "\"A\",\"B\"\r" // L11 + "\"A\",\"B\"\r\n" // L12 + "A,B,C\n" // L13 + "A,B,C\r" // L14 + "A,B,C\r\n" // L15 + "\"A\",\"B\",C\n" // L16 + "\"A\",\"B\",C\r" // L17 + "\"A\",\"B\",C\r\n" // L18 + "A,B,C"; // L19 + + // NOLINTEND +protected: + TestFieldsWithDifferentLineEndings() + : TestLineEndingsBase(data) + { + } +}; + +TEST_F(TestFieldsWithDifferentLineEndings, Part1) +{ + // Line 1 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(1)); + EXPECT_THAT(csv_nfields(&csv_), Eq(1)); + EXPECT_STREQ(csv_field(&csv_, 0), "A"); + + // Line 2 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(1)); + EXPECT_THAT(csv_nfields(&csv_), Eq(1)); + EXPECT_STREQ(csv_field(&csv_, 0), "A"); + + // Line 3 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(1)); + EXPECT_THAT(csv_nfields(&csv_), Eq(1)); + EXPECT_STREQ(csv_field(&csv_, 0), "A"); + + // Line 4 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(1)); + EXPECT_THAT(csv_nfields(&csv_), Eq(1)); + EXPECT_STREQ(csv_field(&csv_, 0), "A"); + + // Line 5 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(1)); + EXPECT_THAT(csv_nfields(&csv_), Eq(1)); + EXPECT_STREQ(csv_field(&csv_, 0), "A"); + + // Line 6 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(1)); + EXPECT_THAT(csv_nfields(&csv_), Eq(1)); + EXPECT_STREQ(csv_field(&csv_, 0), "A"); + + // Line 7 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(2)); + EXPECT_THAT(csv_nfields(&csv_), Eq(2)); + EXPECT_STREQ(csv_field(&csv_, 0), "A"); + EXPECT_STREQ(csv_field(&csv_, 1), "B"); + + // Line 8 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(2)); + EXPECT_THAT(csv_nfields(&csv_), Eq(2)); + EXPECT_STREQ(csv_field(&csv_, 0), "A"); + EXPECT_STREQ(csv_field(&csv_, 1), "B"); + + // Line 9 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(2)); + EXPECT_THAT(csv_nfields(&csv_), Eq(2)); + EXPECT_STREQ(csv_field(&csv_, 0), "A"); + EXPECT_STREQ(csv_field(&csv_, 1), "B"); + + // Line 10 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(2)); + EXPECT_THAT(csv_nfields(&csv_), Eq(2)); + EXPECT_STREQ(csv_field(&csv_, 0), "A"); + EXPECT_STREQ(csv_field(&csv_, 1), "B"); + + // Line 11 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(2)); + EXPECT_THAT(csv_nfields(&csv_), Eq(2)); + EXPECT_STREQ(csv_field(&csv_, 0), "A"); + EXPECT_STREQ(csv_field(&csv_, 1), "B"); + + // Line 12 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(2)); + EXPECT_THAT(csv_nfields(&csv_), Eq(2)); + EXPECT_STREQ(csv_field(&csv_, 0), "A"); + EXPECT_STREQ(csv_field(&csv_, 1), "B"); + + // Line 13 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(3)); + EXPECT_THAT(csv_nfields(&csv_), Eq(3)); + EXPECT_STREQ(csv_field(&csv_, 0), "A"); + EXPECT_STREQ(csv_field(&csv_, 1), "B"); + EXPECT_STREQ(csv_field(&csv_, 2), "C"); + + // Line 14 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(3)); + EXPECT_THAT(csv_nfields(&csv_), Eq(3)); + EXPECT_STREQ(csv_field(&csv_, 0), "A"); + EXPECT_STREQ(csv_field(&csv_, 1), "B"); + EXPECT_STREQ(csv_field(&csv_, 2), "C"); + + // Line 15 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(3)); + EXPECT_THAT(csv_nfields(&csv_), Eq(3)); + EXPECT_STREQ(csv_field(&csv_, 0), "A"); + EXPECT_STREQ(csv_field(&csv_, 1), "B"); + EXPECT_STREQ(csv_field(&csv_, 2), "C"); + + // Line 16 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(3)); + EXPECT_THAT(csv_nfields(&csv_), Eq(3)); + EXPECT_STREQ(csv_field(&csv_, 0), "A"); + EXPECT_STREQ(csv_field(&csv_, 1), "B"); + EXPECT_STREQ(csv_field(&csv_, 2), "C"); + + // Line 17 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(3)); + EXPECT_THAT(csv_nfields(&csv_), Eq(3)); + EXPECT_STREQ(csv_field(&csv_, 0), "A"); + EXPECT_STREQ(csv_field(&csv_, 1), "B"); + EXPECT_STREQ(csv_field(&csv_, 2), "C"); + + // Line 18 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(3)); + EXPECT_THAT(csv_nfields(&csv_), Eq(3)); + EXPECT_STREQ(csv_field(&csv_, 0), "A"); + EXPECT_STREQ(csv_field(&csv_, 1), "B"); + EXPECT_STREQ(csv_field(&csv_, 2), "C"); + + // Line 19 + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(3)); + EXPECT_THAT(csv_nfields(&csv_), Eq(3)); + EXPECT_STREQ(csv_field(&csv_, 0), "A"); + EXPECT_STREQ(csv_field(&csv_, 1), "B"); + EXPECT_STREQ(csv_field(&csv_, 2), "C"); + + // EOF + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(0)); + EXPECT_THAT(csv_nfields(&csv_), Eq(0)); +} + +class TestQuotedFields : public TestLineEndingsBase { + // NOLINTBEGIN + static constexpr const char *data = "foo \"baz\" bar,foo \"\"baz\"\" bar,\"foo \"\"baz\"\" bar\",\"foo \"baz\" bar\",\"foo \"\"baz\"\", bar\""; + // NOLINTEND + +protected: + TestQuotedFields() + : TestLineEndingsBase(data) + { + } +}; + +TEST_F(TestQuotedFields, Part1) +{ + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(5)); + EXPECT_THAT(csv_nfields(&csv_), Eq(5)); + + EXPECT_STREQ(csv_field(&csv_, 0), "foo \"baz\" bar"); + EXPECT_STREQ(csv_field(&csv_, 1), "foo \"\"baz\"\" bar"); + EXPECT_STREQ(csv_field(&csv_, 2), "foo \"baz\" bar"); + EXPECT_STREQ(csv_field(&csv_, 3), "foo \"baz\" bar"); + EXPECT_STREQ(csv_field(&csv_, 4), "foo \"baz\", bar"); + + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(0)); + EXPECT_THAT(csv_nfields(&csv_), Eq(0)); +} + +class TestWrongQuotedFields : public TestLineEndingsBase { +protected: + TestWrongQuotedFields() + : TestLineEndingsBase("\"foo") + { + } +}; + +TEST_F(TestWrongQuotedFields, Part1) +{ + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(1)); + EXPECT_THAT(csv_nfields(&csv_), Eq(1)); + + EXPECT_STREQ(csv_field(&csv_, 0), "foo"); + + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(0)); + EXPECT_THAT(csv_nfields(&csv_), Eq(0)); +} + +class TestOutOfRangeError : public TestLineEndingsBase { +protected: + TestOutOfRangeError() + : TestLineEndingsBase(",") + { + csv_options.cb_error = &cb_error; + csv_init_opt(&csv_, &csv_options); + } + + static void cb_error(csv_err_t /*unused*/, void * /*unused*/) + { + throw out_of_range("out of range"); + } + + csv_options_t csv_options{ csv_default_options }; +}; + +TEST_F(TestOutOfRangeError, Part1) +{ + EXPECT_THAT(csv_read(&csv_, file_.get()), Eq(2)); + EXPECT_THAT(csv_nfields(&csv_), Eq(2)); + + EXPECT_THROW({ csv_field(&csv_, 2); }, out_of_range); +} -- cgit v1.3