diff options
-rw-r--r-- | include/asterisk/sorcery.h | 61 | ||||
-rw-r--r-- | main/sorcery.c | 99 | ||||
-rw-r--r-- | tests/test_sorcery.c | 156 |
3 files changed, 296 insertions, 20 deletions
diff --git a/include/asterisk/sorcery.h b/include/asterisk/sorcery.h index 1a1042fbd..6f3ec3a52 100644 --- a/include/asterisk/sorcery.h +++ b/include/asterisk/sorcery.h @@ -112,8 +112,8 @@ enum ast_sorcery_retrieve_flags { /*! \brief Return all matching objects */ AST_RETRIEVE_FLAG_MULTIPLE = (1 << 0), - /*! \brief Perform no matching, return all objects */ - AST_RETRIEVE_FLAG_ALL = (1 << 1), + /*! \brief Perform no matching, return all objects */ + AST_RETRIEVE_FLAG_ALL = (1 << 1), }; /*! \brief Forward declaration for the sorcery main structure */ @@ -151,6 +151,29 @@ typedef struct ast_variable *(*sorcery_transform_handler)(struct ast_variable *s */ typedef void (*sorcery_apply_handler)(const struct ast_sorcery *sorcery, void *obj); +/*! + * \brief A callback function for copying the contents of one object to another + * + * \param src The source object + * \param dst The destination object + * + * \retval 0 success + * \retval -1 failure + */ +typedef int (*sorcery_copy_handler)(const void *src, void *dst); + +/*! + * \brief A callback function for generating a changeset between two objects + * + * \param original The original object + * \param modified The modified object + * \param changes The changeset + * + * \param 0 success + * \param -1 failure + */ +typedef int (*sorcery_diff_handler)(const void *original, const void *modified, struct ast_variable **changes); + /*! \brief Interface for a sorcery wizard */ struct ast_sorcery_wizard { /*! \brief Name of the wizard */ @@ -290,6 +313,24 @@ int ast_sorcery_apply_default(struct ast_sorcery *sorcery, const char *type, con int ast_sorcery_object_register(struct ast_sorcery *sorcery, const char *type, aco_type_item_alloc alloc, sorcery_transform_handler transform, sorcery_apply_handler apply); /*! + * \brief Set the copy handler for an object type + * + * \param sorcery Pointer to a sorcery structure + * \param type Type of object + * \param copy Copy handler + */ +void ast_sorcery_object_set_copy_handler(struct ast_sorcery *sorcery, const char *type, sorcery_copy_handler copy); + +/*! + * \brief Set the diff handler for an object type + * + * \param sorcery Pointer to a sorcery structure + * \param type Type of object + * \param diff Diff handler + */ +void ast_sorcery_object_set_diff_handler(struct ast_sorcery *sorcery, const char *type, sorcery_diff_handler diff); + +/*! * \brief Register a field within an object * * \param sorcery Pointer to a sorcery structure @@ -346,6 +387,14 @@ int __ast_sorcery_object_field_register(struct ast_sorcery *sorcery, const char void ast_sorcery_load(const struct ast_sorcery *sorcery); /*! + * \brief Inform any wizards of a specific object type to load persistent objects + * + * \param sorcery Pointer to a sorcery structure + * \param type Name of the object type to load + */ +void ast_sorcery_load_object(const struct ast_sorcery *sorcery, const char *type); + +/*! * \brief Inform any wizards to reload persistent objects * * \param sorcery Pointer to a sorcery structure @@ -353,6 +402,14 @@ void ast_sorcery_load(const struct ast_sorcery *sorcery); void ast_sorcery_reload(const struct ast_sorcery *sorcery); /*! + * \brief Inform any wizards of a specific object type to reload persistent objects + * + * \param sorcery Pointer to a sorcery structure + * \param type Name of the object type to reload + */ +void ast_sorcery_reload_object(const struct ast_sorcery *sorcery, const char *type); + +/*! * \brief Increase the reference count of a sorcery structure * * \param sorcery Pointer to a sorcery structure diff --git a/main/sorcery.c b/main/sorcery.c index bed4d8533..f9ff6d563 100644 --- a/main/sorcery.c +++ b/main/sorcery.c @@ -63,6 +63,12 @@ struct ast_sorcery_object_type { /*! \brief Optional object set apply callback */ sorcery_apply_handler apply; + /*! \brief Optional object copy callback */ + sorcery_copy_handler copy; + + /*! \brief Optional object diff callback */ + sorcery_diff_handler diff; + /*! \brief Wizard instances */ struct ao2_container *wizards; @@ -299,7 +305,7 @@ struct ast_sorcery *ast_sorcery_open(void) return NULL; } - if (!(sorcery->types = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, TYPE_BUCKETS, sorcery_type_hash, sorcery_type_cmp))) { + if (!(sorcery->types = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, TYPE_BUCKETS, sorcery_type_hash, sorcery_type_cmp))) { ao2_ref(sorcery, -1); sorcery = NULL; } @@ -467,7 +473,7 @@ int ast_sorcery_object_register(struct ast_sorcery *sorcery, const char *type, a { RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup); - if (!object_type) { + if (!object_type || object_type->type.item_alloc) { return -1; } @@ -487,6 +493,28 @@ int ast_sorcery_object_register(struct ast_sorcery *sorcery, const char *type, a return 0; } +void ast_sorcery_object_set_copy_handler(struct ast_sorcery *sorcery, const char *type, sorcery_copy_handler copy) +{ + RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup); + + if (!object_type) { + return; + } + + object_type->copy = copy; +} + +void ast_sorcery_object_set_diff_handler(struct ast_sorcery *sorcery, const char *type, sorcery_diff_handler diff) +{ + RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup); + + if (!object_type) { + return; + } + + object_type->diff = diff; +} + int __ast_sorcery_object_field_register(struct ast_sorcery *sorcery, const char *type, const char *name, const char *default_val, enum aco_option_type opt_type, aco_option_handler config_handler, sorcery_field_handler sorcery_handler, unsigned int flags, size_t argc, ...) { @@ -573,6 +601,21 @@ void ast_sorcery_load(const struct ast_sorcery *sorcery) ao2_callback(sorcery->types, OBJ_NODATA, sorcery_object_load, &details); } +void ast_sorcery_load_object(const 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 sorcery_load_details details = { + .sorcery = sorcery, + .reload = 0, + }; + + if (!object_type) { + return; + } + + sorcery_object_load(object_type, &details, 0); +} + void ast_sorcery_reload(const struct ast_sorcery *sorcery) { struct sorcery_load_details details = { @@ -583,6 +626,21 @@ void ast_sorcery_reload(const struct ast_sorcery *sorcery) ao2_callback(sorcery->types, OBJ_NODATA, sorcery_object_load, &details); } +void ast_sorcery_reload_object(const 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 sorcery_load_details details = { + .sorcery = sorcery, + .reload = 1, + }; + + if (!object_type) { + return; + } + + sorcery_object_load(object_type, &details, 0); +} + void ast_sorcery_ref(struct ast_sorcery *sorcery) { ao2_ref(sorcery, +1); @@ -753,14 +811,25 @@ void *ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, con void *ast_sorcery_copy(const struct ast_sorcery *sorcery, const void *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); struct ast_sorcery_object_details *copy = ast_sorcery_alloc(sorcery, details->type, details->id); RAII_VAR(struct ast_variable *, objectset, NULL, ast_variables_destroy); + int res = 0; - if (!copy || - !(objectset = ast_sorcery_objectset_create(sorcery, object)) || - ast_sorcery_objectset_apply(sorcery, copy, objectset)) { - ao2_cleanup(copy); + if (!copy) { return NULL; + } else if (object_type->copy) { + res = object_type->copy(object, copy); + } else if ((objectset = ast_sorcery_objectset_create(sorcery, object))) { + res = ast_sorcery_objectset_apply(sorcery, copy, objectset); + } else { + /* No native copy available and could not create an objectset, this copy has failed */ + res = -1; + } + + if (res) { + ao2_cleanup(copy); + copy = NULL; } return copy; @@ -768,8 +837,7 @@ void *ast_sorcery_copy(const struct ast_sorcery *sorcery, const void *object) int ast_sorcery_diff(const struct ast_sorcery *sorcery, const void *original, const void *modified, struct ast_variable **changes) { - RAII_VAR(struct ast_variable *, objectset1, NULL, ast_variables_destroy); - RAII_VAR(struct ast_variable *, objectset2, NULL, ast_variables_destroy); + RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, ast_sorcery_object_get_type(original), OBJ_KEY), ao2_cleanup); *changes = NULL; @@ -777,10 +845,19 @@ int ast_sorcery_diff(const struct ast_sorcery *sorcery, const void *original, co return -1; } - objectset1 = ast_sorcery_objectset_create(sorcery, original); - objectset2 = ast_sorcery_objectset_create(sorcery, modified); + if (original == modified) { + return 0; + } else if (!object_type->diff) { + RAII_VAR(struct ast_variable *, objectset1, NULL, ast_variables_destroy); + RAII_VAR(struct ast_variable *, objectset2, NULL, ast_variables_destroy); - return ast_sorcery_changeset_create(objectset1, objectset2, changes); + objectset1 = ast_sorcery_objectset_create(sorcery, original); + objectset2 = ast_sorcery_objectset_create(sorcery, modified); + + return ast_sorcery_changeset_create(objectset1, objectset2, changes); + } else { + return object_type->diff(original, modified, changes); + } } /*! \brief Internal function used to create an object in caching wizards */ diff --git a/tests/test_sorcery.c b/tests/test_sorcery.c index cf53b1918..68e4fe8fc 100644 --- a/tests/test_sorcery.c +++ b/tests/test_sorcery.c @@ -77,6 +77,22 @@ static struct ast_variable *test_sorcery_transform(struct ast_variable *set) return transformed; } +/*! \brief Internal function which copies pre-defined data into an object, natively */ +static int test_sorcery_copy(const void *src, void *dst) +{ + struct test_sorcery_object *obj = dst; + obj->bob = 10; + obj->joe = 20; + return 0; +} + +/*! \brief Internal function which creates a pre-defined diff natively */ +static int test_sorcery_diff(const void *original, const void *modified, struct ast_variable **changes) +{ + *changes = ast_variable_new("yes", "itworks", ""); + return 0; +} + /*! \brief Test structure for caching */ struct sorcery_test_caching { /*! \brief Whether the object has been created in the cache or not */ @@ -142,18 +158,18 @@ static struct ast_sorcery *alloc_and_initialize_sorcery(void) { struct ast_sorcery *sorcery; - if (!(sorcery = ast_sorcery_open())) { + if (!(sorcery = ast_sorcery_open())) { return NULL; - } + } - if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) || - ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) { + if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) || + ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) { ast_sorcery_unref(sorcery); return NULL; - } + } - ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob)); - ast_sorcery_object_field_register(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe)); + ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob)); + ast_sorcery_object_field_register(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe)); return sorcery; } @@ -333,6 +349,11 @@ AST_TEST_DEFINE(object_register) return AST_TEST_FAIL; } + if (!ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) { + ast_test_status_update(test, "Registered object type a second time, despite it being registered already\n"); + return AST_TEST_FAIL; + } + return AST_TEST_PASS; } @@ -544,6 +565,57 @@ AST_TEST_DEFINE(object_copy) return res; } +AST_TEST_DEFINE(object_copy_native) +{ + 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); + RAII_VAR(struct test_sorcery_object *, copy, NULL, ao2_cleanup); + + switch (cmd) { + case TEST_INIT: + info->name = "object_copy_native"; + info->category = "/main/sorcery/"; + info->summary = "sorcery object native copy unit test"; + info->description = + "Test object native copy in sorcery"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + if (!(sorcery = alloc_and_initialize_sorcery())) { + ast_test_status_update(test, "Failed to open sorcery structure\n"); + return AST_TEST_FAIL; + } + + ast_sorcery_object_set_copy_handler(sorcery, "test", test_sorcery_copy); + + 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->bob = 50; + obj->joe = 100; + + if (!(copy = ast_sorcery_copy(sorcery, obj))) { + ast_test_status_update(test, "Failed to create a copy of a known valid object\n"); + res = AST_TEST_FAIL; + } else if (copy == obj) { + ast_test_status_update(test, "Created copy is actually the original object\n"); + res = AST_TEST_FAIL; + } else if (copy->bob != 10) { + ast_test_status_update(test, "Value of 'bob' on newly created copy is not the predefined native copy value\n"); + res = AST_TEST_FAIL; + } else if (copy->joe != 20) { + ast_test_status_update(test, "Value of 'joe' on newly created copy is not the predefined native copy value\n"); + res = AST_TEST_FAIL; + } + + return res; +} + AST_TEST_DEFINE(object_diff) { RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref); @@ -608,6 +680,72 @@ AST_TEST_DEFINE(object_diff) return res; } +AST_TEST_DEFINE(object_diff_native) +{ + RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref); + RAII_VAR(struct test_sorcery_object *, obj1, NULL, ao2_cleanup); + RAII_VAR(struct test_sorcery_object *, obj2, NULL, ao2_cleanup); + RAII_VAR(struct ast_variable *, changes, NULL, ast_variables_destroy); + struct ast_variable *field; + int res = AST_TEST_PASS; + + switch (cmd) { + case TEST_INIT: + info->name = "object_diff_native"; + info->category = "/main/sorcery/"; + info->summary = "sorcery object native diff unit test"; + info->description = + "Test native object diffing in sorcery"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + if (!(sorcery = alloc_and_initialize_sorcery())) { + ast_test_status_update(test, "Failed to open sorcery structure\n"); + return AST_TEST_FAIL; + } + + ast_sorcery_object_set_diff_handler(sorcery, "test", test_sorcery_diff); + + if (!(obj1 = ast_sorcery_alloc(sorcery, "test", "blah"))) { + ast_test_status_update(test, "Failed to allocate a known object type\n"); + return AST_TEST_FAIL; + } + + obj1->bob = 99; + obj1->joe = 55; + + if (!(obj2 = ast_sorcery_alloc(sorcery, "test", "blah2"))) { + ast_test_status_update(test, "Failed to allocate a second known object type\n"); + return AST_TEST_FAIL; + } + + obj2->bob = 99; + obj2->joe = 42; + + if (ast_sorcery_diff(sorcery, obj1, obj2, &changes)) { + ast_test_status_update(test, "Failed to diff obj1 and obj2\n"); + } else if (!changes) { + ast_test_status_update(test, "Failed to produce a diff of two objects, despite there being differences\n"); + return AST_TEST_FAIL; + } + + for (field = changes; field; field = field->next) { + if (!strcmp(field->name, "yes")) { + if (strcmp(field->value, "itworks")) { + ast_test_status_update(test, "Object diff produced unexpected value '%s' for joe\n", field->value); + res = AST_TEST_FAIL; + } + } else { + ast_test_status_update(test, "Object diff produced unexpected field '%s'\n", field->name); + res = AST_TEST_FAIL; + } + } + + return res; +} + AST_TEST_DEFINE(objectset_create) { int res = AST_TEST_PASS; @@ -1946,7 +2084,9 @@ static int unload_module(void) AST_TEST_UNREGISTER(object_alloc_with_id); AST_TEST_UNREGISTER(object_alloc_without_id); AST_TEST_UNREGISTER(object_copy); + AST_TEST_UNREGISTER(object_copy_native); AST_TEST_UNREGISTER(object_diff); + AST_TEST_UNREGISTER(object_diff_native); AST_TEST_UNREGISTER(objectset_create); AST_TEST_UNREGISTER(objectset_apply); AST_TEST_UNREGISTER(objectset_apply_handler); @@ -1985,7 +2125,9 @@ static int load_module(void) AST_TEST_REGISTER(object_alloc_with_id); AST_TEST_REGISTER(object_alloc_without_id); AST_TEST_REGISTER(object_copy); + AST_TEST_REGISTER(object_copy_native); AST_TEST_REGISTER(object_diff); + AST_TEST_REGISTER(object_diff_native); AST_TEST_REGISTER(objectset_create); AST_TEST_REGISTER(objectset_apply); AST_TEST_REGISTER(objectset_apply_handler); |