Warning, file /plasma/kwin/autotests/integration/xwaylandserver_crash_test.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

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