Warning, file /plasma/plasma-workspace/libtaskmanager/autotests/xwindowtasksmodeltest.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: 2022 Fushan Wen <qydwhotmail@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include <array>
0008 
0009 #include <QX11Info>
0010 
0011 #include <KActivities/Consumer>
0012 
0013 #include <xcb/xcb.h>
0014 #include <xcb/xproto.h>
0015 
0016 #include "common.h"
0017 #include "xwindowtasksmodel.h"
0018 
0019 using namespace TaskManager;
0020 using MimeDataMap = QMap<QString, QByteArray>;
0021 
0022 class XWindowTasksModelTest : public QObject
0023 {
0024     Q_OBJECT
0025 
0026 private Q_SLOTS:
0027     void initTestCase();
0028     void cleanupTestCase();
0029 
0030     void test_winIdFromMimeData_data();
0031     void test_winIdFromMimeData();
0032     void test_winIdsFromMimeData_data();
0033     void test_winIdsFromMimeData();
0034 
0035     void test_openCloseWindow();
0036     void test_modelData();
0037     void test_isMinimized();
0038     void test_fullscreen();
0039     void test_geometry();
0040     void test_stackingOrder();
0041     void test_lastActivated();
0042     void test_modelDataFromDesktopFile();
0043     void test_windowState();
0044 
0045     void test_request();
0046 
0047 private:
0048     WId m_WId = 12345;
0049     QByteArray m_singleWId;
0050     QByteArray m_threeWIds;
0051     QByteArray m_negativeCountWIds;
0052     QByteArray m_insufficientWIds;
0053 
0054     XWindowTasksModel m_model;
0055 };
0056 
0057 void XWindowTasksModelTest::initTestCase()
0058 {
0059     if (!KWindowSystem::isPlatformX11()) {
0060         QSKIP("Test is not running on X11.");
0061     }
0062 
0063     char *singleWIdData = new char[sizeof(WId)];
0064     memcpy(singleWIdData, &m_WId, sizeof(WId));
0065     m_singleWId = QByteArray(singleWIdData, sizeof(WId));
0066     delete[] singleWIdData;
0067 
0068     constexpr int count = 3;
0069     char *threeWIdsData = new char[sizeof(int) + sizeof(WId) * count];
0070     memcpy(threeWIdsData, &count, sizeof(int));
0071     for (int i = 0; i < count; ++i) {
0072         memcpy(threeWIdsData + sizeof(int) + sizeof(WId) * i, &m_WId, sizeof(WId));
0073     }
0074     m_threeWIds = QByteArray(threeWIdsData, sizeof(int) + sizeof(WId) * count);
0075     delete[] threeWIdsData;
0076 
0077     constexpr int negativeCount = -count;
0078     char *negativeWIdsData = new char[sizeof(int) + sizeof(WId) * count];
0079     memcpy(negativeWIdsData, &negativeCount, sizeof(int));
0080     for (int i = 0; i < count; ++i) {
0081         memcpy(negativeWIdsData + sizeof(int) + sizeof(WId) * i, &m_WId, sizeof(WId));
0082     }
0083     m_negativeCountWIds = QByteArray(negativeWIdsData, sizeof(int) + sizeof(WId) * count);
0084     delete[] negativeWIdsData;
0085 
0086     constexpr int insufficientCount = count - 1;
0087     char *insufficientWIdsData = new char[sizeof(int) + sizeof(WId) * insufficientCount];
0088     memcpy(insufficientWIdsData, &count, sizeof(int));
0089     for (int i = 0; i < insufficientCount; ++i) {
0090         memcpy(insufficientWIdsData + sizeof(int) + sizeof(WId) * i, &m_WId, sizeof(WId));
0091     }
0092     m_insufficientWIds = QByteArray(insufficientWIdsData, sizeof(int) + sizeof(WId) * insufficientCount);
0093     delete[] insufficientWIdsData;
0094 
0095     QGuiApplication::setQuitOnLastWindowClosed(false);
0096 
0097     QStandardPaths::setTestModeEnabled(true);
0098 
0099     const QString applicationDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QDir::separator() + QStringLiteral("applications");
0100     QDir dir;
0101     if (!dir.exists(applicationDir)) {
0102         dir.mkpath(applicationDir);
0103     }
0104 }
0105 
0106 void XWindowTasksModelTest::cleanupTestCase()
0107 {
0108     TestUtils::cleanupTestCase();
0109 }
0110 
0111 void XWindowTasksModelTest::test_winIdFromMimeData_data()
0112 {
0113     QTest::addColumn<QString>("mimeType");
0114     QTest::addColumn<QByteArray>("data");
0115     QTest::addColumn<bool>("isOK");
0116 
0117     QTest::newRow("Valid WId") << QStringLiteral("windowsystem/winid") // XWindowTasksModel::Private::mimeType()
0118                                << m_singleWId << true;
0119     QTest::newRow("Invalid WId") << QStringLiteral("windowsystem/winid") << QByteArray("\x06") << false;
0120     QTest::newRow("Invalid mimeType") << QStringLiteral("text/plain") << m_singleWId << false;
0121 }
0122 
0123 void XWindowTasksModelTest::test_winIdFromMimeData()
0124 {
0125     QFETCH(QString, mimeType);
0126     QFETCH(QByteArray, data);
0127     QFETCH(bool, isOK);
0128 
0129     QMimeData mimeData;
0130     mimeData.setData(mimeType, data);
0131     qDebug() << data << "data size:" << data.size();
0132 
0133     bool ok = !isOK;
0134     WId result = XWindowTasksModel::winIdFromMimeData(&mimeData, &ok);
0135     QCOMPARE(ok, isOK);
0136     if (isOK) {
0137         QCOMPARE(result, m_WId);
0138     }
0139 }
0140 
0141 void XWindowTasksModelTest::test_winIdsFromMimeData_data()
0142 {
0143     QTest::addColumn<MimeDataMap>("mimeDataMap");
0144     QTest::addColumn<bool>("isOK");
0145     QTest::addColumn<int>("count");
0146 
0147     // if (!mimeData->hasFormat(Private::groupMimeType()))
0148     MimeDataMap data1;
0149     data1.insert(QStringLiteral("windowsystem/winid"), m_singleWId);
0150     QTest::newRow("Single WId") << data1 << true << data1.size();
0151 
0152     MimeDataMap data2;
0153     data2.insert(QStringLiteral("windowsystem/winid"), QByteArray("\x06"));
0154     QTest::newRow("Invalid WId") << data2 << false << 0;
0155 
0156     MimeDataMap data3;
0157     data3.insert(QStringLiteral("windowsystem/multiple-winids"), m_threeWIds);
0158     QTest::newRow("Three WIds") << data3 << true << 3;
0159 
0160     // if ((unsigned int)data.size() < sizeof(int) + sizeof(WId))
0161     MimeDataMap data4;
0162     data4.insert(QStringLiteral("windowsystem/multiple-winids"), QByteArray("\x06"));
0163     data4.insert(QStringLiteral("windowsystem/winid"), m_singleWId);
0164     QTest::newRow("Invalid WIds") << data4 << false << 0;
0165 
0166     // count < 1
0167     MimeDataMap data5;
0168     data5.insert(QStringLiteral("windowsystem/multiple-winids"), m_negativeCountWIds);
0169     QTest::newRow("Negative count WIds") << data5 << false << 0;
0170 
0171     // (unsigned int)data.size() < sizeof(int) + sizeof(WId) * count
0172     MimeDataMap data6;
0173     data6.insert(QStringLiteral("windowsystem/multiple-winids"), m_insufficientWIds);
0174     QTest::newRow("Insufficient count WIds") << data6 << false << 0;
0175 }
0176 
0177 void XWindowTasksModelTest::test_winIdsFromMimeData()
0178 {
0179     QFETCH(MimeDataMap, mimeDataMap);
0180     QFETCH(bool, isOK);
0181     QFETCH(int, count);
0182 
0183     QMimeData mimeData;
0184     for (auto it = mimeDataMap.cbegin(); it != mimeDataMap.cend(); it = std::next(it)) {
0185         mimeData.setData(it.key(), it.value());
0186     }
0187 
0188     bool ok = !isOK;
0189     auto results = XWindowTasksModel::winIdsFromMimeData(&mimeData, &ok);
0190     QCOMPARE(ok, isOK);
0191     QCOMPARE(results.size(), count);
0192     if (count > 0) {
0193         const bool verified = std::all_of(results.cbegin(), results.cend(), [this](WId id) {
0194             return id == m_WId;
0195         });
0196         QVERIFY(verified);
0197     }
0198 }
0199 
0200 void XWindowTasksModelTest::test_openCloseWindow()
0201 {
0202     TestUtils::testOpenCloseWindow(m_model);
0203 }
0204 
0205 void XWindowTasksModelTest::test_modelData()
0206 {
0207     const QString title = QStringLiteral("__testwindow__%1").arg(QDateTime::currentDateTime().toString());
0208     QModelIndex index;
0209     auto window = TestUtils::createSingleWindow(m_model, title, index);
0210 
0211     // See XWindowTasksModel::data for available roles
0212     { // BEGIN Icon
0213         QSignalSpy dataChangedSpy(&m_model, &XWindowTasksModel::dataChanged);
0214         qDebug() << "Start testing Qt::DecorationRole";
0215         const QIcon oldWindowIcon = index.data(Qt::DecorationRole).value<QIcon>(); // X11 icon
0216         QVERIFY(!oldWindowIcon.isNull());
0217         window->setIcon(QIcon(QFINDTESTDATA("data/windows/samplewidgetwindow.png")));
0218         QVERIFY(dataChangedSpy.wait());
0219         QVERIFY(dataChangedSpy.takeLast().at(2).value<QVector<int>>().contains(Qt::DecorationRole));
0220 
0221         const QIcon newWindowIcon = index.data(Qt::DecorationRole).value<QIcon>();
0222         QVERIFY(!newWindowIcon.isNull());
0223         QVERIFY(oldWindowIcon.pixmap(KIconLoader::SizeLarge).toImage().pixelColor(KIconLoader::SizeLarge / 2, KIconLoader::SizeLarge / 2)
0224                 != newWindowIcon.pixmap(KIconLoader::SizeLarge).toImage().pixelColor(KIconLoader::SizeLarge / 2, KIconLoader::SizeLarge / 2));
0225 
0226         window->setIcon(QIcon());
0227         QVERIFY(dataChangedSpy.wait());
0228         QVERIFY(dataChangedSpy.takeLast().at(2).value<QVector<int>>().contains(Qt::DecorationRole));
0229     } // END Icon
0230 
0231     const NET::Properties windowInfoFlags =
0232         NET::WMState | NET::XAWMState | NET::WMDesktop | NET::WMVisibleName | NET::WMGeometry | NET::WMFrameExtents | NET::WMWindowType | NET::WMPid;
0233     const NET::Properties2 windowInfoFlags2 = NET::WM2DesktopFileName | NET::WM2Activities | NET::WM2WindowClass | NET::WM2AllowedActions
0234         | NET::WM2AppMenuObjectPath | NET::WM2AppMenuServiceName | NET::WM2GTKApplicationId;
0235     KWindowInfo info(window->winId(), windowInfoFlags, windowInfoFlags2);
0236 
0237     QTRY_COMPARE(index.data(AbstractTasksModel::AppId).toString(), info.windowClassClass());
0238     QTRY_COMPARE(index.data(AbstractTasksModel::AppName).toString(), info.windowClassClass());
0239     QTRY_COMPARE(index.data(AbstractTasksModel::GenericName).toString(), QString());
0240     QTRY_VERIFY(index.data(AbstractTasksModel::LauncherUrl).toUrl().toLocalFile().endsWith(info.windowClassClass()));
0241     QTRY_VERIFY(index.data(AbstractTasksModel::LauncherUrlWithoutIcon).toUrl().toLocalFile().endsWith(info.windowClassClass()));
0242     QTRY_COMPARE(index.data(AbstractTasksModel::WinIdList).toList().size(), 1);
0243     QTRY_COMPARE(index.data(AbstractTasksModel::MimeType).toString(), QStringLiteral("windowsystem/winid"));
0244 
0245     {
0246         QMimeData mimeData;
0247         mimeData.setData(index.data(AbstractTasksModel::MimeType).toString(), index.data(AbstractTasksModel::MimeData).toByteArray());
0248         bool ok = false;
0249         XWindowTasksModel::winIdFromMimeData(&mimeData, &ok);
0250         QVERIFY(ok);
0251     }
0252 
0253     QTRY_VERIFY(index.data(AbstractTasksModel::IsWindow).toBool());
0254     QTRY_VERIFY(index.data(AbstractTasksModel::IsActive).toBool());
0255 
0256     QTRY_VERIFY(index.data(AbstractTasksModel::IsClosable).toBool());
0257     QTRY_VERIFY(index.data(AbstractTasksModel::IsMovable).toBool());
0258     QTRY_VERIFY(index.data(AbstractTasksModel::IsResizable).toBool());
0259     QTRY_VERIFY(index.data(AbstractTasksModel::IsMaximizable).toBool());
0260     QTRY_VERIFY(!index.data(AbstractTasksModel::IsMaximized).toBool());
0261     QTRY_VERIFY(index.data(AbstractTasksModel::IsMinimizable).toBool());
0262     QTRY_VERIFY(!index.data(AbstractTasksModel::IsKeepAbove).toBool());
0263     QTRY_VERIFY(!index.data(AbstractTasksModel::IsKeepBelow).toBool());
0264     QTRY_VERIFY(index.data(AbstractTasksModel::IsFullScreenable).toBool());
0265     QTRY_VERIFY(!index.data(AbstractTasksModel::IsFullScreen).toBool());
0266     QTRY_VERIFY(index.data(AbstractTasksModel::IsShadeable).toBool());
0267     QTRY_VERIFY(!index.data(AbstractTasksModel::IsShaded).toBool());
0268     QTRY_VERIFY(index.data(AbstractTasksModel::IsVirtualDesktopsChangeable).toBool());
0269     QTRY_VERIFY(!index.data(AbstractTasksModel::IsOnAllVirtualDesktops).toBool());
0270 
0271     // Due to window decoration, the size of a window can't be determined accurately
0272     const QRect screenGeometry = index.data(AbstractTasksModel::ScreenGeometry).toRect();
0273     QVERIFY(screenGeometry.width() > 0 && screenGeometry.height() > 0);
0274 
0275     KActivities::Consumer activityConsumer;
0276     qDebug() << "Start testing AbstractTasksModel::Activities. Current activity number:" << activityConsumer.runningActivities().size();
0277     if (activityConsumer.runningActivities().size() > 0) {
0278         QCOMPARE(index.data(AbstractTasksModel::Activities).toStringList(), info.activities());
0279     } else {
0280         // In CI the window manager is openbox, so there could be no running activity.
0281         QCOMPARE(index.data(AbstractTasksModel::Activities).toStringList().size(), 0);
0282     }
0283 
0284     QTRY_VERIFY(!index.data(AbstractTasksModel::IsDemandingAttention).toBool());
0285     QTRY_VERIFY(!index.data(AbstractTasksModel::SkipTaskbar).toBool());
0286     QTRY_VERIFY(!index.data(AbstractTasksModel::SkipPager).toBool());
0287     QTRY_COMPARE(index.data(AbstractTasksModel::AppPid).toInt(), info.pid());
0288 
0289     QVERIFY(index.data(AbstractTasksModel::CanLaunchNewInstance).toBool());
0290 }
0291 
0292 void XWindowTasksModelTest::test_isMinimized()
0293 {
0294     const QString title = QStringLiteral("__testwindow__%1").arg(QDateTime::currentDateTime().toString());
0295     QModelIndex index;
0296     auto window = TestUtils::createSingleWindow(m_model, title, index);
0297 
0298     QTRY_VERIFY(!index.data(AbstractTasksModel::IsMinimized).toBool());
0299     QTRY_VERIFY(!index.data(AbstractTasksModel::IsHidden).toBool());
0300 
0301     // Minimize the window
0302     QSignalSpy dataChangedSpy(&m_model, &XWindowTasksModel::dataChanged);
0303     window->showMinimized();
0304     dataChangedSpy.wait();
0305     // There can be more than one dataChanged signal being emitted due to caching
0306     QTRY_VERIFY(std::any_of(dataChangedSpy.cbegin(), dataChangedSpy.cend(), [](const QVariantList &list) {
0307         return list.at(2).value<QVector<int>>().contains(AbstractTasksModel::IsMinimized);
0308     }));
0309     // The model doesn't notify data change stored under IsHidden role
0310     QTRY_VERIFY(std::none_of(dataChangedSpy.cbegin(), dataChangedSpy.cend(), [](const QVariantList &list) {
0311         return list.at(2).value<QVector<int>>().contains(AbstractTasksModel::IsHidden);
0312     }));
0313     QTRY_VERIFY(std::any_of(dataChangedSpy.cbegin(), dataChangedSpy.cend(), [](const QVariantList &list) {
0314         return list.at(2).value<QVector<int>>().contains(AbstractTasksModel::IsActive);
0315     }));
0316     QTRY_VERIFY(index.data(AbstractTasksModel::IsMinimized).toBool());
0317     QTRY_VERIFY(index.data(AbstractTasksModel::IsHidden).toBool());
0318     QTRY_VERIFY(!index.data(AbstractTasksModel::IsActive).toBool());
0319 
0320     // Restore the window
0321     dataChangedSpy.clear();
0322     window->showNormal();
0323     window->raise();
0324     window->requestActivate();
0325     dataChangedSpy.wait();
0326     QTRY_VERIFY(std::any_of(dataChangedSpy.cbegin(), dataChangedSpy.cend(), [](const QVariantList &list) {
0327         return list.at(2).value<QVector<int>>().contains(AbstractTasksModel::IsMinimized);
0328     }));
0329     QVERIFY(std::none_of(dataChangedSpy.cbegin(), dataChangedSpy.cend(), [](const QVariantList &list) {
0330         return list.at(2).value<QVector<int>>().contains(AbstractTasksModel::IsHidden);
0331     }));
0332     QTRY_VERIFY(std::any_of(dataChangedSpy.cbegin(), dataChangedSpy.cend(), [](const QVariantList &list) {
0333         return list.at(2).value<QVector<int>>().contains(AbstractTasksModel::IsActive);
0334     }));
0335     QTRY_VERIFY(!index.data(AbstractTasksModel::IsMinimized).toBool());
0336     QTRY_VERIFY(!index.data(AbstractTasksModel::IsHidden).toBool());
0337     QTRY_VERIFY(index.data(AbstractTasksModel::IsActive).toBool());
0338 }
0339 
0340 void XWindowTasksModelTest::test_fullscreen()
0341 {
0342     TestUtils::testFullscreen(m_model);
0343 }
0344 
0345 void XWindowTasksModelTest::test_geometry()
0346 {
0347     TestUtils::testGeometry(m_model);
0348 }
0349 
0350 void XWindowTasksModelTest::test_stackingOrder()
0351 {
0352     TestUtils::testStackingOrder(m_model);
0353 }
0354 
0355 void XWindowTasksModelTest::test_lastActivated()
0356 // Re-activate the window to update the last activated time
0357 {
0358     const QString title = QStringLiteral("__testwindow__%1").arg(QDateTime::currentDateTime().toString());
0359     QModelIndex index;
0360     auto window = TestUtils::createSingleWindow(m_model, title, index);
0361 
0362     QSignalSpy dataChangedSpy(&m_model, &XWindowTasksModel::dataChanged);
0363     window->showMinimized();
0364     dataChangedSpy.wait();
0365     QTRY_VERIFY(std::any_of(dataChangedSpy.cbegin(), dataChangedSpy.cend(), [](const QVariantList &list) {
0366         return list.at(2).value<QVector<int>>().contains(AbstractTasksModel::IsMinimized);
0367     }));
0368     QTRY_VERIFY(index.data(AbstractTasksModel::IsMinimized).toBool());
0369 
0370     window->showNormal();
0371     window->raise();
0372     window->requestActivate();
0373     const QTime lastActivatedTime = QTime::currentTime();
0374     dataChangedSpy.wait();
0375     // There can be more than one dataChanged signal being emitted due to caching
0376     QTRY_VERIFY(!index.data(AbstractTasksModel::IsMinimized).toBool());
0377     // The model doesn't notify data change stored under LastActivated role
0378     QTRY_VERIFY(std::none_of(dataChangedSpy.cbegin(), dataChangedSpy.cend(), [](const QVariantList &list) {
0379         return list.at(2).value<QVector<int>>().contains(AbstractTasksModel::LastActivated);
0380     }));
0381     qDebug() << lastActivatedTime.msecsSinceStartOfDay() << index.data(AbstractTasksModel::LastActivated).toTime().msecsSinceStartOfDay();
0382     QVERIFY(std::abs(lastActivatedTime.msecsSinceStartOfDay() - index.data(AbstractTasksModel::LastActivated).toTime().msecsSinceStartOfDay()) < 1000);
0383 }
0384 
0385 void XWindowTasksModelTest::test_modelDataFromDesktopFile()
0386 {
0387     TestUtils::testModelDataFromDesktopFile(m_model);
0388 }
0389 
0390 void XWindowTasksModelTest::test_windowState()
0391 {
0392     QSignalSpy rowsInsertedSpy(&m_model, &XWindowTasksModel::rowsInserted);
0393 
0394     const QString title = QStringLiteral("__testwindow__%1").arg(QDateTime::currentDateTime().toString());
0395     QModelIndex index;
0396     auto window = TestUtils::createSingleWindow(m_model, title, index);
0397 
0398     QSignalSpy dataChangedSpy(&m_model, &XWindowTasksModel::dataChanged);
0399 
0400     // NETWinInfo only allows a window manager set window states
0401     std::array<std::string, 11> actions{
0402         "_NET_WM_ACTION_MOVE",
0403         "_NET_WM_ACTION_RESIZE",
0404         "_NET_WM_ACTION_MINIMIZE",
0405         "_NET_WM_ACTION_SHADE",
0406         "_NET_WM_ACTION_STICK",
0407         "_NET_WM_ACTION_MAXIMIZE_VERT",
0408         "_NET_WM_ACTION_MAXIMIZE_HORZ",
0409         "_NET_WM_ACTION_FULLSCREEN",
0410         "_NET_WM_ACTION_CHANGE_DESKTOP",
0411         "_NET_WM_ACTION_CLOSE",
0412         "_NET_WM_ALLOWED_ACTIONS",
0413     };
0414     xcb_atom_t atoms[11];
0415     {
0416         xcb_intern_atom_cookie_t cookies[11];
0417         for (std::size_t i = 0; i < actions.size(); ++i) {
0418             cookies[i] = xcb_intern_atom(QX11Info::connection(), false, actions[i].size(), actions[i].c_str());
0419         }
0420         // Get the replies
0421         for (std::size_t i = 0; i < actions.size(); ++i) {
0422             xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(QX11Info::connection(), cookies[i], nullptr);
0423             if (!reply) {
0424                 continue;
0425             }
0426             atoms[i] = reply->atom;
0427             free(reply);
0428         }
0429         qDebug() << "XCB atom cached";
0430     }
0431 
0432     auto setAllowedActionsAndVerify = [&](AbstractTasksModel::AdditionalRoles role, NET::Actions allowedActions, bool &success) {
0433         qDebug() << "Start testing" << role;
0434         success = false;
0435         dataChangedSpy.clear();
0436         QVERIFY(index.data(role).toBool());
0437 
0438         uint32_t data[50];
0439         int count = 0;
0440         if (allowedActions & NET::ActionMove) {
0441             data[count++] = atoms[0];
0442         }
0443         if (allowedActions & NET::ActionResize) {
0444             data[count++] = atoms[1];
0445         }
0446         if (allowedActions & NET::ActionMinimize) {
0447             data[count++] = atoms[2];
0448         }
0449         if (allowedActions & NET::ActionShade) {
0450             data[count++] = atoms[3];
0451         }
0452         if (allowedActions & NET::ActionStick) {
0453             data[count++] = atoms[4];
0454         }
0455         if (allowedActions & NET::ActionMaxVert) {
0456             data[count++] = atoms[5];
0457         }
0458         if (allowedActions & NET::ActionMaxHoriz) {
0459             data[count++] = atoms[6];
0460         }
0461         if (allowedActions & NET::ActionFullScreen) {
0462             data[count++] = atoms[7];
0463         }
0464         if (allowedActions & NET::ActionChangeDesktop) {
0465             data[count++] = atoms[8];
0466         }
0467         if (allowedActions & NET::ActionClose) {
0468             data[count++] = atoms[9];
0469         }
0470 
0471         xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, window->winId(), atoms[10], XCB_ATOM_ATOM, 32, count, (const void *)data);
0472         xcb_flush(QX11Info::connection());
0473         QCoreApplication::processEvents();
0474 
0475         QTRY_VERIFY(std::any_of(dataChangedSpy.cbegin(), dataChangedSpy.cend(), [role](const QVariantList &list) {
0476             return list.at(2).value<QVector<int>>().contains(role);
0477         }));
0478         QVERIFY(!index.data(role).toBool());
0479         success = true;
0480     };
0481 
0482     bool success = false;
0483     const auto fullFlags = NET::ActionMove | NET::ActionResize | NET::ActionMinimize | NET::ActionShade | NET::ActionStick | NET::ActionMaxVert
0484         | NET::ActionMaxHoriz | NET::ActionFullScreen | NET::ActionChangeDesktop | NET::ActionClose;
0485 
0486     // Make the window not movable
0487     setAllowedActionsAndVerify(AbstractTasksModel::IsMovable, fullFlags & (~NET::ActionMove), success);
0488     QVERIFY(success);
0489 
0490     // Make the window not resizable
0491     setAllowedActionsAndVerify(AbstractTasksModel::IsResizable, fullFlags & (~NET::ActionResize), success);
0492     QVERIFY(success);
0493 
0494     // Make the window not maximizable
0495     setAllowedActionsAndVerify(AbstractTasksModel::IsMaximizable, fullFlags & (~NET::ActionMax), success);
0496     QVERIFY(success);
0497 
0498     // Make the window not minimizable
0499     setAllowedActionsAndVerify(AbstractTasksModel::IsMinimizable, fullFlags & (~NET::ActionMinimize), success);
0500     QVERIFY(success);
0501 
0502     // Make the window not fullscreenable
0503     setAllowedActionsAndVerify(AbstractTasksModel::IsFullScreenable, fullFlags & (~NET::ActionFullScreen), success);
0504     QVERIFY(success);
0505 
0506     // Make the window not shadeable
0507     setAllowedActionsAndVerify(AbstractTasksModel::IsShadeable, fullFlags & (~NET::ActionShade), success);
0508     QVERIFY(success);
0509 
0510     // Make the window not able to change virtual desktop
0511     setAllowedActionsAndVerify(AbstractTasksModel::IsVirtualDesktopsChangeable, fullFlags & (~NET::ActionChangeDesktop), success);
0512     QVERIFY(success);
0513 
0514     // Make the window not closable
0515     setAllowedActionsAndVerify(AbstractTasksModel::IsClosable, fullFlags & (~NET::ActionClose), success);
0516     QVERIFY(success);
0517 }
0518 
0519 void XWindowTasksModelTest::test_request()
0520 {
0521     TestUtils::testRequest(m_model);
0522 }
0523 
0524 QTEST_MAIN(XWindowTasksModelTest)
0525 
0526 #include "xwindowtasksmodeltest.moc"