testlockedfiles.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  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 auto fName = FileSystem::longWinPath(file);
  18. const wchar_t *wuri = reinterpret_cast<const wchar_t *>(fName.utf16());
  19. auto handle = CreateFileW(
  20. wuri,
  21. GENERIC_READ | GENERIC_WRITE,
  22. shareMode,
  23. NULL, OPEN_EXISTING,
  24. FILE_ATTRIBUTE_NORMAL,
  25. NULL);
  26. if (handle == INVALID_HANDLE_VALUE) {
  27. qWarning() << GetLastError();
  28. }
  29. return handle;
  30. }
  31. #endif
  32. class TestLockedFiles : public QObject
  33. {
  34. Q_OBJECT
  35. private slots:
  36. void testBasicLockFileWatcher()
  37. {
  38. QTemporaryDir tmp;
  39. int count = 0;
  40. QString file;
  41. LockWatcher watcher;
  42. watcher.setCheckInterval(std::chrono::milliseconds(50));
  43. connect(&watcher, &LockWatcher::fileUnlocked, &watcher, [&](const QString &f) { ++count; file = f; });
  44. const QString tmpFile = tmp.path() + QString::fromUtf8("/alonglonglonglong/blonglonglonglong/clonglonglonglong/dlonglonglonglong/"
  45. "elonglonglonglong/flonglonglonglong/glonglonglonglong/hlonglonglonglong/ilonglonglonglong/"
  46. "jlonglonglonglong/klonglonglonglong/llonglonglonglong/mlonglonglonglong/nlonglonglonglong/"
  47. "olonglonglonglong/file🐷.txt");
  48. {
  49. // use a long file path to ensure we handle that correctly
  50. QVERIFY(QFileInfo(tmpFile).dir().mkpath("."));
  51. QFile tmp(tmpFile);
  52. QVERIFY(tmp.open(QFile::WriteOnly));
  53. QVERIFY(tmp.write("ownCLoud"));
  54. }
  55. QVERIFY(QFile::exists(tmpFile));
  56. QVERIFY(!FileSystem::isFileLocked(tmpFile));
  57. watcher.addFile(tmpFile);
  58. QVERIFY(watcher.contains(tmpFile));
  59. QEventLoop loop;
  60. QTimer::singleShot(120, &loop, [&] { loop.exit(); });
  61. loop.exec();
  62. QCOMPARE(count, 1);
  63. QCOMPARE(file, tmpFile);
  64. QVERIFY(!watcher.contains(tmpFile));
  65. #ifdef Q_OS_WIN
  66. auto h = makeHandle(tmpFile, 0);
  67. QVERIFY(FileSystem::isFileLocked(tmpFile));
  68. watcher.addFile(tmpFile);
  69. count = 0;
  70. file.clear();
  71. QThread::msleep(120);
  72. qApp->processEvents();
  73. QCOMPARE(count, 0);
  74. QVERIFY(file.isEmpty());
  75. QVERIFY(watcher.contains(tmpFile));
  76. CloseHandle(h);
  77. QVERIFY(!FileSystem::isFileLocked(tmpFile));
  78. QThread::msleep(120);
  79. qApp->processEvents();
  80. QCOMPARE(count, 1);
  81. QCOMPARE(file, tmpFile);
  82. QVERIFY(!watcher.contains(tmpFile));
  83. #endif
  84. QVERIFY(tmp.remove());
  85. }
  86. #ifdef Q_OS_WIN
  87. void testLockedFilePropagation()
  88. {
  89. FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
  90. QStringList seenLockedFiles;
  91. connect(&fakeFolder.syncEngine(), &SyncEngine::seenLockedFile, &fakeFolder.syncEngine(),
  92. [&](const QString &file) { seenLockedFiles.append(file); });
  93. LocalDiscoveryTracker tracker;
  94. connect(&fakeFolder.syncEngine(), &SyncEngine::itemCompleted, &tracker, &LocalDiscoveryTracker::slotItemCompleted);
  95. connect(&fakeFolder.syncEngine(), &SyncEngine::finished, &tracker, &LocalDiscoveryTracker::slotSyncFinished);
  96. auto hasLocalDiscoveryPath = [&](const QString &path) {
  97. auto &paths = tracker.localDiscoveryPaths();
  98. return paths.find(path.toUtf8()) != paths.end();
  99. };
  100. //
  101. // Local change, attempted upload, but file is locked!
  102. //
  103. fakeFolder.localModifier().appendByte("A/a1");
  104. tracker.addTouchedPath("A/a1");
  105. auto h1 = makeHandle(fakeFolder.localPath() + "A/a1", 0);
  106. fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, tracker.localDiscoveryPaths());
  107. tracker.startSyncPartialDiscovery();
  108. QVERIFY(!fakeFolder.syncOnce());
  109. QVERIFY(seenLockedFiles.contains(fakeFolder.localPath() + "A/a1"));
  110. QVERIFY(seenLockedFiles.size() == 1);
  111. QVERIFY(hasLocalDiscoveryPath("A/a1"));
  112. CloseHandle(h1);
  113. fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, tracker.localDiscoveryPaths());
  114. tracker.startSyncPartialDiscovery();
  115. QVERIFY(fakeFolder.syncOnce());
  116. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  117. seenLockedFiles.clear();
  118. QVERIFY(tracker.localDiscoveryPaths().empty());
  119. //
  120. // Remote change, attempted download, but file is locked!
  121. //
  122. fakeFolder.remoteModifier().appendByte("A/a1");
  123. auto h2 = makeHandle(fakeFolder.localPath() + "A/a1", 0);
  124. fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, tracker.localDiscoveryPaths());
  125. tracker.startSyncPartialDiscovery();
  126. QVERIFY(!fakeFolder.syncOnce());
  127. QVERIFY(seenLockedFiles.contains(fakeFolder.localPath() + "A/a1"));
  128. QVERIFY(seenLockedFiles.size() == 1);
  129. CloseHandle(h2);
  130. fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, tracker.localDiscoveryPaths());
  131. tracker.startSyncPartialDiscovery();
  132. QVERIFY(fakeFolder.syncOnce());
  133. QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
  134. }
  135. #endif
  136. };
  137. QTEST_GUILESS_MAIN(TestLockedFiles)
  138. #include "testlockedfiles.moc"