Browse Source

chatgui: add Qt integration tests for file drops and recipient changes

master
evgeny 6 days ago
parent
commit
9c7442544b
  1. 9
      tools/chatgui/CMakeLists.txt
  2. 196
      tools/chatgui/tests/test_file_drop.cpp

9
tools/chatgui/CMakeLists.txt

@ -270,6 +270,15 @@ endif()
enable_testing() enable_testing()
add_executable(test_file_drop tests/test_file_drop.cpp src/inputbar.cpp src/audiorecorder.cpp src/audio_device.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)
target_include_directories(test_file_drop PRIVATE ${UTUN_INCLUDE_DIRS})
target_link_libraries(test_file_drop PRIVATE ${QT_LIBS} ${QT_TEST_LIB} rlottie::rlottie ZLIB::ZLIB
utun_voice utun pthread ${CMAKE_DL_LIBS})
add_test(NAME test_file_drop COMMAND test_file_drop)
set_tests_properties(test_file_drop PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen" TIMEOUT 30)
add_executable(test_member_files_browser tests/test_member_files_browser.cpp src/member_files/browser.cpp db/db_manager.cpp) add_executable(test_member_files_browser tests/test_member_files_browser.cpp src/member_files/browser.cpp db/db_manager.cpp)
target_link_libraries(test_member_files_browser PRIVATE ${QT_LIBS} ${QT_TEST_LIB} utun pthread) target_link_libraries(test_member_files_browser PRIVATE ${QT_LIBS} ${QT_TEST_LIB} utun pthread)
add_test(NAME test_member_files_browser COMMAND test_member_files_browser) add_test(NAME test_member_files_browser COMMAND test_member_files_browser)

196
tools/chatgui/tests/test_file_drop.cpp

@ -0,0 +1,196 @@
/* Реальные Qt-события на viewport: drop только готовит вложение, отправку инициирует пользователь. */
#include "../src/inputbar.h"
#include "../../../lib/debug_config.h"
extern "C" {
#include "media_async/attachment_send.h"
#include "mem.h"
}
#include <QtTest>
#include <QTextEdit>
#include <QListView>
#include <QMimeData>
#include <QDropEvent>
#include <QDragEnterEvent>
#include <QDragMoveEvent>
#include <QTemporaryDir>
#include <QFile>
#include <QMessageBox>
#include <QAbstractButton>
static QString makeFile(const QTemporaryDir& dir, const QString& name, const QByteArray& contents = "test") {
const QString path = dir.filePath(name);
QFile file(path);
if (!file.open(QIODevice::WriteOnly) || file.write(contents) != contents.size()) return {};
return path;
}
static bool dropUrls(QWidget* target, const QList<QUrl>& urls, bool process = true) {
QMimeData mime;
mime.setUrls(urls);
QDragEnterEvent enter(QPoint(5, 5), Qt::CopyAction | Qt::MoveAction, &mime, Qt::LeftButton, Qt::NoModifier);
QApplication::sendEvent(target, &enter);
if (!enter.isAccepted()) return false;
QDragMoveEvent move(QPoint(5, 5), Qt::CopyAction | Qt::MoveAction, &mime, Qt::LeftButton, Qt::NoModifier);
QApplication::sendEvent(target, &move);
QDropEvent drop(QPointF(5, 5), Qt::CopyAction | Qt::MoveAction, &mime, Qt::LeftButton, Qt::NoModifier);
QApplication::sendEvent(target, &drop);
if (process) QCoreApplication::processEvents();
return drop.isAccepted() && drop.dropAction() == Qt::CopyAction;
}
class FileDropTest : public QObject {
Q_OBJECT
private slots:
void initTestCase() { debug_config_init(); debug_set_category_level(DEBUG_CATEGORY_GENERAL, DEBUG_LEVEL_DEBUG); }
void previewBeforeExplicitSend_data() {
QTest::addColumn<bool>("history");
QTest::addColumn<bool>("dm");
QTest::newRow("group-input") << false << false;
QTest::newRow("group-history") << true << false;
QTest::newRow("dm-input") << false << true;
QTest::newRow("dm-history") << true << true;
}
void previewBeforeExplicitSend() {
QFETCH(bool, history); QFETCH(bool, dm);
QTemporaryDir dir; QVERIFY(dir.isValid());
const QString path = makeFile(dir, QString::fromUtf8("файл # 100%.txt")); QVERIFY(!path.isEmpty());
InputBar bar(nullptr, nullptr); bar.resize(600, 150); bar.show();
const QString target = dm ? "dm:0123456789abcdef" : "7206723622466219923";
bar.setRecordingContext(dir.filePath("media"), target); bar.setDmMode(dm);
auto* editor = bar.findChild<QTextEdit*>(); QVERIFY(editor);
editor->setPlainText("draft");
QListView view; view.resize(600, 400); view.show(); bar.installFileDropTarget(view.viewport());
QSignalSpy files(&bar, &InputBar::sendFileAttachment);
QSignalSpy texts(&bar, &InputBar::sendMessage);
QVERIFY(dropUrls(history ? view.viewport() : editor->viewport(), {QUrl::fromLocalFile(path)}));
QCOMPARE(files.count(), 0); QCOMPARE(texts.count(), 0); QCOMPARE(editor->toPlainText(), QString("draft"));
QTest::keyClick(editor, Qt::Key_Return);
QCOMPARE(files.count(), 1); QCOMPARE(texts.count(), 1);
QCOMPARE(files.at(0).at(0).toString(), target); QCOMPARE(files.at(0).at(1).toString(), path);
QCOMPARE(files.at(0).at(3).toString(), QFileInfo(path).fileName());
QCOMPARE(texts.at(0).at(0).toString(), QString("draft"));
QVERIFY(QFile::exists(path)); // CopyAction не удаляет исходный файл.
}
void rejectInvalidDropsWithoutReplacingTheDraft_data() {
QTest::addColumn<QString>("kind");
for (const char* kind : {"multiple", "remote", "directory", "missing", "empty"}) QTest::newRow(kind) << QString(kind);
}
void rejectInvalidDropsWithoutReplacingTheDraft() {
QFETCH(QString, kind);
QTemporaryDir dir; QVERIFY(dir.isValid());
const QString first = makeFile(dir, "first.txt"); QVERIFY(!first.isEmpty());
InputBar bar(nullptr, nullptr); bar.show(); bar.setRecordingContext(dir.filePath("media"), "123");
auto* editor = bar.findChild<QTextEdit*>(); QVERIFY(editor);
QVERIFY(dropUrls(editor->viewport(), {QUrl::fromLocalFile(first)}));
editor->setPlainText("draft");
QSignalSpy rejected(&bar, &InputBar::attachmentRejected);
QSignalSpy files(&bar, &InputBar::sendFileAttachment);
QList<QUrl> urls;
if (kind == "multiple") urls = {QUrl::fromLocalFile(first), QUrl::fromLocalFile(first)};
else if (kind == "remote") urls = {QUrl("https://example.invalid/file.txt")};
else if (kind == "directory") urls = {QUrl::fromLocalFile(dir.path())};
else if (kind == "missing") urls = {QUrl::fromLocalFile(dir.filePath("missing.txt"))};
else urls = {QUrl::fromLocalFile(makeFile(dir, "empty.txt", {}))};
dropUrls(editor->viewport(), urls);
QCOMPARE(rejected.count(), 1); QCOMPARE(editor->toPlainText(), QString("draft"));
QTest::keyClick(editor, Qt::Key_Return);
QCOMPARE(files.count(), 1); QCOMPARE(files.at(0).at(1).toString(), first);
}
void queuedDropCannotFollowAChangedAccountOrChannel_data() {
QTest::addColumn<bool>("account");
QTest::newRow("channel-switch-and-back") << false;
QTest::newRow("account-switch-and-back") << true;
}
void queuedDropCannotFollowAChangedAccountOrChannel() {
QFETCH(bool, account);
QTemporaryDir dir; QVERIFY(dir.isValid());
const QString path = makeFile(dir, "test.txt");
InputBar bar(nullptr, nullptr); bar.show(); bar.setRecordingContext(dir.filePath("media"), "123");
auto* editor = bar.findChild<QTextEdit*>(); QVERIFY(editor);
QSignalSpy rejected(&bar, &InputBar::attachmentRejected);
QSignalSpy files(&bar, &InputBar::sendFileAttachment);
QVERIFY(dropUrls(editor->viewport(), {QUrl::fromLocalFile(path)}, false));
bar.setRecordingContext(dir.filePath(account ? "other-account" : "media"), account ? "123" : "456");
bar.setRecordingContext(dir.filePath("media"), "123");
QCoreApplication::processEvents();
QCOMPARE(rejected.count(), 1);
QTest::keyClick(editor, Qt::Key_Return); QCOMPARE(files.count(), 0);
}
void pendingFileClearedWhenChatChanges() {
QTemporaryDir dir; QVERIFY(dir.isValid());
InputBar bar(nullptr, nullptr); bar.show(); bar.setRecordingContext(dir.filePath("media"), "123");
auto* editor = bar.findChild<QTextEdit*>(); QVERIFY(editor);
QVERIFY(dropUrls(editor->viewport(), {QUrl::fromLocalFile(makeFile(dir, "test.txt"))}));
bar.setRecordingContext(dir.filePath("media"), "456");
QSignalSpy files(&bar, &InputBar::sendFileAttachment);
QTest::keyClick(editor, Qt::Key_Return); QCOMPARE(files.count(), 0);
}
void disabledOrUnselectedChatDoesNotAcceptFiles() {
QTemporaryDir dir; QVERIFY(dir.isValid());
InputBar bar(nullptr, nullptr); bar.show();
auto* editor = bar.findChild<QTextEdit*>(); QVERIFY(editor);
const QList<QUrl> urls{QUrl::fromLocalFile(makeFile(dir, "test.txt"))};
QVERIFY(!dropUrls(editor->viewport(), urls));
bar.setRecordingContext(dir.filePath("media"), "123"); bar.setMediaEnabled(false);
QVERIFY(!dropUrls(editor->viewport(), urls));
QCOMPARE(editor->toPlainText(), QString());
}
void imageKeepsTheExistingGroupAndPmBehaviour_data() {
QTest::addColumn<bool>("dm");
QTest::newRow("group-image") << false; QTest::newRow("dm-file") << true;
}
void imageKeepsTheExistingGroupAndPmBehaviour() {
QFETCH(bool, dm);
QTemporaryDir dir; QVERIFY(dir.isValid());
InputBar bar(nullptr, nullptr); bar.show();
bar.setRecordingContext(dir.filePath("media"), dm ? "dm:abcd" : "123"); bar.setDmMode(dm);
auto* editor = bar.findChild<QTextEdit*>(); QVERIFY(editor);
QSignalSpy files(&bar, &InputBar::sendFileAttachment), images(&bar, &InputBar::sendImageAttachment);
QVERIFY(dropUrls(editor->viewport(), {QUrl::fromLocalFile(makeFile(dir, "фото.PNG"))}));
QTest::keyClick(editor, Qt::Key_Return);
QCOMPARE(files.count(), dm ? 1 : 0); QCOMPARE(images.count(), dm ? 0 : 1);
}
void videoModeSelectionAndCancellation_data() {
QTest::addColumn<QString>("mode");
for (const char* mode : {"file", "video", "cancel", "changed-chat"}) QTest::newRow(mode) << QString(mode);
}
void videoModeSelectionAndCancellation() {
QFETCH(QString, mode);
QTemporaryDir dir; QVERIFY(dir.isValid());
InputBar bar(nullptr, nullptr); bar.show(); bar.setRecordingContext(dir.filePath("media"), "123");
auto* editor = bar.findChild<QTextEdit*>(); QVERIFY(editor);
QSignalSpy files(&bar, &InputBar::sendFileAttachment), rejected(&bar, &InputBar::attachmentRejected);
int videos = 0;
connect(&bar, &InputBar::submitAttachment, &bar, [&](const QString& target, attachment_send_req* request) {
QCOMPARE(target, QString("123")); QCOMPARE(request->info.kind, ATTACHMENT_VIDEO); QVERIFY(request->transcode);
++videos; u_free(request);
});
QVERIFY(dropUrls(editor->viewport(), {QUrl::fromLocalFile(makeFile(dir, "video.mp4"))}, false));
QTimer timer; timer.setInterval(1);
connect(&timer, &QTimer::timeout, &bar, [&] {
auto* box = qobject_cast<QMessageBox*>(QApplication::activeModalWidget());
if (!box) return;
timer.stop();
if (mode == "cancel") { box->reject(); return; }
if (mode == "changed-chat") bar.setRecordingContext(dir.filePath("media"), "456");
for (auto* button : box->buttons()) if (button->text() == (mode == "video" ? "As a video" : "As a file")) {
button->click(); return;
}
box->reject();
});
timer.start(); QCoreApplication::processEvents();
QTest::keyClick(editor, Qt::Key_Return);
QCOMPARE(files.count(), mode == "file" ? 1 : 0); QCOMPARE(videos, mode == "video" ? 1 : 0);
QCOMPARE(rejected.count(), mode == "changed-chat" ? 1 : 0);
}
};
QTEST_MAIN(FileDropTest)
#include "test_file_drop.moc"
Loading…
Cancel
Save