diff options
author | Dominick Allen <djallen@librehumanitas.org> | 2024-10-27 09:04:05 -0500 |
---|---|---|
committer | Dominick Allen <djallen@librehumanitas.org> | 2024-10-27 09:04:05 -0500 |
commit | b8345246dcc2121bcb6d1515a9341789de20199f (patch) | |
tree | 4a25857512a90ff38e8a40166c54694b74920216 /test | |
parent | f84b8259f6e980fed647d8e1ec0634f89ee59c06 (diff) |
First crack at file objects.
Diffstat (limited to 'test')
-rw-r--r-- | test/CMakeLists.txt | 5 | ||||
-rw-r--r-- | test/test_common.cpp | 60 | ||||
-rw-r--r-- | test/test_common.hpp | 13 | ||||
-rw-r--r-- | test/test_directory.cpp | 45 | ||||
-rw-r--r-- | test/test_file.cpp | 97 |
5 files changed, 170 insertions, 50 deletions
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 788e4ba..aef8052 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -13,12 +13,16 @@ endif() set(gtest_URL https://github.com/google/googletest.git) set(gtest_TAG v1.14.0) +# Keep this setting above the FetchContent_Declare for googletest +set(INSTALL_GTEST OFF CACHE BOOL "Enable installation of googletest.") + FetchContent_Declare( googletest GIT_REPOSITORY ${gtest_URL} GIT_TAG ${gtest_TAG} ) FetchContent_MakeAvailable(googletest) + include(GoogleTest) enable_testing() @@ -61,6 +65,7 @@ fud_add_test(test_allocator SOURCES test_allocator.cpp) fud_add_test(test_assert SOURCES test_assert.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) fud_add_test(test_format SOURCES test_format.cpp) fud_add_test(test_option SOURCES test_option.cpp) fud_add_test(test_result SOURCES test_result.cpp) diff --git a/test/test_common.cpp b/test/test_common.cpp index d3e1704..784cb0d 100644 --- a/test/test_common.cpp +++ b/test/test_common.cpp @@ -16,7 +16,12 @@ */ #include "test_common.hpp" + +#include "fud_string.hpp" + #include <cstdlib> +#include <ftw.h> +#include <gtest/gtest.h> namespace fud { @@ -34,22 +39,69 @@ MockFudAlloc globalDefaultMockAlloc{}; MockFudDealloc globalDefaultMockDealloc{}; -void* MockFudAllocator::allocate(size_t size) { +void* MockFudAllocator::allocate(size_t size) +{ return (*m_allocator)(size); } -void MockFudAllocator::deallocate(void* pointer) { +void MockFudAllocator::deallocate(void* pointer) +{ return (*m_deallocator)(pointer); } MockFudAllocator globalMockFudAlloc{}; -void* fudAlloc(size_t size) { +void* fudAlloc(size_t size) +{ return globalMockFudAlloc.allocate(size); } -void fudFree(void* ptr) { +void fudFree(void* ptr) +{ return globalMockFudAlloc.deallocate(ptr); } +int unlink_cb(const char* fpath, const struct stat* sb_unused, int typeflag, struct FTW* ftwbuf) +{ + static_cast<void>(sb_unused); + int retValue = remove(fpath); + + EXPECT_EQ(retValue, 0); + if (retValue != 0) { + perror(fpath); + } + + return retValue; +} + +FudStatus removeRecursive(const String& path) +{ + if (!path.utf8Valid()) { + return FudStatus::Utf8Invalid; + } + if (path.length() < 5) { + return FudStatus::ArgumentInvalid; + } + auto prefix{String::makeFromCString("/tmp/").takeOkay()}; + auto diffResult = compareMem(path.data(), path.length(), prefix.data(), prefix.length()); + if (diffResult.isError()) { + return FudStatus::ArgumentInvalid; + } + auto diff = diffResult.getOkay(); + if (diff != 0) { + return FudStatus::ArgumentInvalid; + } + constexpr int maxOpenFd = 64; + auto status = nftw(path.c_str(), unlink_cb, maxOpenFd, FTW_DEPTH | FTW_PHYS); + if (status == 0) { + return FudStatus::Success; + } + + if (errno == ENOENT) { + return FudStatus::Success; + } + + return FudStatus::Failure; +} + } // namespace fud diff --git a/test/test_common.hpp b/test/test_common.hpp index f049fed..3d7ecba 100644 --- a/test/test_common.hpp +++ b/test/test_common.hpp @@ -18,9 +18,15 @@ #ifndef FUD_TEST_COMMON_HPP #define FUD_TEST_COMMON_HPP +#include "fud_status.hpp" + #include <cstddef> #include <cstdlib> +extern "C" { +int unlink_cb(const char* fpath, const struct stat* sb_unused, int typeflag, struct FTW* ftwbuf); +} + namespace fud { // NOLINTBEGIN(cppcoreguidelines-macro-usage) @@ -70,11 +76,14 @@ struct MockFudAllocator { void deallocate(void* pointer); MockFudAlloc* m_allocator{&globalDefaultMockAlloc}; - MockFudDealloc* m_deallocator{&globalDefaultMockDealloc};; + MockFudDealloc* m_deallocator{&globalDefaultMockDealloc}; }; extern MockFudAllocator globalMockFudAlloc; -} // namespace ext_lib +class String; +FudStatus removeRecursive(const String& path); + +} // namespace fud #endif diff --git a/test/test_directory.cpp b/test/test_directory.cpp index 2f69dab..e30dafb 100644 --- a/test/test_directory.cpp +++ b/test/test_directory.cpp @@ -18,8 +18,8 @@ #include "fud_array.hpp" #include "fud_c_file.hpp" #include "fud_directory.hpp" -#include "fud_memory.hpp" #include "fud_string.hpp" +#include "test_common.hpp" #include "gtest/gtest.h" #include <algorithm> @@ -30,49 +30,6 @@ namespace fud { -int unlink_cb(const char* fpath, const struct stat* sb_unused, int typeflag, struct FTW* ftwbuf) -{ - static_cast<void>(sb_unused); - int retValue = remove(fpath); - - EXPECT_EQ(retValue, 0); - if (retValue != 0) { - perror(fpath); - } - - return retValue; -} - -FudStatus removeRecursive(const String& path) -{ - if (!path.utf8Valid()) { - return FudStatus::Utf8Invalid; - } - if (path.length() < 5) { - return FudStatus::ArgumentInvalid; - } - auto prefix{String::makeFromCString("/tmp/").takeOkay()}; - auto diffResult = compareMem(path.data(), path.length(), prefix.data(), prefix.length()); - if (diffResult.isError()) { - return FudStatus::ArgumentInvalid; - } - auto diff = diffResult.getOkay(); - if (diff != 0) { - return FudStatus::ArgumentInvalid; - } - constexpr int maxOpenFd = 64; - auto status = nftw(path.c_str(), unlink_cb, maxOpenFd, FTW_DEPTH | FTW_PHYS); - if (status == 0) { - return FudStatus::Success; - } - - if (errno == ENOENT) { - return FudStatus::Success; - } - - return FudStatus::Failure; -} - TEST(FudDirectory, Basic) { const auto testDirName{String::makeFromCString("/tmp/fud_directory_test").takeOkay()}; diff --git a/test/test_file.cpp b/test/test_file.cpp new file mode 100644 index 0000000..06e6bcc --- /dev/null +++ b/test/test_file.cpp @@ -0,0 +1,97 @@ +/* + * 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_file.hpp" +#include "fud_string.hpp" +#include "test_common.hpp" + +#include "gtest/gtest.h" +#include <algorithm> +#include <cerrno> +#include <fcntl.h> +#include <ftw.h> +#include <ranges> + +namespace fud { + +TEST(FudFile, Basic) +{ + constexpr const char* testDirCName = "/tmp/fud_directory_test"; + const auto testDirName{String::makeFromCString(testDirCName).takeOkay()}; + ASSERT_TRUE(testDirName.utf8Valid()); + constexpr mode_t pathMode = 0777; + + ASSERT_EQ(removeRecursive(testDirName), FudStatus::Success); + + auto mkdirResult = mkdir(testDirName.c_str(), pathMode); + EXPECT_EQ(mkdirResult, 0); + if (mkdirResult != 0) { + ASSERT_EQ(removeRecursive(testDirName), FudStatus::Success); + return; + } + + String testName1{String::makeFromCStrings(testDirCName, "/", "test1").takeOkay()}; + String testName2{String::makeFromCStrings(testDirCName, "/", "test2").takeOkay()}; + + auto rmFile = [](const auto& filename) -> int { + auto result = unlink(filename.c_str()); + if (result == -1) { + if (errno == ENOENT) { + return 0; + } + } + return result; + }; + ASSERT_EQ(rmFile(testName1), 0); + ASSERT_EQ(rmFile(testName2), 0); + + auto fileResult{RegularFile::open(testName1.asView(), FileAccessMode::Read, OpenFlags{}, NullOpt)}; + EXPECT_EQ(fileResult.takeErrorOr(FudStatus::Success), FudStatus::NotFound); + + /* The irony of the ability for TOCTOU bugs to be present in the test does + * not escape me. */ + + auto createFile = [](const auto& filename) -> int { + constexpr int allPerms = 0777; + auto handleResult = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, allPerms); + if (handleResult >= 0) { + close(handleResult); + } + return handleResult; + }; + ASSERT_GE(createFile(testName1), 0); + + fileResult = RegularFile::open(testName1.asView(), FileAccessMode::Read, OpenFlags{}, NullOpt); + ASSERT_TRUE(fileResult.isOkay()); + auto file{fileResult.takeOkay()}; + ASSERT_EQ(file.close(), FudStatus::Success); + + ASSERT_EQ(rmFile(testName1), 0); + ASSERT_GE(createFile(testName2), 0); + ASSERT_EQ(symlink(testName2.c_str(), testName1.c_str()), 0); + + fileResult = RegularFile::open(testName2.asView(), FileAccessMode::Read, OpenFlags{}, NullOpt); + ASSERT_TRUE(fileResult.isOkay()); + file = fileResult.takeOkay(); + ASSERT_EQ(file.close(), FudStatus::Success); + + fileResult = RegularFile::open(testName1.asView(), FileAccessMode::Read, OpenFlags{}, NullOpt); + ASSERT_TRUE(fileResult.isError()); + EXPECT_EQ(fileResult.getError(), FudStatus::Failure); +} + +} // namespace fud |