Show audio output captions and validate the desktop call settings menus
This commit is contained in:
1 parent
2540909fda
commit
bb0fa13c8b
2 files changed
+68
-2
No files matched your search
@@ -135,11 +135,13 @@ inline QIcon controlIcon(const QPalette& palette, const QString& shape, const QS
|
||||
painter.drawRoundedRect(QRectF(15, 3, 10, 19), 5, 5); painter.drawArc(QRectF(10, 12, 20, 16), 180 * 16, 180 * 16);
|
||||
painter.drawLine(20, 28, 20, 33); painter.drawLine(14, 33, 26, 33);
|
||||
painter.restore(); labelRect = QRectF(1, 23, 38, 13); labelSize = 9;
|
||||
} else if (shape == "sound" || shape == "silent") {
|
||||
} else if (shape == "sound" || shape == "silent" || shape == "output") {
|
||||
if (shape == "output") { painter.save(); painter.translate(7, 0); painter.scale(0.65, 0.65); }
|
||||
QPolygon speaker; speaker << QPoint(6, 14) << QPoint(13, 14) << QPoint(22, 7) << QPoint(22, 29)
|
||||
<< QPoint(13, 22) << QPoint(6, 22); painter.drawPolygon(speaker);
|
||||
if (shape == "sound") painter.drawArc(QRectF(18, 6, 17, 24), -70 * 16, 140 * 16);
|
||||
if (shape != "silent") painter.drawArc(QRectF(18, 6, 17, 24), -70 * 16, 140 * 16);
|
||||
else { painter.drawLine(27, 13, 35, 23); painter.drawLine(27, 23, 35, 13); }
|
||||
if (shape == "output") { painter.restore(); labelRect = QRectF(1, 23, 38, 13); labelSize = 9; }
|
||||
} else {
|
||||
painter.drawRoundedRect(QRectF(3, 5, 34, 26), 4, 4);
|
||||
if (!detail.isEmpty()) labelRect = QRectF(3, 16, 34, 15);
|
||||
|
||||
@@ -2,12 +2,17 @@
|
||||
* Linux hooks наблюдают границу native trampoline и затем вызывают его без подмены освобождения payload. */
|
||||
#include "../src/callwindow.h"
|
||||
#include "../src/call_camera_capture.h"
|
||||
#include "../src/call_video_engine.h"
|
||||
#include "../transport/gui_bridge.h"
|
||||
#include <QtTest>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QPointer>
|
||||
#include <QScopeGuard>
|
||||
#include <QMenuBar>
|
||||
#include <QMenu>
|
||||
#include <QActionGroup>
|
||||
#include <QSpinBox>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <cstring>
|
||||
@@ -154,6 +159,65 @@ private slots:
|
||||
observed.clear(); bindingValid = true;
|
||||
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
|
||||
}
|
||||
void videoSettingsMenus() {
|
||||
CallWindow window; window.setCallId(77); window.setPeerName(QStringLiteral("Video call")); window.setState(CallWindow::Active);
|
||||
auto* engine = window.findChild<CallVideoEngine*>(); QVERIFY(engine);
|
||||
auto* capture = engine->findChild<CallCameraCapture*>(); QVERIFY(capture);
|
||||
QObject::disconnect(capture, nullptr, engine, nullptr); // Детерминированные режимы, без открытия камеры/звонка.
|
||||
CallCameraDevice first{QStringLiteral("Main camera"), QByteArray("main"), {}};
|
||||
stream_video_add_camera_modes(first.modes, QSize(1920, 1080), 29.97, 29.97);
|
||||
stream_video_add_camera_modes(first.modes, QSize(1280, 720), 15, 60);
|
||||
CallCameraDevice second{QStringLiteral("Secondary camera"), QByteArray("secondary"), {}};
|
||||
stream_video_add_camera_modes(second.modes, QSize(640, 480), 7, 7);
|
||||
emit engine->camerasChanged({first, second});
|
||||
auto getMenu = [&window](const char* name) { return window.findChild<QMenu*>(name); };
|
||||
auto chosen = [](QMenu* menu) { auto* action = menu->findChild<QActionGroup*>()->checkedAction(); return action ? action->data() : QVariant(); };
|
||||
auto choose = [](QMenu* menu, const QVariant& value) {
|
||||
for (auto* action : menu->actions()) if (action->isCheckable() && action->data() == value) { action->trigger(); return; }
|
||||
QFAIL("Missing video setting");
|
||||
};
|
||||
auto* camera = getMenu("callVideoCamera"); auto* resolution = getMenu("callVideoResolution"); auto* fps = getMenu("callVideoFps");
|
||||
QVERIFY(camera); QVERIFY(resolution); QVERIFY(fps); QCOMPARE(chosen(camera).toByteArray(), first.id);
|
||||
choose(resolution, QSize(1280, 720)); choose(fps, 60); QCOMPARE(chosen(fps).toInt(), 60);
|
||||
const QImage previousResolution = resolution->menuAction()->icon().pixmap(40, 36).toImage();
|
||||
choose(resolution, QSize(1920, 1080)); QCOMPARE(chosen(fps).toInt(), 30);
|
||||
QVERIFY(resolution->menuAction()->icon().pixmap(40, 36).toImage() != previousResolution);
|
||||
for (auto* action : fps->actions()) if (action->isCheckable())
|
||||
QCOMPARE(action->isEnabled(), action->data().toInt() <= 30);
|
||||
choose(camera, second.id); QCOMPARE(chosen(resolution).toSize(), QSize(640, 480)); QCOMPARE(chosen(fps).toInt(), 7);
|
||||
emit engine->camerasChanged({second, first}); QCOMPARE(chosen(camera).toByteArray(), second.id); QCOMPARE(chosen(fps).toInt(), 7);
|
||||
choose(camera, first.id); choose(resolution, QSize(1920, 1080)); choose(getMenu("callVideoCodec"), 1);
|
||||
auto* minimum = window.findChild<QSpinBox*>("callVideoMinimumBitrate"); QVERIFY(minimum);
|
||||
auto* maximum = window.findChild<QSpinBox*>("callVideoMaximumBitrate"); QVERIFY(maximum);
|
||||
minimum->setValue(2000); maximum->setValue(1000); QCOMPARE(minimum->value(), 1000);
|
||||
minimum->setValue(300); maximum->setValue(4000);
|
||||
auto* settings = window.findChild<QPushButton*>("callVideoSettings"); QVERIFY(settings);
|
||||
settings->click(); window.show(); QTest::qWait(20);
|
||||
auto* bar = window.findChild<QMenuBar*>("callVideoControls"); QVERIFY(bar); QVERIFY(bar->isVisible());
|
||||
QRect previous;
|
||||
for (auto* action : bar->actions()) {
|
||||
QRect button = bar->actionGeometry(action); QCOMPARE(button.size(), QSize(48, 48));
|
||||
if (!previous.isNull()) QCOMPARE(button.left() - previous.right() - 1, 4);
|
||||
previous = button; QVERIFY(bar->rect().contains(button));
|
||||
QVERIFY(!action->menu()->actions().first()->text().isEmpty()); QVERIFY(!action->toolTip().isEmpty());
|
||||
}
|
||||
const QString screenshot = qEnvironmentVariable("UTUN_TEST_CALL_CONTROLS_SCREENSHOT");
|
||||
if (!screenshot.isEmpty()) QVERIFY(window.grab().save(screenshot));
|
||||
auto* action = camera->menuAction(); const QIcon original = action->icon();
|
||||
QPixmap marker(80, 72); marker.fill(Qt::magenta); action->setIcon(QIcon(marker));
|
||||
const QImage rendered = bar->grab().toImage(); QRect iconBounds;
|
||||
for (int y = 0; y < rendered.height(); y++) for (int x = 0; x < rendered.width(); x++) {
|
||||
QColor color = rendered.pixelColor(x, y);
|
||||
if (color.red() > 240 && color.blue() > 240 && color.green() < 15) iconBounds |= QRect(x, y, 1, 1);
|
||||
}
|
||||
action->setIcon(original); QCOMPARE(iconBounds.size(), QSize(40, 36) * rendered.devicePixelRatio());
|
||||
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());
|
||||
settings->click(); QVERIFY(!bar->isVisible()); window.setState(CallWindow::Ended);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "call settings verified: rendered_icon=40x36 buttons=48x48 spacing=4 source/FPS/menu switching passed");
|
||||
}
|
||||
void windowAction_data() {
|
||||
QTest::addColumn<QString>("scenario"); QTest::addColumn<QString>("action");
|
||||
QStringList failures{"unavailable", "stopped", "cancelled", "executed"};
|
||||
|
||||
Reference in new issue
Block a user