Compare commits

...
8 Commits
8 changed files with 519 additions and 97 deletions
+21 -15
View File
@@ -29,25 +29,31 @@ From there you can copy it to anywhere in the working directory,
the config file is searched recursively. \\ the config file is searched recursively. \\
~$HOME/<dotfiles>/<anywhere>~ ~$HOME/<dotfiles>/<anywhere>~
**** Exclude paths **** Ignore patterns
Everything in this list will get excluded when pushing config files from the working directory to the system. \\ Everything in this list will get ignored when pulling/pushing config files from the working directory. \\
Currently three types of file matching are supported: Currently two types of file matching are supported:
- ~file~ full name of the file matches - ~literal~ matches a file or directory literally
- ~directory~ full name of the directory matches - ~wildcard~ the asterisk matches zero or more characters
- ~endsWith~ end of the path matches
The excluded paths from the example config: These behave similarly to a ~.gitignore~ pattern.
- If the pattern starts with a slash, it matches files and directories in the working directory root only.
- If the pattern doesnt start with a slash, it matches files and directories in any directory or subdirectory.
- If the pattern ends with a slash, it matches only directories. When a directory is ignored, \\
all of its files and subdirectories are also ignored.
The ignore patterns from the example config:
#+BEGIN_SRC javascript #+BEGIN_SRC javascript
"excludePaths" : { "ignorePatterns" : [
".git": "directory", ".git/",
".md": "endsWith", "*.md",
"manafiles.json": "endsWith", "manafiles.json",
"packages": "file", "packages",
"README.org": "endsWith", "README.org",
"screenshot.png": "file" "screenshot.png"
} ]
#+END_SRC #+END_SRC
**** System config files **** System config files
+8 -8
View File
@@ -1,12 +1,12 @@
{ {
"excludePaths" : { "ignorePatterns" : [
".git": "directory", ".git/",
".md": "endsWith", "*.md",
"manafiles.json": "endsWith", "manafiles.json",
"packages": "file", "packages",
"README.org": "endsWith", "README.org",
"screenshot.png": "file" "screenshot.png"
}, ],
"systemDirectories": [ "systemDirectories": [
"/boot", "/boot",
"/etc", "/etc",
+3 -11
View File
@@ -75,26 +75,18 @@ void Config::parseConfigFile()
void to_json(nlohmann::json& object, const Settings& settings) void to_json(nlohmann::json& object, const Settings& settings)
{ {
object = nlohmann::json { object = nlohmann::json {
{ "excludePaths", settings.excludePaths }, { "ignorePatterns", settings.ignorePatterns },
{ "systemDirectories", settings.systemDirectories } { "systemDirectories", settings.systemDirectories }
}; };
} }
void from_json(const nlohmann::json& object, Settings& settings) void from_json(const nlohmann::json& object, Settings& settings)
{ {
if (object.find("excludePaths") != object.end()) { if (object.find("ignorePatterns") != object.end()) {
object.at("excludePaths").get_to(settings.excludePaths); object.at("ignorePatterns").get_to(settings.ignorePatterns);
} }
if (object.find("systemDirectories") != object.end()) { if (object.find("systemDirectories") != object.end()) {
object.at("systemDirectories").get_to(settings.systemDirectories); object.at("systemDirectories").get_to(settings.systemDirectories);
} }
// Check for correct exclude type values
for (const auto& path : settings.excludePaths) {
if (path.second != "file" && path.second != "directory" && path.second != "endsWith") {
fprintf(stderr, "\033[31;1mConfig:\033[0m invalid exclude type '%s'\n", path.second.c_str());
raise(SIGABRT);
}
}
} }
+9 -9
View File
@@ -9,7 +9,6 @@
#include <cstddef> // size_t #include <cstddef> // size_t
#include <filesystem> // path #include <filesystem> // path
#include <map>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -18,12 +17,13 @@
#include "util/singleton.h" #include "util/singleton.h"
struct Settings { struct Settings {
std::map<std::string, std::string> excludePaths { std::vector<std::string> ignorePatterns {
{ ".git", "directory" }, ".git/",
{ ".md", "endsWith" }, "*.md",
{ "packages", "file" }, "manafiles.json",
{ "README.org", "endsWith" }, "packages",
{ "screenshot.png", "file" }, "README.org",
"screenshot.png",
}; };
std::vector<std::filesystem::path> systemDirectories { std::vector<std::filesystem::path> systemDirectories {
"/boot", "/boot",
@@ -38,10 +38,10 @@ public:
virtual ~Config(); virtual ~Config();
void setSystemDirectories(const std::vector<std::filesystem::path>& systemDirectories) { m_settings.systemDirectories = systemDirectories; } void setSystemDirectories(const std::vector<std::filesystem::path>& systemDirectories) { m_settings.systemDirectories = systemDirectories; }
void setExcludePaths(const std::map<std::string, std::string>& excludePaths) { m_settings.excludePaths = excludePaths; } void setIgnorePatterns(const std::vector<std::string>& ignorePatterns) { m_settings.ignorePatterns = ignorePatterns; }
void setVerbose(bool verbose) { m_verbose = verbose; } void setVerbose(bool verbose) { m_verbose = verbose; }
const std::map<std::string, std::string>& excludePaths() const { return m_settings.excludePaths; } const std::vector<std::string>& ignorePatterns() const { return m_settings.ignorePatterns; }
const std::vector<std::filesystem::path>& systemDirectories() const { return m_settings.systemDirectories; } const std::vector<std::filesystem::path>& systemDirectories() const { return m_settings.systemDirectories; }
const std::filesystem::path& workingDirectory() const { return m_workingDirectory; } const std::filesystem::path& workingDirectory() const { return m_workingDirectory; }
+127 -24
View File
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <cassert> // assert
#include <cctype> // tolower #include <cctype> // tolower
#include <cstddef> // size_t #include <cstddef> // size_t
#include <cstdio> // fprintf, printf, stderr #include <cstdio> // fprintf, printf, stderr
@@ -105,6 +106,131 @@ void Dotfile::push(const std::vector<std::string>& targets)
pullOrPush(SyncType::Push, targets); pullOrPush(SyncType::Push, targets);
} }
bool Dotfile::filter(const std::filesystem::directory_entry& path)
{
std::string pathString = path.path().string();
assert(pathString.front() == '/');
// Cut off working directory
size_t cutFrom = pathString.find(Config::the().workingDirectory()) == 0 ? Config::the().workingDirectorySize() : 0;
pathString = pathString.substr(cutFrom);
for (const auto& ignorePattern : Config::the().ignorePatterns()) {
if (pathString == ignorePattern) {
return true;
}
// If starts with '/', only match in the working directory root
bool onlyMatchInRoot = false;
if (ignorePattern.front() == '/') {
onlyMatchInRoot = true;
}
// If ends with '/', only match directories
bool onlyMatchDirectories = false;
if (ignorePattern.back() == '/') {
onlyMatchDirectories = true;
}
// Parsing
bool tryPatternState = true;
size_t pathIterator = 0;
size_t ignoreIterator = 0;
if (!onlyMatchInRoot) {
pathIterator++;
}
// Current path charter 'x' == next ignore pattern characters '*x'
// Example, iterator at []: [.]log/output.txt
// [*].log
if (pathIterator < pathString.length()
&& ignoreIterator < ignorePattern.length() - 1
&& ignorePattern.at(ignoreIterator) == '*'
&& pathString.at(pathIterator) == ignorePattern.at(ignoreIterator + 1)) {
ignoreIterator++;
}
for (; pathIterator < pathString.length() && ignoreIterator < ignorePattern.length();) {
char character = pathString.at(pathIterator);
pathIterator++;
if (!tryPatternState && character == '/') {
tryPatternState = true;
continue;
}
if (!tryPatternState) {
continue;
}
if (character == ignorePattern.at(ignoreIterator)) {
// Fail if the final match hasn't reached the end of the ignore pattern
// Example, iterator at []: doc/buil[d]
// buil[d]/
if (pathIterator == pathString.length() && ignoreIterator < ignorePattern.length() - 1) {
break;
}
// Next path character 'x' == next ignore pattern characters '*x', skip the '*'
// Example, iterator at []: /includ[e]/header.h
// /includ[e]*/
if (pathIterator < pathString.length()
&& ignoreIterator < ignorePattern.length() - 2
&& ignorePattern.at(ignoreIterator + 1) == '*'
&& pathString.at(pathIterator) == ignorePattern.at(ignoreIterator + 2)) {
ignoreIterator++;
}
ignoreIterator++;
continue;
}
if (ignorePattern.at(ignoreIterator) == '*') {
// Fail if we're entering a subdirectory and we should only match in the root
// Example, iterator at []: /src[/]include/header.h
// /[*]include/
if (onlyMatchInRoot && character == '/') {
break;
}
// Next path character == next ignore pattern character
if (pathIterator < pathString.length()
&& ignoreIterator + 1 < ignorePattern.length()
&& pathString.at(pathIterator) == ignorePattern.at(ignoreIterator + 1)) {
ignoreIterator++;
}
continue;
}
// Reset filter pattern if it hasnt been completed at this point
// Example, iterator at []: /[s]rc/include/header.h
// /[i]nclude*/
if (ignoreIterator < ignorePattern.length() - 1) {
ignoreIterator = 0;
}
tryPatternState = false;
}
if (ignoreIterator == ignorePattern.length()) {
return true;
}
if (ignorePattern.back() == '*' && ignoreIterator == ignorePattern.length() - 1) {
return true;
}
if (onlyMatchDirectories && ignoreIterator == ignorePattern.length() - 1) {
return true;
}
}
return false;
}
// ----------------------------------------- // -----------------------------------------
void Dotfile::pullOrPush(SyncType type, const std::vector<std::string>& targets) void Dotfile::pullOrPush(SyncType type, const std::vector<std::string>& targets)
@@ -362,36 +488,13 @@ void Dotfile::forEachDotfile(const std::vector<std::string>& targets, const std:
if (path.is_directory() || filter(path)) { if (path.is_directory() || filter(path)) {
continue; continue;
} }
if (!targets.empty() && !include(path.path().string(), targets)) { if (!targets.empty() && !include(path.path(), targets)) {
continue; continue;
} }
callback(path, index++); callback(path, index++);
} }
} }
bool Dotfile::filter(const std::filesystem::path& path)
{
for (auto& excludePath : Config::the().excludePaths()) {
if (excludePath.second == "file") {
if (path.string() == Config::the().workingDirectory() / excludePath.first) {
return true;
}
}
else if (excludePath.second == "directory") {
if (path.string().find(Config::the().workingDirectory() / excludePath.first) == 0) {
return true;
}
}
else if (excludePath.second == "endsWith") {
if (path.string().find(excludePath.first) == path.string().size() - excludePath.first.size()) {
return true;
}
}
}
return false;
}
bool Dotfile::include(const std::filesystem::path& path, const std::vector<std::string>& targets) bool Dotfile::include(const std::filesystem::path& path, const std::vector<std::string>& targets)
{ {
for (const auto& target : targets) { for (const auto& target : targets) {
+2 -1
View File
@@ -30,6 +30,8 @@ public:
void pull(const std::vector<std::string>& targets = {}); void pull(const std::vector<std::string>& targets = {});
void push(const std::vector<std::string>& targets = {}); void push(const std::vector<std::string>& targets = {});
bool filter(const std::filesystem::directory_entry& path);
private: private:
void pullOrPush(SyncType type, const std::vector<std::string>& targets = {}); void pullOrPush(SyncType type, const std::vector<std::string>& targets = {});
void sync(SyncType type, void sync(SyncType type,
@@ -39,7 +41,6 @@ private:
void selectivelyCommentOrUncomment(const std::string& path); void selectivelyCommentOrUncomment(const std::string& path);
void forEachDotfile(const std::vector<std::string>& targets, const std::function<void(const std::filesystem::directory_entry&, size_t)>& callback); void forEachDotfile(const std::vector<std::string>& targets, const std::function<void(const std::filesystem::directory_entry&, size_t)>& callback);
bool filter(const std::filesystem::path& path);
bool include(const std::filesystem::path& path, const std::vector<std::string>& targets); bool include(const std::filesystem::path& path, const std::vector<std::string>& targets);
bool isSystemTarget(const std::string& target); bool isSystemTarget(const std::string& target);
}; };
+51 -10
View File
@@ -4,35 +4,76 @@
#include <cstdio> // fprintf #include <cstdio> // fprintf
#include <iostream> // cerr #include <iostream> // cerr
#define VERIFY(x, result) \ #define GET_2TH_ARG(arg1, arg2, ...) arg2
#define GET_3TH_ARG(arg1, arg2, arg3, ...) arg3
#define GET_4TH_ARG(arg1, arg2, arg3, arg4, ...) arg4
#define MACRO_CHOOSER_1(macro, ...) \
GET_2TH_ARG(__VA_ARGS__, macro##_1, )
#define MACRO_CHOOSER_2(macro, ...) \
GET_3TH_ARG(__VA_ARGS__, macro##_2, macro##_1, )
#define MACRO_CHOOSER_3(macro, ...) \
GET_4TH_ARG(__VA_ARGS__, macro##_3, macro##_2, macro##_1, )
// -----------------------------------------
#define EXPECT_IMPL(x, result) \
if (!(x)) { \ if (!(x)) { \
fprintf(stderr, " \033[31;1mFAIL:\033[0m %s:%d: VERIFY(%s) failed\n", \ fprintf(stderr, " \033[31;1mFAIL:\033[0m %s:%d: EXPECT(%s) failed\n", \
__FILE__, __LINE__, #x); \ __FILE__, __LINE__, #x); \
Test::TestSuite::the().currentTestCaseFailed(); \ Test::TestSuite::the().currentTestCaseFailed(); \
result; \ result; \
} }
#define EXPECT(x) \ #define EXPECT_1(x) \
if (!(x)) { \ EXPECT_IMPL(x, (void)0)
fprintf(stderr, " \033[31;1mFAIL:\033[0m %s:%d: EXPECT(%s) failed\n", \
__FILE__, __LINE__, #x); \
Test::TestSuite::the().currentTestCaseFailed(); \
}
#define EXPECT_EQ(a, b) \ #define EXPECT_2(x, result) \
EXPECT_IMPL(x, result)
#define EXPECT(...) \
MACRO_CHOOSER_2(EXPECT, __VA_ARGS__) \
(__VA_ARGS__)
// -----------------------------------------
#define EXPECT_EQ_IMPL(a, b, result) \
if (a != b) { \ if (a != b) { \
std::cerr << " \033[31;1mFAIL:\033[0m " << __FILE__ << ":" << __LINE__ \ std::cerr << " \033[31;1mFAIL:\033[0m " << __FILE__ << ":" << __LINE__ \
<< ": EXPECT_EQ(" << #a << ", " << #b ") failed with" \ << ": EXPECT_EQ(" << #a << ", " << #b ") failed with" \
<< " lhs='" << a << "' and rhs='" << b << "'" << std::endl; \ << " lhs='" << a << "' and rhs='" << b << "'" << std::endl; \
Test::TestSuite::the().currentTestCaseFailed(); \ Test::TestSuite::the().currentTestCaseFailed(); \
result; \
} }
#define EXPECT_NE(a, b) \ #define EXPECT_EQ_2(a, b) \
EXPECT_EQ_IMPL(a, b, (void)0)
#define EXPECT_EQ_3(a, b, result) \
EXPECT_EQ_IMPL(a, b, result)
#define EXPECT_EQ(...) \
MACRO_CHOOSER_3(EXPECT_EQ, __VA_ARGS__) \
(__VA_ARGS__)
// -----------------------------------------
#define EXPECT_NE_IMPL(a, b, result) \
if (a == b) { \ if (a == b) { \
std::cerr << " \033[31;1mFAIL:\033[0m " << __FILE__ << ":" << __LINE__ \ std::cerr << " \033[31;1mFAIL:\033[0m " << __FILE__ << ":" << __LINE__ \
<< ": EXPECT_NE(" << #a << ", " << #b ") failed with" \ << ": EXPECT_NE(" << #a << ", " << #b ") failed with" \
<< " lhs='" << a << "' and rhs='" << b << "'" << std::endl; \ << " lhs='" << a << "' and rhs='" << b << "'" << std::endl; \
Test::TestSuite::the().currentTestCaseFailed(); \ Test::TestSuite::the().currentTestCaseFailed(); \
result; \
} }
#define EXPECT_NE_2(a, b) \
EXPECT_NE_IMPL(a, b, (void)0)
#define EXPECT_NE_3(a, b, result) \
EXPECT_NE_IMPL(a, b, result)
#define EXPECT_NE(...) \
MACRO_CHOOSER_3(EXPECT_NE, __VA_ARGS__) \
(__VA_ARGS__)
#endif // TEST_H #endif // TEST_H
+298 -19
View File
@@ -10,6 +10,7 @@
#include <filesystem> // path #include <filesystem> // path
#include <string> #include <string>
#include <unistd.h> // geteuid, setegid, seteuid #include <unistd.h> // geteuid, setegid, seteuid
#include <unordered_map>
#include <vector> #include <vector>
#include "config.h" #include "config.h"
@@ -27,7 +28,7 @@ const size_t homeDirectorySize = homeDirectory.string().size();
void createTestDotfiles(const std::vector<std::string>& fileNames, const std::vector<std::string>& fileContents, bool asRoot = false) void createTestDotfiles(const std::vector<std::string>& fileNames, const std::vector<std::string>& fileContents, bool asRoot = false)
{ {
VERIFY(fileNames.size() == fileContents.size(), return); EXPECT(fileNames.size() == fileContents.size(), return );
if (root && !asRoot) { if (root && !asRoot) {
setegid(Machine::the().gid()); setegid(Machine::the().gid());
@@ -54,7 +55,7 @@ void createTestDotfiles(const std::vector<std::string>& fileNames, const std::ve
} }
} }
void removeTestDotfiles(const std::vector<std::string>& files) void removeTestDotfiles(const std::vector<std::string>& files, bool deleteInHome = true)
{ {
for (auto file : files) { for (auto file : files) {
// Get the top-level directory // Get the top-level directory
@@ -68,6 +69,10 @@ void removeTestDotfiles(const std::vector<std::string>& files)
std::filesystem::remove_all(file); std::filesystem::remove_all(file);
} }
if (!deleteInHome) {
continue;
}
// Delete recursively in home directory // Delete recursively in home directory
file = homeDirectory / file; file = homeDirectory / file;
if (std::filesystem::exists(file) || std::filesystem::is_symlink(file)) { if (std::filesystem::exists(file) || std::filesystem::is_symlink(file)) {
@@ -76,8 +81,279 @@ void removeTestDotfiles(const std::vector<std::string>& files)
} }
} }
void testDotfileFilters(const std::unordered_map<std::string, bool>& tests,
const std::vector<std::string>& testIgnorePatterns)
{
std::vector<std::string> fileNames;
std::vector<std::string> fileContents;
for (const auto& test : tests) {
fileNames.push_back(test.first);
fileContents.push_back("");
}
createTestDotfiles(fileNames, fileContents);
auto ignorePatterns = Config::the().ignorePatterns();
Config::the().setIgnorePatterns(testIgnorePatterns);
for (const auto& path : fileNames) {
bool result = Dotfile::the().filter(std::filesystem::directory_entry { "/" + path });
EXPECT_EQ(result, tests.at(path), printf(" path = '%s'\n", path.c_str()));
}
Config::the().setIgnorePatterns(ignorePatterns);
removeTestDotfiles(fileNames, false);
}
// ----------------------------------------- // -----------------------------------------
TEST_CASE(DotfilesLiteralIgnoreAllFiles)
{
std::unordered_map<std::string, bool> tests = {
{ "access.log", true },
{ "logs/access.log", true },
{ "var/logs/access.log", true },
{ "error.log", false },
};
std::vector<std::string> testFilters = {
"access.log",
};
testDotfileFilters(tests, testFilters);
}
TEST_CASE(DotfilesLiteralIgnoreFileInRoot)
{
std::unordered_map<std::string, bool> tests = {
{ "access.log", true },
{ "logs/access.log", false },
{ "var/logs/access.log", false },
{ "error.log", false },
};
std::vector<std::string> testFilters = {
"/access.log",
};
testDotfileFilters(tests, testFilters);
}
TEST_CASE(DotfilesLiteralIgnoreDirectories)
{
std::unordered_map<std::string, bool> tests = {
{ "build/executable", true },
{ "doc/build", false },
};
std::vector<std::string> testFilters = {
"build/",
};
testDotfileFilters(tests, testFilters);
}
TEST_CASE(DotfilesWildcardIgnoreAllFilesWithExtension)
{
std::unordered_map<std::string, bool> tests = {
{ "error.log", true },
{ "logs/debug.log", true },
{ "var/logs/error.log", true },
{ ".log/output.txt", true },
{ "program.log/output.txt", true },
{ "log.txt", false },
};
std::vector<std::string> testFilters = {
"*.log",
};
testDotfileFilters(tests, testFilters);
}
TEST_CASE(DotfilesWildcardIgnoreAllFilesInRootWithExtension)
{
std::unordered_map<std::string, bool> tests = {
{ "error.log", true },
{ "logs/debug.log", false },
{ "var/logs/error.log", false },
{ ".log/output.txt", true },
{ "program.log/output.txt", true },
{ "log.txt", false },
};
std::vector<std::string> testFilters = {
"/*.log",
};
testDotfileFilters(tests, testFilters);
}
TEST_CASE(DotfilesWildcardIgnoreFileWithAllExtensions)
{
std::unordered_map<std::string, bool> tests = {
{ "README.md", true },
{ "doc/README.org", true },
{ "doc/compressed/README.tar.gz", true },
{ "doc/compressed/BIG_README.tar.gz", false }, // FIXME
{ "Documentation.README", false },
{ "Config.org", false },
};
std::vector<std::string> testFilters = {
"README.*",
};
testDotfileFilters(tests, testFilters);
}
TEST_CASE(DotfilesWildcardIgnoreAllWithStartingPattern)
{
std::unordered_map<std::string, bool> tests = {
{ "cmake/uninstall.cmake.in", true },
{ "cmake/templates/template.cmake.in", true },
{ "build/cmake/executable", true },
{ "build/cmakefiles/makefile", true },
{ "CMakeLists.txt", false },
};
std::vector<std::string> testFilters = {
"cmake*",
};
testDotfileFilters(tests, testFilters);
}
TEST_CASE(DotfilesWildcardIgnoreAllInRootWithStartingPattern)
{
std::unordered_map<std::string, bool> tests = {
{ "project-directory/README.org", true },
{ "project-directory/build/executable", true },
{ "project-file", true },
{ "doc/project-instructions.txt", false },
};
std::vector<std::string> testFilters = {
"/project*",
};
testDotfileFilters(tests, testFilters);
}
TEST_CASE(DotfilesWildcardIgnoreAllInDirectory)
{
std::unordered_map<std::string, bool> tests = {
{ "build/x32/executable", true },
{ "build/x64/executable", true },
{ "build/executable", true },
};
std::vector<std::string> testFilters = {
"build/*",
};
testDotfileFilters(tests, testFilters);
}
TEST_CASE(DotfilesWildcardIgnoreFileInSubDirectory)
{
std::unordered_map<std::string, bool> tests = {
{ "build/x32/executable", true },
{ "build/x64/executable", true },
{ "build/x64/config.json", false },
{ "project/build/x64/config.json", false },
{ "build/executable", false },
};
std::vector<std::string> testFilters = {
"build/*/executable",
};
testDotfileFilters(tests, testFilters);
}
TEST_CASE(DotfilesWildcardIgnoreAllInSubDirectory)
{
std::unordered_map<std::string, bool> tests = {
{ "build/x32/executable", true },
{ "build/x64/executable", true },
{ "build/x64/config.json", true },
{ "project/build/x64/config.json", true },
{ "build/executable", false },
};
std::vector<std::string> testFilters = {
"build/*/*",
};
testDotfileFilters(tests, testFilters);
}
TEST_CASE(DotfilesWildcardIgnoreAllDirectoriesWithStartingPattern)
{
std::unordered_map<std::string, bool> tests = {
{ "include/header.h", true },
{ "include-dependency/header.h", true },
{ "include-file", false },
{ "src/include/header.h", true },
};
std::vector<std::string> testFilters = {
"include*/",
};
testDotfileFilters(tests, testFilters);
}
TEST_CASE(DotfilesWildcardIgnoreAllDirectoriesInRootWithStartingPattern)
{
std::unordered_map<std::string, bool> tests = {
{ "include/header.h", true },
{ "include-dependency/header.h", true },
{ "include-file", false },
{ "src/include/header.h", false },
};
std::vector<std::string> testFilters = {
"/include*/",
};
testDotfileFilters(tests, testFilters);
}
TEST_CASE(DotfilesWildcardIgnoreAllDirectoriesWithEndingPattern)
{
std::unordered_map<std::string, bool> tests = {
{ "include/header.h", true },
{ "dependency-include/header.h", true },
{ "file-include", false },
{ "src/include/header.h", true },
};
std::vector<std::string> testFilters = {
"*include/",
};
testDotfileFilters(tests, testFilters);
}
TEST_CASE(DotfilesWildcardIgnoreAllDirectoriesInRootWithEndingPattern)
{
std::unordered_map<std::string, bool> tests = {
{ "include/header.h", true },
{ "dependency-include/header.h", true },
{ "file-include", false },
{ "src/include/header.h", false },
};
std::vector<std::string> testFilters = {
"/*include/",
};
testDotfileFilters(tests, testFilters);
}
TEST_CASE(AddDotfiles) TEST_CASE(AddDotfiles)
{ {
std::vector<std::string> fileNames = { std::vector<std::string> fileNames = {
@@ -100,8 +376,8 @@ TEST_CASE(AddDotfiles)
Dotfile::the().add(fileNames); Dotfile::the().add(fileNames);
for (const auto& file : fileNames) { for (const auto& file : fileNames) {
VERIFY(std::filesystem::exists(file), continue); EXPECT(std::filesystem::exists(file), continue);
VERIFY(std::filesystem::exists(file.substr(homeDirectorySize + 1)), continue); EXPECT(std::filesystem::exists(file.substr(homeDirectorySize + 1)), continue);
Util::File lhs(file); Util::File lhs(file);
Util::File rhs(file.substr(homeDirectorySize + 1)); Util::File rhs(file.substr(homeDirectorySize + 1));
@@ -156,8 +432,8 @@ TEST_CASE(PullDotfiles)
Dotfile::the().pull(fileNames); Dotfile::the().pull(fileNames);
for (size_t i = 0; i < fileNames.size(); ++i) { for (size_t i = 0; i < fileNames.size(); ++i) {
VERIFY(std::filesystem::exists(homeFileNames.at(i)), continue); EXPECT(std::filesystem::exists(homeFileNames.at(i)), continue);
VERIFY(std::filesystem::exists(fileNames.at(i)), continue); EXPECT(std::filesystem::exists(fileNames.at(i)), continue);
Util::File lhs(homeFileNames.at(i)); Util::File lhs(homeFileNames.at(i));
Util::File rhs(fileNames.at(i)); Util::File rhs(fileNames.at(i));
@@ -189,8 +465,8 @@ TEST_CASE(PushDotfiles)
Dotfile::the().push(fileNames); Dotfile::the().push(fileNames);
for (const auto& file : fileNames) { for (const auto& file : fileNames) {
VERIFY(std::filesystem::exists(file), continue); EXPECT(std::filesystem::exists(file), continue);
VERIFY(std::filesystem::exists(homeDirectory / file), continue); EXPECT(std::filesystem::exists(homeDirectory / file), continue);
Util::File lhs(file); Util::File lhs(file);
Util::File rhs(homeDirectory / file); Util::File rhs(homeDirectory / file);
@@ -200,7 +476,7 @@ TEST_CASE(PushDotfiles)
removeTestDotfiles(fileNames); removeTestDotfiles(fileNames);
} }
TEST_CASE(PushDotfilesWithExcludePath) TEST_CASE(PushDotfilesWithIgnorePattern)
{ {
std::vector<std::string> fileNames = { std::vector<std::string> fileNames = {
"__test-file-1", "__test-file-1",
@@ -211,13 +487,16 @@ TEST_CASE(PushDotfilesWithExcludePath)
createTestDotfiles(fileNames, { "", "", "", "" }); createTestDotfiles(fileNames, { "", "", "", "" });
Config::the().setExcludePaths({ auto ignorePatterns = Config::the().ignorePatterns();
{ "__test-file-1", "file" }, Config::the().setIgnorePatterns({
{ "__subdir", "directory" }, "__test-file-1",
{ ".test", "endsWith" }, "__subdir/",
"*.test",
}); });
Dotfile::the().push(fileNames); Dotfile::the().push(fileNames);
Config::the().setExcludePaths({});
Config::the().setIgnorePatterns(ignorePatterns);
for (const auto& file : fileNames) { for (const auto& file : fileNames) {
EXPECT(!std::filesystem::exists(homeDirectory / file)); EXPECT(!std::filesystem::exists(homeDirectory / file));
@@ -230,7 +509,7 @@ TEST_CASE(PushDotfilesSelectivelyComment)
{ {
std::vector<std::string> fileNames; std::vector<std::string> fileNames;
for (size_t i = 0; i < 36; ++i) { for (size_t i = 0; i < 36; ++i) {
fileNames.push_back( "__test-file-" + std::to_string(i + 1)); fileNames.push_back("__test-file-" + std::to_string(i + 1));
} }
auto distro = Machine::the().distroId(); auto distro = Machine::the().distroId();
@@ -580,8 +859,8 @@ test data /**/ uncomment
for (size_t i = 0; i < fileNames.size(); ++i) { for (size_t i = 0; i < fileNames.size(); ++i) {
const auto& file = fileNames.at(i); const auto& file = fileNames.at(i);
VERIFY(std::filesystem::exists(file), continue); EXPECT(std::filesystem::exists(file), continue);
VERIFY(std::filesystem::exists(homeDirectory / file), continue); EXPECT(std::filesystem::exists(homeDirectory / file), continue);
Util::File lhs(homeDirectory / file); Util::File lhs(homeDirectory / file);
EXPECT_EQ(lhs.data(), pushedFileContents.at(i)); EXPECT_EQ(lhs.data(), pushedFileContents.at(i));
@@ -592,7 +871,7 @@ test data /**/ uncomment
TEST_CASE(AddSystemDotfiles) TEST_CASE(AddSystemDotfiles)
{ {
VERIFY(geteuid() == 0, return); EXPECT(geteuid() == 0, return );
Config::the().setSystemDirectories({ "/etc", "/usr/lib" }); Config::the().setSystemDirectories({ "/etc", "/usr/lib" });
Dotfile::the().add({ "/etc/group", "/usr/lib/os-release" }); Dotfile::the().add({ "/etc/group", "/usr/lib/os-release" });
@@ -607,7 +886,7 @@ TEST_CASE(AddSystemDotfiles)
TEST_CASE(PullSystemDotfiles) TEST_CASE(PullSystemDotfiles)
{ {
VERIFY(geteuid() == 0, return); EXPECT(geteuid() == 0, return );
createTestDotfiles({ "etc/group" }, { "" }, true); createTestDotfiles({ "etc/group" }, { "" }, true);