From 6d8c28f228769879bf0dc184c658b97927a379e1 Mon Sep 17 00:00:00 2001 From: evgeny Date: Sat, 10 Oct 2026 00:22:22 +0200 Subject: [PATCH] Verify delayed file MIME across success failure and cancellation --- tools/chatgui/src/member_files/browser.cpp | 2 +- .../tests/test_member_files_browser.cpp | 56 ++++++++++++++----- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/tools/chatgui/src/member_files/browser.cpp b/tools/chatgui/src/member_files/browser.cpp index 131dc6ac..7335b3dc 100644 --- a/tools/chatgui/src/member_files/browser.cpp +++ b/tools/chatgui/src/member_files/browser.cpp @@ -626,7 +626,7 @@ void MemberFileBrowser::exportFile(bool clipboard) { DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "file browser: drag data ready id=%llu local=%s", (unsigned long long)id, qPrintable(local)); return local; }; - auto* drag = new QDrag(m_entries); drag->setParent(QCoreApplication::instance()); drag->setMimeData(mime); + auto* drag = new QDrag(m_entries); drag->setMimeData(mime); drag->setParent(QCoreApplication::instance()); // MIME живёт до возврата из native drag, даже если окно файлов закрыли во вложенном цикле событий. connect(this, &QObject::destroyed, drag, [] { QDrag::cancel(); }); Qt::DropAction action = drag->exec(Qt::CopyAction, Qt::CopyAction); diff --git a/tools/chatgui/tests/test_member_files_browser.cpp b/tools/chatgui/tests/test_member_files_browser.cpp index 117e4e71..85f860bd 100644 --- a/tools/chatgui/tests/test_member_files_browser.cpp +++ b/tools/chatgui/tests/test_member_files_browser.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include extern "C" { #include "member_files/member_files.h" @@ -77,6 +78,20 @@ static QPushButton* cancelButton(MemberFileBrowser& browser) { return nullptr; } +// Offscreen не имеет native drag. Получатель запрашивает реальный MIME при публикации QDrag приложению. +class DragReceiver : public QObject { +public: + std::function receive; +protected: + bool eventFilter(QObject* object, QEvent* event) override { + if (object == QCoreApplication::instance() && event->type() == QEvent::ChildAdded) { + auto* drag = qobject_cast(static_cast(event)->child()); + if (drag && drag->mimeData()) receive(drag->mimeData()); + } + return false; + } +}; + class BrowserTest : public QObject { Q_OBJECT private slots: @@ -87,7 +102,15 @@ private slots: } void init() { transfers.clear(); cancelled.clear(); ready = true; transferStarted = {}; } + void exportToSystemClipboardAndSingleDrag_data() { + QTest::addColumn("dragStatus"); + QTest::newRow("success") << int(MF_OK); + QTest::newRow("failure") << int(MF_IO_ERROR); + QTest::newRow("cancel") << int(MF_CANCELLED); + } + void exportToSystemClipboardAndSingleDrag() { + QFETCH(int, dragStatus); 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(); }); @@ -127,24 +150,29 @@ private slots: 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); + transferStarted = [&browser, &committed, dragStatus](mf_transfer_request transfer) { + QTimer::singleShot(50, &browser, [&browser, &committed, transfer, dragStatus] { + if (!dragStatus) { + QFile file(QString::fromUtf8(transfer.local)); QVERIFY(file.open(QIODevice::WriteOnly)); + QCOMPARE(file.write("drag bytes"), 10LL); file.close(); + } + committed = true; + if (dragStatus == MF_CANCELLED) cancelButton(browser)->click(); + complete(browser, transfer, dragStatus); }); }; - 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(); + DragReceiver receiver; + receiver.receive = [&](const QMimeData* data) { 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(); + auto urls = data->urls(); QVERIFY(committed); + if (!dragStatus) { + QCOMPARE(urls.size(), 1); QFile file(urls.first().toLocalFile()); + QVERIFY(file.open(QIODevice::ReadOnly)); QCOMPARE(file.readAll(), QByteArray("drag bytes")); + } else QVERIFY(urls.isEmpty()); + received = true; + }; + QCoreApplication::instance()->installEventFilter(&receiver); 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);