[Asterisk-code-review] sorcery: Add ability to insert a wizard in an object type's ... (asterisk[13])

George Joseph asteriskteam at digium.com
Thu May 7 14:30:00 CDT 2015


George Joseph has uploaded a new change for review.

  https://gerrit.asterisk.org/394

Change subject: sorcery: Add ability to insert a wizard in an object type's list
......................................................................

sorcery: Add ability to insert a wizard in an object type's list

Currently you can 'apply' a wizard to an object type but the wizard
always goes at the end of the object type's wizard list.  This patch
adds a new ast_sorcery_insert_wizard_mapping function that allows
you to insert a wizard anyplace in the list.  I.E.  You could
add a caching wizard to an object type and place it before all
wizards.

As part of this patch, the object type's wizard list was converted
from an ao2_container to an AST_VECTOR.

A new test was added to test_sorcery for this capability.

ASTERISK-25044 #close

Change-Id: I9d2469a9296b2698082c0989e25e6848dc403b57
---
M include/asterisk/sorcery.h
M main/sorcery.c
M tests/test_sorcery.c
3 files changed, 304 insertions(+), 70 deletions(-)


  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/94/394/1

diff --git a/include/asterisk/sorcery.h b/include/asterisk/sorcery.h
index 874dac2..4f1fc6b 100644
--- a/include/asterisk/sorcery.h
+++ b/include/asterisk/sorcery.h
@@ -102,6 +102,7 @@
 
 #include "asterisk/config_options.h"
 #include "asterisk/uuid.h"
+#include "asterisk/vector.h"
 
 /*! \brief Maximum size of an object type */
 #define MAX_OBJECT_TYPE 64
@@ -442,6 +443,10 @@
 #define ast_sorcery_apply_config(sorcery, name) \
 	__ast_sorcery_apply_config((sorcery), (name), AST_MODULE)
 
+
+/*! \brief Interface for a sorcery object type wizards */
+AST_VECTOR_RW(ast_sorcery_object_wizards, struct ast_sorcery_object_wizard *);
+
 /*!
  * \brief Apply default object wizard mappings
  *
@@ -462,7 +467,6 @@
 
 #define ast_sorcery_apply_default(sorcery, type, name, data) \
 	__ast_sorcery_apply_default((sorcery), (type), AST_MODULE, (name), (data))
-
 
 /*!
  * \brief Apply additional object wizard mappings
@@ -497,6 +501,72 @@
 #define ast_sorcery_apply_wizard_mapping(sorcery, type, name, data, caching) \
 	__ast_sorcery_apply_wizard_mapping((sorcery), (type), AST_MODULE, (name), (data), (caching));
 
+
+/*!
+ * \brief Pre-defined locations to insert at
+ */
+enum ast_sorcery_wizard_position {
+	ast_sorcery_wizard_position_last = -1,
+	ast_sorcery_wizard_position_first = 0,
+};
+
+/*!
+ * \brief Insert an additional object wizard mapping at a specific position
+ * in the wizard list
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object to apply to
+ * \param module The name of the module, typically AST_MODULE
+ * \param name Name of the wizard to use
+ * \param data Data to be passed to wizard
+ * \param caching Wizard should cache
+ * \param position An index to inser to or one of ast_sorcery_wizard_position
+ *
+ * \return What occurred when applying the mapping
+ *
+ * \note This should be called *after* applying default mappings
+ */
+enum ast_sorcery_apply_result __ast_sorcery_insert_wizard_mapping(struct ast_sorcery *sorcery,
+	const char *type, const char *module, const char *name, const char *data,
+	unsigned int caching, int position);
+
+/*!
+ * \brief Insert an additional object wizard mapping at a specific position
+ * in the wizard list
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object to apply to
+ * \param module The name of the module, typically AST_MODULE
+ * \param name Name of the wizard to use
+ * \param data Data to be passed to wizard
+ * \param position One of ast_sorcery_wizard_position
+ *
+ * \return What occurred when applying the mapping
+ *
+ * \note This should be called *after* applying default mappings
+ */
+#define ast_sorcery_insert_wizard_mapping(sorcery, type, name, data, caching, position) \
+	__ast_sorcery_insert_wizard_mapping((sorcery), (type), AST_MODULE, (name), (data), \
+		(caching), (position))
+
+/*!
+ * \brief Return a vector of an object type's wizards
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object
+ *
+ * \return A vector containing the object type's wizards
+ *
+ * \warning The returned vector must have AST_VECTOR_PTR_FREE()
+ * called on it after you've finished with it.
+ *
+ * \note This vector is a copy.  Modifying it will have no effect
+ * on wizard behavior.
+ *
+ */
+struct ast_sorcery_object_wizards *ast_sorcery_get_wizard_mappings(struct ast_sorcery *sorcery,
+	const char *type);
+
 /*!
  * \brief Register an object type
  *
diff --git a/main/sorcery.c b/main/sorcery.c
index d3d6f3d..71abcce 100644
--- a/main/sorcery.c
+++ b/main/sorcery.c
@@ -43,6 +43,7 @@
 #include "asterisk/taskprocessor.h"
 #include "asterisk/threadpool.h"
 #include "asterisk/json.h"
+#include "asterisk/vector.h"
 
 /* To prevent DEBUG_FD_LEAKS from interfering with things we undef open and close */
 #undef open
