From dc156c7f2b5a468d8317f06535184cd5d06ad74f Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 6 Oct 2026 12:49:26 +0200 Subject: [PATCH] Test bounded downloads and late completion after avatar replacement --- tools/chatgui/tests/test_avatars.cpp | 39 +++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/tools/chatgui/tests/test_avatars.cpp b/tools/chatgui/tests/test_avatars.cpp index 72b95fb5..6848bdc9 100644 --- a/tools/chatgui/tests/test_avatars.cpp +++ b/tools/chatgui/tests/test_avatars.cpp @@ -21,17 +21,31 @@ extern "C" { } static UTUN_INSTANCE* testInstance; static unsigned downloads; +static QSet downloadIds; +static QHash finished; +static bool deferDownload; +static QByteArray delayedDownload; extern "C" int gui_bridge_is_uasync_ready(void) { return testInstance != nullptr; } extern "C" UTUN_INSTANCE* gui_bridge_get_inst(void) { return testInstance; } extern "C" void gui_bridge_post_uasync_fn(void (*fn)(void*), void* arg) { - if (fn == member_files_gui_transfer && !((mf_transfer_request*)arg)->upload) downloads++; + if (fn == member_files_gui_transfer && !((mf_transfer_request*)arg)->upload) { + downloads++; downloadIds.insert(((mf_transfer_request*)arg)->id); + } uasync_call_soon(testInstance->ua, arg, fn); } static void avatarEvent(UTUN_INSTANCE*, int type, const uint8_t* data, int length) { DEBUG_DEBUG(DEBUG_CATEGORY_MEDIA, "avatar test: event type=%d bytes=%d", type, length); auto* manager = AvatarManager::instance(); if (!manager) return; if (type == CHAT_EVT_MEMBER_FILES_RESPONSE) manager->onResponse(data, length); - if (type == CHAT_EVT_MEMBER_FILES_TRANSFER) manager->onTransfer(data, length); + if (type == CHAT_EVT_MEMBER_FILES_TRANSFER) { + if (length == 51 && data[50] == 2) { + finished[mf_get64(data)] = data[48] | (int(data[49]) << 8); + if (deferDownload && downloadIds.contains(mf_get64(data)) && !finished[mf_get64(data)]) { + delayedDownload = QByteArray((const char*)data, length); return; + } + } + manager->onTransfer(data, length); + } if (type == CHAT_EVT_AVATAR_RESULT) manager->onPublished(data, length); if (type == CHAT_EVT_MEMBER_UPDATED && length >= 2 + data[0] + CHAT_MEMBER_DISPLAY_SIZE) manager->onMembers(QString::fromLatin1((const char*)data + 1, data[0]), 1, data + 2 + data[0], CHAT_MEMBER_DISPLAY_SIZE, false); @@ -108,6 +122,15 @@ private slots: QVERIFY(!manager.image("pm:test", testInstance->node_id).isNull()); cached = directory.path() + "/avatar_cache/" + QString::number(testInstance->node_id, 16) + '/' + member.avatar_file; } + auto* limited = (mf_transfer_request*)u_calloc(1, sizeof(mf_transfer_request)); QVERIFY(limited); + limited->inst = testInstance; limited->id = 41; limited->group = channels[0].channelId.toULongLong(); + limited->server = limited->owner = testInstance->node_id; limited->max_bytes = 1; + const QByteArray remote = "avatars/" + QFileInfo(cached).fileName().toLatin1(); + const QString rejected = directory.path() + "/rejected.png"; + const QByteArray local = rejected.toUtf8(); memcpy(limited->remote, remote.constData(), remote.size()); + memcpy(limited->local, local.constData(), local.size()); gui_bridge_post_uasync_fn(member_files_gui_transfer, limited); + QTRY_VERIFY_WITH_TIMEOUT(finished.contains(41), 5000); QCOMPARE(finished.take(41), int(MF_INVALID_RANGE)); + QVERIFY(!QFile::exists(rejected)); chat_core_update_my_name(testInstance, "quote\"\\name"); chat_core_sync_my_addresses(testInstance); for (const auto& channel : channels) { char profile[MS_USERINFO_MAX + 1], name[128]; uint8_t signature[64]; uint64_t timestamp; @@ -132,8 +155,16 @@ private slots: QVERIFY(QFile::remove(cached)); downloads = 0; { AvatarManager manager(&db); manager.initialize(); - QTRY_VERIFY_WITH_TIMEOUT(!manager.image(channels[0].channelId, testInstance->node_id).isNull(), 10000); - QVERIFY(downloads == 1); QVERIFY(QFile::exists(cached)); + deferDownload = true; delayedDownload.clear(); + QTRY_VERIFY_WITH_TIMEOUT((manager.image(channels[0].channelId, testInstance->node_id), !delayedDownload.isEmpty()), 10000); + QCOMPARE(downloads, 1u); + const QString replacement = directory.path() + "/replacement.png"; image.fill(Qt::red); QVERIFY(image.save(replacement)); + manager.uploadDefault(replacement, testInstance->node_id); + for (const auto& channel : channels) + QTRY_VERIFY_WITH_TIMEOUT(manager.image(channel.channelId, testInstance->node_id).pixelColor(128, 128) == QColor(Qt::red), 10000); + deferDownload = false; manager.onTransfer((const uint8_t*)delayedDownload.constData(), delayedDownload.size()); + QTRY_VERIFY_WITH_TIMEOUT(QFile::exists(cached), 5000); + for (const auto& channel : channels) QCOMPARE(manager.image(channel.channelId, testInstance->node_id).pixelColor(128, 128), QColor(Qt::red)); manager.clearDefault(); for (const auto& channel : channels) { uint8_t wire[CHAT_MEMBER_DISPLAY_SIZE];