[asterisk-commits] file: branch file/sorcery r378198 - in /team/file/sorcery: include/asterisk/ ...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Dec 24 10:30:48 CST 2012


Author: file
Date: Mon Dec 24 10:30:43 2012
New Revision: 378198

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=378198
Log:
Implement all of the wizard callbacks in res_sorcery_memory, add support for returning multiple objects from retrieve, flush out more stuff, and add more tests.

Modified:
    team/file/sorcery/include/asterisk/sorcery.h
    team/file/sorcery/main/sorcery.c
    team/file/sorcery/res/res_sorcery_memory.c
    team/file/sorcery/tests/test_sorcery.c

Modified: team/file/sorcery/include/asterisk/sorcery.h
URL: http://svnview.digium.com/svn/asterisk/team/file/sorcery/include/asterisk/sorcery.h?view=diff&rev=378198&r1=378197&r2=378198
==============================================================================
--- team/file/sorcery/include/asterisk/sorcery.h (original)
+++ team/file/sorcery/include/asterisk/sorcery.h Mon Dec 24 10:30:43 2012
@@ -37,9 +37,7 @@
 #endif
 
 #include "asterisk/config_options.h"
-
-/*! \brief Maximum size of an object unique identifier */
-#define MAX_OBJECT_ID 64
+#include "asterisk/uuid.h"
 
 /*! \brief Maximum size of an object type */
 #define MAX_OBJECT_TYPE 64
@@ -53,6 +51,12 @@
 enum ast_sorcery_retrieve_flags {
 	/*! \brief Default retrieval flags */
 	AST_RETRIEVE_FLAG_DEFAULT = 0,
+
+	/*! \brief Return all matching objects */
+	AST_RETRIEVE_FLAG_MULTIPLE = (1 << 0),
+
+        /*! \brief Perform no matching, return all objects */
+        AST_RETRIEVE_FLAG_ALL = (1 << 1),
 };
 
 /*!
@@ -68,6 +72,9 @@
 	/*! \brief End of retrieval parameters */
 	AST_RETRIEVE_PARAM_END,
 };
+
+/*! \brief Forward declaration for the sorcery main structure */
+struct ast_sorcery;
 
 /*!
  * \brief A callback function for translating a value into a string
@@ -82,6 +89,25 @@
  */
 typedef int (*sorcery_field_handler)(const void *obj, const intptr_t *args, char *buf, size_t len);
 
+/*!
+ * \brief A callback function for performing a transformation on an object set
+ *
+ * \param set The existing object set
+ *
+ * \retval non-NULL new object set if changed
+ * \retval NULL if no changes present
+ *
+ * \note The returned ast_variable list must be *new*. You can not return the input set.
+ */
+typedef struct ast_variable *(*sorcery_transform_handler)(struct ast_variable *set);
+
+/*!
+ * \brief A callback function for when an object set is successfully applied to an object
+ *
+ * \param obj The object itself
+ */
+typedef void (*sorcery_apply_handler)(void *obj);
+
 /*! \brief Interface for a sorcery wizard */
 struct ast_sorcery_wizard {
 	/*! \brief Name of the wizard */
@@ -93,14 +119,23 @@
 	/*! \brief Callback for opening a wizard */
 	void *(*open)(const char *data);
 
+	/*! \brief Optional callback for loading persistent objects */
+	void (*load)(void *data);
+
+	/*! \brief Optional callback for reloading persistent objects */
+	void (*reload)(void *data);
+
 	/*! \brief Callback for creating an object */
 	int (*create)(void *data, void *object);
 
 	/*! \brief Callback for retrieving an object using an id */
-	void *(*retrieve_id)(void *data, const char *id);
+	void *(*retrieve_id)(const struct ast_sorcery *sorcery, void *data, const char *id);
 
 	/*! \brief Optional callback for retrieving an object using fields */
-	void *(*retrieve_fields)(void *data, const struct ast_variable *fields);
+	void *(*retrieve_fields)(const struct ast_sorcery *sorcery, void *data, const struct ast_variable *fields);
+
+	/*! \brief Optional callback for retrieving multiple objects using some optional field criteria */
+	void (*retrieve_multiple)(const struct ast_sorcery *sorcery, void *data, struct ao2_container *objects, const struct ast_variable *fields);
 
 	/*! \brief Callback for updating an object */
 	int (*update)(void *data, void *object);
@@ -112,13 +147,10 @@
 	void (*close)(void *data);
 };
 
