File indexing completed on 2024-11-10 04:56:12

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 "core/output.h"
0011 #include "main.h"
0012 #include "scene/workspacescene.h"
0013 #include "wayland_server.h"
0014 #include "workspace.h"
0015 #include "x11window.h"
0016 #include "xwayland/xwayland.h"
0017 #include "xwayland/xwaylandlauncher.h"
0018 
0019 #include <xcb/xcb_icccm.h>
0020 
0021 namespace KWin
0022 {
0023 
0024 static const QString s_socketName = QStringLiteral("wayland_test_kwin_xwayland_server_crash-0");
0025 
0026 class XwaylandServerCrashTest : public QObject
0027 {
0028     Q_OBJECT
0029 
0030 private Q_SLOTS:
0031     void initTestCase();
0032     void testCrash();
0033 };
0034 
0035 void XwaylandServerCrashTest::initTestCase()
0036 {
0037     qRegisterMetaType<X11Window *>();
0038     QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
0039     QVERIFY(waylandServer()->init(s_socketName));
0040     Test::setOutputConfig({
0041         QRect(0, 0, 1280, 1024),
0042         QRect(1280, 0, 1280, 1024),
0043     });
0044 
0045     KSharedConfig::Ptr config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
0046     KConfigGroup xwaylandGroup = config->group(QStringLiteral("Xwayland"));
0047     xwaylandGroup.writeEntry(QStringLiteral("XwaylandCrashPolicy"), QStringLiteral("Stop"));
0048     xwaylandGroup.sync();
0049     kwinApp()->setConfig(config);
0050 
0051     kwinApp()->start();
0052     QVERIFY(applicationStartedSpy.wait());
0053     const auto outputs = workspace()->outputs();
0054     QCOMPARE(outputs.count(), 2);
0055     QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
0056     QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024));
0057 }
0058 
0059 void XwaylandServerCrashTest::testCrash()
0060 {
0061     // This test verifies that all connected X11 clients get destroyed when Xwayland crashes.
0062 
0063     // Create a normal window.
0064     Test::XcbConnectionPtr c = Test::createX11Connection();
0065     QVERIFY(!xcb_connection_has_error(c.get()));
0066     const QRect windowGeometry(0, 0, 100, 200);
0067     xcb_window_t windowId1 = xcb_generate_id(c.get());
0068     xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId1, rootWindow(),
0069                       windowGeometry.x(),
0070                       windowGeometry.y(),
0071                       windowGeometry.width(),
0072                       windowGeometry.height(),
0073                       0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
0074     xcb_size_hints_t hints;
0075     memset(&hints, 0, sizeof(hints));
0076     xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
0077     xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
0078     xcb_icccm_size_hints_set_min_size(&hints, windowGeometry.width(), windowGeometry.height());
0079     xcb_icccm_set_wm_normal_hints(c.get(), windowId1, &hints);
0080     xcb_map_window(c.get(), windowId1);
0081     xcb_flush(c.get());
0082 
0083     QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
0084     QVERIFY(windowCreatedSpy.wait());
0085     QPointer<X11Window> window = windowCreatedSpy.last().first().value<X11Window *>();
0086     QVERIFY(window);
0087     QVERIFY(window->isDecorated());
0088 
0089     // Create an override-redirect window.
0090     xcb_window_t windowId2 = xcb_generate_id(c.get());
0091     const uint32_t values[] = {true};
0092     xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId2, rootWindow(),
0093                       windowGeometry.x(), windowGeometry.y(),
0094                       windowGeometry.width(), windowGeometry.height(), 0,
0095                       XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT,
0096                       XCB_CW_OVERRIDE_REDIRECT, values);
0097     xcb_map_window(c.get(), windowId2);
0098     xcb_flush(c.get());
0099 
0100     QSignalSpy unmanagedAddedSpy(workspace(), &Workspace::windowAdded);
0101     QVERIFY(unmanagedAddedSpy.wait());
0102     QPointer<X11Window> unmanaged = unmanagedAddedSpy.last().first().value<X11Window *>();
0103     QVERIFY(unmanaged);
0104 
0105     // Let's pretend that the Xwayland process has crashed.
0106     QSignalSpy x11ConnectionChangedSpy(kwinApp(), &Application::x11ConnectionChanged);
0107     Xwl::Xwayland *xwayland = static_cast<Xwl::Xwayland *>(kwinApp()->xwayland());
0108     xwayland->xwaylandLauncher()->process()->terminate();
0109     QVERIFY(x11ConnectionChangedSpy.wait());
0110 
0111     // When Xwayland crashes, the compositor should tear down the XCB connection and destroy
0112     // all connected X11 clients.
0113     QTRY_VERIFY(!window);
0114     QTRY_VERIFY(!unmanaged);
0115     QCOMPARE(kwinApp()->x11Connection(), nullptr);
0116     QCOMPARE(kwinApp()->x11RootWindow(), XCB_WINDOW_NONE);
0117 
0118     // Render a frame to ensure that the compositor doesn't crash.
0119     Compositor::self()->scene()->addRepaintFull();
0120     QSignalSpy frameRenderedSpy(Compositor::self()->scene(), &WorkspaceScene::frameRendered);
0121     QVERIFY(frameRenderedSpy.wait());
0122 }
0123 
0124 } // namespace KWin
0125 
0126 WAYLANDTEST_MAIN(KWin::XwaylandServerCrashTest)
0127 #include "xwaylandserver_crash_test.moc"