File indexing completed on 2024-05-05 17:35:46

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2018 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "kwin_wayland_test.h"
0011 
0012 #include "composite.h"
0013 #include "core/output.h"
0014 #include "core/outputbackend.h"
0015 #include "core/renderbackend.h"
0016 #include "deleted.h"
0017 #include "effectloader.h"
0018 #include "effects.h"
0019 #include "wayland_server.h"
0020 #include "window.h"
0021 #include "workspace.h"
0022 
0023 #include <KWayland/Client/surface.h>
0024 
0025 namespace KWin
0026 {
0027 
0028 static const QString s_socketName = QStringLiteral("wayland_test_kwin_dont_crash_reinitialize_compositor-0");
0029 
0030 class DontCrashReinitializeCompositorTest : public QObject
0031 {
0032     Q_OBJECT
0033 
0034 private Q_SLOTS:
0035     void initTestCase();
0036     void init();
0037     void cleanup();
0038 
0039     void testReinitializeCompositor_data();
0040     void testReinitializeCompositor();
0041 };
0042 
0043 void DontCrashReinitializeCompositorTest::initTestCase()
0044 {
0045     qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
0046 
0047     qRegisterMetaType<KWin::Window *>();
0048     qRegisterMetaType<KWin::Deleted *>();
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     auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
0054     KConfigGroup plugins(config, QStringLiteral("Plugins"));
0055     const auto builtinNames = EffectLoader().listOfKnownEffects();
0056     for (const QString &name : builtinNames) {
0057         plugins.writeEntry(name + QStringLiteral("Enabled"), false);
0058     }
0059     config->sync();
0060     kwinApp()->setConfig(config);
0061 
0062     qputenv("KWIN_COMPOSE", QByteArrayLiteral("O2"));
0063     qputenv("KWIN_EFFECTS_FORCE_ANIMATIONS", QByteArrayLiteral("1"));
0064 
0065     kwinApp()->start();
0066     QVERIFY(applicationStartedSpy.wait());
0067     const auto outputs = workspace()->outputs();
0068     QCOMPARE(outputs.count(), 2);
0069     QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
0070     QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024));
0071 
0072     QCOMPARE(Compositor::self()->backend()->compositingType(), KWin::OpenGLCompositing);
0073 }
0074 
0075 void DontCrashReinitializeCompositorTest::init()
0076 {
0077     QVERIFY(Test::setupWaylandConnection());
0078 }
0079 
0080 void DontCrashReinitializeCompositorTest::cleanup()
0081 {
0082     // Unload all effects.
0083     auto effectsImpl = qobject_cast<EffectsHandlerImpl *>(effects);
0084     QVERIFY(effectsImpl);
0085     effectsImpl->unloadAllEffects();
0086     QVERIFY(effectsImpl->loadedEffects().isEmpty());
0087 
0088     Test::destroyWaylandConnection();
0089 }
0090 
0091 void DontCrashReinitializeCompositorTest::testReinitializeCompositor_data()
0092 {
0093     QTest::addColumn<QString>("effectName");
0094 
0095     QTest::newRow("Fade") << QStringLiteral("kwin4_effect_fade");
0096     QTest::newRow("Glide") << QStringLiteral("glide");
0097     QTest::newRow("Scale") << QStringLiteral("kwin4_effect_scale");
0098 }
0099 
0100 void DontCrashReinitializeCompositorTest::testReinitializeCompositor()
0101 {
0102     // This test verifies that KWin doesn't crash when the compositor settings
0103     // have been changed while a scripted effect animates the disappearing of
0104     // a window.
0105 
0106     // Make sure that we have the right effects ptr.
0107     auto effectsImpl = qobject_cast<EffectsHandlerImpl *>(effects);
0108     QVERIFY(effectsImpl);
0109 
0110     // Create the test window.
0111     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0112     QVERIFY(surface != nullptr);
0113     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0114     QVERIFY(shellSurface != nullptr);
0115     Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0116     QVERIFY(window);
0117 
0118     // Make sure that only the test effect is loaded.
0119     QFETCH(QString, effectName);
0120     QVERIFY(effectsImpl->loadEffect(effectName));
0121     QCOMPARE(effectsImpl->loadedEffects().count(), 1);
0122     QCOMPARE(effectsImpl->loadedEffects().first(), effectName);
0123     Effect *effect = effectsImpl->findEffect(effectName);
0124     QVERIFY(effect);
0125     QVERIFY(!effect->isActive());
0126 
0127     // Close the test window.
0128     QSignalSpy windowClosedSpy(window, &Window::windowClosed);
0129     shellSurface.reset();
0130     surface.reset();
0131     QVERIFY(windowClosedSpy.wait());
0132 
0133     // The test effect should start animating the test window. Is there a better
0134     // way to verify that the test effect actually animates the test window?
0135     QVERIFY(effect->isActive());
0136 
0137     // Re-initialize the compositor, effects will be destroyed and created again.
0138     Compositor::self()->reinitialize();
0139 
0140     // By this time, KWin should still be alive.
0141 }
0142 
0143 } // namespace KWin
0144 
0145 WAYLANDTEST_MAIN(KWin::DontCrashReinitializeCompositorTest)
0146 #include "dont_crash_reinitialize_compositor.moc"