Commits

Christian Fischer committed dc4d813

added module api

Comments (0)

Files changed (4)

libraries/desktop/wiesel-common/module.cmake

 
 
 # collect all files which belongs to this library
-wiesel_create_module(wiesel-common ${WIESEL_SRC_DIR}/common ${WIESEL_TESTS_DIR}/tests)
+wiesel_create_module(wiesel-common ${WIESEL_SRC_DIR}/common ${WIESEL_TESTS_DIR}/common)
 
 # export include directories of this library for other targets
 wiesel_module_export_includes(wiesel-common ${WIESEL_SRC_DIR}/common)

src/common/wiesel/modules.cpp

+/**
+ * Copyright (C) 2012
+ * Christian Fischer
+ *
+ * https://bitbucket.org/baldur/wiesel/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+#include "modules.h"
+
+using namespace wiesel;
+
+
+
+ModuleRegistry *ModuleRegistry::getInstance() {
+	static ModuleRegistry instance = ModuleRegistry();
+	return &instance;
+}
+
+
+
+bool wiesel::SortModuleLoadersPredicate(IModuleLoader *a, IModuleLoader *b) {
+	// priority is the most important value
+	if (a->getPriority() != b->getPriority()) {
+		return a->getPriority() > b->getPriority();
+	}
+
+	// when the API on both modules are the same, order by API version
+	if (
+			a->getApi() == b->getApi()
+		&&	a->getApiVersion() != b->getApiVersion()
+	) {
+		return a->getApiVersion() > b->getApiVersion();
+	}
+
+	// default order is API name
+	return a->getApi() > b->getApi();
+}
+

src/common/wiesel/modules.h

+/**
+ * Copyright (C) 2012
+ * Christian Fischer
+ *
+ * https://bitbucket.org/baldur/wiesel/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+#ifndef __WIESEL_MODULES_H__
+#define __WIESEL_MODULES_H__
+
+#include <wiesel/wiesel-common.def>
+#include <wiesel/util/shared_object.h>
+
+#include <algorithm>
+#include <map>
+#include <string>
+#include <typeinfo>
+#include <vector>
+
+
+
+/**
+ * @brief Registers a new module class.
+ * Each time the module is requested, a new instance will be created.
+ * @param interface_class		Class of the interface, the new module belongs to.
+ * @param implementation_class	Class which implements the intterface above.
+ * @param factory				Function-pointer to a function, which creates an instance of the implementation_class.
+ *								The factory may return \c NULL, when a module cannot be created.
+ * @param api					(string) name of the module-API (for example: OpenGL, DirectX, ...)
+ *								When multiple modules with the same API are registered, they will be ordered by their API version.
+ * @param api_version			Version of the API as unsigned hex-number. For example, version 7.1.25 could be 0x07012500.
+ * @param priotiry				An priority value for ordering the modules.
+ *								Default values are: \ref IModuleLoader::PriorityNull, \ref IModuleLoader::PriorityLow,
+ *								\ref IModuleLoader::PriorityNormal, \ref IModuleLoader::PriorityHigh.
+ */
+#define REGISTER_MODULE(interface_class, implementation_class, factory, api, api_version, priotiry) \
+	ModuleLoaderImpl<interface_class, implementation_class> implementation_class##_Loader(factory, api, api_version, priotiry);
+
+
+/**
+ * @brief Registers a new module class.
+ * Each time the module is requested, a new instance will be created.
+ * @param interface_class		Class of the interface, the new module belongs to.
+ * @param implementation_class	Class which implements the intterface above.
+ * @param factory				Function-pointer to a function, which creates an instance of the implementation_class.
+ *								The factory may return \c NULL, when a module cannot be created.
+ * @param api					(string) name of the module-API (for example: OpenGL, DirectX, ...)
+ *								When multiple modules with the same API are registered, they will be ordered by their API version.
+ * @param api_version			Version of the API as unsigned hex-number. For example, version 7.1.25 could be 0x07012500.
+ * @param priotiry				An priority value for ordering the modules.
+ *								Default values are: \ref IModuleLoader::PriorityNull, \ref IModuleLoader::PriorityLow,
+ *								\ref IModuleLoader::PriorityNormal, \ref IModuleLoader::PriorityHigh.
+ */
+#define REGISTER_MODULE_SINGLETON(interface_class, implementation_class, factory, api, api_version, priotiry) \
+	ModuleLoaderSingletonImpl<interface_class, implementation_class> implementation_class##_Loader(factory, api, api_version, priotiry);
+
+
+namespace wiesel {
+
+	/**
+	 * @brief
+	 */
+	class WIESEL_COMMON_EXPORT Module : public virtual SharedObject
+	{
+	public:
+
+
+	};
+
+
+	class WIESEL_COMMON_EXPORT IModuleLoader;
+
+	template <class INTERFACE_CLASS>
+	class WIESEL_COMMON_EXPORT ModuleLoader;
+
+	template <class INTERFACE_CLASS, class IMPLEMENTATION_CLASS>
+	class WIESEL_COMMON_EXPORT ModuleLoaderImpl;
+
+	bool SortModuleLoadersPredicate(IModuleLoader *a, IModuleLoader *b);
+
+
+
+
+	/**
+	 * @brief The module registry remembers all modules, which are known in the current engine's context.
+	 * It's possible to register new modules via \ref REGISTER_MODULE and query for implementations of
+	 * a specific interface.
+	 */
+	class WIESEL_COMMON_EXPORT ModuleRegistry
+	{
+	private:
+		ModuleRegistry() {}
+		~ModuleRegistry() {}
+
+	public:
+		static ModuleRegistry *getInstance();
+
+	public:
+		/**
+		 * @brief Registering a new module implementation.
+		 * @param loader	A loader object, which loads an implementation for a specific interface.
+		 *					The loader will automatically be registered for the loader's interface type.
+		 */
+		template <class INTERFACE_CLASS>
+		void registerModule(ModuleLoader<INTERFACE_CLASS> *loader) {
+			module_loader_registry.insert(std::make_pair(
+							std::string(typeid(INTERFACE_CLASS).name()),
+							static_cast<IModuleLoader*>(loader)
+			));
+		}
+
+		/**
+		 * @brief Removes a module loader from the registry.
+		 * The given loader will be removed from the whole registry,
+		 * independent of which, or how many, interface(s) it was registered to.
+		 */
+		void unregisterModule(IModuleLoader *loader) {
+			std::multimap<std::string, IModuleLoader*>::iterator it=module_loader_registry.begin();
+			while(it != module_loader_registry.end()) {
+				std::multimap<std::string, IModuleLoader*>::iterator current = it++;
+				if (current->second == loader) {
+					module_loader_registry.erase(current);
+				}
+			}
+
+			return;
+		}
+
+		/**
+		 * @brief Finds all module implemenations for a specific interface class.
+		 * The resulting list will be sorted by priority and API version.
+		 */
+		template <class INTERFACE_CLASS>
+		std::vector<ModuleLoader<INTERFACE_CLASS>*> findModules() const {
+			std::vector<ModuleLoader<INTERFACE_CLASS>*> loaders;
+
+			// find all loaders and put them into our vector
+			std::multimap<std::string, IModuleLoader*>::const_iterator it;
+			std::string if_name = std::string(typeid(INTERFACE_CLASS).name());
+			for(it=module_loader_registry.find(if_name); it!=module_loader_registry.end(); it++) {
+				ModuleLoader<INTERFACE_CLASS> *loader = dynamic_cast<ModuleLoader<INTERFACE_CLASS>*>(it->second);
+				if (loader) {
+					loaders.push_back(loader);
+				}
+			}
+
+			// sort the list by priority and API version.
+			std::sort(loaders.begin(), loaders.end(), &SortModuleLoadersPredicate);
+
+			return loaders;
+		}
+
+		/**
+		 * @brief Find the first registered interface implementation.
+		 * The result will be the first implementation after sorting by priority and API version.
+		 * The result may be \c NULL, if no implementation was found.
+		 * @return The first valid interface implementaion or \c NULL, if no module is available.
+		 */
+		template <class INTERFACE_CLASS>
+		ModuleLoader<INTERFACE_CLASS>* findFirst() const {
+			std::vector<ModuleLoader<INTERFACE_CLASS>*> loaders = findModules<INTERFACE_CLASS>();
+			return loaders.empty() ? NULL : loaders[0];
+		}
+
+		/**
+		 * @brief Finds the first registered interface implementation and create amn instance of it.
+		 * The result will be a new instance of the first implementation class in a list ordered by priority and API version.
+		 * The result may be \c NULL, if no implementation was found.
+		 * @return An instance of the first valid interface implementation or \c NULL, if no module is available.
+		 */
+		template <class INTERFACE_CLASS>
+		INTERFACE_CLASS* createFirst() const {
+			std::vector<ModuleLoader<INTERFACE_CLASS>*> loaders = findModules<INTERFACE_CLASS>();
+			for(std::vector<ModuleLoader<INTERFACE_CLASS>*>::iterator it=loeaders.begin(); it!=loaders.end(); it++) {
+				INTERFACE_CLASS *module = (*it)->create();
+				if (module) {
+					return module;
+				}
+			}
+
+			return NULL;
+		}
+
+	private:
+		std::multimap<std::string, IModuleLoader*>	module_loader_registry;
+	};
+
+
+
+
+
+	/**
+	 * @brief Basic interface for module loaders with no concrete type.
+	 */
+	class WIESEL_COMMON_EXPORT IModuleLoader
+	{
+	public:
+		/**
+		 * @brief Determines the version of the module's API.
+		 * Should be defined in hexadecimal form like 0x03020800u for version 3.2.8
+		 */
+		typedef unsigned int	ApiVersion;
+
+		/**
+		 * @brief A priority value to configure how important a specigic module would be.
+		 * Default value for normal priority would be \ref PriorityNormal.
+		 */
+		typedef unsigned short	Priority;
+
+		enum {
+			PriorityNull	=     0,	//!< Special priority value for fallback modules, which does nothing.
+			PriorityLow		=    10,	//!< Modules with priority lower than default modules.
+			PriorityNormal	=   100,	//!< Default modules.
+			PriorityHigh	=  1000,	//!< Priority for modules, which should be preferred.
+		};
+
+	private:
+		IModuleLoader() {}
+
+	protected:
+		IModuleLoader(const std::string &api, ApiVersion version, Priority priority) :
+			api(api),
+			version(version),
+			priority(priority)
+		{ }
+
+		virtual ~IModuleLoader() {}
+
+	public:
+		inline const std::string& getApi() const {
+			return api;
+		}
+
+		inline ApiVersion getApiVersion() const {
+			return version;
+		}
+
+		inline Priority getPriority() const {
+			return priority;
+		}
+
+	private:
+		std::string		api;
+		ApiVersion		version;
+		Priority		priority;
+	};
+
+
+
+	/**
+	 * @brief A class for loading a module of a specific interface.
+	 */
+	template <class INTERFACE_CLASS>
+	class WIESEL_COMMON_EXPORT ModuleLoader : public IModuleLoader
+	{
+	protected:
+		/**
+		 * @brief Initialize the module loader.
+		 */
+		ModuleLoader(const std::string &api, ApiVersion version, Priority priority) :
+		IModuleLoader(api, version, priority) {
+			ModuleRegistry::getInstance()->registerModule<INTERFACE_CLASS>(this);
+			return;
+		}
+
+		virtual ~ModuleLoader() {
+			ModuleRegistry::getInstance()->unregisterModule(this);
+		}
+
+	public:
+		/**
+		 * @brief Creates the instance of the module interface class.
+		 * This may be a shared instance.
+		 */
+		virtual INTERFACE_CLASS* create() = 0;
+	};
+
+
+
+	/**
+	 * @brief Implements the module loader for a concrete implementation class.
+	 * This class would be usually be instanciated via macro \ref REGISTER_MODULE.
+	 */
+	template <class INTERFACE_CLASS, class IMPLEMENTATION_CLASS>
+	class WIESEL_COMMON_EXPORT ModuleLoaderImpl : public ModuleLoader<INTERFACE_CLASS>
+	{
+	public:
+		/**
+		 * @brief Alias type for the factory function, which creates the concrete instance.
+		 */
+		typedef IMPLEMENTATION_CLASS* (*Factory)();
+
+		/**
+		 * @brief Creates the module loader object.
+		 * The loader will be automatically registered into module registry.
+		 * @see REGISTER_MODULE
+		 */
+		ModuleLoaderImpl(Factory factory, const std::string &api, IModuleLoader::ApiVersion version, IModuleLoader::Priority priority=IModuleLoader::PriorityNormal) :
+		ModuleLoader<INTERFACE_CLASS>(api, version, priority) {
+			this->module_factory = factory;
+			return;
+		}
+
+		virtual ~ModuleLoaderImpl() {
+			return;
+		}
+
+	public:
+		/**
+		 * @brief Creates the instance of the module interface class.
+		 * Each call creates a new instance.
+		 */
+		virtual INTERFACE_CLASS* create() {
+			return (*module_factory)();
+		}
+
+	private:
+		Factory module_factory;
+	};
+
+
+
+	/**
+	 * @brief Implements the module loader for a concrete implementation class.
+	 * This class would be usually be instanciated via macro \ref REGISTER_MODULE.
+	 */
+	template <class INTERFACE_CLASS, class IMPLEMENTATION_CLASS>
+	class WIESEL_COMMON_EXPORT ModuleLoaderSingletonImpl : public ModuleLoader<INTERFACE_CLASS>
+	{
+	public:
+		/**
+		 * @brief Alias type for the factory function, which creates the concrete instance.
+		 */
+		typedef IMPLEMENTATION_CLASS* (*Factory)();
+
+		/**
+		 * @brief Creates the module loader object.
+		 * The loader will be automatically registered into module registry.
+		 * @see REGISTER_MODULE
+		 */
+		ModuleLoaderSingletonImpl(Factory factory, const std::string &api, IModuleLoader::ApiVersion version, IModuleLoader::Priority priority=IModuleLoader::PriorityNormal) :
+		ModuleLoader<INTERFACE_CLASS>(api, version, priority) {
+			this->module_factory = factory;
+			this->instance       = NULL;
+			return;
+		}
+
+		virtual ~ModuleLoaderSingletonImpl() {
+			release();
+			return;
+		}
+
+	public:
+		/**
+		 * @brief Creates the instance of the module interface class.
+		 * The instance will be shared and will be released on application end.
+		 */
+		virtual INTERFACE_CLASS* create() {
+			if (instance == NULL) {
+				instance = (*module_factory)();
+				instance->retain();
+			}
+
+			return instance;
+		}
+
+		/**
+		 * @brief Release the instance, if any.
+		 * If the instance is retained by other parts of the application, the object will not be destroyed,
+		 * but the module loader will create a new instance next time calling \ref create().
+		 */
+		virtual void release() {
+			if (instance) {
+				instance->release();
+				instance = NULL;
+			}
+
+			return;
+		}
+
+	private:
+		IMPLEMENTATION_CLASS*	instance;
+		Factory					module_factory;
+	};
+}
+
+#endif /* __WIESEL_MODULES_H__ */

