From 16a9094770f11433c6e16f533985f15e89ea686f Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 7 Oct 2026 16:01:22 +0200 Subject: [PATCH] Test live message metadata and document notification limits --- tools/chatgui/CMakeLists.txt | 5 ++ tools/chatgui/chatgui_doc.md | 15 +++++ tools/chatgui/tests/test_message_events.cpp | 75 +++++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 tools/chatgui/tests/test_message_events.cpp diff --git a/tools/chatgui/CMakeLists.txt b/tools/chatgui/CMakeLists.txt index f421dd70..a394fe3e 100644 --- a/tools/chatgui/CMakeLists.txt +++ b/tools/chatgui/CMakeLists.txt @@ -318,6 +318,11 @@ else() endif() set_tests_properties(test_message_notification PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen" TIMEOUT 60) +add_executable(test_message_events tests/test_message_events.cpp transport/gui_bridge_impl.cpp) +target_link_libraries(test_message_events PRIVATE ${QT_LIBS} ${QT_TEST_LIB} utun utun_voice pthread ${CMAKE_DL_LIBS}) +add_test(NAME test_message_events COMMAND test_message_events) +set_tests_properties(test_message_events PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen" TIMEOUT 15) + add_executable(test_file_drop tests/test_file_drop.cpp src/inputbar.cpp src/audiorecorder.cpp src/audio_device.cpp src/sound_manager.cpp src/voicemessageencoder.cpp src/emojipanel.cpp src/emojitabbar.cpp src/emoji.cpp src/animtimer.cpp src/lottieicon.cpp src/voice_audio_io.cpp transport/miniaudio_impl.c transport/audio_diagnostics.cpp) diff --git a/tools/chatgui/chatgui_doc.md b/tools/chatgui/chatgui_doc.md index e7489775..bed54f49 100644 --- a/tools/chatgui/chatgui_doc.md +++ b/tools/chatgui/chatgui_doc.md @@ -77,6 +77,21 @@ Системный трей: QSystemTrayIcon + QMenu (показать/скрыть/выход). Закрытие окна сворачивает в трей. +**Уведомления о сообщениях** (`src/message_notification.*`, `src/user_activity_monitor.*`): чужое новое сообщение +в канале или личной беседе показывает одно окно возле трея, если главное окно не активно. Загрузка истории не уведомляет: +`MSG_RECEIVED` содержит `[ch_id_len:1][ch_id:var][author_node_id:8][initial_sync:1]`. +Без активности уведомление остаётся; первый ввод запускает отсчёт 10 секунд, дальнейший ввод его не продлевает. +Новое сообщение заменяет содержимое и отменяет прежний отсчёт. Нажатие открывает соответствующий чат; крестик закрывает окно. + +Windows использует `GetLastInputInfo`, macOS — CoreGraphics, X11 — опциональную библиотеку Xss (`libxss-dev`). +Wayland/XWayland в GNOME использует асинхронный `org.gnome.Mutter.IdleMonitor` через D-Bus. При нативном Qt Wayland +доступен также опциональный KF6/KF5 KIdleTime с установленным backend композитора; idle inhibitors могут ограничивать его события. +X11 idle time в Wayland-сессии не используется: он не гарантирует активность в других приложениях. +Если системный механизм недоступен, учитываются только события внутри ChatGUI; закрытие крестиком остаётся доступным. +При отсутствии координат трея выбирается угол доступной области экрана; нативный Wayland управляет позицией и порядком окон. +Показ/скрытие и причины ограничений логируются в `general`; запуск отсчёта и первая активность — на уровне `debug`. +Проверка: `ctest --test-dir build -R 'test_message_(notification|events)' --output-on-failure`. + **ChatView** (`src/chatview.h`) — QListView с фоновым изображением и drag-to-select. При движении >5px от точки нажатия стартует выделение сообщений. Ctrl+C копирует: для одного — выделенный текст, для нескольких — `[time] author: text`. Сигнал `hoveredIndexChanged` используется для активации анимаций эмодзи в MessageList. **MessageList / MessageDelegate** — баббл-интерфейс сообщений: diff --git a/tools/chatgui/tests/test_message_events.cpp b/tools/chatgui/tests/test_message_events.cpp new file mode 100644 index 00000000..bc62ce6b --- /dev/null +++ b/tools/chatgui/tests/test_message_events.cpp @@ -0,0 +1,75 @@ +/* Формат канального события: live/history сохраняются при передаче в GUI, битые события отбрасываются. */ +#include "../transport/gui_bridge.h" +#include "../../../lib/debug_config.h" +#include +#include + +struct ReceivedMessage { + QString channel; + quint64 author; + bool initialSync; + bool guiThread; +}; +static QList received; + +static void message_received(const char* channel, int length, uint64_t author, int initial_sync) { + received.append({QString::fromUtf8(channel, length), author, initial_sync != 0, QThread::currentThread() == qApp->thread()}); +} + +static QByteArray message_event(quint64 author, bool initialSync) { + const QByteArray channel("12345"); + QByteArray event(1 + channel.size() + 9, '\0'); + event[0] = char(channel.size()); + memcpy(event.data() + 1, channel.constData(), channel.size()); + memcpy(event.data() + 1 + channel.size(), &author, 8); + event[event.size() - 1] = initialSync; + return event; +} + +static void post_message(const QByteArray& event) { + gui_bridge_post(GUI_EVT_MSG_RECEIVED, reinterpret_cast(event.constData()), event.size()); +} + +class MessageEventTest : public QObject { + Q_OBJECT +private slots: + void initTestCase() { + debug_config_init(); + debug_set_level(DEBUG_LEVEL_DEBUG); + gui_bridge_init(); + gui_bridge_set_msg_received_cb(message_received); + } + void cleanupTestCase() { gui_bridge_set_msg_received_cb(nullptr); } + void init() { received.clear(); } + + void liveAndHistory() { + post_message(message_event(42, false)); + post_message(message_event(43, true)); + QTRY_COMPARE(received.size(), 2); + QCOMPARE(received[0].channel, QString("12345")); + QCOMPARE(received[0].author, quint64(42)); + QVERIFY(!received[0].initialSync); + QVERIFY(received[0].guiThread); + QCOMPARE(received[1].author, quint64(43)); + QVERIFY(received[1].initialSync); + QVERIFY(received[1].guiThread); + } + + void rejectsMalformedEvents() { + QByteArray event = message_event(42, false); + event.chop(1); + post_message(event); + event = message_event(42, false); + event[event.size() - 1] = 2; + post_message(event); + event = message_event(42, false); + event[0] = char(255); + post_message(event); + post_message({}); + QTest::qWait(100); + QVERIFY(received.isEmpty()); + } +}; + +QTEST_MAIN(MessageEventTest) +#include "test_message_events.moc"