File indexing completed on 2024-05-12 05:38:09

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