testlocaldiscovery.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  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. fakeFolder.syncJournal().wipeErrorBlacklist();
  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. QVERIFY(fakeFolder.syncOnce());
  185. QVERIFY(fakeFolder.currentLocalState().find(fileWithSpaces1.trimmed()));
  186. QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces1));
  187. QVERIFY(fakeFolder.currentLocalState().find(fileWithSpaces2.trimmed()));
  188. QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces2));
  189. QVERIFY(fakeFolder.currentLocalState().find(fileWithSpaces3.trimmed()));
  190. QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces3));
  191. QVERIFY(fakeFolder.currentLocalState().find("A/foo"));
  192. QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces4));
  193. QVERIFY(fakeFolder.currentLocalState().find("A/bar"));
  194. QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces5));
  195. QVERIFY(fakeFolder.currentLocalState().find("A/bla"));
  196. QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces6));
  197. QVERIFY(fakeFolder.currentLocalState().find(QStringLiteral("with spaces")));
  198. QVERIFY(!fakeFolder.currentLocalState().find(QStringLiteral(" with spaces ")));
  199. fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, {QStringLiteral("foo"), QStringLiteral("bar"), QStringLiteral("bla"), QStringLiteral("A/foo"), QStringLiteral("A/bar"), QStringLiteral("A/bla")});
  200. QVERIFY(fakeFolder.syncOnce());
  201. QVERIFY(fakeFolder.currentRemoteState().find(fileWithSpaces1.trimmed()));
  202. QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces1));
  203. QVERIFY(fakeFolder.currentLocalState().find(fileWithSpaces1.trimmed()));
  204. QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces1));
  205. QVERIFY(fakeFolder.currentRemoteState().find(fileWithSpaces2.trimmed()));
  206. QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces2));
  207. QVERIFY(fakeFolder.currentLocalState().find(fileWithSpaces2.trimmed()));
  208. QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces2));
  209. QVERIFY(fakeFolder.currentRemoteState().find(fileWithSpaces3.trimmed()));
  210. QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces3));
  211. QVERIFY(fakeFolder.currentLocalState().find(fileWithSpaces3.trimmed()));
  212. QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces3));
  213. QVERIFY(fakeFolder.currentRemoteState().find("A/foo"));
  214. QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces4));
  215. QVERIFY(fakeFolder.currentLocalState().find("A/foo"));
  216. QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces4));
  217. QVERIFY(fakeFolder.currentRemoteState().find("A/bar"));
  218. QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces5));
  219. QVERIFY(fakeFolder.currentLocalState().find("A/bar"));
  220. QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces5));
  221. QVERIFY(fakeFolder.currentRemoteState().find("A/bla"));
  222. QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces6));
  223. QVERIFY(fakeFolder.currentLocalState().find("A/bla"));
  224. QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces6));
  225. QVERIFY(fakeFolder.currentRemoteState().find(QStringLiteral("with spaces")));
  226. QVERIFY(!fakeFolder.currentRemoteState().find(QStringLiteral(" with spaces ")));
  227. QVERIFY(fakeFolder.currentLocalState().find(QStringLiteral("with spaces")));
  228. QVERIFY(!fakeFolder.currentLocalState().find(QStringLiteral(" with spaces ")));
  229. }
  230. void testCreateFileWithTrailingSpaces_localAndRemoteTrimmedDoNotExist_renameFile()
  231. {
  232. FakeFolder fakeFolder{FileInfo{}};
  233. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  234. const QString fileWithSpaces4("A/ foo");
  235. const QString fileWithSpaces5("A/ bar ");
  236. const QString fileWithSpaces6("A/bla ");
  237. fakeFolder.remoteModifier().mkdir("A");
  238. fakeFolder.remoteModifier().insert(fileWithSpaces4);
  239. fakeFolder.remoteModifier().insert(fileWithSpaces5);
  240. fakeFolder.remoteModifier().insert(fileWithSpaces6);
  241. qDebug() << fakeFolder.currentRemoteState();
  242. QVERIFY(fakeFolder.syncOnce());
  243. qDebug() << fakeFolder.currentRemoteState();
  244. QVERIFY(fakeFolder.currentRemoteState().find("A/foo"));
  245. QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces4));
  246. QVERIFY(fakeFolder.currentRemoteState().find("A/bar"));
  247. QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces5));
  248. QVERIFY(fakeFolder.currentRemoteState().find("A/bla"));
  249. QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces6));
  250. QVERIFY(fakeFolder.syncOnce());
  251. QVERIFY(fakeFolder.currentRemoteState().find("A/foo"));
  252. QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces4));
  253. QVERIFY(fakeFolder.currentLocalState().find("A/foo"));
  254. QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces4));
  255. QVERIFY(fakeFolder.currentRemoteState().find("A/bar"));
  256. QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces5));
  257. QVERIFY(fakeFolder.currentLocalState().find("A/bar"));
  258. QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces5));
  259. QVERIFY(fakeFolder.currentRemoteState().find("A/bla"));
  260. QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces6));
  261. QVERIFY(fakeFolder.currentLocalState().find("A/bla"));
  262. QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces6));
  263. auto expectedState = fakeFolder.currentLocalState();
  264. QCOMPARE(fakeFolder.currentRemoteState(), expectedState);
  265. }
  266. void testCreateFileWithTrailingSpaces_localTrimmedDoesExist_dontRenameAndUploadFile()
  267. {
  268. FakeFolder fakeFolder{FileInfo{}};
  269. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  270. const QString fileWithSpaces(" foo");
  271. const QString fileTrimmed("foo");
  272. fakeFolder.localModifier().insert(fileTrimmed);
  273. QVERIFY(fakeFolder.syncOnce());
  274. fakeFolder.localModifier().insert(fileWithSpaces);
  275. QVERIFY(!fakeFolder.syncOnce());
  276. QVERIFY(fakeFolder.currentRemoteState().find(fileTrimmed));
  277. QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces));
  278. QVERIFY(fakeFolder.currentLocalState().find(fileWithSpaces));
  279. QVERIFY(fakeFolder.currentLocalState().find(fileTrimmed));
  280. }
  281. void testCreateFileWithTrailingSpaces_localTrimmedAlsoCreated_dontRenameAndUploadFile()
  282. {
  283. FakeFolder fakeFolder{FileInfo{}};
  284. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  285. const QString fileWithSpaces(" foo");
  286. const QString fileTrimmed("foo");
  287. fakeFolder.localModifier().insert(fileTrimmed);
  288. fakeFolder.localModifier().insert(fileWithSpaces);
  289. QVERIFY(!fakeFolder.syncOnce());
  290. QVERIFY(fakeFolder.currentRemoteState().find(fileTrimmed));
  291. QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces));
  292. QVERIFY(fakeFolder.currentLocalState().find(fileWithSpaces));
  293. QVERIFY(fakeFolder.currentLocalState().find(fileTrimmed));
  294. }
  295. void testCreateFileWithTrailingSpaces_localAndRemoteTrimmedExists_renameFile()
  296. {
  297. FakeFolder fakeFolder{FileInfo{}};
  298. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  299. const QString fileWithSpaces1(" foo");
  300. const QString fileWithSpaces2(" bar ");
  301. const QString fileWithSpaces3("bla ");
  302. fakeFolder.localModifier().insert(fileWithSpaces1);
  303. fakeFolder.localModifier().insert(fileWithSpaces2);
  304. fakeFolder.localModifier().insert(fileWithSpaces3);
  305. fakeFolder.remoteModifier().insert(fileWithSpaces1);
  306. fakeFolder.remoteModifier().insert(fileWithSpaces2);
  307. fakeFolder.remoteModifier().insert(fileWithSpaces3);
  308. QVERIFY(fakeFolder.syncOnce());
  309. QVERIFY(fakeFolder.syncOnce());
  310. QVERIFY(fakeFolder.syncOnce());
  311. auto expectedState = fakeFolder.currentLocalState();
  312. qDebug() << expectedState;
  313. QCOMPARE(fakeFolder.currentRemoteState(), expectedState);
  314. }
  315. void testBlockInvalidMtimeSyncRemote()
  316. {
  317. constexpr auto INVALID_MODTIME1 = 0;
  318. constexpr auto INVALID_MODTIME2 = -3600;
  319. FakeFolder fakeFolder{FileInfo{}};
  320. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  321. const QString fooFileRootFolder("foo");
  322. const QString barFileRootFolder("bar");
  323. const QString blaFileRootFolder("bla");
  324. const QString fooFileSubFolder("subfolder/foo");
  325. const QString barFileSubFolder("subfolder/bar");
  326. const QString blaFileSubFolder("subfolder/bla");
  327. fakeFolder.remoteModifier().insert(fooFileRootFolder);
  328. fakeFolder.remoteModifier().insert(barFileRootFolder);
  329. fakeFolder.remoteModifier().insert(blaFileRootFolder);
  330. fakeFolder.remoteModifier().mkdir(QStringLiteral("subfolder"));
  331. fakeFolder.remoteModifier().insert(fooFileSubFolder);
  332. fakeFolder.remoteModifier().insert(barFileSubFolder);
  333. fakeFolder.remoteModifier().insert(blaFileSubFolder);
  334. QVERIFY(fakeFolder.syncOnce());
  335. fakeFolder.remoteModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  336. fakeFolder.remoteModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  337. fakeFolder.remoteModifier().setModTime(blaFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  338. fakeFolder.remoteModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  339. fakeFolder.remoteModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  340. fakeFolder.remoteModifier().setModTime(blaFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  341. QVERIFY(!fakeFolder.syncOnce());
  342. QVERIFY(!fakeFolder.syncOnce());
  343. fakeFolder.remoteModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  344. fakeFolder.remoteModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  345. fakeFolder.remoteModifier().setModTime(blaFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  346. fakeFolder.remoteModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  347. fakeFolder.remoteModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  348. fakeFolder.remoteModifier().setModTime(blaFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  349. QVERIFY(!fakeFolder.syncOnce());
  350. QVERIFY(!fakeFolder.syncOnce());
  351. }
  352. void testBlockInvalidMtimeSyncLocal()
  353. {
  354. constexpr auto INVALID_MODTIME1 = 0;
  355. constexpr auto INVALID_MODTIME2 = -3600;
  356. FakeFolder fakeFolder{FileInfo{}};
  357. int nGET = 0;
  358. fakeFolder.setServerOverride([&](QNetworkAccessManager::Operation op, const QNetworkRequest &, QIODevice *) {
  359. if (op == QNetworkAccessManager::GetOperation)
  360. ++nGET;
  361. return nullptr;
  362. });
  363. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  364. const QString fooFileRootFolder("foo");
  365. const QString barFileRootFolder("bar");
  366. const QString blaFileRootFolder("bla");
  367. const QString fooFileSubFolder("subfolder/foo");
  368. const QString barFileSubFolder("subfolder/bar");
  369. const QString blaFileSubFolder("subfolder/bla");
  370. fakeFolder.remoteModifier().insert(fooFileRootFolder);
  371. fakeFolder.remoteModifier().insert(barFileRootFolder);
  372. fakeFolder.remoteModifier().insert(blaFileRootFolder);
  373. fakeFolder.remoteModifier().mkdir(QStringLiteral("subfolder"));
  374. fakeFolder.remoteModifier().insert(fooFileSubFolder);
  375. fakeFolder.remoteModifier().insert(barFileSubFolder);
  376. fakeFolder.remoteModifier().insert(blaFileSubFolder);
  377. QVERIFY(fakeFolder.syncOnce());
  378. nGET = 0;
  379. fakeFolder.localModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  380. fakeFolder.localModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  381. fakeFolder.localModifier().setModTime(blaFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  382. fakeFolder.localModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  383. fakeFolder.localModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  384. fakeFolder.localModifier().setModTime(blaFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
  385. QVERIFY(fakeFolder.syncOnce());
  386. QCOMPARE(nGET, 0);
  387. QVERIFY(fakeFolder.syncOnce());
  388. QCOMPARE(nGET, 0);
  389. fakeFolder.localModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  390. fakeFolder.localModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  391. fakeFolder.localModifier().setModTime(blaFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  392. fakeFolder.localModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  393. fakeFolder.localModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  394. fakeFolder.localModifier().setModTime(blaFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
  395. QVERIFY(fakeFolder.syncOnce());
  396. QCOMPARE(nGET, 0);
  397. QVERIFY(fakeFolder.syncOnce());
  398. QCOMPARE(nGET, 0);
  399. }
  400. void testDoNotSyncInvalidFutureMtime()
  401. {
  402. constexpr auto FUTURE_MTIME = 0xFFFFFFFF;
  403. constexpr auto CURRENT_MTIME = 1646057277;
  404. FakeFolder fakeFolder{FileInfo{}};
  405. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  406. const QString fooFileRootFolder("foo");
  407. const QString barFileRootFolder("bar");
  408. const QString fooFileSubFolder("subfolder/foo");
  409. const QString barFileSubFolder("subfolder/bar");
  410. const QString fooFileAaaSubFolder("aaa/subfolder/foo");
  411. const QString barFileAaaSubFolder("aaa/subfolder/bar");
  412. fakeFolder.remoteModifier().insert(fooFileRootFolder);
  413. fakeFolder.remoteModifier().insert(barFileRootFolder);
  414. fakeFolder.remoteModifier().mkdir(QStringLiteral("subfolder"));
  415. fakeFolder.remoteModifier().insert(fooFileSubFolder);
  416. fakeFolder.remoteModifier().insert(barFileSubFolder);
  417. fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa"));
  418. fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa/subfolder"));
  419. fakeFolder.remoteModifier().insert(fooFileAaaSubFolder);
  420. fakeFolder.remoteModifier().insert(barFileAaaSubFolder);
  421. QVERIFY(fakeFolder.syncOnce());
  422. fakeFolder.remoteModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  423. fakeFolder.remoteModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  424. fakeFolder.remoteModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  425. fakeFolder.remoteModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  426. fakeFolder.remoteModifier().setModTime(fooFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  427. fakeFolder.remoteModifier().setModTime(barFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  428. fakeFolder.localModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  429. fakeFolder.localModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  430. fakeFolder.localModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  431. fakeFolder.localModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  432. fakeFolder.localModifier().setModTime(fooFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  433. fakeFolder.localModifier().setModTime(barFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  434. QVERIFY(!fakeFolder.syncOnce());
  435. }
  436. void testInvalidFutureMtimeRecovery()
  437. {
  438. constexpr auto FUTURE_MTIME = 0xFFFFFFFF;
  439. constexpr auto CURRENT_MTIME = 1646057277;
  440. FakeFolder fakeFolder{FileInfo{}};
  441. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  442. const QString fooFileRootFolder("foo");
  443. const QString barFileRootFolder("bar");
  444. const QString fooFileSubFolder("subfolder/foo");
  445. const QString barFileSubFolder("subfolder/bar");
  446. const QString fooFileAaaSubFolder("aaa/subfolder/foo");
  447. const QString barFileAaaSubFolder("aaa/subfolder/bar");
  448. fakeFolder.remoteModifier().insert(fooFileRootFolder);
  449. fakeFolder.remoteModifier().insert(barFileRootFolder);
  450. fakeFolder.remoteModifier().mkdir(QStringLiteral("subfolder"));
  451. fakeFolder.remoteModifier().insert(fooFileSubFolder);
  452. fakeFolder.remoteModifier().insert(barFileSubFolder);
  453. fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa"));
  454. fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa/subfolder"));
  455. fakeFolder.remoteModifier().insert(fooFileAaaSubFolder);
  456. fakeFolder.remoteModifier().insert(barFileAaaSubFolder);
  457. QVERIFY(fakeFolder.syncOnce());
  458. fakeFolder.remoteModifier().setModTimeKeepEtag(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  459. fakeFolder.remoteModifier().setModTimeKeepEtag(barFileRootFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  460. fakeFolder.remoteModifier().setModTimeKeepEtag(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  461. fakeFolder.remoteModifier().setModTimeKeepEtag(barFileSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  462. fakeFolder.remoteModifier().setModTimeKeepEtag(fooFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  463. fakeFolder.remoteModifier().setModTimeKeepEtag(barFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
  464. fakeFolder.localModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  465. fakeFolder.localModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  466. fakeFolder.localModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  467. fakeFolder.localModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  468. fakeFolder.localModifier().setModTime(fooFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  469. fakeFolder.localModifier().setModTime(barFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
  470. QVERIFY(fakeFolder.syncOnce());
  471. QVERIFY(fakeFolder.syncOnce());
  472. auto expectedState = fakeFolder.currentLocalState();
  473. QCOMPARE(fakeFolder.currentRemoteState(), expectedState);
  474. }
  475. void testDiscoverLockChanges()
  476. {
  477. FakeFolder fakeFolder{FileInfo{}};
  478. fakeFolder.syncEngine().account()->setCapabilities({{"activity", QVariantMap{{"apiv2", QVariantList{"filters", "filters-api", "previews", "rich-strings"}}}},
  479. {"bruteforce", QVariantMap{{"delay", 0}}},
  480. {"core", QVariantMap{{"pollinterval", 60}, {"webdav-root", "remote.php/webdav"}}},
  481. {"dav", QVariantMap{{"chunking", "1.0"}}},
  482. {"files", QVariantMap{{"bigfilechunking", true}, {"blacklisted_files", QVariantList{".htaccess"}},
  483. {"comments", true},
  484. {"directEditing", QVariantMap{{"etag", "c748e8fc588b54fc5af38c4481a19d20"}, {"url", "https://nextcloud.local/ocs/v2.php/apps/files/api/v1/directEditing"}}},
  485. {"locking", "1.0"}}}});
  486. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  487. const QString fooFileRootFolder("foo");
  488. const QString barFileRootFolder("bar");
  489. const QString fooFileSubFolder("subfolder/foo");
  490. const QString barFileSubFolder("subfolder/bar");
  491. const QString fooFileAaaSubFolder("aaa/subfolder/foo");
  492. const QString barFileAaaSubFolder("aaa/subfolder/bar");
  493. fakeFolder.remoteModifier().insert(fooFileRootFolder);
  494. fakeFolder.remoteModifier().insert(barFileRootFolder);
  495. fakeFolder.remoteModifier().find("bar")->extraDavProperties = "<nc:lock>1</nc:lock>"
  496. "<nc:lock-owner-type>0</nc:lock-owner-type>"
  497. "<nc:lock-owner>user1</nc:lock-owner>"
  498. "<nc:lock-owner-displayname>user1</nc:lock-owner-displayname>"
  499. "<nc:lock-owner-editor>user1</nc:lock-owner-editor>"
  500. "<nc:lock-time>1648046707</nc:lock-time>";
  501. fakeFolder.remoteModifier().mkdir(QStringLiteral("subfolder"));
  502. fakeFolder.remoteModifier().insert(fooFileSubFolder);
  503. fakeFolder.remoteModifier().insert(barFileSubFolder);
  504. fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa"));
  505. fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa/subfolder"));
  506. fakeFolder.remoteModifier().insert(fooFileAaaSubFolder);
  507. fakeFolder.remoteModifier().insert(barFileAaaSubFolder);
  508. QVERIFY(fakeFolder.syncOnce());
  509. fakeFolder.remoteModifier().find("bar")->extraDavProperties = "<nc:lock>0</nc:lock>";
  510. fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem);
  511. QVERIFY(fakeFolder.syncOnce());
  512. }
  513. };
  514. QTEST_GUILESS_MAIN(TestLocalDiscovery)
  515. #include "testlocaldiscovery.moc"