@@ -86,6 +87,27 @@
 /*! \brief Thread pool for observers */
 static struct ast_threadpool *threadpool;
 
+/*! \brief Structure for an internal wizard instance */
+struct ast_sorcery_internal_wizard {
+	/*! \brief Wizard interface itself */
+	struct ast_sorcery_wizard callbacks;
+
+	/*! \brief Observers */
+	struct ao2_container *observers;
+};
+
+/*! \brief Structure for a wizard instance which operates on objects */
+struct ast_sorcery_object_wizard {
+	/*! \brief Wizard interface itself */
+	struct ast_sorcery_internal_wizard *wizard;
+
+	/*! \brief Unique data for the wizard */
+	void *data;
+
+	/*! \brief Wizard is acting as an object cache */
+	unsigned int caching:1;
+};
+
 /*! \brief Structure for internal sorcery object information */
 struct ast_sorcery_object {
 	/*! \brief Unique identifier of this object */
@@ -119,7 +141,7 @@
 	sorcery_diff_handler diff;
 
 	/*! \brief Wizard instances */
-	struct ao2_container *wizards;
+	struct ast_sorcery_object_wizards wizards;
 
 	/*! \brief Object fields */
 	struct ao2_container *fields;
@@ -174,27 +196,6 @@
 
 	/*! \brief Position of the field */
 	intptr_t args[];
-};
-
-/*! \brief Structure for an internal wizard instance */
-struct ast_sorcery_internal_wizard {
-	/*! \brief Wizard interface itself */
-	struct ast_sorcery_wizard callbacks;
-
-	/*! \brief Observers */
-	struct ao2_container *observers;
-};
-
-/*! \brief Structure for a wizard instance which operates on objects */
-struct ast_sorcery_object_wizard {
-	/*! \brief Wizard interface itself */
-	struct ast_sorcery_internal_wizard *wizard;
-
-	/*! \brief Unique data for the wizard */
-	void *data;
-
-	/*! \brief Wizard is acting as an object cache */
-	unsigned int caching:1;
 };
 
 /*! \brief Full structure for sorcery */
@@ -789,7 +790,10 @@
 {
 	struct ast_sorcery_object_type *object_type = obj;
 
-	ao2_cleanup(object_type->wizards);
+	AST_VECTOR_RW_WRLOCK(&object_type->wizards);
+	AST_VECTOR_CALLBACK_VOID(&object_type->wizards, ao2_cleanup);
+	AST_VECTOR_RW_UNLOCK(&object_type->wizards);
+	AST_VECTOR_RW_FREE(&object_type->wizards);
 	ao2_cleanup(object_type->fields);
 	ao2_cleanup(object_type->observers);
 
@@ -814,7 +818,7 @@
 	}
 
 	/* Order matters for object wizards */
-	if (!(object_type->wizards = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, sorcery_wizard_cmp))) {
+	if (AST_VECTOR_RW_INIT(&object_type->wizards, 5) != 0) {
 		ao2_ref(object_type, -1);
 		return NULL;
 	}
@@ -875,9 +879,23 @@
 	ao2_cleanup(object_wizard->wizard);
 }
 