-/*! \brief Forward declaration for the sorcery main structure */
-struct ast_sorcery;
-
 /*! \brief Structure which contains details about a sorcery object */
 struct ast_sorcery_object_details {
 	/*! \brief Unique identifier of this object */
-	char id[MAX_OBJECT_ID];
+	char id[AST_UUID_STR_LEN];
 
 	/*! \brief Type of object */
 	char type[MAX_OBJECT_TYPE];
@@ -204,11 +236,13 @@
  * \param sorcery Pointer to a sorcery structure
  * \param type Type of object
  * \param aco_type Configuration framework details for type
- *
- * \retval 0 success
- * \retval -1 failure
- */
-int ast_sorcery_object_register(struct ast_sorcery *sorcery, const char *type, struct aco_type *aco_type);
+ * \param transform Optional transformation callback
+ * \param apply Optional object set apply callback
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_sorcery_object_register(struct ast_sorcery *sorcery, const char *type, struct aco_type *aco_type, sorcery_transform_handler transform, sorcery_apply_handler apply);
 
 /*!
  * \brief Register a field within an object
@@ -260,6 +294,20 @@
     __ast_sorcery_object_field_register(sorcery, type, name, default_val, OPT_CUSTOM_T, config_handler, sorcery_handler, flags, VA_NARGS(__VA_ARGS__), __VA_ARGS__);
 
 /*!
+ * \brief Inform any wizards to load persistent objects
+ *
+ * \param sorcery Pointer to a sorcery structure
+ */
+void ast_sorcery_load(const struct ast_sorcery *sorcery);
+
+/*!
+ * \brief Inform any wizards to reload persistent objects
+ *
+ * \param sorcery Pointer to a sorcery structure
+ */
+void ast_sorcery_reload(const struct ast_sorcery *sorcery);
+
+/*!
  * \brief Increase the reference count of a sorcery structure
  *
  * \param sorcery Pointer to a sorcery structure
@@ -275,7 +323,7 @@
  * \retval non-NULL success
  * \retval NULL if error occurred
  *
- * \note The reference to the returned container must be released using ao2_ref or ao2_cleanup
+ * \note The returned ast_variable list must be destroyed using ast_variables_destroy
  */
 struct ast_variable *ast_sorcery_objectset_create(const struct ast_sorcery *sorcery, const void *object);
 
@@ -352,7 +400,7 @@
  * \retval 0 success
  * \retval -1 failure
  */
-int ast_sorcery_create(struct ast_sorcery *sorcery, void *object);
+int ast_sorcery_create(const struct ast_sorcery *sorcery, void *object);
 
 /*!
  * \brief Retrieve an object or multiple objects
@@ -366,6 +414,9 @@
  * \retval NULL if not found
  *
  * \note The parameters must end with AST_RETRIEVE_PARAM_END
+ *
+ * \note If the AST_RETRIEVE_FLAG_MULTIPLE flag is specified the returned value will be an
+ *       ao2_container that must be unreferenced after use.
  */
 void *ast_sorcery_retrieve(const struct ast_sorcery *sorcery, const char *type, unsigned int flags, ...);
 
@@ -378,7 +429,7 @@
  * \retval 0 success
  * \retval -1 failure
  */
-int ast_sorcery_update(struct ast_sorcery *sorcery, void *object);
+int ast_sorcery_update(const struct ast_sorcery *sorcery, void *object);
 
 /*!
  * \brief Delete an object
@@ -389,7 +440,7 @@
  * \retval 0 success
  * \retval -1 failure
  */
-int ast_sorcery_delete(struct ast_sorcery *sorcery, void *object);
+int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object);
 
 /*!
  * \brief Decrease the reference count of a sorcery structure

Modified: team/file/sorcery/main/sorcery.c
URL: http://svnview.digium.com/svn/asterisk/team/file/sorcery/main/sorcery.c?view=diff&rev=378198&r1=378197&r2=378198
==============================================================================
--- team/file/sorcery/main/sorcery.c (original)
+++ team/file/sorcery/main/sorcery.c Mon Dec 24 10:30:43 2012
@@ -53,6 +53,12 @@
 	/*! \brief Unique name of the object type */
 	char name[MAX_OBJECT_TYPE];
 
+	/*! \brief Optional transformation callback */
+	sorcery_transform_handler transform;
+
+	/*! \brief Optional object set apply callback */
+	sorcery_apply_handler apply;
+
 	/*! \brief Wizard instances */
 	struct ao2_container *wizards;
 
@@ -443,7 +449,7 @@
 	return sorcery_apply_wizard_mapping(sorcery, type, name, data);
 }
 
-int ast_sorcery_object_register(struct ast_sorcery *sorcery, const char *type, struct aco_type *aco_type)
+int ast_sorcery_object_register(struct ast_sorcery *sorcery, const char *type, struct aco_type *aco_type, sorcery_transform_handler transform, sorcery_apply_handler apply)
 {
 	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
 
@@ -451,6 +457,7 @@
 		return -1;
 	}
 
+	object_type->transform = transform;
 	object_type->file->types[0] = aco_type;
 	object_type->file->types[1] = NULL;
 
@@ -516,6 +523,56 @@
 	return 0;
 }
 
