227 lines
9.1 KiB
C++
227 lines
9.1 KiB
C++
#include "../src/animtimer.h"
|
|
#include "../src/chatview.h"
|
|
#include "../src/emoji.h"
|
|
#include "../src/messagedelegate.h"
|
|
#include "../src/emojipanel.h"
|
|
#include "../src/lottieicon.h"
|
|
#include "../../../lib/debug_config.h"
|
|
#include <QtTest>
|
|
#include <QEnterEvent>
|
|
#include <QPushButton>
|
|
#include <QTabWidget>
|
|
#include <QStandardItemModel>
|
|
#include <zlib.h>
|
|
#include <memory>
|
|
|
|
static QStringList playbackLog;
|
|
|
|
static void captureLog(int, const char *, const char *message) {
|
|
playbackLog.append(QString::fromUtf8(message));
|
|
}
|
|
|
|
// Два коротких цикла разной длины выявляют остановку посреди кадра и ожидание общего кратного.
|
|
static QByteArray animationData(int frames) {
|
|
QByteArray json = QString(R"({"v":"5.7.4","fr":30,"ip":0,"op":%1,"w":32,"h":32,"layers":[]})").arg(frames).toUtf8();
|
|
QByteArray gzip(1024, '\0');
|
|
z_stream stream = {};
|
|
if (deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 16 + MAX_WBITS, 8, Z_DEFAULT_STRATEGY) != Z_OK) return {};
|
|
stream.next_in = reinterpret_cast<Bytef *>(json.data());
|
|
stream.avail_in = json.size();
|
|
stream.next_out = reinterpret_cast<Bytef *>(gzip.data());
|
|
stream.avail_out = gzip.size();
|
|
int result = deflate(&stream, Z_FINISH);
|
|
gzip.resize(stream.total_out);
|
|
deflateEnd(&stream);
|
|
return result == Z_STREAM_END ? gzip : QByteArray();
|
|
}
|
|
|
|
class EmojiPlaybackTest : public QObject {
|
|
Q_OBJECT
|
|
std::unique_ptr<LottieIcon> shortIcon, longIcon;
|
|
|
|
private slots:
|
|
void init() {
|
|
debug_config_init();
|
|
debug_set_category_level(DEBUG_CATEGORY_GENERAL, DEBUG_LEVEL_DEBUG);
|
|
debug_set_log_hook(captureLog);
|
|
playbackLog.clear();
|
|
shortIcon = std::make_unique<LottieIcon>(animationData(7), "short");
|
|
longIcon = std::make_unique<LottieIcon>(animationData(11), "long");
|
|
QVERIFY(shortIcon->isValid());
|
|
QVERIFY(longIcon->isValid());
|
|
AnimTimer::instance()->registerIcon("short", shortIcon.get());
|
|
AnimTimer::instance()->registerIcon("long", longIcon.get());
|
|
}
|
|
|
|
void cleanup() {
|
|
AnimTimer::shutdown();
|
|
debug_set_log_hook(nullptr);
|
|
shortIcon.reset();
|
|
longIcon.reset();
|
|
}
|
|
|
|
void messagesFinishEachCycle() {
|
|
auto *timer = AnimTimer::instance();
|
|
QSignalSpy ticks(timer, &AnimTimer::ticked);
|
|
timer->play();
|
|
QTest::qWait(1850);
|
|
QVERIFY(playbackLog.filter("AnimTimer: playback finished").isEmpty());
|
|
int count = ticks.count();
|
|
QTest::qWait(100);
|
|
QVERIFY(ticks.count() > count);
|
|
QTRY_COMPARE_WITH_TIMEOUT(playbackLog.filter("AnimTimer: playback finished").size(), 1, 1000);
|
|
QCOMPARE(shortIcon->currentFrame(), 0);
|
|
QCOMPARE(longIcon->currentFrame(), 0);
|
|
count = ticks.count();
|
|
QTest::qWait(150);
|
|
QCOMPARE(ticks.count(), count);
|
|
}
|
|
|
|
void anotherHoverRestartsDeadline() {
|
|
auto *timer = AnimTimer::instance();
|
|
QSignalSpy ticks(timer, &AnimTimer::ticked);
|
|
timer->play();
|
|
QTest::qWait(1400);
|
|
timer->play();
|
|
QTest::qWait(1000);
|
|
QVERIFY(playbackLog.filter("AnimTimer: playback finished").isEmpty());
|
|
int count = ticks.count();
|
|
QTest::qWait(100);
|
|
QVERIFY(ticks.count() > count);
|
|
QTRY_COMPARE_WITH_TIMEOUT(playbackLog.filter("AnimTimer: playback finished").size(), 1, 1500);
|
|
QCOMPARE(shortIcon->currentFrame(), 0);
|
|
QCOMPARE(longIcon->currentFrame(), 0);
|
|
}
|
|
|
|
void pickerFinishesWithOrWithoutHover() {
|
|
AnimTimer::instance()->registerIcon(":/animations/robot.tgs", shortIcon.get());
|
|
EmojiPanel panel;
|
|
panel.resize(320, 240);
|
|
panel.findChild<QTabWidget *>()->setCurrentIndex(3);
|
|
panel.show();
|
|
QPushButton *button = nullptr;
|
|
for (auto *candidate : panel.findChildren<QPushButton *>()) {
|
|
if (candidate->toolTip() == ":robot:" && candidate->hasMouseTracking()) button = candidate;
|
|
}
|
|
QVERIFY(button);
|
|
QEvent leave(QEvent::Leave);
|
|
QApplication::sendEvent(button, &leave);
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
QEnterEvent enter(QPointF(1, 1), QPointF(1, 1), QPointF(1, 1));
|
|
#else
|
|
QEvent enter(QEvent::Enter);
|
|
#endif
|
|
QApplication::sendEvent(button, &enter);
|
|
QTest::qWait(1850);
|
|
QVERIFY(playbackLog.filter("EmojiButton: playback finished").isEmpty());
|
|
QTRY_COMPARE_WITH_TIMEOUT(playbackLog.filter("EmojiButton: playback finished").size(), 1, 1000);
|
|
QTest::qWait(150);
|
|
QCOMPARE(playbackLog.filter("EmojiButton: hover trigger").size(), 1);
|
|
|
|
QApplication::sendEvent(button, &leave);
|
|
QApplication::sendEvent(button, &enter);
|
|
QTest::qWait(100);
|
|
QApplication::sendEvent(button, &leave);
|
|
QTest::qWait(1750);
|
|
QCOMPARE(playbackLog.filter("EmojiButton: playback finished").size(), 1);
|
|
QTRY_COMPARE_WITH_TIMEOUT(playbackLog.filter("EmojiButton: playback finished").size(), 2, 1000);
|
|
}
|
|
|
|
void visibleMessagesTriggerOnce() {
|
|
builtinEmojiSet();
|
|
QStandardItemModel model;
|
|
for (int row = 0; row < 6; ++row) {
|
|
auto *item = new QStandardItem("message");
|
|
item->setSizeHint(QSize(200, 60));
|
|
item->setData(row + 1, MsgIdRole);
|
|
item->setData("test-channel", MsgChannelIdRole);
|
|
item->setData(row == 0 || row == 5 ? QString::fromUtf8("🤖") : QString("text"), MsgTextRole);
|
|
model.appendRow(item);
|
|
}
|
|
ChatView view;
|
|
view.setModel(&model);
|
|
view.resize(240, 140);
|
|
QSignalSpy triggers(&view, &ChatView::animationTriggered);
|
|
connect(&view, &ChatView::animationTriggered, AnimTimer::instance(), &AnimTimer::play);
|
|
view.show();
|
|
QTRY_COMPARE(triggers.count(), 1);
|
|
view.viewport()->update();
|
|
QTest::qWait(100);
|
|
QCOMPARE(triggers.count(), 1);
|
|
|
|
view.scrollToBottom();
|
|
QTRY_COMPARE(triggers.count(), 2);
|
|
view.scrollToTop();
|
|
QTRY_COMPARE(triggers.count(), 3);
|
|
view.scrollToBottom();
|
|
QTRY_COMPARE(triggers.count(), 4);
|
|
|
|
auto *message = new QStandardItem("new message");
|
|
message->setSizeHint(QSize(200, 60));
|
|
message->setData(7, MsgIdRole);
|
|
message->setData("test-channel", MsgChannelIdRole);
|
|
message->setData(QString::fromUtf8("🤖"), MsgTextRole);
|
|
model.appendRow(message);
|
|
view.scrollToBottom();
|
|
QTRY_COMPARE(triggers.count(), 5);
|
|
view.viewport()->update();
|
|
QTest::qWait(100);
|
|
QCOMPARE(triggers.count(), 5);
|
|
}
|
|
|
|
void hoverTriggersOnEntry() {
|
|
builtinEmojiSet();
|
|
QStandardItemModel model;
|
|
auto *message = new QStandardItem("message");
|
|
message->setSizeHint(QSize(200, 60));
|
|
message->setData(1, MsgIdRole);
|
|
message->setData(QString::fromUtf8("🤖"), MsgTextRole);
|
|
model.appendRow(message);
|
|
ChatView view;
|
|
view.setModel(&model);
|
|
view.resize(240, 140);
|
|
view.show();
|
|
QTest::qWait(50);
|
|
QSignalSpy triggers(&view, &ChatView::animationTriggered);
|
|
const QPointF point = view.visualRect(model.index(0, 0)).center();
|
|
QMouseEvent move(QEvent::MouseMove, point, point, Qt::NoButton, Qt::NoButton, Qt::NoModifier);
|
|
QApplication::sendEvent(view.viewport(), &move);
|
|
QCOMPARE(triggers.count(), 1);
|
|
QApplication::sendEvent(view.viewport(), &move);
|
|
QCOMPARE(triggers.count(), 1);
|
|
QEvent leave(QEvent::Leave);
|
|
QApplication::sendEvent(view.viewport(), &leave);
|
|
QCOMPARE(triggers.count(), 1);
|
|
QApplication::sendEvent(view.viewport(), &move);
|
|
QCOMPARE(triggers.count(), 2);
|
|
}
|
|
|
|
void inviteClickDoesNotActivateOnDrag() {
|
|
QStandardItemModel model;
|
|
auto* message = new QStandardItem;
|
|
message->setSizeHint(QSize(200, 60));
|
|
message->setData(QStringLiteral("utun://abc+/=="), MsgTextRole);
|
|
model.appendRow(message);
|
|
ChatView view;
|
|
view.setModel(&model); view.resize(240, 140);
|
|
view.setLinkHitTest([](const QModelIndex& index, const QPoint&) { return index.data(MsgTextRole).toString(); });
|
|
view.show();
|
|
QTest::qWait(30);
|
|
const QPoint point = view.visualRect(model.index(0, 0)).center();
|
|
QSignalSpy links(&view, &ChatView::linkActivated);
|
|
QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, point);
|
|
QCOMPARE(links.count(), 1);
|
|
QCOMPARE(links.first().first().toString(), QStringLiteral("utun://abc+/=="));
|
|
QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::NoModifier, point);
|
|
const QPointF dragged = point + QPoint(15, 0);
|
|
QMouseEvent move(QEvent::MouseMove, dragged, dragged, Qt::NoButton, Qt::LeftButton, Qt::NoModifier);
|
|
QApplication::sendEvent(view.viewport(), &move);
|
|
QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::NoModifier, dragged.toPoint());
|
|
QCOMPARE(links.count(), 1);
|
|
QVERIFY(view.hasSelection());
|
|
}
|
|
};
|
|
|
|
QTEST_MAIN(EmojiPlaybackTest)
|
|
#include "test_emoji_playback.moc"
|