Revision control
++ // Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ #include <QtTest/QTest>
++ #include <QList>
++ #include <Qt3DCore/private/qhandle_p.h>
++ #include <Qt3DCore/private/qresourcemanager_p.h>
++ #include <QThread>
++
++ class tst_QResourceManager : public QObject
++ {
++ Q_OBJECT
++ public:
++ tst_QResourceManager() {}
++ ~tst_QResourceManager() {}
++
++ private slots:
++ void createResourcesManager();
++ void acquireResources();
++ void getResources();
++ void registerResourcesResize();
++ void removeResource();
++ void lookupResource();
++ void releaseResource();
++ void heavyDutyMultiThreadedAccess();
++ void heavyDutyMultiThreadedAccessRelease();
++ void collectResources();
++ void activeHandles();
++ void checkCleanup();
++ };
++
++ class tst_ArrayResource
++ {
++ public:
++ tst_ArrayResource() : m_value(0)
++ {}
++
++ void cleanup() { m_value = 0; }
++
++ QAtomicInt m_value;
++ };
++
++ QT_BEGIN_NAMESPACE
++ Q_DECLARE_RESOURCE_INFO(tst_ArrayResource, Q_REQUIRES_CLEANUP)
++ QT_END_NAMESPACE
++
++ typedef Qt3DCore::QHandle<tst_ArrayResource> tHandle;
++
++ void tst_QResourceManager::createResourcesManager()
++ {
++ Qt3DCore::QResourceManager<tst_ArrayResource, int> manager;
++ }
++
++ /*!
++ * Check that the handles returned when a registering resources
++ * have a correct index and counter.
++ */
++ void tst_QResourceManager::acquireResources()
++ {
++ Qt3DCore::QResourceManager<tst_ArrayResource, uint> manager;
++
++ QList<tHandle> handles;
++
++ for (int i = 0; i < 5; i++) {
++ handles << manager.acquire();
++ }
++
++ for (uint i = 0; i < 5; i++) {
++ QVERIFY(!handles.at(i).isNull());
++ if (i > 0)
++ QVERIFY(handles.at(i) != handles.at(i-1));
++ }
++ }
++
++ /*!
++ * Test that values can be properly retrieved.
++ */
++ void tst_QResourceManager::getResources()
++ {
++
++ Qt3DCore::QResourceManager<tst_ArrayResource, int> manager;
++ QList<tst_ArrayResource *> resources;
++ QList<tHandle> handles;
++
++ for (int i = 0; i < 5; i++) {
++ handles << manager.acquire();
++ }
++
++ for (uint i = 0; i < 5; i++) {
++ resources << manager.data(handles.at(i));
++ QVERIFY(resources.at(i) != nullptr);
++ resources.at(i)->m_value = i;
++ }
++
++ for (int i = 0; i < 5; i++)
++ QVERIFY(manager.data(handles.at(i))->m_value == i);
++
++ // Check that an invalid resource returns NULL
++ tHandle iHandle;
++ QVERIFY(manager.data(iHandle) == nullptr);
++
++ }
++
++ /*!
++ * Test that when a resize of the data vectors in the manager occurs,
++ * everything behaves correctly.
++ */
++ void tst_QResourceManager::registerResourcesResize()
++ {
++ Qt3DCore::QResourceManager<tst_ArrayResource, uint> manager;
++ QList<tHandle> handles;
++
++ for (uint i = 0; i < 2; i++) {
++ handles << manager.acquire();
++ manager.data(handles.at(i))->m_value = i + 2;
++ }
++
++ for (uint i = 0; i < 5; i++) {
++ handles << manager.acquire();
++ manager.data(handles.at(i + 2))->m_value = i + 2 + 5;
++ }
++
++ for (int i = 0; i < 7; i++) {
++ if (i < 2)
++ QVERIFY(manager.data(handles.at(i))->m_value == i + 2);
++ else
++ QVERIFY(manager.data(handles.at(i))->m_value == i + 5);
++ }
++ }
++
++ /*!
++ * Checks for the removal of resources.
++ */
++ void tst_QResourceManager::removeResource()
++ {
++ Qt3DCore::QResourceManager<tst_ArrayResource, int> manager;
++
++ QList<tHandle> handles;
++
++ for (int i = 0; i < 32; i++) {
++ handles << manager.acquire();
++ }
++
++
++ tst_ArrayResource *resource = handles.at(2).data();
++ QVERIFY(resource != nullptr);
++
++ manager.release(handles.at(2));
++ QVERIFY(manager.data(handles.at(2)) == nullptr);
++ // Triggers QASSERT so commented
++ // manager.release(handles.at(2));
++
++ tHandle nHandle = manager.acquire();
++ QVERIFY(manager.data(nHandle) != nullptr);
++ }
++
++ void tst_QResourceManager::lookupResource()
++ {
++ Qt3DCore::QResourceManager<tst_ArrayResource, uint> manager;
++
++ QList<tst_ArrayResource *> resources;
++ QList<tHandle> handles;
++
++ for (int i = 0; i < 5; i++) {
++ handles << manager.acquire();
++ resources << manager.data(handles.at(i));
++ resources.at(i)->m_value = 4;
++ }
++
++ tHandle t = manager.lookupHandle(2);
++ QVERIFY(t.handle() == 0);
++ QVERIFY(manager.data(t) == nullptr);
++ tst_ArrayResource *resource = manager.getOrCreateResource(2);
++ QVERIFY(resource != nullptr);
++ t = manager.lookupHandle(2);
++ QVERIFY(manager.data(t) == manager.lookupResource(2));
++ QVERIFY(t == manager.getOrAcquireHandle(2));
++ QVERIFY(resource == manager.getOrCreateResource(2));
++ QVERIFY(manager.data(t) == resource);
++ }
++
++ void tst_QResourceManager::releaseResource()
++ {
++ Qt3DCore::QResourceManager<tst_ArrayResource, uint> manager;
++ QList<tst_ArrayResource *> resources;
++
++ for (int i = 0; i < 5; i++) {
++ resources << manager.getOrCreateResource(i);
++ }
++
++ for (int i = 0; i < 5; i++) {
++ QVERIFY(resources.at(i) == manager.lookupResource(i));
++ }
++
++ for (int i = 0; i < 5; i++) {
++ manager.releaseResource(i);
++ QVERIFY(manager.lookupResource(i) == nullptr);
++ }
++ }
++
++ class tst_Thread : public QThread
++ {
++ Q_OBJECT
++ public:
++
++ typedef Qt3DCore::QResourceManager<tst_ArrayResource,
++ int,
++ Qt3DCore::ObjectLevelLockingPolicy> Manager;
++
++ tst_Thread()
++ : QThread()
++ {
++ }
++
++ void setManager(Manager *manager)
++ {
++ m_manager = manager;
++ }
++
++ // QThread interface
++ protected:
++ void run() override
++ {
++ int i = 0;
++ int max = 65535;
++ while (i < max) {
++ tst_ArrayResource *r = m_manager->getOrCreateResource(i);
++ i++;
++ QVERIFY(r != nullptr);
++ r->m_value.fetchAndAddOrdered(+1);
++ }
++ qDebug() << QThread::currentThread() << "Done";
++ }
++
++ Manager *m_manager;
++ };
++
++ void tst_QResourceManager::heavyDutyMultiThreadedAccess()
++ {
++ tst_Thread::Manager *manager = new tst_Thread::Manager();
++
++ QList<tst_Thread *> threads;
++
++ int iterations = 8;
++ int max = 65535;
++
++ for (int i = 0; i < iterations; i++) {
++ tst_Thread *thread = new tst_Thread();
++ thread->setManager(manager);
++ threads << thread;
++ }
++
++ for (int i = 0; i < iterations; i++) {
++ threads[i]->start();
++ }
++
++ for (int i = 0; i < iterations; i++) {
++ threads[i]->wait();
++ }
++
++ for (int i = 0; i < max; i++) {
++ QVERIFY(manager->lookupResource(i) != nullptr);
++ QVERIFY(manager->lookupResource(i)->m_value == iterations);
++ }
++
++ qDeleteAll(threads);
++ delete manager;
++ }
++
++ class tst_Thread2 : public QThread
++ {
++ Q_OBJECT
++ public:
++
++ typedef Qt3DCore::QResourceManager<tst_ArrayResource,
++ int,
++ Qt3DCore::ObjectLevelLockingPolicy> Manager;
++
++ tst_Thread2(int releaseAbove = 7)
++ : QThread()
++ , m_releaseAbove(releaseAbove)
++ {
++ }
++
++ void setManager(Manager *manager)
++ {
++ m_manager = manager;
++ }
++
++ // QThread interface
++ protected:
++ void run() override
++ {
++ int i = 0;
++ int max = 65535;
++ while (i < max) {
++ tst_ArrayResource *r = m_manager->getOrCreateResource(i);
++ QVERIFY(r != nullptr);
++ int oldValue = r->m_value.fetchAndAddOrdered(+1);
++ if (oldValue == m_releaseAbove)
++ m_manager->releaseResource(i);
++ i++;
++ }
++ qDebug() << QThread::currentThread() << "Done";
++ }
++
++ Manager *m_manager;
++ int m_releaseAbove;
++ };
++
++ void tst_QResourceManager::heavyDutyMultiThreadedAccessRelease()
++ {
++ tst_Thread2::Manager *manager = new tst_Thread2::Manager();
++
++ QList<tst_Thread2 *> threads;
++
++ int iterations = 8;
++ int max = 65535;
++
++ for (int u = 0; u < 2; u++) {
++
++ for (int i = 0; i < iterations; i++) {
++ tst_Thread2 *thread = new tst_Thread2();
++ thread->setManager(manager);
++ threads << thread;
++ }
++
++ for (int i = 0; i < iterations; i++) {
++ threads[i]->start();
++ }
++
++ for (int i = 0; i < iterations; i++) {
++ threads[i]->wait();
++ }
++
++ for (int i = 0; i < max; i++) {
++ QVERIFY(manager->lookupResource(i) == nullptr);
++ }
++
++ qDeleteAll(threads);
++ threads.clear();
++ }
++
++ delete manager;
++ }
++
++ void tst_QResourceManager::collectResources()
++ {
++ Qt3DCore::QResourceManager<tst_ArrayResource, uint> manager;
++
++ QList<tst_ArrayResource *> resources;
++ QList<tHandle> handles;
++
++ for (int i = 0; i < 65536; i++) {
++ handles << manager.acquire();
++ resources << manager.data(handles.at(i));
++ resources.at(i)->m_value = 4;
++ }
++ for (auto h : handles) {
++ manager.release(h);
++ }
++ Q_ASSERT(manager.count() == 0);
++ handles.clear();
++ manager.acquire();
++ Q_ASSERT(manager.count() == 1);
++ }
++
++ void tst_QResourceManager::activeHandles()
++ {
++ // GIVEN
++ Qt3DCore::QResourceManager<tst_ArrayResource, uint> manager;
++
++ {
++ // WHEN
++ const tHandle newHandle = manager.getOrAcquireHandle(883U);
++ // THEN
++ QCOMPARE(manager.activeHandles().size(), size_t(1));
++ QCOMPARE(manager.activeHandles()[0], newHandle);
++ }
++
++ {
++ // WHEN
++ manager.releaseResource(883U);
++ // THEN
++ QVERIFY(manager.activeHandles().empty());
++ }
++
++ {
++ // WHEN
++ const tHandle newHandle = manager.acquire();
++ // THEN
++ QCOMPARE(manager.activeHandles().size(), size_t(1));
++ QCOMPARE(manager.activeHandles()[0], newHandle);
++
++ // WHEN
++ manager.release(newHandle);
++ // THEN
++ QVERIFY(manager.activeHandles().empty());
++ }
++ }
++
++ void tst_QResourceManager::checkCleanup()
++ {
++ // GIVEN
++ Qt3DCore::QResourceManager<tst_ArrayResource, uint> manager;
++
++ // WHEN
++ tHandle newHandle = manager.getOrAcquireHandle(883U);
++ tst_ArrayResource *data = manager.data(newHandle);
++
++ data->m_value.ref();
++ // THEN
++ QCOMPARE(data->m_value.loadRelaxed(), 1);
++
++ // WHEN
++ manager.release(newHandle);
++
++ // THEN
++ QCOMPARE(data->m_value.loadRelaxed(), 0);
++ }
++
++
++
++
++ QTEST_APPLESS_MAIN(tst_QResourceManager)
++
++ #include "tst_qresourcemanager.moc"