162 lines
6.3 KiB
C++
162 lines
6.3 KiB
C++
// std
|
|
#include <tuple>
|
|
#include <iostream>
|
|
// Qt
|
|
#include <QDebug>
|
|
#include <QTest>
|
|
#include <QSignalSpy>
|
|
// DOtherSide
|
|
#include "DOtherSide/BaseQObject.h"
|
|
#include "DOtherSide/DynamicQObject.h"
|
|
#include "DOtherSide/DynamicQObject2.h"
|
|
#include "DOtherSide/DynamicQObjectFactory.h"
|
|
|
|
// Templates that convers a T to a string
|
|
template <typename T>
|
|
struct TypeName
|
|
{
|
|
static const char* Get() { return typeid(T).name(); }
|
|
};
|
|
template <> struct TypeName<int> { static const char* Get() { return "int"; } };
|
|
template <> struct TypeName<QString> { static const char* Get() { return "QString"; } };
|
|
template <> struct TypeName<bool> { static const char* Get() { return "bool"; } };
|
|
template <> struct TypeName<QVariant> { static const char* Get() { return "QVariant"; } };
|
|
|
|
class TestDynamicQObject : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
private slots:
|
|
void memoryLeakTest() {
|
|
DynamicQObject<QObject> dynamicQObject;
|
|
BaseQObject baseQObject;
|
|
}
|
|
|
|
void testRegisterSignal() {
|
|
DynamicQObject<QObject> dynamicQObject;
|
|
int index;
|
|
dynamicQObject.registerSignal("fooSignal", {}, index);
|
|
QVERIFY(index != -1);
|
|
|
|
QSignalSpy signalSpy(&dynamicQObject, SIGNAL(fooSignal()));
|
|
dynamicQObject.emitSignal("fooSignal", {});
|
|
QCOMPARE(signalSpy.count(), 1);
|
|
}
|
|
|
|
void testSlotExecution() {
|
|
testSlotExecutionForType<int>(10);
|
|
testSlotExecutionForType<QString>("foo");
|
|
testSlotExecutionForType<bool>(false);
|
|
testSlotExecutionForType<QVariant>(QVariant(40));
|
|
}
|
|
|
|
void testRegisterProperty() {
|
|
DynamicQObject<QObject> dynamicQObject;
|
|
int index = -1;
|
|
bool result = false;
|
|
result = dynamicQObject.registerSlot("foo", QMetaType::Int, {}, index);
|
|
QVERIFY(index != -1);
|
|
QVERIFY(result);
|
|
result = dynamicQObject.registerSlot("setFoo", QMetaType::Void, {QMetaType::Int}, index);
|
|
QVERIFY(index != -1);
|
|
QVERIFY(result);
|
|
result = dynamicQObject.registerSignal("fooChanged", {QMetaType::Int}, index);
|
|
QVERIFY(index != -1);
|
|
QVERIFY(result);
|
|
result = dynamicQObject.registerProperty("foo", QMetaType::Int, "foo", "setFoo", "fooChanged");
|
|
QVERIFY(result);;
|
|
|
|
int propertyValue = -1;
|
|
|
|
auto handler = [&propertyValue](const DynamicSlot &slot, const std::vector<QVariant> &args) -> QVariant {
|
|
if (slot.name() == "foo")
|
|
return propertyValue;
|
|
else if (slot.name() == "setFoo")
|
|
propertyValue = args.front().toInt();
|
|
return QVariant();
|
|
};
|
|
|
|
dynamicQObject.setOnSlotExecutedHandler(handler);
|
|
|
|
// Test property read
|
|
QCOMPARE(dynamicQObject.property("foo").toInt(), -1);
|
|
|
|
// Test property write and signal emittion
|
|
dynamicQObject.setProperty("foo", 10);
|
|
QCOMPARE(propertyValue, 10);
|
|
}
|
|
|
|
void benchmarkDynamicQObjectPerformance() {
|
|
QBENCHMARK {
|
|
for (int i = 0; i < 1000; ++i) {
|
|
DynamicQObject<QObject> dynamicQObject;
|
|
int index = -1;
|
|
dynamicQObject.registerSlot("foo", QMetaType::Int, {}, index);
|
|
dynamicQObject.registerSlot("setFoo", QMetaType::Void, {QMetaType::Int}, index);
|
|
dynamicQObject.registerSignal("fooChanged", {QMetaType::Int}, index);
|
|
dynamicQObject.registerProperty("foo", QMetaType::Int, "foo", "setFoo", "fooChanged");
|
|
}
|
|
}
|
|
}
|
|
|
|
void benchmarkDynamicQObject2Performance() {
|
|
|
|
QBENCHMARK {
|
|
DOS::DynamicQObjectFactory factory {{DOS::SignalDefinition{"fooChanged", {QMetaType::Int}}},
|
|
{DOS::SlotDefinition{"foo", QMetaType::Int, {}}, DOS::SlotDefinition{"setFoo", QMetaType::Void, {QMetaType::Int}}},
|
|
{DOS::PropertyDefinition{"foo", QMetaType::Int, "foo", "setFoo", "fooChanged"}}};
|
|
for (int i = 0; i < 1000; ++i) {
|
|
std::unique_ptr<DOS::DynamicQObject2> dynamicQObject(factory.create([](const QString&, const std::vector<QVariant>&)-> QVariant{}));
|
|
}
|
|
}
|
|
}
|
|
|
|
void testDynamicQObject2() {
|
|
DOS::DynamicQObjectFactory factory {{DOS::SignalDefinition{"fooChanged", {QMetaType::Int}}},
|
|
{DOS::SlotDefinition{"foo", QMetaType::Int, {}}, DOS::SlotDefinition{"setFoo", QMetaType::Void, {QMetaType::Int}}},
|
|
{DOS::PropertyDefinition{"foo", QMetaType::Int, "foo", "setFoo", "fooChanged"}}};
|
|
std::unique_ptr<DOS::DynamicQObject2> dynamicQObject(factory.create([](const QString&, const std::vector<QVariant>&)-> QVariant{}));
|
|
QVERIFY(dynamicQObject != nullptr);
|
|
|
|
QSignalSpy signalSpy(dynamicQObject.get(), SIGNAL(fooChanged(int)));
|
|
dynamicQObject->emitSignal("fooChanged", {10});
|
|
QCOMPARE(signalSpy.count(), 1);
|
|
}
|
|
|
|
private:
|
|
template<typename ReturnType>
|
|
void testSlotExecutionForType(ReturnType expectedReturnValue) {
|
|
DynamicQObject<QObject> dynamicQObject;
|
|
int index;
|
|
auto type = static_cast<QMetaType::Type>(qMetaTypeId<ReturnType>());
|
|
dynamicQObject.registerSlot("fooSlot", type, {type}, index);
|
|
QVERIFY(index != -1);
|
|
|
|
// Call the slot and check return value
|
|
bool called = false;
|
|
bool obtainedArgument = false;
|
|
bool argumentMatch = false;
|
|
auto handler = [&called, expectedReturnValue, &obtainedArgument, &argumentMatch]
|
|
(const DynamicSlot &, const std::vector<QVariant> &args) -> QVariant
|
|
{
|
|
obtainedArgument = args.size() == 1;
|
|
argumentMatch = (!args.empty() && args.front().value<ReturnType>() == expectedReturnValue);
|
|
called = true;
|
|
return expectedReturnValue;
|
|
};
|
|
dynamicQObject.setOnSlotExecutedHandler(handler);
|
|
ReturnType result;
|
|
QMetaObject::invokeMethod(&dynamicQObject, "fooSlot",
|
|
QGenericReturnArgument(TypeName<ReturnType>::Get(), &result),
|
|
QGenericArgument(TypeName<ReturnType>::Get(), &expectedReturnValue));
|
|
QVERIFY(called);
|
|
QVERIFY(obtainedArgument);
|
|
QVERIFY(argumentMatch);
|
|
QCOMPARE(result, expectedReturnValue);
|
|
}
|
|
};
|
|
|
|
QTEST_MAIN(TestDynamicQObject)
|
|
|
|
#include "test_dynamicqobject.moc"
|