-/*! \brief Internal function which creates an object type and adds a wizard mapping */
-enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery,
-		const char *type, const char *module, const char *name, const char *data, unsigned int caching)
+/*! \brief Return a vector with the object_type's wizards */
+struct ast_sorcery_object_wizards *ast_sorcery_get_wizard_mappings(struct ast_sorcery *sorcery, const char *type)
+{
+	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+	struct ast_sorcery_object_wizards *result;
+
+	AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+	result = AST_VECTOR_CALLBACK_MULTIPLE(&object_type->wizards, AST_VECTOR_MATCH_ALL);
+	AST_VECTOR_RW_UNLOCK(&object_type->wizards);
+
+	return result;
+}
+
+/*! \brief Internal function which creates an object type and inserts a wizard mapping */
+enum ast_sorcery_apply_result __ast_sorcery_insert_wizard_mapping(struct ast_sorcery *sorcery,
+		const char *type, const char *module, const char *name, const char *data,
+		unsigned int caching, int position)
 {
 	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
 	RAII_VAR(struct ast_sorcery_internal_wizard *, wizard, ao2_find(wizards, name, OBJ_KEY), ao2_cleanup);
@@ -899,19 +917,23 @@
 		created = 1;
 	}
 
+	AST_VECTOR_RW_WRLOCK(&object_type->wizards);
 	if (!created) {
-		struct ast_sorcery_wizard *found;
+		struct ast_sorcery_object_wizard *found;
 
-		found = ao2_find(object_type->wizards, wizard, OBJ_SEARCH_OBJECT);
+#define WIZARD_COMPARE(a, b) ((a)->wizard == (b))
+		found = AST_VECTOR_GET_CMP(&object_type->wizards, wizard, WIZARD_COMPARE);
+#undef WIZARD_COMPARE
 		if (found) {
 			ast_debug(1, "Wizard %s already applied to object type %s\n",
 					wizard->callbacks.name, object_type->name);
-			ao2_cleanup(found);
+			AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 			return AST_SORCERY_APPLY_DUPLICATE;
 		}
 	}
 
 	if (wizard->callbacks.open && !(object_wizard->data = wizard->callbacks.open(data))) {
+		AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 		return AST_SORCERY_APPLY_FAIL;
 	}
 
@@ -920,7 +942,16 @@
 	object_wizard->wizard = ao2_bump(wizard);
 	object_wizard->caching = caching;
 
-	ao2_link(object_type->wizards, object_wizard);
+	if (position == ast_sorcery_wizard_position_last) {
+		position = AST_VECTOR_SIZE(&object_type->wizards);
+	}
+
+	if (AST_VECTOR_INSERT_AT(&object_type->wizards, position, object_wizard) != 0) {
+		AST_VECTOR_RW_UNLOCK(&object_type->wizards);
+		return AST_SORCERY_APPLY_FAIL;
+	}
+	AST_VECTOR_RW_UNLOCK(&object_type->wizards);
+	ao2_bump(object_wizard);
 
 	if (created) {
 		ao2_link(sorcery->types, object_type);
@@ -930,6 +961,14 @@
 		sorcery->module_name, sorcery, type, &wizard->callbacks, data, object_wizard->data);
 
 	return AST_SORCERY_APPLY_SUCCESS;
+}
+
+/*! \brief Internal function which creates an object type and adds a wizard mapping */
+enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery,
+		const char *type, const char *module, const char *name, const char *data, unsigned int caching)
+{
+	return __ast_sorcery_insert_wizard_mapping(sorcery, type, module, name, data,
+		caching, ast_sorcery_wizard_position_last);
 }
 
 enum ast_sorcery_apply_result  __ast_sorcery_apply_config(struct ast_sorcery *sorcery, const char *name, const char *module)
@@ -1260,7 +1299,9 @@
 	NOTIFY_INSTANCE_OBSERVERS(details->sorcery->observers, object_type_loading,
 		details->sorcery->module_name, details->sorcery, type->name, details->reload);
 
-	ao2_callback(type->wizards, OBJ_NODATA, sorcery_wizard_load, details);
+	AST_VECTOR_RW_RDLOCK(&type->wizards);
+	AST_VECTOR_CALLBACK(&type->wizards, sorcery_wizard_load, NULL, details, 0);
+	AST_VECTOR_RW_UNLOCK(&type->wizards);
 
 	NOTIFY_INSTANCE_OBSERVERS(details->sorcery->observers, object_type_loaded,
 		details->sorcery->module_name, details->sorcery, type->name, details->reload);
