|
|
|
|
@ -19,6 +19,12 @@
|
|
|
|
|
#include <QDragEnterEvent> |
|
|
|
|
#include <QDropEvent> |
|
|
|
|
#include <QLineEdit> |
|
|
|
|
#include <QAction> |
|
|
|
|
#include <QClipboard> |
|
|
|
|
#include <QDrag> |
|
|
|
|
#include <QMouseEvent> |
|
|
|
|
#include <QScopeGuard> |
|
|
|
|
#include <functional> |
|
|
|
|
extern "C" { |
|
|
|
|
#include "member_files/member_files.h" |
|
|
|
|
#include "mem.h" |
|
|
|
|
@ -29,6 +35,7 @@ static mf_gui_request quotaCaptured;
|
|
|
|
|
static QList<mf_transfer_request> transfers; |
|
|
|
|
static QList<quint64> cancelled; |
|
|
|
|
static bool ready = true; |
|
|
|
|
static std::function<void(mf_transfer_request)> 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<QTreeWidget*>(); |
|
|
|
|
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<QAction*>("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>{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<QDrag*>(); |
|
|
|
|
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); |
|
|
|
|
|