Merge "doc/lang/language-criteria.txt: Link to wiki."
[asterisk/asterisk.git] / tests / test_sorcery_realtime.c
index 7cf42d2..8639c0f 100644 (file)
@@ -31,8 +31,6 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "")
-
 #include "asterisk/test.h"
 #include "asterisk/module.h"
 #include "asterisk/sorcery.h"
@@ -42,64 +40,12 @@ ASTERISK_FILE_VERSION(__FILE__, "")
 /*! \brief Configuration structure which contains all stored objects */
 static struct ast_config *realtime_objects;
 
-/*! \brief Helper function which finds a given variable */
-static const struct ast_variable *realtime_find_variable(const struct ast_variable *fields, const char *name)
-{
-       const struct ast_variable *variable;
-
-       for (variable = fields; variable; variable = variable->next) {
-               if (!strcmp(variable->name, name)) {
-                       return variable;
-               }
-       }
-
-       return NULL;
-}
-
-/*! \brief Helper function which returns if an object is matching or not */
-static int realtime_is_object_matching(const char *object_id, const struct ast_variable *fields)
-{
-       const struct ast_variable *field;
-
-       for (field = fields; field; field = field->next) {
-               char *name = ast_strdupa(field->name), *like;
-               const char *value;
-
-               /* If we are doing a pattern matching we need to remove the LIKE from the name */
-               if ((like = strstr(name, " LIKE"))) {
-                       char *pattern, *field_value = ast_strdupa(field->value);
-
-                       *like = '\0';
-
-                       value = ast_strdupa(ast_variable_retrieve(realtime_objects, object_id, name));
-
-                       if (!(pattern = strchr(field_value, '%'))) {
-                               return 0;
-                       }
-
-                       *pattern = '\0';
-
-                       if (strncmp(value, field_value, strlen(field_value))) {
-                               return 0;
-                       }
-               } else {
-                       value = ast_variable_retrieve(realtime_objects, object_id, name);
-
-                       if (ast_strlen_zero(value) || strcmp(value, field->value)) {
-                               return 0;
-                       }
-               }
-       }
-
-       return 1;
-}
-
 static struct ast_variable *realtime_sorcery(const char *database, const char *table, const struct ast_variable *fields)
 {
        char *object_id = NULL;
 
        while ((object_id = ast_category_browse(realtime_objects, object_id))) {
-               if (!realtime_is_object_matching(object_id, fields)) {
+               if (!ast_variable_lists_match(ast_category_root(realtime_objects, object_id), fields, 0)) {
                        continue;
                }
 
@@ -120,8 +66,9 @@ static struct ast_config *realtime_sorcery_multi(const char *database, const cha
 
        while ((object_id = ast_category_browse(realtime_objects, object_id))) {
                struct ast_category *object;
+               const struct ast_variable *object_fields = ast_category_root(realtime_objects, object_id);
 
-               if (!realtime_is_object_matching(object_id, fields)) {
+               if (!ast_variable_lists_match(object_fields, fields, 0)) {
                        continue;
                }
 
@@ -139,15 +86,15 @@ static struct ast_config *realtime_sorcery_multi(const char *database, const cha
 
 static int realtime_sorcery_update(const char *database, const char *table, const char *keyfield, const char *entity, const struct ast_variable *fields)
 {
-       struct ast_category *object;
+       struct ast_category *object, *found;
 
-       if (!ast_category_exist(realtime_objects, entity)) {
+       if (!(found = ast_category_get(realtime_objects, entity, NULL))) {
                return 0;
        } else if (!(object = ast_category_new(entity, "", 0))) {
                return -1;
        }
 
-       ast_category_delete(realtime_objects, entity);
+       ast_category_delete(realtime_objects, found);
        ast_variable_append(object, ast_variables_dup((struct ast_variable*)fields));
        ast_variable_append(object, ast_variable_new(keyfield, entity, ""));
        ast_category_append(realtime_objects, object);
@@ -158,10 +105,10 @@ static int realtime_sorcery_update(const char *database, const char *table, cons
 static int realtime_sorcery_store(const char *database, const char *table, const struct ast_variable *fields)
 {
        /* The key field is explicit within res_sorcery_realtime */
-       const struct ast_variable *keyfield = realtime_find_variable(fields, "id");
+       const struct ast_variable *keyfield = ast_variable_find_variable_in_list(fields, "id");
        struct ast_category *object;
 
-       if (!keyfield || ast_category_exist(realtime_objects, keyfield->value) || !(object = ast_category_new(keyfield->value, "", 0))) {
+       if (!keyfield || ast_category_exist(realtime_objects, keyfield->value, NULL) || !(object = ast_category_new(keyfield->value, "", 0))) {
                return -1;
        }
 
@@ -173,11 +120,12 @@ static int realtime_sorcery_store(const char *database, const char *table, const
 
 static int realtime_sorcery_destroy(const char *database, const char *table, const char *keyfield, const char *entity, const struct ast_variable *fields)
 {
-       if (!ast_category_exist(realtime_objects, entity)) {
+       struct ast_category *found;
+       if (!(found = ast_category_get(realtime_objects, entity, NULL))) {
                return 0;
        }
 
-       ast_category_delete(realtime_objects, entity);
+       ast_category_delete(realtime_objects, found);
 
        return 1;
 }
@@ -204,7 +152,7 @@ static void *test_sorcery_object_alloc(const char *id)
        return ast_sorcery_generic_alloc(sizeof(struct test_sorcery_object), NULL);
 }
 
-static struct ast_sorcery *alloc_and_initialize_sorcery(void)
+static struct ast_sorcery *alloc_and_initialize_sorcery(char *table)
 {
        struct ast_sorcery *sorcery;
 
@@ -212,7 +160,7 @@ static struct ast_sorcery *alloc_and_initialize_sorcery(void)
                return NULL;
        }
 
-       if (ast_sorcery_apply_default(sorcery, "test", "realtime", "sorcery_realtime_test") ||
+       if ((ast_sorcery_apply_default(sorcery, "test", "realtime", table) != AST_SORCERY_APPLY_SUCCESS) ||
                ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL) ||
                !(realtime_objects = ast_config_new())) {
                ast_sorcery_unref(sorcery);
@@ -249,7 +197,7 @@ AST_TEST_DEFINE(object_create)
                break;
        }
 
-       if (!(sorcery = alloc_and_initialize_sorcery())) {
+       if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
                ast_test_status_update(test, "Failed to open sorcery structure\n");
                return AST_TEST_FAIL;
        }
@@ -284,7 +232,7 @@ AST_TEST_DEFINE(object_retrieve_id)
                break;
        }
 
-       if (!(sorcery = alloc_and_initialize_sorcery())) {
+       if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
                ast_test_status_update(test, "Failed to open sorcery structure\n");
                return AST_TEST_FAIL;
        }
@@ -347,7 +295,7 @@ AST_TEST_DEFINE(object_retrieve_field)
                return AST_TEST_FAIL;
        }
 
-       if (!(sorcery = alloc_and_initialize_sorcery())) {
+       if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
                ast_test_status_update(test, "Failed to open sorcery structure\n");
                return AST_TEST_FAIL;
        }
@@ -405,7 +353,7 @@ AST_TEST_DEFINE(object_retrieve_multiple_all)
                break;
        }
 
-       if (!(sorcery = alloc_and_initialize_sorcery())) {
+       if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
                ast_test_status_update(test, "Failed to open sorcery structure\n");
                return AST_TEST_FAIL;
        }
@@ -443,6 +391,63 @@ AST_TEST_DEFINE(object_retrieve_multiple_all)
        return AST_TEST_PASS;
 }
 
+AST_TEST_DEFINE(object_retrieve_multiple_all_nofetch)
+{
+       RAII_VAR(struct ast_sorcery *, sorcery, NULL, deinitialize_sorcery);
+       RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
+       RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = "object_retrieve_multiple_all_nofetch";
+               info->category = "/res/sorcery_realtime/";
+               info->summary = "sorcery multiple object retrieval unit test";
+               info->description =
+                       "Test multiple object retrieval in sorcery using realtime wizard";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test,allow_unqualified_fetch=no"))) {
+               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 (ast_sorcery_create(sorcery, obj)) {
+               ast_test_status_update(test, "Failed to create object using realtime wizard\n");
+               return AST_TEST_FAIL;
+       }
+
+       ao2_cleanup(obj);
+
+       if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah2"))) {
+               ast_test_status_update(test, "Failed to allocate second instance of a known object type\n");
+               return AST_TEST_FAIL;
+       }
+
+       if (ast_sorcery_create(sorcery, obj)) {
+               ast_test_status_update(test, "Failed to create second object using realtime wizard\n");
+               return AST_TEST_FAIL;
+       }
+
+       if (!(objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL))) {
+               ast_test_status_update(test, "Failed to retrieve a container of all objects\n");
+               return AST_TEST_FAIL;
+       } else if (ao2_container_count(objects) != 0) {
+               ast_test_status_update(test, "Received a container with objects in it when there should be none\n");
+               return AST_TEST_FAIL;
+       }
+
+       return AST_TEST_PASS;
+}
+
+
 AST_TEST_DEFINE(object_retrieve_multiple_field)
 {
        RAII_VAR(struct ast_sorcery *, sorcery, NULL, deinitialize_sorcery);
@@ -467,7 +472,7 @@ AST_TEST_DEFINE(object_retrieve_multiple_field)
                return AST_TEST_FAIL;
        }
 
-       if (!(sorcery = alloc_and_initialize_sorcery())) {
+       if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
                ast_test_status_update(test, "Failed to open sorcery structure\n");
                return AST_TEST_FAIL;
        }
@@ -527,7 +532,7 @@ AST_TEST_DEFINE(object_retrieve_regex)
                break;
        }
 
-       if (!(sorcery = alloc_and_initialize_sorcery())) {
+       if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
                ast_test_status_update(test, "Failed to open sorcery structure\n");
                return AST_TEST_FAIL;
        }
@@ -566,41 +571,41 @@ AST_TEST_DEFINE(object_retrieve_regex)
                return AST_TEST_FAIL;
        }
 
-       if (!(objects = ast_sorcery_retrieve_by_regex(sorcery, "test", "^blah-"))) {
+       if (!(objects = ast_sorcery_retrieve_by_regex(sorcery, "test", "blah-"))) {
                ast_test_status_update(test, "Failed to retrieve a container of objects\n");
                return AST_TEST_FAIL;
        } else if (ao2_container_count(objects) != 2) {
-               ast_test_status_update(test, "Received a container with incorrect number of objects in it\n");
+               ast_test_status_update(test, "Received a container with incorrect number of objects in it: %d instead of 2\n", ao2_container_count(objects));
                return AST_TEST_FAIL;
        }
 
        return AST_TEST_PASS;
 }
 
-AST_TEST_DEFINE(object_update)
+AST_TEST_DEFINE(object_retrieve_regex_nofetch)
 {
        RAII_VAR(struct ast_sorcery *, sorcery, NULL, deinitialize_sorcery);
        RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
-       RAII_VAR(struct test_sorcery_object *, obj2, NULL, ao2_cleanup);
+       RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
 
        switch (cmd) {
        case TEST_INIT:
-               info->name = "object_update";
+               info->name = "object_retrieve_regex_nofetch";
                info->category = "/res/sorcery_realtime/";
-               info->summary = "sorcery object update unit test";
+               info->summary = "sorcery multiple object retrieval using regex unit test";
                info->description =
-                       "Test object updating in sorcery using realtime wizard";
+                       "Test multiple object retrieval in sorcery using regular expression for matching using realtime wizard";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
        }
 
-       if (!(sorcery = alloc_and_initialize_sorcery())) {
+       if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test,allow_unqualified_fetch=no"))) {
                ast_test_status_update(test, "Failed to open sorcery structure\n");
                return AST_TEST_FAIL;
        }
 
-       if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
+       if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah-98joe"))) {
                ast_test_status_update(test, "Failed to allocate a known object type\n");
                return AST_TEST_FAIL;
        }
@@ -610,50 +615,60 @@ AST_TEST_DEFINE(object_update)
                return AST_TEST_FAIL;
        }
 
-       if (!(obj2 = ast_sorcery_copy(sorcery, obj))) {
-               ast_test_status_update(test, "Failed to allocate a known object type for updating\n");
+       ao2_cleanup(obj);
+
+       if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah-93joe"))) {
+               ast_test_status_update(test, "Failed to allocate second instance of a known object type\n");
+               return AST_TEST_FAIL;
+       }
+
+       if (ast_sorcery_create(sorcery, obj)) {
+               ast_test_status_update(test, "Failed to create second object using astdb wizard\n");
                return AST_TEST_FAIL;
        }
 
        ao2_cleanup(obj);
 
-       obj2->bob = 1000;
-       obj2->joe = 2000;
+       if (!(obj = ast_sorcery_alloc(sorcery, "test", "neener-93joe"))) {
+               ast_test_status_update(test, "Failed to allocate third instance of a known object type\n");
+               return AST_TEST_FAIL;
+       }
 
-       if (ast_sorcery_update(sorcery, obj2)) {
-               ast_test_status_update(test, "Failed to update sorcery with new object\n");
+       if (ast_sorcery_create(sorcery, obj)) {
+               ast_test_status_update(test, "Failed to create third object using astdb wizard\n");
                return AST_TEST_FAIL;
        }
 
-       if (!(obj = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
-               ast_test_status_update(test, "Failed to retrieve properly updated object\n");
+       if (!(objects = ast_sorcery_retrieve_by_regex(sorcery, "test", ""))) {
+               ast_test_status_update(test, "Failed to retrieve a container of objects\n");
                return AST_TEST_FAIL;
-       } else if ((obj->bob != obj2->bob) || (obj->joe != obj2->joe)) {
-               ast_test_status_update(test, "Object retrieved is not the updated object\n");
+       } else if (ao2_container_count(objects) != 0) {
+               ast_test_status_update(test, "Received a container with incorrect number of objects in it: %d instead of 0\n", ao2_container_count(objects));
                return AST_TEST_FAIL;
        }
 
        return AST_TEST_PASS;
 }
 
-AST_TEST_DEFINE(object_update_uncreated)
+AST_TEST_DEFINE(object_update)
 {
        RAII_VAR(struct ast_sorcery *, sorcery, NULL, deinitialize_sorcery);
        RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
+       RAII_VAR(struct test_sorcery_object *, obj2, NULL, ao2_cleanup);
 
        switch (cmd) {
        case TEST_INIT:
-               info->name = "object_update_uncreated";
+               info->name = "object_update";
                info->category = "/res/sorcery_realtime/";
                info->summary = "sorcery object update unit test";
                info->description =
-                       "Test updating of an uncreated object in sorcery using realtime wizard";
+                       "Test object updating in sorcery using realtime wizard";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
        }
 
-       if (!(sorcery = alloc_and_initialize_sorcery())) {
+       if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
                ast_test_status_update(test, "Failed to open sorcery structure\n");
                return AST_TEST_FAIL;
        }
@@ -663,8 +678,31 @@ AST_TEST_DEFINE(object_update_uncreated)
                return AST_TEST_FAIL;
        }
 
-       if (!ast_sorcery_update(sorcery, obj)) {
-               ast_test_status_update(test, "Successfully updated an object which has not been created yet\n");
+       if (ast_sorcery_create(sorcery, obj)) {
+               ast_test_status_update(test, "Failed to create object using realtime wizard\n");
+               return AST_TEST_FAIL;
+       }
+
+       if (!(obj2 = ast_sorcery_copy(sorcery, obj))) {
+               ast_test_status_update(test, "Failed to allocate a known object type for updating\n");
+               return AST_TEST_FAIL;
+       }
+
+       ao2_cleanup(obj);
+
+       obj2->bob = 1000;
+       obj2->joe = 2000;
+
+       if (ast_sorcery_update(sorcery, obj2)) {
+               ast_test_status_update(test, "Failed to update sorcery with new object\n");
+               return AST_TEST_FAIL;
+       }
+
+       if (!(obj = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
+               ast_test_status_update(test, "Failed to retrieve properly updated object\n");
+               return AST_TEST_FAIL;
+       } else if ((obj->bob != obj2->bob) || (obj->joe != obj2->joe)) {
+               ast_test_status_update(test, "Object retrieved is not the updated object\n");
                return AST_TEST_FAIL;
        }
 
@@ -688,7 +726,7 @@ AST_TEST_DEFINE(object_delete)
                break;
        }
 
-       if (!(sorcery = alloc_and_initialize_sorcery())) {
+       if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
                ast_test_status_update(test, "Failed to open sorcery structure\n");
                return AST_TEST_FAIL;
        }
@@ -735,7 +773,7 @@ AST_TEST_DEFINE(object_delete_uncreated)
                break;
        }
 
-       if (!(sorcery = alloc_and_initialize_sorcery())) {
+       if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
                ast_test_status_update(test, "Failed to open sorcery structure\n");
                return AST_TEST_FAIL;
        }
@@ -773,7 +811,7 @@ AST_TEST_DEFINE(object_allocate_on_retrieval)
                break;
        }
 
-       if (!(sorcery = alloc_and_initialize_sorcery())) {
+       if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
                ast_test_status_update(test, "Failed to open sorcery structure\n");
                return AST_TEST_FAIL;
        }
@@ -790,11 +828,11 @@ AST_TEST_DEFINE(object_allocate_on_retrieval)
        }
 
        if (obj->bob != 42) {
-               ast_test_status_update(test, "Object's 'bob' field does not have expected value: %d != 42\n",
+               ast_test_status_update(test, "Object's 'bob' field does not have expected value: %u != 42\n",
                                obj->bob);
                return AST_TEST_FAIL;
        } else if (obj->joe != 93) {
-               ast_test_status_update(test, "Object's 'joe' field does not have expected value: %d != 93\n",
+               ast_test_status_update(test, "Object's 'joe' field does not have expected value: %u != 93\n",
                                obj->joe);
                return AST_TEST_FAIL;
        }
@@ -820,13 +858,13 @@ AST_TEST_DEFINE(object_filter)
                        "realtime backend that is unknown to sorcery. When sorcery attempts to retrieve\n"
                        "the object from the realtime backend, the data unknown to sorcery should be\n"
                        "filtered out of the returned objectset, and the object should be successfully\n"
-                       "allocated by sorcery\n";
+                       "allocated by sorcery";
                return AST_TEST_NOT_RUN;
        case TEST_EXECUTE:
                break;
        }
 
-       if (!(sorcery = alloc_and_initialize_sorcery())) {
+       if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
                ast_test_status_update(test, "Failed to open sorcery structure\n");
                return AST_TEST_FAIL;
        }
@@ -844,11 +882,11 @@ AST_TEST_DEFINE(object_filter)
        }
 
        if (obj->bob != 42) {
-               ast_test_status_update(test, "Object's 'bob' field does not have expected value: %d != 42\n",
+               ast_test_status_update(test, "Object's 'bob' field does not have expected value: %u != 42\n",
                                obj->bob);
                return AST_TEST_FAIL;
        } else if (obj->joe != 93) {
-               ast_test_status_update(test, "Object's 'joe' field does not have expected value: %d != 93\n",
+               ast_test_status_update(test, "Object's 'joe' field does not have expected value: %u != 93\n",
                                obj->joe);
                return AST_TEST_FAIL;
        }
@@ -862,10 +900,11 @@ static int unload_module(void)
        AST_TEST_UNREGISTER(object_retrieve_id);
        AST_TEST_UNREGISTER(object_retrieve_field);
        AST_TEST_UNREGISTER(object_retrieve_multiple_all);
+       AST_TEST_UNREGISTER(object_retrieve_multiple_all_nofetch);
        AST_TEST_UNREGISTER(object_retrieve_multiple_field);
        AST_TEST_UNREGISTER(object_retrieve_regex);
+       AST_TEST_UNREGISTER(object_retrieve_regex_nofetch);
        AST_TEST_UNREGISTER(object_update);
-       AST_TEST_UNREGISTER(object_update_uncreated);
        AST_TEST_UNREGISTER(object_delete);
        AST_TEST_UNREGISTER(object_delete_uncreated);
        AST_TEST_UNREGISTER(object_allocate_on_retrieval);
@@ -882,10 +921,11 @@ static int load_module(void)
        AST_TEST_REGISTER(object_retrieve_id);
        AST_TEST_REGISTER(object_retrieve_field);
        AST_TEST_REGISTER(object_retrieve_multiple_all);
+       AST_TEST_REGISTER(object_retrieve_multiple_all_nofetch);
        AST_TEST_REGISTER(object_retrieve_multiple_field);
        AST_TEST_REGISTER(object_retrieve_regex);
+       AST_TEST_REGISTER(object_retrieve_regex_nofetch);
        AST_TEST_REGISTER(object_update);
-       AST_TEST_REGISTER(object_update_uncreated);
        AST_TEST_REGISTER(object_delete);
        AST_TEST_REGISTER(object_delete_uncreated);
        AST_TEST_REGISTER(object_allocate_on_retrieval);