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

SVN commits to the Digium repositories svn-commits at lists.digium.com
Sat Dec 29 15:14:21 CST 2012


Author: file
Date: Sat Dec 29 15:14:18 2012
New Revision: 378216

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=378216
Log:
Add the concept of caching wizards and unit tests.

Modified:
    team/file/sorcery/include/asterisk/sorcery.h
    team/file/sorcery/main/sorcery.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=378216&r1=378215&r2=378216
==============================================================================
--- team/file/sorcery/include/asterisk/sorcery.h (original)
+++ team/file/sorcery/include/asterisk/sorcery.h Sat Dec 29 15:14:18 2012
@@ -97,9 +97,6 @@
 /*! \brief Maximum size of an object type */
 #define MAX_OBJECT_TYPE 64
 
-/*! \brief Forward declaration for an object wizard instance */
-struct ast_sorcery_object_wizard;
-
 /*!
  * \brief Retrieval flags
  */
@@ -209,9 +206,6 @@
 
 	/*! \brief Type of object */
 	char type[MAX_OBJECT_TYPE];
-
-	/*! \brief Object wizard which has conjured this object */
-	struct ast_sorcery_object_wizard *wizard;
 };
 
 /*! \brief Macro which must be used at the beginning of each sorcery capable object */

Modified: team/file/sorcery/main/sorcery.c
URL: http://svnview.digium.com/svn/asterisk/team/file/sorcery/main/sorcery.c?view=diff&rev=378216&r1=378215&r2=378216
==============================================================================
--- team/file/sorcery/main/sorcery.c (original)
+++ team/file/sorcery/main/sorcery.c Sat Dec 29 15:14:18 2012
@@ -94,6 +94,9 @@
 
 	/*! \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 */
@@ -366,7 +369,7 @@
 }
 
 /*! \brief Internal function which creates an object type and adds a wizard mapping */
-static int sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery, const char *type, const char *name, const char *data)
+static int sorcery_apply_wizard_mapping(struct ast_sorcery *sorcery, const char *type, const char *name, const char *data, unsigned int caching)
 {
 	RAII_VAR(struct ast_sorcery_object_type *, object_type,  ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
 	RAII_VAR(struct ast_sorcery_wizard *, wizard, ao2_find(wizards, name, OBJ_KEY), ao2_cleanup);
@@ -388,7 +391,7 @@
 		return -1;
 	}
 
-	if (!(object_wizard->data = wizard->open(data))) {
+	if (wizard->open && !(object_wizard->data = wizard->open(data))) {
 		return -1;
 	}
 
@@ -397,6 +400,7 @@
 	}
 
 	object_wizard->wizard = wizard;
+	object_wizard->caching = caching;
 
 	ao2_link(object_type->wizards, object_wizard);
 
@@ -419,15 +423,22 @@
 	}
 
 	for (mapping = ast_variable_browse(config, name); mapping; mapping = mapping->next) {
+		char *options = ast_strdupa(mapping->name), *name = strsep(&options, "/");
 		char *data = ast_strdupa(mapping->value), *wizard = strsep(&data, ",");
+		unsigned int caching = 0;
 
 		/* If no wizard exists just skip, nothing we can do */
 		if (ast_strlen_zero(wizard)) {
 			continue;
 		}
 
+		/* If the wizard is configured as a cache treat it as such */
+		if (!ast_strlen_zero(options) && strstr(options, "cache")) {
+			caching = 1;
+		}
+
 		/* Any error immediately causes us to stop */
-		if ((res = sorcery_apply_wizard_mapping(sorcery, mapping->name, wizard, data))) {
+		if ((res = sorcery_apply_wizard_mapping(sorcery, name, wizard, data, caching))) {
 			break;
 		}
 	}
@@ -446,7 +457,7 @@
 		return -1;
 	}
 
-	return sorcery_apply_wizard_mapping(sorcery, type, name, data);
+	return sorcery_apply_wizard_mapping(sorcery, type, name, data, 0);
 }
 
 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)
@@ -748,8 +759,6 @@
 		return NULL;
 	}
 
-	copy->wizard = details->wizard;
-
 	return copy;
 }
 
@@ -766,6 +775,20 @@
 	objectset2 = ast_sorcery_objectset_create(sorcery, modified);
 
 	return ast_sorcery_changeset_create(objectset1, objectset2);
+}
+
+/*! \brief Internal function used to create an object in caching wizards */
+static int sorcery_cache_create(void *obj, void *arg, int flags)
+{
+	struct ast_sorcery_object_wizard *object_wizard = obj;
+
+	if (!object_wizard->caching) {
+		return 0;
+	}
+
+	object_wizard->wizard->create(object_wizard->data, arg);
+
+	return 0;
 }
 
 void *ast_sorcery_retrieve(const struct ast_sorcery *sorcery, const char *type, unsigned int flags, ...)
@@ -778,6 +801,7 @@
 	void *object = NULL;
 	struct ao2_iterator i;
 	struct ast_sorcery_object_wizard *wizard;
+	unsigned int cached = 0;
 
 	if (!object_type) {
 		return NULL;
@@ -835,33 +859,32 @@
 			continue;
 		}
 
