File indexing completed on 2024-09-22 05:02:00

0001 /****************************************************************************
0002 **
0003 ** Copyright (C) 2018 The Qt Company Ltd.
0004 ** Contact: https://www.qt.io/licensing/
0005 **
0006 ** This file is part of the test suite of the Qt Toolkit.
0007 **
0008 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
0009 ** Commercial License Usage
0010 ** Licensees holding valid commercial Qt licenses may use this file in
0011 ** accordance with the commercial license agreement provided with the
0012 ** Software or, alternatively, in accordance with the terms contained in
0013 ** a written agreement between you and The Qt Company. For licensing terms
0014 ** and conditions see https://www.qt.io/terms-conditions. For further
0015 ** information use the contact form at https://www.qt.io/contact-us.
0016 **
0017 ** GNU General Public License Usage
0018 ** Alternatively, this file may be used under the terms of the GNU
0019 ** General Public License version 3 as published by the Free Software
0020 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
0021 ** included in the packaging of this file. Please review the following
0022 ** information to ensure the GNU General Public License requirements will
0023 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
0024 **
0025 ** $QT_END_LICENSE$
0026 **
0027 ****************************************************************************/
0028 
0029 #pragma once
0030 
0031 #include <QtTest/QtTest>
0032 
0033 #include <wayland-server-core.h>
0034 
0035 struct wl_resource;
0036 
0037 namespace MockCompositor
0038 {
0039 class Global : public QObject
0040 {
0041     Q_OBJECT
0042 public:
0043     virtual bool isClean()
0044     {
0045         return true;
0046     }
0047     virtual QString dirtyMessage()
0048     {
0049         return isClean() ? "clean" : "dirty";
0050     }
0051 };
0052 
0053 class CoreCompositor
0054 {
0055 public:
0056     explicit CoreCompositor();
0057     ~CoreCompositor();
0058     bool isClean();
0059     QString dirtyMessage();
0060     void dispatch();
0061 
0062     template<typename function_type, typename... arg_types>
0063     auto exec(function_type func, arg_types &&...args) -> decltype(func())
0064     {
0065         Lock lock(this);
0066         return func(std::forward<arg_types>(args)...);
0067     }
0068 
0069     // Unsafe section below, YOU are responsible that the compositor is locked or
0070     // this is run through the mutex() method!
0071 
0072     void add(Global *global);
0073     void remove(Global *global);
0074 
0075     /*!
0076      * \brief Constructs and adds a new global with the given parameters
0077      *
0078      * Convenience function. i.e.
0079      *
0080      *     compositor->add(new MyGlobal(compositor, version);
0081      *
0082      * can be written as:
0083      *
0084      *     compositor->add<MyGlobal>(version);
0085      *
0086      * Returns the new global
0087      */
0088     template<typename global_type, typename... arg_types>
0089     global_type *add(arg_types &&...args)
0090     {
0091         warnIfNotLockedByThread(Q_FUNC_INFO);
0092         auto *global = new global_type(this, std::forward<arg_types>(args)...);
0093         m_globals.append(global);
0094         return global;
0095     }
0096 
0097     /*!
0098      * \brief Removes all globals of the given type
0099      *
0100      * Convenience function
0101      */
0102     template<typename global_type, typename... arg_types>
0103     void removeAll()
0104     {
0105         const auto globals = getAll<global_type>();
0106         for (auto global : globals)
0107             remove(global);
0108     }
0109 
0110     /*!
0111      * \brief Returns a global with the given type, if any
0112      */
0113     template<typename global_type>
0114     global_type *get()
0115     {
0116         warnIfNotLockedByThread(Q_FUNC_INFO);
0117         for (auto *global : std::as_const(m_globals)) {
0118             if (auto *casted = qobject_cast<global_type *>(global))
0119                 return casted;
0120         }
0121         return nullptr;
0122     }
0123 
0124     /*!
0125      * \brief Returns the nth global with the given type, if any
0126      */
0127     template<typename global_type>
0128     global_type *get(int index)
0129     {
0130         warnIfNotLockedByThread(Q_FUNC_INFO);
0131         for (auto *global : std::as_const(m_globals)) {
0132             if (auto *casted = qobject_cast<global_type *>(global)) {
0133                 if (index--)
0134                     continue;
0135                 return casted;
0136             }
0137         }
0138         return nullptr;
0139     }
0140 
0141     /*!
0142      * \brief Returns all globals with the given type, if any
0143      */
0144     template<typename global_type>
0145     QList<global_type *> getAll()
0146     {
0147         warnIfNotLockedByThread(Q_FUNC_INFO);
0148         QList<global_type *> matching;
0149         for (auto *global : std::as_const(m_globals)) {
0150             if (auto *casted = qobject_cast<global_type *>(global))
0151                 matching.append(casted);
0152         }
0153         return matching;
0154     }
0155 
0156     uint nextSerial();
0157     uint currentTimeMilliseconds();
0158     wl_client *client(int index = 0);
0159     void warnIfNotLockedByThread(const char *caller = "warnIfNotLockedbyThread");
0160 
0161 public:
0162     // Only use this carefully from the test thread (i.e. lock first)
0163     wl_display *m_display = nullptr;
0164 
0165 protected:
0166     class Lock
0167     {
0168     public:
0169         explicit Lock(CoreCompositor *compositor)
0170             : m_compositor(compositor)
0171             , m_threadId(std::this_thread::get_id())
0172         {
0173             // Can't use a QMutexLocker here, as it's not movable
0174             compositor->m_mutex.lock();
0175             Q_ASSERT(compositor->m_lock == nullptr);
0176             compositor->m_lock = this;
0177         }
0178         ~Lock()
0179         {
0180             Q_ASSERT(m_compositor->m_lock == this);
0181             m_compositor->m_lock = nullptr;
0182             m_compositor->m_mutex.unlock();
0183         }
0184 
0185         // Move semantics
0186         Lock(Lock &&) = default;
0187         Lock &operator=(Lock &&) = default;
0188 
0189         // Disable copying
0190         Lock(const Lock &) = delete;
0191         Lock &operator=(const Lock &) = delete;
0192 
0193         bool isOwnedByCurrentThread() const
0194         {
0195             return m_threadId == std::this_thread::get_id();
0196         }
0197 
0198     private:
0199         CoreCompositor *m_compositor = nullptr;
0200         std::thread::id m_threadId;
0201     };
0202     QByteArray m_socketName;
0203     wl_event_loop *m_eventLoop = nullptr;
0204     bool m_running = true;
0205     QList<Global *> m_globals;
0206     QElapsedTimer m_timer;
0207 
0208 private:
0209     Lock *m_lock = nullptr;
0210     QMutex m_mutex;
0211     std::thread m_dispatchThread;
0212 };
0213 
0214 template<typename container_type>
0215 QByteArray toByteArray(container_type container)
0216 {
0217     return QByteArray(reinterpret_cast<const char *>(container.data()), sizeof(container[0]) * container.size());
0218 }
0219 
0220 template<typename return_type>
0221 return_type *fromResource(::wl_resource *resource)
0222 {
0223     if (auto *r = return_type::Resource::fromResource(resource))
0224         return static_cast<return_type *>(r->object());
0225     return nullptr;
0226 }
0227 
0228 } // namespace MockCompositor