@@ -1700,7 +1741,7 @@
 {
 	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
 	void *object = NULL;
-	struct ao2_iterator i;
+	int i;
 	struct ast_sorcery_object_wizard *wizard;
 	unsigned int cached = 0;
 
@@ -1708,23 +1749,23 @@
 		return NULL;
 	}
 
-	i = ao2_iterator_init(object_type->wizards, 0);
-	for (; (wizard = ao2_iterator_next(&i)); ao2_ref(wizard, -1)) {
+	AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+	for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+		wizard = AST_VECTOR_GET(&object_type->wizards, i);
+
 		if (wizard->wizard->callbacks.retrieve_id &&
 			!(object = wizard->wizard->callbacks.retrieve_id(sorcery, wizard->data, object_type->name, id))) {
 			continue;
 		}
 
 		cached = wizard->caching;
-
-		ao2_ref(wizard, -1);
 		break;
 	}
-	ao2_iterator_destroy(&i);
 
 	if (!cached && object) {
-		ao2_callback(object_type->wizards, 0, sorcery_cache_create, object);
+		AST_VECTOR_CALLBACK(&object_type->wizards, sorcery_cache_create, NULL, object, 0);
 	}
+	AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 
 	return object;
 }
@@ -1733,7 +1774,7 @@
 {
 	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
 	void *object = NULL;
-	struct ao2_iterator i;
+	int i;
 	struct ast_sorcery_object_wizard *wizard;
 	unsigned int cached = 0;
 
@@ -1748,9 +1789,10 @@
 		}
 	}
 
-	/* Inquire with the available wizards for retrieval */
-	i = ao2_iterator_init(object_type->wizards, 0);
-	for (; (wizard = ao2_iterator_next(&i)); ao2_ref(wizard, -1)) {
+	AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+	for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+		wizard = AST_VECTOR_GET(&object_type->wizards, i);
+
 		if ((flags & AST_RETRIEVE_FLAG_MULTIPLE)) {
 			if (wizard->wizard->callbacks.retrieve_multiple) {
 				wizard->wizard->callbacks.retrieve_multiple(sorcery, wizard->data, object_type->name, object, fields);
@@ -1767,15 +1809,14 @@
 
 		cached = wizard->caching;
 
-		ao2_ref(wizard, -1);
 		break;
 	}
-	ao2_iterator_destroy(&i);
 
 	/* If we are returning a single object and it came from a non-cache source create it in any caches */
 	if (!(flags & AST_RETRIEVE_FLAG_MULTIPLE) && !cached && object) {
-		ao2_callback(object_type->wizards, 0, sorcery_cache_create, object);
+		AST_VECTOR_CALLBACK(&object_type->wizards, sorcery_cache_create, NULL, object, 0);
 	}
+	AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 
 	return object;
 }
@@ -1784,22 +1825,23 @@
 {
 	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
 	struct ao2_container *objects;
-	struct ao2_iterator i;
+	int i;
 	struct ast_sorcery_object_wizard *wizard;
 
 	if (!object_type || !(objects = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) {
 		return NULL;
 	}
 
-	i = ao2_iterator_init(object_type->wizards, 0);
-	for (; (wizard = ao2_iterator_next(&i)); ao2_ref(wizard, -1)) {
+	AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+	for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+		wizard = AST_VECTOR_GET(&object_type->wizards, i);
 		if (!wizard->wizard->callbacks.retrieve_regex) {
 			continue;
 		}
 
 		wizard->wizard->callbacks.retrieve_regex(sorcery, wizard->data, object_type->name, objects, regex);
 	}
-	ao2_iterator_destroy(&i);
+	AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 
 	return objects;
 }
@@ -1846,7 +1888,9 @@
 {
 	const struct ast_sorcery_object_details *details = object;
 	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->object->type, OBJ_KEY), ao2_cleanup);
-	RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, NULL, ao2_cleanup);
+	struct ast_sorcery_object_wizard *object_wizard = NULL;
+	struct ast_sorcery_object_wizard *found_wizard;
+	int i;
 	struct sorcery_details sdetails = {
 		.sorcery = sorcery,
 		.obj = object,
@@ -1856,14 +1900,21 @@
 		return -1;
 	}
 
