/* * Copyright (C) 2022 Riyyi * * SPDX-License-Identifier: MIT */ #include // stderr #include #include #include // geteuid, setegid, seteuid #include #include "dotfile.h" #include "machine.h" #include "macro.h" #include "testcase.h" #include "testsuite.h" #include "util/file.h" #include "util/system.h" const bool root = !geteuid() ? true : false; const std::string homeDirectory = "/home/" + Machine::the().username(); const size_t homeDirectorySize = homeDirectory.size(); void createTestDotfiles(const std::vector& fileNames, const std::vector& fileContents, bool asRoot = false) { VERIFY(fileNames.size() == fileContents.size()); if (root && !asRoot) { setegid(Machine::the().gid()); seteuid(Machine::the().uid()); } for (size_t i = 0; i < fileNames.size(); ++i) { auto fileName = fileNames.at(i); auto directory = std::filesystem::path { fileName }.parent_path(); if (!directory.empty() && !std::filesystem::exists(directory)) { std::filesystem::create_directories(directory); } auto file = Util::File::create(fileName); if (file.data().size() == 0) { file.append(fileContents.at(i).c_str()).flush(); } } if (root && !asRoot) { seteuid(0); setegid(0); } } void removeTestDotfiles(const std::vector& files) { for (auto file : files) { // Get the top-level directory if (file.find(homeDirectory) == 0) { file = file.substr(homeDirectorySize + 1); } file = file.substr(0, file.find_first_of('/')); // Delete recursively in working directory if (std::filesystem::exists(file)) { std::filesystem::remove_all(file); } // Delete recursively in home directory file = homeDirectory + '/' + file; if (std::filesystem::exists(file)) { std::filesystem::remove_all(file); } } } // ----------------------------------------- TEST_CASE(AddDotfiles) { std::vector fileNames = { homeDirectory + "/__test-file-1", homeDirectory + "/__subdir/__test-file-2", homeDirectory + "/__subdir/__another-subdir/__test-file-3", }; std::vector fileContents = { R"(home directory file 1 )", R"(home directory file 2 )", R"(home directory file 3 )", }; createTestDotfiles(fileNames, fileContents); Dotfile::the().add(fileNames); for (const auto& file : fileNames) { EXPECT(std::filesystem::exists(file)); EXPECT(std::filesystem::exists(file.substr(homeDirectorySize + 1))); Util::File lhs(file); Util::File rhs(file.substr(homeDirectorySize + 1)); EXPECT_EQ(lhs.data(), rhs.data()); } removeTestDotfiles(fileNames); } TEST_CASE(AddNonExistentDotfiles) { stderr = Test::TestSuite::the().outputNull(); Dotfile::the().add({ homeDirectory + "/__non-existent-test-file" }); stderr = Test::TestSuite::the().outputErr(); EXPECT(!std::filesystem::exists("__non-existent-test-file")); removeTestDotfiles({ "__non-existent-test-file" }); } TEST_CASE(PullDotfiles) { std::vector fileNames = { "__test-file-1", "__subdir/__test-file-2", "__subdir/__another-subdir/__test-file-3", }; std::vector homeFileNames; for (const auto& file : fileNames) { homeFileNames.push_back(homeDirectory + '/' + file); } std::vector homeFileContents = { R"(file 1 after pulling )", R"(file 2 after pulling )", R"(file 3 after pulling )", }; std::vector workingDirectoryFileContents = { R"(file 1 )", R"(file 2 )", R"(file 3 )", }; createTestDotfiles(homeFileNames, homeFileContents); createTestDotfiles(fileNames, workingDirectoryFileContents); Dotfile::the().pull(fileNames); for (size_t i = 0; i < fileNames.size(); ++i) { EXPECT(std::filesystem::exists(homeFileNames.at(i))); EXPECT(std::filesystem::exists(fileNames.at(i))); Util::File lhs(homeFileNames.at(i)); Util::File rhs(fileNames.at(i)); EXPECT_EQ(lhs.data(), rhs.data()); } removeTestDotfiles(fileNames); } TEST_CASE(PushDotfiles) { std::vector fileNames = { "__test-file-1", "__subdir/__test-file-2", "__subdir/__another-subdir/__test-file-3", }; std::vector fileContents = { R"(working directory file 1 )", R"(working directory file 2 )", R"(working directory file 3 )", }; createTestDotfiles(fileNames, fileContents); Dotfile::the().push(fileNames); for (const auto& file : fileNames) { EXPECT(std::filesystem::exists(file)); EXPECT(std::filesystem::exists(homeDirectory + '/' + file)); Util::File lhs(file); Util::File rhs(homeDirectory + '/' + file); EXPECT_EQ(lhs.data(), rhs.data()); } removeTestDotfiles(fileNames); } TEST_CASE(PushDotfilesWithExcludePath) { std::vector fileNames = { "__test-file-1", "__subdir/__test-file-2", "__subdir/__test-file-3", "__another-subdir/__test-file-4.test", }; createTestDotfiles(fileNames, { "", "", "", "" }); Dotfile::the().setExcludePaths({ { Dotfile::ExcludeType::File, "__test-file-1" }, { Dotfile::ExcludeType::Directory, "__subdir" }, { Dotfile::ExcludeType::EndsWith, ".test" }, }); Dotfile::the().push(fileNames); Dotfile::the().setExcludePaths({}); for (const auto& file : fileNames) { EXPECT(!std::filesystem::exists(homeDirectory + '/' + file)); } removeTestDotfiles(fileNames); } TEST_CASE(AddSystemDotfiles) { VERIFY(geteuid() == 0); Dotfile::the().setSystemDirectories({ "/etc", "/usr/lib" }); Dotfile::the().add({ "/etc/group", "/usr/lib/os-release" }); Dotfile::the().setSystemDirectories({}); EXPECT(std::filesystem::exists("etc/group")); EXPECT(std::filesystem::exists("usr/lib/os-release")); std::filesystem::remove_all(Dotfile::the().workingDirectory() / "etc"); std::filesystem::remove_all(Dotfile::the().workingDirectory() / "usr"); } TEST_CASE(PullSystemDotfiles) { VERIFY(geteuid() == 0); createTestDotfiles({ "etc/group" }, { "" }, true); Dotfile::the().setSystemDirectories({ "/etc" }); Dotfile::the().pull({ "etc/group" }); Dotfile::the().setSystemDirectories({}); Util::File lhs("/etc/group"); Util::File rhs(Dotfile::the().workingDirectory() / "etc/group"); EXPECT_EQ(lhs.data(), rhs.data()); std::filesystem::remove_all(Dotfile::the().workingDirectory() / "etc"); }