+static int sorcery_wizard_load(void *obj, void *arg, int flags)
+{
+	struct ast_sorcery_object_wizard *wizard = obj;
+
+	if (wizard->wizard->load) {
+		wizard->wizard->load(wizard->data);
+	}
+
+	return 0;
+}
+
+static int sorcery_object_load(void *obj, void *arg, int flags)
+{
+	struct ast_sorcery_object_type *type = obj;
+
+	ao2_callback(type->wizards, OBJ_NODATA, sorcery_wizard_load, NULL);
+
+	return 0;
+}
+
+void ast_sorcery_load(const struct ast_sorcery *sorcery)
+{
+	ao2_callback(sorcery->types, OBJ_NODATA, sorcery_object_load, NULL);
+}
+
+static int sorcery_wizard_reload(void *obj, void *arg, int flags)
+{
+	struct ast_sorcery_object_wizard *wizard = obj;
+
+	if (wizard->wizard->reload) {
+		wizard->wizard->reload(wizard->data);
+	}
+
+	return 0;
+}
+
+static int sorcery_object_reload(void *obj, void *arg, int flags)
+{
+	struct ast_sorcery_object_type *type = obj;
+
+	ao2_callback(type->wizards, OBJ_NODATA, sorcery_wizard_reload, NULL);
+
+	return 0;
+}
+
+void ast_sorcery_reload(const struct ast_sorcery *sorcery)
+{
+	ao2_callback(sorcery->types, OBJ_NODATA, sorcery_object_reload, NULL);
+}
+
 void ast_sorcery_ref(struct ast_sorcery *sorcery)
 {
 	ao2_ref(sorcery, +1);
@@ -571,6 +628,7 @@
 {
 	const struct ast_sorcery_object_details *details = object;
 	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->type, OBJ_KEY), ao2_cleanup);
+	RAII_VAR(struct ast_variable *, transformed, NULL, ast_variables_destroy);
 	struct ast_variable *field;
 	int res = 0;
 
@@ -578,10 +636,20 @@
 		return -1;
 	}
 
