From 87071200872c2450c947047350132aee493033c1 Mon Sep 17 00:00:00 2001 From: Dominick Allen Date: Thu, 2 Jan 2025 15:11:51 -0600 Subject: Get basic CSV parser operating. --- test/CMakeLists.txt | 1 + test/test_common.cpp | 19 +++++++++++-- test/test_common.hpp | 4 ++- test/test_csv.cpp | 74 +++++++++++++++++++++++++++++++++++++++++++++++++ test/test_directory.cpp | 4 +-- test/test_file.cpp | 41 ++++++++++----------------- test/test_format.cpp | 9 ++++++ test/test_string.cpp | 14 ++++++++++ 8 files changed, 135 insertions(+), 31 deletions(-) create mode 100644 test/test_csv.cpp (limited to 'test') diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1ceca71..0a1a1e7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -63,6 +63,7 @@ endfunction() fud_add_test(test_fud SOURCES test_fud.cpp) fud_add_test(test_allocator SOURCES test_allocator.cpp) fud_add_test(test_assert SOURCES test_assert.cpp) +fud_add_test(test_csv SOURCES test_csv.cpp) # fud_add_test(test_c_file SOURCES test_c_file.cpp) fud_add_test(test_directory SOURCES test_directory.cpp) fud_add_test(test_file SOURCES test_file.cpp) diff --git a/test/test_common.cpp b/test/test_common.cpp index 07a0088..f272dad 100644 --- a/test/test_common.cpp +++ b/test/test_common.cpp @@ -74,7 +74,7 @@ int unlink_cb(const char* fpath, const struct stat* sb_unused, int typeflag, str return retValue; } -FudStatus removeRecursive(const String& path) +FudStatus removeRecursive(StringView path) { if (!path.utf8Valid()) { return FudStatus::Utf8Invalid; @@ -82,6 +82,9 @@ FudStatus removeRecursive(const String& path) if (path.length() < 5) { return FudStatus::ArgumentInvalid; } + if (not path.nullTerminated()) { + return FudStatus::StringInvalid; + } auto prefix{String::makeFromCString("/tmp/").takeOkay()}; auto diffResult = compareMem(path.data(), path.length(), prefix.data(), prefix.length()); if (diffResult.isError()) { @@ -92,7 +95,8 @@ FudStatus removeRecursive(const String& path) return FudStatus::ArgumentInvalid; } constexpr int maxOpenFd = 64; - auto status = nftw(path.c_str(), unlink_cb, maxOpenFd, FTW_DEPTH | FTW_PHYS); + + auto status = nftw(reinterpret_cast(path.data()), unlink_cb, maxOpenFd, FTW_DEPTH | FTW_PHYS); if (status == 0) { return FudStatus::Success; } @@ -104,4 +108,15 @@ FudStatus removeRecursive(const String& path) return FudStatus::Failure; } +auto rmFile(StringView filename) -> int +{ + auto result = unlink(filename.c_str()); + if (result == -1) { + if (errno == ENOENT) { + return 0; + } + } + return result; +} + } // namespace fud diff --git a/test/test_common.hpp b/test/test_common.hpp index 8912e42..5f6828f 100644 --- a/test/test_common.hpp +++ b/test/test_common.hpp @@ -81,7 +81,9 @@ struct MockFudAllocator { extern MockFudAllocator globalMockFudAlloc; class String; -FudStatus removeRecursive(const String& path); +class StringView; +auto rmFile(StringView filename) -> int; +FudStatus removeRecursive(StringView path); } // namespace fud diff --git a/test/test_csv.cpp b/test/test_csv.cpp new file mode 100644 index 0000000..6923f6f --- /dev/null +++ b/test/test_csv.cpp @@ -0,0 +1,74 @@ +/* + * libfud + * Copyright 2024 Dominick Allen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fud_csv.hpp" +#include "fud_print.hpp" + +#include "gtest/gtest.h" + +namespace fud { + +const StringView happyCsvFilename{u8"fud-happy-test.csv"}; + +const StringView happyData{ + u8"foo,bar,baz\n" + u8"1,Unquoted Text,\"Quoted Text with embedded \"\" quote and embedded newline \n" + u8"see\"\n,,\"Prior two fields are empty\"\n"}; + +auto writeHappyCsv() -> FudStatus +{ + auto fileResult{RegularFile::create( + happyCsvFilename, + FileAccessMode::Write, + OpenFlags{OpenFlagEnum::Truncate}, + PermUserRwGroupRead, + false, + NullOpt)}; + if (fileResult.isError()) { + debugPrint(u8"Error opening file: {}\n", FudStatusToString(fileResult.getError())); + return fileResult.takeError(); + } + auto file{fileResult.takeOkay()}; + + auto writeResult = file.write(reinterpret_cast(happyData.data()), happyData.length()); + if (writeResult.status != FudStatus::Success) { + return writeResult.status; + } + + if (writeResult.bytesDrained != happyData.length()) { + return FudStatus::Failure; + } + + return FudStatus::Success; +} + +TEST(FudCsv, ParseCsvFromFilename) +{ + Csv csv{Csv::makeDefault()}; + + ASSERT_EQ(writeHappyCsv(), FudStatus::Success); + + debugPrint(u8"Wrote happy data:\n-----\n{}\n-----\n", happyData); + + auto parseStatus = Csv::parseFromFilename(csv, NullOpt, happyCsvFilename); + if (parseStatus != FudStatus::Success) { + debugPrint(u8"Error parsing file: {}\n", FudStatusToString(parseStatus)); + } + ASSERT_EQ(parseStatus, FudStatus::Success); +} + +} // namespace fud diff --git a/test/test_directory.cpp b/test/test_directory.cpp index 96b9c2d..0f7dc8d 100644 --- a/test/test_directory.cpp +++ b/test/test_directory.cpp @@ -41,12 +41,12 @@ TEST(FudDirectory, Basic) ASSERT_TRUE(files[0].utf8Valid()); ASSERT_TRUE(files[1].utf8Valid()); - ASSERT_EQ(removeRecursive(testDirName), FudStatus::Success); + ASSERT_EQ(removeRecursive(testDirName.asView()), FudStatus::Success); auto mkdirResult = mkdir(testDirName.c_str(), pathMode); EXPECT_EQ(mkdirResult, 0); if (mkdirResult != 0) { - ASSERT_EQ(removeRecursive(testDirName), FudStatus::Success); + ASSERT_EQ(removeRecursive(testDirName.asView()), FudStatus::Success); return; } diff --git a/test/test_file.cpp b/test/test_file.cpp index 9727e94..12cfb98 100644 --- a/test/test_file.cpp +++ b/test/test_file.cpp @@ -29,38 +29,27 @@ namespace fud { -auto rmFile(const auto& filename) -> int -{ - auto result = unlink(filename.c_str()); - if (result == -1) { - if (errno == ENOENT) { - return 0; - } - } - return result; -} - TEST(FudFile, Basic) { - constexpr const char* testDirCName = "/tmp/fud_directory_test"; - const auto testDirName{String::makeFromCString(testDirCName).takeOkay()}; + StringView testDirCName{StringView::makeFromCString("/tmp/fud_directory_test")}; + const auto testDirName{String::from(testDirCName).takeOkay()}; ASSERT_TRUE(testDirName.utf8Valid()); constexpr mode_t pathMode = 0777; - ASSERT_EQ(removeRecursive(testDirName), FudStatus::Success); + ASSERT_EQ(removeRecursive(testDirName.asView()), FudStatus::Success); - auto mkdirResult = mkdir(testDirName.c_str(), pathMode); + auto mkdirResult = mkdir(testDirCName.c_str(), pathMode); EXPECT_EQ(mkdirResult, 0); if (mkdirResult != 0) { - ASSERT_EQ(removeRecursive(testDirName), FudStatus::Success); + ASSERT_EQ(removeRecursive(testDirCName), FudStatus::Success); return; } - String testName1{String::makeFromCStrings(testDirCName, "/", "test1").takeOkay()}; - String testName2{String::makeFromCStrings(testDirCName, "/", "test2").takeOkay()}; + String testName1{String::makeFromCStrings(testDirCName.c_str(), "/", "test1").takeOkay()}; + String testName2{String::makeFromCStrings(testDirCName.c_str(), "/", "test2").takeOkay()}; - ASSERT_EQ(rmFile(testName1), 0); - ASSERT_EQ(rmFile(testName2), 0); + ASSERT_EQ(rmFile(testName1.asView()), 0); + ASSERT_EQ(rmFile(testName2.asView()), 0); auto fileResult{RegularFile::open(testName1.asView(), FileAccessMode::Read, OpenFlags{}, NullOpt)}; EXPECT_EQ(fileResult.takeErrorOr(FudStatus::Success), FudStatus::NotFound); @@ -83,8 +72,8 @@ TEST(FudFile, Basic) auto file{fileResult.takeOkay()}; ASSERT_EQ(file.close(), FudStatus::Success); - ASSERT_EQ(rmFile(testName1), 0); - ASSERT_GE(createFile(testName2), 0); + ASSERT_EQ(rmFile(testName1.asView()), 0); + ASSERT_GE(createFile(testName2.asView()), 0); ASSERT_EQ(symlink(testName2.c_str(), testName1.c_str()), 0); fileResult = RegularFile::open(testName2.asView(), FileAccessMode::Read, OpenFlags{}, NullOpt); @@ -99,9 +88,9 @@ TEST(FudFile, Basic) TEST(FudBufferedFile, OpenReadWrite) { - constexpr const char* testDirCName = "/tmp/fud_directory_test"; - const auto testDirName{String::makeFromCString(testDirCName).takeOkay()}; + StringView testDirName{StringView::makeFromCString("/tmp/fud_directory_test")}; ASSERT_TRUE(testDirName.utf8Valid()); + ASSERT_TRUE(testDirName.nullTerminated()); constexpr mode_t pathMode = 0777; ASSERT_EQ(removeRecursive(testDirName), FudStatus::Success); @@ -113,7 +102,7 @@ TEST(FudBufferedFile, OpenReadWrite) return; } - String testName{String::makeFromCStrings(testDirCName, "/", "test1").takeOkay()}; + String testName{String::makeFromCStrings(testDirName.c_str(), "/", "test1").takeOkay()}; auto fileResult{RegularFile::create( testName.asView(), FileAccessMode::ReadWrite, @@ -171,7 +160,7 @@ TEST(FudBufferedFile, OpenReadWrite) EXPECT_EQ(output[testName.size() - 1], testName.data()[testName.size() - 1]); EXPECT_EQ(bufferedFile.close(true), FudStatus::Success); - ASSERT_EQ(rmFile(testName), 0); + ASSERT_EQ(rmFile(testName.asView()), 0); } } // namespace fud diff --git a/test/test_format.cpp b/test/test_format.cpp index b9d373c..738b551 100644 --- a/test/test_format.cpp +++ b/test/test_format.cpp @@ -709,4 +709,13 @@ TEST(FormatTest, TwoArgFormatTest) EXPECT_STREQ(sink.c_str(), expected.c_str()); } +TEST(FormatTest, StringView) +{ + String sink{}; + auto expected = std::format("Test {}", std::string_view{"Hello, World!"}); + auto formatResult = format(sink, FormatCharMode::Unchecked, u8"Test {}", StringView{u8"Hello, World!"}); + EXPECT_TRUE(formatResult.isOkay()); + EXPECT_STREQ(sink.c_str(), expected.c_str()); +} + } // namespace fud diff --git a/test/test_string.cpp b/test/test_string.cpp index 6bcbd37..ba2df6c 100644 --- a/test/test_string.cpp +++ b/test/test_string.cpp @@ -56,11 +56,25 @@ TEST(FudString, BasicStringOps) StringView view1{}; ASSERT_FALSE(view1.utf8Valid()); + StringView view2{fudString}; ASSERT_TRUE(view2.utf8Valid()); ASSERT_TRUE(view2.nullTerminated()); } +TEST(FudString, ViewFromCString) +{ + StringView viewFromU8{u8"Test"}; + EXPECT_EQ(viewFromU8.length(), 4); + EXPECT_TRUE(viewFromU8.utf8Valid()); + EXPECT_TRUE(viewFromU8.nullTerminated()); + + StringView viewFromCString{StringView::makeFromCString("Test")}; + EXPECT_EQ(viewFromCString.length(), 4); + EXPECT_TRUE(viewFromCString.utf8Valid()); + EXPECT_TRUE(viewFromCString.nullTerminated()); +} + TEST(FudString, HeapAlloc) { constexpr const char filenameLiteral[] = "Amazing Saga Volume 01/000.jpg"; -- cgit v1.2.3