testallfilesdeleted.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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 <configfile.h>
  11. using namespace OCC;
  12. static void changeAllFileId(FileInfo &info) {
  13. info.fileId = generateFileId();
  14. if (!info.isDir)
  15. return;
  16. info.etag = generateEtag();
  17. for (auto &child : info.children) {
  18. changeAllFileId(child);
  19. }
  20. }
  21. /*
  22. * This test ensure that the SyncEngine::aboutToRemoveAllFiles is correctly called and that when
  23. * we the user choose to remove all files SyncJournalDb::clearFileTable makes works as expected
  24. */
  25. class TestAllFilesDeleted : public QObject
  26. {
  27. Q_OBJECT
  28. private slots:
  29. void testAllFilesDeletedKeep_data()
  30. {
  31. QTest::addColumn<bool>("deleteOnRemote");
  32. QTest::newRow("local") << false;
  33. QTest::newRow("remote") << true;
  34. }
  35. /*
  36. * In this test, all files are deleted in the client, or the server, and we simulate
  37. * that the users press "keep"
  38. */
  39. void testAllFilesDeletedKeep()
  40. {
  41. QFETCH(bool, deleteOnRemote);
  42. FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
  43. ConfigFile config;
  44. config.setPromptDeleteFiles(true);
  45. //Just set a blacklist so we can check it is still there. This directory does not exists but
  46. // that does not matter for our purposes.
  47. QStringList selectiveSyncBlackList = { "Q/" };
  48. fakeFolder.syncEngine().journal()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList,
  49. selectiveSyncBlackList);
  50. auto initialState = fakeFolder.currentLocalState();
  51. int aboutToRemoveAllFilesCalled = 0;
  52. QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveAllFiles,
  53. [&](SyncFileItem::Direction dir, bool *cancel) {
  54. QCOMPARE(aboutToRemoveAllFilesCalled, 0);
  55. aboutToRemoveAllFilesCalled++;
  56. QCOMPARE(dir, deleteOnRemote ? SyncFileItem::Down : SyncFileItem::Up);
  57. *cancel = true;
  58. fakeFolder.syncEngine().journal()->clearFileTable(); // That's what Folder is doing
  59. });
  60. auto &modifier = deleteOnRemote ? fakeFolder.remoteModifier() : fakeFolder.localModifier();
  61. for (const auto &s : fakeFolder.currentRemoteState().children.keys())
  62. modifier.remove(s);
  63. QVERIFY(!fakeFolder.syncOnce()); // Should fail because we cancel the sync
  64. QCOMPARE(aboutToRemoveAllFilesCalled, 1);
  65. // Next sync should recover all files
  66. QVERIFY(fakeFolder.syncOnce());
  67. QCOMPARE(fakeFolder.currentLocalState(), initialState);
  68. QCOMPARE(fakeFolder.currentRemoteState(), initialState);
  69. // The selective sync blacklist should be not have been deleted.
  70. bool ok = true;
  71. QCOMPARE(fakeFolder.syncEngine().journal()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, &ok),
  72. selectiveSyncBlackList);
  73. }
  74. void testAllFilesDeletedDelete_data()
  75. {
  76. testAllFilesDeletedKeep_data();
  77. }
  78. /*
  79. * This test is like the previous one but we simulate that the user presses "delete"
  80. */
  81. void testAllFilesDeletedDelete()
  82. {
  83. QFETCH(bool, deleteOnRemote);
  84. FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
  85. int aboutToRemoveAllFilesCalled = 0;
  86. QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveAllFiles,
  87. [&](SyncFileItem::Direction dir, bool *cancel) {
  88. QCOMPARE(aboutToRemoveAllFilesCalled, 0);
  89. aboutToRemoveAllFilesCalled++;
  90. QCOMPARE(dir, deleteOnRemote ? SyncFileItem::Down : SyncFileItem::Up);
  91. *cancel = false;
  92. });
  93. auto &modifier = deleteOnRemote ? fakeFolder.remoteModifier() : fakeFolder.localModifier();
  94. for (const auto &s : fakeFolder.currentRemoteState().children.keys())
  95. modifier.remove(s);
  96. QVERIFY(fakeFolder.syncOnce()); // Should succeed, and all files must then be deleted
  97. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  98. QCOMPARE(fakeFolder.currentLocalState().children.count(), 0);
  99. // Try another sync to be sure.
  100. QVERIFY(fakeFolder.syncOnce()); // Should succeed (doing nothing)
  101. QCOMPARE(aboutToRemoveAllFilesCalled, 1); // should not have been called.
  102. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  103. QCOMPARE(fakeFolder.currentLocalState().children.count(), 0);
  104. }
  105. void testNotDeleteMetaDataChange() {
  106. /**
  107. * This test make sure that we don't popup a file deleted message if all the metadata have
  108. * been updated (for example when the server is upgraded or something)
  109. **/
  110. FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
  111. // We never remove all files.
  112. QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToRemoveAllFiles,
  113. [&] { QVERIFY(false); });
  114. QVERIFY(fakeFolder.syncOnce());
  115. for (const auto &s : fakeFolder.currentRemoteState().children.keys())
  116. fakeFolder.syncJournal().avoidRenamesOnNextSync(s); // clears all the fileid and inodes.
  117. fakeFolder.localModifier().remove("A/a1");
  118. auto expectedState = fakeFolder.currentLocalState();
  119. QVERIFY(fakeFolder.syncOnce());
  120. QCOMPARE(fakeFolder.currentLocalState(), expectedState);
  121. QCOMPARE(fakeFolder.currentRemoteState(), expectedState);
  122. fakeFolder.remoteModifier().remove("B/b1");
  123. changeAllFileId(fakeFolder.remoteModifier());
  124. expectedState = fakeFolder.currentRemoteState();
  125. QVERIFY(fakeFolder.syncOnce());
  126. QCOMPARE(fakeFolder.currentLocalState(), expectedState);
  127. QCOMPARE(fakeFolder.currentRemoteState(), expectedState);
  128. }
  129. void testDataFingetPrint_data()
  130. {
  131. QTest::addColumn<bool>("hasInitialFingerPrint");
  132. QTest::newRow("initial finger print") << true;
  133. QTest::newRow("no initial finger print") << false;
  134. }
  135. void testDataFingetPrint()
  136. {
  137. QFETCH(bool, hasInitialFingerPrint);
  138. FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
  139. fakeFolder.remoteModifier().setContents("C/c1", 'N');
  140. fakeFolder.remoteModifier().setModTime("C/c1", QDateTime::currentDateTimeUtc().addDays(-2));
  141. fakeFolder.remoteModifier().remove("C/c2");
  142. if (hasInitialFingerPrint) {
  143. fakeFolder.remoteModifier().extraDavProperties = "<oc:data-fingerprint>initial_finger_print</oc:data-fingerprint>";
  144. } else {
  145. //Server support finger print, but none is set.
  146. fakeFolder.remoteModifier().extraDavProperties = "<oc:data-fingerprint></oc:data-fingerprint>";
  147. }
  148. QVERIFY(fakeFolder.syncOnce());
  149. // First sync, we did not change the finger print, so the file should be downloaded as normal
  150. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  151. QCOMPARE(fakeFolder.currentRemoteState().find("C/c1")->contentChar, 'N');
  152. QVERIFY(!fakeFolder.currentRemoteState().find("C/c2"));
  153. /* Simulate a backup restoration */
  154. // A/a1 is an old file
  155. fakeFolder.remoteModifier().setContents("A/a1", 'O');
  156. fakeFolder.remoteModifier().setModTime("A/a1", QDateTime::currentDateTimeUtc().addDays(-2));
  157. // B/b1 did not exist at the time of the backup
  158. fakeFolder.remoteModifier().remove("B/b1");
  159. // B/b2 was uploaded by another user in the mean time.
  160. fakeFolder.remoteModifier().setContents("B/b2", 'N');
  161. fakeFolder.remoteModifier().setModTime("B/b2", QDateTime::currentDateTimeUtc().addDays(2));
  162. // C/c3 was removed since we made the backup
  163. fakeFolder.remoteModifier().insert("C/c3_removed");
  164. // C/c4 was moved to A/a2 since we made the backup
  165. fakeFolder.remoteModifier().rename("A/a2", "C/old_a2_location");
  166. // The admin sets the data-fingerprint property
  167. fakeFolder.remoteModifier().extraDavProperties = "<oc:data-fingerprint>new_finger_print</oc:data-fingerprint>";
  168. QVERIFY(fakeFolder.syncOnce());
  169. auto currentState = fakeFolder.currentLocalState();
  170. // Altough the local file is kept as a conflict, the server file is downloaded
  171. QCOMPARE(currentState.find("A/a1")->contentChar, 'O');
  172. auto conflict = findConflict(currentState, "A/a1");
  173. QVERIFY(conflict);
  174. QCOMPARE(conflict->contentChar, 'W');
  175. fakeFolder.localModifier().remove(conflict->path());
  176. // b1 was restored (re-uploaded)
  177. QVERIFY(currentState.find("B/b1"));
  178. // b2 has the new content (was not restored), since its mode time goes forward in time
  179. QCOMPARE(currentState.find("B/b2")->contentChar, 'N');
  180. conflict = findConflict(currentState, "B/b2");
  181. QVERIFY(conflict); // Just to be sure, we kept the old file in a conflict
  182. QCOMPARE(conflict->contentChar, 'W');
  183. fakeFolder.localModifier().remove(conflict->path());
  184. // We actually do not remove files that technically should have been removed (we don't want data-loss)
  185. QVERIFY(currentState.find("C/c3_removed"));
  186. QVERIFY(currentState.find("C/old_a2_location"));
  187. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  188. }
  189. };
  190. QTEST_GUILESS_MAIN(TestAllFilesDeleted)
  191. #include "testallfilesdeleted.moc"