-	for (field = objectset; field; field = field->next) {
+	if (object_type->transform && (transformed = object_type->transform(objectset))) {
+		field = transformed;
+	} else {
+		field = objectset;
+	}
+
+	for (; field; field = field->next) {
 		if ((res = aco_process_var(object_type->type, details->id, field, object))) {
 			break;
 		}
+	}
+
+	if (!res && object_type->apply) {
+		object_type->apply(object);
 	}
 
 	return res;
@@ -639,11 +707,24 @@
 
 	if (!object_type || !object_type->type ||
 	    !object_type->type->item_alloc ||
-	    !(details = object_type->type->item_alloc(id))) {
-		return NULL;
-	}
-
-	ast_copy_string(details->id, id, sizeof(details->id));
+	    !(details = object_type->type->item_alloc(""))) {
+		return NULL;
+	}
+
+	if (ast_strlen_zero(id)) {
+		struct ast_uuid *uuid = ast_uuid_generate();
+		
+		if (!uuid) {
+			ao2_ref(details, -1);
+			return NULL;
+		}
+
+		ast_uuid_to_str(uuid, details->id, AST_UUID_STR_LEN);
+		ast_free(uuid);
+	} else {
+		ast_copy_string(details->id, id, sizeof(details->id));
+	}
+
 	ast_copy_string(details->type, type, sizeof(details->type));
 
 	if (aco_set_defaults(object_type->type, id, details)) {
@@ -657,7 +738,7 @@
 void *ast_sorcery_copy(const struct ast_sorcery *sorcery, const void *object)
 {
 	const struct ast_sorcery_object_details *details = object;
-	void *copy = ast_sorcery_alloc(sorcery, details->type, details->id);
+	struct ast_sorcery_object_details *copy = ast_sorcery_alloc(sorcery, details->type, details->id);
 	RAII_VAR(struct ast_variable *, objectset, NULL, ast_variables_destroy);
 
 	if (!copy ||
@@ -666,6 +747,8 @@
 		ao2_cleanup(copy);
 		return NULL;
 	}
+
+	copy->wizard = details->wizard;
 
 	return copy;
 }
@@ -724,20 +807,31 @@
 	va_end(args);
 
 	/* Uh... if no parameters are there we obviously can't do anything */
-	if (ast_strlen_zero(id) && !fields) {
-		return NULL;
+	if (ast_strlen_zero(id) && !fields && (!(flags & AST_RETRIEVE_FLAG_MULTIPLE) || !(flags & AST_RETRIEVE_FLAG_ALL))) {
+		return NULL;
+	}
+
+	/* If returning multiple objects create a container to store them in */
+	if ((flags & AST_RETRIEVE_FLAG_MULTIPLE)) {
+		if (!(object = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) {
+			return NULL;
+		}
 	}
 
 	/* 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)) {
-		if (!ast_strlen_zero(id)) {
-			object = wizard->wizard->retrieve_id(wizard->data, id);
+		if ((flags & AST_RETRIEVE_FLAG_MULTIPLE)) {
+			if (wizard->wizard->retrieve_multiple) {
+				wizard->wizard->retrieve_multiple(sorcery, wizard->data, object, fields);
+			}
+		} else if (!ast_strlen_zero(id)) {
+			object = wizard->wizard->retrieve_id(sorcery, wizard->data, id);
 		} else if (fields && wizard->wizard->retrieve_fields) {
-			object = wizard->wizard->retrieve_fields(wizard->data, fields);
-		}
-
-		if (!object) {
+			object = wizard->wizard->retrieve_fields(sorcery, wizard->data, fields);
+		}
+
+		if ((flags & AST_RETRIEVE_FLAG_MULTIPLE) || !object) {
 			continue;
 		}
 
@@ -757,7 +851,7 @@
 	return !object_wizard->wizard->create(object_wizard->data, arg) ? CMP_MATCH | CMP_STOP : 0;
 }
 
-int ast_sorcery_create(struct ast_sorcery *sorcery, void *object)
+int ast_sorcery_create(const struct ast_sorcery *sorcery, void *object)
 {
 	struct ast_sorcery_object_details *details = object;
 	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->type, OBJ_KEY), ao2_cleanup);
@@ -776,9 +870,9 @@
 	}
 }
 
-int ast_sorcery_update(struct ast_sorcery *sorcery, void *object)
-{
-	struct ast_sorcery_object_details *details = object;
+int ast_sorcery_update(const struct ast_sorcery *sorcery, void *object)
+{
+	const struct ast_sorcery_object_details *details = object;
 
 	ast_assert(sorcery != NULL);
 	ast_assert(details->wizard != NULL);
@@ -786,9 +880,9 @@
 	return details->wizard->wizard->update(details->wizard->data, object);
 }
 
-int ast_sorcery_delete(struct ast_sorcery *sorcery, void *object)
-{
-	struct ast_sorcery_object_details *details = object;
+int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object)
+{
+	const struct ast_sorcery_object_details *details = object;
 
 	ast_assert(sorcery != NULL);
 	ast_assert(details->wizard != NULL);

Modified: team/file/sorcery/res/res_sorcery_memory.c
URL: http://svnview.digium.com/svn/asterisk/team/file/sorcery/res/res_sorcery_memory.c?view=diff&rev=378198&r1=378197&r2=378198
==============================================================================
--- team/file/sorcery/res/res_sorcery_memory.c (original)
+++ team/file/sorcery/res/res_sorcery_memory.c Mon Dec 24 10:30:43 2012
@@ -41,7 +41,9 @@
 
 static void *sorcery_memory_open(const char *data);
 static int sorcery_memory_create(void *data, void *object);
-static void *sorcery_memory_retrieve_id(void *data, const char *id);
+static void *sorcery_memory_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *id);
+static void *sorcery_memory_retrieve_fields(const struct ast_sorcery *sorcery, void *data, const struct ast_variable *fields);
+static void sorcery_memory_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, struct ao2_container *objects, const struct ast_variable *fields);
 static int sorcery_memory_update(void *data, void *object);
 static int sorcery_memory_delete(void *data, void *object);
 static void sorcery_memory_close(void *data);
@@ -51,11 +53,25 @@
 	.open = sorcery_memory_open,
 	.create = sorcery_memory_create,
 	.retrieve_id = sorcery_memory_retrieve_id,
+	.retrieve_fields = sorcery_memory_retrieve_fields,
+	.retrieve_multiple = sorcery_memory_retrieve_multiple,
 	.update = sorcery_memory_update,
 	.delete = sorcery_memory_delete,
 	.close = sorcery_memory_close,
 };
 
+/*! \brief Structure used for fields comparison */
+struct sorcery_memory_fields_cmp_params {
+	/*! \brief Pointer to the sorcery structure */
+	const struct ast_sorcery *sorcery;
+
+	/*! \brief Pointer to the fields to check */
+	const struct ast_variable *fields;
+
+	/*! \brief Optional container to put object into */
+	struct ao2_container *container;
+};
+
 /*! \brief Hashing function for sorcery objects */
 static int sorcery_memory_hash(const void *obj, const int flags)
 {
@@ -78,20 +94,87 @@
 	return 0;
 }
 
-static void *sorcery_memory_retrieve_id(void *data, const char *id)
+static int sorcery_memory_fields_cmp(void *obj, void *arg, int flags)
+{
+	const struct sorcery_memory_fields_cmp_params *params = arg;
+	RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
+	RAII_VAR(struct ast_variable *, diff, NULL, ast_variables_destroy);
+
+	/* If we can't turn the object into an object set OR if differences exist between the fields
+	 * passed in and what are present on the object they are not a match.
+	 */
+	if (params->fields &&
+	    (!(objset = ast_sorcery_objectset_create(params->sorcery, obj)) ||
+	     (diff = ast_sorcery_changeset_create(objset, params->fields)))) {
+		return 0;
+	}
+
+	if (params->container) {
+		ao2_link(params->container, obj);
+
+		/* As multiple objects are being returned keep going */
+		return 0;
+	} else {
+		/* Immediately stop and return, we only want a single object */
+		return CMP_MATCH | CMP_STOP;
+	}
+}
+
+static void *sorcery_memory_retrieve_fields(const struct ast_sorcery *sorcery, void *data, const struct ast_variable *fields)
+{
+	struct sorcery_memory_fields_cmp_params params = {
+		.sorcery = sorcery,
+		.fields = fields,
+		.container = NULL,
+	};
+
+	/* If no fields are present return nothing, we require *something* */
+	if (!fields) {
+		return NULL;
+	}
+
+	return ao2_callback(data, 0, sorcery_memory_fields_cmp, &params);
+}
+
+static void *sorcery_memory_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *id)
 {
 	return ao2_find(data, id, OBJ_KEY);
 }
 
+static void sorcery_memory_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, struct ao2_container *objects, const struct ast_variable *fields)
+{
+	struct sorcery_memory_fields_cmp_params params = {
+		.sorcery = sorcery,
+		.fields = fields,
+		.container = objects,
+	};
+
+	ao2_callback(data, 0, sorcery_memory_fields_cmp, &params);
+}
+
 static int sorcery_memory_update(void *data, void *object)
 {
-	return -1;
+	RAII_VAR(void *, existing, NULL, ao2_cleanup);
+
+	ao2_lock(data);
+
+	if (!(existing = ao2_find(data, ast_sorcery_object_get_id(object), OBJ_KEY | OBJ_UNLINK))) {
+		ao2_unlock(data);
+		return -1;
+	}
+
+	ao2_link(data, object);
+
+	ao2_unlock(data);
+
+	return 0;
 }
 
 static int sorcery_memory_delete(void *data, void *object)
 {
-	ao2_unlink(data, object);
-	return 0;
+	RAII_VAR(void *, existing, ao2_find(data, ast_sorcery_object_get_id(object), OBJ_KEY | OBJ_UNLINK), ao2_cleanup);
+
+	return existing ? 0 : -1;
 }
 
 static void *sorcery_memory_open(const char *data)

Modified: team/file/sorcery/tests/test_sorcery.c
URL: http://svnview.digium.com/svn/asterisk/team/file/sorcery/tests/test_sorcery.c?view=diff&rev=378198&r1=378197&r2=378198
==============================================================================
--- team/file/sorcery/tests/test_sorcery.c (original)
+++ team/file/sorcery/tests/test_sorcery.c Mon Dec 24 10:30:43 2012
@@ -51,6 +51,32 @@
 	return ao2_alloc(sizeof(struct test_sorcery_object), NULL);
 }
 
