From 7aa86f6b2b4858fd1325db933733634451ad92ca Mon Sep 17 00:00:00 2001 From: Skycoder42 Date: Sat, 28 Jul 2018 23:03:09 +0200 Subject: [PATCH] added service registry tests --- src/mvvmcore/serviceregistry.h | 1 + tests/auto/mvvmcore/mvvmcore.pro | 6 +- .../serviceregistry/plugintestinterface.h | 19 ++ .../serviceregistry/serviceregistry.pro | 22 ++ .../mvvmcore/serviceregistry/testinterface.h | 17 ++ .../mvvmcore/serviceregistry/testobject.cpp | 1 + .../mvvmcore/serviceregistry/testobject.h | 56 ++++ .../mvvmcore/serviceregistry/testservice.cpp | 9 + .../mvvmcore/serviceregistry/testservice.h | 24 ++ .../serviceregistry/tst_serviceregistry.cpp | 262 ++++++++++++++++++ .../serviceregistrytestplugin.json | 3 + .../serviceregistrytestplugin.pro | 18 ++ .../serviceregistrytestplugin/testplugin.cpp | 10 + .../serviceregistrytestplugin/testplugin.h | 18 ++ .../settingsgenerator/settingsgenerator.pro | 2 - 15 files changed, 465 insertions(+), 3 deletions(-) create mode 100644 tests/auto/mvvmcore/serviceregistry/plugintestinterface.h create mode 100644 tests/auto/mvvmcore/serviceregistry/serviceregistry.pro create mode 100644 tests/auto/mvvmcore/serviceregistry/testinterface.h create mode 100644 tests/auto/mvvmcore/serviceregistry/testobject.cpp create mode 100644 tests/auto/mvvmcore/serviceregistry/testobject.h create mode 100644 tests/auto/mvvmcore/serviceregistry/testservice.cpp create mode 100644 tests/auto/mvvmcore/serviceregistry/testservice.h create mode 100644 tests/auto/mvvmcore/serviceregistry/tst_serviceregistry.cpp create mode 100644 tests/auto/mvvmcore/serviceregistrytestplugin/serviceregistrytestplugin.json create mode 100644 tests/auto/mvvmcore/serviceregistrytestplugin/serviceregistrytestplugin.pro create mode 100644 tests/auto/mvvmcore/serviceregistrytestplugin/testplugin.cpp create mode 100644 tests/auto/mvvmcore/serviceregistrytestplugin/testplugin.h diff --git a/src/mvvmcore/serviceregistry.h b/src/mvvmcore/serviceregistry.h index ff67709..b9da75b 100644 --- a/src/mvvmcore/serviceregistry.h +++ b/src/mvvmcore/serviceregistry.h @@ -47,6 +47,7 @@ public: //! Register a service for its interface via an already existing instance template void registerInterface(TService *service, DestructionScope scope = DestroyOnAppDestroy, bool weak = false); + //! Register a service via its type template void registerObject(DestructionScope scope = DestroyOnAppDestroy, bool weak = false); diff --git a/tests/auto/mvvmcore/mvvmcore.pro b/tests/auto/mvvmcore/mvvmcore.pro index bc3a8df..9bf0070 100644 --- a/tests/auto/mvvmcore/mvvmcore.pro +++ b/tests/auto/mvvmcore/mvvmcore.pro @@ -1,4 +1,8 @@ TEMPLATE = subdirs SUBDIRS += \ - settingsgenerator + settingsgenerator \ + serviceregistry \ + serviceregistrytestplugin + +serviceregistry.depends += serviceregistrytestplugin diff --git a/tests/auto/mvvmcore/serviceregistry/plugintestinterface.h b/tests/auto/mvvmcore/serviceregistry/plugintestinterface.h new file mode 100644 index 0000000..1044519 --- /dev/null +++ b/tests/auto/mvvmcore/serviceregistry/plugintestinterface.h @@ -0,0 +1,19 @@ +#ifndef PLUGINTESTINTERFACE_H +#define PLUGINTESTINTERFACE_H + +#include + +class PluginTestInterface +{ + Q_DISABLE_COPY(PluginTestInterface) +public: + inline PluginTestInterface() = default; + virtual inline ~PluginTestInterface() = default; + + virtual int magicNumber() const = 0; +}; + +#define PluginTestInterfaceIId "de.skycoder42.qtmvvm.core.tests.PluginTestInterface" +Q_DECLARE_INTERFACE(PluginTestInterface, PluginTestInterfaceIId) + +#endif // PLUGINTESTINTERFACE_H diff --git a/tests/auto/mvvmcore/serviceregistry/serviceregistry.pro b/tests/auto/mvvmcore/serviceregistry/serviceregistry.pro new file mode 100644 index 0000000..58178e7 --- /dev/null +++ b/tests/auto/mvvmcore/serviceregistry/serviceregistry.pro @@ -0,0 +1,22 @@ +TEMPLATE = app + +QT += testlib mvvmcore +QT -= gui +CONFIG += console +CONFIG -= app_bundle + +TARGET = tst_serviceregistry + +SOURCES += \ + tst_serviceregistry.cpp \ + testservice.cpp \ + testobject.cpp + +HEADERS += \ + testinterface.h \ + testservice.h \ + testobject.h \ + plugintestinterface.h + +load(qt_build_paths) +DEFINES += PLUGIN_TESTDIR=\\\"$$MODULE_BASE_OUTDIR/tests/plugins\\\" diff --git a/tests/auto/mvvmcore/serviceregistry/testinterface.h b/tests/auto/mvvmcore/serviceregistry/testinterface.h new file mode 100644 index 0000000..9ed71a9 --- /dev/null +++ b/tests/auto/mvvmcore/serviceregistry/testinterface.h @@ -0,0 +1,17 @@ +#ifndef TESTINTERFACE_H +#define TESTINTERFACE_H + +#include + +class TestInterface +{ + Q_DISABLE_COPY(TestInterface) +public: + inline TestInterface() = default; + virtual inline ~TestInterface() = default; +}; + +#define TestInterfaceIId "de.skycoder42.qtmvvm.core.tests.TestInterface" +Q_DECLARE_INTERFACE(TestInterface, TestInterfaceIId) + +#endif // TESTINTERFACE_H diff --git a/tests/auto/mvvmcore/serviceregistry/testobject.cpp b/tests/auto/mvvmcore/serviceregistry/testobject.cpp new file mode 100644 index 0000000..52999a3 --- /dev/null +++ b/tests/auto/mvvmcore/serviceregistry/testobject.cpp @@ -0,0 +1 @@ +#include "testobject.h" diff --git a/tests/auto/mvvmcore/serviceregistry/testobject.h b/tests/auto/mvvmcore/serviceregistry/testobject.h new file mode 100644 index 0000000..eb7e750 --- /dev/null +++ b/tests/auto/mvvmcore/serviceregistry/testobject.h @@ -0,0 +1,56 @@ +#ifndef TESTOBJECT_H +#define TESTOBJECT_H + +#include +#include +#include + +#define CREATE_TEST_OBJECT(index, ...) \ + class TestObject ## index : public QObject \ + { \ + Q_OBJECT \ + \ + public: \ + __VA_ARGS__ \ + \ + Q_INVOKABLE explicit inline TestObject ## index(QObject *parent = nullptr) : \ + QObject{parent} \ + {} \ + }; + +#define DEP(index) \ + TestObject ## index *dep = nullptr; \ + QTMVVM_INJECT_PROP(TestObject ## index *, dep, dep) + +#define DESTROYER \ + QSharedPointer holder; + +#define SOMETIME_DESTROYER(index) \ + DESTROYER \ + inline ~TestObject ## index() { qDebug(Q_FUNC_INFO); } + +#define NEVER_DESTROYER(index) \ + DESTROYER \ + inline ~TestObject ## index() { Q_ASSERT_X(false, Q_FUNC_INFO, "destructor of never-destroy-object called"); } + +CREATE_TEST_OBJECT(0) + +CREATE_TEST_OBJECT(1, int data = 42;) +CREATE_TEST_OBJECT(2, int data = 42;) +CREATE_TEST_OBJECT(3, int data = 42;) + +CREATE_TEST_OBJECT(4) +CREATE_TEST_OBJECT(5) +CREATE_TEST_OBJECT(6, DEP(4)) +CREATE_TEST_OBJECT(7, DEP(5)) +CREATE_TEST_OBJECT(8, bool hasBoth = false;) + +CREATE_TEST_OBJECT(9, DEP(0)) +CREATE_TEST_OBJECT(10, DEP(9)) + +CREATE_TEST_OBJECT(11, SOMETIME_DESTROYER(11)) +CREATE_TEST_OBJECT(12, SOMETIME_DESTROYER(12)) +CREATE_TEST_OBJECT(13, SOMETIME_DESTROYER(13)) +CREATE_TEST_OBJECT(14, NEVER_DESTROYER(14)) + +#endif // TESTOBJECT_H diff --git a/tests/auto/mvvmcore/serviceregistry/testservice.cpp b/tests/auto/mvvmcore/serviceregistry/testservice.cpp new file mode 100644 index 0000000..5837e69 --- /dev/null +++ b/tests/auto/mvvmcore/serviceregistry/testservice.cpp @@ -0,0 +1,9 @@ +#include "testservice.h" + +TestService::TestService(QObject *parent) : + QObject{parent} +{} + +WeakTestService::WeakTestService(QObject *parent) : + TestService{parent} +{} diff --git a/tests/auto/mvvmcore/serviceregistry/testservice.h b/tests/auto/mvvmcore/serviceregistry/testservice.h new file mode 100644 index 0000000..6f736d5 --- /dev/null +++ b/tests/auto/mvvmcore/serviceregistry/testservice.h @@ -0,0 +1,24 @@ +#ifndef TESTSERVICE_H +#define TESTSERVICE_H + +#include +#include "testinterface.h" + +class TestService : public QObject, public TestInterface +{ + Q_OBJECT + Q_INTERFACES(TestInterface) + +public: + Q_INVOKABLE explicit TestService(QObject *parent = nullptr); +}; + +class WeakTestService : public TestService +{ + Q_OBJECT + +public: + Q_INVOKABLE explicit WeakTestService(QObject *parent = nullptr); +}; + +#endif // TESTSERVICE_H diff --git a/tests/auto/mvvmcore/serviceregistry/tst_serviceregistry.cpp b/tests/auto/mvvmcore/serviceregistry/tst_serviceregistry.cpp new file mode 100644 index 0000000..afea0d2 --- /dev/null +++ b/tests/auto/mvvmcore/serviceregistry/tst_serviceregistry.cpp @@ -0,0 +1,262 @@ +#include +#include +#include +#include + +#include "testinterface.h" +#include "testservice.h" +#include "testobject.h" +#include "plugintestinterface.h" +using namespace QtMvvm; + +class ServiceRegistryTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void testRegistrations(); + void testPlainConstructions(); + void testDepedencyConstructions(); + void testInjection(); + + void testPluginRegistration(); + + void testDestructionScopes(); + +public: + static QWeakPointer weak0; + static QWeakPointer weak1; + static QWeakPointer weak2; + static QWeakPointer weak3; +}; + +QWeakPointer ServiceRegistryTest::weak0; +QWeakPointer ServiceRegistryTest::weak1; +QWeakPointer ServiceRegistryTest::weak2; +QWeakPointerServiceRegistryTest::weak3; + +void ServiceRegistryTest::testRegistrations() +{ + try { + QVERIFY(!ServiceRegistry::instance()->isRegistered()); + QVERIFY(!ServiceRegistry::instance()->isRegistered()); + QVERIFY(!ServiceRegistry::instance()->isRegistered()); + QVERIFY_EXCEPTION_THROWN(ServiceRegistry::instance()->service(), ServiceDependencyException); + QVERIFY_EXCEPTION_THROWN(ServiceRegistry::instance()->service(), ServiceDependencyException); + + //register service + ServiceRegistry::instance()->registerInterface(ServiceRegistry::DestroyOnAppDestroy, true); + QVERIFY(ServiceRegistry::instance()->isRegistered()); + QVERIFY(ServiceRegistry::instance()->isRegistered(TestInterfaceIId)); + QVERIFY(!ServiceRegistry::instance()->isRegistered()); + QVERIFY(!ServiceRegistry::instance()->isRegistered()); + + ServiceRegistry::instance()->registerInterface(); + QVERIFY(ServiceRegistry::instance()->isRegistered()); + QVERIFY(!ServiceRegistry::instance()->isRegistered()); +#define TMP_REGISTER ServiceRegistry::instance()->registerInterface() + QVERIFY_EXCEPTION_THROWN(TMP_REGISTER, ServiceExistsException); +#undef TMP_REGISTER + + QVERIFY(ServiceRegistry::instance()->service()); + QVERIFY(ServiceRegistry::instance()->serviceObj(TestInterfaceIId)); + QVERIFY(dynamic_cast(ServiceRegistry::instance()->service())); + + //register object + ServiceRegistry::instance()->registerObject(); + QVERIFY(ServiceRegistry::instance()->isRegistered()); + QVERIFY(ServiceRegistry::instance()->service()); + } catch(QException &e) { + QFAIL(e.what()); + } +} + +void ServiceRegistryTest::testPlainConstructions() +{ + try { + // test default (meta) construction + ServiceRegistry::instance()->registerObject(); + auto obj1 = ServiceRegistry::instance()->service(); + QVERIFY(obj1); + QCOMPARE(obj1->data, 42); + + // test function construction + ServiceRegistry::instance()->registerObject([](){ + auto obj = new TestObject2{}; + obj->data = 13; + return obj; + }); + auto obj2 = ServiceRegistry::instance()->service(); + QVERIFY(obj2); + QCOMPARE(obj2->data, 13); + + // test instance construction + auto obj3Instance = new TestObject3{}; + ServiceRegistry::instance()->registerObject(obj3Instance); + auto obj3 = ServiceRegistry::instance()->service(); + QCOMPARE(obj3, obj3Instance); + } catch(QException &e) { + QFAIL(e.what()); + } +} + +void ServiceRegistryTest::testDepedencyConstructions() +{ + try { + //try valid (inverse order must work) + ServiceRegistry::instance()->registerObject(); + ServiceRegistry::instance()->registerObject(); + auto obj = ServiceRegistry::instance()->service(); + QVERIFY(obj); + QVERIFY(obj->dep); + + //try without dep + ServiceRegistry::instance()->registerObject(); + QVERIFY_EXCEPTION_THROWN(ServiceRegistry::instance()->service(), ServiceDependencyException); + ServiceRegistry::instance()->registerObject(); + QVERIFY(ServiceRegistry::instance()->service()); + + // try with fn + ServiceRegistry::instance()->registerObject([](TestObject6 *obj1, TestObject0 *obj2){ + auto retObj = new TestObject8{}; + retObj->hasBoth = obj1 && obj2; + return retObj; + }); + auto fnObj = ServiceRegistry::instance()->service(); + QVERIFY(fnObj); + QVERIFY(fnObj->hasBoth); + } catch(QException &e) { + QFAIL(e.what()); + } +} + +void ServiceRegistryTest::testInjection() +{ + try { + // try normal injection + auto obj1 = new TestObject9{this}; + QVERIFY(!obj1->dep); + ServiceRegistry::instance()->injectServices(obj1); + QVERIFY(obj1->dep); + obj1->deleteLater(); + + //try construct injection + auto obj2 = ServiceRegistry::instance()->constructInjected(this); + QVERIFY(obj2); + QVERIFY(obj2->dep); + obj2->deleteLater(); + + //try invalid injection + QVERIFY_EXCEPTION_THROWN(ServiceRegistry::instance()->constructInjected(this), ServiceDependencyException); + auto obj3 = new TestObject10{this}; + QVERIFY_EXCEPTION_THROWN(ServiceRegistry::instance()->injectServices(obj3), ServiceDependencyException); + obj3->deleteLater(); + } catch(QException &e) { + QFAIL(e.what()); + } +} + +void ServiceRegistryTest::testPluginRegistration() +{ + try { +#ifdef Q_OS_LINUX + QVERIFY(QFile::exists(QStringLiteral(PLUGIN_TESTDIR "/serviceregistry/libserviceregistry_testplugin.so"))); +#elif defined(Q_OS_DARWIN) + QVERIFY(QFile::exists(QStringLiteral(PLUGIN_TESTDIR "/serviceregistry/libserviceregistry_testplugin.dylib"))); + QVERIFY(QFile::exists(QStringLiteral(PLUGIN_TESTDIR "/serviceregistry/libserviceregistry_testplugin_debug.dylib"))); +#elif defined(Q_OS_WIN) + QVERIFY(QFile::exists(QStringLiteral(PLUGIN_TESTDIR "/serviceregistry/libserviceregistry_testplugin.dll"))); + QVERIFY(QFile::exists(QStringLiteral(PLUGIN_TESTDIR "/serviceregistry/libserviceregistry_testplugind.dll"))); +#endif + + qputenv("QT_PLUGIN_PATH", PLUGIN_TESTDIR); + ServiceRegistry::instance()->registerPlugin(QStringLiteral("serviceregistry"), QStringLiteral("test2")); + QVERIFY(ServiceRegistry::instance()->isRegistered()); + QVERIFY(ServiceRegistry::instance()->service()); + QCOMPARE(ServiceRegistry::instance()->service()->magicNumber(), 72); + } catch(QException &e) { + QFAIL(e.what()); + } +} + +void ServiceRegistryTest::testDestructionScopes() +{ + try { + { + auto holder0 = QSharedPointer::create(); + auto holder1 = QSharedPointer::create(); + auto holder2 = QSharedPointer::create(); + auto holder3 = QSharedPointer::create(); + + weak0 = holder0; + weak1 = holder1; + weak2 = holder2; + weak3 = holder3; + + //register and create all destruction instances + ServiceRegistry::instance()->registerObject(ServiceRegistry::DestroyOnAppQuit); + QVERIFY(ServiceRegistry::instance()->service()); + ServiceRegistry::instance()->service()->holder = holder0; + ServiceRegistry::instance()->registerObject(ServiceRegistry::DestroyOnAppDestroy); + QVERIFY(ServiceRegistry::instance()->service()); + ServiceRegistry::instance()->service()->holder = holder1; + ServiceRegistry::instance()->registerObject(ServiceRegistry::DestroyOnRegistryDestroy); + QVERIFY(ServiceRegistry::instance()->service()); + ServiceRegistry::instance()->service()->holder = holder2; + ServiceRegistry::instance()->registerObject(ServiceRegistry::DestroyNever); + QVERIFY(ServiceRegistry::instance()->service()); + ServiceRegistry::instance()->service()->holder = holder3; + } + + QVERIFY(weak0); + QVERIFY(weak1); + QVERIFY(weak2); + QVERIFY(weak3); + + bool called = false; + connect(qApp, &QCoreApplication::aboutToQuit, [&](){ //called after the method destructor + QVERIFY(!weak0); + QVERIFY(weak1); + QVERIFY(weak2); + QVERIFY(weak3); + called = true; + }); + + // "run" the main eventloop to test the signal + QTimer::singleShot(0, qApp, &QCoreApplication::quit); + QCoreApplication::exec(); + QVERIFY(called); + QVERIFY(!weak0); + QVERIFY(weak1); + QVERIFY(weak2); + QVERIFY(weak3); + } catch(QException &e) { + QFAIL(e.what()); + } +} + +// manual main for testing destruction +int main(int argc, char *argv[]) +{ + int res = EXIT_FAILURE; + { + QCoreApplication app(argc, argv); + app.setAttribute(Qt::AA_Use96Dpi, true); + ServiceRegistryTest tc; + QTEST_SET_MAIN_SOURCE_PATH + res = QTest::qExec(&tc, argc, argv); + Q_ASSERT(!ServiceRegistryTest::weak0); + Q_ASSERT(ServiceRegistryTest::weak1); + Q_ASSERT(ServiceRegistryTest::weak2); + Q_ASSERT(ServiceRegistryTest::weak3); + } + + Q_ASSERT(!ServiceRegistryTest::weak0); + Q_ASSERT(!ServiceRegistryTest::weak1); + Q_ASSERT(ServiceRegistryTest::weak2); + Q_ASSERT(ServiceRegistryTest::weak3); + + return res; +} + +#include "tst_serviceregistry.moc" diff --git a/tests/auto/mvvmcore/serviceregistrytestplugin/serviceregistrytestplugin.json b/tests/auto/mvvmcore/serviceregistrytestplugin/serviceregistrytestplugin.json new file mode 100644 index 0000000..474da5e --- /dev/null +++ b/tests/auto/mvvmcore/serviceregistrytestplugin/serviceregistrytestplugin.json @@ -0,0 +1,3 @@ +{ + "Keys" : [ "test1", "test2" ] +} diff --git a/tests/auto/mvvmcore/serviceregistrytestplugin/serviceregistrytestplugin.pro b/tests/auto/mvvmcore/serviceregistrytestplugin/serviceregistrytestplugin.pro new file mode 100644 index 0000000..5995303 --- /dev/null +++ b/tests/auto/mvvmcore/serviceregistrytestplugin/serviceregistrytestplugin.pro @@ -0,0 +1,18 @@ +TEMPLATE = lib +CONFIG += plugin + +QT = core mvvmcore + +load(qt_build_paths) + +TARGET = serviceregistry_testplugin +DESTDIR = $$MODULE_BASE_OUTDIR/tests/plugins/serviceregistry + +HEADERS += \ + testplugin.h \ + ../serviceregistry/plugintestinterface.h + +SOURCES += \ + testplugin.cpp + +DISTFILES += serviceregistrytestplugin.json diff --git a/tests/auto/mvvmcore/serviceregistrytestplugin/testplugin.cpp b/tests/auto/mvvmcore/serviceregistrytestplugin/testplugin.cpp new file mode 100644 index 0000000..fb0fc58 --- /dev/null +++ b/tests/auto/mvvmcore/serviceregistrytestplugin/testplugin.cpp @@ -0,0 +1,10 @@ +#include "testplugin.h" + +TestPlugin::TestPlugin(QObject *parent) : + QObject{parent} +{} + +int TestPlugin::magicNumber() const +{ + return 72; +} diff --git a/tests/auto/mvvmcore/serviceregistrytestplugin/testplugin.h b/tests/auto/mvvmcore/serviceregistrytestplugin/testplugin.h new file mode 100644 index 0000000..90cc896 --- /dev/null +++ b/tests/auto/mvvmcore/serviceregistrytestplugin/testplugin.h @@ -0,0 +1,18 @@ +#ifndef TESTPLUGIN_H +#define TESTPLUGIN_H + +#include "../serviceregistry/plugintestinterface.h" + +class TestPlugin : public QObject, public PluginTestInterface +{ + Q_OBJECT + Q_INTERFACES(PluginTestInterface) + Q_PLUGIN_METADATA(IID PluginTestInterfaceIId FILE "serviceregistrytestplugin.json") + +public: + TestPlugin(QObject *parent = nullptr); + + int magicNumber() const override; +}; + +#endif // TESTPLUGIN_H diff --git a/tests/auto/mvvmcore/settingsgenerator/settingsgenerator.pro b/tests/auto/mvvmcore/settingsgenerator/settingsgenerator.pro index 740187f..57869f9 100644 --- a/tests/auto/mvvmcore/settingsgenerator/settingsgenerator.pro +++ b/tests/auto/mvvmcore/settingsgenerator/settingsgenerator.pro @@ -21,8 +21,6 @@ SETTINGS_DEFINITIONS += \ SETTINGS_TRANSLATIONS += \ translatortest.xml -#_never_true: SOURCES += $$files(.ts-dummy/*) - DISTFILES += \ import_normal.xml \ import_config.xml