/* Проверяем время жизни уведомления и первую активность без ввода в настоящую desktop-сессию. */ #include "../src/message_notification.h" #include "../src/user_activity_monitor.h" #include "../../../lib/debug_config.h" #include #include #include #include #include #ifdef UTUN_TEST_ACTIVITY_DBUS #include #include #include #endif class FakeSystemActivity : public UserActivityMonitor { public: FakeSystemActivity() { clock.start(); } QElapsedTimer clock; qint64 lastInput = -5000; bool available = true; protected: qint64 systemIdleTime() override { return available ? clock.elapsed() - lastInput : -1; } }; #ifdef UTUN_TEST_ACTIVITY_DBUS class FakeGnomeIdle : public QObject, protected QDBusContext { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.gnome.Mutter.IdleMonitor") public: FakeGnomeIdle() { clock.start(); } QElapsedTimer clock; qint64 lastInput = -5000; int delayMs = 0; int requests = 0; public slots: qulonglong GetIdletime() { ++requests; const qulonglong idle = clock.elapsed() - lastInput; if (delayMs) { const QDBusMessage reply = message().createReply(QVariant::fromValue(idle)); setDelayedReply(true); QTimer::singleShot(delayMs, this, [reply] { QDBusConnection::sessionBus().send(reply); }); } return idle; } }; #endif class MessageNotificationTest : public QObject { Q_OBJECT private slots: void initTestCase() { debug_config_init(); debug_set_level(DEBUG_LEVEL_DEBUG); qunsetenv("XDG_SESSION_TYPE"); } void firstSystemActivityAndCancellation() { FakeSystemActivity activity; QSignalSpy spy(&activity, &UserActivityMonitor::activity); activity.start(); QTest::qWait(600); QCOMPARE(spy.size(), 0); // Старый ввод до начала наблюдения не считается активностью. activity.lastInput = activity.clock.elapsed(); QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 700); QTest::qWait(300); QCOMPARE(spy.size(), 1); activity.start(); activity.stop(); activity.lastInput = activity.clock.elapsed(); QTest::qWait(400); QCOMPARE(spy.size(), 1); activity.start(); QTest::qWait(20); activity.lastInput = activity.clock.elapsed(); QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 2, 700); } void localFallbackDoesNotConsumeInput() { FakeSystemActivity activity; activity.available = false; QSignalSpy spy(&activity, &UserActivityMonitor::activity); QLineEdit input; input.show(); activity.start(); QTest::keyClick(&input, Qt::Key_A); QTest::keyClick(&input, Qt::Key_B); QCOMPARE(input.text(), QString("ab")); QCOMPARE(spy.size(), 1); } void waitsForActivityThenExpiresWithoutExtension() { QWidget hiddenParent; QLineEdit otherWindow; otherWindow.show(); QApplication::setActiveWindow(&otherWindow); MessageNotification notification(&hiddenParent); notification.showMessage("42", "Alice", "Test channel", {}); QVERIFY(notification.isVisible()); QCOMPARE(QApplication::activeWindow(), &otherWindow); QTest::qWait(10200); QVERIFY(notification.isVisible()); // Без активности автоматического таймера нет. QElapsedTimer elapsed; elapsed.start(); QTest::keyClick(&otherWindow, Qt::Key_A); QTest::qWait(5500); QVERIFY(notification.isVisible()); QTest::keyClick(&otherWindow, Qt::Key_B); QTRY_VERIFY_WITH_TIMEOUT(!notification.isVisible(), 6000); QVERIFY(elapsed.elapsed() >= 10000); QVERIFY(elapsed.elapsed() < 12000); QCOMPARE(otherWindow.text(), QString("ab")); } void replacementCancelsOldCountdownAndOpensLatestChat() { QLineEdit otherWindow; otherWindow.show(); MessageNotification notification; QSignalSpy activated(¬ification, &MessageNotification::activated); notification.showMessage("42", "Alice", "First channel", {}); QTest::keyClick(&otherWindow, Qt::Key_A); QTest::qWait(100); notification.showMessage("dm:7", "Bob", "Direct messages", QRect(-10, -10, 20, 20)); QTest::qWait(10200); QVERIFY(notification.isVisible()); // Прежний таймер не закрывает новое уведомление. const QRect area = notification.screen()->availableGeometry(); QVERIFY(area.contains(notification.geometry())); QTest::mouseClick(¬ification, Qt::LeftButton, Qt::NoModifier, QPoint(30, notification.height() - 15)); QVERIFY(!notification.isVisible()); QCOMPARE(activated.size(), 1); QCOMPARE(activated[0][0].toString(), QString("dm:7")); notification.showMessage("43", "Charlie", "Third channel", {}); auto* closeButton = notification.findChild("notificationCloseButton"); QVERIFY(closeButton); QTest::mouseClick(closeButton, Qt::LeftButton); QVERIFY(!notification.isVisible()); QCOMPARE(activated.size(), 1); } #ifdef UTUN_TEST_ACTIVITY_DBUS void gnomeIdleAndStaleReplies() { auto bus = QDBusConnection::sessionBus(); QVERIFY(bus.registerService("org.gnome.Mutter.IdleMonitor")); FakeGnomeIdle idle; QVERIFY(bus.registerObject("/org/gnome/Mutter/IdleMonitor/Core", &idle, QDBusConnection::ExportAllSlots)); qputenv("XDG_SESSION_TYPE", "wayland"); UserActivityMonitor activity; QSignalSpy spy(&activity, &UserActivityMonitor::activity); activity.start(); QTRY_VERIFY_WITH_TIMEOUT(idle.requests >= 2, 1000); QCOMPARE(spy.size(), 0); idle.lastInput = idle.clock.elapsed(); QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 1000); idle.delayMs = 300; activity.start(); QTest::qWait(20); idle.lastInput = idle.clock.elapsed(); const int before = idle.requests; QTRY_VERIFY_WITH_TIMEOUT(idle.requests > before, 1000); activity.stop(); idle.lastInput = idle.clock.elapsed() - 5000; activity.start(); QTest::qWait(800); QCOMPARE(spy.size(), 1); // Ответ об активности прежнего наблюдения должен быть отброшен. activity.stop(); qunsetenv("XDG_SESSION_TYPE"); bus.unregisterObject("/org/gnome/Mutter/IdleMonitor/Core"); bus.unregisterService("org.gnome.Mutter.IdleMonitor"); } #endif }; QTEST_MAIN(MessageNotificationTest) #include "test_message_notification.moc"