+		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);
+	}
+
 	return object;
 }
 
 /*! \brief Internal function which returns if the wizard has created the object */
 static int sorcery_wizard_create(void *obj, void *arg, int flags)
 {
-	struct ast_sorcery_object_wizard *object_wizard = obj;
-	struct ast_sorcery_object_details *details = arg;
-
-	details->wizard = object_wizard;
-
-	if (object_wizard->wizard->create(object_wizard->data, arg)) {
-		details->wizard = NULL;
-		return 0;
-	}
-
-	return CMP_MATCH | CMP_STOP;
+	const struct ast_sorcery_object_wizard *object_wizard = obj;
+
+	return (!object_wizard->caching && !object_wizard->wizard->create(object_wizard->data, arg)) ? CMP_MATCH | CMP_STOP : 0;
 }
 
 int ast_sorcery_create(const struct ast_sorcery *sorcery, void *object)
 {
-	struct ast_sorcery_object_details *details = object;
+	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_sorcery_object_wizard *, object_wizard, NULL, ao2_cleanup);
 
@@ -874,26 +897,50 @@
 	return object_wizard ? 0 : -1;
 }
 
+/*! \brief Internal function which returns if a wizard has updated the object */
+static int sorcery_wizard_update(void *obj, void *arg, int flags)
+{
+	const struct ast_sorcery_object_wizard *object_wizard = obj;
+
+	return (!object_wizard->wizard->update(object_wizard->data, arg) && !object_wizard->caching) ? CMP_MATCH | CMP_STOP : 0;
+}
+
 int ast_sorcery_update(const struct ast_sorcery *sorcery, void *object)
 {
 	const struct ast_sorcery_object_details *details = object;
-
-	if (!details->wizard) {
-		return -1;
-	}
-
-	return details->wizard->wizard->update(details->wizard->data, object);
+	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->type, OBJ_KEY), ao2_cleanup);
+	RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, NULL, ao2_cleanup);
+
+	if (!object_type) {
+		return -1;
+	}
+
+	object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_update, object);
+	
+	return object_wizard ? 0 : -1;
+}
+
+/*! \brief Internal function which returns if a wizard has deleted the object */
+static int sorcery_wizard_delete(void *obj, void *arg, int flags)
+{
+	const struct ast_sorcery_object_wizard *object_wizard = obj;
+
+	return (!object_wizard->wizard->delete(object_wizard->data, arg) && !object_wizard->caching) ? CMP_MATCH | CMP_STOP : 0;
 }
 
 int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object)
 {
 	const struct ast_sorcery_object_details *details = object;
-
-	if (!details->wizard) {
-		return -1;
-	}
-
-	return details->wizard->wizard->delete(details->wizard->data, object);
+	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->type, OBJ_KEY), ao2_cleanup);
+	RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, NULL, ao2_cleanup);
+
+	if (!object_type) {
+		return -1;
+	}
+
+	object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_delete, object);
+
+	return object_wizard ? 0 : -1;
 }
 
 void ast_sorcery_unref(struct ast_sorcery *sorcery)

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=378216&r1=378215&r2=378216
==============================================================================
--- team/file/sorcery/tests/test_sorcery.c (original)
+++ team/file/sorcery/tests/test_sorcery.c Sat Dec 29 15:14:18 2012
@@ -85,9 +85,56 @@
         .item_alloc = test_sorcery_object_alloc,
 };
 
