1
0
mirror of https://github.com/chylex/Nextcloud-Desktop.git synced 2025-05-08 02:34:09 +02:00

Read .sync_exclude.lst in each subdirectory

Signed-off-by: Samir Benmendil <me@rmz.io>
This commit is contained in:
Samir Benmendil 2019-08-08 23:30:49 +01:00
parent 7843660bbf
commit 14279104ae
4 changed files with 264 additions and 95 deletions

View File

@ -236,13 +236,29 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(const char *path, bool excludeC
return match;
}
static QByteArray leftIncludeLast(const QByteArray & arr, char c)
{
// left up to and including `c`
return arr.left(arr.lastIndexOf(c, arr.size() - 2) + 1);
}
using namespace OCC;
ExcludedFiles::ExcludedFiles()
ExcludedFiles::ExcludedFiles(QString localPath)
: _localPath(std::move(localPath))
{
Q_ASSERT(_localPath.endsWith("/"));
// Windows used to use PathMatchSpec which allows *foo to match abc/deffoo.
_wildcardsMatchSlash = Utility::isWindows();
// We're in a detached exclude probably coming from a partial sync or test
if (_localPath.isEmpty())
return;
// Load exclude file from base dir
QFileInfo fi(_localPath + ".sync-exclude.lst");
if (fi.isReadable())
addInTreeExcludeFilePath(fi.absoluteFilePath());
}
ExcludedFiles::~ExcludedFiles()
@ -251,7 +267,13 @@ ExcludedFiles::~ExcludedFiles()
void ExcludedFiles::addExcludeFilePath(const QString &path)
{
_excludeFiles.insert(path);
_excludeFiles[_localPath.toUtf8()].append(path);
}
void ExcludedFiles::addInTreeExcludeFilePath(const QString &path)
{
BasePathByteArray basePath = leftIncludeLast(path.toUtf8(), '/');
_excludeFiles[basePath].append(path);
}
void ExcludedFiles::setExcludeConflictFiles(bool onoff)
@ -259,11 +281,15 @@ void ExcludedFiles::setExcludeConflictFiles(bool onoff)
_excludeConflictFiles = onoff;
}
void ExcludedFiles::addManualExclude(const QByteArray &expr)
void ExcludedFiles::addManualExclude(const QByteArray &expr, const QByteArray &basePath)
{
_manualExcludes.append(expr);
_allExcludes.append(expr);
prepare();
Q_ASSERT(basePath.startsWith('/'));
Q_ASSERT(basePath.endsWith('/'));
auto key = basePath;
_manualExcludes[key].append(expr);
_allExcludes[key].append(expr);
prepare(key);
}
void ExcludedFiles::clearManualExcludes()
@ -282,21 +308,27 @@ bool ExcludedFiles::reloadExcludeFiles()
{
_allExcludes.clear();
bool success = true;
foreach (const QString &file, _excludeFiles) {
QFile f(file);
if (!f.open(QIODevice::ReadOnly)) {
success = false;
continue;
}
while (!f.atEnd()) {
QByteArray line = f.readLine().trimmed();
if (line.isEmpty() || line.startsWith('#'))
for (auto basePath : _excludeFiles.keys()) {
for (auto file : _excludeFiles.value(basePath)) {
QFile f(file);
if (!f.open(QIODevice::ReadOnly)) {
success = false;
continue;
csync_exclude_expand_escapes(line);
_allExcludes.append(line);
}
while (!f.atEnd()) {
QByteArray line = f.readLine().trimmed();
if (line.isEmpty() || line.startsWith('#'))
continue;
csync_exclude_expand_escapes(line);
_allExcludes[basePath].append(line);
}
}
}
_allExcludes.append(_manualExcludes);
auto endManual = _manualExcludes.cend();
for (auto kv = _manualExcludes.cbegin(); kv != endManual; ++kv)
_allExcludes[kv.key()].append(kv.value());
prepare();
return success;
}
@ -317,6 +349,8 @@ bool ExcludedFiles::isExcluded(
// We do want to be able to sync with a hidden folder as the target.
while (path.size() > basePath.size()) {
QFileInfo fi(path);
//TODO probably not ignore `.sync-exclude.lst` files as it makes sense for them to be
//synced
if (fi.isHidden() || fi.fileName().startsWith(QLatin1Char('.'))) {
return true;
}
@ -340,7 +374,7 @@ bool ExcludedFiles::isExcluded(
return fullPatternMatch(relativePath.toUtf8(), type) != CSYNC_NOT_EXCLUDED;
}
CSYNC_EXCLUDE_TYPE ExcludedFiles::traversalPatternMatch(const char *path, ItemType filetype) const
CSYNC_EXCLUDE_TYPE ExcludedFiles::traversalPatternMatch(const char *path, ItemType filetype)
{
auto match = _csync_excluded_common(path, _excludeConflictFiles);
if (match != CSYNC_NOT_EXCLUDED)
@ -348,6 +382,16 @@ CSYNC_EXCLUDE_TYPE ExcludedFiles::traversalPatternMatch(const char *path, ItemTy
if (_allExcludes.isEmpty())
return CSYNC_NOT_EXCLUDED;
// Directories are guaranteed to be visited before their files
if (filetype == ItemTypeDirectory) {
QFileInfo fi = QFileInfo(_localPath + path + "/.sync-exclude.lst");
if (fi.isReadable()) {
addInTreeExcludeFilePath(fi.absoluteFilePath());
//really we only need to load this file and prepare(this basePath)
reloadExcludeFiles();
}
}
// Check the bname part of the path to see whether the full
// regex should be run.
@ -359,35 +403,53 @@ CSYNC_EXCLUDE_TYPE ExcludedFiles::traversalPatternMatch(const char *path, ItemTy
}
QString bnameStr = QString::fromUtf8(bname);
QRegularExpressionMatch m;
if (filetype == ItemTypeDirectory) {
m = _bnameTraversalRegexDir.match(bnameStr);
} else {
m = _bnameTraversalRegexFile.match(bnameStr);
}
if (!m.hasMatch())
return CSYNC_NOT_EXCLUDED;
if (m.capturedStart(QStringLiteral("exclude")) != -1) {
return CSYNC_FILE_EXCLUDE_LIST;
} else if (m.capturedStart(QStringLiteral("excluderemove")) != -1) {
return CSYNC_FILE_EXCLUDE_AND_REMOVE;
}
QByteArray basePath(_localPath.toUtf8() + path);
while (basePath.size() > _localPath.size()) {
basePath = leftIncludeLast(basePath, '/');
QRegularExpressionMatch m;
if (filetype == ItemTypeDirectory
&& _bnameTraversalRegexDir.contains(basePath)) {
m = _bnameTraversalRegexDir[basePath].match(bnameStr);
} else if (filetype == ItemTypeFile
&& _bnameTraversalRegexFile.contains(basePath)) {
m = _bnameTraversalRegexFile[basePath].match(bnameStr);
} else {
continue;
}
// third capture: full path matching is triggered
QString pathStr = QString::fromUtf8(path);
if (filetype == ItemTypeDirectory) {
m = _fullTraversalRegexDir.match(pathStr);
} else {
m = _fullTraversalRegexFile.match(pathStr);
}
if (m.hasMatch()) {
if (!m.hasMatch())
return CSYNC_NOT_EXCLUDED;
if (m.capturedStart(QStringLiteral("exclude")) != -1) {
return CSYNC_FILE_EXCLUDE_LIST;
} else if (m.capturedStart(QStringLiteral("excluderemove")) != -1) {
return CSYNC_FILE_EXCLUDE_AND_REMOVE;
}
}
// third capture: full path matching is triggered
QString pathStr = QString::fromUtf8(path);
basePath = _localPath.toUtf8() + path;
while (basePath.size() > _localPath.size()) {
basePath = leftIncludeLast(basePath, '/');
QRegularExpressionMatch m;
if (filetype == ItemTypeDirectory
&& _fullTraversalRegexDir.contains(basePath)) {
m = _fullTraversalRegexDir[basePath].match(pathStr);
} else if (filetype == ItemTypeFile
&& _fullTraversalRegexFile.contains(basePath)) {
m = _fullTraversalRegexFile[basePath].match(pathStr);
} else {
continue;
}
if (m.hasMatch()) {
if (m.capturedStart(QStringLiteral("exclude")) != -1) {
return CSYNC_FILE_EXCLUDE_LIST;
} else if (m.capturedStart(QStringLiteral("excluderemove")) != -1) {
return CSYNC_FILE_EXCLUDE_AND_REMOVE;
}
}
}
return CSYNC_NOT_EXCLUDED;
}
@ -400,23 +462,38 @@ CSYNC_EXCLUDE_TYPE ExcludedFiles::fullPatternMatch(const char *path, ItemType fi
return CSYNC_NOT_EXCLUDED;
QString p = QString::fromUtf8(path);
QRegularExpressionMatch m;
if (filetype == ItemTypeDirectory) {
m = _fullRegexDir.match(p);
} else {
m = _fullRegexFile.match(p);
}
if (m.hasMatch()) {
if (m.capturedStart(QStringLiteral("exclude")) != -1) {
return CSYNC_FILE_EXCLUDE_LIST;
} else if (m.capturedStart(QStringLiteral("excluderemove")) != -1) {
return CSYNC_FILE_EXCLUDE_AND_REMOVE;
// `path` seems to always be relative to `_localPath`, the tests however have not been
// written that way... this makes the tests happy for now. TODO Fix the tests at some point
if (path[0] == '/')
++path;
QByteArray basePath(_localPath.toUtf8() + path);
while (basePath.size() > _localPath.size()) {
basePath = leftIncludeLast(basePath, '/');
QRegularExpressionMatch m;
if (filetype == ItemTypeDirectory
&& _fullRegexDir.contains(basePath)) {
m = _fullRegexDir[basePath].match(p);
} else if (filetype == ItemTypeFile
&& _fullRegexFile.contains(basePath)) {
m = _fullRegexFile[basePath].match(p);
} else {
continue;
}
if (m.hasMatch()) {
if (m.capturedStart(QStringLiteral("exclude")) != -1) {
return CSYNC_FILE_EXCLUDE_LIST;
} else if (m.capturedStart(QStringLiteral("excluderemove")) != -1) {
return CSYNC_FILE_EXCLUDE_AND_REMOVE;
}
}
}
return CSYNC_NOT_EXCLUDED;
}
auto ExcludedFiles::csyncTraversalMatchFun() const
auto ExcludedFiles::csyncTraversalMatchFun()
-> std::function<CSYNC_EXCLUDE_TYPE(const char *path, ItemType filetype)>
{
return [this](const char *path, ItemType filetype) { return this->traversalPatternMatch(path, filetype); };
@ -555,6 +632,22 @@ static QString extractBnameTrigger(const QString &exclude, bool wildcardsMatchSl
void ExcludedFiles::prepare()
{
// clear all regex
_bnameTraversalRegexFile.clear();
_bnameTraversalRegexDir.clear();
_fullTraversalRegexFile.clear();
_fullTraversalRegexDir.clear();
_fullRegexFile.clear();
_fullRegexDir.clear();
for (auto const & basePath : _allExcludes.keys())
prepare(basePath);
}
void ExcludedFiles::prepare(const BasePathByteArray & basePath)
{
Q_ASSERT(_allExcludes.contains(basePath));
// Build regular expressions for the different cases.
//
// To compose the _bnameTraversalRegex, _fullTraversalRegex and _fullRegex
@ -596,7 +689,7 @@ void ExcludedFiles::prepare()
pattern.append(appendMe);
};
for (auto exclude : _allExcludes) {
for (auto exclude : _allExcludes.value(basePath)) {
if (exclude[0] == '\n')
continue; // empty line
if (exclude[0] == '\r')
@ -654,11 +747,11 @@ void ExcludedFiles::prepare()
// (exclude)|(excluderemove)|(bname triggers).
// If the third group matches, the fullActivatedRegex needs to be applied
// to the full path.
_bnameTraversalRegexFile.setPattern(
_bnameTraversalRegexFile[basePath].setPattern(
"^(?P<exclude>" + bnameFileDirKeep + ")$|"
+ "^(?P<excluderemove>" + bnameFileDirRemove + ")$|"
+ "^(?P<trigger>" + bnameTriggerFileDir + ")$");
_bnameTraversalRegexDir.setPattern(
_bnameTraversalRegexDir[basePath].setPattern(
"^(?P<exclude>" + bnameFileDirKeep + "|" + bnameDirKeep + ")$|"
+ "^(?P<excluderemove>" + bnameFileDirRemove + "|" + bnameDirRemove + ")$|"
+ "^(?P<trigger>" + bnameTriggerFileDir + "|" + bnameTriggerDir + ")$");
@ -667,13 +760,13 @@ void ExcludedFiles::prepare()
// the bname regex matches. Its basic form is (exclude)|(excluderemove)".
// This pattern can be much simpler than fullRegex since we can assume a traversal
// situation and doesn't need to look for bname patterns in parent paths.
_fullTraversalRegexFile.setPattern(
_fullTraversalRegexFile[basePath].setPattern(
QLatin1String("")
// Full patterns are anchored to the beginning
+ "^(?P<exclude>" + fullFileDirKeep + ")(?:$|/)"
+ "|"
+ "^(?P<excluderemove>" + fullFileDirRemove + ")(?:$|/)");
_fullTraversalRegexDir.setPattern(
_fullTraversalRegexDir[basePath].setPattern(
QLatin1String("")
+ "^(?P<exclude>" + fullFileDirKeep + "|" + fullDirKeep + ")(?:$|/)"
+ "|"
@ -681,7 +774,7 @@ void ExcludedFiles::prepare()
// The full regex is applied to the full path and incorporates both bname and
// full-path patterns. It has the form "(exclude)|(excluderemove)".
_fullRegexFile.setPattern(
_fullRegexFile[basePath].setPattern(
QLatin1String("(?P<exclude>")
// Full patterns are anchored to the beginning
+ "^(?:" + fullFileDirKeep + ")(?:$|/)" + "|"
@ -697,7 +790,7 @@ void ExcludedFiles::prepare()
+ "(?:^|/)(?:" + bnameFileDirRemove + ")(?:$|/)" + "|"
+ "(?:^|/)(?:" + bnameDirRemove + ")/"
+ ")");
_fullRegexDir.setPattern(
_fullRegexDir[basePath].setPattern(
QLatin1String("(?P<exclude>")
+ "^(?:" + fullFileDirKeep + "|" + fullDirKeep + ")(?:$|/)" + "|"
+ "(?:^|/)(?:" + bnameFileDirKeep + "|" + bnameDirKeep + ")(?:$|/)"
@ -711,16 +804,16 @@ void ExcludedFiles::prepare()
QRegularExpression::PatternOptions patternOptions = QRegularExpression::NoPatternOption;
if (OCC::Utility::fsCasePreserving())
patternOptions |= QRegularExpression::CaseInsensitiveOption;
_bnameTraversalRegexFile.setPatternOptions(patternOptions);
_bnameTraversalRegexFile.optimize();
_bnameTraversalRegexDir.setPatternOptions(patternOptions);
_bnameTraversalRegexDir.optimize();
_fullTraversalRegexFile.setPatternOptions(patternOptions);
_fullTraversalRegexFile.optimize();
_fullTraversalRegexDir.setPatternOptions(patternOptions);
_fullTraversalRegexDir.optimize();
_fullRegexFile.setPatternOptions(patternOptions);
_fullRegexFile.optimize();
_fullRegexDir.setPatternOptions(patternOptions);
_fullRegexDir.optimize();
_bnameTraversalRegexFile[basePath].setPatternOptions(patternOptions);
_bnameTraversalRegexFile[basePath].optimize();
_bnameTraversalRegexDir[basePath].setPatternOptions(patternOptions);
_bnameTraversalRegexDir[basePath].optimize();
_fullTraversalRegexFile[basePath].setPatternOptions(patternOptions);
_fullTraversalRegexFile[basePath].optimize();
_fullTraversalRegexDir[basePath].setPatternOptions(patternOptions);
_fullTraversalRegexDir[basePath].optimize();
_fullRegexFile[basePath].setPatternOptions(patternOptions);
_fullRegexFile[basePath].optimize();
_fullRegexDir[basePath].setPatternOptions(patternOptions);
_fullRegexDir[basePath].optimize();
}

View File

@ -66,7 +66,7 @@ class OCSYNC_EXPORT ExcludedFiles : public QObject
{
Q_OBJECT
public:
ExcludedFiles();
ExcludedFiles(QString localPath = "/");
~ExcludedFiles();
/**
@ -75,6 +75,7 @@ public:
* Does not load the file. Use reloadExcludeFiles() afterwards.
*/
void addExcludeFilePath(const QString &path);
void addInTreeExcludeFilePath(const QString &path);
/**
* Whether conflict files shall be excluded.
@ -95,12 +96,12 @@ public:
bool excludeHidden) const;
/**
* Adds an exclude pattern.
* Adds an exclude pattern anchored to base path
*
* Primarily used in tests. Patterns added this way are preserved when
* reloadExcludeFiles() is called.
*/
void addManualExclude(const QByteArray &expr);
void addManualExclude(const QByteArray &expr, const QByteArray &basePath = "/");
/**
* Removes all manually added exclude patterns.
@ -121,7 +122,7 @@ public:
* Careful: The function will only be valid for as long as this
* ExcludedFiles instance stays alive.
*/
auto csyncTraversalMatchFun() const
auto csyncTraversalMatchFun()
-> std::function<CSYNC_EXCLUDE_TYPE(const char *path, ItemType filetype)>;
public slots:
@ -156,10 +157,32 @@ private:
* Note that this only matches patterns. It does not check whether the file
* or directory pointed to is hidden (or whether it even exists).
*/
CSYNC_EXCLUDE_TYPE traversalPatternMatch(const char *path, ItemType filetype) const;
CSYNC_EXCLUDE_TYPE traversalPatternMatch(const char *path, ItemType filetype);
// Our BasePath need to end with '/'
class BasePathByteArray : public QByteArray
{
public:
BasePathByteArray(QByteArray && other)
: QByteArray(std::move(other))
{
Q_ASSERT(this->endsWith('/'));
}
BasePathByteArray(const QByteArray & other)
: QByteArray(other)
{
Q_ASSERT(this->endsWith('/'));
}
BasePathByteArray(const char * data, int size = -1)
: BasePathByteArray(QByteArray(data, size))
{
}
};
/**
* Generate optimized regular expressions for the exclude patterns.
* Generate optimized regular expressions for the exclude patterns anchored to basePath.
*
* The optimization works in two steps: First, all supported patterns are put
* into _fullRegexFile/_fullRegexDir. These regexes can be applied to the full
@ -187,24 +210,28 @@ private:
* full matcher would exclude. Example: "b" is excluded. traversal("b/c")
* returns not-excluded because "c" isn't a bname activation pattern.
*/
void prepare(const BasePathByteArray & basePath);
void prepare();
QString _localPath;
/// Files to load excludes from
QSet<QString> _excludeFiles;
QMap<BasePathByteArray, QList<QString>> _excludeFiles;
/// Exclude patterns added with addManualExclude()
QList<QByteArray> _manualExcludes;
QMap<BasePathByteArray, QList<QByteArray>> _manualExcludes;
/// List of all active exclude patterns
QList<QByteArray> _allExcludes;
QMap<BasePathByteArray, QList<QByteArray>> _allExcludes;
/// see prepare()
QRegularExpression _bnameTraversalRegexFile;
QRegularExpression _bnameTraversalRegexDir;
QRegularExpression _fullTraversalRegexFile;
QRegularExpression _fullTraversalRegexDir;
QRegularExpression _fullRegexFile;
QRegularExpression _fullRegexDir;
QMap<BasePathByteArray, QRegularExpression> _bnameTraversalRegexFile;
QMap<BasePathByteArray, QRegularExpression> _bnameTraversalRegexDir;
QMap<BasePathByteArray, QRegularExpression> _fullTraversalRegexFile;
QMap<BasePathByteArray, QRegularExpression> _fullTraversalRegexDir;
QMap<BasePathByteArray, QRegularExpression> _fullRegexFile;
QMap<BasePathByteArray, QRegularExpression> _fullRegexDir;
bool _excludeConflictFiles = true;

View File

@ -91,7 +91,7 @@ SyncEngine::SyncEngine(AccountPtr account, const QString &localPath,
_csync_ctx.reset(new CSYNC(localPath.toUtf8().data(), journal));
_excludedFiles.reset(new ExcludedFiles);
_excludedFiles.reset(new ExcludedFiles(localPath));
_csync_ctx->exclude_traversal_fn = _excludedFiles->csyncTraversalMatchFun();
_syncFileStatusTracker.reset(new SyncFileStatusTracker(this));

View File

@ -21,6 +21,7 @@
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <stdio.h>
#define CSYNC_TEST 1
#include "csync_exclude.cpp"
@ -115,16 +116,27 @@ static void check_csync_exclude_add(void **)
excludedFiles->addManualExclude("/tmp/check_csync1/*");
assert_int_equal(check_file_full("/tmp/check_csync1/foo"), CSYNC_FILE_EXCLUDE_LIST);
assert_int_equal(check_file_full("/tmp/check_csync2/foo"), CSYNC_NOT_EXCLUDED);
assert_true(excludedFiles->_allExcludes.contains("/tmp/check_csync1/*"));
assert_true(excludedFiles->_allExcludes["/"].contains("/tmp/check_csync1/*"));
assert_true(excludedFiles->_fullRegexFile.pattern().contains("csync1"));
assert_true(excludedFiles->_fullTraversalRegexFile.pattern().contains("csync1"));
assert_false(excludedFiles->_bnameTraversalRegexFile.pattern().contains("csync1"));
assert_true(excludedFiles->_fullRegexFile["/"].pattern().contains("csync1"));
assert_true(excludedFiles->_fullTraversalRegexFile["/"].pattern().contains("csync1"));
assert_false(excludedFiles->_bnameTraversalRegexFile["/"].pattern().contains("csync1"));
excludedFiles->addManualExclude("foo");
assert_true(excludedFiles->_bnameTraversalRegexFile.pattern().contains("foo"));
assert_true(excludedFiles->_fullRegexFile.pattern().contains("foo"));
assert_false(excludedFiles->_fullTraversalRegexFile.pattern().contains("foo"));
assert_true(excludedFiles->_bnameTraversalRegexFile["/"].pattern().contains("foo"));
assert_true(excludedFiles->_fullRegexFile["/"].pattern().contains("foo"));
assert_false(excludedFiles->_fullTraversalRegexFile["/"].pattern().contains("foo"));
}
static void check_csync_exclude_add_per_dir(void **)
{
excludedFiles->addManualExclude("*", "/tmp/check_csync1/");
assert_int_equal(check_file_full("/tmp/check_csync1/foo"), CSYNC_FILE_EXCLUDE_LIST);
assert_int_equal(check_file_full("/tmp/check_csync2/foo"), CSYNC_NOT_EXCLUDED);
assert_true(excludedFiles->_allExcludes["/tmp/check_csync1/"].contains("*"));
excludedFiles->addManualExclude("foo");
assert_true(excludedFiles->_fullRegexFile["/"].pattern().contains("foo"));
}
static void check_csync_excluded(void **)
@ -232,6 +244,41 @@ static void check_csync_excluded(void **)
assert_int_equal(check_file_full("c [d]"), CSYNC_FILE_EXCLUDE_LIST);
}
static void check_csync_excluded_per_dir(void **)
{
excludedFiles->addManualExclude("A");
excludedFiles->reloadExcludeFiles();
assert_int_equal(check_file_full("A"), CSYNC_FILE_EXCLUDE_LIST);
excludedFiles->clearManualExcludes();
excludedFiles->addManualExclude("A", "/B/");
excludedFiles->reloadExcludeFiles();
assert_int_equal(check_file_full("A"), CSYNC_NOT_EXCLUDED);
assert_int_equal(check_file_full("B/A"), CSYNC_FILE_EXCLUDE_LIST);
#define FOO_DIR "/tmp/check_csync1/foo"
#define FOO_EXCLUDE_LIST FOO_DIR "/.sync-exclude.lst"
int rc;
rc = system("mkdir -p " FOO_DIR);
assert_int_equal(rc, 0);
FILE *fh = fopen(FOO_EXCLUDE_LIST, "w");
assert_non_null(fh);
rc = fprintf(fh, "bar");
assert_int_not_equal(rc, 0);
rc = fclose(fh);
assert_int_equal(rc, 0);
excludedFiles->addInTreeExcludeFilePath(FOO_EXCLUDE_LIST);
excludedFiles->reloadExcludeFiles();
assert_int_equal(check_file_full(FOO_DIR), CSYNC_NOT_EXCLUDED);
assert_int_equal(check_file_full(FOO_DIR "/bar"), CSYNC_FILE_EXCLUDE_LIST);
assert_int_equal(check_file_full(FOO_DIR "/baz"), CSYNC_NOT_EXCLUDED);
#undef FOO_DIR
#undef FOO_EXCLUDE_LIST
}
static void check_csync_excluded_traversal(void **)
{
assert_int_equal(check_file_traversal(""), CSYNC_NOT_EXCLUDED);
@ -633,7 +680,9 @@ int torture_run_tests(void)
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(T::check_csync_exclude_add, T::setup, T::teardown),
cmocka_unit_test_setup_teardown(T::check_csync_exclude_add_per_dir, T::setup, T::teardown),
cmocka_unit_test_setup_teardown(T::check_csync_excluded, T::setup_init, T::teardown),
cmocka_unit_test_setup_teardown(T::check_csync_excluded_per_dir, T::setup, T::teardown),
cmocka_unit_test_setup_teardown(T::check_csync_excluded_traversal, T::setup_init, T::teardown),
cmocka_unit_test_setup_teardown(T::check_csync_dir_only, T::setup, T::teardown),
cmocka_unit_test_setup_teardown(T::check_csync_pathes, T::setup_init, T::teardown),