From 9640a18b0321d5979aa034b6d52b0a9da5e2d93d Mon Sep 17 00:00:00 2001 From: evgeny Date: Sat, 10 Oct 2026 00:20:27 +0200 Subject: [PATCH] Test file manager copy and deferred drag completion --- .../tests/test_member_files_browser.cpp | 84 ++++++++++++++++++- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/tools/chatgui/tests/test_member_files_browser.cpp b/tools/chatgui/tests/test_member_files_browser.cpp index 3dfac178..117e4e71 100644 --- a/tools/chatgui/tests/test_member_files_browser.cpp +++ b/tools/chatgui/tests/test_member_files_browser.cpp @@ -19,6 +19,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include extern "C" { #include "member_files/member_files.h" #include "mem.h" @@ -29,6 +35,7 @@ static mf_gui_request quotaCaptured; static QList transfers; static QList cancelled; static bool ready = true; +static std::function transferStarted; extern "C" int gui_bridge_is_uasync_ready(void) { return ready; } extern "C" UTUN_INSTANCE* gui_bridge_get_inst(void) { return nullptr; } extern "C" void gui_bridge_post_uasync_fn(void (*function)(void*), void* arg) { @@ -37,7 +44,10 @@ extern "C" void gui_bridge_post_uasync_fn(void (*function)(void*), void* arg) { if (request->message.command == MF_QUOTA) quotaCaptured = *request; else captured = *request; } - if (function == member_files_gui_transfer) transfers.append(*(mf_transfer_request*)arg); + if (function == member_files_gui_transfer) { + auto request = *(mf_transfer_request*)arg; transfers.append(request); + if (transferStarted) transferStarted(request); + } if (function == member_files_gui_cancel) cancelled.append(((mf_cancel_request*)arg)->id); u_free(arg); } @@ -75,7 +85,77 @@ private slots: debug_set_category_level(DEBUG_CATEGORY_DEBUG, DEBUG_LEVEL_DEBUG); debug_set_category_level(DEBUG_CATEGORY_MEDIA, DEBUG_LEVEL_INFO); } - void init() { transfers.clear(); cancelled.clear(); ready = true; } + void init() { transfers.clear(); cancelled.clear(); ready = true; transferStarted = {}; } + + void exportToSystemClipboardAndSingleDrag() { + QTemporaryDir cache; QVERIFY(cache.isValid()); QByteArray oldCache = qgetenv("XDG_CACHE_HOME"); + qputenv("XDG_CACHE_HOME", cache.path().toUtf8()); + auto restoreCache = qScopeGuard([oldCache] { qputenv("XDG_CACHE_HOME", oldCache); QApplication::clipboard()->clear(); }); + sqlite3* sql = nullptr; QCOMPARE(sqlite3_open(":memory:", &sql), SQLITE_OK); + QCOMPARE(sqlite3_exec(sql, "CREATE TABLE accounts(node_id INTEGER,display_name TEXT);" + "INSERT INTO accounts VALUES(11,'Other'),(22,'Me'),(33,'Server');" + "CREATE TABLE peers_42(node_id INTEGER,adm_tags TEXT,adm_tags_sig BLOB,deleted INTEGER,source INTEGER);" + "INSERT INTO peers_42 VALUES(11,'{}',NULL,0,0),(22,'{}',NULL,0,0)," + "(33,'{\"storage\":\"yes\"}',zeroblob(64),0,0);", nullptr, nullptr, nullptr), SQLITE_OK); + DbManager db; db.setDb(sql); db.setMyNodeId(22); QString exported; + { + MemberFileBrowser browser(&db); browser.setChannel("42", 11); browser.show(); + auto* tree = browser.findChild(); + auto* item = new QTreeWidgetItem(tree, {"remote.txt"}); + item->setData(0, Qt::UserRole, "remote.txt"); item->setData(0, Qt::UserRole + 1, MF_FILE); + item->setData(0, Qt::UserRole + 2, QByteArray(16, '\7')); tree->setCurrentItem(item); + auto* copy = browser.findChild("memberFilesCopy"); QVERIFY(copy); QVERIFY(copy->isEnabled()); + QApplication::clipboard()->setText("unchanged"); copy->trigger(); + QTRY_COMPARE(transfers.size(), 1); auto request = transfers.last(); + QCOMPARE(request.upload, 0); QCOMPARE(request.owner, 11ULL); QCOMPARE(request.group, 42ULL); QCOMPARE(request.server, 33ULL); + QCOMPARE(QString::fromUtf8(request.remote), QString("remote.txt")); + QCOMPARE(QByteArray((const char*)request.version, 16), QByteArray(16, '\7')); + QCOMPARE(QApplication::clipboard()->text(), QString("unchanged")); + exported = QString::fromUtf8(request.local); + { QFile file(exported); QVERIFY(file.open(QIODevice::WriteOnly)); QCOMPARE(file.write("downloaded bytes"), 16LL); } + complete(browser, request); + const auto* mime = QApplication::clipboard()->mimeData(); QVERIFY(mime->hasUrls()); + QCOMPARE(mime->urls(), QList{QUrl::fromLocalFile(exported)}); + QVERIFY(mime->data("x-special/gnome-copied-files").startsWith("copy\n")); + copy->trigger(); QCOMPARE(transfers.size(), 1); // Готовая версия не скачивается повторно. + + item->setData(0, Qt::UserRole + 2, QByteArray(16, '\10')); + QApplication::clipboard()->setText("keep on error"); copy->trigger(); QTRY_COMPARE(transfers.size(), 2); + complete(browser, transfers.last(), MF_IO_ERROR); QCOMPARE(QApplication::clipboard()->text(), QString("keep on error")); + copy->trigger(); QTRY_COMPARE(transfers.size(), 3); auto cancelledRequest = transfers.last(); + cancelButton(browser)->click(); complete(browser, cancelledRequest, MF_CANCELLED); + QCOMPARE(QApplication::clipboard()->text(), QString("keep on error")); + + bool committed = false, received = false; + transferStarted = [&browser, &committed](mf_transfer_request transfer) { + QTimer::singleShot(50, &browser, [&browser, &committed, transfer] { + QFile file(QString::fromUtf8(transfer.local)); QVERIFY(file.open(QIODevice::WriteOnly)); + QCOMPARE(file.write("drag bytes"), 10LL); file.close(); committed = true; complete(browser, transfer); + }); + }; + QTimer receiver; receiver.setInterval(5); + connect(&receiver, &QTimer::timeout, &browser, [&] { + auto* drag = QCoreApplication::instance()->findChild(); + if (!drag || !drag->mimeData()) return; + receiver.stop(); auto* data = drag->mimeData(); + QVERIFY(data->hasUrls()); QVERIFY(!committed); + // Запрос системного менеджера приходит до скачивания и возвращается лишь после commit. + auto urls = data->urls(); QVERIFY(committed); QCOMPARE(urls.size(), 1); + QFile file(urls.first().toLocalFile()); QVERIFY(file.open(QIODevice::ReadOnly)); QCOMPARE(file.readAll(), QByteArray("drag bytes")); + received = true; QDrag::cancel(); + }); + receiver.start(); + QTimer::singleShot(3000, &browser, [] { QDrag::cancel(); }); + tree->setFocus(); QCoreApplication::processEvents(); QPoint point = tree->visualItemRect(item).center(); + QTest::mousePress(tree->viewport(), Qt::LeftButton, Qt::NoModifier, point); + QMouseEvent move(QEvent::MouseMove, QPointF(point + QPoint(40, 0)), Qt::NoButton, Qt::LeftButton, Qt::NoModifier); + QApplication::sendEvent(tree->viewport(), &move); + QTest::mouseRelease(tree->viewport(), Qt::LeftButton, Qt::NoModifier, point + QPoint(40, 0)); + QVERIFY(received); QCOMPARE(transfers.size(), 4); transferStarted = {}; + } + QVERIFY(QFile::exists(exported)); // Закрытие Group files не ломает локальный URI. + db.setDb(nullptr); QCOMPARE(sqlite3_close(sql), SQLITE_OK); + } void browserDropsIntoCurrentDirectory() { sqlite3* sql = nullptr; QCOMPARE(sqlite3_open(":memory:", &sql), SQLITE_OK);