[asterisk-commits] file: branch file/sorcery r378216 - in /team/file/sorcery: include/asterisk/ ...
SVN commits to the Asterisk project
asterisk-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 asterisk-commits
mailing list