+/*! \brief Test structure for caching */
+struct sorcery_test_caching {
+	/*! \brief Whether the object has been created in the cache or not */
+	unsigned int created:1;
+
+	/*! \brief Whether the object has been updated in the cache or not */
+	unsigned int updated:1;
+
+	/*! \brief Whether the object has been deleted from the cache or not */
+	unsigned int deleted:1;
+
+	/*! \brief Object to return when asked */
+	struct test_sorcery_object object;
+};
+
+/*! \brief Global scope caching structure for testing */
+static struct sorcery_test_caching cache = { 0, };
+
+static int sorcery_test_create(void *data, void *object)
+{
+	cache.created = 1;
+	cache.updated = 0;
+	cache.deleted = 0;
+	return 0;
+}
+
+static void *sorcery_test_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *id)
+{
+	return (cache.created && !cache.deleted) ? ast_sorcery_alloc(sorcery, "test", id) : NULL; 
+}
+
+static int sorcery_test_update(void *data, void *object)
+{
+	cache.updated = 1;
+	return 0;
+}
+
+static int sorcery_test_delete(void *data, void *object)
+{
+	cache.deleted = 1;
+	return 0;
+}
+
 /*! \brief Dummy sorcery wizard, 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,
 };
 
 AST_TEST_DEFINE(wizard_registration)
@@ -385,8 +432,11 @@
 	} else if (strcmp(ast_sorcery_object_get_id(obj), "blah")) {
 		ast_test_status_update(test, "Allocated object does not have correct id\n");
 		res = AST_TEST_FAIL;
-	} else if (obj->details.wizard) {
-		ast_test_status_update(test, "Allocated object has wizard when it should not\n");
+	} else if (ast_strlen_zero(ast_sorcery_object_get_type(obj))) {
+		ast_test_status_update(test, "Allocated object has empty type when it should not\n");
+		res = AST_TEST_FAIL;
+	} else if (strcmp(ast_sorcery_object_get_type(obj), "test")) {
+		ast_test_status_update(test, "Allocated object does not have correct type\n");
 		res = AST_TEST_FAIL;
 	} else if ((obj->bob != 5) || (obj->joe != 10)) {
 		ast_test_status_update(test, "Allocated object does not have defaults set as it should\n");
@@ -1458,6 +1508,113 @@
 	}
 
 	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(caching_wizard_behavior)
+{
+	struct ast_flags flags = { CONFIG_FLAG_NOCACHE };
+	struct ast_config *config;
+	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);
+	int res = AST_TEST_FAIL;
+
+	switch (cmd) {
+        case TEST_INIT:
+                info->name = "caching_wizard_behavior";
+                info->category = "/main/sorcery/";
+                info->summary = "sorcery caching wizard behavior unit test";
+                info->description =
+                        "Test internal behavior of caching wizards";
+                return AST_TEST_NOT_RUN;
+        case TEST_EXECUTE:
+                break;
+        }
+
+	if (!(config = ast_config_load2("sorcery.conf", "test_sorcery_cache", flags))) {
+		ast_test_status_update(test, "Sorcery configuration file not present - skipping caching_wizard_behavior test\n");
+		return AST_TEST_NOT_RUN;
+	}
+
+	ast_config_destroy(config);
+
+        if (ast_sorcery_wizard_register(&test_wizard, NULL)) {
+                ast_test_status_update(test, "Failed to register a perfectly valid sorcery wizard\n");
+                return AST_TEST_FAIL;
+        }
+
+	if (!(sorcery = ast_sorcery_open())) {
+		ast_test_status_update(test, "Failed to open sorcery structure\n");
+		goto end;
+	}
+
+	if (ast_sorcery_apply_config(sorcery, "test_sorcery_cache")) {
+		ast_test_status_update(test, "Failed to apply configured object mappings\n");
+		goto end;
+	}
+
+	if (ast_sorcery_object_register(sorcery, "test", &test_object, NULL, NULL)) {
+		ast_test_status_update(test, "Failed to register object type\n");
+		goto end;
+	}
+
+	if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
+		ast_test_status_update(test, "Failed to allocate a known object type\n");
+		goto end;
+	}
+
+	if (ast_sorcery_create(sorcery, obj)) {
+		ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
+		goto end;
+	}
+
+	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 just created object\n");
+		goto end;
+	} else if (!cache.created) {
+		ast_test_status_update(test, "Caching wizard was not told to cache just created object\n");
+		goto end;
+	} else if (!(obj2 = 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 just cached object\n");
+		goto end;
+	} else if (obj == obj2) {
+		ast_test_status_update(test, "Returned object is *NOT* a cached object\n");
+		goto end;
+	} else if (ast_sorcery_update(sorcery, obj)) {
+		ast_test_status_update(test, "Failed to update a known stored object\n");
+		goto end;
+	} else if (!cache.updated) {
+		ast_test_status_update(test, "Caching wizard was not told to update object\n");
+		goto end;
+	} else if (ast_sorcery_delete(sorcery, obj)) {
+		ast_test_status_update(test, "Failed to delete a known stored object\n");
+		goto end;
+	} else if (!cache.deleted) {
+		ast_test_status_update(test, "Caching wizard was not told to delete object\n");
+		goto end;
+	}
+
+	ao2_cleanup(obj2);
+
+	if ((obj2 = ast_sorcery_retrieve(sorcery, "test", AST_RETRIEVE_FLAG_DEFAULT, AST_RETRIEVE_PARAM_ID, "blah", AST_RETRIEVE_PARAM_END))) {
+		ast_test_status_update(test, "Retrieved an object that should have been deleted\n");
+		goto end;
+	}
+
+	res = AST_TEST_PASS;
+
+end:
+	ast_sorcery_unref(sorcery);
+	sorcery = NULL;
+
+	if (ast_sorcery_wizard_unregister(&test_wizard)) {
+		ast_test_status_update(test, "Failed to unregister test sorcery wizard\n");
+		return AST_TEST_FAIL;
+	}
+
+	return res;
 }
 
 static int unload_module(void)
@@ -1488,6 +1645,7 @@
 	AST_TEST_UNREGISTER(object_update_uncreated);
 	AST_TEST_UNREGISTER(object_delete);
 	AST_TEST_UNREGISTER(object_delete_uncreated);
+	AST_TEST_UNREGISTER(caching_wizard_behavior);
 	return 0;
 }
 
@@ -1519,6 +1677,7 @@
 	AST_TEST_REGISTER(object_update_uncreated);
 	AST_TEST_REGISTER(object_delete);
 	AST_TEST_REGISTER(object_delete_uncreated);
+	AST_TEST_REGISTER(caching_wizard_behavior);
 	return AST_MODULE_LOAD_SUCCESS;
 }
 




More information about the svn-commits mailing list