+/*! \brief Internal function for object set transformation */
+static struct ast_variable *test_sorcery_transform(struct ast_variable *set)
+{
+	struct ast_variable *field, *transformed = NULL;
+
+	for (field = set; field; field = field->next) {
+		struct ast_variable *transformed_field;
+
+		if (!strcmp(field->name, "joe")) {
+			transformed_field = ast_variable_new(field->name, "5000", "");
+		} else {
+			transformed_field = ast_variable_new(field->name, field->value, "");
+		}
+
+		if (!transformed_field) {
+			ast_variables_destroy(transformed);
+			return NULL;
+		}
+
+		transformed_field->next = transformed;
+		transformed = transformed_field;
+	}
+
+	return transformed;
+}
+
 /*! \brief Configuration framework definition for test object */
 static struct aco_type test_object = {
         .type = ACO_ITEM,
@@ -190,7 +216,7 @@
 		return AST_TEST_FAIL;
 	}
 
-	if (ast_sorcery_object_register(sorcery, "test", &test_object)) {
+	if (ast_sorcery_object_register(sorcery, "test", &test_object, NULL, NULL)) {
 		ast_test_status_update(test, "Failed to register object type\n");
 		return AST_TEST_FAIL;
 	}
@@ -198,7 +224,7 @@
 	return AST_TEST_PASS;
 }
 
