File indexing completed on 2024-05-19 09:23:23

0001 /*
0002     SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "kwin_wayland_test.h"
0008 
0009 #include "compositor.h"
0010 #include "main.h"
0011 #include "scene/workspacescene.h"
0012 #include "wayland_server.h"
0013 #include "workspace.h"
0014 #include "x11window.h"
0015 #include "xwayland/xwayland.h"
0016 #include "xwayland/xwaylandlauncher.h"
0017 
0018 #include <xcb/xcb_icccm.h>
0019 
0020 namespace KWin
0021 {
0022 
0023 static const QString s_socketName = QStringLiteral("wayland_test_kwin_xwayland_server_restart-0");
0024 
0025 class XwaylandServerRestartTest : public QObject
0026 {
0027     Q_OBJECT
0028 
0029 private Q_SLOTS:
0030     void initTestCase();
0031     void testRestart();
0032 };
0033 
0034 void XwaylandServerRestartTest::initTestCase()
0035 {
0036     QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
0037     QVERIFY(waylandServer()->init(s_socketName));
0038     Test::setOutputConfig({
0039         QRect(0, 0, 1280, 1024),
0040         QRect(1280, 0, 1280, 1024),
0041     });
0042 
0043     KSharedConfig::Ptr config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
0044     KConfigGroup xwaylandGroup = config->group(QStringLiteral("Xwayland"));
0045     xwaylandGroup.writeEntry(QStringLiteral("XwaylandCrashPolicy"), QStringLiteral("Restart"));
0046     xwaylandGroup.sync();
0047     kwinApp()->setConfig(config);
0048 
0049     kwinApp()->start();
0050     QVERIFY(applicationStartedSpy.wait());
0051 }
0052 
0053 static void kwin_safe_kill(QProcess *process)
0054 {
0055     // The SIGKILL signal must be sent when the event loop is spinning.
0056     QTimer::singleShot(1, process, &QProcess::kill);
0057 }
0058 
0059 void XwaylandServerRestartTest::testRestart()
0060 {
0061     // This test verifies that the Xwayland server will be restarted after a crash.
0062 
0063     Xwl::Xwayland *xwayland = static_cast<Xwl::Xwayland *>(kwinApp()->xwayland());
0064 
0065     QSignalSpy startedSpy(xwayland, &Xwl::Xwayland::started);
0066     QSignalSpy stoppedSpy(xwayland, &Xwl::Xwayland::errorOccurred);
0067 
0068     Test::createX11Connection(); // trigger an X11 start
0069     QTRY_COMPARE(startedSpy.count(), 1);
0070 
0071     // Pretend that the Xwayland process has crashed by sending a SIGKILL to it.
0072     kwin_safe_kill(xwayland->xwaylandLauncher()->process());
0073 
0074     QTRY_COMPARE(stoppedSpy.count(), 1);
0075 
0076     // Check that the compositor still accepts new X11 clients.
0077     Test::XcbConnectionPtr c = Test::createX11Connection();
0078     QVERIFY(!xcb_connection_has_error(c.get()));
0079 
0080     QTRY_COMPARE(startedSpy.count(), 2);
0081     const QRect rect(0, 0, 100, 200);
0082     xcb_window_t windowId = xcb_generate_id(c.get());
0083     xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
0084                       rect.x(), rect.y(), rect.width(), rect.height(), 0,
0085                       XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
0086     xcb_size_hints_t hints;
0087     memset(&hints, 0, sizeof(hints));
0088     xcb_icccm_size_hints_set_position(&hints, 1, rect.x(), rect.y());
0089     xcb_icccm_size_hints_set_size(&hints, 1, rect.width(), rect.height());
0090     xcb_icccm_size_hints_set_min_size(&hints, rect.width(), rect.height());
0091     xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
0092     xcb_map_window(c.get(), windowId);
0093     xcb_flush(c.get());
0094 
0095     QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
0096     QVERIFY(windowCreatedSpy.wait());
0097     X11Window *window = windowCreatedSpy.last().first().value<X11Window *>();
0098     QVERIFY(window);
0099     QCOMPARE(window->window(), windowId);
0100     QVERIFY(window->isDecorated());
0101 
0102     // Render a frame to ensure that the compositor doesn't crash.
0103     Compositor::self()->scene()->addRepaintFull();
0104     QSignalSpy frameRenderedSpy(Compositor::self()->scene(), &WorkspaceScene::frameRendered);
0105     QVERIFY(frameRenderedSpy.wait());
0106 
0107     // Destroy the test window.
0108     xcb_destroy_window(c.get(), windowId);
0109     xcb_flush(c.get());
0110     QVERIFY(Test::waitForWindowClosed(window));
0111 }
0112 
0113 } // namespace KWin
0114 
0115 WAYLANDTEST_MAIN(KWin::XwaylandServerRestartTest)
0116 #include "xwaylandserver_restart_test.moc"