-	if ((object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_create, &sdetails)) &&
-		ao2_container_count(object_type->observers)) {
-		struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
+	AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+	for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+		found_wizard = AST_VECTOR_GET(&object_type->wizards, i);
+		if (sorcery_wizard_create(found_wizard, &sdetails, 0) == (CMP_MATCH | CMP_STOP)) {
+			object_wizard = found_wizard;
+			if(ao2_container_count(object_type->observers)) {
+				struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
 
-		if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_create, invocation)) {
-			ao2_cleanup(invocation);
+				if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_create, invocation)) {
+					ao2_cleanup(invocation);
+				}
+			}
 		}
 	}
+	AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 
 	return object_wizard ? 0 : -1;
 }
@@ -1905,7 +1956,9 @@
 {
 	const struct ast_sorcery_object_details *details = object;
 	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->object->type, OBJ_KEY), ao2_cleanup);
-	RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, NULL, ao2_cleanup);
+	struct ast_sorcery_object_wizard *object_wizard = NULL;
+	struct ast_sorcery_object_wizard *found_wizard;
+	int i;
 	struct sorcery_details sdetails = {
 		.sorcery = sorcery,
 		.obj = object,
@@ -1915,14 +1968,21 @@
 		return -1;
 	}
 
-	if ((object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_update, &sdetails)) &&
-		ao2_container_count(object_type->observers)) {
-		struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
+	AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+	for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+		found_wizard = AST_VECTOR_GET(&object_type->wizards, i);
+		if (sorcery_wizard_update(found_wizard, &sdetails, 0) == (CMP_MATCH | CMP_STOP)) {
+			object_wizard = found_wizard;
+			if (ao2_container_count(object_type->observers)) {
+				struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
 
-		if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_update, invocation)) {
-			ao2_cleanup(invocation);
+				if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_update, invocation)) {
+					ao2_cleanup(invocation);
+				}
+			}
 		}
 	}
+	AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 
 	return object_wizard ? 0 : -1;
 }
@@ -1964,7 +2024,9 @@
 {
 	const struct ast_sorcery_object_details *details = object;
 	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->object->type, OBJ_KEY), ao2_cleanup);
-	RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, NULL, ao2_cleanup);
+	struct ast_sorcery_object_wizard *object_wizard = NULL;
+	struct ast_sorcery_object_wizard *found_wizard;
+	int i;
 	struct sorcery_details sdetails = {
 		.sorcery = sorcery,
 		.obj = object,
@@ -1974,14 +2036,21 @@
 		return -1;
 	}
 
-	if ((object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_delete, &sdetails)) &&
-		ao2_container_count(object_type->observers)) {
-		struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
+	AST_VECTOR_RW_RDLOCK(&object_type->wizards);
+	for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+		found_wizard = AST_VECTOR_GET(&object_type->wizards, i);
+		if (sorcery_wizard_delete(found_wizard, &sdetails, 0) == (CMP_MATCH | CMP_STOP)) {
+			object_wizard = found_wizard;
+			if (ao2_container_count(object_type->observers)) {
+				struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
 
-		if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_delete, invocation)) {
-			ao2_cleanup(invocation);
+				if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_delete, invocation)) {
+					ao2_cleanup(invocation);
+				}
+			}
 		}
 	}
+	AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 
 	return object_wizard ? 0 : -1;
 }
diff --git a/tests/test_sorcery.c b/tests/test_sorcery.c
index 9d32e3b..3da1e35 100644
--- a/tests/test_sorcery.c
+++ b/tests/test_sorcery.c
@@ -204,9 +204,17 @@
 	return 0;
 }
 
-/*! \brief Dummy sorcery wizard, not actually used so we only populate the name and nothing else */
+/*! \brief Dummy sorcery wizards, not actually used so we only populate the name and nothing else */
 static struct ast_sorcery_wizard test_wizard = {
 	.name = "test",
+	.create = sorcery_test_create,
+	.retrieve_id = sorcery_test_retrieve_id,
+	.update = sorcery_test_update,
+	.delete = sorcery_test_delete,
+};
+
+static struct ast_sorcery_wizard test_wizard2 = {
+	.name = "test2",
 	.create = sorcery_test_create,
 	.retrieve_id = sorcery_test_retrieve_id,
 	.update = sorcery_test_update,
@@ -3340,6 +3348,91 @@
 	return AST_TEST_PASS;
 }
 