-AST_TEST_DEFINE(object_alloc)
+AST_TEST_DEFINE(object_alloc_with_id)
 {
 	int res = AST_TEST_PASS;
 	RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
@@ -206,27 +232,27 @@
 
 	switch (cmd) {
 	case TEST_INIT:
-		info->name = "object_alloc";
-		info->category = "/main/sorcery/";
-		info->summary = "sorcery object allocation unit test";
-		info->description =
-			"Test object allocation in sorcery";
-		return AST_TEST_NOT_RUN;
-	case TEST_EXECUTE:
-		break;
-	}
-
-	if (!(sorcery = ast_sorcery_open())) {
-		ast_test_status_update(test, "Failed to open sorcery with NULL name\n");
-		return AST_TEST_FAIL;
-	}
-
-	if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
-		ast_test_status_update(test, "Failed to set a known wizard as a default\n");
-		return AST_TEST_FAIL;
-	}
-
-	if (ast_sorcery_object_register(sorcery, "test", &test_object)) {
+		info->name = "object_alloc_with_id";
+		info->category = "/main/sorcery/";
+		info->summary = "sorcery object allocation (with id) unit test";
+		info->description =
+			"Test object allocation in sorcery with a provided id";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (!(sorcery = ast_sorcery_open())) {
+		ast_test_status_update(test, "Failed to open sorcery with NULL name\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
+		ast_test_status_update(test, "Failed to set a known wizard as a default\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_sorcery_object_register(sorcery, "test", &test_object, NULL, NULL)) {
 		ast_test_status_update(test, "Failed to register object type\n");
 		return AST_TEST_FAIL;
 	}
@@ -254,6 +280,54 @@
 	return res;
 }
 
+AST_TEST_DEFINE(object_alloc_without_id)
+{
+	int res = AST_TEST_PASS;
+	RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+	RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
+	
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "object_alloc_without_id";
+		info->category = "/main/sorcery/";
+		info->summary = "sorcery object allocation (without id) unit test";
+		info->description =
+			"Test object allocation in sorcery with no provided id";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (!(sorcery = ast_sorcery_open())) {
+		ast_test_status_update(test, "Failed to open sorcery with NULL name\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
+		ast_test_status_update(test, "Failed to set a known wizard as a default\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_sorcery_object_register(sorcery, "test", &test_object, NULL, NULL)) {
+		ast_test_status_update(test, "Failed to register object type\n");
+		return AST_TEST_FAIL;
+	}
+
+	ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct test_sorcery_object, bob));
+	ast_sorcery_object_field_register(sorcery, "test", "joe", "10", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct test_sorcery_object, joe));
+
+	if (!(obj = ast_sorcery_alloc(sorcery, "test", NULL))) {
+		ast_test_status_update(test, "Failed to allocate a known object type\n");
+		res = AST_TEST_FAIL;
+	} else if (ast_strlen_zero(ast_sorcery_object_get_id(obj))) {
+		ast_test_status_update(test, "Allocated object has empty id when it should not\n");
+		res = AST_TEST_FAIL;
+	}
+
+	return res;
+}
+
+
 AST_TEST_DEFINE(object_copy)
 {
 	int res = AST_TEST_PASS;
@@ -283,7 +357,7 @@
 		return AST_TEST_FAIL;
 	}
 
-	if (ast_sorcery_object_register(sorcery, "test", &test_object)) {
+	if (ast_sorcery_object_register(sorcery, "test", &test_object, NULL, NULL)) {
 		ast_test_status_update(test, "Failed to register object type\n");
 		return AST_TEST_FAIL;
 	}
@@ -344,7 +418,7 @@
 	       return AST_TEST_FAIL;
        }
 
-       if (ast_sorcery_object_register(sorcery, "test", &test_object)) {
+       if (ast_sorcery_object_register(sorcery, "test", &test_object, NULL, NULL)) {
 	       ast_test_status_update(test, "Failed to register object type\n");
 	       return AST_TEST_FAIL;
        }
@@ -418,7 +492,7 @@
 		return AST_TEST_FAIL;
 	}
 
-	if (ast_sorcery_object_register(sorcery, "test", &test_object)) {
+	if (ast_sorcery_object_register(sorcery, "test", &test_object, NULL, NULL)) {
 		ast_test_status_update(test, "Failed to register object type\n");
 		return AST_TEST_FAIL;
 	}
@@ -485,7 +559,7 @@
 		return AST_TEST_FAIL;
 	}
 
-	if (ast_sorcery_object_register(sorcery, "test", &test_object)) {
+	if (ast_sorcery_object_register(sorcery, "test", &test_object, NULL, NULL)) {
 		ast_test_status_update(test, "Failed to register object type\n");
 		return AST_TEST_FAIL;
 	}
@@ -510,6 +584,71 @@
 	}
 
 	return res;
+}
+
+AST_TEST_DEFINE(objectset_transform)
+{
+	RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+	RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "objectset_transform";
+		info->category = "/main/sorcery/";
+		info->summary = "sorcery object set transformation unit test";
+		info->description =
+			"Test object set transformation in sorcery";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (!(sorcery = ast_sorcery_open())) {
+		ast_test_status_update(test, "Failed to open sorcery with NULL name\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
+		ast_test_status_update(test, "Failed to set a known wizard as a default\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_sorcery_object_register(sorcery, "test", &test_object, test_sorcery_transform, NULL)) {
+		ast_test_status_update(test, "Failed to register object type\n");
+		return AST_TEST_FAIL;
+	}
+
+	ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct test_sorcery_object, bob));
+	ast_sorcery_object_field_register(sorcery, "test", "joe", "10", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct test_sorcery_object, joe));
+
+	if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
+		ast_test_status_update(test, "Failed to allocate a known object type\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (!(objset = ast_sorcery_objectset_create(sorcery, obj))) {
+		ast_test_status_update(test, "Failed to create an object set for a known sane object\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_sorcery_objectset_apply(sorcery, obj, objset)) {
+		ast_test_status_update(test, "Failed to apply properly created object set against object\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (obj->bob != 5) {
+		ast_test_status_update(test, "Application of object set produced incorrect value on 'bob'\n");
+		return AST_TEST_FAIL;
+	} else if (obj->joe == 10) {
+		ast_test_status_update(test, "Transformation callback did not change value of 'joe' from provided value\n");
+		return AST_TEST_FAIL;
+	} else if (obj->joe != 5000) {
+		ast_test_status_update(test, "Value of 'joe' differs from default AND from transformation value\n");
+		return AST_TEST_FAIL;
+	}
+
+	return AST_TEST_PASS;
 }
 
 AST_TEST_DEFINE(changeset_create)
@@ -647,7 +786,7 @@
 		return AST_TEST_FAIL;
 	}
 
-	if (ast_sorcery_object_register(sorcery, "test", &test_object)) {
+	if (ast_sorcery_object_register(sorcery, "test", &test_object, NULL, NULL)) {
 		ast_test_status_update(test, "Failed to register object type\n");
 		return AST_TEST_FAIL;
 	}
@@ -668,34 +807,34 @@
 	return AST_TEST_PASS;
 }
 
