testlockedfiles.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  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 "lockwatcher.h"
  10. #include <syncengine.h>
  11. #include <localdiscoverytracker.h>
  12. using namespace OCC;
  13. #ifdef Q_OS_WIN
  14. // pass combination of FILE_SHARE_READ, FILE_SHARE_WRITE, FILE_SHARE_DELETE
  15. HANDLE makeHandle(const QString &file, int shareMode)
  16. {
  17. const wchar_t *wuri = reinterpret_cast<const wchar_t *>(file.utf16());
  18. auto handle = CreateFileW(
  19. wuri,
  20. GENERIC_READ | GENERIC_WRITE,
  21. shareMode,
  22. NULL, OPEN_EXISTING,
  23. FILE_ATTRIBUTE_NORMAL,
  24. NULL);
  25. if (handle == INVALID_HANDLE_VALUE) {
  26. qWarning() << GetLastError();
  27. }
  28. return handle;
  29. }
  30. #endif
  31. class TestLockedFiles : public QObject
  32. {
  33. Q_OBJECT
  34. private slots:
  35. void testBasicLockFileWatcher()
  36. {
  37. int count = 0;
  38. QString file;
  39. LockWatcher watcher;
  40. watcher.setCheckInterval(std::chrono::milliseconds(50));
  41. connect(&watcher, &LockWatcher::fileUnlocked, &watcher, [&](const QString &f) { ++count; file = f; });
  42. QString tmpFile;
  43. {
  44. QTemporaryFile tmp;
  45. tmp.setAutoRemove(false);
  46. tmp.open();
  47. tmpFile = tmp.fileName();
  48. }
  49. QVERIFY(QFile::exists(tmpFile));
  50. QVERIFY(!FileSystem::isFileLocked(tmpFile));
  51. watcher.addFile(tmpFile);
  52. QVERIFY(watcher.contains(tmpFile));
  53. QEventLoop loop;
  54. QTimer::singleShot(120, &loop, [&] { loop.exit(); });
  55. loop.exec();
  56. QCOMPARE(count, 1);
  57. QCOMPARE(file, tmpFile);
  58. QVERIFY(!watcher.contains(tmpFile));
  59. #ifdef Q_OS_WIN
  60. auto h = makeHandle(tmpFile, 0);
  61. QVERIFY(FileSystem::isFileLocked(tmpFile));
  62. watcher.addFile(tmpFile);
  63. count = 0;
  64. file.clear();
  65. QThread::msleep(120);
  66. qApp->processEvents();
  67. QCOMPARE(count, 0);
  68. QVERIFY(file.isEmpty());
  69. QVERIFY(watcher.contains(tmpFile));
  70. CloseHandle(h);
  71. QVERIFY(!FileSystem::isFileLocked(tmpFile));
  72. QThread::msleep(120);
  73. qApp->processEvents();
  74. QCOMPARE(count, 1);
  75. QCOMPARE(file, tmpFile);
  76. QVERIFY(!watcher.contains(tmpFile));
  77. #endif
  78. QFile::remove(tmpFile);
  79. }
  80. #ifdef Q_OS_WIN
  81. void testLockedFilePropagation()
  82. {
  83. FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
  84. QStringList seenLockedFiles;
  85. connect(&fakeFolder.syncEngine(), &SyncEngine::seenLockedFile, &fakeFolder.syncEngine(),
  86. [&](const QString &file) { seenLockedFiles.append(file); });
  87. LocalDiscoveryTracker tracker;
  88. connect(&fakeFolder.syncEngine(), &SyncEngine::itemCompleted, &tracker, &LocalDiscoveryTracker::slotItemCompleted);
  89. connect(&fakeFolder.syncEngine(), &SyncEngine::finished, &tracker, &LocalDiscoveryTracker::slotSyncFinished);
  90. auto hasLocalDiscoveryPath = [&](const QString &path) {
  91. auto &paths = tracker.localDiscoveryPaths();
  92. return paths.find(path.toUtf8()) != paths.end();
  93. };
  94. //
  95. // Local change, attempted upload, but file is locked!
  96. //
  97. fakeFolder.localModifier().appendByte("A/a1");
  98. tracker.addTouchedPath("A/a1");
  99. auto h1 = makeHandle(fakeFolder.localPath() + "A/a1", 0);
  100. fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, tracker.localDiscoveryPaths());
  101. tracker.startSyncPartialDiscovery();
  102. QVERIFY(!fakeFolder.syncOnce());
  103. QVERIFY(seenLockedFiles.contains(fakeFolder.localPath() + "A/a1"));
  104. QVERIFY(seenLockedFiles.size() == 1);
  105. QVERIFY(hasLocalDiscoveryPath("A/a1"));
  106. CloseHandle(h1);
  107. fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, tracker.localDiscoveryPaths());
  108. tracker.startSyncPartialDiscovery();
  109. QVERIFY(fakeFolder.syncOnce());
  110. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  111. seenLockedFiles.clear();
  112. QVERIFY(tracker.localDiscoveryPaths().empty());
  113. //
  114. // Remote change, attempted download, but file is locked!
  115. //
  116. fakeFolder.remoteModifier().appendByte("A/a1");
  117. auto h2 = makeHandle(fakeFolder.localPath() + "A/a1", 0);
  118. fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, tracker.localDiscoveryPaths());
  119. tracker.startSyncPartialDiscovery();
  120. QVERIFY(!fakeFolder.syncOnce());
  121. QVERIFY(seenLockedFiles.contains(fakeFolder.localPath() + "A/a1"));
  122. QVERIFY(seenLockedFiles.size() == 1);
  123. CloseHandle(h2);
  124. fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, tracker.localDiscoveryPaths());
  125. tracker.startSyncPartialDiscovery();
  126. QVERIFY(fakeFolder.syncOnce());
  127. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  128. }
  129. #endif
  130. };
  131. QTEST_GUILESS_MAIN(TestLockedFiles)
  132. #include "testlockedfiles.moc"