[asterisk-commits] file: trunk r392586 - in /trunk: include/asterisk/ main/ res/res_sip/ tests/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Sat Jun 22 09:26:31 CDT 2013


Author: file
Date: Sat Jun 22 09:26:25 2013
New Revision: 392586

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=392586
Log:
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/

Modified:
    trunk/include/asterisk/sorcery.h
    trunk/main/sorcery.c
    trunk/res/res_sip/config_auth.c
    trunk/res/res_sip/config_domain_aliases.c
    trunk/res/res_sip/config_transport.c
    trunk/res/res_sip/location.c
    trunk/res/res_sip/sip_configuration.c
    trunk/res/res_sip/sip_options.c
    trunk/tests/test_sorcery.c

Modified: trunk/include/asterisk/sorcery.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/sorcery.h?view=diff&rev=392586&r1=392585&r2=392586
==============================================================================
--- trunk/include/asterisk/sorcery.h (original)
+++ trunk/include/asterisk/sorcery.h Sat Jun 22 09:26:25 2013
@@ -247,13 +247,13 @@
 	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_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_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

Modified: trunk/main/sorcery.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/sorcery.c?view=diff&rev=392586&r1=392585&r2=392586
==============================================================================
--- trunk/main/sorcery.c (original)
+++ trunk/main/sorcery.c Sat Jun 22 09:26:25 2013
@@ -58,6 +58,21 @@
 /*! \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 @@
 	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);
@@ -544,6 +577,10 @@
 	object_type->file->types[1] = NULL;
 
 	if (aco_info_init(object_type->info)) {
+		return -1;
+	}
+
+	if (ast_sorcery_object_fields_register(sorcery, type, "^@", sorcery_extended_config_handler, sorcery_extended_fields_handler)) {
 		return -1;
 	}
 
@@ -784,7 +821,7 @@
 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 @@
 			continue;
 		}
 
-		if (!res) {
+		if (!res && tmp) {
 			tmp->next = fields;
 			fields = tmp;
 		}
@@ -836,7 +873,7 @@
 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 @@
 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 @@
 	}
 
 	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 @@
 	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 @@
 	}
 
 	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->type, type, sizeof(details->type));
+		ast_copy_string(details->object->id, id, sizeof(details->object->id));
+	}
+
+	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_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 @@
 	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 @@
 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 @@
 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 @@
 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 @@
 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)

Modified: trunk/res/res_sip/config_auth.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_sip/config_auth.c?view=diff&rev=392586&r1=392585&r2=392586
==============================================================================
--- trunk/res/res_sip/config_auth.c (original)
+++ trunk/res/res_sip/config_auth.c Sat Jun 22 09:26:25 2013
@@ -32,7 +32,7 @@
 
 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;

Modified: trunk/res/res_sip/config_domain_aliases.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_sip/config_domain_aliases.c?view=diff&rev=392586&r1=392585&r2=392586
==============================================================================
--- trunk/res/res_sip/config_domain_aliases.c (original)
+++ trunk/res/res_sip/config_domain_aliases.c Sat Jun 22 09:26:25 2013
@@ -33,7 +33,7 @@
 
 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;

Modified: trunk/res/res_sip/config_transport.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_sip/config_transport.c?view=diff&rev=392586&r1=392585&r2=392586
==============================================================================
--- trunk/res/res_sip/config_transport.c (original)
+++ trunk/res/res_sip/config_transport.c Sat Jun 22 09:26:25 2013
@@ -62,7 +62,7 @@
 /*! \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;

Modified: trunk/res/res_sip/location.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_sip/location.c?view=diff&rev=392586&r1=392585&r2=392586
==============================================================================
--- trunk/res/res_sip/location.c (original)
+++ trunk/res/res_sip/location.c Sat Jun 22 09:26:25 2013
@@ -41,7 +41,7 @@
 /*! \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 @@
 /*! \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;

Modified: trunk/res/res_sip/sip_configuration.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_sip/sip_configuration.c?view=diff&rev=392586&r1=392585&r2=392586
==============================================================================
--- trunk/res/res_sip/sip_configuration.c (original)
+++ trunk/res/res_sip/sip_configuration.c Sat Jun 22 09:26:25 2013
@@ -515,7 +515,7 @@
 
 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 @@
 
 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;
 	}

Modified: trunk/res/res_sip/sip_options.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_sip/sip_options.c?view=diff&rev=392586&r1=392585&r2=392586
==============================================================================
--- trunk/res/res_sip/sip_options.c (original)
+++ trunk/res/res_sip/sip_options.c Sat Jun 22 09:26:25 2013
@@ -42,8 +42,7 @@
  */
 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");

Modified: trunk/tests/test_sorcery.c
URL: http://svnview.digium.com/svn/asterisk/trunk/tests/test_sorcery.c?view=diff&rev=392586&r1=392585&r2=392586
==============================================================================
--- trunk/tests/test_sorcery.c (original)
+++ trunk/tests/test_sorcery.c Sat Jun 22 09:26:25 2013
@@ -49,7 +49,7 @@
 /*! \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 */
@@ -1294,6 +1294,71 @@
 		res = AST_TEST_FAIL;
 	} else if (obj->bob != 256) {
 		ast_test_status_update(test, "Regex field handler was not called when it should have been\n");
+		res = AST_TEST_FAIL;
+	}
+
+	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;
 	}
 
@@ -2604,6 +2669,7 @@
 	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 @@
 	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);




More information about the asterisk-commits mailing list