testsyncengine.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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. using namespace OCC;
  11. bool itemDidComplete(const QSignalSpy &spy, const QString &path)
  12. {
  13. for(const QList<QVariant> &args : spy) {
  14. auto item = args[0].value<SyncFileItemPtr>();
  15. if (item->destination() == path)
  16. return true;
  17. }
  18. return false;
  19. }
  20. bool itemDidCompleteSuccessfully(const QSignalSpy &spy, const QString &path)
  21. {
  22. for(const QList<QVariant> &args : spy) {
  23. auto item = args[0].value<SyncFileItemPtr>();
  24. if (item->destination() == path)
  25. return item->_status == SyncFileItem::Success;
  26. }
  27. return false;
  28. }
  29. class TestSyncEngine : public QObject
  30. {
  31. Q_OBJECT
  32. private slots:
  33. void testFileDownload() {
  34. FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
  35. QSignalSpy completeSpy(&fakeFolder.syncEngine(), SIGNAL(itemCompleted(const SyncFileItemPtr &)));
  36. fakeFolder.remoteModifier().insert("A/a0");
  37. fakeFolder.syncOnce();
  38. QVERIFY(itemDidCompleteSuccessfully(completeSpy, "A/a0"));
  39. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  40. }
  41. void testFileUpload() {
  42. FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
  43. QSignalSpy completeSpy(&fakeFolder.syncEngine(), SIGNAL(itemCompleted(const SyncFileItemPtr &)));
  44. fakeFolder.localModifier().insert("A/a0");
  45. fakeFolder.syncOnce();
  46. QVERIFY(itemDidCompleteSuccessfully(completeSpy, "A/a0"));
  47. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  48. }
  49. void testDirDownload() {
  50. FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
  51. QSignalSpy completeSpy(&fakeFolder.syncEngine(), SIGNAL(itemCompleted(const SyncFileItemPtr &)));
  52. fakeFolder.remoteModifier().mkdir("Y");
  53. fakeFolder.remoteModifier().mkdir("Z");
  54. fakeFolder.remoteModifier().insert("Z/d0");
  55. fakeFolder.syncOnce();
  56. QVERIFY(itemDidCompleteSuccessfully(completeSpy, "Y"));
  57. QVERIFY(itemDidCompleteSuccessfully(completeSpy, "Z"));
  58. QVERIFY(itemDidCompleteSuccessfully(completeSpy, "Z/d0"));
  59. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  60. }
  61. void testDirUpload() {
  62. FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
  63. QSignalSpy completeSpy(&fakeFolder.syncEngine(), SIGNAL(itemCompleted(const SyncFileItemPtr &)));
  64. fakeFolder.localModifier().mkdir("Y");
  65. fakeFolder.localModifier().mkdir("Z");
  66. fakeFolder.localModifier().insert("Z/d0");
  67. fakeFolder.syncOnce();
  68. QVERIFY(itemDidCompleteSuccessfully(completeSpy, "Y"));
  69. QVERIFY(itemDidCompleteSuccessfully(completeSpy, "Z"));
  70. QVERIFY(itemDidCompleteSuccessfully(completeSpy, "Z/d0"));
  71. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  72. }
  73. void testLocalDelete() {
  74. FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
  75. QSignalSpy completeSpy(&fakeFolder.syncEngine(), SIGNAL(itemCompleted(const SyncFileItemPtr &)));
  76. fakeFolder.remoteModifier().remove("A/a1");
  77. fakeFolder.syncOnce();
  78. QVERIFY(itemDidCompleteSuccessfully(completeSpy, "A/a1"));
  79. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  80. }
  81. void testRemoteDelete() {
  82. FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
  83. QSignalSpy completeSpy(&fakeFolder.syncEngine(), SIGNAL(itemCompleted(const SyncFileItemPtr &)));
  84. fakeFolder.localModifier().remove("A/a1");
  85. fakeFolder.syncOnce();
  86. QVERIFY(itemDidCompleteSuccessfully(completeSpy, "A/a1"));
  87. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  88. }
  89. void testEmlLocalChecksum() {
  90. FakeFolder fakeFolder{FileInfo{}};
  91. fakeFolder.localModifier().insert("a1.eml", 64, 'A');
  92. fakeFolder.localModifier().insert("a2.eml", 64, 'A');
  93. fakeFolder.localModifier().insert("a3.eml", 64, 'A');
  94. // Upload and calculate the checksums
  95. // fakeFolder.syncOnce();
  96. fakeFolder.syncOnce();
  97. QSignalSpy completeSpy(&fakeFolder.syncEngine(), SIGNAL(itemCompleted(const SyncFileItemPtr &)));
  98. // Touch the file without changing the content, shouldn't upload
  99. fakeFolder.localModifier().setContents("a1.eml", 'A');
  100. // Change the content/size
  101. fakeFolder.localModifier().setContents("a2.eml", 'B');
  102. fakeFolder.localModifier().appendByte("a3.eml");
  103. fakeFolder.syncOnce();
  104. QVERIFY(!itemDidComplete(completeSpy, "a1.eml"));
  105. QVERIFY(itemDidCompleteSuccessfully(completeSpy, "a2.eml"));
  106. QVERIFY(itemDidCompleteSuccessfully(completeSpy, "a3.eml"));
  107. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  108. }
  109. void testRemoteChangeInMovedFolder() {
  110. // issue #5192
  111. FakeFolder fakeFolder{FileInfo{ QString(), {
  112. FileInfo { QStringLiteral("folder"), {
  113. FileInfo{ QStringLiteral("folderA"), { { QStringLiteral("file.txt"), 400 } } },
  114. QStringLiteral("folderB")
  115. }
  116. }}}};
  117. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  118. // Edit a file in a moved directory.
  119. fakeFolder.remoteModifier().setContents("folder/folderA/file.txt", 'a');
  120. fakeFolder.remoteModifier().rename("folder/folderA", "folder/folderB/folderA");
  121. fakeFolder.syncOnce();
  122. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  123. auto oldState = fakeFolder.currentLocalState();
  124. QVERIFY(oldState.find("folder/folderB/folderA/file.txt"));
  125. QVERIFY(!oldState.find("folder/folderA/file.txt"));
  126. // This sync should not remove the file
  127. fakeFolder.syncOnce();
  128. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  129. QCOMPARE(fakeFolder.currentLocalState(), oldState);
  130. }
  131. void testSelectiveSyncModevFolder() {
  132. // issue #5224
  133. FakeFolder fakeFolder{FileInfo{ QString(), {
  134. FileInfo { QStringLiteral("parentFolder"), {
  135. FileInfo{ QStringLiteral("subFolderA"), { { QStringLiteral("fileA.txt"), 400 } } },
  136. FileInfo{ QStringLiteral("subFolderB"), { { QStringLiteral("fileB.txt"), 400 } } }
  137. }
  138. }}}};
  139. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  140. auto expectedServerState = fakeFolder.currentRemoteState();
  141. // Remove subFolderA with selectiveSync:
  142. fakeFolder.syncEngine().journal()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList,
  143. {"parentFolder/subFolderA/"});
  144. fakeFolder.syncEngine().journal()->avoidReadFromDbOnNextSync("parentFolder/subFolderA/");
  145. fakeFolder.syncOnce();
  146. {
  147. // Nothing changed on the server
  148. QCOMPARE(fakeFolder.currentRemoteState(), expectedServerState);
  149. // The local state should not have subFolderA
  150. auto remoteState = fakeFolder.currentRemoteState();
  151. remoteState.remove("parentFolder/subFolderA");
  152. QCOMPARE(fakeFolder.currentLocalState(), remoteState);
  153. }
  154. // Rename parentFolder on the server
  155. fakeFolder.remoteModifier().rename("parentFolder", "parentFolderRenamed");
  156. expectedServerState = fakeFolder.currentRemoteState();
  157. fakeFolder.syncOnce();
  158. {
  159. QCOMPARE(fakeFolder.currentRemoteState(), expectedServerState);
  160. auto remoteState = fakeFolder.currentRemoteState();
  161. // The subFolderA should still be there on the server.
  162. QVERIFY(remoteState.find("parentFolderRenamed/subFolderA/fileA.txt"));
  163. // But not on the client because of the selective sync
  164. remoteState.remove("parentFolderRenamed/subFolderA");
  165. QCOMPARE(fakeFolder.currentLocalState(), remoteState);
  166. }
  167. // Rename it again, locally this time.
  168. fakeFolder.localModifier().rename("parentFolderRenamed", "parentThirdName");
  169. fakeFolder.syncOnce();
  170. {
  171. auto remoteState = fakeFolder.currentRemoteState();
  172. // The subFolderA should still be there on the server.
  173. QVERIFY(remoteState.find("parentThirdName/subFolderA/fileA.txt"));
  174. // But not on the client because of the selective sync
  175. remoteState.remove("parentThirdName/subFolderA");
  176. QCOMPARE(fakeFolder.currentLocalState(), remoteState);
  177. expectedServerState = fakeFolder.currentRemoteState();
  178. QSignalSpy completeSpy(&fakeFolder.syncEngine(), SIGNAL(itemCompleted(const SyncFileItemPtr &)));
  179. fakeFolder.syncOnce(); // This sync should do nothing
  180. QCOMPARE(completeSpy.count(), 0);
  181. QCOMPARE(fakeFolder.currentRemoteState(), expectedServerState);
  182. QCOMPARE(fakeFolder.currentLocalState(), remoteState);
  183. }
  184. }
  185. void abortAfterFailedMkdir() {
  186. FakeFolder fakeFolder{FileInfo{}};
  187. QSignalSpy finishedSpy(&fakeFolder.syncEngine(), SIGNAL(finished(bool)));
  188. fakeFolder.serverErrorPaths().append("NewFolder");
  189. fakeFolder.localModifier().mkdir("NewFolder");
  190. // This should be aborted and would otherwise fail in FileInfo::create.
  191. fakeFolder.localModifier().insert("NewFolder/NewFile");
  192. fakeFolder.syncOnce();
  193. QCOMPARE(finishedSpy.size(), 1);
  194. QCOMPARE(finishedSpy.first().first().toBool(), false);
  195. }
  196. void testDirDownloadWithError() {
  197. FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
  198. QSignalSpy completeSpy(&fakeFolder.syncEngine(), SIGNAL(itemCompleted(const SyncFileItemPtr &)));
  199. fakeFolder.remoteModifier().mkdir("Y");
  200. fakeFolder.remoteModifier().mkdir("Y/Z");
  201. fakeFolder.remoteModifier().insert("Y/Z/d0");
  202. fakeFolder.remoteModifier().insert("Y/Z/d1");
  203. fakeFolder.remoteModifier().insert("Y/Z/d2");
  204. fakeFolder.remoteModifier().insert("Y/Z/d3");
  205. fakeFolder.remoteModifier().insert("Y/Z/d4");
  206. fakeFolder.remoteModifier().insert("Y/Z/d5");
  207. fakeFolder.remoteModifier().insert("Y/Z/d6");
  208. fakeFolder.remoteModifier().insert("Y/Z/d7");
  209. fakeFolder.remoteModifier().insert("Y/Z/d8");
  210. fakeFolder.remoteModifier().insert("Y/Z/d9");
  211. fakeFolder.serverErrorPaths().append("Y/Z/d2", 503); // 503 is a fatal error
  212. fakeFolder.serverErrorPaths().append("Y/Z/d3", 503); // 503 is a fatal error
  213. QVERIFY(!fakeFolder.syncOnce());
  214. QCoreApplication::processEvents(); // should not crash
  215. QSet<QString> seen;
  216. for(const QList<QVariant> &args : completeSpy) {
  217. auto item = args[0].value<SyncFileItemPtr>();
  218. qDebug() << item->_file << item->_isDirectory << item->_status;
  219. QVERIFY(!seen.contains(item->_file)); // signal only sent once per item
  220. seen.insert(item->_file);
  221. if (item->_file == "Y/Z/d2") {
  222. QVERIFY(item->_status == SyncFileItem::FatalError);
  223. } else if(item->_file == "Y/Z/d3") {
  224. QVERIFY(item->_status != SyncFileItem::Success);
  225. }
  226. QVERIFY(item->_file != "Y/Z/d9"); // we should have aborted the sync before d9 starts
  227. }
  228. }
  229. };
  230. QTEST_GUILESS_MAIN(TestSyncEngine)
  231. #include "testsyncengine.moc"