+/*! \brief The following 2 structures are copied from sorcery.c
+ * so the tests can properly dereference the pointers.
+ */
+struct ast_sorcery_internal_wizard {
+	/*! \brief Wizard interface itself */
+	struct ast_sorcery_wizard callbacks;
+
+	/*! \brief Observers */
+	struct ao2_container *observers;
+};
+
+struct ast_sorcery_object_wizard {
+	/*! \brief Wizard interface itself */
+	struct ast_sorcery_internal_wizard *wizard;
+
+	/*! \brief Unique data for the wizard */
+	void *data;
+
+	/*! \brief Wizard is acting as an object cache */
+	unsigned int caching:1;
+};
+
+AST_TEST_DEFINE(wizard_apply_and_insert)
+{
+	RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+	RAII_VAR(struct ast_sorcery_wizard *, wizard1, &test_wizard, ast_sorcery_wizard_unregister);
+	RAII_VAR(struct ast_sorcery_wizard *, wizard2, &test_wizard2, ast_sorcery_wizard_unregister);
+	struct ast_sorcery_object_wizards *wizards;
+	struct ast_sorcery_object_wizard *owizard1;
+	struct ast_sorcery_object_wizard *owizard2;
+	int res = AST_TEST_PASS;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "wizard apply and insert";
+		info->category = "/main/sorcery/";
+		info->summary = "sorcery wizard1 apply and insert test";
+		info->description =
+			"sorcery wizard1 apply and insert test";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	wizard1->load = sorcery_test_load;
+	wizard1->reload = sorcery_test_load;
+
+	wizard2->load = sorcery_test_load;
+	wizard2->reload = sorcery_test_load;
+
+	if (!(sorcery = ast_sorcery_open())) {
+		ast_test_status_update(test, "Failed to open a sorcery instance\n");
+		return AST_TEST_FAIL;
+	}
+
+	ast_sorcery_wizard_register(wizard1);
+	ast_sorcery_wizard_register(wizard2);
+
+	ast_sorcery_apply_default(sorcery, "test_object_type", "test", NULL);
+	wizards = ast_sorcery_get_wizard_mappings(sorcery, "test_object_type");
+	ast_test_validate_cleanup(test, wizards, res, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(wizards) == 1, res, cleanup);
+
+	ast_test_validate_cleanup(test,
+		(ast_sorcery_insert_wizard_mapping(sorcery, "test_object_type", "test2", NULL, 0, 0) == AST_SORCERY_APPLY_SUCCESS), res, cleanup);
+
+	AST_VECTOR_FREE(wizards);
+	ast_free(wizards);
+	wizards = ast_sorcery_get_wizard_mappings(sorcery, "test_object_type");
+	ast_test_validate_cleanup(test, wizards, res, cleanup);
+	ast_test_validate_cleanup(test, AST_VECTOR_SIZE(wizards) == 2, res, cleanup);
+
+	/* Check the order.  test2 should be first */
+	owizard1 = AST_VECTOR_GET(wizards, 0);
+	owizard2 = AST_VECTOR_GET(wizards, 1);
+
+	ast_test_validate_cleanup(test, strcmp(owizard1->wizard->callbacks.name, "test2") == 0, res, cleanup);
+	ast_test_validate_cleanup(test, strcmp(owizard2->wizard->callbacks.name, "test") == 0, res, cleanup);
+
+cleanup:
+	AST_VECTOR_PTR_FREE(wizards);
+
+	return res;
+}
+
 static int unload_module(void)
 {
 	AST_TEST_UNREGISTER(wizard_registration);
@@ -3390,12 +3483,14 @@
 	AST_TEST_UNREGISTER(global_observation);
 	AST_TEST_UNREGISTER(instance_observation);
 	AST_TEST_UNREGISTER(wizard_observation);
+	AST_TEST_UNREGISTER(wizard_apply_and_insert);
 
 	return 0;
 }
 
 static int load_module(void)
 {
+	AST_TEST_REGISTER(wizard_apply_and_insert);
 	AST_TEST_REGISTER(wizard_registration);
 	AST_TEST_REGISTER(sorcery_open);
 	AST_TEST_REGISTER(apply_default);

-- 
To view, visit https://gerrit.asterisk.org/394
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I9d2469a9296b2698082c0989e25e6848dc403b57
Gerrit-PatchSet: 1
Gerrit-Project: asterisk
Gerrit-Branch: 13
Gerrit-Owner: George Joseph <george.joseph at fairview5.com>



More information about the asterisk-code-review mailing list