/* * 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_array.hpp" #include "fud_c_file.hpp" #include "fud_directory.hpp" #include "fud_memory.hpp" #include "fud_string.hpp" #include "gtest/gtest.h" #include #include #include #include #include namespace fud { int unlink_cb(const char* fpath, const struct stat* sb_unused, int typeflag, struct FTW* ftwbuf) { static_cast(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()}; ASSERT_TRUE(testDirName.utf8Valid()); constexpr mode_t pathMode = 0777; const Array files{ String::makeFromCString("file1").takeOkay(), String::makeFromCString("file2").takeOkay()}; ASSERT_TRUE(files[0].utf8Valid()); ASSERT_TRUE(files[1].utf8Valid()); 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; } const String testDirNamePrefix{testDirName.catenate("/").takeOkay()}; ASSERT_TRUE(testDirNamePrefix.utf8Valid()); for (const auto& fnameBase : files) { const auto fname{testDirNamePrefix.catenate(fnameBase).takeOkay()}; ASSERT_TRUE(fname.utf8Valid()); auto fileResult{CBinaryFile::make(fname, CFileMode::ReadWriteTruncate)}; ASSERT_TRUE(fileResult.isOkay()); CBinaryFile file{std::move(fileResult).takeOkay()}; ASSERT_EQ(file.open(), FudStatus::Success); Array data{"test"}; WriteResult expected{data.size(), FudStatus::Success}; auto writeResult = file.write(data); ASSERT_EQ(writeResult.bytesWritten, expected.bytesWritten); ASSERT_EQ(writeResult.status, expected.status); } auto directoryResult{Directory::make(testDirName)}; ASSERT_TRUE(directoryResult.isOkay()); Directory directory{directoryResult.takeOkay()}; ASSERT_EQ(directory.errorCode(), 0); const Array expectedFiles{ DirectoryEntry{String::makeFromCString(".").takeOkay(), 0, 0, 2, 0, DirectoryEntryType::Directory}, DirectoryEntry{String::makeFromCString("..").takeOkay(), 0, 0, 1, 0, DirectoryEntryType::Directory}, DirectoryEntry{String::from(files[0]).takeOkay(), 0, files[0].size(), 1, 0, DirectoryEntryType::RegularFile}, DirectoryEntry{String::from(files[1]).takeOkay(), 0, files[1].size(), 1, 0, DirectoryEntryType::RegularFile}, }; ASSERT_TRUE(expectedFiles[0].name.compare(expectedFiles[0].name)); for (auto idx = 0; idx < expectedFiles.size(); ++idx) { auto dirEntryResult = directory.getNextEntry(); EXPECT_TRUE(dirEntryResult.isOkay()); auto dirEntryOpt = dirEntryResult.takeOkay(); if (dirEntryOpt == std::nullopt) { break; } auto dirEntry{std::move(dirEntryOpt.value())}; const auto* expected = std::find_if( expectedFiles.begin(), expectedFiles.end(), [&dirEntry](const DirectoryEntry& entry) { return entry.name.compare(dirEntry.name) && entry.entryType == dirEntry.entryType; }); EXPECT_NE(expected, nullptr); EXPECT_NE(expected, expectedFiles.end()); } auto finalDirEntryResult = directory.getNextEntry(); EXPECT_TRUE(finalDirEntryResult.isOkay()); EXPECT_EQ(finalDirEntryResult.getOkay(), std::nullopt); // ASSERT_EQ(removeRecursive(testDirName), FudStatus::Success); } } // namespace fud