tests/common/modules.cpp

+/**
+ * Copyright (C) 2012
+ * Christian Fischer
+ *
+ * https://bitbucket.org/baldur/wiesel/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+#include "gtest/gtest.h"
+
+#include <wiesel/modules.h>
+
+
+using namespace wiesel;
+
+
+
+class ITestModule : public Module
+{
+public:
+	ITestModule() {
+		return;
+	}
+
+	virtual ~ITestModule() {
+		return;
+	}
+
+public:
+	virtual int getTestValue() = 0;
+};
+
+
+
+
+class TestMod17 : public ITestModule
+{
+public:
+	TestMod17() {
+		return;
+	}
+
+	static TestMod17* create() {
+		return new TestMod17();
+	}
+
+	virtual ~TestMod17() {
+		return;
+	}
+
+	virtual int getTestValue() {
+		return 17;
+	}
+};
+
+
+class TestMod23 : public ITestModule
+{
+public:
+	TestMod23() {
+		return;
+	}
+
+	static TestMod23* create() {
+		return new TestMod23();
+	}
+
+	virtual ~TestMod23() {
+		return;
+	}
+
+	virtual int getTestValue() {
+		return 23;
+	}
+};
+
+
+class TestMod42 : public ITestModule
+{
+public:
+	TestMod42() {
+		return;
+	}
+
+	static TestMod42* create() {
+		return new TestMod42();
+	}
+
+	virtual ~TestMod42() {
+		return;
+	}
+
+	virtual int getTestValue() {
+		return 42;
+	}
+};
+
+
+
+
+class ITrackingModule : public Module
+{
+};
+
+
+class TrackingModuleImpl : public ITrackingModule
+{
+public:
+	static TrackingModuleImpl *create() {
+		return new TrackingModuleImpl();
+	}
+
+	TrackingModuleImpl() {
+		++construction_count;
+	}
+
+	virtual ~TrackingModuleImpl() {
+		++destruction_count;
+	}
+
+public:
+	static int construction_count;
+	static int destruction_count;
+};
+
+int TrackingModuleImpl::construction_count	= 0;
+int TrackingModuleImpl::destruction_count	= 0;
+
+
+
+
+
+/**
+ * Test if the module loaders work.
+ */
+TEST(ModuleApi, ModuleLoaders) {
+	// for shorter code...
+	typedef std::vector<ModuleLoader<ITestModule>*> TestModList;
+
+	// there shouldn't be any module in the registry
+	TestModList list1 = ModuleRegistry::getInstance()->findModules<ITestModule>();
+	ASSERT_TRUE(list1.empty());
+
+	// create a new scope
+	{
+		// register the module within this scope, so it should be gone when leaving the scope
+		REGISTER_MODULE(ITestModule, TestMod17, &TestMod17::create, "TestMod", 0x01000000u, IModuleLoader::PriorityNormal);
+
+		// now we should have exactly one module registered
+		TestModList list2 = ModuleRegistry::getInstance()->findModules<ITestModule>();
+		ASSERT_EQ(1u, list2.size());
+
+		// get the module loader
+		ModuleLoader<ITestModule> *test_loader = list2[0];
+
+		// get the module
+		ITestModule *testmod1 = test_loader->create();
+		ASSERT_TRUE(NULL != testmod1);
+
+		// compare the result of the module
+		ASSERT_EQ(17, testmod1->getTestValue());
+	}
+
+	// after we left the scope, the registry should be empty
+	TestModList list3 = ModuleRegistry::getInstance()->findModules<ITestModule>();
+	ASSERT_TRUE(list3.empty());
+
+	return;
+}
+
+
+TEST(ModuleApi, ModuleConstruction) {
+	// for shorter code...
+	typedef std::vector<ModuleLoader<ITrackingModule>*> TrackingModList;
+
+	// reset vars
+	TrackingModuleImpl::construction_count	= 0;
+	TrackingModuleImpl::destruction_count	= 0;
+
+	ITrackingModule *module1 = NULL;
+	ITrackingModule *module2 = NULL;
+
+	// create a new scope
+	{
+		// register the module within this scope, so it should be gone when leaving the scope
+		REGISTER_MODULE(ITrackingModule, TrackingModuleImpl, &TrackingModuleImpl::create, "TrackingModule", 0x01000000u, IModuleLoader::PriorityNormal);
+
+		// no module should be created at this moment
+		ASSERT_EQ(0, TrackingModuleImpl::construction_count);
+		ASSERT_EQ(0, TrackingModuleImpl::destruction_count);
+
+		// get the module loader
+		TrackingModList list = ModuleRegistry::getInstance()->findModules<ITrackingModule>();
+		ASSERT_EQ(1u, list.size());
+
+		// there should still no module instance
+		ASSERT_EQ(0, TrackingModuleImpl::construction_count);
+		ASSERT_EQ(0, TrackingModuleImpl::destruction_count);
+
+		// now create the first module...
+		module1 = list[0]->create();
+		module1->retain();
+
+		// one creation, but no destruction.
+		ASSERT_EQ(1, TrackingModuleImpl::construction_count);
+		ASSERT_EQ(0, TrackingModuleImpl::destruction_count);
+
+		// create the second module
+		module2 = list[0]->create();
+		module2->retain();
+
+		// two creations, but still no destruction
+		ASSERT_EQ(2, TrackingModuleImpl::construction_count);
+		ASSERT_EQ(0, TrackingModuleImpl::destruction_count);
+
+		// release the first module
+		module1->release();
+		module1 = NULL;
+
+		// the first one should be destructed, but the first one should be still alive.
+		ASSERT_EQ(2, TrackingModuleImpl::construction_count);
+		ASSERT_EQ(1, TrackingModuleImpl::destruction_count);
+	}
+
+	// we left scope. the module loader should be destroyed
+	ASSERT_TRUE(ModuleRegistry::getInstance()->findModules<ITrackingModule>().empty());
+
+	// ... but the second module itself should still be alive.
+	ASSERT_EQ(2, TrackingModuleImpl::construction_count);
+	ASSERT_EQ(1, TrackingModuleImpl::destruction_count);
+
+	// now release the second module
+	module2->release();
+	module2 = NULL;
+
+	// ... and check, if it's really gone
+	ASSERT_EQ(2, TrackingModuleImpl::construction_count);
+	ASSERT_EQ(2, TrackingModuleImpl::destruction_count);
+
+	return;
+}
+
+
+TEST(ModuleApi, ModuleConstructionSingleton) {
+	// for shorter code...
+	typedef std::vector<ModuleLoader<ITrackingModule>*> TrackingModList;
+
+	// reset vars
+	TrackingModuleImpl::construction_count	= 0;
+	TrackingModuleImpl::destruction_count	= 0;
+
+	ITrackingModule *module1 = NULL;
+	ITrackingModule *module2 = NULL;
+
+	// create a new scope
+	{
+		// register the module within this scope, so it should be gone when leaving the scope
+		REGISTER_MODULE_SINGLETON(ITrackingModule, TrackingModuleImpl, &TrackingModuleImpl::create, "TrackingModule", 0x01000000u, IModuleLoader::PriorityNormal);
+
+		// no module should be created at this moment
+		ASSERT_EQ(0, TrackingModuleImpl::construction_count);
+		ASSERT_EQ(0, TrackingModuleImpl::destruction_count);
+
+		// get the module loader
+		TrackingModList list = ModuleRegistry::getInstance()->findModules<ITrackingModule>();
+		ASSERT_EQ(1u, list.size());
+
+		// there should still no module instance
+		ASSERT_EQ(0, TrackingModuleImpl::construction_count);
+		ASSERT_EQ(0, TrackingModuleImpl::destruction_count);
+
+		// now create the first module...
+		module1 = list[0]->create();
+		module1->retain();
+
+		// one creation, but no destruction.
+		ASSERT_EQ(1, TrackingModuleImpl::construction_count);
+		ASSERT_EQ(0, TrackingModuleImpl::destruction_count);
+
+		// the reference count of our module should be 2 (module1 + singleton reference)
+		ASSERT_EQ(2, module1->getReferenceCount());
+
+		// create the second module
+		module2 = list[0]->create();
+		module2->retain();
+
+		// we should got the same object
+		ASSERT_EQ(module1, module2);
+
+		// since the module is registered as singleton, there should still be only one instance
+		ASSERT_EQ(1, TrackingModuleImpl::construction_count);
+		ASSERT_EQ(0, TrackingModuleImpl::destruction_count);
+
+		// the reference count of our module should be 3 (module1, module2, singleton reference)
+		ASSERT_EQ(3, module1->getReferenceCount());
+		ASSERT_EQ(3, module2->getReferenceCount());
+
+		// release the first module
+		module1->release();
+		module1 = NULL;
+
+		// there should still be only one valid object, it should not be released
+		ASSERT_EQ(1, TrackingModuleImpl::construction_count);
+		ASSERT_EQ(0, TrackingModuleImpl::destruction_count);
+
+		// ... but the ref count should have been decreased
+		ASSERT_EQ(2, module2->getReferenceCount());
+	}
+
+	// we left scope. the module loader should be destroyed
+	ASSERT_TRUE(ModuleRegistry::getInstance()->findModules<ITrackingModule>().empty());
+
+	// ... but the module itself should still be alive.
+	ASSERT_EQ(1, TrackingModuleImpl::construction_count);
+	ASSERT_EQ(0, TrackingModuleImpl::destruction_count);
+
+	// ... and only one reference left
+	ASSERT_EQ(1, module2->getReferenceCount());
+
+	// now release the second module
+	module2->release();
+	module2 = NULL;
+
+	// ... and check, if it's really gone
+	ASSERT_EQ(1, TrackingModuleImpl::construction_count);
+	ASSERT_EQ(1, TrackingModuleImpl::destruction_count);
+
+	return;
+}
+
+
+/**
+ * Test if modules are sorted correctly by their API version.
+ */
+TEST(ModuleApi, ApiVersionOrder) {
+	// for shorter code...
+	typedef std::vector<ModuleLoader<ITestModule>*> TestModList;
+
+	// there shouldn't be any module in the registry
+	TestModList list1 = ModuleRegistry::getInstance()->findModules<ITestModule>();
+	ASSERT_TRUE(list1.empty());
+
+	// create a new scope
+	{
+		// register the module within this scope, so it should be gone when leaving the scope
+		REGISTER_MODULE(ITestModule, TestMod17, &TestMod17::create, "TestMod", 0x01001700u, IModuleLoader::PriorityNormal);
+		REGISTER_MODULE(ITestModule, TestMod42, &TestMod42::create, "TestMod", 0x01004200u, IModuleLoader::PriorityNormal);
+		REGISTER_MODULE(ITestModule, TestMod23, &TestMod23::create, "TestMod", 0x01002300u, IModuleLoader::PriorityNormal);
+
+		// now we should have exactly one module registered
+		TestModList list2 = ModuleRegistry::getInstance()->findModules<ITestModule>();
+		ASSERT_EQ(3u, list2.size());
+
+		// get the moduiles
+		ITestModule *testmod1 = list2[0]->create();
+		ITestModule *testmod2 = list2[1]->create();
+		ITestModule *testmod3 = list2[2]->create();
+
+		// check if all modules were created
+		ASSERT_TRUE(NULL != testmod1);
+		ASSERT_TRUE(NULL != testmod2);
+		ASSERT_TRUE(NULL != testmod3);
+
+		// compare the result of the module
+		EXPECT_EQ(42, testmod1->getTestValue());
+		EXPECT_EQ(23, testmod2->getTestValue());
+		EXPECT_EQ(17, testmod3->getTestValue());
+	}
+
+	// after we left the scope, the registry should be empty
+	TestModList list3 = ModuleRegistry::getInstance()->findModules<ITestModule>();
+	ASSERT_TRUE(list3.empty());
+
+	return;
+}
+
+
+/**
+ * Test if modules are sorted correctly by their priority.
+ * The API version should be ignored, because their API names are different.
+ */
+TEST(ModuleApi, PriorityOrder) {
+	// for shorter code...
+	typedef std::vector<ModuleLoader<ITestModule>*> TestModList;
+
+	// there shouldn't be any module in the registry
+	TestModList list1 = ModuleRegistry::getInstance()->findModules<ITestModule>();
+	ASSERT_TRUE(list1.empty());
+
+	// create a new scope
+	{
+		// register the module within this scope, so it should be gone when leaving the scope
+		REGISTER_MODULE(ITestModule, TestMod23, &TestMod23::create, "TestMod3", 0x01002300u, IModuleLoader::PriorityNormal);
+		REGISTER_MODULE(ITestModule, TestMod42, &TestMod42::create, "TestMod2", 0x01004200u, IModuleLoader::PriorityLow);
+		REGISTER_MODULE(ITestModule, TestMod17, &TestMod17::create, "TestMod1", 0x01001700u, IModuleLoader::PriorityHigh);
+
+		// now we should have exactly one module registered
+		TestModList list2 = ModuleRegistry::getInstance()->findModules<ITestModule>();
+		ASSERT_EQ(3u, list2.size());
+
+		// get the moduiles
+		ITestModule *testmod1 = list2[0]->create();
+		ITestModule *testmod2 = list2[1]->create();
+		ITestModule *testmod3 = list2[2]->create();
+
+		// check if all modules were created
+		ASSERT_TRUE(NULL != testmod1);
+		ASSERT_TRUE(NULL != testmod2);
+		ASSERT_TRUE(NULL != testmod3);
+
+		// compare the result of the module
+		EXPECT_EQ(17, testmod1->getTestValue());
+		EXPECT_EQ(23, testmod2->getTestValue());
+		EXPECT_EQ(42, testmod3->getTestValue());
+	}
+
+	// after we left the scope, the registry should be empty
+	TestModList list3 = ModuleRegistry::getInstance()->findModules<ITestModule>();
+	ASSERT_TRUE(list3.empty());
+
+	return;
+}
+
+
+/**
+ * Test if modules are sorted correctly, when using both api version and priority.
+ */
+TEST(ModuleApi, CombinedOrder) {
+	// for shorter code...
+	typedef std::vector<ModuleLoader<ITestModule>*> TestModList;
+
+	// there shouldn't be any module in the registry
+	TestModList list1 = ModuleRegistry::getInstance()->findModules<ITestModule>();
+	ASSERT_TRUE(list1.empty());
+
+	// create a new scope
+	{
+		// register the module within this scope, so it should be gone when leaving the scope
+		REGISTER_MODULE(ITestModule, TestMod23, &TestMod23::create, "TestMod", 0x01002300u, IModuleLoader::PriorityNormal);
+		REGISTER_MODULE(ITestModule, TestMod42, &TestMod42::create, "TestMod", 0x01004200u, IModuleLoader::PriorityNormal);
+		REGISTER_MODULE(ITestModule, TestMod17, &TestMod17::create, "TestMod", 0x01001700u, IModuleLoader::PriorityHigh);
+
+		// now we should have exactly one module registered
+		TestModList list2 = ModuleRegistry::getInstance()->findModules<ITestModule>();
+		ASSERT_EQ(3u, list2.size());
+
+		// get the moduiles
+		ITestModule *testmod1 = list2[0]->create();
+		ITestModule *testmod2 = list2[1]->create();
+		ITestModule *testmod3 = list2[2]->create();
+
+		// check if all modules were created
+		ASSERT_TRUE(NULL != testmod1);
+		ASSERT_TRUE(NULL != testmod2);
+		ASSERT_TRUE(NULL != testmod3);
+
+		// compare the result of the module
+		EXPECT_EQ(17, testmod1->getTestValue());
+		EXPECT_EQ(42, testmod2->getTestValue());
+		EXPECT_EQ(23, testmod3->getTestValue());
+	}
+
+	// after we left the scope, the registry should be empty
+	TestModList list3 = ModuleRegistry::getInstance()->findModules<ITestModule>();
+	ASSERT_TRUE(list3.empty());
+
+	return;
+}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.