From 96af98f06326edb6d0d593c42f85a7fe6728c95e Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 6 Oct 2026 19:58:36 +0200 Subject: [PATCH] Exercise avatar crop gestures, gallery navigation and EXIF originals --- tools/chatgui/CMakeLists.txt | 3 +- tools/chatgui/tests/test_avatars.cpp | 54 ++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/tools/chatgui/CMakeLists.txt b/tools/chatgui/CMakeLists.txt index 4e30fe4b..5c3d9f99 100644 --- a/tools/chatgui/CMakeLists.txt +++ b/tools/chatgui/CMakeLists.txt @@ -286,7 +286,8 @@ target_link_libraries(test_member_files_browser PRIVATE ${QT_LIBS} ${QT_TEST_LIB add_test(NAME test_member_files_browser COMMAND test_member_files_browser) set_tests_properties(test_member_files_browser PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen" TIMEOUT 30) -add_executable(test_avatars tests/test_avatars.cpp src/avatars/avatarcache.cpp src/avatars/avatarmanager.cpp db/db_manager.cpp) +add_executable(test_avatars tests/test_avatars.cpp src/avatars/avatarcache.cpp src/avatars/avatarmanager.cpp + src/avatars/avatarcrop.cpp src/avatars/avatargallery.cpp src/imageviewer_window.cpp db/db_manager.cpp) target_link_libraries(test_avatars PRIVATE ${QT_LIBS} ${QT_TEST_LIB} utun pthread) add_test(NAME test_avatars COMMAND test_avatars) set_tests_properties(test_avatars PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen" TIMEOUT 60) diff --git a/tools/chatgui/tests/test_avatars.cpp b/tools/chatgui/tests/test_avatars.cpp index 96608520..db3c300c 100644 --- a/tools/chatgui/tests/test_avatars.cpp +++ b/tools/chatgui/tests/test_avatars.cpp @@ -1,5 +1,8 @@ /* Настоящие core/storage, Qt worker и кеш: два канала, публикация после COMMIT и restart. */ #include "../src/avatars/avatarmanager.h" +#include "../src/avatars/avatarcrop.h" +#include "../src/avatars/avatargallery.h" +#include "../src/imageviewer_window.h" #include "../db/db_manager.h" #include "../transport/gui_bridge.h" #include @@ -7,6 +10,8 @@ #include #include #include +#include +#include extern "C" { #include "utun_instance.h" #include "chat/chat_core.h" @@ -99,6 +104,44 @@ private slots: QVERIFY(!AvatarCache::prepare(input,directory.path(),QRectF(0.7,0,0.5,1)).error.isEmpty()); } + void exifRotationKeepsOriginalBytesAndSelectedArea() { + QTemporaryDir directory; QVERIFY(directory.isValid()); + QImage image(800,400,QImage::Format_RGB32); image.fill(Qt::blue); + QPainter painter(&image); painter.fillRect(QRect(0,0,400,400),Qt::red); painter.end(); + QByteArray jpeg; QBuffer buffer(&jpeg); QVERIFY(buffer.open(QIODevice::WriteOnly)); QVERIFY(image.save(&buffer,"JPEG",95)); + const QByteArray exif=QByteArray::fromHex("ffe1002245786966000049492a0008000000010012010300010000000600000000000000"); + const QByteArray original=jpeg.left(2)+exif+jpeg.mid(2); + const QString path=directory.path()+"/rotated.jpg"; QFile file(path); QVERIFY(file.open(QIODevice::WriteOnly)); + QCOMPARE(file.write(original),qint64(original.size())); file.close(); + const auto prepared=AvatarCache::prepare(path,directory.path(),QRectF(0,0,1,0.5)); + QVERIFY2(prepared.error.isEmpty(),qPrintable(prepared.error)); QCOMPARE(prepared.record.size,QSize(400,800)); + const auto loaded=AvatarCache::load(directory.path()+'/'+prepared.hash+".json",prepared.hash); + QVERIFY(loaded.image.pixelColor(128,128).red()>240); + QFile stored(directory.path()+'/'+prepared.record.original); QVERIFY(stored.open(QIODevice::ReadOnly)); QCOMPARE(stored.readAll(),original); + } + + void editorDragSelectsTheEdgeAndCancelDoesNotPrepareFiles() { + QTemporaryDir directory; QVERIFY(directory.isValid()); + QImage image(800,400,QImage::Format_RGB32); image.fill(Qt::blue); + const QString source=directory.path()+"/source.png"; QVERIFY(image.save(source)); + QTimer automate; automate.setInterval(10); + connect(&automate,&QTimer::timeout,this,[] { + auto* dialog=QApplication::activeModalWidget(); if (!dialog) return; + auto* view=dialog->findChild("avatarCropView"); auto* upload=dialog->findChild("avatarCropUpload"); + if (!view || !upload || !upload->isEnabled()) return; + const QPoint center=view->rect().center(); + QTest::mousePress(view,Qt::LeftButton,Qt::NoModifier,center); + QTest::mouseMove(view,center+QPoint(1000,0)); QTest::mouseRelease(view,Qt::LeftButton,Qt::NoModifier,center+QPoint(1000,0)); + QTest::mouseClick(upload,Qt::LeftButton); + }); + automate.start(); QRectF crop; QVERIFY(selectAvatarCrop(source,crop,nullptr)); automate.stop(); + QVERIFY(qAbs(crop.x())<0.001); QVERIFY(qAbs(crop.y())<0.001); + QVERIFY(qAbs(crop.width()-0.5)<0.001); QVERIFY(qAbs(crop.height()-1)<0.001); + QTimer::singleShot(10,[] { if (auto* dialog=qobject_cast(QApplication::activeModalWidget())) dialog->reject(); }); + QVERIFY(!selectAvatarCrop(source,crop,nullptr)); + QCOMPARE(QDir(directory.path()).entryList({"*.json","*.webp"},QDir::Files).size(),0); + } + void uploadReplicateCacheRestartAndRemove() { debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO); debug_set_category_level(DEBUG_CATEGORY_CHAT_SYNC, DEBUG_LEVEL_DEBUG); @@ -208,6 +251,17 @@ private slots: QCOMPARE(manager.galleryImage(selected,true).size(),QSize(300,200)); QVERIFY(manager.galleryImage(selected,true).pixelColor(0,0).red()>240); manager.closeGallery(); + showAvatarGallery(channels[0].channelId,testInstance->node_id); + QDialog* gallery=nullptr; + for (auto* widget : QApplication::topLevelWidgets()) if (widget->windowTitle()=="Avatars") gallery=qobject_cast(widget); + QVERIFY(gallery); auto* viewer=gallery->findChild(); QVERIFY(viewer); + QTRY_VERIFY_WITH_TIMEOUT(viewer->grab().toImage().pixelColor(viewer->rect().center()).red()>240,5000); + for (int i=0;i<5;i++) QVERIFY(gallery->findChild(QString("avatarPreview%1").arg(i))); + QTRY_COMPARE_WITH_TIMEOUT(manager.gallery().size(),2,5000); + auto* next=gallery->findChild(manager.gallery().indexOf(selected)==0 ? "avatarNext" : "avatarPrevious"); + QVERIFY(next); QTest::mouseClick(next,Qt::LeftButton); + QTRY_VERIFY_WITH_TIMEOUT(viewer->grab().toImage().pixelColor(viewer->rect().center()).blue()>240,5000); + gallery->close(); QTRY_VERIFY_WITH_TIMEOUT(manager.gallery().isEmpty(),5000); manager.clearDefault(); for (const auto& channel : channels) { uint8_t wire[CHAT_MEMBER_DISPLAY_SIZE];