qt6-bb10/qtmultimedia/tests/auto/integration/qwindowcapturebackend/fixture.cpp

235 lines
7.4 KiB
C++

// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "fixture.h"
#include <qmediaplayer.h>
#include <qvideowidget.h>
#include <qsystemsemaphore.h>
#include <quuid.h>
DisableCursor::DisableCursor()
{
QCursor cursor(Qt::BlankCursor);
QApplication::setOverrideCursor(cursor);
}
DisableCursor::~DisableCursor()
{
QGuiApplication::restoreOverrideCursor();
}
WindowCaptureFixture::WindowCaptureFixture()
{
m_session.setWindowCapture(&m_capture);
m_session.setVideoSink(&m_grabber);
}
QString WindowCaptureFixture::getResultsPath(const QString &fileName)
{
const QString sep = QStringLiteral("--");
QString stem = QCoreApplication::applicationName();
if (const char *currentTest = QTest::currentTestFunction())
stem += sep + QString::fromLatin1(currentTest);
if (const char *currentTag = QTest::currentDataTag())
stem += sep + QString::fromLatin1(currentTag);
stem += sep + fileName;
const QDir resultsDir = qEnvironmentVariable("COIN_CTEST_RESULTSDIR", QDir::tempPath());
return resultsDir.filePath(stem);
}
bool WindowCaptureFixture::compareImages(QImage actual, const QImage &expected,
const QString &fileSuffix)
{
// Convert to same format so that we can compare images
actual = actual.convertToFormat(expected.format());
if (actual == expected)
return true;
qWarning() << "Image comparison failed.";
qWarning() << "Actual image:";
qWarning() << actual;
qWarning() << "Expected image:";
qWarning() << expected;
const QString actualName = getResultsPath(QStringLiteral("actual%1.png").arg(fileSuffix));
if (!actual.save(actualName))
qWarning() << "Failed to save actual file to " << actualName;
const QString expectedName = getResultsPath(QStringLiteral("expected%1.png").arg(fileSuffix));
if (!expected.save(expectedName))
qWarning() << "Failed to save expected file to " << expectedName;
return false;
}
bool WindowCaptureWithWidgetFixture::start(QSize size)
{
// In case of window capture failure, signal the grabber so we can stop
// waiting for frames that will never come.
connect(&m_capture, &QWindowCapture::errorOccurred, &m_grabber, &FrameGrabber::stop);
m_widget.setSize(size);
m_widget.show();
// Make sure window is in a state that allows it to be found by QWindowCapture.
// Not necessary on Windows, but seems to be necessary on some platforms.
if (!QTest::qWaitForWindowExposed(&m_widget, static_cast<int>(s_testTimeout.count()))) {
qWarning() << "Failed to display widget within timeout";
return false;
}
m_captureWindow = findCaptureWindow(m_widget.windowTitle());
if (!m_captureWindow.isValid())
return false;
m_capture.setWindow(m_captureWindow);
m_capture.setActive(true);
return true;
}
QVideoFrame WindowCaptureWithWidgetFixture::waitForFrame(qint64 noOlderThanTime)
{
const std::vector<QVideoFrame> frames = m_grabber.waitAndTakeFrames(1u, noOlderThanTime);
if (frames.empty())
return QVideoFrame{};
return frames.back();
}
QCapturableWindow WindowCaptureWithWidgetFixture::findCaptureWindow(const QString &windowTitle)
{
QList<QCapturableWindow> allWindows = QWindowCapture::capturableWindows();
const auto window = std::find_if(allWindows.begin(), allWindows.end(),
[windowTitle](const QCapturableWindow &win) {
return win.description() == windowTitle;
});
// Extra debug output to help understanding if test widget window could not be found
if (window == allWindows.end()) {
qDebug() << "Could not find window" << windowTitle << ". Existing capturable windows:";
std::for_each(allWindows.begin(), allWindows.end(), [](const QCapturableWindow &win) {
qDebug() << " " << win.description();
});
return QCapturableWindow{};
}
return *window;
}
void WindowCaptureWithWidgetAndRecorderFixture::start(QSize size, bool togglePattern)
{
if (togglePattern) {
// Drive animation
connect(&m_grabber, &FrameGrabber::videoFrameChanged, &m_widget,
&TestWidget::togglePattern);
}
connect(&m_recorder, &QMediaRecorder::recorderStateChanged, this,
&WindowCaptureWithWidgetAndRecorderFixture::recorderStateChanged);
m_session.setRecorder(&m_recorder);
m_recorder.setQuality(QMediaRecorder::HighQuality);
m_recorder.setOutputLocation(QUrl::fromLocalFile(m_mediaFile));
m_recorder.setVideoResolution(size);
WindowCaptureWithWidgetFixture::start(size);
m_recorder.record();
}
bool WindowCaptureWithWidgetAndRecorderFixture::stop()
{
m_recorder.stop();
const auto recorderStopped = [this] { return m_recorderState == QMediaRecorder::StoppedState; };
return QTest::qWaitFor(recorderStopped, s_testTimeout);
}
bool WindowCaptureWithWidgetAndRecorderFixture::testVideoFilePlayback(const QString &fileName)
{
QVideoWidget widget;
QMediaPlayer player;
bool playing = true;
connect(&player, &QMediaPlayer::playbackStateChanged, this,
[&](QMediaPlayer::PlaybackState state) {
if (state == QMediaPlayer::StoppedState)
playing = false;
});
QMediaPlayer::Error error = QMediaPlayer::NoError;
connect(&player, &QMediaPlayer::errorOccurred, this,
[&](QMediaPlayer::Error e, const QString &errorString) {
error = e;
qWarning() << errorString;
});
player.setSource(QUrl{ fileName });
player.setVideoOutput(&widget);
widget.show();
player.play();
const bool completed = QTest::qWaitFor(
[&] { return !playing || error != QMediaPlayer::NoError; }, s_testTimeout);
return completed && error == QMediaPlayer::NoError;
}
void WindowCaptureWithWidgetAndRecorderFixture::recorderStateChanged(
QMediaRecorder::RecorderState state)
{
m_recorderState = state;
}
bool WindowCaptureWithWidgetInOtherProcessFixture::start()
{
// In case of window capture failure, signal the grabber so we can stop
// waiting for frames that will never come.
connect(&m_capture, &QWindowCapture::errorOccurred, &m_grabber, &FrameGrabber::stop);
// Create a new window title that is also used as a semaphore key with less than 30 characters
const QString windowTitle = QString::number(qHash(QUuid::createUuid().toString()));
QSystemSemaphore windowVisible{ QNativeIpcKey{ windowTitle } };
// Start another instance of the test executable and ask it to show a
// its test widget.
m_windowProcess.setArguments({ "--show", windowTitle });
m_windowProcess.setProgram(QApplication::applicationFilePath());
m_windowProcess.start();
// Make sure window is in a state that allows it to be found by QWindowCapture.
// We do this by waiting for the process to release the semaphore once its window is visible
windowVisible.acquire();
m_captureWindow = findCaptureWindow(windowTitle);
if (!m_captureWindow.isValid())
return false;
// Start capturing the out-of-process window
m_capture.setWindow(m_captureWindow);
m_capture.setActive(true);
// Show in-process widget used to create a reference image
m_widget.show();
return true;
}
#include "moc_fixture.cpp"