testexcludedfiles.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777
  1. /*
  2. * This software is in the public domain, furnished "as is", without technical
  3. * support, and with no warranty, express or implied, as to its usefulness for
  4. * any purpose.
  5. *
  6. */
  7. #include <QtTest>
  8. #include <QTemporaryDir>
  9. #include "csync_exclude.h"
  10. using namespace OCC;
  11. #define EXCLUDE_LIST_FILE SOURCEDIR "/../../sync-exclude.lst"
  12. // The tests were converted from the old CMocka framework, that's why there is a global
  13. static QScopedPointer<ExcludedFiles> excludedFiles;
  14. static void setup() {
  15. excludedFiles.reset(new ExcludedFiles);
  16. excludedFiles->setWildcardsMatchSlash(false);
  17. }
  18. static void setup_init() {
  19. setup();
  20. excludedFiles->addExcludeFilePath(EXCLUDE_LIST_FILE);
  21. QVERIFY(excludedFiles->reloadExcludeFiles());
  22. /* and add some unicode stuff */
  23. excludedFiles->addManualExclude("*.💩"); // is this source file utf8 encoded?
  24. excludedFiles->addManualExclude("пятницы.*");
  25. excludedFiles->addManualExclude("*/*.out");
  26. excludedFiles->addManualExclude("latex*/*.run.xml");
  27. excludedFiles->addManualExclude("latex/*/*.tex.tmp");
  28. QVERIFY(excludedFiles->reloadExcludeFiles());
  29. }
  30. class TestExcludedFiles: public QObject
  31. {
  32. Q_OBJECT
  33. static auto check_file_full(const char *path)
  34. {
  35. return excludedFiles->fullPatternMatch(path, ItemTypeFile);
  36. }
  37. static auto check_dir_full(const char *path)
  38. {
  39. return excludedFiles->fullPatternMatch(path, ItemTypeDirectory);
  40. }
  41. static auto check_file_traversal(const char *path)
  42. {
  43. return excludedFiles->traversalPatternMatch(path, ItemTypeFile);
  44. }
  45. static auto check_dir_traversal(const char *path)
  46. {
  47. return excludedFiles->traversalPatternMatch(path, ItemTypeDirectory);
  48. }
  49. private slots:
  50. void testFun()
  51. {
  52. ExcludedFiles excluded;
  53. bool excludeHidden = true;
  54. bool keepHidden = false;
  55. QVERIFY(!excluded.isExcluded("/a/b", "/a", keepHidden));
  56. QVERIFY(!excluded.isExcluded("/a/b~", "/a", keepHidden));
  57. QVERIFY(!excluded.isExcluded("/a/.b", "/a", keepHidden));
  58. QVERIFY(excluded.isExcluded("/a/.b", "/a", excludeHidden));
  59. excluded.addExcludeFilePath(EXCLUDE_LIST_FILE);
  60. excluded.reloadExcludeFiles();
  61. QVERIFY(!excluded.isExcluded("/a/b", "/a", keepHidden));
  62. QVERIFY(excluded.isExcluded("/a/b~", "/a", keepHidden));
  63. QVERIFY(!excluded.isExcluded("/a/.b", "/a", keepHidden));
  64. QVERIFY(excluded.isExcluded("/a/.Trashes", "/a", keepHidden));
  65. QVERIFY(excluded.isExcluded("/a/foo_conflict-bar", "/a", keepHidden));
  66. QVERIFY(excluded.isExcluded("/a/foo (conflicted copy bar)", "/a", keepHidden));
  67. QVERIFY(excluded.isExcluded("/a/.b", "/a", excludeHidden));
  68. QVERIFY(excluded.isExcluded("/a/#b#", "/a", keepHidden));
  69. }
  70. void check_csync_exclude_add()
  71. {
  72. setup();
  73. excludedFiles->addManualExclude("/tmp/check_csync1/*");
  74. QCOMPARE(check_file_full("/tmp/check_csync1/foo"), CSYNC_FILE_EXCLUDE_LIST);
  75. QCOMPARE(check_file_full("/tmp/check_csync2/foo"), CSYNC_NOT_EXCLUDED);
  76. QVERIFY(excludedFiles->_allExcludes[QStringLiteral("/")].contains("/tmp/check_csync1/*"));
  77. QVERIFY(excludedFiles->_fullRegexFile[QStringLiteral("/")].pattern().contains("csync1"));
  78. QVERIFY(excludedFiles->_fullTraversalRegexFile[QStringLiteral("/")].pattern().contains("csync1"));
  79. QVERIFY(!excludedFiles->_bnameTraversalRegexFile[QStringLiteral("/")].pattern().contains("csync1"));
  80. excludedFiles->addManualExclude("foo");
  81. QVERIFY(excludedFiles->_bnameTraversalRegexFile[QStringLiteral("/")].pattern().contains("foo"));
  82. QVERIFY(excludedFiles->_fullRegexFile[QStringLiteral("/")].pattern().contains("foo"));
  83. QVERIFY(!excludedFiles->_fullTraversalRegexFile[QStringLiteral("/")].pattern().contains("foo"));
  84. }
  85. void check_csync_exclude_add_per_dir()
  86. {
  87. setup();
  88. excludedFiles->addManualExclude("*", "/tmp/check_csync1/");
  89. QCOMPARE(check_file_full("/tmp/check_csync1/foo"), CSYNC_FILE_EXCLUDE_LIST);
  90. QCOMPARE(check_file_full("/tmp/check_csync2/foo"), CSYNC_NOT_EXCLUDED);
  91. QVERIFY(excludedFiles->_allExcludes[QStringLiteral("/tmp/check_csync1/")].contains("*"));
  92. excludedFiles->addManualExclude("foo");
  93. QVERIFY(excludedFiles->_fullRegexFile[QStringLiteral("/")].pattern().contains("foo"));
  94. excludedFiles->addManualExclude("foo/bar", "/tmp/check_csync1/");
  95. QVERIFY(excludedFiles->_fullRegexFile[QStringLiteral("/tmp/check_csync1/")].pattern().contains("bar"));
  96. QVERIFY(excludedFiles->_fullTraversalRegexFile[QStringLiteral("/tmp/check_csync1/")].pattern().contains("bar"));
  97. QVERIFY(!excludedFiles->_bnameTraversalRegexFile[QStringLiteral("/tmp/check_csync1/")].pattern().contains("foo"));
  98. }
  99. void check_csync_excluded()
  100. {
  101. setup_init();
  102. QCOMPARE(check_file_full(""), CSYNC_NOT_EXCLUDED);
  103. QCOMPARE(check_file_full("/"), CSYNC_NOT_EXCLUDED);
  104. QCOMPARE(check_file_full("A"), CSYNC_NOT_EXCLUDED);
  105. QCOMPARE(check_file_full("krawel_krawel"), CSYNC_NOT_EXCLUDED);
  106. QCOMPARE(check_file_full(".kde/share/config/kwin.eventsrc"), CSYNC_NOT_EXCLUDED);
  107. QCOMPARE(check_file_full(".directory/cache-maximegalon/cache1.txt"), CSYNC_FILE_EXCLUDE_LIST);
  108. QCOMPARE(check_dir_full("mozilla/.directory"), CSYNC_FILE_EXCLUDE_LIST);
  109. /*
  110. * Test for patterns in subdirs. '.beagle' is defined as a pattern and has
  111. * to be found in top dir as well as in directories underneath.
  112. */
  113. QCOMPARE(check_dir_full(".apdisk"), CSYNC_FILE_EXCLUDE_LIST);
  114. QCOMPARE(check_dir_full("foo/.apdisk"), CSYNC_FILE_EXCLUDE_LIST);
  115. QCOMPARE(check_dir_full("foo/bar/.apdisk"), CSYNC_FILE_EXCLUDE_LIST);
  116. QCOMPARE(check_file_full(".java"), CSYNC_NOT_EXCLUDED);
  117. /* Files in the ignored dir .java will also be ignored. */
  118. QCOMPARE(check_file_full(".apdisk/totally_amazing.jar"), CSYNC_FILE_EXCLUDE_LIST);
  119. /* and also in subdirs */
  120. QCOMPARE(check_file_full("projects/.apdisk/totally_amazing.jar"), CSYNC_FILE_EXCLUDE_LIST);
  121. /* csync-journal is ignored in general silently. */
  122. QCOMPARE(check_file_full(".csync_journal.db"), CSYNC_FILE_SILENTLY_EXCLUDED);
  123. QCOMPARE(check_file_full(".csync_journal.db.ctmp"), CSYNC_FILE_SILENTLY_EXCLUDED);
  124. QCOMPARE(check_file_full("subdir/.csync_journal.db"), CSYNC_FILE_SILENTLY_EXCLUDED);
  125. /* also the new form of the database name */
  126. QCOMPARE(check_file_full("._sync_5bdd60bdfcfa.db"), CSYNC_FILE_SILENTLY_EXCLUDED);
  127. QCOMPARE(check_file_full("._sync_5bdd60bdfcfa.db.ctmp"), CSYNC_FILE_SILENTLY_EXCLUDED);
  128. QCOMPARE(check_file_full("._sync_5bdd60bdfcfa.db-shm"), CSYNC_FILE_SILENTLY_EXCLUDED);
  129. QCOMPARE(check_file_full("subdir/._sync_5bdd60bdfcfa.db"), CSYNC_FILE_SILENTLY_EXCLUDED);
  130. QCOMPARE(check_file_full(".sync_5bdd60bdfcfa.db"), CSYNC_FILE_SILENTLY_EXCLUDED);
  131. QCOMPARE(check_file_full(".sync_5bdd60bdfcfa.db.ctmp"), CSYNC_FILE_SILENTLY_EXCLUDED);
  132. QCOMPARE(check_file_full(".sync_5bdd60bdfcfa.db-shm"), CSYNC_FILE_SILENTLY_EXCLUDED);
  133. QCOMPARE(check_file_full("subdir/.sync_5bdd60bdfcfa.db"), CSYNC_FILE_SILENTLY_EXCLUDED);
  134. /* pattern ]*.directory - ignore and remove */
  135. QCOMPARE(check_file_full("my.~directory"), CSYNC_FILE_EXCLUDE_AND_REMOVE);
  136. QCOMPARE(check_file_full("/a_folder/my.~directory"), CSYNC_FILE_EXCLUDE_AND_REMOVE);
  137. /* Not excluded because the pattern .netscape/cache requires directory. */
  138. QCOMPARE(check_file_full(".netscape/cache"), CSYNC_NOT_EXCLUDED);
  139. /* Not excluded */
  140. QCOMPARE(check_file_full("unicode/中文.hé"), CSYNC_NOT_EXCLUDED);
  141. /* excluded */
  142. QCOMPARE(check_file_full("unicode/пятницы.txt"), CSYNC_FILE_EXCLUDE_LIST);
  143. QCOMPARE(check_file_full("unicode/中文.💩"), CSYNC_FILE_EXCLUDE_LIST);
  144. /* path wildcards */
  145. QCOMPARE(check_file_full("foobar/my_manuscript.out"), CSYNC_FILE_EXCLUDE_LIST);
  146. QCOMPARE(check_file_full("latex_tmp/my_manuscript.run.xml"), CSYNC_FILE_EXCLUDE_LIST);
  147. QCOMPARE(check_file_full("word_tmp/my_manuscript.run.xml"), CSYNC_NOT_EXCLUDED);
  148. QCOMPARE(check_file_full("latex/my_manuscript.tex.tmp"), CSYNC_NOT_EXCLUDED);
  149. QCOMPARE(check_file_full("latex/songbook/my_manuscript.tex.tmp"), CSYNC_FILE_EXCLUDE_LIST);
  150. #ifdef _WIN32
  151. QCOMPARE(check_file_full("file_trailing_space "), CSYNC_FILE_EXCLUDE_TRAILING_SPACE);
  152. QCOMPARE(check_file_full("file_trailing_dot."), CSYNC_FILE_EXCLUDE_INVALID_CHAR);
  153. QCOMPARE(check_file_full("AUX"), CSYNC_FILE_EXCLUDE_INVALID_CHAR);
  154. QCOMPARE(check_file_full("file_invalid_char<"), CSYNC_FILE_EXCLUDE_INVALID_CHAR);
  155. QCOMPARE(check_file_full("file_invalid_char\n"), CSYNC_FILE_EXCLUDE_INVALID_CHAR);
  156. #endif
  157. /* ? character */
  158. excludedFiles->addManualExclude("bond00?");
  159. excludedFiles->reloadExcludeFiles();
  160. QCOMPARE(check_file_full("bond00"), CSYNC_NOT_EXCLUDED);
  161. QCOMPARE(check_file_full("bond007"), CSYNC_FILE_EXCLUDE_LIST);
  162. QCOMPARE(check_file_full("bond0071"), CSYNC_NOT_EXCLUDED);
  163. /* brackets */
  164. excludedFiles->addManualExclude("a [bc] d");
  165. excludedFiles->reloadExcludeFiles();
  166. QCOMPARE(check_file_full("a d d"), CSYNC_NOT_EXCLUDED);
  167. QCOMPARE(check_file_full("a d"), CSYNC_NOT_EXCLUDED);
  168. QCOMPARE(check_file_full("a b d"), CSYNC_FILE_EXCLUDE_LIST);
  169. QCOMPARE(check_file_full("a c d"), CSYNC_FILE_EXCLUDE_LIST);
  170. #ifndef Q_OS_WIN // Because of CSYNC_FILE_EXCLUDE_INVALID_CHAR on windows
  171. /* escapes */
  172. excludedFiles->addManualExclude("a \\*");
  173. excludedFiles->addManualExclude("b \\?");
  174. excludedFiles->addManualExclude("c \\[d]");
  175. excludedFiles->reloadExcludeFiles();
  176. QCOMPARE(check_file_full("a \\*"), CSYNC_NOT_EXCLUDED);
  177. QCOMPARE(check_file_full("a bc"), CSYNC_NOT_EXCLUDED);
  178. QCOMPARE(check_file_full("a *"), CSYNC_FILE_EXCLUDE_LIST);
  179. QCOMPARE(check_file_full("b \\?"), CSYNC_NOT_EXCLUDED);
  180. QCOMPARE(check_file_full("b f"), CSYNC_NOT_EXCLUDED);
  181. QCOMPARE(check_file_full("b ?"), CSYNC_FILE_EXCLUDE_LIST);
  182. QCOMPARE(check_file_full("c \\[d]"), CSYNC_NOT_EXCLUDED);
  183. QCOMPARE(check_file_full("c d"), CSYNC_NOT_EXCLUDED);
  184. QCOMPARE(check_file_full("c [d]"), CSYNC_FILE_EXCLUDE_LIST);
  185. #endif
  186. }
  187. void check_csync_excluded_per_dir()
  188. {
  189. const auto tempDir = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
  190. excludedFiles.reset(new ExcludedFiles(tempDir + "/"));
  191. excludedFiles->setWildcardsMatchSlash(false);
  192. excludedFiles->addManualExclude("A");
  193. excludedFiles->reloadExcludeFiles();
  194. QCOMPARE(check_file_full("A"), CSYNC_FILE_EXCLUDE_LIST);
  195. excludedFiles->clearManualExcludes();
  196. excludedFiles->addManualExclude("A", tempDir + "/B/");
  197. excludedFiles->reloadExcludeFiles();
  198. QCOMPARE(check_file_full("A"), CSYNC_NOT_EXCLUDED);
  199. QCOMPARE(check_file_full("B/A"), CSYNC_FILE_EXCLUDE_LIST);
  200. excludedFiles->clearManualExcludes();
  201. excludedFiles->addManualExclude("A/a1", tempDir + "/B/");
  202. excludedFiles->reloadExcludeFiles();
  203. QCOMPARE(check_file_full("A"), CSYNC_NOT_EXCLUDED);
  204. QCOMPARE(check_file_full("B/A/a1"), CSYNC_FILE_EXCLUDE_LIST);
  205. const auto fooDir = QStringLiteral("check_csync1/foo");
  206. QVERIFY(QDir(tempDir).mkpath(fooDir));
  207. const auto fooExcludeList = QString(tempDir + '/' + fooDir + "/.sync-exclude.lst");
  208. QFile excludeList(fooExcludeList);
  209. QVERIFY(excludeList.open(QFile::WriteOnly));
  210. QCOMPARE(excludeList.write("bar"), 3);
  211. excludeList.close();
  212. excludedFiles->addExcludeFilePath(fooExcludeList);
  213. excludedFiles->reloadExcludeFiles();
  214. QCOMPARE(check_file_full(QByteArray(fooDir.toUtf8() + "/bar")), CSYNC_FILE_EXCLUDE_LIST);
  215. QCOMPARE(check_file_full(QByteArray(fooDir.toUtf8() + "/baz")), CSYNC_NOT_EXCLUDED);
  216. }
  217. void check_csync_excluded_traversal_per_dir()
  218. {
  219. setup_init();
  220. QCOMPARE(check_file_traversal("/"), CSYNC_NOT_EXCLUDED);
  221. /* path wildcards */
  222. excludedFiles->addManualExclude("*/*.tex.tmp", "/latex/");
  223. QCOMPARE(check_file_traversal("latex/my_manuscript.tex.tmp"), CSYNC_NOT_EXCLUDED);
  224. QCOMPARE(check_file_traversal("latex/songbook/my_manuscript.tex.tmp"), CSYNC_FILE_EXCLUDE_LIST);
  225. }
  226. void check_csync_excluded_traversal()
  227. {
  228. setup_init();
  229. QCOMPARE(check_file_traversal(""), CSYNC_NOT_EXCLUDED);
  230. QCOMPARE(check_file_traversal("/"), CSYNC_NOT_EXCLUDED);
  231. QCOMPARE(check_file_traversal("A"), CSYNC_NOT_EXCLUDED);
  232. QCOMPARE(check_file_traversal("krawel_krawel"), CSYNC_NOT_EXCLUDED);
  233. QCOMPARE(check_file_traversal(".kde/share/config/kwin.eventsrc"), CSYNC_NOT_EXCLUDED);
  234. QCOMPARE(check_dir_traversal("mozilla/.directory"), CSYNC_FILE_EXCLUDE_LIST);
  235. /*
  236. * Test for patterns in subdirs. '.beagle' is defined as a pattern and has
  237. * to be found in top dir as well as in directories underneath.
  238. */
  239. QCOMPARE(check_dir_traversal(".apdisk"), CSYNC_FILE_EXCLUDE_LIST);
  240. QCOMPARE(check_dir_traversal("foo/.apdisk"), CSYNC_FILE_EXCLUDE_LIST);
  241. QCOMPARE(check_dir_traversal("foo/bar/.apdisk"), CSYNC_FILE_EXCLUDE_LIST);
  242. QCOMPARE(check_file_traversal(".java"), CSYNC_NOT_EXCLUDED);
  243. /* csync-journal is ignored in general silently. */
  244. QCOMPARE(check_file_traversal(".csync_journal.db"), CSYNC_FILE_SILENTLY_EXCLUDED);
  245. QCOMPARE(check_file_traversal(".csync_journal.db.ctmp"), CSYNC_FILE_SILENTLY_EXCLUDED);
  246. QCOMPARE(check_file_traversal("subdir/.csync_journal.db"), CSYNC_FILE_SILENTLY_EXCLUDED);
  247. QCOMPARE(check_file_traversal("/two/subdir/.csync_journal.db"), CSYNC_FILE_SILENTLY_EXCLUDED);
  248. /* also the new form of the database name */
  249. QCOMPARE(check_file_traversal("._sync_5bdd60bdfcfa.db"), CSYNC_FILE_SILENTLY_EXCLUDED);
  250. QCOMPARE(check_file_traversal("._sync_5bdd60bdfcfa.db.ctmp"), CSYNC_FILE_SILENTLY_EXCLUDED);
  251. QCOMPARE(check_file_traversal("._sync_5bdd60bdfcfa.db-shm"), CSYNC_FILE_SILENTLY_EXCLUDED);
  252. QCOMPARE(check_file_traversal("subdir/._sync_5bdd60bdfcfa.db"), CSYNC_FILE_SILENTLY_EXCLUDED);
  253. QCOMPARE(check_file_traversal(".sync_5bdd60bdfcfa.db"), CSYNC_FILE_SILENTLY_EXCLUDED);
  254. QCOMPARE(check_file_traversal(".sync_5bdd60bdfcfa.db.ctmp"), CSYNC_FILE_SILENTLY_EXCLUDED);
  255. QCOMPARE(check_file_traversal(".sync_5bdd60bdfcfa.db-shm"), CSYNC_FILE_SILENTLY_EXCLUDED);
  256. QCOMPARE(check_file_traversal("subdir/.sync_5bdd60bdfcfa.db"), CSYNC_FILE_SILENTLY_EXCLUDED);
  257. /* Other builtin excludes */
  258. QCOMPARE(check_file_traversal("foo/Desktop.ini"), CSYNC_FILE_SILENTLY_EXCLUDED);
  259. QCOMPARE(check_file_traversal("Desktop.ini"), CSYNC_FILE_SILENTLY_EXCLUDED);
  260. /* pattern ]*.directory - ignore and remove */
  261. QCOMPARE(check_file_traversal("my.~directory"), CSYNC_FILE_EXCLUDE_AND_REMOVE);
  262. QCOMPARE(check_file_traversal("/a_folder/my.~directory"), CSYNC_FILE_EXCLUDE_AND_REMOVE);
  263. /* Not excluded because the pattern .netscape/cache requires directory. */
  264. QCOMPARE(check_file_traversal(".netscape/cache"), CSYNC_NOT_EXCLUDED);
  265. /* Not excluded */
  266. QCOMPARE(check_file_traversal("unicode/中文.hé"), CSYNC_NOT_EXCLUDED);
  267. /* excluded */
  268. QCOMPARE(check_file_traversal("unicode/пятницы.txt"), CSYNC_FILE_EXCLUDE_LIST);
  269. QCOMPARE(check_file_traversal("unicode/中文.💩"), CSYNC_FILE_EXCLUDE_LIST);
  270. /* path wildcards */
  271. QCOMPARE(check_file_traversal("foobar/my_manuscript.out"), CSYNC_FILE_EXCLUDE_LIST);
  272. QCOMPARE(check_file_traversal("latex_tmp/my_manuscript.run.xml"), CSYNC_FILE_EXCLUDE_LIST);
  273. QCOMPARE(check_file_traversal("word_tmp/my_manuscript.run.xml"), CSYNC_NOT_EXCLUDED);
  274. QCOMPARE(check_file_traversal("latex/my_manuscript.tex.tmp"), CSYNC_NOT_EXCLUDED);
  275. QCOMPARE(check_file_traversal("latex/songbook/my_manuscript.tex.tmp"), CSYNC_FILE_EXCLUDE_LIST);
  276. #ifdef _WIN32
  277. QCOMPARE(check_file_traversal("file_trailing_space "), CSYNC_FILE_EXCLUDE_TRAILING_SPACE);
  278. QCOMPARE(check_file_traversal("file_trailing_dot."), CSYNC_FILE_EXCLUDE_INVALID_CHAR);
  279. QCOMPARE(check_file_traversal("AUX"), CSYNC_FILE_EXCLUDE_INVALID_CHAR);
  280. QCOMPARE(check_file_traversal("file_invalid_char<"), CSYNC_FILE_EXCLUDE_INVALID_CHAR);
  281. #endif
  282. /* From here the actual traversal tests */
  283. excludedFiles->addManualExclude("/exclude");
  284. excludedFiles->reloadExcludeFiles();
  285. /* Check toplevel dir, the pattern only works for toplevel dir. */
  286. QCOMPARE(check_dir_traversal("/exclude"), CSYNC_FILE_EXCLUDE_LIST);
  287. QCOMPARE(check_dir_traversal("/foo/exclude"), CSYNC_NOT_EXCLUDED);
  288. /* check for a file called exclude. Must still work */
  289. QCOMPARE(check_file_traversal("/exclude"), CSYNC_FILE_EXCLUDE_LIST);
  290. QCOMPARE(check_file_traversal("/foo/exclude"), CSYNC_NOT_EXCLUDED);
  291. /* Add an exclude for directories only: excl/ */
  292. excludedFiles->addManualExclude("excl/");
  293. excludedFiles->reloadExcludeFiles();
  294. QCOMPARE(check_dir_traversal("/excl"), CSYNC_FILE_EXCLUDE_LIST);
  295. QCOMPARE(check_dir_traversal("meep/excl"), CSYNC_FILE_EXCLUDE_LIST);
  296. // because leading dirs aren't checked!
  297. QCOMPARE(check_file_traversal("meep/excl/file"), CSYNC_NOT_EXCLUDED);
  298. QCOMPARE(check_file_traversal("/excl"), CSYNC_NOT_EXCLUDED);
  299. excludedFiles->addManualExclude("/excludepath/withsubdir");
  300. excludedFiles->reloadExcludeFiles();
  301. QCOMPARE(check_dir_traversal("/excludepath/withsubdir"), CSYNC_FILE_EXCLUDE_LIST);
  302. QCOMPARE(check_file_traversal("/excludepath/withsubdir"), CSYNC_FILE_EXCLUDE_LIST);
  303. QCOMPARE(check_dir_traversal("/excludepath/withsubdir2"), CSYNC_NOT_EXCLUDED);
  304. // because leading dirs aren't checked!
  305. QCOMPARE(check_dir_traversal("/excludepath/withsubdir/foo"), CSYNC_NOT_EXCLUDED);
  306. /* Check ending of pattern */
  307. QCOMPARE(check_file_traversal("/exclude"), CSYNC_FILE_EXCLUDE_LIST);
  308. QCOMPARE(check_file_traversal("/excludeX"), CSYNC_NOT_EXCLUDED);
  309. QCOMPARE(check_file_traversal("exclude"), CSYNC_NOT_EXCLUDED);
  310. excludedFiles->addManualExclude("exclude");
  311. excludedFiles->reloadExcludeFiles();
  312. QCOMPARE(check_file_traversal("exclude"), CSYNC_FILE_EXCLUDE_LIST);
  313. /* ? character */
  314. excludedFiles->addManualExclude("bond00?");
  315. excludedFiles->reloadExcludeFiles();
  316. QCOMPARE(check_file_traversal("bond00"), CSYNC_NOT_EXCLUDED);
  317. QCOMPARE(check_file_traversal("bond007"), CSYNC_FILE_EXCLUDE_LIST);
  318. QCOMPARE(check_file_traversal("bond0071"), CSYNC_NOT_EXCLUDED);
  319. /* brackets */
  320. excludedFiles->addManualExclude("a [bc] d");
  321. excludedFiles->reloadExcludeFiles();
  322. QCOMPARE(check_file_traversal("a d d"), CSYNC_NOT_EXCLUDED);
  323. QCOMPARE(check_file_traversal("a d"), CSYNC_NOT_EXCLUDED);
  324. QCOMPARE(check_file_traversal("a b d"), CSYNC_FILE_EXCLUDE_LIST);
  325. QCOMPARE(check_file_traversal("a c d"), CSYNC_FILE_EXCLUDE_LIST);
  326. #ifndef Q_OS_WIN // Because of CSYNC_FILE_EXCLUDE_INVALID_CHAR on windows
  327. /* escapes */
  328. excludedFiles->addManualExclude("a \\*");
  329. excludedFiles->addManualExclude("b \\?");
  330. excludedFiles->addManualExclude("c \\[d]");
  331. excludedFiles->reloadExcludeFiles();
  332. QCOMPARE(check_file_traversal("a \\*"), CSYNC_NOT_EXCLUDED);
  333. QCOMPARE(check_file_traversal("a bc"), CSYNC_NOT_EXCLUDED);
  334. QCOMPARE(check_file_traversal("a *"), CSYNC_FILE_EXCLUDE_LIST);
  335. QCOMPARE(check_file_traversal("b \\?"), CSYNC_NOT_EXCLUDED);
  336. QCOMPARE(check_file_traversal("b f"), CSYNC_NOT_EXCLUDED);
  337. QCOMPARE(check_file_traversal("b ?"), CSYNC_FILE_EXCLUDE_LIST);
  338. QCOMPARE(check_file_traversal("c \\[d]"), CSYNC_NOT_EXCLUDED);
  339. QCOMPARE(check_file_traversal("c d"), CSYNC_NOT_EXCLUDED);
  340. QCOMPARE(check_file_traversal("c [d]"), CSYNC_FILE_EXCLUDE_LIST);
  341. #endif
  342. }
  343. void check_csync_dir_only()
  344. {
  345. setup();
  346. excludedFiles->addManualExclude("filedir");
  347. excludedFiles->addManualExclude("dir/");
  348. QCOMPARE(check_file_traversal("other"), CSYNC_NOT_EXCLUDED);
  349. QCOMPARE(check_file_traversal("filedir"), CSYNC_FILE_EXCLUDE_LIST);
  350. QCOMPARE(check_file_traversal("dir"), CSYNC_NOT_EXCLUDED);
  351. QCOMPARE(check_file_traversal("s/other"), CSYNC_NOT_EXCLUDED);
  352. QCOMPARE(check_file_traversal("s/filedir"), CSYNC_FILE_EXCLUDE_LIST);
  353. QCOMPARE(check_file_traversal("s/dir"), CSYNC_NOT_EXCLUDED);
  354. QCOMPARE(check_dir_traversal("other"), CSYNC_NOT_EXCLUDED);
  355. QCOMPARE(check_dir_traversal("filedir"), CSYNC_FILE_EXCLUDE_LIST);
  356. QCOMPARE(check_dir_traversal("dir"), CSYNC_FILE_EXCLUDE_LIST);
  357. QCOMPARE(check_dir_traversal("s/other"), CSYNC_NOT_EXCLUDED);
  358. QCOMPARE(check_dir_traversal("s/filedir"), CSYNC_FILE_EXCLUDE_LIST);
  359. QCOMPARE(check_dir_traversal("s/dir"), CSYNC_FILE_EXCLUDE_LIST);
  360. QCOMPARE(check_dir_full("filedir/foo"), CSYNC_FILE_EXCLUDE_LIST);
  361. QCOMPARE(check_file_full("filedir/foo"), CSYNC_FILE_EXCLUDE_LIST);
  362. QCOMPARE(check_dir_full("dir/foo"), CSYNC_FILE_EXCLUDE_LIST);
  363. QCOMPARE(check_file_full("dir/foo"), CSYNC_FILE_EXCLUDE_LIST);
  364. }
  365. void check_csync_pathes()
  366. {
  367. setup_init();
  368. excludedFiles->addManualExclude("/exclude");
  369. excludedFiles->reloadExcludeFiles();
  370. /* Check toplevel dir, the pattern only works for toplevel dir. */
  371. QCOMPARE(check_dir_full("/exclude"), CSYNC_FILE_EXCLUDE_LIST);
  372. QCOMPARE(check_dir_full("/foo/exclude"), CSYNC_NOT_EXCLUDED);
  373. /* check for a file called exclude. Must still work */
  374. QCOMPARE(check_file_full("/exclude"), CSYNC_FILE_EXCLUDE_LIST);
  375. QCOMPARE(check_file_full("/foo/exclude"), CSYNC_NOT_EXCLUDED);
  376. /* Add an exclude for directories only: excl/ */
  377. excludedFiles->addManualExclude("excl/");
  378. excludedFiles->reloadExcludeFiles();
  379. QCOMPARE(check_dir_full("/excl"), CSYNC_FILE_EXCLUDE_LIST);
  380. QCOMPARE(check_dir_full("meep/excl"), CSYNC_FILE_EXCLUDE_LIST);
  381. QCOMPARE(check_file_full("meep/excl/file"), CSYNC_FILE_EXCLUDE_LIST);
  382. QCOMPARE(check_file_full("/excl"), CSYNC_NOT_EXCLUDED);
  383. excludedFiles->addManualExclude("/excludepath/withsubdir");
  384. excludedFiles->reloadExcludeFiles();
  385. QCOMPARE(check_dir_full("/excludepath/withsubdir"), CSYNC_FILE_EXCLUDE_LIST);
  386. QCOMPARE(check_file_full("/excludepath/withsubdir"), CSYNC_FILE_EXCLUDE_LIST);
  387. QCOMPARE(check_dir_full("/excludepath/withsubdir2"), CSYNC_NOT_EXCLUDED);
  388. QCOMPARE(check_dir_full("/excludepath/withsubdir/foo"), CSYNC_FILE_EXCLUDE_LIST);
  389. }
  390. void check_csync_wildcards()
  391. {
  392. setup();
  393. excludedFiles->addManualExclude("a/foo*bar");
  394. excludedFiles->addManualExclude("b/foo*bar*");
  395. excludedFiles->addManualExclude("c/foo?bar");
  396. excludedFiles->addManualExclude("d/foo?bar*");
  397. excludedFiles->addManualExclude("e/foo?bar?");
  398. excludedFiles->addManualExclude("g/bar*");
  399. excludedFiles->addManualExclude("h/bar?");
  400. excludedFiles->setWildcardsMatchSlash(false);
  401. QCOMPARE(check_file_traversal("a/fooXYZbar"), CSYNC_FILE_EXCLUDE_LIST);
  402. QCOMPARE(check_file_traversal("a/fooX/Zbar"), CSYNC_NOT_EXCLUDED);
  403. QCOMPARE(check_file_traversal("b/fooXYZbarABC"), CSYNC_FILE_EXCLUDE_LIST);
  404. QCOMPARE(check_file_traversal("b/fooX/ZbarABC"), CSYNC_NOT_EXCLUDED);
  405. QCOMPARE(check_file_traversal("c/fooXbar"), CSYNC_FILE_EXCLUDE_LIST);
  406. QCOMPARE(check_file_traversal("c/foo/bar"), CSYNC_NOT_EXCLUDED);
  407. QCOMPARE(check_file_traversal("d/fooXbarABC"), CSYNC_FILE_EXCLUDE_LIST);
  408. QCOMPARE(check_file_traversal("d/foo/barABC"), CSYNC_NOT_EXCLUDED);
  409. QCOMPARE(check_file_traversal("e/fooXbarA"), CSYNC_FILE_EXCLUDE_LIST);
  410. QCOMPARE(check_file_traversal("e/foo/barA"), CSYNC_NOT_EXCLUDED);
  411. QCOMPARE(check_file_traversal("g/barABC"), CSYNC_FILE_EXCLUDE_LIST);
  412. QCOMPARE(check_file_traversal("g/XbarABC"), CSYNC_NOT_EXCLUDED);
  413. QCOMPARE(check_file_traversal("h/barZ"), CSYNC_FILE_EXCLUDE_LIST);
  414. QCOMPARE(check_file_traversal("h/XbarZ"), CSYNC_NOT_EXCLUDED);
  415. excludedFiles->setWildcardsMatchSlash(true);
  416. QCOMPARE(check_file_traversal("a/fooX/Zbar"), CSYNC_FILE_EXCLUDE_LIST);
  417. QCOMPARE(check_file_traversal("b/fooX/ZbarABC"), CSYNC_FILE_EXCLUDE_LIST);
  418. QCOMPARE(check_file_traversal("c/foo/bar"), CSYNC_FILE_EXCLUDE_LIST);
  419. QCOMPARE(check_file_traversal("d/foo/barABC"), CSYNC_FILE_EXCLUDE_LIST);
  420. QCOMPARE(check_file_traversal("e/foo/barA"), CSYNC_FILE_EXCLUDE_LIST);
  421. }
  422. void check_csync_regex_translation()
  423. {
  424. setup();
  425. QByteArray storage;
  426. auto translate = [&storage](const char *pattern) {
  427. storage = ExcludedFiles::convertToRegexpSyntax(pattern, false).toUtf8();
  428. return storage.constData();
  429. };
  430. QCOMPARE(translate(""), "");
  431. QCOMPARE(translate("abc"), "abc");
  432. QCOMPARE(translate("a*c"), "a[^/]*c");
  433. QCOMPARE(translate("a?c"), "a[^/]c");
  434. QCOMPARE(translate("a[xyz]c"), "a[xyz]c");
  435. QCOMPARE(translate("a[xyzc"), "a\\[xyzc");
  436. QCOMPARE(translate("a[!xyz]c"), "a[^xyz]c");
  437. QCOMPARE(translate("a\\*b\\?c\\[d\\\\e"), "a\\*b\\?c\\[d\\\\e");
  438. QCOMPARE(translate("a.c"), "a\\.c");
  439. QCOMPARE(translate("?𠜎?"), "[^/]\\𠜎[^/]"); // 𠜎 is 4-byte utf8
  440. }
  441. void check_csync_bname_trigger()
  442. {
  443. setup();
  444. bool wildcardsMatchSlash = false;
  445. QByteArray storage;
  446. auto translate = [&storage, &wildcardsMatchSlash](const char *pattern) {
  447. storage = ExcludedFiles::extractBnameTrigger(pattern, wildcardsMatchSlash).toUtf8();
  448. return storage.constData();
  449. };
  450. QCOMPARE(translate(""), "");
  451. QCOMPARE(translate("a/b/"), "");
  452. QCOMPARE(translate("a/b/c"), "c");
  453. QCOMPARE(translate("c"), "c");
  454. QCOMPARE(translate("a/foo*"), "foo*");
  455. QCOMPARE(translate("a/abc*foo*"), "abc*foo*");
  456. wildcardsMatchSlash = true;
  457. QCOMPARE(translate(""), "");
  458. QCOMPARE(translate("a/b/"), "");
  459. QCOMPARE(translate("a/b/c"), "c");
  460. QCOMPARE(translate("c"), "c");
  461. QCOMPARE(translate("*"), "*");
  462. QCOMPARE(translate("a/foo*"), "foo*");
  463. QCOMPARE(translate("a/abc?foo*"), "*foo*");
  464. QCOMPARE(translate("a/abc*foo*"), "*foo*");
  465. QCOMPARE(translate("a/abc?foo?"), "*foo?");
  466. QCOMPARE(translate("a/abc*foo?*"), "*foo?*");
  467. QCOMPARE(translate("a/abc*/foo*"), "foo*");
  468. }
  469. void check_csync_is_windows_reserved_word()
  470. {
  471. auto csync_is_windows_reserved_word = [](const char *fn) {
  472. QString s = QString::fromLatin1(fn);
  473. extern bool csync_is_windows_reserved_word(const QStringRef &filename);
  474. return csync_is_windows_reserved_word(&s);
  475. };
  476. QVERIFY(csync_is_windows_reserved_word("CON"));
  477. QVERIFY(csync_is_windows_reserved_word("con"));
  478. QVERIFY(csync_is_windows_reserved_word("CON."));
  479. QVERIFY(csync_is_windows_reserved_word("con."));
  480. QVERIFY(csync_is_windows_reserved_word("CON.ference"));
  481. QVERIFY(!csync_is_windows_reserved_word("CONference"));
  482. QVERIFY(!csync_is_windows_reserved_word("conference"));
  483. QVERIFY(!csync_is_windows_reserved_word("conf.erence"));
  484. QVERIFY(!csync_is_windows_reserved_word("co"));
  485. QVERIFY(csync_is_windows_reserved_word("COM2"));
  486. QVERIFY(csync_is_windows_reserved_word("com2"));
  487. QVERIFY(csync_is_windows_reserved_word("COM2."));
  488. QVERIFY(csync_is_windows_reserved_word("com2."));
  489. QVERIFY(csync_is_windows_reserved_word("COM2.ference"));
  490. QVERIFY(!csync_is_windows_reserved_word("COM2ference"));
  491. QVERIFY(!csync_is_windows_reserved_word("com2ference"));
  492. QVERIFY(!csync_is_windows_reserved_word("com2f.erence"));
  493. QVERIFY(!csync_is_windows_reserved_word("com"));
  494. QVERIFY(csync_is_windows_reserved_word("CLOCK$"));
  495. QVERIFY(csync_is_windows_reserved_word("$Recycle.Bin"));
  496. QVERIFY(csync_is_windows_reserved_word("ClocK$"));
  497. QVERIFY(csync_is_windows_reserved_word("$recycle.bin"));
  498. QVERIFY(csync_is_windows_reserved_word("A:"));
  499. QVERIFY(csync_is_windows_reserved_word("a:"));
  500. QVERIFY(csync_is_windows_reserved_word("z:"));
  501. QVERIFY(csync_is_windows_reserved_word("Z:"));
  502. QVERIFY(csync_is_windows_reserved_word("M:"));
  503. QVERIFY(csync_is_windows_reserved_word("m:"));
  504. }
  505. /* QT_ENABLE_REGEXP_JIT=0 to get slower results :-) */
  506. void check_csync_excluded_performance1()
  507. {
  508. setup_init();
  509. const int N = 1000;
  510. int totalRc = 0;
  511. QBENCHMARK {
  512. for (int i = 0; i < N; ++i) {
  513. totalRc += check_dir_full("/this/is/quite/a/long/path/with/many/components");
  514. totalRc += check_file_full("/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");
  515. }
  516. QCOMPARE(totalRc, 0); // mainly to avoid optimization
  517. }
  518. }
  519. void check_csync_excluded_performance2()
  520. {
  521. const int N = 1000;
  522. int totalRc = 0;
  523. QBENCHMARK {
  524. for (int i = 0; i < N; ++i) {
  525. totalRc += check_dir_traversal("/this/is/quite/a/long/path/with/many/components");
  526. totalRc += check_file_traversal("/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");
  527. }
  528. QCOMPARE(totalRc, 0); // mainly to avoid optimization
  529. }
  530. }
  531. void check_csync_exclude_expand_escapes()
  532. {
  533. extern void csync_exclude_expand_escapes(QByteArray &input);
  534. QByteArray line = R"(keep \' \" \? \\ \a \b \f \n \r \t \v \z \#)";
  535. csync_exclude_expand_escapes(line);
  536. QVERIFY(0 == strcmp(line.constData(), "keep ' \" ? \\\\ \a \b \f \n \r \t \v \\z #"));
  537. line = "";
  538. csync_exclude_expand_escapes(line);
  539. QVERIFY(0 == strcmp(line.constData(), ""));
  540. line = "\\";
  541. csync_exclude_expand_escapes(line);
  542. QVERIFY(0 == strcmp(line.constData(), "\\"));
  543. }
  544. void check_version_directive()
  545. {
  546. ExcludedFiles excludes;
  547. excludes.setClientVersion(ExcludedFiles::Version(2, 5, 0));
  548. std::vector<std::pair<const char *, bool>> tests = {
  549. { "#!version == 2.5.0", true },
  550. { "#!version == 2.6.0", false },
  551. { "#!version < 2.6.0", true },
  552. { "#!version <= 2.6.0", true },
  553. { "#!version > 2.6.0", false },
  554. { "#!version >= 2.6.0", false },
  555. { "#!version < 2.4.0", false },
  556. { "#!version <= 2.4.0", false },
  557. { "#!version > 2.4.0", true },
  558. { "#!version >= 2.4.0", true },
  559. { "#!version < 2.5.0", false },
  560. { "#!version <= 2.5.0", true },
  561. { "#!version > 2.5.0", false },
  562. { "#!version >= 2.5.0", true },
  563. };
  564. for (auto test : tests) {
  565. QVERIFY(excludes.versionDirectiveKeepNextLine(test.first) == test.second);
  566. }
  567. }
  568. void testAddExcludeFilePath_addSameFilePath_listSizeDoesNotIncrease()
  569. {
  570. excludedFiles.reset(new ExcludedFiles());
  571. const auto filePath = QString("exclude/.sync-exclude.lst");
  572. excludedFiles->addExcludeFilePath(filePath);
  573. excludedFiles->addExcludeFilePath(filePath);
  574. QCOMPARE(excludedFiles->_excludeFiles.size(), 1);
  575. }
  576. void testAddExcludeFilePath_addDifferentFilePaths_listSizeIncrease()
  577. {
  578. excludedFiles.reset(new ExcludedFiles());
  579. const auto filePath1 = QString("exclude1/.sync-exclude.lst");
  580. const auto filePath2 = QString("exclude2/.sync-exclude.lst");
  581. excludedFiles->addExcludeFilePath(filePath1);
  582. excludedFiles->addExcludeFilePath(filePath2);
  583. QCOMPARE(excludedFiles->_excludeFiles.size(), 2);
  584. }
  585. void testAddExcludeFilePath_addDefaultExcludeFile_returnCorrectMap()
  586. {
  587. const QString basePath("syncFolder/");
  588. const QString folder1("syncFolder/folder1/");
  589. const QString folder2(folder1 + "folder2/");
  590. excludedFiles.reset(new ExcludedFiles(basePath));
  591. const QString defaultExcludeList("desktop-client/config-folder/sync-exclude.lst");
  592. const QString folder1ExcludeList(folder1 + ".sync-exclude.lst");
  593. const QString folder2ExcludeList(folder2 + ".sync-exclude.lst");
  594. excludedFiles->addExcludeFilePath(defaultExcludeList);
  595. excludedFiles->addExcludeFilePath(folder1ExcludeList);
  596. excludedFiles->addExcludeFilePath(folder2ExcludeList);
  597. QCOMPARE(excludedFiles->_excludeFiles.size(), 3);
  598. QCOMPARE(excludedFiles->_excludeFiles[basePath].first(), defaultExcludeList);
  599. QCOMPARE(excludedFiles->_excludeFiles[folder1].first(), folder1ExcludeList);
  600. QCOMPARE(excludedFiles->_excludeFiles[folder2].first(), folder2ExcludeList);
  601. }
  602. void testReloadExcludeFiles_fileDoesNotExist_returnFalse() {
  603. excludedFiles.reset(new ExcludedFiles());
  604. const QString nonExistingFile("directory/.sync-exclude.lst");
  605. excludedFiles->addExcludeFilePath(nonExistingFile);
  606. QCOMPARE(excludedFiles->reloadExcludeFiles(), false);
  607. QCOMPARE(excludedFiles->_allExcludes.size(), 0);
  608. }
  609. void testReloadExcludeFiles_fileExists_returnTrue()
  610. {
  611. const auto tempDir = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
  612. excludedFiles.reset(new ExcludedFiles(tempDir + "/"));
  613. const auto subTempDir = QStringLiteral("exclude");
  614. QVERIFY(QDir(tempDir).mkpath(subTempDir));
  615. const auto existingFilePath = QString(tempDir + '/' + subTempDir + "/.sync-exclude.lst");
  616. QFile excludeList(existingFilePath);
  617. QVERIFY(excludeList.open(QFile::WriteOnly));
  618. excludeList.close();
  619. excludedFiles->addExcludeFilePath(existingFilePath);
  620. QCOMPARE(excludedFiles->reloadExcludeFiles(), true);
  621. QCOMPARE(excludedFiles->_allExcludes.size(), 1);
  622. }
  623. };
  624. QTEST_APPLESS_MAIN(TestExcludedFiles)
  625. #include "testexcludedfiles.moc"