testexcludedfiles.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781
  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_leading_space"), CSYNC_NOT_EXCLUDED);
  152. QCOMPARE(check_file_full("file_trailing_space "), CSYNC_NOT_EXCLUDED);
  153. QCOMPARE(check_file_full(" file_leading_and_trailing_space "), CSYNC_NOT_EXCLUDED);
  154. QCOMPARE(check_file_full("file_trailing_dot."), CSYNC_FILE_EXCLUDE_INVALID_CHAR);
  155. QCOMPARE(check_file_full("AUX"), CSYNC_FILE_EXCLUDE_INVALID_CHAR);
  156. QCOMPARE(check_file_full("file_invalid_char<"), CSYNC_FILE_EXCLUDE_INVALID_CHAR);
  157. QCOMPARE(check_file_full("file_invalid_char\n"), CSYNC_FILE_EXCLUDE_INVALID_CHAR);
  158. #endif
  159. /* ? character */
  160. excludedFiles->addManualExclude("bond00?");
  161. excludedFiles->reloadExcludeFiles();
  162. QCOMPARE(check_file_full("bond00"), CSYNC_NOT_EXCLUDED);
  163. QCOMPARE(check_file_full("bond007"), CSYNC_FILE_EXCLUDE_LIST);
  164. QCOMPARE(check_file_full("bond0071"), CSYNC_NOT_EXCLUDED);
  165. /* brackets */
  166. excludedFiles->addManualExclude("a [bc] d");
  167. excludedFiles->reloadExcludeFiles();
  168. QCOMPARE(check_file_full("a d d"), CSYNC_NOT_EXCLUDED);
  169. QCOMPARE(check_file_full("a d"), CSYNC_NOT_EXCLUDED);
  170. QCOMPARE(check_file_full("a b d"), CSYNC_FILE_EXCLUDE_LIST);
  171. QCOMPARE(check_file_full("a c d"), CSYNC_FILE_EXCLUDE_LIST);
  172. #ifndef Q_OS_WIN // Because of CSYNC_FILE_EXCLUDE_INVALID_CHAR on windows
  173. /* escapes */
  174. excludedFiles->addManualExclude("a \\*");
  175. excludedFiles->addManualExclude("b \\?");
  176. excludedFiles->addManualExclude("c \\[d]");
  177. excludedFiles->reloadExcludeFiles();
  178. QCOMPARE(check_file_full("a \\*"), CSYNC_NOT_EXCLUDED);
  179. QCOMPARE(check_file_full("a bc"), CSYNC_NOT_EXCLUDED);
  180. QCOMPARE(check_file_full("a *"), CSYNC_FILE_EXCLUDE_LIST);
  181. QCOMPARE(check_file_full("b \\?"), CSYNC_NOT_EXCLUDED);
  182. QCOMPARE(check_file_full("b f"), CSYNC_NOT_EXCLUDED);
  183. QCOMPARE(check_file_full("b ?"), CSYNC_FILE_EXCLUDE_LIST);
  184. QCOMPARE(check_file_full("c \\[d]"), CSYNC_NOT_EXCLUDED);
  185. QCOMPARE(check_file_full("c d"), CSYNC_NOT_EXCLUDED);
  186. QCOMPARE(check_file_full("c [d]"), CSYNC_FILE_EXCLUDE_LIST);
  187. #endif
  188. }
  189. void check_csync_excluded_per_dir()
  190. {
  191. const auto tempDir = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
  192. excludedFiles.reset(new ExcludedFiles(tempDir + "/"));
  193. excludedFiles->setWildcardsMatchSlash(false);
  194. excludedFiles->addManualExclude("A");
  195. excludedFiles->reloadExcludeFiles();
  196. QCOMPARE(check_file_full("A"), CSYNC_FILE_EXCLUDE_LIST);
  197. excludedFiles->clearManualExcludes();
  198. excludedFiles->addManualExclude("A", tempDir + "/B/");
  199. excludedFiles->reloadExcludeFiles();
  200. QCOMPARE(check_file_full("A"), CSYNC_NOT_EXCLUDED);
  201. QCOMPARE(check_file_full("B/A"), CSYNC_FILE_EXCLUDE_LIST);
  202. excludedFiles->clearManualExcludes();
  203. excludedFiles->addManualExclude("A/a1", tempDir + "/B/");
  204. excludedFiles->reloadExcludeFiles();
  205. QCOMPARE(check_file_full("A"), CSYNC_NOT_EXCLUDED);
  206. QCOMPARE(check_file_full("B/A/a1"), CSYNC_FILE_EXCLUDE_LIST);
  207. const auto fooDir = QStringLiteral("check_csync1/foo");
  208. QVERIFY(QDir(tempDir).mkpath(fooDir));
  209. const auto fooExcludeList = QString(tempDir + '/' + fooDir + "/.sync-exclude.lst");
  210. QFile excludeList(fooExcludeList);
  211. QVERIFY(excludeList.open(QFile::WriteOnly));
  212. QCOMPARE(excludeList.write("bar"), 3);
  213. excludeList.close();
  214. excludedFiles->addExcludeFilePath(fooExcludeList);
  215. excludedFiles->reloadExcludeFiles();
  216. QCOMPARE(check_file_full(QByteArray(fooDir.toUtf8() + "/bar")), CSYNC_FILE_EXCLUDE_LIST);
  217. QCOMPARE(check_file_full(QByteArray(fooDir.toUtf8() + "/baz")), CSYNC_NOT_EXCLUDED);
  218. }
  219. void check_csync_excluded_traversal_per_dir()
  220. {
  221. setup_init();
  222. QCOMPARE(check_file_traversal("/"), CSYNC_NOT_EXCLUDED);
  223. /* path wildcards */
  224. excludedFiles->addManualExclude("*/*.tex.tmp", "/latex/");
  225. QCOMPARE(check_file_traversal("latex/my_manuscript.tex.tmp"), CSYNC_NOT_EXCLUDED);
  226. QCOMPARE(check_file_traversal("latex/songbook/my_manuscript.tex.tmp"), CSYNC_FILE_EXCLUDE_LIST);
  227. }
  228. void check_csync_excluded_traversal()
  229. {
  230. setup_init();
  231. QCOMPARE(check_file_traversal(""), CSYNC_NOT_EXCLUDED);
  232. QCOMPARE(check_file_traversal("/"), CSYNC_NOT_EXCLUDED);
  233. QCOMPARE(check_file_traversal("A"), CSYNC_NOT_EXCLUDED);
  234. QCOMPARE(check_file_traversal("krawel_krawel"), CSYNC_NOT_EXCLUDED);
  235. QCOMPARE(check_file_traversal(".kde/share/config/kwin.eventsrc"), CSYNC_NOT_EXCLUDED);
  236. QCOMPARE(check_dir_traversal("mozilla/.directory"), CSYNC_FILE_EXCLUDE_LIST);
  237. /*
  238. * Test for patterns in subdirs. '.beagle' is defined as a pattern and has
  239. * to be found in top dir as well as in directories underneath.
  240. */
  241. QCOMPARE(check_dir_traversal(".apdisk"), CSYNC_FILE_EXCLUDE_LIST);
  242. QCOMPARE(check_dir_traversal("foo/.apdisk"), CSYNC_FILE_EXCLUDE_LIST);
  243. QCOMPARE(check_dir_traversal("foo/bar/.apdisk"), CSYNC_FILE_EXCLUDE_LIST);
  244. QCOMPARE(check_file_traversal(".java"), CSYNC_NOT_EXCLUDED);
  245. /* csync-journal is ignored in general silently. */
  246. QCOMPARE(check_file_traversal(".csync_journal.db"), CSYNC_FILE_SILENTLY_EXCLUDED);
  247. QCOMPARE(check_file_traversal(".csync_journal.db.ctmp"), CSYNC_FILE_SILENTLY_EXCLUDED);
  248. QCOMPARE(check_file_traversal("subdir/.csync_journal.db"), CSYNC_FILE_SILENTLY_EXCLUDED);
  249. QCOMPARE(check_file_traversal("/two/subdir/.csync_journal.db"), CSYNC_FILE_SILENTLY_EXCLUDED);
  250. /* also the new form of the database name */
  251. QCOMPARE(check_file_traversal("._sync_5bdd60bdfcfa.db"), CSYNC_FILE_SILENTLY_EXCLUDED);
  252. QCOMPARE(check_file_traversal("._sync_5bdd60bdfcfa.db.ctmp"), CSYNC_FILE_SILENTLY_EXCLUDED);
  253. QCOMPARE(check_file_traversal("._sync_5bdd60bdfcfa.db-shm"), CSYNC_FILE_SILENTLY_EXCLUDED);
  254. QCOMPARE(check_file_traversal("subdir/._sync_5bdd60bdfcfa.db"), CSYNC_FILE_SILENTLY_EXCLUDED);
  255. QCOMPARE(check_file_traversal(".sync_5bdd60bdfcfa.db"), CSYNC_FILE_SILENTLY_EXCLUDED);
  256. QCOMPARE(check_file_traversal(".sync_5bdd60bdfcfa.db.ctmp"), CSYNC_FILE_SILENTLY_EXCLUDED);
  257. QCOMPARE(check_file_traversal(".sync_5bdd60bdfcfa.db-shm"), CSYNC_FILE_SILENTLY_EXCLUDED);
  258. QCOMPARE(check_file_traversal("subdir/.sync_5bdd60bdfcfa.db"), CSYNC_FILE_SILENTLY_EXCLUDED);
  259. /* Other builtin excludes */
  260. QCOMPARE(check_file_traversal("foo/Desktop.ini"), CSYNC_FILE_SILENTLY_EXCLUDED);
  261. QCOMPARE(check_file_traversal("Desktop.ini"), CSYNC_FILE_SILENTLY_EXCLUDED);
  262. /* pattern ]*.directory - ignore and remove */
  263. QCOMPARE(check_file_traversal("my.~directory"), CSYNC_FILE_EXCLUDE_AND_REMOVE);
  264. QCOMPARE(check_file_traversal("/a_folder/my.~directory"), CSYNC_FILE_EXCLUDE_AND_REMOVE);
  265. /* Not excluded because the pattern .netscape/cache requires directory. */
  266. QCOMPARE(check_file_traversal(".netscape/cache"), CSYNC_NOT_EXCLUDED);
  267. /* Not excluded */
  268. QCOMPARE(check_file_traversal("unicode/中文.hé"), CSYNC_NOT_EXCLUDED);
  269. /* excluded */
  270. QCOMPARE(check_file_traversal("unicode/пятницы.txt"), CSYNC_FILE_EXCLUDE_LIST);
  271. QCOMPARE(check_file_traversal("unicode/中文.💩"), CSYNC_FILE_EXCLUDE_LIST);
  272. /* path wildcards */
  273. QCOMPARE(check_file_traversal("foobar/my_manuscript.out"), CSYNC_FILE_EXCLUDE_LIST);
  274. QCOMPARE(check_file_traversal("latex_tmp/my_manuscript.run.xml"), CSYNC_FILE_EXCLUDE_LIST);
  275. QCOMPARE(check_file_traversal("word_tmp/my_manuscript.run.xml"), CSYNC_NOT_EXCLUDED);
  276. QCOMPARE(check_file_traversal("latex/my_manuscript.tex.tmp"), CSYNC_NOT_EXCLUDED);
  277. QCOMPARE(check_file_traversal("latex/songbook/my_manuscript.tex.tmp"), CSYNC_FILE_EXCLUDE_LIST);
  278. #ifdef _WIN32
  279. QCOMPARE(check_file_traversal(" file_leading_space"), CSYNC_NOT_EXCLUDED);
  280. QCOMPARE(check_file_traversal("file_trailing_space "), CSYNC_NOT_EXCLUDED);
  281. QCOMPARE(check_file_traversal(" file_leading_and_trailing_space "), CSYNC_NOT_EXCLUDED);
  282. QCOMPARE(check_file_traversal("file_trailing_dot."), CSYNC_FILE_EXCLUDE_INVALID_CHAR);
  283. QCOMPARE(check_file_traversal("AUX"), CSYNC_FILE_EXCLUDE_INVALID_CHAR);
  284. QCOMPARE(check_file_traversal("file_invalid_char<"), CSYNC_FILE_EXCLUDE_INVALID_CHAR);
  285. #endif
  286. /* From here the actual traversal tests */
  287. excludedFiles->addManualExclude("/exclude");
  288. excludedFiles->reloadExcludeFiles();
  289. /* Check toplevel dir, the pattern only works for toplevel dir. */
  290. QCOMPARE(check_dir_traversal("/exclude"), CSYNC_FILE_EXCLUDE_LIST);
  291. QCOMPARE(check_dir_traversal("/foo/exclude"), CSYNC_NOT_EXCLUDED);
  292. /* check for a file called exclude. Must still work */
  293. QCOMPARE(check_file_traversal("/exclude"), CSYNC_FILE_EXCLUDE_LIST);
  294. QCOMPARE(check_file_traversal("/foo/exclude"), CSYNC_NOT_EXCLUDED);
  295. /* Add an exclude for directories only: excl/ */
  296. excludedFiles->addManualExclude("excl/");
  297. excludedFiles->reloadExcludeFiles();
  298. QCOMPARE(check_dir_traversal("/excl"), CSYNC_FILE_EXCLUDE_LIST);
  299. QCOMPARE(check_dir_traversal("meep/excl"), CSYNC_FILE_EXCLUDE_LIST);
  300. // because leading dirs aren't checked!
  301. QCOMPARE(check_file_traversal("meep/excl/file"), CSYNC_NOT_EXCLUDED);
  302. QCOMPARE(check_file_traversal("/excl"), CSYNC_NOT_EXCLUDED);
  303. excludedFiles->addManualExclude("/excludepath/withsubdir");
  304. excludedFiles->reloadExcludeFiles();
  305. QCOMPARE(check_dir_traversal("/excludepath/withsubdir"), CSYNC_FILE_EXCLUDE_LIST);
  306. QCOMPARE(check_file_traversal("/excludepath/withsubdir"), CSYNC_FILE_EXCLUDE_LIST);
  307. QCOMPARE(check_dir_traversal("/excludepath/withsubdir2"), CSYNC_NOT_EXCLUDED);
  308. // because leading dirs aren't checked!
  309. QCOMPARE(check_dir_traversal("/excludepath/withsubdir/foo"), CSYNC_NOT_EXCLUDED);
  310. /* Check ending of pattern */
  311. QCOMPARE(check_file_traversal("/exclude"), CSYNC_FILE_EXCLUDE_LIST);
  312. QCOMPARE(check_file_traversal("/excludeX"), CSYNC_NOT_EXCLUDED);
  313. QCOMPARE(check_file_traversal("exclude"), CSYNC_NOT_EXCLUDED);
  314. excludedFiles->addManualExclude("exclude");
  315. excludedFiles->reloadExcludeFiles();
  316. QCOMPARE(check_file_traversal("exclude"), CSYNC_FILE_EXCLUDE_LIST);
  317. /* ? character */
  318. excludedFiles->addManualExclude("bond00?");
  319. excludedFiles->reloadExcludeFiles();
  320. QCOMPARE(check_file_traversal("bond00"), CSYNC_NOT_EXCLUDED);
  321. QCOMPARE(check_file_traversal("bond007"), CSYNC_FILE_EXCLUDE_LIST);
  322. QCOMPARE(check_file_traversal("bond0071"), CSYNC_NOT_EXCLUDED);
  323. /* brackets */
  324. excludedFiles->addManualExclude("a [bc] d");
  325. excludedFiles->reloadExcludeFiles();
  326. QCOMPARE(check_file_traversal("a d d"), CSYNC_NOT_EXCLUDED);
  327. QCOMPARE(check_file_traversal("a d"), CSYNC_NOT_EXCLUDED);
  328. QCOMPARE(check_file_traversal("a b d"), CSYNC_FILE_EXCLUDE_LIST);
  329. QCOMPARE(check_file_traversal("a c d"), CSYNC_FILE_EXCLUDE_LIST);
  330. #ifndef Q_OS_WIN // Because of CSYNC_FILE_EXCLUDE_INVALID_CHAR on windows
  331. /* escapes */
  332. excludedFiles->addManualExclude("a \\*");
  333. excludedFiles->addManualExclude("b \\?");
  334. excludedFiles->addManualExclude("c \\[d]");
  335. excludedFiles->reloadExcludeFiles();
  336. QCOMPARE(check_file_traversal("a \\*"), CSYNC_NOT_EXCLUDED);
  337. QCOMPARE(check_file_traversal("a bc"), CSYNC_NOT_EXCLUDED);
  338. QCOMPARE(check_file_traversal("a *"), CSYNC_FILE_EXCLUDE_LIST);
  339. QCOMPARE(check_file_traversal("b \\?"), CSYNC_NOT_EXCLUDED);
  340. QCOMPARE(check_file_traversal("b f"), CSYNC_NOT_EXCLUDED);
  341. QCOMPARE(check_file_traversal("b ?"), CSYNC_FILE_EXCLUDE_LIST);
  342. QCOMPARE(check_file_traversal("c \\[d]"), CSYNC_NOT_EXCLUDED);
  343. QCOMPARE(check_file_traversal("c d"), CSYNC_NOT_EXCLUDED);
  344. QCOMPARE(check_file_traversal("c [d]"), CSYNC_FILE_EXCLUDE_LIST);
  345. #endif
  346. }
  347. void check_csync_dir_only()
  348. {
  349. setup();
  350. excludedFiles->addManualExclude("filedir");
  351. excludedFiles->addManualExclude("dir/");
  352. QCOMPARE(check_file_traversal("other"), CSYNC_NOT_EXCLUDED);
  353. QCOMPARE(check_file_traversal("filedir"), CSYNC_FILE_EXCLUDE_LIST);
  354. QCOMPARE(check_file_traversal("dir"), CSYNC_NOT_EXCLUDED);
  355. QCOMPARE(check_file_traversal("s/other"), CSYNC_NOT_EXCLUDED);
  356. QCOMPARE(check_file_traversal("s/filedir"), CSYNC_FILE_EXCLUDE_LIST);
  357. QCOMPARE(check_file_traversal("s/dir"), CSYNC_NOT_EXCLUDED);
  358. QCOMPARE(check_dir_traversal("other"), CSYNC_NOT_EXCLUDED);
  359. QCOMPARE(check_dir_traversal("filedir"), CSYNC_FILE_EXCLUDE_LIST);
  360. QCOMPARE(check_dir_traversal("dir"), CSYNC_FILE_EXCLUDE_LIST);
  361. QCOMPARE(check_dir_traversal("s/other"), CSYNC_NOT_EXCLUDED);
  362. QCOMPARE(check_dir_traversal("s/filedir"), CSYNC_FILE_EXCLUDE_LIST);
  363. QCOMPARE(check_dir_traversal("s/dir"), CSYNC_FILE_EXCLUDE_LIST);
  364. QCOMPARE(check_dir_full("filedir/foo"), CSYNC_FILE_EXCLUDE_LIST);
  365. QCOMPARE(check_file_full("filedir/foo"), CSYNC_FILE_EXCLUDE_LIST);
  366. QCOMPARE(check_dir_full("dir/foo"), CSYNC_FILE_EXCLUDE_LIST);
  367. QCOMPARE(check_file_full("dir/foo"), CSYNC_FILE_EXCLUDE_LIST);
  368. }
  369. void check_csync_pathes()
  370. {
  371. setup_init();
  372. excludedFiles->addManualExclude("/exclude");
  373. excludedFiles->reloadExcludeFiles();
  374. /* Check toplevel dir, the pattern only works for toplevel dir. */
  375. QCOMPARE(check_dir_full("/exclude"), CSYNC_FILE_EXCLUDE_LIST);
  376. QCOMPARE(check_dir_full("/foo/exclude"), CSYNC_NOT_EXCLUDED);
  377. /* check for a file called exclude. Must still work */
  378. QCOMPARE(check_file_full("/exclude"), CSYNC_FILE_EXCLUDE_LIST);
  379. QCOMPARE(check_file_full("/foo/exclude"), CSYNC_NOT_EXCLUDED);
  380. /* Add an exclude for directories only: excl/ */
  381. excludedFiles->addManualExclude("excl/");
  382. excludedFiles->reloadExcludeFiles();
  383. QCOMPARE(check_dir_full("/excl"), CSYNC_FILE_EXCLUDE_LIST);
  384. QCOMPARE(check_dir_full("meep/excl"), CSYNC_FILE_EXCLUDE_LIST);
  385. QCOMPARE(check_file_full("meep/excl/file"), CSYNC_FILE_EXCLUDE_LIST);
  386. QCOMPARE(check_file_full("/excl"), CSYNC_NOT_EXCLUDED);
  387. excludedFiles->addManualExclude("/excludepath/withsubdir");
  388. excludedFiles->reloadExcludeFiles();
  389. QCOMPARE(check_dir_full("/excludepath/withsubdir"), CSYNC_FILE_EXCLUDE_LIST);
  390. QCOMPARE(check_file_full("/excludepath/withsubdir"), CSYNC_FILE_EXCLUDE_LIST);
  391. QCOMPARE(check_dir_full("/excludepath/withsubdir2"), CSYNC_NOT_EXCLUDED);
  392. QCOMPARE(check_dir_full("/excludepath/withsubdir/foo"), CSYNC_FILE_EXCLUDE_LIST);
  393. }
  394. void check_csync_wildcards()
  395. {
  396. setup();
  397. excludedFiles->addManualExclude("a/foo*bar");
  398. excludedFiles->addManualExclude("b/foo*bar*");
  399. excludedFiles->addManualExclude("c/foo?bar");
  400. excludedFiles->addManualExclude("d/foo?bar*");
  401. excludedFiles->addManualExclude("e/foo?bar?");
  402. excludedFiles->addManualExclude("g/bar*");
  403. excludedFiles->addManualExclude("h/bar?");
  404. excludedFiles->setWildcardsMatchSlash(false);
  405. QCOMPARE(check_file_traversal("a/fooXYZbar"), CSYNC_FILE_EXCLUDE_LIST);
  406. QCOMPARE(check_file_traversal("a/fooX/Zbar"), CSYNC_NOT_EXCLUDED);
  407. QCOMPARE(check_file_traversal("b/fooXYZbarABC"), CSYNC_FILE_EXCLUDE_LIST);
  408. QCOMPARE(check_file_traversal("b/fooX/ZbarABC"), CSYNC_NOT_EXCLUDED);
  409. QCOMPARE(check_file_traversal("c/fooXbar"), CSYNC_FILE_EXCLUDE_LIST);
  410. QCOMPARE(check_file_traversal("c/foo/bar"), CSYNC_NOT_EXCLUDED);
  411. QCOMPARE(check_file_traversal("d/fooXbarABC"), CSYNC_FILE_EXCLUDE_LIST);
  412. QCOMPARE(check_file_traversal("d/foo/barABC"), CSYNC_NOT_EXCLUDED);
  413. QCOMPARE(check_file_traversal("e/fooXbarA"), CSYNC_FILE_EXCLUDE_LIST);
  414. QCOMPARE(check_file_traversal("e/foo/barA"), CSYNC_NOT_EXCLUDED);
  415. QCOMPARE(check_file_traversal("g/barABC"), CSYNC_FILE_EXCLUDE_LIST);
  416. QCOMPARE(check_file_traversal("g/XbarABC"), CSYNC_NOT_EXCLUDED);
  417. QCOMPARE(check_file_traversal("h/barZ"), CSYNC_FILE_EXCLUDE_LIST);
  418. QCOMPARE(check_file_traversal("h/XbarZ"), CSYNC_NOT_EXCLUDED);
  419. excludedFiles->setWildcardsMatchSlash(true);
  420. QCOMPARE(check_file_traversal("a/fooX/Zbar"), CSYNC_FILE_EXCLUDE_LIST);
  421. QCOMPARE(check_file_traversal("b/fooX/ZbarABC"), CSYNC_FILE_EXCLUDE_LIST);
  422. QCOMPARE(check_file_traversal("c/foo/bar"), CSYNC_FILE_EXCLUDE_LIST);
  423. QCOMPARE(check_file_traversal("d/foo/barABC"), CSYNC_FILE_EXCLUDE_LIST);
  424. QCOMPARE(check_file_traversal("e/foo/barA"), CSYNC_FILE_EXCLUDE_LIST);
  425. }
  426. void check_csync_regex_translation()
  427. {
  428. setup();
  429. QByteArray storage;
  430. auto translate = [&storage](const char *pattern) {
  431. storage = ExcludedFiles::convertToRegexpSyntax(pattern, false).toUtf8();
  432. return storage.constData();
  433. };
  434. QCOMPARE(translate(""), "");
  435. QCOMPARE(translate("abc"), "abc");
  436. QCOMPARE(translate("a*c"), "a[^/]*c");
  437. QCOMPARE(translate("a?c"), "a[^/]c");
  438. QCOMPARE(translate("a[xyz]c"), "a[xyz]c");
  439. QCOMPARE(translate("a[xyzc"), "a\\[xyzc");
  440. QCOMPARE(translate("a[!xyz]c"), "a[^xyz]c");
  441. QCOMPARE(translate("a\\*b\\?c\\[d\\\\e"), "a\\*b\\?c\\[d\\\\e");
  442. QCOMPARE(translate("a.c"), "a\\.c");
  443. QCOMPARE(translate("?𠜎?"), "[^/]\\𠜎[^/]"); // 𠜎 is 4-byte utf8
  444. }
  445. void check_csync_bname_trigger()
  446. {
  447. setup();
  448. bool wildcardsMatchSlash = false;
  449. QByteArray storage;
  450. auto translate = [&storage, &wildcardsMatchSlash](const char *pattern) {
  451. storage = ExcludedFiles::extractBnameTrigger(pattern, wildcardsMatchSlash).toUtf8();
  452. return storage.constData();
  453. };
  454. QCOMPARE(translate(""), "");
  455. QCOMPARE(translate("a/b/"), "");
  456. QCOMPARE(translate("a/b/c"), "c");
  457. QCOMPARE(translate("c"), "c");
  458. QCOMPARE(translate("a/foo*"), "foo*");
  459. QCOMPARE(translate("a/abc*foo*"), "abc*foo*");
  460. wildcardsMatchSlash = true;
  461. QCOMPARE(translate(""), "");
  462. QCOMPARE(translate("a/b/"), "");
  463. QCOMPARE(translate("a/b/c"), "c");
  464. QCOMPARE(translate("c"), "c");
  465. QCOMPARE(translate("*"), "*");
  466. QCOMPARE(translate("a/foo*"), "foo*");
  467. QCOMPARE(translate("a/abc?foo*"), "*foo*");
  468. QCOMPARE(translate("a/abc*foo*"), "*foo*");
  469. QCOMPARE(translate("a/abc?foo?"), "*foo?");
  470. QCOMPARE(translate("a/abc*foo?*"), "*foo?*");
  471. QCOMPARE(translate("a/abc*/foo*"), "foo*");
  472. }
  473. void check_csync_is_windows_reserved_word()
  474. {
  475. auto csync_is_windows_reserved_word = [](const char *fn) {
  476. QString s = QString::fromLatin1(fn);
  477. extern bool csync_is_windows_reserved_word(const QStringRef &filename);
  478. return csync_is_windows_reserved_word(&s);
  479. };
  480. QVERIFY(csync_is_windows_reserved_word("CON"));
  481. QVERIFY(csync_is_windows_reserved_word("con"));
  482. QVERIFY(csync_is_windows_reserved_word("CON."));
  483. QVERIFY(csync_is_windows_reserved_word("con."));
  484. QVERIFY(csync_is_windows_reserved_word("CON.ference"));
  485. QVERIFY(!csync_is_windows_reserved_word("CONference"));
  486. QVERIFY(!csync_is_windows_reserved_word("conference"));
  487. QVERIFY(!csync_is_windows_reserved_word("conf.erence"));
  488. QVERIFY(!csync_is_windows_reserved_word("co"));
  489. QVERIFY(csync_is_windows_reserved_word("COM2"));
  490. QVERIFY(csync_is_windows_reserved_word("com2"));
  491. QVERIFY(csync_is_windows_reserved_word("COM2."));
  492. QVERIFY(csync_is_windows_reserved_word("com2."));
  493. QVERIFY(csync_is_windows_reserved_word("COM2.ference"));
  494. QVERIFY(!csync_is_windows_reserved_word("COM2ference"));
  495. QVERIFY(!csync_is_windows_reserved_word("com2ference"));
  496. QVERIFY(!csync_is_windows_reserved_word("com2f.erence"));
  497. QVERIFY(!csync_is_windows_reserved_word("com"));
  498. QVERIFY(csync_is_windows_reserved_word("CLOCK$"));
  499. QVERIFY(csync_is_windows_reserved_word("$Recycle.Bin"));
  500. QVERIFY(csync_is_windows_reserved_word("ClocK$"));
  501. QVERIFY(csync_is_windows_reserved_word("$recycle.bin"));
  502. QVERIFY(csync_is_windows_reserved_word("A:"));
  503. QVERIFY(csync_is_windows_reserved_word("a:"));
  504. QVERIFY(csync_is_windows_reserved_word("z:"));
  505. QVERIFY(csync_is_windows_reserved_word("Z:"));
  506. QVERIFY(csync_is_windows_reserved_word("M:"));
  507. QVERIFY(csync_is_windows_reserved_word("m:"));
  508. }
  509. /* QT_ENABLE_REGEXP_JIT=0 to get slower results :-) */
  510. void check_csync_excluded_performance1()
  511. {
  512. setup_init();
  513. const int N = 1000;
  514. int totalRc = 0;
  515. QBENCHMARK {
  516. for (int i = 0; i < N; ++i) {
  517. totalRc += check_dir_full("/this/is/quite/a/long/path/with/many/components");
  518. 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");
  519. }
  520. QCOMPARE(totalRc, 0); // mainly to avoid optimization
  521. }
  522. }
  523. void check_csync_excluded_performance2()
  524. {
  525. const int N = 1000;
  526. int totalRc = 0;
  527. QBENCHMARK {
  528. for (int i = 0; i < N; ++i) {
  529. totalRc += check_dir_traversal("/this/is/quite/a/long/path/with/many/components");
  530. 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");
  531. }
  532. QCOMPARE(totalRc, 0); // mainly to avoid optimization
  533. }
  534. }
  535. void check_csync_exclude_expand_escapes()
  536. {
  537. extern void csync_exclude_expand_escapes(QByteArray &input);
  538. QByteArray line = R"(keep \' \" \? \\ \a \b \f \n \r \t \v \z \#)";
  539. csync_exclude_expand_escapes(line);
  540. QVERIFY(0 == strcmp(line.constData(), "keep ' \" ? \\\\ \a \b \f \n \r \t \v \\z #"));
  541. line = "";
  542. csync_exclude_expand_escapes(line);
  543. QVERIFY(0 == strcmp(line.constData(), ""));
  544. line = "\\";
  545. csync_exclude_expand_escapes(line);
  546. QVERIFY(0 == strcmp(line.constData(), "\\"));
  547. }
  548. void check_version_directive()
  549. {
  550. ExcludedFiles excludes;
  551. excludes.setClientVersion(ExcludedFiles::Version(2, 5, 0));
  552. std::vector<std::pair<const char *, bool>> tests = {
  553. { "#!version == 2.5.0", true },
  554. { "#!version == 2.6.0", false },
  555. { "#!version < 2.6.0", true },
  556. { "#!version <= 2.6.0", true },
  557. { "#!version > 2.6.0", false },
  558. { "#!version >= 2.6.0", false },
  559. { "#!version < 2.4.0", false },
  560. { "#!version <= 2.4.0", false },
  561. { "#!version > 2.4.0", true },
  562. { "#!version >= 2.4.0", true },
  563. { "#!version < 2.5.0", false },
  564. { "#!version <= 2.5.0", true },
  565. { "#!version > 2.5.0", false },
  566. { "#!version >= 2.5.0", true },
  567. };
  568. for (auto test : tests) {
  569. QVERIFY(excludes.versionDirectiveKeepNextLine(test.first) == test.second);
  570. }
  571. }
  572. void testAddExcludeFilePath_addSameFilePath_listSizeDoesNotIncrease()
  573. {
  574. excludedFiles.reset(new ExcludedFiles());
  575. const auto filePath = QString("exclude/.sync-exclude.lst");
  576. excludedFiles->addExcludeFilePath(filePath);
  577. excludedFiles->addExcludeFilePath(filePath);
  578. QCOMPARE(excludedFiles->_excludeFiles.size(), 1);
  579. }
  580. void testAddExcludeFilePath_addDifferentFilePaths_listSizeIncrease()
  581. {
  582. excludedFiles.reset(new ExcludedFiles());
  583. const auto filePath1 = QString("exclude1/.sync-exclude.lst");
  584. const auto filePath2 = QString("exclude2/.sync-exclude.lst");
  585. excludedFiles->addExcludeFilePath(filePath1);
  586. excludedFiles->addExcludeFilePath(filePath2);
  587. QCOMPARE(excludedFiles->_excludeFiles.size(), 2);
  588. }
  589. void testAddExcludeFilePath_addDefaultExcludeFile_returnCorrectMap()
  590. {
  591. const QString basePath("syncFolder/");
  592. const QString folder1("syncFolder/folder1/");
  593. const QString folder2(folder1 + "folder2/");
  594. excludedFiles.reset(new ExcludedFiles(basePath));
  595. const QString defaultExcludeList("desktop-client/config-folder/sync-exclude.lst");
  596. const QString folder1ExcludeList(folder1 + ".sync-exclude.lst");
  597. const QString folder2ExcludeList(folder2 + ".sync-exclude.lst");
  598. excludedFiles->addExcludeFilePath(defaultExcludeList);
  599. excludedFiles->addExcludeFilePath(folder1ExcludeList);
  600. excludedFiles->addExcludeFilePath(folder2ExcludeList);
  601. QCOMPARE(excludedFiles->_excludeFiles.size(), 3);
  602. QCOMPARE(excludedFiles->_excludeFiles[basePath].first(), defaultExcludeList);
  603. QCOMPARE(excludedFiles->_excludeFiles[folder1].first(), folder1ExcludeList);
  604. QCOMPARE(excludedFiles->_excludeFiles[folder2].first(), folder2ExcludeList);
  605. }
  606. void testReloadExcludeFiles_fileDoesNotExist_returnTrue() {
  607. excludedFiles.reset(new ExcludedFiles());
  608. const QString nonExistingFile("directory/.sync-exclude.lst");
  609. excludedFiles->addExcludeFilePath(nonExistingFile);
  610. QCOMPARE(excludedFiles->reloadExcludeFiles(), true);
  611. QCOMPARE(excludedFiles->_allExcludes.size(), 0);
  612. }
  613. void testReloadExcludeFiles_fileExists_returnTrue()
  614. {
  615. const auto tempDir = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
  616. excludedFiles.reset(new ExcludedFiles(tempDir + "/"));
  617. const auto subTempDir = QStringLiteral("exclude");
  618. QVERIFY(QDir(tempDir).mkpath(subTempDir));
  619. const auto existingFilePath = QString(tempDir + '/' + subTempDir + "/.sync-exclude.lst");
  620. QFile excludeList(existingFilePath);
  621. QVERIFY(excludeList.open(QFile::WriteOnly));
  622. excludeList.close();
  623. excludedFiles->addExcludeFilePath(existingFilePath);
  624. QCOMPARE(excludedFiles->reloadExcludeFiles(), true);
  625. QCOMPARE(excludedFiles->_allExcludes.size(), 1);
  626. }
  627. };
  628. QTEST_APPLESS_MAIN(TestExcludedFiles)
  629. #include "testexcludedfiles.moc"