kwin/autotests/integration/screencasting_test.cpp
Vlad Zahorodnii abd790c4b5 autotests: Skip testScreencasting in CI
pipewire 1.2 has a known regression where the client's process callback
will not be triggered if the producer has sent only one buffer.

The issue has already been fixed upstream
525360d70a

Until our CI picks up that change, temporarily disable the test to unblock
merging patches.
2024-07-11 09:34:34 +00:00

184 lines
6.6 KiB
C++

/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2023 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "compositor.h"
#include "core/output.h"
#include "generic_scene_opengl_test.h"
#include "opengl/glplatform.h"
#include "pointer_input.h"
#include "scene/workspacescene.h"
#include "wayland_server.h"
#include "window.h"
#include "workspace.h"
#include <KWayland/Client/output.h>
#include <KWayland/Client/subsurface.h>
#include <KWayland/Client/surface.h>
#include <PipeWireSourceStream>
#include <QPainter>
#include <QScreen>
#define QCOMPAREIMG(actual, expected, id) \
{ \
if ((actual) != (expected)) { \
const auto actualFile = QStringLiteral("appium_artifact_actual_%1.png").arg(id); \
const auto expectedFile = QStringLiteral("appium_artifact_expected_%1.png").arg(id); \
(actual).save(actualFile); \
(expected).save(expectedFile); \
qDebug() << "Generated failed file" << actualFile << expectedFile; \
} \
QCOMPARE(actual, expected); \
}
namespace KWin
{
static const QString s_socketName = QStringLiteral("wayland_test_buffer_size_change-0");
class ScreencastingTest : public GenericSceneOpenGLTest
{
Q_OBJECT
public:
ScreencastingTest()
: GenericSceneOpenGLTest(QByteArrayLiteral("O2"))
{
auto wrap = [this](const QString &process, const QStringList &arguments = {}) {
// Make sure PipeWire is running. If it's already running it will just exit
QProcess *p = new QProcess(this);
p->setProcessChannelMode(QProcess::MergedChannels);
p->setArguments(arguments);
connect(this, &QObject::destroyed, p, [p] {
p->terminate();
p->waitForFinished();
p->kill();
});
connect(p, &QProcess::errorOccurred, p, [p](auto status) {
qDebug() << "error" << status << p->program();
});
connect(p, &QProcess::finished, p, [p](int code, auto status) {
if (code != 0) {
qDebug() << p->readAll();
}
qDebug() << "finished" << code << status << p->program();
});
p->setProgram(process);
p->start();
};
// If I run this outside the CI, it breaks the system's pipewire
if (qgetenv("KDECI_BUILD") == "TRUE") {
wrap("pipewire");
wrap("dbus-launch", {"wireplumber"});
}
}
private Q_SLOTS:
void init();
void testWindowCasting();
void testOutputCasting();
private:
std::optional<QImage> oneFrameAndClose(Test::ScreencastingStreamV1 *stream);
};
void ScreencastingTest::init()
{
if (qgetenv("KDECI_BUILD") == "TRUE") {
QSKIP("CI has pipewire 1.2 that has known process callback issues"); // TODO: Remove it later when CI ships pipewire 1.2 with the fix
}
QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::ScreencastingV1));
QVERIFY(KWin::Test::screencasting());
Cursors::self()->hideCursor();
}
std::optional<QImage> ScreencastingTest::oneFrameAndClose(Test::ScreencastingStreamV1 *stream)
{
Q_ASSERT(stream);
PipeWireSourceStream pwStream;
qDebug() << "start" << stream;
connect(stream, &Test::ScreencastingStreamV1::failed, qGuiApp, [](const QString &error) {
qDebug() << "stream failed with error" << error;
Q_ASSERT(false);
});
connect(stream, &Test::ScreencastingStreamV1::closed, qGuiApp, [&pwStream] {
pwStream.setActive(false);
});
connect(stream, &Test::ScreencastingStreamV1::created, qGuiApp, [&pwStream](quint32 nodeId) {
pwStream.createStream(nodeId, 0);
});
std::optional<QImage> img;
connect(&pwStream, &PipeWireSourceStream::frameReceived, qGuiApp, [&img](const PipeWireFrame &frame) {
if (frame.dataFrame) {
img = frame.dataFrame->toImage();
}
});
QSignalSpy spy(&pwStream, &PipeWireSourceStream::frameReceived);
if (!spy.wait()) {
qDebug() << "Did not receive any frames";
}
pwStream.stopStreaming();
return img;
}
void ScreencastingTest::testWindowCasting()
{
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
QVERIFY(surface != nullptr);
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
QVERIFY(shellSurface != nullptr);
QImage sourceImage(QSize(30, 10), QImage::Format_RGBA8888_Premultiplied);
sourceImage.fill(Qt::red);
Window *window = Test::renderAndWaitForShown(surface.get(), sourceImage);
QVERIFY(window);
auto stream = KWin::Test::screencasting()->createWindowStream(window->internalId().toString(), QtWayland::zkde_screencast_unstable_v1::pointer_hidden);
std::optional<QImage> img = oneFrameAndClose(stream);
QVERIFY(img);
img->convertTo(sourceImage.format());
QCOMPAREIMG(*img, sourceImage, QLatin1String("window_cast"));
}
void ScreencastingTest::testOutputCasting()
{
auto theOutput = KWin::Test::waylandOutputs().constFirst();
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
QVERIFY(surface != nullptr);
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
QVERIFY(shellSurface != nullptr);
QImage sourceImage(theOutput->pixelSize(), QImage::Format_RGBA8888_Premultiplied);
sourceImage.fill(Qt::green);
{
QPainter p(&sourceImage);
p.drawRect(100, 100, 100, 100);
}
Window *window = Test::renderAndWaitForShown(surface.get(), sourceImage);
QVERIFY(window);
QCOMPARE(window->frameGeometry(), window->output()->geometry());
auto stream = KWin::Test::screencasting()->createOutputStream(theOutput->output(), QtWayland::zkde_screencast_unstable_v1::pointer_hidden);
std::optional<QImage> img = oneFrameAndClose(stream);
QVERIFY(img);
img->convertTo(sourceImage.format());
QCOMPAREIMG(*img, sourceImage, QLatin1String("output_cast"));
}
}
WAYLANDTEST_MAIN(KWin::ScreencastingTest)
#include "screencasting_test.moc"