Files
QSfera/Desktop/test/testexcludedfiles.cpp
Курнат Андрей 2315f25754 Initial QSfera import
2026-06-07 10:20:04 +03:00

648 lines
33 KiB
C++

/*
* This software is in the public domain, furnished "as is", without technical
* support, and with no warranty, express or implied, as to its usefulness for
* any purpose.
*
*/
#include "libsync/configfile.h"
#include <QtTest>
#include "libsync/csync_exclude.h"
#include "testutils.h"
using namespace OCC;
// The tests were converted from the old CMocka framework, that's why there is a global
static QScopedPointer<ExcludedFiles> excludedFiles;
static void setup() {
excludedFiles.reset(new ExcludedFiles);
excludedFiles->setWildcardsMatchSlash(false);
}
static void setup_init() {
setup();
excludedFiles->addExcludeFilePath(ConfigFile::defaultExcludeFile());
QVERIFY(excludedFiles->reloadExcludeFiles());
/* and add some unicode stuff */
excludedFiles->addManualExclude(QStringLiteral("*.💩")); // is this source file utf8 encoded?
excludedFiles->addManualExclude(QStringLiteral("пятницы.*"));
excludedFiles->addManualExclude(QStringLiteral("*/*.out"));
excludedFiles->addManualExclude(QStringLiteral("latex*/*.run.xml"));
excludedFiles->addManualExclude(QStringLiteral("latex/*/*.tex.tmp"));
QVERIFY(excludedFiles->reloadExcludeFiles());
}
class TestExcludedFiles: public QObject
{
Q_OBJECT
static auto check_file_full(const QString &path) { return excludedFiles->fullPatternMatch(path, ItemTypeFile); }
static auto check_dir_full(const QString &path) { return excludedFiles->fullPatternMatch(path, ItemTypeDirectory); }
static auto check_file_traversal(const QString &path) { return excludedFiles->traversalPatternMatch(path, ItemTypeFile); }
static auto check_dir_traversal(const QString &path) { return excludedFiles->traversalPatternMatch(path, ItemTypeDirectory); }
private Q_SLOTS:
void testFun()
{
ExcludedFiles excluded;
bool excludeHidden = true;
bool keepHidden = false;
auto check_isExcluded = [&](const QString &a, bool keepHidden, bool create = true) {
auto tmp = OCC::TestUtils::createTempDir();
Q_ASSERT(tmp.isValid());
auto createTree = [&](const QString &path) {
auto parts = path.split(QLatin1Char('/'), Qt::SkipEmptyParts);
const auto fileName = parts.back();
parts.pop_back(); // remove file name
QString workPath = tmp.path() + QLatin1Char('/');
for (const auto &p : std::as_const(parts)) {
QDir dir(workPath);
dir.mkdir(p);
workPath += p + QLatin1Char('/');
}
workPath += fileName;
QFile file(workPath);
QVERIFY(file.open(QFile::WriteOnly));
file.write("QSfera");
file.close();
};
if (create) {
createTree(a);
}
return excluded.isExcluded(QString{tmp.path() + a}, QString{tmp.path() + QStringLiteral("/a")}, keepHidden);
};
QVERIFY(!check_isExcluded(QStringLiteral("/a/b"), keepHidden));
QVERIFY(!check_isExcluded(QStringLiteral("/a/b~"), keepHidden));
QVERIFY(!check_isExcluded(QStringLiteral("/a/.b"), keepHidden));
QVERIFY(check_isExcluded(QStringLiteral("/a/.b"), excludeHidden));
excluded.addExcludeFilePath(ConfigFile::defaultExcludeFile());
excluded.reloadExcludeFiles();
QVERIFY(!check_isExcluded(QStringLiteral("/a/b"), keepHidden));
QVERIFY(check_isExcluded(QStringLiteral("/a/b~"), keepHidden));
QVERIFY(!check_isExcluded(QStringLiteral("/a/.b"), keepHidden));
QVERIFY(check_isExcluded(QStringLiteral("/a/.Trashes"), keepHidden));
QVERIFY(check_isExcluded(QStringLiteral("/a/foo_conflict-bar"), keepHidden));
QVERIFY(check_isExcluded(QStringLiteral("/a/foo (conflicted copy bar)"), keepHidden));
QVERIFY(check_isExcluded(QStringLiteral("/a/.b"), excludeHidden));
// test non existing folder
QVERIFY(check_isExcluded(QStringLiteral("/a/.b"), excludeHidden, false));
}
void check_csync_exclude_add()
{
setup();
excludedFiles->addManualExclude(QStringLiteral("/tmp/check_csync1/*"));
QCOMPARE(check_file_full(QStringLiteral("/tmp/check_csync1/foo")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_full(QStringLiteral("/tmp/check_csync2/foo")), CSYNC_NOT_EXCLUDED);
QVERIFY(excludedFiles->_allExcludes.contains(QStringLiteral("/tmp/check_csync1/*")));
QVERIFY(excludedFiles->_fullRegexFile.pattern().contains(QStringLiteral("csync1")));
QVERIFY(excludedFiles->_fullTraversalRegexFile.pattern().contains(QStringLiteral("csync1")));
QVERIFY(!excludedFiles->_bnameTraversalRegexFile.pattern().contains(QStringLiteral("csync1")));
excludedFiles->addManualExclude(QStringLiteral("foo"));
QVERIFY(excludedFiles->_bnameTraversalRegexFile.pattern().contains(QStringLiteral("foo")));
QVERIFY(excludedFiles->_fullRegexFile.pattern().contains(QStringLiteral("foo")));
QVERIFY(!excludedFiles->_fullTraversalRegexFile.pattern().contains(QStringLiteral("foo")));
}
void check_csync_excluded()
{
setup_init();
QCOMPARE(check_file_full(QString()), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral("/")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral("A")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral("krawel_krawel")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral(".kde/share/config/kwin.eventsrc")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral(".directory/cache-maximegalon/cache1.txt")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_dir_full(QStringLiteral("mozilla/.directory")), CSYNC_FILE_EXCLUDE_LIST);
/*
* Test for patterns in subdirs. '.beagle' is defined as a pattern and has
* to be found in top dir as well as in directories underneath.
*/
QCOMPARE(check_dir_full(QStringLiteral(".apdisk")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_dir_full(QStringLiteral("foo/.apdisk")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_dir_full(QStringLiteral("foo/bar/.apdisk")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_full(QStringLiteral(".java")), CSYNC_NOT_EXCLUDED);
/* Files in the ignored dir .java will also be ignored. */
QCOMPARE(check_file_full(QStringLiteral(".apdisk/totally_amazing.jar")), CSYNC_FILE_EXCLUDE_LIST);
/* and also in subdirs */
QCOMPARE(check_file_full(QStringLiteral("projects/.apdisk/totally_amazing.jar")), CSYNC_FILE_EXCLUDE_LIST);
/* csync-journal is ignored in general silently. */
QCOMPARE(check_file_full(QStringLiteral(".csync_journal.db")), CSYNC_FILE_SILENTLY_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral(".csync_journal.db.ctmp")), CSYNC_FILE_SILENTLY_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral("subdir/.csync_journal.db")), CSYNC_FILE_SILENTLY_EXCLUDED);
/* also the new form of the database name */
QCOMPARE(check_file_full(QStringLiteral("._sync_5bdd60bdfcfa.db")), CSYNC_FILE_SILENTLY_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral("._sync_5bdd60bdfcfa.db.ctmp")), CSYNC_FILE_SILENTLY_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral("._sync_5bdd60bdfcfa.db-shm")), CSYNC_FILE_SILENTLY_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral("subdir/._sync_5bdd60bdfcfa.db")), CSYNC_FILE_SILENTLY_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral(".sync_5bdd60bdfcfa.db")), CSYNC_FILE_SILENTLY_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral(".sync_5bdd60bdfcfa.db.ctmp")), CSYNC_FILE_SILENTLY_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral(".sync_5bdd60bdfcfa.db-shm")), CSYNC_FILE_SILENTLY_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral("subdir/.sync_5bdd60bdfcfa.db")), CSYNC_FILE_SILENTLY_EXCLUDED);
/* pattern ]*.directory - ignore and remove */
QCOMPARE(check_file_full(QStringLiteral("my.~directory")), CSYNC_FILE_EXCLUDE_AND_REMOVE);
QCOMPARE(check_file_full(QStringLiteral("/a_folder/my.~directory")), CSYNC_FILE_EXCLUDE_AND_REMOVE);
/* Not excluded because the pattern .netscape/cache requires directory. */
QCOMPARE(check_file_full(QStringLiteral(".netscape/cache")), CSYNC_NOT_EXCLUDED);
/* Not excluded */
QCOMPARE(check_file_full(QStringLiteral("unicode/中文.hé")), CSYNC_NOT_EXCLUDED);
/* excluded */
QCOMPARE(check_file_full(QStringLiteral("unicode/пятницы.txt")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_full(QStringLiteral("unicode/中文.💩")), CSYNC_FILE_EXCLUDE_LIST);
/* path wildcards */
QCOMPARE(check_file_full(QStringLiteral("foobar/my_manuscript.out")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_full(QStringLiteral("latex_tmp/my_manuscript.run.xml")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_full(QStringLiteral("word_tmp/my_manuscript.run.xml")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral("latex/my_manuscript.tex.tmp")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral("latex/songbook/my_manuscript.tex.tmp")), CSYNC_FILE_EXCLUDE_LIST);
#ifdef _WIN32
QCOMPARE(check_file_full(QStringLiteral("file_trailing_space ")), CSYNC_FILE_EXCLUDE_TRAILING_SPACE);
QCOMPARE(check_file_full(QStringLiteral("file_trailing_dot.")), CSYNC_FILE_EXCLUDE_INVALID_CHAR);
QCOMPARE(check_file_full(QStringLiteral("AUX")), CSYNC_FILE_EXCLUDE_INVALID_CHAR);
QCOMPARE(check_file_full(QStringLiteral("file_invalid_char<")), CSYNC_FILE_EXCLUDE_INVALID_CHAR);
QCOMPARE(check_file_full(QStringLiteral("file_invalid_char\n")), CSYNC_FILE_EXCLUDE_INVALID_CHAR);
#endif
/* ? character */
excludedFiles->addManualExclude(QStringLiteral("bond00?"));
excludedFiles->reloadExcludeFiles();
QCOMPARE(check_file_full(QStringLiteral("bond00")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral("bond007")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_full(QStringLiteral("bond0071")), CSYNC_NOT_EXCLUDED);
/* brackets */
excludedFiles->addManualExclude(QStringLiteral("a [bc] d"));
excludedFiles->reloadExcludeFiles();
QCOMPARE(check_file_full(QStringLiteral("a d d")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral("a d")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral("a b d")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_full(QStringLiteral("a c d")), CSYNC_FILE_EXCLUDE_LIST);
#ifndef Q_OS_WIN // Because of CSYNC_FILE_EXCLUDE_INVALID_CHAR on windows
/* escapes */
excludedFiles->addManualExclude(QStringLiteral("a \\*"));
excludedFiles->addManualExclude(QStringLiteral("b \\?"));
excludedFiles->addManualExclude(QStringLiteral("c \\[d]"));
excludedFiles->reloadExcludeFiles();
QCOMPARE(check_file_full(QStringLiteral("a \\*")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral("a bc")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral("a *")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_full(QStringLiteral("b \\?")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral("b f")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral("b ?")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_full(QStringLiteral("c \\[d]")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral("c d")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_full(QStringLiteral("c [d]")), CSYNC_FILE_EXCLUDE_LIST);
#endif
}
void check_csync_excluded_traversal()
{
setup_init();
QCOMPARE(check_file_traversal(QString()), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("/")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("A")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("krawel_krawel")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral(".kde/share/config/kwin.eventsrc")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_dir_traversal(QStringLiteral("mozilla/.directory")), CSYNC_FILE_EXCLUDE_LIST);
/*
* Test for patterns in subdirs. '.beagle' is defined as a pattern and has
* to be found in top dir as well as in directories underneath.
*/
QCOMPARE(check_dir_traversal(QStringLiteral(".apdisk")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_dir_traversal(QStringLiteral("foo/.apdisk")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_dir_traversal(QStringLiteral("foo/bar/.apdisk")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral(".java")), CSYNC_NOT_EXCLUDED);
/* csync-journal is ignored in general silently. */
QCOMPARE(check_file_traversal(QStringLiteral(".csync_journal.db")), CSYNC_FILE_SILENTLY_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral(".csync_journal.db.ctmp")), CSYNC_FILE_SILENTLY_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("subdir/.csync_journal.db")), CSYNC_FILE_SILENTLY_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("/two/subdir/.csync_journal.db")), CSYNC_FILE_SILENTLY_EXCLUDED);
/* also the new form of the database name */
QCOMPARE(check_file_traversal(QStringLiteral("._sync_5bdd60bdfcfa.db")), CSYNC_FILE_SILENTLY_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("._sync_5bdd60bdfcfa.db.ctmp")), CSYNC_FILE_SILENTLY_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("._sync_5bdd60bdfcfa.db-shm")), CSYNC_FILE_SILENTLY_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("subdir/._sync_5bdd60bdfcfa.db")), CSYNC_FILE_SILENTLY_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral(".sync_5bdd60bdfcfa.db")), CSYNC_FILE_SILENTLY_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral(".sync_5bdd60bdfcfa.db.ctmp")), CSYNC_FILE_SILENTLY_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral(".sync_5bdd60bdfcfa.db-shm")), CSYNC_FILE_SILENTLY_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("subdir/.sync_5bdd60bdfcfa.db")), CSYNC_FILE_SILENTLY_EXCLUDED);
/* Other builtin excludes */
QCOMPARE(check_file_traversal(QStringLiteral("foo/Desktop.ini")), CSYNC_FILE_SILENTLY_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("Desktop.ini")), CSYNC_FILE_SILENTLY_EXCLUDED);
/* pattern ]*.directory - ignore and remove */
QCOMPARE(check_file_traversal(QStringLiteral("my.~directory")), CSYNC_FILE_EXCLUDE_AND_REMOVE);
QCOMPARE(check_file_traversal(QStringLiteral("/a_folder/my.~directory")), CSYNC_FILE_EXCLUDE_AND_REMOVE);
/* Not excluded because the pattern .netscape/cache requires directory. */
QCOMPARE(check_file_traversal(QStringLiteral(".netscape/cache")), CSYNC_NOT_EXCLUDED);
/* Not excluded */
QCOMPARE(check_file_traversal(QStringLiteral("unicode/中文.hé")), CSYNC_NOT_EXCLUDED);
/* excluded */
QCOMPARE(check_file_traversal(QStringLiteral("unicode/пятницы.txt")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("unicode/中文.💩")), CSYNC_FILE_EXCLUDE_LIST);
/* path wildcards */
QCOMPARE(check_file_traversal(QStringLiteral("foobar/my_manuscript.out")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("latex_tmp/my_manuscript.run.xml")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("word_tmp/my_manuscript.run.xml")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("latex/my_manuscript.tex.tmp")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("latex/songbook/my_manuscript.tex.tmp")), CSYNC_FILE_EXCLUDE_LIST);
#ifdef _WIN32
QCOMPARE(check_file_traversal(QStringLiteral("file_trailing_space ")), CSYNC_FILE_EXCLUDE_TRAILING_SPACE);
QCOMPARE(check_file_traversal(QStringLiteral("file_trailing_dot.")), CSYNC_FILE_EXCLUDE_INVALID_CHAR);
QCOMPARE(check_file_traversal(QStringLiteral("AUX")), CSYNC_FILE_EXCLUDE_INVALID_CHAR);
QCOMPARE(check_file_traversal(QStringLiteral("file_invalid_char<")), CSYNC_FILE_EXCLUDE_INVALID_CHAR);
QCOMPARE(check_file_traversal(QStringLiteral("file_invalid_char\n")), CSYNC_FILE_EXCLUDE_INVALID_CHAR);
#endif
/* From here the actual traversal tests */
excludedFiles->addManualExclude(QStringLiteral("/exclude"));
excludedFiles->reloadExcludeFiles();
/* Check toplevel dir, the pattern only works for toplevel dir. */
QCOMPARE(check_dir_traversal(QStringLiteral("/exclude")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_dir_traversal(QStringLiteral("/foo/exclude")), CSYNC_NOT_EXCLUDED);
/* check for a file called exclude. Must still work */
QCOMPARE(check_file_traversal(QStringLiteral("/exclude")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("/foo/exclude")), CSYNC_NOT_EXCLUDED);
/* Add an exclude for directories only: excl/ */
excludedFiles->addManualExclude(QStringLiteral("excl/"));
excludedFiles->reloadExcludeFiles();
QCOMPARE(check_dir_traversal(QStringLiteral("/excl")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_dir_traversal(QStringLiteral("meep/excl")), CSYNC_FILE_EXCLUDE_LIST);
// because leading dirs aren't checked!
QCOMPARE(check_file_traversal(QStringLiteral("meep/excl/file")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("/excl")), CSYNC_NOT_EXCLUDED);
excludedFiles->addManualExclude(QStringLiteral("/excludepath/withsubdir"));
excludedFiles->reloadExcludeFiles();
QCOMPARE(check_dir_traversal(QStringLiteral("/excludepath/withsubdir")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("/excludepath/withsubdir")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_dir_traversal(QStringLiteral("/excludepath/withsubdir2")), CSYNC_NOT_EXCLUDED);
// because leading dirs aren't checked!
QCOMPARE(check_dir_traversal(QStringLiteral("/excludepath/withsubdir/foo")), CSYNC_NOT_EXCLUDED);
/* Check ending of pattern */
QCOMPARE(check_file_traversal(QStringLiteral("/exclude")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("/excludeX")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("exclude")), CSYNC_NOT_EXCLUDED);
excludedFiles->addManualExclude(QStringLiteral("exclude"));
excludedFiles->reloadExcludeFiles();
QCOMPARE(check_file_traversal(QStringLiteral("exclude")), CSYNC_FILE_EXCLUDE_LIST);
/* ? character */
excludedFiles->addManualExclude(QStringLiteral("bond00?"));
excludedFiles->reloadExcludeFiles();
QCOMPARE(check_file_traversal(QStringLiteral("bond00")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("bond007")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("bond0071")), CSYNC_NOT_EXCLUDED);
/* brackets */
excludedFiles->addManualExclude(QStringLiteral("a [bc] d"));
excludedFiles->reloadExcludeFiles();
QCOMPARE(check_file_traversal(QStringLiteral("a d d")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("a d")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("a b d")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("a c d")), CSYNC_FILE_EXCLUDE_LIST);
#ifndef Q_OS_WIN // Because of CSYNC_FILE_EXCLUDE_INVALID_CHAR on windows
/* escapes */
excludedFiles->addManualExclude(QStringLiteral("a \\*"));
excludedFiles->addManualExclude(QStringLiteral("b \\?"));
excludedFiles->addManualExclude(QStringLiteral("c \\[d]"));
excludedFiles->reloadExcludeFiles();
QCOMPARE(check_file_traversal(QStringLiteral("a \\*")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("a bc")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("a *")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("b \\?")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("b f")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("b ?")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("c \\[d]")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("c d")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("c [d]")), CSYNC_FILE_EXCLUDE_LIST);
#endif
}
void check_csync_dir_only()
{
setup();
excludedFiles->addManualExclude(QStringLiteral("filedir"));
excludedFiles->addManualExclude(QStringLiteral("dir/"));
QCOMPARE(check_file_traversal(QStringLiteral("other")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("filedir")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("dir")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("s/other")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("s/filedir")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("s/dir")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_dir_traversal(QStringLiteral("other")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_dir_traversal(QStringLiteral("filedir")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_dir_traversal(QStringLiteral("dir")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_dir_traversal(QStringLiteral("s/other")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_dir_traversal(QStringLiteral("s/filedir")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_dir_traversal(QStringLiteral("s/dir")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_dir_full(QStringLiteral("filedir/foo")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_full(QStringLiteral("filedir/foo")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_dir_full(QStringLiteral("dir/foo")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_full(QStringLiteral("dir/foo")), CSYNC_FILE_EXCLUDE_LIST);
}
void check_csync_pathes()
{
setup_init();
excludedFiles->addManualExclude(QStringLiteral("/exclude"));
excludedFiles->reloadExcludeFiles();
/* Check toplevel dir, the pattern only works for toplevel dir. */
QCOMPARE(check_dir_full(QStringLiteral("/exclude")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_dir_full(QStringLiteral("/foo/exclude")), CSYNC_NOT_EXCLUDED);
/* check for a file called exclude. Must still work */
QCOMPARE(check_file_full(QStringLiteral("/exclude")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_full(QStringLiteral("/foo/exclude")), CSYNC_NOT_EXCLUDED);
/* Add an exclude for directories only: excl/ */
excludedFiles->addManualExclude(QStringLiteral("excl/"));
excludedFiles->reloadExcludeFiles();
QCOMPARE(check_dir_full(QStringLiteral("/excl")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_dir_full(QStringLiteral("meep/excl")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_full(QStringLiteral("meep/excl/file")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_full(QStringLiteral("/excl")), CSYNC_NOT_EXCLUDED);
excludedFiles->addManualExclude(QStringLiteral("/excludepath/withsubdir"));
excludedFiles->reloadExcludeFiles();
QCOMPARE(check_dir_full(QStringLiteral("/excludepath/withsubdir")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_full(QStringLiteral("/excludepath/withsubdir")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_dir_full(QStringLiteral("/excludepath/withsubdir2")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_dir_full(QStringLiteral("/excludepath/withsubdir/foo")), CSYNC_FILE_EXCLUDE_LIST);
}
void check_csync_wildcards()
{
setup();
excludedFiles->addManualExclude(QStringLiteral("a/foo*bar"));
excludedFiles->addManualExclude(QStringLiteral("b/foo*bar*"));
excludedFiles->addManualExclude(QStringLiteral("c/foo?bar"));
excludedFiles->addManualExclude(QStringLiteral("d/foo?bar*"));
excludedFiles->addManualExclude(QStringLiteral("e/foo?bar?"));
excludedFiles->addManualExclude(QStringLiteral("g/bar*"));
excludedFiles->addManualExclude(QStringLiteral("h/bar?"));
excludedFiles->setWildcardsMatchSlash(false);
QCOMPARE(check_file_traversal(QStringLiteral("a/fooXYZbar")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("a/fooX/Zbar")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("b/fooXYZbarABC")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("b/fooX/ZbarABC")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("c/fooXbar")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("c/foo/bar")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("d/fooXbarABC")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("d/foo/barABC")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("e/fooXbarA")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("e/foo/barA")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("g/barABC")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("g/XbarABC")), CSYNC_NOT_EXCLUDED);
QCOMPARE(check_file_traversal(QStringLiteral("h/barZ")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("h/XbarZ")), CSYNC_NOT_EXCLUDED);
excludedFiles->setWildcardsMatchSlash(true);
QCOMPARE(check_file_traversal(QStringLiteral("a/fooX/Zbar")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("b/fooX/ZbarABC")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("c/foo/bar")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("d/foo/barABC")), CSYNC_FILE_EXCLUDE_LIST);
QCOMPARE(check_file_traversal(QStringLiteral("e/foo/barA")), CSYNC_FILE_EXCLUDE_LIST);
}
void check_csync_regex_translation()
{
setup();
auto translate = [](const QString &pattern) { return ExcludedFiles::convertToRegexpSyntax(pattern, false).toUtf8(); };
QCOMPARE(translate(QString()), "");
QCOMPARE(translate(QStringLiteral("abc")), "abc");
QCOMPARE(translate(QStringLiteral("a*c")), "a[^/]*c");
QCOMPARE(translate(QStringLiteral("a?c")), "a[^/]c");
QCOMPARE(translate(QStringLiteral("a[xyz]c")), "a[xyz]c");
QCOMPARE(translate(QStringLiteral("a[xyzc")), "a\\[xyzc");
QCOMPARE(translate(QStringLiteral("a[!xyz]c")), "a[^xyz]c");
QCOMPARE(translate(QStringLiteral("a\\*b\\?c\\[d\\\\e")), "a\\*b\\?c\\[d\\\\e");
QCOMPARE(translate(QStringLiteral("a.c")), "a\\.c");
QCOMPARE(translate(QStringLiteral("?𠜎?")), "[^/]\\𠜎[^/]"); // 𠜎 is 4-byte utf8
}
void check_csync_bname_trigger()
{
setup();
bool wildcardsMatchSlash = false;
auto translate = [&wildcardsMatchSlash](const QString &pattern) { return ExcludedFiles::extractBnameTrigger(pattern, wildcardsMatchSlash).toUtf8(); };
QCOMPARE(translate(QString()), "");
QCOMPARE(translate(QStringLiteral("a/b/")), "");
QCOMPARE(translate(QStringLiteral("a/b/c")), "c");
QCOMPARE(translate(QStringLiteral("c")), "c");
QCOMPARE(translate(QStringLiteral("a/foo*")), "foo*");
QCOMPARE(translate(QStringLiteral("a/abc*foo*")), "abc*foo*");
wildcardsMatchSlash = true;
QCOMPARE(translate(QString()), "");
QCOMPARE(translate(QStringLiteral("a/b/")), "");
QCOMPARE(translate(QStringLiteral("a/b/c")), "c");
QCOMPARE(translate(QStringLiteral("c")), "c");
QCOMPARE(translate(QStringLiteral("*")), "*");
QCOMPARE(translate(QStringLiteral("a/foo*")), "foo*");
QCOMPARE(translate(QStringLiteral("a/abc?foo*")), "*foo*");
QCOMPARE(translate(QStringLiteral("a/abc*foo*")), "*foo*");
QCOMPARE(translate(QStringLiteral("a/abc?foo?")), "*foo?");
QCOMPARE(translate(QStringLiteral("a/abc*foo?*")), "*foo?*");
QCOMPARE(translate(QStringLiteral("a/abc*/foo*")), "foo*");
}
void check_csync_is_windows_reserved_word()
{
auto csync_is_windows_reserved_word = [](const char *fn) {
QString s = QString::fromLatin1(fn);
extern bool csync_is_windows_reserved_word(QStringView filename);
return csync_is_windows_reserved_word(s);
};
QVERIFY(csync_is_windows_reserved_word("CON"));
QVERIFY(csync_is_windows_reserved_word("con"));
QVERIFY(csync_is_windows_reserved_word("CON."));
QVERIFY(csync_is_windows_reserved_word("con."));
QVERIFY(csync_is_windows_reserved_word("CON.ference"));
QVERIFY(!csync_is_windows_reserved_word("CONference"));
QVERIFY(!csync_is_windows_reserved_word("conference"));
QVERIFY(!csync_is_windows_reserved_word("conf.erence"));
QVERIFY(!csync_is_windows_reserved_word("co"));
QVERIFY(csync_is_windows_reserved_word("COM2"));
QVERIFY(csync_is_windows_reserved_word("com2"));
QVERIFY(csync_is_windows_reserved_word("COM2."));
QVERIFY(csync_is_windows_reserved_word("com2."));
QVERIFY(csync_is_windows_reserved_word("COM2.ference"));
QVERIFY(!csync_is_windows_reserved_word("COM2ference"));
QVERIFY(!csync_is_windows_reserved_word("com2ference"));
QVERIFY(!csync_is_windows_reserved_word("com2f.erence"));
QVERIFY(!csync_is_windows_reserved_word("com"));
QVERIFY(csync_is_windows_reserved_word("CLOCK$"));
QVERIFY(csync_is_windows_reserved_word("$Recycle.Bin"));
QVERIFY(csync_is_windows_reserved_word("ClocK$"));
QVERIFY(csync_is_windows_reserved_word("$recycle.bin"));
QVERIFY(csync_is_windows_reserved_word("A:"));
QVERIFY(csync_is_windows_reserved_word("a:"));
QVERIFY(csync_is_windows_reserved_word("z:"));
QVERIFY(csync_is_windows_reserved_word("Z:"));
QVERIFY(csync_is_windows_reserved_word("M:"));
QVERIFY(csync_is_windows_reserved_word("m:"));
}
/* QT_ENABLE_REGEXP_JIT=0 to get slower results :-) */
void check_csync_excluded_performance1()
{
setup_init();
const int N = 1000;
int totalRc = 0;
QBENCHMARK {
for (int i = 0; i < N; ++i) {
totalRc += check_dir_full(QStringLiteral("/this/is/quite/a/long/path/with/many/components"));
totalRc += check_file_full(QStringLiteral("/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/29"));
}
QCOMPARE(totalRc, 0); // mainly to avoid optimization
}
}
void check_csync_excluded_performance2()
{
const int N = 1000;
int totalRc = 0;
QBENCHMARK {
for (int i = 0; i < N; ++i) {
totalRc += check_dir_traversal(QStringLiteral("/this/is/quite/a/long/path/with/many/components"));
totalRc += check_file_traversal(QStringLiteral("/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/29"));
}
QCOMPARE(totalRc, 0); // mainly to avoid optimization
}
}
void check_csync_exclude_expand_escapes()
{
extern void csync_exclude_expand_escapes(QByteArray &input);
QByteArray line = "keep \\' \\\" \\? \\\\ \\a \\b \\f \\n \\r \\t \\v \\z \\#";
csync_exclude_expand_escapes(line);
QVERIFY(0 == strcmp(line.constData(), "keep ' \" ? \\\\ \a \b \f \n \r \t \v \\z #"));
line = "";
csync_exclude_expand_escapes(line);
QVERIFY(0 == strcmp(line.constData(), ""));
line = "\\";
csync_exclude_expand_escapes(line);
QVERIFY(0 == strcmp(line.constData(), "\\"));
}
void check_version_directive()
{
ExcludedFiles excludes;
excludes.setClientVersion(QVersionNumber(2, 5, 0));
std::vector<std::pair<const char *, bool>> tests = {
{ "#!version == 2.5.0", true },
{ "#!version == 2.6.0", false },
{ "#!version < 2.6.0", true },
{ "#!version <= 2.6.0", true },
{ "#!version > 2.6.0", false },
{ "#!version >= 2.6.0", false },
{ "#!version < 2.4.0", false },
{ "#!version <= 2.4.0", false },
{ "#!version > 2.4.0", true },
{ "#!version >= 2.4.0", true },
{ "#!version < 2.5.0", false },
{ "#!version <= 2.5.0", true },
{ "#!version > 2.5.0", false },
{ "#!version >= 2.5.0", true },
};
for (auto test : tests) {
QVERIFY(excludes.versionDirectiveKeepNextLine(test.first) == test.second);
}
}
};
QTEST_GUILESS_MAIN(TestExcludedFiles)
#include "testexcludedfiles.moc"