summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua Colp <jcolp@digium.com>2013-06-22 14:26:25 +0000
committerJoshua Colp <jcolp@digium.com>2013-06-22 14:26:25 +0000
commita330d0867e3155e34ecbfd23a84fe6e7ebf51469 (patch)
treed19cbb217482b0dc2952afe2495d59b868d1ff82
parent77002bc377f19ea11e60732c486b6ef371688773 (diff)
Make sorcery details opaque and add extended fields.
Sorcery specific object information is now opaque and allocated with the object. This means that modules do not need to be recompiled if the sorcery specific part is changed. It also means that sorcery can store additional information on objects and ensure it is freed or the reference count decreased when the object goes away. To facilitate the above a generic sorcery allocator function has been added which also ensures that allocated objects do not have a lock. Extended fields have been added thanks to all of the above which allows specific fields to be marked as extended, and thus simply stored as-is within the object. Type safety is *NOT* enforced on these fields. A consumer of them has to query and ultimately perform their own safety check. What does this mean? Extra modules can extend already defined structures without having to modify them. Tests have also been included to verify extended field functionality. Review: https://reviewboard.asterisk.org/r/2585/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@392586 65c4cc65-6c06-0410-ace0-fbb531ad65f3
-rw-r--r--include/asterisk/sorcery.h49
-rw-r--r--main/sorcery.c136
-rw-r--r--res/res_sip/config_auth.c2
-rw-r--r--res/res_sip/config_domain_aliases.c2
-rw-r--r--res/res_sip/config_transport.c2
-rw-r--r--res/res_sip/location.c4
-rw-r--r--res/res_sip/sip_configuration.c4
-rw-r--r--res/res_sip/sip_options.c3
-rw-r--r--tests/test_sorcery.c69
9 files changed, 240 insertions, 31 deletions
diff --git a/include/asterisk/sorcery.h b/include/asterisk/sorcery.h
index ae97da5cc..464a83c83 100644
--- a/include/asterisk/sorcery.h
+++ b/include/asterisk/sorcery.h
@@ -247,13 +247,13 @@ struct ast_sorcery_observer {
void (*loaded)(const char *object_type);
};
+/*! \brief Opaque structure for internal sorcery object */
+struct ast_sorcery_object;
+
/*! \brief Structure which contains details about a sorcery object */
struct ast_sorcery_object_details {
- /*! \brief Unique identifier of this object */
- char id[AST_UUID_STR_LEN];
-
- /*! \brief Type of object */
- char type[MAX_OBJECT_TYPE];
+ /*! \brief Pointer to internal sorcery object information */
+ struct ast_sorcery_object *object;
};
/*! \brief Macro which must be used at the beginning of each sorcery capable object */
@@ -529,6 +529,17 @@ int ast_sorcery_objectset_apply(const struct ast_sorcery *sorcery, void *object,
int ast_sorcery_changeset_create(const struct ast_variable *original, const struct ast_variable *modified, struct ast_variable **changes);
/*!
+ * \brief Allocate a generic sorcery capable object
+ *
+ * \param size Size of the object
+ * \param destructor Optional destructor function
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ */
+void *ast_sorcery_generic_alloc(size_t size, ao2_destructor_fn destructor);
+
+/*!
* \brief Allocate an object
*
* \param sorcery Pointer to a sorcery structure
@@ -695,6 +706,34 @@ const char *ast_sorcery_object_get_id(const void *object);
*/
const char *ast_sorcery_object_get_type(const void *object);
+/*!
+ * \brief Get an extended field value from a sorcery object
+ *
+ * \param object Pointer to a sorcery object
+ * \param name Name of the extended field value
+ *
+ * \retval non-NULL if found
+ * \retval NULL if not found
+ *
+ * \note The returned string does NOT need to be freed and is guaranteed to remain valid for the lifetime of the object
+ */
+const char *ast_sorcery_object_get_extended(const void *object, const char *name);
+
+/*!
+ * \brief Set an extended field value on a sorcery object
+ *
+ * \param object Pointer to a sorcery object
+ * \param name Name of the extended field
+ * \param value Value of the extended field
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \note The field name MUST begin with '@' to indicate it is an extended field.
+ * \note If the extended field already exists it will be overwritten with the new value.
+ */
+int ast_sorcery_object_set_extended(const void *object, const char *name, const char *value);
+
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
diff --git a/main/sorcery.c b/main/sorcery.c
index 99e4c5586..cf4cd0213 100644
--- a/main/sorcery.c
+++ b/main/sorcery.c
@@ -58,6 +58,21 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
/*! \brief Thread pool for observers */
static struct ast_threadpool *threadpool;
+/*! \brief Structure for internal sorcery object information */
+struct ast_sorcery_object {
+ /*! \brief Unique identifier of this object */
+ char id[AST_UUID_STR_LEN];
+
+ /*! \brief Type of object */
+ char type[MAX_OBJECT_TYPE];
+
+ /*! \brief Optional object destructor */
+ ao2_destructor_fn destructor;
+
+ /*! \brief Extended object fields */
+ struct ast_variable *extended;
+};
+
/*! \brief Structure for registered object type */
struct ast_sorcery_object_type {
/*! \brief Unique name of the object type */
@@ -525,6 +540,24 @@ int __ast_sorcery_apply_default(struct ast_sorcery *sorcery, const char *type, c
return sorcery_apply_wizard_mapping(sorcery, type, module, name, data, 0);
}
+static int sorcery_extended_config_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+ return ast_sorcery_object_set_extended(obj, var->name, var->value);
+}
+
+static int sorcery_extended_fields_handler(const void *obj, struct ast_variable **fields)
+{
+ const struct ast_sorcery_object_details *details = obj;
+
+ if (details->object->extended) {
+ *fields = ast_variables_dup(details->object->extended);
+ } else {
+ *fields = NULL;
+ }
+
+ return 0;
+}
+
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)
{
RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
@@ -547,6 +580,10 @@ int ast_sorcery_object_register(struct ast_sorcery *sorcery, const char *type, a
return -1;
}
+ if (ast_sorcery_object_fields_register(sorcery, type, "^@", sorcery_extended_config_handler, sorcery_extended_fields_handler)) {
+ return -1;
+ }
+
return 0;
}
@@ -784,7 +821,7 @@ void ast_sorcery_ref(struct ast_sorcery *sorcery)
struct ast_variable *ast_sorcery_objectset_create(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);
+ RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->object->type, OBJ_KEY), ao2_cleanup);
struct ao2_iterator i;
struct ast_sorcery_object_field *object_field;
struct ast_variable *fields = NULL;
@@ -816,7 +853,7 @@ struct ast_variable *ast_sorcery_objectset_create(const struct ast_sorcery *sorc
continue;
}
- if (!res) {
+ if (!res && tmp) {
tmp->next = fields;
fields = tmp;
}
@@ -836,7 +873,7 @@ struct ast_variable *ast_sorcery_objectset_create(const struct ast_sorcery *sorc
struct ast_json *ast_sorcery_objectset_json_create(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);
+ RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->object->type, OBJ_KEY), ao2_cleanup);
struct ao2_iterator i;
struct ast_sorcery_object_field *object_field;
struct ast_json *json = ast_json_object_create();
@@ -898,7 +935,7 @@ struct ast_json *ast_sorcery_objectset_json_create(const struct ast_sorcery *sor
int ast_sorcery_objectset_apply(const struct ast_sorcery *sorcery, void *object, struct ast_variable *objectset)
{
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_type *, object_type, ao2_find(sorcery->types, details->object->type, OBJ_KEY), ao2_cleanup);
RAII_VAR(struct ast_variable *, transformed, NULL, ast_variables_destroy);
struct ast_variable *field;
int res = 0;
@@ -914,7 +951,7 @@ int ast_sorcery_objectset_apply(const struct ast_sorcery *sorcery, void *object,
}
for (; field; field = field->next) {
- if ((res = aco_process_var(&object_type->type, details->id, field, object))) {
+ if ((res = aco_process_var(&object_type->type, details->object->id, field, object))) {
break;
}
}
@@ -977,6 +1014,32 @@ int ast_sorcery_changeset_create(const struct ast_variable *original, const stru
return res;
}
+static void sorcery_object_destructor(void *object)
+{
+ struct ast_sorcery_object_details *details = object;
+
+ if (details->object->destructor) {
+ details->object->destructor(object);
+ }
+
+ ast_variables_destroy(details->object->extended);
+}
+
+void *ast_sorcery_generic_alloc(size_t size, ao2_destructor_fn destructor)
+{
+ void *object = ao2_alloc_options(size + sizeof(struct ast_sorcery_object), sorcery_object_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK);
+ struct ast_sorcery_object_details *details = object;
+
+ if (!object) {
+ return NULL;
+ }
+
+ details->object = object + size;
+ details->object->destructor = destructor;
+
+ return object;
+}
+
void *ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, const char *id)
{
RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
@@ -988,12 +1051,12 @@ void *ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, con
}
if (ast_strlen_zero(id)) {
- ast_uuid_generate_str(details->id, sizeof(details->id));
+ ast_uuid_generate_str(details->object->id, sizeof(details->object->id));
} else {
- ast_copy_string(details->id, id, sizeof(details->id));
+ ast_copy_string(details->object->id, id, sizeof(details->object->id));
}
- ast_copy_string(details->type, type, sizeof(details->type));
+ ast_copy_string(details->object->type, type, sizeof(details->object->type));
if (aco_set_defaults(&object_type->type, id, details)) {
ao2_ref(details, -1);
@@ -1006,8 +1069,8 @@ 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_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->object->type, OBJ_KEY), ao2_cleanup);
+ struct ast_sorcery_object_details *copy = ast_sorcery_alloc(sorcery, details->object->type, details->object->id);
RAII_VAR(struct ast_variable *, objectset, NULL, ast_variables_destroy);
int res = 0;
@@ -1120,7 +1183,6 @@ void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const ch
unsigned int cached = 0;
if (!object_type) {
- ast_log(LOG_NOTICE, "Can't find object type '%s'\n", type);
return NULL;
}
@@ -1222,7 +1284,7 @@ static int sorcery_observers_notify_create(void *data)
int ast_sorcery_create(const struct ast_sorcery *sorcery, 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);
+ 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 sorcery_details sdetails = {
.sorcery = sorcery,
@@ -1281,7 +1343,7 @@ static int sorcery_wizard_update(void *obj, void *arg, int flags)
int ast_sorcery_update(const struct ast_sorcery *sorcery, 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);
+ 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 sorcery_details sdetails = {
.sorcery = sorcery,
@@ -1340,7 +1402,7 @@ static int sorcery_wizard_delete(void *obj, void *arg, int flags)
int ast_sorcery_delete(const struct ast_sorcery *sorcery, 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);
+ 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 sorcery_details sdetails = {
.sorcery = sorcery,
@@ -1371,13 +1433,55 @@ void ast_sorcery_unref(struct ast_sorcery *sorcery)
const char *ast_sorcery_object_get_id(const void *object)
{
const struct ast_sorcery_object_details *details = object;
- return details->id;
+ return details->object->id;
}
const char *ast_sorcery_object_get_type(const void *object)
{
const struct ast_sorcery_object_details *details = object;
- return details->type;
+ return details->object->type;
+}
+
+const char *ast_sorcery_object_get_extended(const void *object, const char *name)
+{
+ const struct ast_sorcery_object_details *details = object;
+ struct ast_variable *field;
+
+ for (field = details->object->extended; field; field = field->next) {
+ if (!strcmp(field->name + 1, name)) {
+ return field->value;
+ }
+ }
+
+ return NULL;
+}
+
+int ast_sorcery_object_set_extended(const void *object, const char *name, const char *value)
+{
+ RAII_VAR(struct ast_variable *, field, NULL, ast_variables_destroy);
+ struct ast_variable *extended = ast_variable_new(name, value, ""), *previous = NULL;
+ const struct ast_sorcery_object_details *details = object;
+
+ if (!extended) {
+ return -1;
+ }
+
+ for (field = details->object->extended; field; previous = field, field = field->next) {
+ if (!strcmp(field->name, name)) {
+ if (previous) {
+ previous->next = field->next;
+ } else {
+ details->object->extended = field->next;
+ }
+ field->next = NULL;
+ break;
+ }
+ }
+
+ extended->next = details->object->extended;
+ details->object->extended = extended;
+
+ return 0;
}
int ast_sorcery_observer_add(const struct ast_sorcery *sorcery, const char *type, const struct ast_sorcery_observer *callbacks)
diff --git a/res/res_sip/config_auth.c b/res/res_sip/config_auth.c
index 9881dd8c6..47ff42deb 100644
--- a/res/res_sip/config_auth.c
+++ b/res/res_sip/config_auth.c
@@ -32,7 +32,7 @@ static void auth_destroy(void *obj)
static void *auth_alloc(const char *name)
{
- struct ast_sip_auth *auth = ao2_alloc(sizeof(*auth), auth_destroy);
+ struct ast_sip_auth *auth = ast_sorcery_generic_alloc(sizeof(*auth), auth_destroy);
if (!auth) {
return NULL;
diff --git a/res/res_sip/config_domain_aliases.c b/res/res_sip/config_domain_aliases.c
index 86b4636ea..90cd82f94 100644
--- a/res/res_sip/config_domain_aliases.c
+++ b/res/res_sip/config_domain_aliases.c
@@ -33,7 +33,7 @@ static void domain_alias_destroy(void *obj)
static void *domain_alias_alloc(const char *name)
{
- struct ast_sip_domain_alias *alias = ao2_alloc(sizeof(*alias), domain_alias_destroy);
+ struct ast_sip_domain_alias *alias = ast_sorcery_generic_alloc(sizeof(*alias), domain_alias_destroy);
if (!alias) {
return NULL;
diff --git a/res/res_sip/config_transport.c b/res/res_sip/config_transport.c
index 1d60274b7..30c1362a5 100644
--- a/res/res_sip/config_transport.c
+++ b/res/res_sip/config_transport.c
@@ -62,7 +62,7 @@ static void transport_destroy(void *obj)
/*! \brief Allocator for transport */
static void *transport_alloc(const char *name)
{
- struct ast_sip_transport *transport = ao2_alloc(sizeof(*transport), transport_destroy);
+ struct ast_sip_transport *transport = ast_sorcery_generic_alloc(sizeof(*transport), transport_destroy);
if (!transport) {
return NULL;
diff --git a/res/res_sip/location.c b/res/res_sip/location.c
index d0b0a28c9..bc1b5211e 100644
--- a/res/res_sip/location.c
+++ b/res/res_sip/location.c
@@ -41,7 +41,7 @@ static void aor_destroy(void *obj)
/*! \brief Allocator for AOR */
static void *aor_alloc(const char *name)
{
- struct ast_sip_aor *aor = ao2_alloc_options(sizeof(struct ast_sip_aor), aor_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
+ struct ast_sip_aor *aor = ast_sorcery_generic_alloc(sizeof(struct ast_sip_aor), aor_destroy);
if (!aor) {
return NULL;
}
@@ -60,7 +60,7 @@ static void contact_destroy(void *obj)
/*! \brief Allocator for contact */
static void *contact_alloc(const char *name)
{
- struct ast_sip_contact *contact = ao2_alloc_options(sizeof(*contact), contact_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
+ struct ast_sip_contact *contact = ast_sorcery_generic_alloc(sizeof(*contact), contact_destroy);
if (!contact) {
return NULL;
diff --git a/res/res_sip/sip_configuration.c b/res/res_sip/sip_configuration.c
index 5864bdeec..49c2da28c 100644
--- a/res/res_sip/sip_configuration.c
+++ b/res/res_sip/sip_configuration.c
@@ -515,7 +515,7 @@ static int named_groups_handler(const struct aco_option *opt,
static void *sip_nat_hook_alloc(const char *name)
{
- return ao2_alloc(sizeof(struct ast_sip_nat_hook), NULL);
+ return ast_sorcery_generic_alloc(sizeof(struct ast_sip_nat_hook), NULL);
}
/*! \brief Destructor function for persistent endpoint information */
@@ -722,7 +722,7 @@ static void endpoint_destructor(void* obj)
void *ast_sip_endpoint_alloc(const char *name)
{
- struct ast_sip_endpoint *endpoint = ao2_alloc(sizeof(*endpoint), endpoint_destructor);
+ struct ast_sip_endpoint *endpoint = ast_sorcery_generic_alloc(sizeof(*endpoint), endpoint_destructor);
if (!endpoint) {
return NULL;
}
diff --git a/res/res_sip/sip_options.c b/res/res_sip/sip_options.c
index 4c8a9f6a7..4f21238b7 100644
--- a/res/res_sip/sip_options.c
+++ b/res/res_sip/sip_options.c
@@ -42,8 +42,7 @@ static int qualify_contact(struct ast_sip_contact *contact);
*/
static void *contact_status_alloc(const char *name)
{
- struct ast_sip_contact_status *status = ao2_alloc_options(
- sizeof(*status), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
+ struct ast_sip_contact_status *status = ast_sorcery_generic_alloc(sizeof(*status), NULL);
if (!status) {
ast_log(LOG_ERROR, "Unable to allocate ast_sip_contact_status\n");
diff --git a/tests/test_sorcery.c b/tests/test_sorcery.c
index e1d4b7fce..868c58268 100644
--- a/tests/test_sorcery.c
+++ b/tests/test_sorcery.c
@@ -49,7 +49,7 @@ struct test_sorcery_object {
/*! \brief Internal function to allocate a test object */
static void *test_sorcery_object_alloc(const char *id)
{
- return ao2_alloc(sizeof(struct test_sorcery_object), NULL);
+ return ast_sorcery_generic_alloc(sizeof(struct test_sorcery_object), NULL);
}
/*! \brief Internal function for object set transformation */
@@ -1300,6 +1300,71 @@ AST_TEST_DEFINE(objectset_apply_fields)
return res;
}
+AST_TEST_DEFINE(extended_fields)
+{
+ 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 ast_variable *, objset, NULL, ast_variables_destroy);
+ const char *value;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "extended_fields";
+ info->category = "/main/sorcery/";
+ info->summary = "sorcery object extended fields unit test";
+ info->description =
+ "Test extended fields support 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;
+ }
+
+ 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_variable_new("@testing", "toast", ""))) {
+ ast_test_status_update(test, "Failed to create an object set, test could not occur\n");
+ res = AST_TEST_FAIL;
+ } else if (ast_sorcery_objectset_apply(sorcery, obj, objset)) {
+ ast_test_status_update(test, "Failed to apply valid object set to object\n");
+ res = AST_TEST_FAIL;
+ } else if (!(value = ast_sorcery_object_get_extended(obj, "testing"))) {
+ ast_test_status_update(test, "Extended field, which was set using object set, could not be found\n");
+ res = AST_TEST_FAIL;
+ } else if (strcmp(value, "toast")) {
+ ast_test_status_update(test, "Extended field does not contain expected value\n");
+ res = AST_TEST_FAIL;
+ } else if (ast_sorcery_object_set_extended(obj, "@tacos", "supreme")) {
+ ast_test_status_update(test, "Extended field could not be set\n");
+ res = AST_TEST_FAIL;
+ } else if (!(value = ast_sorcery_object_get_extended(obj, "tacos"))) {
+ ast_test_status_update(test, "Extended field, which was set using the API, could not be found\n");
+ res = AST_TEST_FAIL;
+ } else if (strcmp(value, "supreme")) {
+ ast_test_status_update(test, "Extended field does not contain expected value\n");
+ res = AST_TEST_FAIL;
+ } else if (ast_sorcery_object_set_extended(obj, "@tacos", "canadian")) {
+ ast_test_status_update(test, "Extended field could not be set a second time\n");
+ res = AST_TEST_FAIL;
+ } else if (!(value = ast_sorcery_object_get_extended(obj, "tacos"))) {
+ ast_test_status_update(test, "Extended field, which was set using the API, could not be found\n");
+ res = AST_TEST_FAIL;
+ } else if (strcmp(value, "canadian")) {
+ ast_test_status_update(test, "Extended field does not contain expected value\n");
+ res = AST_TEST_FAIL;
+ }
+
+ return res;
+}
+
AST_TEST_DEFINE(changeset_create)
{
int res = AST_TEST_PASS;
@@ -2604,6 +2669,7 @@ static int unload_module(void)
AST_TEST_UNREGISTER(objectset_apply_invalid);
AST_TEST_UNREGISTER(objectset_transform);
AST_TEST_UNREGISTER(objectset_apply_fields);
+ AST_TEST_UNREGISTER(extended_fields);
AST_TEST_UNREGISTER(changeset_create);
AST_TEST_UNREGISTER(changeset_create_unchanged);
AST_TEST_UNREGISTER(object_create);
@@ -2651,6 +2717,7 @@ static int load_module(void)
AST_TEST_REGISTER(objectset_apply_invalid);
AST_TEST_REGISTER(objectset_transform);
AST_TEST_REGISTER(objectset_apply_fields);
+ AST_TEST_REGISTER(extended_fields);
AST_TEST_REGISTER(changeset_create);
AST_TEST_REGISTER(changeset_create_unchanged);
AST_TEST_REGISTER(object_create);