testlocaldiscovery.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  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 "syncenginetestutils.h"
  9. #include <syncengine.h>
  10. #include <localdiscoverytracker.h>
  11. using namespace OCC;
  12. class TestLocalDiscovery : public QObject
  13. {
  14. Q_OBJECT
  15. private slots:
  16. // Check correct behavior when local discovery is partially drawn from the db
  17. void testLocalDiscoveryStyle()
  18. {
  19. FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
  20. LocalDiscoveryTracker tracker;
  21. connect(&fakeFolder.syncEngine(), &SyncEngine::itemCompleted, &tracker, &LocalDiscoveryTracker::slotItemCompleted);
  22. connect(&fakeFolder.syncEngine(), &SyncEngine::finished, &tracker, &LocalDiscoveryTracker::slotSyncFinished);
  23. // More subdirectories are useful for testing
  24. fakeFolder.localModifier().mkdir("A/X");
  25. fakeFolder.localModifier().mkdir("A/Y");
  26. fakeFolder.localModifier().insert("A/X/x1");
  27. fakeFolder.localModifier().insert("A/Y/y1");
  28. tracker.addTouchedPath("A/X");
  29. tracker.startSyncFullDiscovery();
  30. QVERIFY(fakeFolder.syncOnce());
  31. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  32. QVERIFY(tracker.localDiscoveryPaths().empty());
  33. // Test begins
  34. fakeFolder.localModifier().insert("A/a3");
  35. fakeFolder.localModifier().insert("A/X/x2");
  36. fakeFolder.localModifier().insert("A/Y/y2");
  37. fakeFolder.localModifier().insert("B/b3");
  38. fakeFolder.remoteModifier().insert("C/c3");
  39. fakeFolder.remoteModifier().appendByte("C/c1");
  40. tracker.addTouchedPath("A/X");
  41. fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, tracker.localDiscoveryPaths());
  42. tracker.startSyncPartialDiscovery();
  43. QVERIFY(fakeFolder.syncOnce());
  44. QVERIFY(fakeFolder.currentRemoteState().find("A/a3"));
  45. QVERIFY(fakeFolder.currentRemoteState().find("A/X/x2"));
  46. QVERIFY(!fakeFolder.currentRemoteState().find("A/Y/y2"));
  47. QVERIFY(!fakeFolder.currentRemoteState().find("B/b3"));
  48. QVERIFY(fakeFolder.currentLocalState().find("C/c3"));
  49. QCOMPARE(fakeFolder.syncEngine().lastLocalDiscoveryStyle(), LocalDiscoveryStyle::DatabaseAndFilesystem);
  50. QVERIFY(tracker.localDiscoveryPaths().empty());
  51. QVERIFY(fakeFolder.syncOnce());
  52. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  53. QCOMPARE(fakeFolder.syncEngine().lastLocalDiscoveryStyle(), LocalDiscoveryStyle::FilesystemOnly);
  54. QVERIFY(tracker.localDiscoveryPaths().empty());
  55. }
  56. void testLocalDiscoveryDecision()
  57. {
  58. FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
  59. auto &engine = fakeFolder.syncEngine();
  60. QVERIFY(engine.shouldDiscoverLocally(""));
  61. QVERIFY(engine.shouldDiscoverLocally("A"));
  62. QVERIFY(engine.shouldDiscoverLocally("A/X"));
  63. fakeFolder.syncEngine().setLocalDiscoveryOptions(
  64. LocalDiscoveryStyle::DatabaseAndFilesystem,
  65. { "A/X", "A/X space", "A/X/beta", "foo bar space/touch", "foo/", "zzz", "zzzz" });
  66. QVERIFY(engine.shouldDiscoverLocally(""));
  67. QVERIFY(engine.shouldDiscoverLocally("A"));
  68. QVERIFY(engine.shouldDiscoverLocally("A/X"));
  69. QVERIFY(!engine.shouldDiscoverLocally("B"));
  70. QVERIFY(!engine.shouldDiscoverLocally("A B"));
  71. QVERIFY(!engine.shouldDiscoverLocally("B/X"));
  72. QVERIFY(engine.shouldDiscoverLocally("foo bar space"));
  73. QVERIFY(engine.shouldDiscoverLocally("foo"));
  74. QVERIFY(!engine.shouldDiscoverLocally("foo bar"));
  75. QVERIFY(!engine.shouldDiscoverLocally("foo bar/touch"));
  76. // These are within "A/X" so they should be discovered
  77. QVERIFY(engine.shouldDiscoverLocally("A/X/alpha"));
  78. QVERIFY(engine.shouldDiscoverLocally("A/X beta"));
  79. QVERIFY(engine.shouldDiscoverLocally("A/X/Y"));
  80. QVERIFY(engine.shouldDiscoverLocally("A/X space"));
  81. QVERIFY(engine.shouldDiscoverLocally("A/X space/alpha"));
  82. QVERIFY(!engine.shouldDiscoverLocally("A/Xylo/foo"));
  83. QVERIFY(engine.shouldDiscoverLocally("zzzz/hello"));
  84. QVERIFY(!engine.shouldDiscoverLocally("zzza/hello"));
  85. QEXPECT_FAIL("", "There is a possibility of false positives if the set contains a path "
  86. "which is a prefix, and that prefix is followed by a character less than '/'", Continue);
  87. QVERIFY(!engine.shouldDiscoverLocally("A/X o"));
  88. fakeFolder.syncEngine().setLocalDiscoveryOptions(
  89. LocalDiscoveryStyle::DatabaseAndFilesystem,
  90. {});
  91. QVERIFY(!engine.shouldDiscoverLocally(""));
  92. }
  93. // Check whether item success and item failure adjusts the
  94. // tracker correctly.
  95. void testTrackerItemCompletion()
  96. {
  97. FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
  98. LocalDiscoveryTracker tracker;
  99. connect(&fakeFolder.syncEngine(), &SyncEngine::itemCompleted, &tracker, &LocalDiscoveryTracker::slotItemCompleted);
  100. connect(&fakeFolder.syncEngine(), &SyncEngine::finished, &tracker, &LocalDiscoveryTracker::slotSyncFinished);
  101. auto trackerContains = [&](const char *path) {
  102. return tracker.localDiscoveryPaths().find(path) != tracker.localDiscoveryPaths().end();
  103. };
  104. tracker.addTouchedPath("A/spurious");
  105. fakeFolder.localModifier().insert("A/a3");
  106. tracker.addTouchedPath("A/a3");
  107. fakeFolder.localModifier().insert("A/a4");
  108. fakeFolder.serverErrorPaths().append("A/a4");
  109. // We're not adding a4 as touched, it's in the same folder as a3 and will be seen.
  110. // And due to the error it should be added to the explicit list while a3 gets removed.
  111. fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, tracker.localDiscoveryPaths());
  112. tracker.startSyncPartialDiscovery();
  113. QVERIFY(!fakeFolder.syncOnce());
  114. QVERIFY(fakeFolder.currentRemoteState().find("A/a3"));
  115. QVERIFY(!fakeFolder.currentRemoteState().find("A/a4"));
  116. QVERIFY(!trackerContains("A/a3"));
  117. QVERIFY(trackerContains("A/a4"));
  118. QVERIFY(trackerContains("A/spurious")); // not removed since overall sync not successful
  119. fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::FilesystemOnly);
  120. tracker.startSyncFullDiscovery();
  121. QVERIFY(!fakeFolder.syncOnce());
  122. QVERIFY(!fakeFolder.currentRemoteState().find("A/a4"));
  123. QVERIFY(trackerContains("A/a4")); // had an error, still here
  124. QVERIFY(!trackerContains("A/spurious")); // removed due to full discovery
  125. fakeFolder.serverErrorPaths().clear();
  126. QVERIFY(fakeFolder.syncJournal().wipeErrorBlacklist() != -1);
  127. tracker.addTouchedPath("A/newspurious"); // will be removed due to successful sync
  128. fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, tracker.localDiscoveryPaths());
  129. tracker.startSyncPartialDiscovery();
  130. QVERIFY(fakeFolder.syncOnce());
  131. QVERIFY(fakeFolder.currentRemoteState().find("A/a4"));
  132. QVERIFY(tracker.localDiscoveryPaths().empty());
  133. }
  134. void testDirectoryAndSubDirectory()
  135. {
  136. FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
  137. fakeFolder.localModifier().mkdir("A/newDir");
  138. fakeFolder.localModifier().mkdir("A/newDir/subDir");
  139. fakeFolder.localModifier().insert("A/newDir/subDir/file", 10);
  140. auto expectedState = fakeFolder.currentLocalState();
  141. // Only "A" was modified according to the file system tracker
  142. fakeFolder.syncEngine().setLocalDiscoveryOptions(
  143. LocalDiscoveryStyle::DatabaseAndFilesystem,
  144. { "A" });
  145. QVERIFY(fakeFolder.syncOnce());
  146. QCOMPARE(fakeFolder.currentLocalState(), expectedState);
  147. QCOMPARE(fakeFolder.currentRemoteState(), expectedState);
  148. }
  149. // Tests the behavior of invalid filename detection
  150. void testServerBlacklist()
  151. {
  152. FakeFolder fakeFolder { FileInfo::A12_B12_C12_S12() };
  153. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  154. fakeFolder.syncEngine().account()->setCapabilities({ { "files",
  155. QVariantMap { { "blacklisted_files", QVariantList { ".foo", "bar" } } } } });
  156. fakeFolder.localModifier().insert("C/.foo");
  157. fakeFolder.localModifier().insert("C/bar");
  158. fakeFolder.localModifier().insert("C/moo");
  159. fakeFolder.localModifier().insert("C/.moo");
  160. QVERIFY(fakeFolder.syncOnce());
  161. QVERIFY(fakeFolder.currentRemoteState().find("C/moo"));
  162. QVERIFY(fakeFolder.currentRemoteState().find("C/.moo"));
  163. QVERIFY(!fakeFolder.currentRemoteState().find("C/.foo"));
  164. QVERIFY(!fakeFolder.currentRemoteState().find("C/bar"));
  165. }
  166. void testCreateFileWithTrailingSpaces_localAndRemoteTrimmedDoNotExist_renameAndUploadFile()
  167. {
  168. FakeFolder fakeFolder{FileInfo{}};
  169. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  170. const QString fileWithSpaces1(" foo");
  171. const QString fileWithSpaces2(" bar ");
  172. const QString fileWithSpaces3("bla ");
  173. const QString fileWithSpaces4("A/ foo");
  174. const QString fileWithSpaces5("A/ bar ");
  175. const QString fileWithSpaces6("A/bla ");
  176. fakeFolder.localModifier().insert(fileWithSpaces1);
  177. fakeFolder.localModifier().insert(fileWithSpaces2);
  178. fakeFolder.localModifier().insert(fileWithSpaces3);
  179. fakeFolder.localModifier().mkdir("A");
  180. fakeFolder.localModifier().insert(fileWithSpaces4);
  181. fakeFolder.localModifier().insert(fileWithSpaces5);
  182. fakeFolder.localModifier().insert(fileWithSpaces6);
  183. fakeFolder.localModifier().mkdir(QStringLiteral(" with spaces "));
  184. ItemCompletedSpy completeSpy(fakeFolder);
  185. completeSpy.clear();
  186. QVERIFY(fakeFolder.syncOnce());
  187. QCOMPARE(completeSpy.findItem(fileWithSpaces1)->_status, SyncFileItem::Status::FileNameInvalid);
  188. QCOMPARE(completeSpy.findItem(fileWithSpaces2)->_status, SyncFileItem::Status::FileNameInvalid);
  189. QCOMPARE(completeSpy.findItem(fileWithSpaces3)->_status, SyncFileItem::Status::FileNameInvalid);
  190. QCOMPARE(completeSpy.findItem(fileWithSpaces4)->_status, SyncFileItem::Status::FileNameInvalid);
  191. QCOMPARE(completeSpy.findItem(fileWithSpaces5)->_status, SyncFileItem::Status::FileNameInvalid);
  192. QCOMPARE(completeSpy.findItem(fileWithSpaces6)->_status, SyncFileItem::Status::FileNameInvalid);
  193. QCOMPARE(completeSpy.findItem(QStringLiteral(" with spaces "))->_status, SyncFileItem::Status::FileNameInvalid);
  194. fakeFolder.syncEngine().addAcceptedInvalidFileName(fakeFolder.localPath() + fileWithSpaces1);
  195. fakeFolder.syncEngine().addAcceptedInvalidFileName(fakeFolder.localPath() + fileWithSpaces2);
  196. fakeFolder.syncEngine().addAcceptedInvalidFileName(fakeFolder.localPath() + fileWithSpaces3);
  197. fakeFolder.syncEngine().addAcceptedInvalidFileName(fakeFolder.localPath() + fileWithSpaces4);
  198. fakeFolder.syncEngine().addAcceptedInvalidFileName(fakeFolder.localPath() + fileWithSpaces5);
  199. fakeFolder.syncEngine().addAcceptedInvalidFileName(fakeFolder.localPath() + fileWithSpaces6);
  200. fakeFolder.syncEngine().addAcceptedInvalidFileName(fakeFolder.localPath() + QStringLiteral(" with spaces "));
  201. completeSpy.clear();
  202. fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, {QStringLiteral("foo"), QStringLiteral("bar"), QStringLiteral("bla"), QStringLiteral("A/foo"), QStringLiteral("A/bar"), QStringLiteral("A/bla")});
  203. QVERIFY(fakeFolder.syncOnce());
  204. QCOMPARE(completeSpy.findItem(fileWithSpaces1)->_status, SyncFileItem::Status::Success);
  205. QCOMPARE(completeSpy.findItem(fileWithSpaces2)->_status, SyncFileItem::Status::Success);
  206. QCOMPARE(completeSpy.findItem(fileWithSpaces3)->_status, SyncFileItem::Status::Success);
  207. QCOMPARE(completeSpy.findItem(fileWithSpaces4)->_status, SyncFileItem::Status::Success);
  208. QCOMPARE(completeSpy.findItem(fileWithSpaces5)->_status, SyncFileItem::Status::Success);
  209. QCOMPARE(completeSpy.findItem(fileWithSpaces6)->_status, SyncFileItem::Status::Success);
  210. QCOMPARE(completeSpy.findItem(QStringLiteral(" with spaces "))->_status, SyncFileItem::Status::Success);
  211. }
  212. void testCreateFileWithTrailingSpaces_remoteDontGetRenamedAutomatically()
  213. {
  214. // On Windows we can't create files/folders with leading/trailing spaces locally. So, we have to fail those items. On other OSs - we just sync them down normally.
  215. FakeFolder fakeFolder{FileInfo()};
  216. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  217. const QString fileWithSpaces4("A/ foo");
  218. const QString fileWithSpaces5("A/ bar ");
  219. const QString fileWithSpaces6("A/bla ");
  220. fakeFolder.remoteModifier().mkdir("A");
  221. fakeFolder.remoteModifier().insert(fileWithSpaces4);
  222. fakeFolder.remoteModifier().insert(fileWithSpaces5);
  223. fakeFolder.remoteModifier().insert(fileWithSpaces6);
  224. ItemCompletedSpy completeSpy(fakeFolder);
  225. completeSpy.clear();
  226. QVERIFY(fakeFolder.syncOnce());
  227. if (Utility::isWindows()) {
  228. QCOMPARE(completeSpy.findItem(fileWithSpaces4)->_status, SyncFileItem::Status::FileNameInvalid);
  229. QCOMPARE(completeSpy.findItem(fileWithSpaces5)->_status, SyncFileItem::Status::FileNameInvalid);
  230. QCOMPARE(completeSpy.findItem(fileWithSpaces6)->_status, SyncFileItem::Status::FileNameInvalid);
  231. } else {
  232. QCOMPARE(completeSpy.findItem(fileWithSpaces4)->_status, SyncFileItem::Status::Success);
  233. QCOMPARE(completeSpy.findItem(fileWithSpaces5)->_status, SyncFileItem::Status::Success);
  234. QCOMPARE(completeSpy.findItem(fileWithSpaces6)->_status, SyncFileItem::Status::Success);
  235. }
  236. }
  237. void testCreateFileWithTrailingSpaces_remoteGetRenamedManually()
  238. {
  239. // On Windows we can't create files/folders with leading/trailing spaces locally. So, we have to fail those items. On other OSs - we just sync them down normally.
  240. FakeFolder fakeFolder{FileInfo()};
  241. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  242. const QString fileWithSpaces4("A/ foo");
  243. const QString fileWithSpaces5("A/ bar ");
  244. const QString fileWithSpaces6("A/bla ");
  245. const QString fileWithoutSpaces4("A/foo");
  246. const QString fileWithoutSpaces5("A/bar");
  247. const QString fileWithoutSpaces6("A/bla");
  248. fakeFolder.remoteModifier().mkdir("A");
  249. fakeFolder.remoteModifier().insert(fileWithSpaces4);
  250. fakeFolder.remoteModifier().insert(fileWithSpaces5);
  251. fakeFolder.remoteModifier().insert(fileWithSpaces6);
  252. ItemCompletedSpy completeSpy(fakeFolder);
  253. completeSpy.clear();
  254. QVERIFY(fakeFolder.syncOnce());
  255. if (Utility::isWindows()) {
  256. QCOMPARE(completeSpy.findItem(fileWithSpaces4)->_status, SyncFileItem::Status::FileNameInvalid);
  257. QCOMPARE(completeSpy.findItem(fileWithSpaces5)->_status, SyncFileItem::Status::FileNameInvalid);
  258. QCOMPARE(completeSpy.findItem(fileWithSpaces6)->_status, SyncFileItem::Status::FileNameInvalid);
  259. } else {
  260. QCOMPARE(completeSpy.findItem(fileWithSpaces4)->_status, SyncFileItem::Status::Success);
  261. QCOMPARE(completeSpy.findItem(fileWithSpaces5)->_status, SyncFileItem::Status::Success);
  262. QCOMPARE(completeSpy.findItem(fileWithSpaces6)->_status, SyncFileItem::Status::Success);
  263. }
  264. fakeFolder.remoteModifier().rename(fileWithSpaces4, fileWithoutSpaces4);
  265. fakeFolder.remoteModifier().rename(fileWithSpaces5, fileWithoutSpaces5);
  266. fakeFolder.remoteModifier().rename(fileWithSpaces6, fileWithoutSpaces6);
  267. completeSpy.clear();
  268. QVERIFY(fakeFolder.syncOnce());
  269. QCOMPARE(completeSpy.findItem(fileWithoutSpaces4)->_status, SyncFileItem::Status::Success);
  270. QCOMPARE(completeSpy.findItem(fileWithoutSpaces5)->_status, SyncFileItem::Status::Success);
  271. QCOMPARE(completeSpy.findItem(fileWithoutSpaces6)->_status, SyncFileItem::Status::Success);
  272. }
  273. void testCreateFileWithTrailingSpaces_localTrimmedAlsoCreated_dontRenameAutomaticallyAndDontUploadFile()
  274. {
  275. FakeFolder fakeFolder{FileInfo{}};
  276. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  277. const QString fileWithSpaces(" foo");
  278. const QString fileTrimmed("foo");
  279. fakeFolder.localModifier().insert(fileTrimmed);
  280. fakeFolder.localModifier().insert(fileWithSpaces);
  281. QVERIFY(fakeFolder.syncOnce());
  282. QVERIFY(fakeFolder.currentRemoteState().find(fileTrimmed));
  283. QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces));
  284. QVERIFY(fakeFolder.currentLocalState().find(fileWithSpaces));
  285. QVERIFY(fakeFolder.currentLocalState().find(fileTrimmed));
  286. }
  287. void testCreateFileWithTrailingSpaces_localTrimmedAlsoCreated_dontRenameAutomaticallyAndUploadBothFiles()
  288. {
  289. FakeFolder fakeFolder{FileInfo{}};
  290. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  291. const QString fileWithSpaces(" foo");
  292. const QString fileTrimmed("foo");
  293. fakeFolder.localModifier().insert(fileTrimmed);
  294. fakeFolder.localModifier().insert(fileWithSpaces);
  295. fakeFolder.syncEngine().addAcceptedInvalidFileName(fakeFolder.localPath() + fileWithSpaces);
  296. QVERIFY(fakeFolder.syncOnce());
  297. QVERIFY(fakeFolder.currentRemoteState().find(fileTrimmed));
  298. QVERIFY(fakeFolder.currentRemoteState().find(fileWithSpaces));
  299. QVERIFY(fakeFolder.currentLocalState().find(fileWithSpaces));
  300. QVERIFY(fakeFolder.currentLocalState().find(fileTrimmed));
  301. }
  302. void testCreateFileWithTrailingSpaces_localAndRemoteTrimmedExists_renameFile()
  303. {
  304. FakeFolder fakeFolder{FileInfo{}};
  305. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  306. const QString fileWithSpaces1(" foo");
  307. const QString fileWithSpaces2(" bar ");
  308. const QString fileWithSpaces3("bla ");
  309. fakeFolder.localModifier().insert(fileWithSpaces1);
  310. fakeFolder.localModifier().insert(fileWithSpaces2);
  311. fakeFolder.localModifier().insert(fileWithSpaces3);
  312. fakeFolder.remoteModifier().insert(fileWithSpaces1);
  313. fakeFolder.remoteModifier().insert(fileWithSpaces2);
  314. fakeFolder.remoteModifier().insert(fileWithSpaces3);
  315. QVERIFY(fakeFolder.syncOnce());
  316. QVERIFY(fakeFolder.syncOnce());
  317. QVERIFY(fakeFolder.syncOnce());
  318. auto expectedState = fakeFolder.currentLocalState();
  319. qDebug() << expectedState;
  320. QCOMPARE(fakeFolder.currentRemoteState(), expectedState);
  321. }
  322. void testBlockInvalidMtimeSyncRemote()
  323. {
  324. constexpr auto INVALID_MODTIME1 = 0;
  325. constexpr auto INVALID_MODTIME2 = -3600;
  326. FakeFolder fakeFolder{FileInfo{}};
  327. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  328. const QString fooFileRootFolder("foo");
  329. const QString barFileRootFolder("bar");
  330. const QString blaFileRootFolder("bla");
  331. const QString fooFileSubFolder("subfolder/foo");
  332. const QString barFileSubFolder("subfolder/bar");
  333. const QString blaFileSubFolder("subfolder/bla");
  334. fakeFolder.remoteModifier().insert(fooFileRootFolder);
  335. fakeFolder.remoteModifier().insert(barFileRootFolder);
  336. fakeFolder.remoteModifier().insert(blaFileRootFolder);
  337. fakeFolder.remoteModifier().mkdir(QStringLiteral("subfolder"));
  338. fakeFolder.remoteModifier().insert(fooFileSubFolder);
  339. fakeFolder.remoteModifier().insert(barFileSubFolder);
  340. fakeFolder.remoteModifier().insert(blaFileSubFolder);
  341. QVERIFY(fakeFolder.syncOnce());
  342. fakeFolder.remoteModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  343. fakeFolder.remoteModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  344. fakeFolder.remoteModifier().setModTime(blaFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  345. fakeFolder.remoteModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  346. fakeFolder.remoteModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  347. fakeFolder.remoteModifier().setModTime(blaFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  348. QVERIFY(!fakeFolder.syncOnce());
  349. QVERIFY(!fakeFolder.syncOnce());
  350. fakeFolder.remoteModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  351. fakeFolder.remoteModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  352. fakeFolder.remoteModifier().setModTime(blaFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  353. fakeFolder.remoteModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  354. fakeFolder.remoteModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  355. fakeFolder.remoteModifier().setModTime(blaFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  356. QVERIFY(!fakeFolder.syncOnce());
  357. QVERIFY(!fakeFolder.syncOnce());
  358. }
  359. void testBlockInvalidMtimeSyncLocal()
  360. {
  361. constexpr auto INVALID_MODTIME1 = 0;
  362. constexpr auto INVALID_MODTIME2 = -3600;
  363. FakeFolder fakeFolder{FileInfo{}};
  364. int nGET = 0;
  365. fakeFolder.setServerOverride([&](QNetworkAccessManager::Operation op, const QNetworkRequest &, QIODevice *) {
  366. if (op == QNetworkAccessManager::GetOperation)
  367. ++nGET;
  368. return nullptr;
  369. });
  370. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  371. const QString fooFileRootFolder("foo");
  372. const QString barFileRootFolder("bar");
  373. const QString blaFileRootFolder("bla");
  374. const QString fooFileSubFolder("subfolder/foo");
  375. const QString barFileSubFolder("subfolder/bar");
  376. const QString blaFileSubFolder("subfolder/bla");
  377. fakeFolder.remoteModifier().insert(fooFileRootFolder);
  378. fakeFolder.remoteModifier().insert(barFileRootFolder);
  379. fakeFolder.remoteModifier().insert(blaFileRootFolder);
  380. fakeFolder.remoteModifier().mkdir(QStringLiteral("subfolder"));
  381. fakeFolder.remoteModifier().insert(fooFileSubFolder);
  382. fakeFolder.remoteModifier().insert(barFileSubFolder);
  383. fakeFolder.remoteModifier().insert(blaFileSubFolder);
  384. QVERIFY(fakeFolder.syncOnce());
  385. nGET = 0;
  386. fakeFolder.localModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  387. fakeFolder.localModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  388. fakeFolder.localModifier().setModTime(blaFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  389. fakeFolder.localModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  390. fakeFolder.localModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  391. fakeFolder.localModifier().setModTime(blaFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  392. QVERIFY(fakeFolder.syncOnce());
  393. QCOMPARE(nGET, 0);
  394. QVERIFY(fakeFolder.syncOnce());
  395. QCOMPARE(nGET, 0);
  396. fakeFolder.localModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  397. fakeFolder.localModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  398. fakeFolder.localModifier().setModTime(blaFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  399. fakeFolder.localModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  400. fakeFolder.localModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  401. fakeFolder.localModifier().setModTime(blaFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  402. QVERIFY(fakeFolder.syncOnce());
  403. QCOMPARE(nGET, 0);
  404. QVERIFY(fakeFolder.syncOnce());
  405. QCOMPARE(nGET, 0);
  406. }
  407. void testDoNotSyncInvalidFutureMtime()
  408. {
  409. constexpr auto FUTURE_MTIME = 0xFFFFFFFF;
  410. constexpr auto CURRENT_MTIME = 1646057277;
  411. FakeFolder fakeFolder{FileInfo{}};
  412. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  413. const QString fooFileRootFolder("foo");
  414. const QString barFileRootFolder("bar");
  415. const QString fooFileSubFolder("subfolder/foo");
  416. const QString barFileSubFolder("subfolder/bar");
  417. const QString fooFileAaaSubFolder("aaa/subfolder/foo");
  418. const QString barFileAaaSubFolder("aaa/subfolder/bar");
  419. fakeFolder.remoteModifier().insert(fooFileRootFolder);
  420. fakeFolder.remoteModifier().insert(barFileRootFolder);
  421. fakeFolder.remoteModifier().mkdir(QStringLiteral("subfolder"));
  422. fakeFolder.remoteModifier().insert(fooFileSubFolder);
  423. fakeFolder.remoteModifier().insert(barFileSubFolder);
  424. fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa"));
  425. fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa/subfolder"));
  426. fakeFolder.remoteModifier().insert(fooFileAaaSubFolder);
  427. fakeFolder.remoteModifier().insert(barFileAaaSubFolder);
  428. QVERIFY(fakeFolder.syncOnce());
  429. fakeFolder.remoteModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  430. fakeFolder.remoteModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  431. fakeFolder.remoteModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  432. fakeFolder.remoteModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  433. fakeFolder.remoteModifier().setModTime(fooFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  434. fakeFolder.remoteModifier().setModTime(barFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  435. fakeFolder.localModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  436. fakeFolder.localModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  437. fakeFolder.localModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  438. fakeFolder.localModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  439. fakeFolder.localModifier().setModTime(fooFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  440. fakeFolder.localModifier().setModTime(barFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  441. QVERIFY(!fakeFolder.syncOnce());
  442. }
  443. void testInvalidFutureMtimeRecovery()
  444. {
  445. constexpr auto FUTURE_MTIME = 0xFFFFFFFF;
  446. constexpr auto CURRENT_MTIME = 1646057277;
  447. FakeFolder fakeFolder{FileInfo{}};
  448. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  449. const QString fooFileRootFolder("foo");
  450. const QString barFileRootFolder("bar");
  451. const QString fooFileSubFolder("subfolder/foo");
  452. const QString barFileSubFolder("subfolder/bar");
  453. const QString fooFileAaaSubFolder("aaa/subfolder/foo");
  454. const QString barFileAaaSubFolder("aaa/subfolder/bar");
  455. fakeFolder.remoteModifier().insert(fooFileRootFolder);
  456. fakeFolder.remoteModifier().insert(barFileRootFolder);
  457. fakeFolder.remoteModifier().mkdir(QStringLiteral("subfolder"));
  458. fakeFolder.remoteModifier().insert(fooFileSubFolder);
  459. fakeFolder.remoteModifier().insert(barFileSubFolder);
  460. fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa"));
  461. fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa/subfolder"));
  462. fakeFolder.remoteModifier().insert(fooFileAaaSubFolder);
  463. fakeFolder.remoteModifier().insert(barFileAaaSubFolder);
  464. QVERIFY(fakeFolder.syncOnce());
  465. fakeFolder.remoteModifier().setModTimeKeepEtag(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  466. fakeFolder.remoteModifier().setModTimeKeepEtag(barFileRootFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  467. fakeFolder.remoteModifier().setModTimeKeepEtag(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  468. fakeFolder.remoteModifier().setModTimeKeepEtag(barFileSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  469. fakeFolder.remoteModifier().setModTimeKeepEtag(fooFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  470. fakeFolder.remoteModifier().setModTimeKeepEtag(barFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  471. fakeFolder.localModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  472. fakeFolder.localModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  473. fakeFolder.localModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  474. fakeFolder.localModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  475. fakeFolder.localModifier().setModTime(fooFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  476. fakeFolder.localModifier().setModTime(barFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  477. QVERIFY(fakeFolder.syncOnce());
  478. QVERIFY(fakeFolder.syncOnce());
  479. auto expectedState = fakeFolder.currentLocalState();
  480. QCOMPARE(fakeFolder.currentRemoteState(), expectedState);
  481. }
  482. void testDiscoverLockChanges()
  483. {
  484. FakeFolder fakeFolder{FileInfo{}};
  485. fakeFolder.syncEngine().account()->setCapabilities({{"activity", QVariantMap{{"apiv2", QVariantList{"filters", "filters-api", "previews", "rich-strings"}}}},
  486. {"bruteforce", QVariantMap{{"delay", 0}}},
  487. {"core", QVariantMap{{"pollinterval", 60}, {"webdav-root", "remote.php/webdav"}}},
  488. {"dav", QVariantMap{{"chunking", "1.0"}}},
  489. {"files", QVariantMap{{"bigfilechunking", true}, {"blacklisted_files", QVariantList{".htaccess"}},
  490. {"comments", true},
  491. {"directEditing", QVariantMap{{"etag", "c748e8fc588b54fc5af38c4481a19d20"}, {"url", "https://nextcloud.local/ocs/v2.php/apps/files/api/v1/directEditing"}}},
  492. {"locking", "1.0"}}}});
  493. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  494. const QString fooFileRootFolder("foo");
  495. const QString barFileRootFolder("bar");
  496. const QString fooFileSubFolder("subfolder/foo");
  497. const QString barFileSubFolder("subfolder/bar");
  498. const QString fooFileAaaSubFolder("aaa/subfolder/foo");
  499. const QString barFileAaaSubFolder("aaa/subfolder/bar");
  500. fakeFolder.remoteModifier().insert(fooFileRootFolder);
  501. fakeFolder.remoteModifier().insert(barFileRootFolder);
  502. fakeFolder.remoteModifier().modifyLockState(QStringLiteral("bar"), FileInfo::LockState::FileLocked, 0, QStringLiteral("user1"), {}, QStringLiteral("user1"), 1648046707, 0);
  503. fakeFolder.remoteModifier().mkdir(QStringLiteral("subfolder"));
  504. fakeFolder.remoteModifier().insert(fooFileSubFolder);
  505. fakeFolder.remoteModifier().insert(barFileSubFolder);
  506. fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa"));
  507. fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa/subfolder"));
  508. fakeFolder.remoteModifier().insert(fooFileAaaSubFolder);
  509. fakeFolder.remoteModifier().insert(barFileAaaSubFolder);
  510. ItemCompletedSpy completeSpy(fakeFolder);
  511. completeSpy.clear();
  512. QVERIFY(fakeFolder.syncOnce());
  513. QCOMPARE(completeSpy.findItem("bar")->_locked, OCC::SyncFileItem::LockStatus::LockedItem);
  514. SyncJournalFileRecord fileRecordBefore;
  515. QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("bar"), &fileRecordBefore));
  516. QVERIFY(fileRecordBefore._lockstate._locked);
  517. fakeFolder.remoteModifier().modifyLockState(QStringLiteral("bar"), FileInfo::LockState::FileUnlocked, {}, {}, {}, {}, {}, {});
  518. fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem);
  519. completeSpy.clear();
  520. QVERIFY(fakeFolder.syncOnce());
  521. QCOMPARE(completeSpy.findItem("bar")->_locked, OCC::SyncFileItem::LockStatus::UnlockedItem);
  522. SyncJournalFileRecord fileRecordAfter;
  523. QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("bar"), &fileRecordAfter));
  524. QVERIFY(!fileRecordAfter._lockstate._locked);
  525. }
  526. };
  527. QTEST_GUILESS_MAIN(TestLocalDiscovery)
  528. #include "testlocaldiscovery.moc"