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 8 #include <QtTest> 9 #include "syncenginetestutils.h" 10 #include "csync_exclude.h" 11 12 using namespace OCC; 13 14 class StatusPushSpy : public QSignalSpy 15 { 16 SyncEngine &_syncEngine; 17 public: StatusPushSpy(SyncEngine & syncEngine)18 StatusPushSpy(SyncEngine &syncEngine) 19 : QSignalSpy(&syncEngine.syncFileStatusTracker(), SIGNAL(fileStatusChanged(const QString&, SyncFileStatus))) 20 , _syncEngine(syncEngine) 21 { } 22 statusOf(const QString & relativePath) const23 SyncFileStatus statusOf(const QString &relativePath) const { 24 QFileInfo file(_syncEngine.localPath(), relativePath); 25 // Start from the end to get the latest status 26 for (int i = size() - 1; i >= 0; --i) { 27 if (QFileInfo(at(i)[0].toString()) == file) 28 return at(i)[1].value<SyncFileStatus>(); 29 } 30 return SyncFileStatus(); 31 } 32 statusEmittedBefore(const QString & firstPath,const QString & secondPath) const33 bool statusEmittedBefore(const QString &firstPath, const QString &secondPath) const { 34 QFileInfo firstFile(_syncEngine.localPath(), firstPath); 35 QFileInfo secondFile(_syncEngine.localPath(), secondPath); 36 // Start from the end to get the latest status 37 int i = size() - 1; 38 for (; i >= 0; --i) { 39 if (QFileInfo(at(i)[0].toString()) == secondFile) 40 break; 41 else if (QFileInfo(at(i)[0].toString()) == firstFile) 42 return false; 43 } 44 for (; i >= 0; --i) { 45 if (QFileInfo(at(i)[0].toString()) == firstFile) 46 return true; 47 } 48 return false; 49 } 50 }; 51 52 class TestSyncFileStatusTracker : public QObject 53 { 54 Q_OBJECT 55 verifyThatPushMatchesPull(const FakeFolder & fakeFolder,const StatusPushSpy & statusSpy)56 void verifyThatPushMatchesPull(const FakeFolder &fakeFolder, const StatusPushSpy &statusSpy) { 57 QString root = fakeFolder.localPath(); 58 QDirIterator it(root, QDir::AllEntries | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); 59 while (it.hasNext()) { 60 QString filePath = it.next().mid(root.size()); 61 SyncFileStatus pushedStatus = statusSpy.statusOf(filePath); 62 if (pushedStatus != SyncFileStatus()) 63 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus(filePath), pushedStatus); 64 } 65 } 66 67 private slots: parentsGetSyncStatusUploadDownload()68 void parentsGetSyncStatusUploadDownload() { 69 FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()}; 70 fakeFolder.localModifier().appendByte("B/b1"); 71 fakeFolder.remoteModifier().appendByte("C/c1"); 72 StatusPushSpy statusSpy(fakeFolder.syncEngine()); 73 74 fakeFolder.scheduleSync(); 75 fakeFolder.execUntilBeforePropagation(); 76 verifyThatPushMatchesPull(fakeFolder, statusSpy); 77 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusSync)); 78 QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusSync)); 79 QCOMPARE(statusSpy.statusOf("B/b1"), SyncFileStatus(SyncFileStatus::StatusSync)); 80 QCOMPARE(statusSpy.statusOf("C"), SyncFileStatus(SyncFileStatus::StatusSync)); 81 QCOMPARE(statusSpy.statusOf("C/c1"), SyncFileStatus(SyncFileStatus::StatusSync)); 82 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("A"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 83 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("A/a1"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 84 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("B/b2"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 85 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("C/c2"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 86 statusSpy.clear(); 87 88 fakeFolder.execUntilFinished(); 89 verifyThatPushMatchesPull(fakeFolder, statusSpy); 90 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 91 QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 92 QCOMPARE(statusSpy.statusOf("B/b1"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 93 QCOMPARE(statusSpy.statusOf("C"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 94 QCOMPARE(statusSpy.statusOf("C/c1"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 95 96 QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); 97 } 98 parentsGetSyncStatusNewFileUploadDownload()99 void parentsGetSyncStatusNewFileUploadDownload() { 100 FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()}; 101 fakeFolder.localModifier().insert("B/b0"); 102 fakeFolder.remoteModifier().insert("C/c0"); 103 StatusPushSpy statusSpy(fakeFolder.syncEngine()); 104 105 fakeFolder.scheduleSync(); 106 fakeFolder.execUntilBeforePropagation(); 107 verifyThatPushMatchesPull(fakeFolder, statusSpy); 108 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusSync)); 109 QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusSync)); 110 QCOMPARE(statusSpy.statusOf("B/b0"), SyncFileStatus(SyncFileStatus::StatusSync)); 111 QCOMPARE(statusSpy.statusOf("C"), SyncFileStatus(SyncFileStatus::StatusSync)); 112 QCOMPARE(statusSpy.statusOf("C/c0"), SyncFileStatus(SyncFileStatus::StatusSync)); 113 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("A"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 114 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("A/a1"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 115 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("B/b1"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 116 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("C/c1"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 117 statusSpy.clear(); 118 119 fakeFolder.execUntilFinished(); 120 verifyThatPushMatchesPull(fakeFolder, statusSpy); 121 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 122 QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 123 QCOMPARE(statusSpy.statusOf("B/b0"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 124 QCOMPARE(statusSpy.statusOf("C"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 125 QCOMPARE(statusSpy.statusOf("C/c0"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 126 127 QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); 128 } 129 parentsGetSyncStatusNewDirDownload()130 void parentsGetSyncStatusNewDirDownload() { 131 FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()}; 132 fakeFolder.remoteModifier().mkdir("D"); 133 fakeFolder.remoteModifier().insert("D/d0"); 134 StatusPushSpy statusSpy(fakeFolder.syncEngine()); 135 136 fakeFolder.scheduleSync(); 137 fakeFolder.execUntilBeforePropagation(); 138 verifyThatPushMatchesPull(fakeFolder, statusSpy); 139 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusSync)); 140 QCOMPARE(statusSpy.statusOf("D"), SyncFileStatus(SyncFileStatus::StatusSync)); 141 QCOMPARE(statusSpy.statusOf("D/d0"), SyncFileStatus(SyncFileStatus::StatusSync)); 142 143 fakeFolder.execUntilItemCompleted("D"); 144 verifyThatPushMatchesPull(fakeFolder, statusSpy); 145 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusSync)); 146 QCOMPARE(statusSpy.statusOf("D"), SyncFileStatus(SyncFileStatus::StatusSync)); 147 QCOMPARE(statusSpy.statusOf("D/d0"), SyncFileStatus(SyncFileStatus::StatusSync)); 148 149 fakeFolder.execUntilFinished(); 150 verifyThatPushMatchesPull(fakeFolder, statusSpy); 151 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 152 QCOMPARE(statusSpy.statusOf("D"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 153 QCOMPARE(statusSpy.statusOf("D/d0"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 154 155 QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); 156 } 157 parentsGetSyncStatusNewDirUpload()158 void parentsGetSyncStatusNewDirUpload() { 159 FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()}; 160 fakeFolder.localModifier().mkdir("D"); 161 fakeFolder.localModifier().insert("D/d0"); 162 StatusPushSpy statusSpy(fakeFolder.syncEngine()); 163 164 fakeFolder.scheduleSync(); 165 fakeFolder.execUntilBeforePropagation(); 166 verifyThatPushMatchesPull(fakeFolder, statusSpy); 167 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusSync)); 168 QCOMPARE(statusSpy.statusOf("D"), SyncFileStatus(SyncFileStatus::StatusSync)); 169 QCOMPARE(statusSpy.statusOf("D/d0"), SyncFileStatus(SyncFileStatus::StatusSync)); 170 171 fakeFolder.execUntilItemCompleted("D"); 172 verifyThatPushMatchesPull(fakeFolder, statusSpy); 173 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusSync)); 174 QCOMPARE(statusSpy.statusOf("D"), SyncFileStatus(SyncFileStatus::StatusSync)); 175 QCOMPARE(statusSpy.statusOf("D/d0"), SyncFileStatus(SyncFileStatus::StatusSync)); 176 177 fakeFolder.execUntilFinished(); 178 verifyThatPushMatchesPull(fakeFolder, statusSpy); 179 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 180 QCOMPARE(statusSpy.statusOf("D"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 181 QCOMPARE(statusSpy.statusOf("D/d0"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 182 183 QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); 184 } 185 parentsGetSyncStatusDeleteUpDown()186 void parentsGetSyncStatusDeleteUpDown() { 187 FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()}; 188 fakeFolder.remoteModifier().remove("B/b1"); 189 fakeFolder.localModifier().remove("C/c1"); 190 StatusPushSpy statusSpy(fakeFolder.syncEngine()); 191 192 fakeFolder.scheduleSync(); 193 fakeFolder.execUntilBeforePropagation(); 194 verifyThatPushMatchesPull(fakeFolder, statusSpy); 195 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusSync)); 196 QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusSync)); 197 // Discovered as remotely removed, pending for local removal. 198 QCOMPARE(statusSpy.statusOf("B/b1"), SyncFileStatus(SyncFileStatus::StatusSync)); 199 QCOMPARE(statusSpy.statusOf("C"), SyncFileStatus(SyncFileStatus::StatusSync)); 200 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("A"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 201 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("B/b2"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 202 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("C/c2"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 203 statusSpy.clear(); 204 205 fakeFolder.execUntilFinished(); 206 verifyThatPushMatchesPull(fakeFolder, statusSpy); 207 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 208 QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 209 QCOMPARE(statusSpy.statusOf("C"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 210 211 QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); 212 } 213 warningStatusForExcludedFile()214 void warningStatusForExcludedFile() { 215 FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()}; 216 fakeFolder.syncEngine().excludedFiles().addManualExclude("A/a1"); 217 fakeFolder.syncEngine().excludedFiles().addManualExclude("B"); 218 fakeFolder.localModifier().appendByte("A/a1"); 219 fakeFolder.localModifier().appendByte("B/b1"); 220 StatusPushSpy statusSpy(fakeFolder.syncEngine()); 221 222 fakeFolder.scheduleSync(); 223 fakeFolder.execUntilBeforePropagation(); 224 verifyThatPushMatchesPull(fakeFolder, statusSpy); 225 QCOMPARE(statusSpy.statusOf("A/a1"), SyncFileStatus(SyncFileStatus::StatusExcluded)); 226 QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusExcluded)); 227 QEXPECT_FAIL("", "csync will stop at ignored directories without traversing children, so we don't currently push the status for newly ignored children of an ignored directory.", Continue); 228 QCOMPARE(statusSpy.statusOf("B/b1"), SyncFileStatus(SyncFileStatus::StatusExcluded)); 229 230 fakeFolder.execUntilFinished(); 231 verifyThatPushMatchesPull(fakeFolder, statusSpy); 232 QCOMPARE(statusSpy.statusOf("A/a1"), SyncFileStatus(SyncFileStatus::StatusExcluded)); 233 QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusExcluded)); 234 QEXPECT_FAIL("", "csync will stop at ignored directories without traversing children, so we don't currently push the status for newly ignored children of an ignored directory.", Continue); 235 QCOMPARE(statusSpy.statusOf("B/b1"), SyncFileStatus(SyncFileStatus::StatusExcluded)); 236 QEXPECT_FAIL("", "csync will stop at ignored directories without traversing children, so we don't currently push the status for newly ignored children of an ignored directory.", Continue); 237 QCOMPARE(statusSpy.statusOf("B/b2"), SyncFileStatus(SyncFileStatus::StatusExcluded)); 238 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus(""), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 239 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("A"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 240 statusSpy.clear(); 241 242 // Clears the exclude expr above 243 fakeFolder.syncEngine().excludedFiles().clearManualExcludes(); 244 fakeFolder.scheduleSync(); 245 fakeFolder.execUntilBeforePropagation(); 246 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusSync)); 247 QCOMPARE(statusSpy.statusOf("A"), SyncFileStatus(SyncFileStatus::StatusSync)); 248 QCOMPARE(statusSpy.statusOf("A/a1"), SyncFileStatus(SyncFileStatus::StatusSync)); 249 QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusSync)); 250 QCOMPARE(statusSpy.statusOf("B/b1"), SyncFileStatus(SyncFileStatus::StatusSync)); 251 statusSpy.clear(); 252 253 fakeFolder.execUntilFinished(); 254 verifyThatPushMatchesPull(fakeFolder, statusSpy); 255 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 256 QCOMPARE(statusSpy.statusOf("A"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 257 QCOMPARE(statusSpy.statusOf("A/a1"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 258 QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 259 QCOMPARE(statusSpy.statusOf("B/b1"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 260 261 QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); 262 } 263 warningStatusForExcludedFile_CasePreserving()264 void warningStatusForExcludedFile_CasePreserving() { 265 FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()}; 266 fakeFolder.syncEngine().excludedFiles().addManualExclude("B"); 267 fakeFolder.serverErrorPaths().append("A/a1"); 268 fakeFolder.localModifier().appendByte("A/a1"); 269 270 fakeFolder.syncOnce(); 271 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus(""), SyncFileStatus(SyncFileStatus::StatusWarning)); 272 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("A"), SyncFileStatus(SyncFileStatus::StatusWarning)); 273 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("A/a1"), SyncFileStatus(SyncFileStatus::StatusError)); 274 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("B"), SyncFileStatus(SyncFileStatus::StatusExcluded)); 275 276 // Should still get the status for different casing on macOS and Windows. 277 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("a"), SyncFileStatus(Utility::fsCasePreserving() ? SyncFileStatus::StatusWarning : SyncFileStatus::StatusNone)); 278 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("A/A1"), SyncFileStatus(Utility::fsCasePreserving() ? SyncFileStatus::StatusError : SyncFileStatus::StatusNone)); 279 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("b"), SyncFileStatus(Utility::fsCasePreserving() ? SyncFileStatus::StatusExcluded : SyncFileStatus::StatusNone)); 280 } 281 parentsGetWarningStatusForError()282 void parentsGetWarningStatusForError() { 283 FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()}; 284 fakeFolder.serverErrorPaths().append("A/a1"); 285 fakeFolder.serverErrorPaths().append("B/b0"); 286 fakeFolder.localModifier().appendByte("A/a1"); 287 fakeFolder.localModifier().insert("B/b0"); 288 StatusPushSpy statusSpy(fakeFolder.syncEngine()); 289 290 fakeFolder.scheduleSync(); 291 fakeFolder.execUntilBeforePropagation(); 292 verifyThatPushMatchesPull(fakeFolder, statusSpy); 293 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusSync)); 294 QCOMPARE(statusSpy.statusOf("A"), SyncFileStatus(SyncFileStatus::StatusSync)); 295 QCOMPARE(statusSpy.statusOf("A/a1"), SyncFileStatus(SyncFileStatus::StatusSync)); 296 QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusSync)); 297 QCOMPARE(statusSpy.statusOf("B/b0"), SyncFileStatus(SyncFileStatus::StatusSync)); 298 statusSpy.clear(); 299 300 fakeFolder.execUntilFinished(); 301 verifyThatPushMatchesPull(fakeFolder, statusSpy); 302 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusWarning)); 303 QCOMPARE(statusSpy.statusOf("A"), SyncFileStatus(SyncFileStatus::StatusWarning)); 304 QCOMPARE(statusSpy.statusOf("A/a1"), SyncFileStatus(SyncFileStatus::StatusError)); 305 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("A/a2"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 306 QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusWarning)); 307 QCOMPARE(statusSpy.statusOf("B/b0"), SyncFileStatus(SyncFileStatus::StatusError)); 308 statusSpy.clear(); 309 310 // Remove the error and start a second sync, the blacklist should kick in 311 fakeFolder.serverErrorPaths().clear(); 312 fakeFolder.scheduleSync(); 313 fakeFolder.execUntilBeforePropagation(); 314 verifyThatPushMatchesPull(fakeFolder, statusSpy); 315 // A/a1 and B/b0 should be on the black list for the next few seconds 316 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusWarning)); 317 QCOMPARE(statusSpy.statusOf("A"), SyncFileStatus(SyncFileStatus::StatusWarning)); 318 QCOMPARE(statusSpy.statusOf("A/a1"), SyncFileStatus(SyncFileStatus::StatusError)); 319 QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusWarning)); 320 QCOMPARE(statusSpy.statusOf("B/b0"), SyncFileStatus(SyncFileStatus::StatusError)); 321 statusSpy.clear(); 322 fakeFolder.execUntilFinished(); 323 verifyThatPushMatchesPull(fakeFolder, statusSpy); 324 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusWarning)); 325 QCOMPARE(statusSpy.statusOf("A"), SyncFileStatus(SyncFileStatus::StatusWarning)); 326 QCOMPARE(statusSpy.statusOf("A/a1"), SyncFileStatus(SyncFileStatus::StatusError)); 327 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("A/a2"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 328 QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusWarning)); 329 QCOMPARE(statusSpy.statusOf("B/b0"), SyncFileStatus(SyncFileStatus::StatusError)); 330 statusSpy.clear(); 331 332 // Start a third sync, this time together with a real file to sync 333 fakeFolder.localModifier().appendByte("C/c1"); 334 fakeFolder.scheduleSync(); 335 fakeFolder.execUntilBeforePropagation(); 336 verifyThatPushMatchesPull(fakeFolder, statusSpy); 337 // The root should show SYNC even though there is an error underneath, 338 // since C/c1 is syncing and the SYNC status has priority. 339 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusSync)); 340 QCOMPARE(statusSpy.statusOf("A"), SyncFileStatus(SyncFileStatus::StatusWarning)); 341 QCOMPARE(statusSpy.statusOf("A/a1"), SyncFileStatus(SyncFileStatus::StatusError)); 342 QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusWarning)); 343 QCOMPARE(statusSpy.statusOf("B/b0"), SyncFileStatus(SyncFileStatus::StatusError)); 344 QCOMPARE(statusSpy.statusOf("C"), SyncFileStatus(SyncFileStatus::StatusSync)); 345 QCOMPARE(statusSpy.statusOf("C/c1"), SyncFileStatus(SyncFileStatus::StatusSync)); 346 statusSpy.clear(); 347 fakeFolder.execUntilFinished(); 348 verifyThatPushMatchesPull(fakeFolder, statusSpy); 349 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusWarning)); 350 QCOMPARE(statusSpy.statusOf("A"), SyncFileStatus(SyncFileStatus::StatusWarning)); 351 QCOMPARE(statusSpy.statusOf("A/a1"), SyncFileStatus(SyncFileStatus::StatusError)); 352 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("A/a2"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 353 QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusWarning)); 354 QCOMPARE(statusSpy.statusOf("B/b0"), SyncFileStatus(SyncFileStatus::StatusError)); 355 QCOMPARE(statusSpy.statusOf("C"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 356 QCOMPARE(statusSpy.statusOf("C/c1"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 357 statusSpy.clear(); 358 359 // Another sync after clearing the blacklist entry, everything should return to order. 360 fakeFolder.syncEngine().journal()->wipeErrorBlacklistEntry("A/a1"); 361 fakeFolder.syncEngine().journal()->wipeErrorBlacklistEntry("B/b0"); 362 fakeFolder.scheduleSync(); 363 fakeFolder.execUntilBeforePropagation(); 364 verifyThatPushMatchesPull(fakeFolder, statusSpy); 365 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusSync)); 366 QCOMPARE(statusSpy.statusOf("A"), SyncFileStatus(SyncFileStatus::StatusSync)); 367 QCOMPARE(statusSpy.statusOf("A/a1"), SyncFileStatus(SyncFileStatus::StatusSync)); 368 QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusSync)); 369 QCOMPARE(statusSpy.statusOf("B/b0"), SyncFileStatus(SyncFileStatus::StatusSync)); 370 statusSpy.clear(); 371 fakeFolder.execUntilFinished(); 372 verifyThatPushMatchesPull(fakeFolder, statusSpy); 373 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 374 QCOMPARE(statusSpy.statusOf("A"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 375 QCOMPARE(statusSpy.statusOf("A/a1"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 376 QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 377 QCOMPARE(statusSpy.statusOf("B/b0"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 378 379 QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); 380 } 381 parentsGetWarningStatusForError_SibblingStartsWithPath()382 void parentsGetWarningStatusForError_SibblingStartsWithPath() { 383 // A is a parent of A/a1, but A/a is not even if it's a substring of A/a1 384 FakeFolder fakeFolder{{QString{},{ 385 {QStringLiteral("A"), { 386 {QStringLiteral("a"), 4}, 387 {QStringLiteral("a1"), 4} 388 }}}}}; 389 fakeFolder.serverErrorPaths().append("A/a1"); 390 fakeFolder.localModifier().appendByte("A/a1"); 391 392 fakeFolder.scheduleSync(); 393 fakeFolder.execUntilBeforePropagation(); 394 // The SyncFileStatusTraker won't push any status for all of them, test with a pull. 395 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus(""), SyncFileStatus(SyncFileStatus::StatusSync)); 396 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("A"), SyncFileStatus(SyncFileStatus::StatusSync)); 397 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("A/a1"), SyncFileStatus(SyncFileStatus::StatusSync)); 398 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("A/a"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 399 400 fakeFolder.execUntilFinished(); 401 // We use string matching for paths in the implementation, 402 // an error should affect only parents and not every path that starts with the problem path. 403 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus(""), SyncFileStatus(SyncFileStatus::StatusWarning)); 404 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("A"), SyncFileStatus(SyncFileStatus::StatusWarning)); 405 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("A/a1"), SyncFileStatus(SyncFileStatus::StatusError)); 406 QCOMPARE(fakeFolder.syncEngine().syncFileStatusTracker().fileStatus("A/a"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 407 } 408 409 // Even for status pushes immediately following each other, macOS 410 // can sometimes have 1s delays between updates, so make sure that 411 // children are marked as OK before their parents do. childOKEmittedBeforeParent()412 void childOKEmittedBeforeParent() { 413 FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()}; 414 fakeFolder.localModifier().appendByte("B/b1"); 415 fakeFolder.remoteModifier().appendByte("C/c1"); 416 StatusPushSpy statusSpy(fakeFolder.syncEngine()); 417 418 fakeFolder.syncOnce(); 419 verifyThatPushMatchesPull(fakeFolder, statusSpy); 420 QVERIFY(statusSpy.statusEmittedBefore("B/b1", "B")); 421 QVERIFY(statusSpy.statusEmittedBefore("C/c1", "C")); 422 QVERIFY(statusSpy.statusEmittedBefore("B", "")); 423 QVERIFY(statusSpy.statusEmittedBefore("C", "")); 424 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 425 QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 426 QCOMPARE(statusSpy.statusOf("B/b1"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 427 QCOMPARE(statusSpy.statusOf("C"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 428 QCOMPARE(statusSpy.statusOf("C/c1"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 429 } 430 sharedStatus()431 void sharedStatus() { 432 SyncFileStatus sharedUpToDateStatus(SyncFileStatus::StatusUpToDate); 433 sharedUpToDateStatus.setShared(true); 434 435 FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()}; 436 fakeFolder.remoteModifier().insert("S/s0"); 437 fakeFolder.remoteModifier().appendByte("S/s1"); 438 fakeFolder.remoteModifier().insert("B/b3"); 439 fakeFolder.remoteModifier().find("B/b3")->extraDavProperties = "<oc:share-types><oc:share-type>0</oc:share-type></oc:share-types>"; 440 fakeFolder.remoteModifier().find("A/a1")->isShared = true; // becomes shared 441 fakeFolder.remoteModifier().find("A", true); // change the etags of the parent 442 443 StatusPushSpy statusSpy(fakeFolder.syncEngine()); 444 445 fakeFolder.scheduleSync(); 446 fakeFolder.execUntilBeforePropagation(); 447 verifyThatPushMatchesPull(fakeFolder, statusSpy); 448 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusSync)); 449 // We don't care about the shared flag for the sync status, 450 // Mac and Windows won't show it and we can't know it for new files. 451 QCOMPARE(statusSpy.statusOf("S").tag(), SyncFileStatus::StatusSync); 452 QCOMPARE(statusSpy.statusOf("S/s0").tag(), SyncFileStatus::StatusSync); 453 QCOMPARE(statusSpy.statusOf("S/s1").tag(), SyncFileStatus::StatusSync); 454 455 fakeFolder.execUntilFinished(); 456 verifyThatPushMatchesPull(fakeFolder, statusSpy); 457 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 458 QCOMPARE(statusSpy.statusOf("S"), sharedUpToDateStatus); 459 QEXPECT_FAIL("", "We currently only know if a new file is shared on the second sync, after a PROPFIND.", Continue); 460 QCOMPARE(statusSpy.statusOf("S/s0"), sharedUpToDateStatus); 461 QCOMPARE(statusSpy.statusOf("S/s1"), sharedUpToDateStatus); 462 QCOMPARE(statusSpy.statusOf("B/b1").shared(), false); 463 QCOMPARE(statusSpy.statusOf("B/b3"), sharedUpToDateStatus); 464 QCOMPARE(statusSpy.statusOf("A/a1"), sharedUpToDateStatus); 465 466 QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); 467 } 468 renameError()469 void renameError() { 470 FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()}; 471 fakeFolder.serverErrorPaths().append("A/a1"); 472 fakeFolder.localModifier().rename("A/a1", "A/a1m"); 473 fakeFolder.localModifier().rename("B/b1", "B/b1m"); 474 StatusPushSpy statusSpy(fakeFolder.syncEngine()); 475 476 fakeFolder.scheduleSync(); 477 fakeFolder.execUntilBeforePropagation(); 478 479 verifyThatPushMatchesPull(fakeFolder, statusSpy); 480 481 QCOMPARE(statusSpy.statusOf("A/a1m"), SyncFileStatus(SyncFileStatus::StatusSync)); 482 QCOMPARE(statusSpy.statusOf("A/a1"), statusSpy.statusOf("A/a1notexist")); 483 QCOMPARE(statusSpy.statusOf("A"), SyncFileStatus(SyncFileStatus::StatusSync)); 484 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusSync)); 485 QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusSync)); 486 QCOMPARE(statusSpy.statusOf("B/b1m"), SyncFileStatus(SyncFileStatus::StatusSync)); 487 488 fakeFolder.execUntilFinished(); 489 verifyThatPushMatchesPull(fakeFolder, statusSpy); 490 QCOMPARE(statusSpy.statusOf("A/a1m"), SyncFileStatus(SyncFileStatus::StatusError)); 491 QCOMPARE(statusSpy.statusOf("A/a1"), statusSpy.statusOf("A/a1notexist")); 492 QCOMPARE(statusSpy.statusOf("A"), SyncFileStatus(SyncFileStatus::StatusWarning)); 493 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusWarning)); 494 QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 495 QCOMPARE(statusSpy.statusOf("B/b1m"), SyncFileStatus(SyncFileStatus::StatusUpToDate)); 496 statusSpy.clear(); 497 498 QVERIFY(!fakeFolder.syncOnce()); 499 verifyThatPushMatchesPull(fakeFolder, statusSpy); 500 statusSpy.clear(); 501 QVERIFY(!fakeFolder.syncOnce()); 502 verifyThatPushMatchesPull(fakeFolder, statusSpy); 503 QCOMPARE(statusSpy.statusOf("A/a1m"), SyncFileStatus(SyncFileStatus::StatusError)); 504 QCOMPARE(statusSpy.statusOf("A/a1"), statusSpy.statusOf("A/a1notexist")); 505 QCOMPARE(statusSpy.statusOf("A"), SyncFileStatus(SyncFileStatus::StatusWarning)); 506 QCOMPARE(statusSpy.statusOf(""), SyncFileStatus(SyncFileStatus::StatusWarning)); 507 QCOMPARE(statusSpy.statusOf("B"), SyncFileStatus(SyncFileStatus::StatusNone)); 508 QCOMPARE(statusSpy.statusOf("B/b1m"), SyncFileStatus(SyncFileStatus::StatusNone)); 509 statusSpy.clear(); 510 } 511 512 }; 513 514 QTEST_GUILESS_MAIN(TestSyncFileStatusTracker) 515 #include "testsyncfilestatustracker.moc" 516