You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

201 lines
7.7 KiB

#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);
}
};
QTEST_MAIN(EmojiPlaybackTest)
#include "test_emoji_playback.moc"