-AST_TEST_DEFINE(object_retrieve)
+AST_TEST_DEFINE(object_retrieve_id)
 {
 	RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
 	RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
 
 	switch (cmd) {
 	case TEST_INIT:
-		info->name = "object_retrieve";
-		info->category = "/main/sorcery/";
-		info->summary = "sorcery object retrieval unit test";
-		info->description =
-			"Test object retrieval in sorcery";
-		return AST_TEST_NOT_RUN;
-	case TEST_EXECUTE:
-		break;
-	}
-
-	if (!(sorcery = ast_sorcery_open())) {
-		ast_test_status_update(test, "Failed to open sorcery with NULL name\n");
-		return AST_TEST_FAIL;
-	}
-
-	if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
-		ast_test_status_update(test, "Failed to set a known wizard as a default\n");
-		return AST_TEST_FAIL;
-	}
-
-	if (ast_sorcery_object_register(sorcery, "test", &test_object)) {
+		info->name = "object_retrieve_id";
+		info->category = "/main/sorcery/";
+		info->summary = "sorcery object retrieval using id unit test";
+		info->description =
+			"Test object retrieval using id in sorcery";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (!(sorcery = ast_sorcery_open())) {
+		ast_test_status_update(test, "Failed to open sorcery with NULL name\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
+		ast_test_status_update(test, "Failed to set a known wizard as a default\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_sorcery_object_register(sorcery, "test", &test_object, NULL, NULL)) {
 		ast_test_status_update(test, "Failed to register object type\n");
 		return AST_TEST_FAIL;
 	}
@@ -713,57 +852,320 @@
 	ao2_cleanup(obj);
 
 	if (!(obj = ast_sorcery_retrieve(sorcery, "test", AST_RETRIEVE_FLAG_DEFAULT, AST_RETRIEVE_PARAM_ID, "blah", AST_RETRIEVE_PARAM_END))) {
-		ast_test_status_update(test, "Failed to retrieve properly created object\n");
+		ast_test_status_update(test, "Failed to retrieve properly created object using id of 'blah'\n");
 		return AST_TEST_FAIL;
 	}
 
 	return AST_TEST_PASS;
 }
 
-AST_TEST_DEFINE(object_delete)
+AST_TEST_DEFINE(object_retrieve_field)
 {
 	RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
 	RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
 
 	switch (cmd) {
 	case TEST_INIT:
-		info->name = "object_delete";
-		info->category = "/main/sorcery/";
-		info->summary = "sorcery object deletion unit test";
-		info->description =
-			"Test object deletion in sorcery";
-		return AST_TEST_NOT_RUN;
-	case TEST_EXECUTE:
-		break;
-	}
-
-	if (!(sorcery = ast_sorcery_open())) {
-		ast_test_status_update(test, "Failed to open sorcery with NULL name\n");
-		return AST_TEST_FAIL;
-	}
-
-	if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
-		ast_test_status_update(test, "Failed to set a known wizard as a default\n");
-		return AST_TEST_FAIL;
-	}
-
-	if (ast_sorcery_object_register(sorcery, "test", &test_object)) {
-		ast_test_status_update(test, "Failed to register object type\n");
-		return AST_TEST_FAIL;
-	}
+		info->name = "object_retrieve_field";
+		info->category = "/main/sorcery/";
+		info->summary = "sorcery object retrieval using a specific field unit test";
+		info->description =
+			"Test object retrieval using a specific field in sorcery";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (!(sorcery = ast_sorcery_open())) {
+		ast_test_status_update(test, "Failed to open sorcery with NULL name\n");
+		return AST_TEST_FAIL;
+	}
+	
+	if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
+		ast_test_status_update(test, "Failed to set a known wizard as a default\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_sorcery_object_register(sorcery, "test", &test_object, NULL, NULL)) {
+		ast_test_status_update(test, "Failed to register object type\n");
+		return AST_TEST_FAIL;
+	}
+
+	ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct test_sorcery_object, bob));
+	ast_sorcery_object_field_register(sorcery, "test", "joe", "10", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct test_sorcery_object, joe));
 
 	if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
 		ast_test_status_update(test, "Failed to allocate a known object type\n");
 		return AST_TEST_FAIL;
 	}
 
+	obj->joe = 42;
+
 	if (ast_sorcery_create(sorcery, obj)) {
 		ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
 		return AST_TEST_FAIL;
 	}
 
