Browse Source

Test live message metadata and document notification limits

master
evgeny 4 days ago
parent
commit
16a9094770
  1. 5
      tools/chatgui/CMakeLists.txt
  2. 15
      tools/chatgui/chatgui_doc.md
  3. 75
      tools/chatgui/tests/test_message_events.cpp

5
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)

15
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** — баббл-интерфейс сообщений:

75
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 <QtTest>
#include <cstring>
struct ReceivedMessage {
QString channel;
quint64 author;
bool initialSync;
bool guiThread;
};
static QList<ReceivedMessage> 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<const uint8_t*>(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"
Loading…
Cancel
Save