Update broadcast control tests and Russian menu labels

This commit is contained in:
evgeny committed 2026-10-28 13:42:58 +01:00
1 parent 35875a2c43
commit 21ab92d2b4
2 files changed
+100 -42

No files matched your search

+92 -42
View File
@@ -3,10 +3,11 @@
#include <QtTest>
#include <QTimer>
#include <QLabel>
#include <QComboBox>
#include <QSpinBox>
#include <QCheckBox>
#include <QButtonGroup>
#include <QMenu>
#include <QMenuBar>
#include <QActionGroup>
#include <QToolButton>
#include <QPushButton>
#include <QMessageBox>
#include <QJsonDocument>
@@ -146,6 +147,19 @@ class StreamPublishTest : public QObject {
QTimer poll;
uint64_t group = 0;
size_t allocations = 0;
static QMenu* menu(QWidget& controls, const char* name) { return controls.findChild<QMenu*>(name); }
static QVariant chosen(QMenu* menu) {
auto* action = menu->findChild<QActionGroup*>()->checkedAction(); return action ? action->data() : QVariant();
}
static QList<QAction*> choices(QMenu* menu) {
QList<QAction*> result;
for (auto* action : menu->actions()) if (action->isCheckable()) result.append(action);
return result;
}
static void choose(QMenu* menu, const QVariant& data) {
for (auto* action : choices(menu)) if (action->data() == data) { action->trigger(); return; }
QFAIL("Missing menu choice");
}
static QByteArray keyframe(int codec, int width = 160, int height = 90, uint64_t pts = 100000) {
#ifndef HAVE_FFMPEG
Q_UNUSED(codec); Q_UNUSED(width); Q_UNUSED(height); Q_UNUSED(pts);
@@ -201,8 +215,8 @@ private slots:
QVERIFY(!StreamPublishEngine::supported()); QVERIFY(StreamPublishEngine::sources().isEmpty());
QVERIFY(engine.codecs().isEmpty()); QVERIFY(!engine.previewFrame().isValid());
StreamPublishWindow window(&engine, group); window.show();
auto* source = window.findChild<QComboBox*>(QStringLiteral("streamVideoSource"));
QVERIFY(source); QCOMPARE(source->count(), 0);
auto* source = menu(window, "streamVideoSource");
QVERIFY(source); QCOMPARE(choices(source).size(), 0);
QPushButton* start = nullptr;
for (auto* button : window.findChildren<QPushButton*>())
if (button->text() == QCoreApplication::translate("ChatGui", "Start stream")) start = button;
@@ -264,60 +278,61 @@ private slots:
}
void captureSettingsSurviveReopening() {
QString saved;
QVariantList selectedMode;
QSize selectedSize;
{
StreamPublishEngine engine;
StreamPublishWindow controls(&engine, group);
connect(&controls, &StreamPublishWindow::settingsChanged, this, [&saved](const QString& value) { saved = value; });
auto boxes = controls.findChildren<QComboBox*>(); QCOMPARE(boxes.size(), 5);
if (StreamPublishEngine::supported()) QTRY_VERIFY_WITH_TIMEOUT(boxes[2]->count() > 0, 15000);
auto* codec = menu(controls, "streamVideoCodec");
if (StreamPublishEngine::supported()) QTRY_VERIFY_WITH_TIMEOUT(!choices(codec).isEmpty(), 15000);
auto sources = StreamPublishEngine::sources();
int screen = -1;
for (int i = 0; i < sources.size(); i++) if (sources[i].kind == StreamVideoSource::Screen && !sources[i].size.isEmpty()) { screen = i; break; }
if (screen < 0) QSKIP("No enumerated screen for capture settings");
boxes[0]->setCurrentIndex(screen);
choose(menu(controls, "streamVideoSource"), screen);
auto* cursor = controls.findChild<QCheckBox*>("streamShowCursor"); QVERIFY(cursor); QVERIFY(cursor->isChecked());
cursor->setChecked(false);
QSize maximum = stream_video_screen_size(sources[screen].size);
QCOMPARE(boxes[1]->currentData().toList(), (QVariantList{maximum.width(), maximum.height(), 30}));
QVERIFY(boxes[1]->count() >= 6);
boxes[1]->setCurrentIndex(boxes[1]->count() - 1); // Меньший размер сохраняется при повторном открытии.
selectedMode = boxes[1]->currentData().toList();
boxes[3]->setCurrentIndex(boxes[3]->findData(4000000));
controls.findChild<QSpinBox*>("streamMaxQp")->setValue(25);
auto* resolution = menu(controls, "streamVideoResolution");
QCOMPARE(chosen(resolution).toSize(), maximum); QCOMPARE(chosen(menu(controls, "streamVideoFps")).toInt(), 30);
QVERIFY(choices(resolution).size() >= 2);
choices(resolution).last()->trigger(); // Меньший размер сохраняется при повторном открытии.
selectedSize = chosen(resolution).toSize(); choose(menu(controls, "streamVideoFps"), 60);
choose(menu(controls, "streamVideoBitrate"), 4000000);
choose(menu(controls, "streamMaxQp"), 23);
controls.findChild<QCheckBox*>("streamAdaptiveQp")->setChecked(true);
controls.findChild<QButtonGroup*>()->button(270)->click();
controls.findChildren<QCheckBox*>().first()->setChecked(false);
if (boxes[2]->count() > 1) boxes[2]->setCurrentIndex(boxes[2]->count() - 1);
choose(menu(controls, "streamVideoRotation"), 270);
choose(menu(controls, "streamAudioSource"), QVariant());
if (choices(codec).size() > 1) choices(codec).last()->trigger();
QVERIFY(!saved.isEmpty());
}
const auto stored = QJsonDocument::fromJson(saved.toUtf8()).object();
QCOMPARE(stored.value("width").toInt(), selectedMode[0].toInt()); QCOMPARE(stored.value("fps").toInt(), 60);
QCOMPARE(stored.value("width").toInt(), selectedSize.width()); QCOMPARE(stored.value("fps").toInt(), 60);
QVERIFY(!stored.value("native_size").toBool());
QCOMPARE(stored.value("bitrate").toInt(), 4000000); QCOMPARE(stored.value("rotation").toInt(), 270);
QVERIFY(!stored.value("audio").toBool());
QVERIFY(!stored.value("show_cursor").toBool(true));
QCOMPARE(stored.value("max_qp").toInt(), 25); QVERIFY(stored.value("adaptive_qp").toBool());
QCOMPARE(stored.value("max_qp").toInt(), 23); QVERIFY(stored.value("adaptive_qp").toBool());
StreamPublishEngine reopenedEngine;
StreamPublishWindow reopened(&reopenedEngine, group, nullptr, saved);
QSignalSpy changes(&reopened, &StreamPublishWindow::settingsChanged);
if (StreamPublishEngine::supported()) QTRY_VERIFY_WITH_TIMEOUT(!reopenedEngine.codecs().isEmpty(), 15000);
auto boxes = reopened.findChildren<QComboBox*>();
QCOMPARE(boxes[1]->currentData().toList(), selectedMode);
QCOMPARE(boxes[3]->currentData().toInt(), 4000000);
QCOMPARE(reopened.findChild<QSpinBox*>("streamMaxQp")->value(), 25);
QCOMPARE(chosen(menu(reopened, "streamVideoResolution")).toSize(), selectedSize);
QCOMPARE(chosen(menu(reopened, "streamVideoFps")).toInt(), 60);
QCOMPARE(chosen(menu(reopened, "streamVideoBitrate")).toInt(), 4000000);
QCOMPARE(chosen(menu(reopened, "streamMaxQp")).toInt(), 23);
QVERIFY(reopened.findChild<QCheckBox*>("streamAdaptiveQp")->isChecked());
if (StreamPublishEngine::supported()) QTRY_COMPARE(boxes[2]->currentData().toInt(), stored.value("codec").toInt());
QCOMPARE(reopened.findChild<QButtonGroup*>()->checkedId(), 270);
QVERIFY(!reopened.findChildren<QCheckBox*>().first()->isChecked());
if (StreamPublishEngine::supported()) QTRY_COMPARE(chosen(menu(reopened, "streamVideoCodec")).toInt(), stored.value("codec").toInt());
QCOMPARE(chosen(menu(reopened, "streamVideoRotation")).toInt(), 270);
QVERIFY(!chosen(menu(reopened, "streamAudioSource")).isValid());
QVERIFY(!reopened.findChild<QCheckBox*>("streamShowCursor")->isChecked());
QVERIFY(changes.isEmpty()); // Асинхронный список кодеков не перезаписывает сохранённый выбор.
boxes[3]->setCurrentIndex(0);
choose(menu(reopened, "streamVideoBitrate"), 0);
QCOMPARE(changes.size(), 1);
auto automatic = changes.takeFirst().front().toString();
QCOMPARE(QJsonDocument::fromJson(automatic.toUtf8()).object().value("bitrate").toInt(-1), 0);
StreamPublishWindow restoredAuto(&reopenedEngine, group, nullptr, automatic);
QCOMPARE(restoredAuto.findChildren<QComboBox*>()[3]->currentIndex(), 0);
QCOMPARE(chosen(menu(restoredAuto, "streamVideoBitrate")).toInt(), 0);
}
void qpAdaptationFollowsBitrateWithoutSpikes() {
StreamQpControl qp; qp.reset(28, 1000000, true, 0);
@@ -423,8 +438,8 @@ private slots:
}
void screenSelectionAndRefresh() {
StreamPublishEngine engine; StreamPublishWindow controls(&engine, group);
auto* sourceBox = controls.findChild<QComboBox*>("streamVideoSource");
auto* modeBox = controls.findChild<QComboBox*>("streamVideoMode");
auto* sourceBox = menu(controls, "streamVideoSource");
auto* resolution = menu(controls, "streamVideoResolution");
auto sources = StreamPublishEngine::sources();
QSignalSpy changes(&controls, &StreamPublishWindow::settingsChanged);
QList<QByteArray> screenIds;
@@ -432,17 +447,52 @@ private slots:
for (int i = 0; i < sources.size(); i++) {
if (sources[i].kind != StreamVideoSource::Screen || sources[i].size.isEmpty()) continue;
QVERIFY(!screenIds.contains(sources[i].id)); screenIds.append(sources[i].id);
screens++; sourceBox->setCurrentIndex(-1); changes.clear(); sourceBox->setCurrentIndex(i);
screens++; changes.clear(); choose(sourceBox, i);
QSize size = stream_video_screen_size(sources[i].size);
auto mode = modeBox->currentData().toList();
QCOMPARE(mode[0].toInt(), size.width()); QCOMPARE(mode[1].toInt(), size.height());
QVERIFY(modeBox->currentData(Qt::UserRole + 1).toBool()); QCOMPARE(changes.size(), 1);
QVERIFY(sourceBox->currentText().contains(sources[i].name));
modeBox->setCurrentIndex(modeBox->count() - 1); mode = modeBox->currentData().toList(); changes.clear();
emit engine.sourcesChanged(); QCOMPARE(modeBox->currentData().toList(), mode); QVERIFY(changes.isEmpty());
QCOMPARE(chosen(resolution).toSize(), size); QCOMPARE(changes.size(), 1);
QVERIFY(sourceBox->findChild<QActionGroup*>()->checkedAction()->text().contains(sources[i].name));
choices(resolution).last()->trigger(); QSize selectedSize = chosen(resolution).toSize(); changes.clear();
emit engine.sourcesChanged(); QCOMPARE(chosen(resolution).toSize(), selectedSize); QVERIFY(changes.isEmpty());
}
if (!screens) QSKIP("No enumerated screens");
}
void compactControlMenus() {
StreamPublishEngine engine; StreamPublishWindow controls(&engine, group); controls.show();
controls.resize(controls.minimumSize()); QTest::qWait(20);
auto* bar = controls.findChild<QMenuBar*>("streamControls"); QVERIFY(bar);
auto* start = controls.findChild<QPushButton*>("streamStart"); QVERIFY(start);
auto* sound = controls.findChild<QToolButton*>("streamSound"); QVERIFY(sound);
auto* buffer = controls.findChild<QWidget*>("streamBufferIndicator"); QVERIFY(buffer);
QCOMPARE(buffer->height(), 20); QCOMPARE(buffer->width(), std::max(80, start->sizeHint().width()));
QCOMPARE(buffer->geometry().center().y(), start->geometry().center().y());
QVERIFY(std::abs(bar->geometry().center().y() - start->geometry().center().y()) <= 1);
QVERIFY(bar->geometry().right() < sound->geometry().left()); QVERIFY(sound->geometry().right() < buffer->geometry().left());
auto* qp = menu(controls, "streamMaxQp"); QCOMPARE(choices(qp).size(), 6); QCOMPARE(chosen(qp).toInt(), 28);
auto* audio = menu(controls, "streamAudioSource");
choose(audio, QVariant()); QVERIFY(!chosen(audio).isValid()); QVERIFY(!sound->isChecked());
choose(audio, QString()); QVERIFY(chosen(audio).isValid()); QVERIFY(sound->isChecked());
auto* resolution = menu(controls, "streamVideoResolution"); auto* fps = menu(controls, "streamVideoFps");
for (auto* submenu : {resolution, fps, qp, audio}) {
QVERIFY(!submenu->actions().first()->text().isEmpty()); QVERIFY(!submenu->actions().first()->isCheckable());
QVERIFY(bar->rect().contains(bar->actionGeometry(submenu->menuAction())));
}
QTest::mouseClick(bar, Qt::LeftButton, {}, bar->actionGeometry(resolution->menuAction()).center());
QTRY_VERIFY(resolution->isVisible());
QTest::mouseClick(bar, Qt::LeftButton, {}, bar->actionGeometry(fps->menuAction()).center());
QTRY_VERIFY(fps->isVisible()); QVERIFY(!resolution->isVisible());
QTest::mouseClick(bar, Qt::LeftButton, {}, bar->actionGeometry(fps->menuAction()).center());
QTRY_VERIFY(!fps->isVisible());
const auto sources = engine.sources();
for (int i = 0; i < sources.size(); i++) {
choose(menu(controls, "streamVideoSource"), i);
const auto modes = sources[i].kind == StreamVideoSource::Camera ? sources[i].modes : stream_video_screen_modes(sources[i].size);
for (auto* sizeAction : choices(resolution)) {
sizeAction->trigger(); const QSize size = chosen(resolution).toSize();
for (auto* rate : choices(fps)) QCOMPARE(rate->isEnabled(), modes.contains({size, rate->data().toInt()}));
if (!modes.isEmpty()) QVERIFY(modes.contains({size, chosen(fps).toInt()}));
}
}
}
void nativeScreenCapture_data() {
QTest::addColumn<int>("codec");
QTest::newRow("h264") << int(STREAM_MEDIA_H264);
@@ -606,9 +656,9 @@ private slots:
if (source.kind != StreamVideoSource::Screen || source.screen != screen) continue;
QVERIFY(StreamPublishEngine::cursorSupported(source));
StreamPublishWindow controls(&engine, group);
auto* sources = controls.findChild<QComboBox*>("streamVideoSource"); QVERIFY(sources);
auto* sources = menu(controls, "streamVideoSource"); QVERIFY(sources);
auto all = engine.sources();
for (int i = 0; i < all.size(); i++) if (all[i].sameSource(source)) sources->setCurrentIndex(i);
for (int i = 0; i < all.size(); i++) if (all[i].sameSource(source)) choose(sources, i);
auto* toggle = controls.findChild<QCheckBox*>("streamShowCursor"); QVERIFY(toggle);
QVERIFY(!toggle->isHidden()); QVERIFY(toggle->isChecked());
}
@@ -933,7 +983,7 @@ private slots:
auto* preview = controls.findChild<QVideoWidget*>(); QVERIFY(preview);
QTRY_VERIFY(preview->videoSink()->videoFrame().isValid());
QSignalSpy closed(&controls, &StreamPublishWindow::closed);
auto* choice = controls.findChild<QComboBox*>(); QVERIFY(choice); choice->setFocus();
auto* choice = controls.findChild<QMenuBar*>(); QVERIFY(choice); choice->setFocus();
QTest::keyClick(choice, Qt::Key_Escape);
QTRY_COMPARE(closed.count(), 1); QVERIFY(!controls.isVisible()); QVERIFY(engine.running());
QVERIFY(record(205, StreamRecordKind::Video, out)); QVERIFY(out.pts > firstPts);
@@ -980,7 +1030,7 @@ private slots:
QTRY_VERIFY(preview->videoSink()->videoFrame().isValid());
QSignalSpy closed(&controls, &StreamPublishWindow::closed);
controls.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&controls));
auto* sourceChoice = controls.findChild<QComboBox*>(); QVERIFY(sourceChoice); sourceChoice->setFocus();
auto* sourceChoice = controls.findChild<QMenuBar*>(); QVERIFY(sourceChoice); sourceChoice->setFocus();
QTest::keyClick(sourceChoice, Qt::Key_Escape);
QTRY_COMPARE(closed.count(), 1); QVERIFY(!controls.isVisible()); QVERIFY(engine.running());
uint64_t first = out.pts; unsigned audio = 0, video = 0; uint64_t latest = first, left = 0, right = 0;
+8
View File
@@ -154,6 +154,14 @@ Use CPU encoding instead? This may cause high CPU usage and slow down the comput
<message><source>Cannot read video</source><translation>Не удалось прочитать видео</translation></message>
<message><source>Could not send the call command</source><translation>Не удалось отправить команду звонка</translation></message>
<message><source>Video source</source><translation>Источник видео</translation></message>
<message><source>Video resolution</source><translation>Разрешение видео</translation></message>
<message><source>Frame rate</source><translation>Частота кадров</translation></message>
<message><source>Original resolution</source><translation>Исходное разрешение</translation></message>
<message><source>%1 fps</source><translation>%1 кадр/с</translation></message>
<message><source>Audio source</source><translation>Источник звука</translation></message>
<message><source>No audio</source><translation>Без звука</translation></message>
<message><source>Turn stream audio on</source><translation>Включить звук трансляции</translation></message>
<message><source>Turn stream audio off</source><translation>Выключить звук трансляции</translation></message>
<message><source>Show mouse cursor</source><translation>Показывать курсор мыши</translation></message>
<message><source>Change cursor visibility before starting screen sharing</source><translation>Показ курсора можно изменить до начала трансляции экрана</translation></message>
<message><source>Screen capture failed: %1</source><translation>Ошибка захвата экрана: %1</translation></message>