+	ao2_cleanup(obj);
+
+	if (!(obj = ast_sorcery_retrieve(sorcery, "test", AST_RETRIEVE_FLAG_DEFAULT, AST_RETRIEVE_PARAM_FIELD, "joe", "42", AST_RETRIEVE_PARAM_END))) {
+		ast_test_status_update(test, "Failed to retrieve properly created object using 'joe' field\n");
+		return AST_TEST_FAIL;
+	}
+
+	ao2_cleanup(obj);
+
+	if ((obj = ast_sorcery_retrieve(sorcery, "test", AST_RETRIEVE_FLAG_DEFAULT, AST_RETRIEVE_PARAM_FIELD, "joe", "49", AST_RETRIEVE_PARAM_END))) {
+		ast_test_status_update(test, "Retrieved an object using a field with an in-correct value... that should not happen\n");
+		return AST_TEST_FAIL;
+	}
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(object_retrieve_multiple_all)
+{
+	RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+	RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
+	RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "object_retrieve_multiple_all";
+		info->category = "/main/sorcery/";
+		info->summary = "sorcery multiple object retrieval unit test";
+		info->description =
+			"Test multiple object retrieval in sorcery";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (!(sorcery = ast_sorcery_open())) {
+		ast_test_status_update(test, "Failed to open sorcery with NULL name\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
+		ast_test_status_update(test, "Failed to set a known wizard as a default\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_sorcery_object_register(sorcery, "test", &test_object, NULL, NULL)) {
+		ast_test_status_update(test, "Failed to register object type\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
+		ast_test_status_update(test, "Failed to allocate a known object type\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_sorcery_create(sorcery, obj)) {
+		ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (!(objects = ast_sorcery_retrieve(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, AST_RETRIEVE_PARAM_END))) {
+		ast_test_status_update(test, "Failed to retrieve a container of all objects\n");
+		return AST_TEST_FAIL;
+	} else if (!ao2_container_count(objects)) {
+		ast_test_status_update(test, "Received a container with no objects in it when there should be some\n");
+		return AST_TEST_FAIL;
+	}
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(object_retrieve_multiple_field)
+{
+	RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+	RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
+	RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "object_retrieve_multiple_field";
+		info->category = "/main/sorcery/";
+		info->summary = "sorcery multiple object retrieval unit test";
+		info->description =
+			"Test multiple object retrieval in sorcery using fields";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (!(sorcery = ast_sorcery_open())) {
+		ast_test_status_update(test, "Failed to open sorcery with NULL name\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
+		ast_test_status_update(test, "Failed to set a known wizard as a default\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_sorcery_object_register(sorcery, "test", &test_object, NULL, NULL)) {
+		ast_test_status_update(test, "Failed to register object type\n");
+		return AST_TEST_FAIL;
+	}
+
+	ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct test_sorcery_object, bob));
+	ast_sorcery_object_field_register(sorcery, "test", "joe", "10", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct test_sorcery_object, joe));
+
+	if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
+		ast_test_status_update(test, "Failed to allocate a known object type\n");
+		return AST_TEST_FAIL;
+	}
+
+	obj->joe = 6;
+
+	if (ast_sorcery_create(sorcery, obj)) {
+		ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (!(objects = ast_sorcery_retrieve(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE, AST_RETRIEVE_PARAM_FIELD, "joe", "6", AST_RETRIEVE_PARAM_END))) {
+		ast_test_status_update(test, "Failed to retrieve a container of all objects\n");
+		return AST_TEST_FAIL;
+	} else if (!ao2_container_count(objects)) {
+		ast_test_status_update(test, "Received a container with no objects in it when there should be some\n");
+		return AST_TEST_FAIL;
+	}
+
+	ao2_cleanup(objects);
+
+	if (!(objects = ast_sorcery_retrieve(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE, AST_RETRIEVE_PARAM_FIELD, "joe", "7", AST_RETRIEVE_PARAM_END))) {
+		ast_test_status_update(test, "Failed to retrieve an empty container when retrieving multiple\n");
+		return AST_TEST_FAIL;
+	} else if (ao2_container_count(objects)) {
+		ast_test_status_update(test, "Received a container with objects when there should be none in it\n");
+		return AST_TEST_FAIL;
+	}
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(object_update)
+{
+	RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+	RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
+	RAII_VAR(struct test_sorcery_object *, obj2, NULL, ao2_cleanup);
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "object_update";
+		info->category = "/main/sorcery/";
+		info->summary = "sorcery object update unit test";
+		info->description =
+			"Test object updating in sorcery";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (!(sorcery = ast_sorcery_open())) {
+		ast_test_status_update(test, "Failed to open sorcery with NULL name\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
+		ast_test_status_update(test, "Failed to set a known wizard as a default\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_sorcery_object_register(sorcery, "test", &test_object, NULL, NULL)) {
+		ast_test_status_update(test, "Failed to register object type\n");
+		return AST_TEST_FAIL;
+	}
+
+	ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct test_sorcery_object, bob));
+	ast_sorcery_object_field_register(sorcery, "test", "joe", "10", OPT_UINT_T, PARSE_DEFAULT, FLDSET(struct test_sorcery_object, joe));
+
+	if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
+		ast_test_status_update(test, "Failed to allocate a known object type\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_sorcery_create(sorcery, obj)) {
+		ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (!(obj2 = ast_sorcery_copy(sorcery, obj))) {
+		ast_test_status_update(test, "Failed to allocate a known object type for updating\n");
+		return AST_TEST_FAIL;
+	}
+
+	ao2_cleanup(obj);
+
+	if (ast_sorcery_update(sorcery, obj2)) {
+		ast_test_status_update(test, "Failed to update sorcery with new object\n");
+		return AST_TEST_FAIL;
+	}
+

[... 114 lines stripped ...]



More information about the asterisk-commits mailing list