Fix an issue where building with DEBUG_FD_LEAKS enabled would not work due to sorcery...
[asterisk/asterisk.git] / tests / test_sorcery.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2012 - 2013, Digium, Inc.
5  *
6  * Joshua Colp <jcolp@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*!
20  * \file
21  * \brief Sorcery Unit Tests
22  *
23  * \author Joshua Colp <jcolp@digium.com>
24  *
25  */
26
27 /*** MODULEINFO
28         <depend>TEST_FRAMEWORK</depend>
29         <support_level>core</support_level>
30  ***/
31
32 #include "asterisk.h"
33
34 ASTERISK_FILE_VERSION(__FILE__, "")
35
36 #include "asterisk/test.h"
37 #include "asterisk/module.h"
38 #include "asterisk/sorcery.h"
39 #include "asterisk/logger.h"
40
41 /*! \brief Dummy sorcery object */
42 struct test_sorcery_object {
43         SORCERY_OBJECT(details);
44         unsigned int bob;
45         unsigned int joe;
46 };
47
48 /*! \brief Internal function to allocate a test object */
49 static void *test_sorcery_object_alloc(const char *id)
50 {
51         return ao2_alloc(sizeof(struct test_sorcery_object), NULL);
52 }
53
54 /*! \brief Internal function for object set transformation */
55 static struct ast_variable *test_sorcery_transform(struct ast_variable *set)
56 {
57         struct ast_variable *field, *transformed = NULL;
58
59         for (field = set; field; field = field->next) {
60                 struct ast_variable *transformed_field;
61
62                 if (!strcmp(field->name, "joe")) {
63                         transformed_field = ast_variable_new(field->name, "5000", "");
64                 } else {
65                         transformed_field = ast_variable_new(field->name, field->value, "");
66                 }
67
68                 if (!transformed_field) {
69                         ast_variables_destroy(transformed);
70                         return NULL;
71                 }
72
73                 transformed_field->next = transformed;
74                 transformed = transformed_field;
75         }
76
77         return transformed;
78 }
79
80 /*! \brief Test structure for caching */
81 struct sorcery_test_caching {
82         /*! \brief Whether the object has been created in the cache or not */
83         unsigned int created:1;
84
85         /*! \brief Whether the object has been updated in the cache or not */
86         unsigned int updated:1;
87
88         /*! \brief Whether the object has been deleted from the cache or not */
89         unsigned int deleted:1;
90
91         /*! \brief Object to return when asked */
92         struct test_sorcery_object object;
93 };
94
95 /*! \brief Global scope apply handler integer to make sure it executed */
96 static int apply_handler_called;
97
98 /*! \brief Simple apply handler which sets global scope integer to 1 if called */
99 static void test_apply_handler(const struct ast_sorcery *sorcery, void *obj)
100 {
101         apply_handler_called = 1;
102 }
103
104 /*! \brief Global scope caching structure for testing */
105 static struct sorcery_test_caching cache = { 0, };
106
107 static int sorcery_test_create(void *data, void *object)
108 {
109         cache.created = 1;
110         cache.updated = 0;
111         cache.deleted = 0;
112         return 0;
113 }
114
115 static void *sorcery_test_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type, const char *id)
116 {
117         return (cache.created && !cache.deleted) ? ast_sorcery_alloc(sorcery, type, id) : NULL;
118 }
119
120 static int sorcery_test_update(void *data, void *object)
121 {
122         cache.updated = 1;
123         return 0;
124 }
125
126 static int sorcery_test_delete(void *data, void *object)
127 {
128         cache.deleted = 1;
129         return 0;
130 }
131
132 /*! \brief Dummy sorcery wizard, not actually used so we only populate the name and nothing else */
133 static struct ast_sorcery_wizard test_wizard = {
134         .name = "test",
135         .create = sorcery_test_create,
136         .retrieve_id = sorcery_test_retrieve_id,
137         .update = sorcery_test_update,
138         .delete = sorcery_test_delete,
139 };
140
141 static struct ast_sorcery *alloc_and_initialize_sorcery(void)
142 {
143         struct ast_sorcery *sorcery;
144
145         if (!(sorcery = ast_sorcery_open())) {
146                 return NULL;
147         }
148
149         if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) ||
150             ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
151                 ast_sorcery_unref(sorcery);
152                 return NULL;
153         }
154
155         ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
156         ast_sorcery_object_field_register(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
157
158         return sorcery;
159 }
160
161 AST_TEST_DEFINE(wizard_registration)
162 {
163         switch (cmd) {
164         case TEST_INIT:
165                 info->name = "wizard_registration";
166                 info->category = "/main/sorcery/";
167                 info->summary = "sorcery wizard registration and unregistration unit test";
168                 info->description =
169                         "Test registration and unregistration of a sorcery wizard";
170                 return AST_TEST_NOT_RUN;
171         case TEST_EXECUTE:
172                 break;
173         }
174
175         if (ast_sorcery_wizard_register(&test_wizard)) {
176                 ast_test_status_update(test, "Failed to register a perfectly valid sorcery wizard\n");
177                 return AST_TEST_FAIL;
178         }
179
180         if (!ast_sorcery_wizard_register(&test_wizard)) {
181                 ast_test_status_update(test, "Successfully registered a sorcery wizard twice, which is bad\n");
182                 return AST_TEST_FAIL;
183         }
184
185         if (ast_sorcery_wizard_unregister(&test_wizard)) {
186                 ast_test_status_update(test, "Failed to unregister a perfectly valid sorcery wizard\n");
187                 return AST_TEST_FAIL;
188         }
189
190         if (!ast_sorcery_wizard_unregister(&test_wizard)) {
191                 ast_test_status_update(test, "Successfully unregistered a sorcery wizard twice, which is bad\n");
192                 return AST_TEST_FAIL;
193         }
194
195         return AST_TEST_PASS;
196 }
197
198 AST_TEST_DEFINE(sorcery_open)
199 {
200         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
201
202         switch (cmd) {
203         case TEST_INIT:
204                 info->name = "open";
205                 info->category = "/main/sorcery/";
206                 info->summary = "sorcery open unit test";
207                 info->description =
208                         "Test opening of sorcery";
209                 return AST_TEST_NOT_RUN;
210         case TEST_EXECUTE:
211                 break;
212         }
213
214         if (!(sorcery = ast_sorcery_open())) {
215                 ast_test_status_update(test, "Failed to open new sorcery structure\n");
216                 return AST_TEST_FAIL;
217         }
218
219         return AST_TEST_PASS;
220 }
221
222 AST_TEST_DEFINE(apply_default)
223 {
224         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
225
226         switch (cmd) {
227         case TEST_INIT:
228                 info->name = "apply_default";
229                 info->category = "/main/sorcery/";
230                 info->summary = "sorcery default wizard unit test";
231                 info->description =
232                         "Test setting default type wizard in sorcery";
233                 return AST_TEST_NOT_RUN;
234         case TEST_EXECUTE:
235                 break;
236         }
237
238         if (!(sorcery = ast_sorcery_open())) {
239                 ast_test_status_update(test, "Failed to open sorcery structure\n");
240                 return AST_TEST_FAIL;
241         }
242
243         if (!ast_sorcery_apply_default(sorcery, "test", "dummy", NULL)) {
244                 ast_test_status_update(test, "Successfully set a default wizard that doesn't exist\n");
245                 return AST_TEST_FAIL;
246         }
247
248         if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
249                 ast_test_status_update(test, "Failed to set a known wizard as a default\n");
250                 return AST_TEST_FAIL;
251         }
252
253         if (!ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
254                 ast_test_status_update(test, "Successfully set a default wizard on a type twice\n");
255                 return AST_TEST_FAIL;
256         }
257
258         return AST_TEST_PASS;
259 }
260
261 AST_TEST_DEFINE(apply_config)
262 {
263         struct ast_flags flags = { CONFIG_FLAG_NOCACHE };
264         struct ast_config *config;
265         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
266
267         switch (cmd) {
268         case TEST_INIT:
269                 info->name = "apply_config";
270                 info->category = "/main/sorcery/";
271                 info->summary = "sorcery object mapping configuration unit test";
272                 info->description =
273                         "Test configured object mapping in sorcery";
274                 return AST_TEST_NOT_RUN;
275         case TEST_EXECUTE:
276                 break;
277         }
278
279         if (!(config = ast_config_load2("sorcery.conf", "test_sorcery", flags))) {
280                 ast_test_status_update(test, "Sorcery configuration file not present - skipping apply_config test\n");
281                 return AST_TEST_NOT_RUN;
282         }
283
284         if (!ast_category_get(config, "test_sorcery")) {
285                 ast_test_status_update(test, "Sorcery configuration file does not have test_sorcery section\n");
286                 ast_config_destroy(config);
287                 return AST_TEST_NOT_RUN;
288         }
289
290         ast_config_destroy(config);
291
292         if (!(sorcery = ast_sorcery_open())) {
293                 ast_test_status_update(test, "Failed to open sorcery structure\n");
294                 return AST_TEST_FAIL;
295         }
296
297         if (ast_sorcery_apply_config(sorcery, "test_sorcery")) {
298                 ast_test_status_update(test, "Failed to apply configured object mappings\n");
299                 return AST_TEST_FAIL;
300         }
301
302         return AST_TEST_PASS;
303 }
304
305 AST_TEST_DEFINE(object_register)
306 {
307         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
308
309         switch (cmd) {
310         case TEST_INIT:
311                 info->name = "object_register";
312                 info->category = "/main/sorcery/";
313                 info->summary = "sorcery object type registration unit test";
314                 info->description =
315                         "Test object type registration in sorcery";
316                 return AST_TEST_NOT_RUN;
317         case TEST_EXECUTE:
318                 break;
319         }
320
321         if (!(sorcery = ast_sorcery_open())) {
322                 ast_test_status_update(test, "Failed to open structure\n");
323                 return AST_TEST_FAIL;
324         }
325
326         if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
327                 ast_test_status_update(test, "Failed to set a known wizard as a default\n");
328                 return AST_TEST_FAIL;
329         }
330
331         if (ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
332                 ast_test_status_update(test, "Failed to register object type\n");
333                 return AST_TEST_FAIL;
334         }
335
336         return AST_TEST_PASS;
337 }
338
339 AST_TEST_DEFINE(object_register_without_mapping)
340 {
341         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
342
343         switch (cmd) {
344         case TEST_INIT:
345                 info->name = "object_register_without_mapping";
346                 info->category = "/main/sorcery/";
347                 info->summary = "sorcery object type registration (without mapping) unit test";
348                 info->description =
349                         "Test object type registration when no mapping exists in sorcery";
350                 return AST_TEST_NOT_RUN;
351         case TEST_EXECUTE:
352                 break;
353         }
354
355         if (!(sorcery = ast_sorcery_open())) {
356                 ast_test_status_update(test, "Failed to open sorcery structure\n");
357                 return AST_TEST_FAIL;
358         }
359
360         if (!ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
361                 ast_test_status_update(test, "Registered object type when no object mapping exists\n");
362                 return AST_TEST_FAIL;
363         }
364
365         return AST_TEST_PASS;
366 }
367
368 AST_TEST_DEFINE(object_field_register)
369 {
370         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
371
372         switch (cmd) {
373         case TEST_INIT:
374                 info->name = "object_field_register";
375                 info->category = "/main/sorcery/";
376                 info->summary = "sorcery object field registration unit test";
377                 info->description =
378                         "Test object field registration in sorcery with a provided id";
379                 return AST_TEST_NOT_RUN;
380         case TEST_EXECUTE:
381                 break;
382         }
383
384         if (!(sorcery = ast_sorcery_open())) {
385                 ast_test_status_update(test, "Failed to open sorcery structure\n");
386                 return AST_TEST_FAIL;
387         }
388
389         if (!ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob))) {
390                 ast_test_status_update(test, "Registered an object field successfully when no mappings or object types exist\n");
391                 return AST_TEST_FAIL;
392         }
393
394         if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
395                 ast_test_status_update(test, "Failed to set a known wizard as a default\n");
396                 return AST_TEST_FAIL;
397         }
398
399         if (!ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob))) {
400                 ast_test_status_update(test, "Registered an object field successfully when object type does not exist\n");
401                 return AST_TEST_FAIL;
402         }
403
404         if (ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
405                 ast_test_status_update(test, "Failed to register object type\n");
406                 return AST_TEST_FAIL;
407         }
408
409         if (ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob))) {
410                 ast_test_status_update(test, "Could not successfully register object field when mapping and object type exists\n");
411                 return AST_TEST_FAIL;
412         }
413
414         return AST_TEST_PASS;
415 }
416
417 AST_TEST_DEFINE(object_alloc_with_id)
418 {
419         int res = AST_TEST_PASS;
420         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
421         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
422
423         switch (cmd) {
424         case TEST_INIT:
425                 info->name = "object_alloc_with_id";
426                 info->category = "/main/sorcery/";
427                 info->summary = "sorcery object allocation (with id) unit test";
428                 info->description =
429                         "Test object allocation in sorcery with a provided id";
430                 return AST_TEST_NOT_RUN;
431         case TEST_EXECUTE:
432                 break;
433         }
434
435         if (!(sorcery = alloc_and_initialize_sorcery())) {
436                 ast_test_status_update(test, "Failed to open sorcery structure\n");
437                 return AST_TEST_FAIL;
438         }
439
440         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
441                 ast_test_status_update(test, "Failed to allocate a known object type\n");
442                 res = AST_TEST_FAIL;
443         } else if (ast_strlen_zero(ast_sorcery_object_get_id(obj))) {
444                 ast_test_status_update(test, "Allocated object has empty id when it should not\n");
445                 res = AST_TEST_FAIL;
446         } else if (strcmp(ast_sorcery_object_get_id(obj), "blah")) {
447                 ast_test_status_update(test, "Allocated object does not have correct id\n");
448                 res = AST_TEST_FAIL;
449         } else if (ast_strlen_zero(ast_sorcery_object_get_type(obj))) {
450                 ast_test_status_update(test, "Allocated object has empty type when it should not\n");
451                 res = AST_TEST_FAIL;
452         } else if (strcmp(ast_sorcery_object_get_type(obj), "test")) {
453                 ast_test_status_update(test, "Allocated object does not have correct type\n");
454                 res = AST_TEST_FAIL;
455         } else if ((obj->bob != 5) || (obj->joe != 10)) {
456                 ast_test_status_update(test, "Allocated object does not have defaults set as it should\n");
457                 res = AST_TEST_FAIL;
458         }
459
460         return res;
461 }
462
463 AST_TEST_DEFINE(object_alloc_without_id)
464 {
465         int res = AST_TEST_PASS;
466         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
467         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
468
469         switch (cmd) {
470         case TEST_INIT:
471                 info->name = "object_alloc_without_id";
472                 info->category = "/main/sorcery/";
473                 info->summary = "sorcery object allocation (without id) unit test";
474                 info->description =
475                         "Test object allocation in sorcery with no provided id";
476                 return AST_TEST_NOT_RUN;
477         case TEST_EXECUTE:
478                 break;
479         }
480
481         if (!(sorcery = alloc_and_initialize_sorcery())) {
482                 ast_test_status_update(test, "Failed to open sorcery structure\n");
483                 return AST_TEST_FAIL;
484         }
485
486         if (!(obj = ast_sorcery_alloc(sorcery, "test", NULL))) {
487                 ast_test_status_update(test, "Failed to allocate a known object type\n");
488                 res = AST_TEST_FAIL;
489         } else if (ast_strlen_zero(ast_sorcery_object_get_id(obj))) {
490                 ast_test_status_update(test, "Allocated object has empty id when it should not\n");
491                 res = AST_TEST_FAIL;
492         }
493
494         return res;
495 }
496
497
498 AST_TEST_DEFINE(object_copy)
499 {
500         int res = AST_TEST_PASS;
501         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
502         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
503         RAII_VAR(struct test_sorcery_object *, copy, NULL, ao2_cleanup);
504
505         switch (cmd) {
506         case TEST_INIT:
507                 info->name = "object_copy";
508                 info->category = "/main/sorcery/";
509                 info->summary = "sorcery object copy unit test";
510                 info->description =
511                         "Test object copy in sorcery";
512                 return AST_TEST_NOT_RUN;
513         case TEST_EXECUTE:
514                 break;
515         }
516
517         if (!(sorcery = alloc_and_initialize_sorcery())) {
518                 ast_test_status_update(test, "Failed to open sorcery structure\n");
519                 return AST_TEST_FAIL;
520         }
521
522         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
523                 ast_test_status_update(test, "Failed to allocate a known object type\n");
524                 return AST_TEST_FAIL;
525         }
526
527         obj->bob = 50;
528         obj->joe = 100;
529
530         if (!(copy = ast_sorcery_copy(sorcery, obj))) {
531                 ast_test_status_update(test, "Failed to create a copy of a known valid object\n");
532                 res = AST_TEST_FAIL;
533         } else if (copy == obj) {
534                 ast_test_status_update(test, "Created copy is actually the original object\n");
535                 res = AST_TEST_FAIL;
536         } else if (copy->bob != obj->bob) {
537                 ast_test_status_update(test, "Value of 'bob' on newly created copy is not the same as original\n");
538                 res = AST_TEST_FAIL;
539         } else if (copy->joe != obj->joe) {
540                 ast_test_status_update(test, "Value of 'joe' on newly created copy is not the same as original\n");
541                 res = AST_TEST_FAIL;
542         }
543
544         return res;
545 }
546
547 AST_TEST_DEFINE(object_diff)
548 {
549        RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
550        RAII_VAR(struct test_sorcery_object *, obj1, NULL, ao2_cleanup);
551        RAII_VAR(struct test_sorcery_object *, obj2, NULL, ao2_cleanup);
552        RAII_VAR(struct ast_variable *, changes, NULL, ast_variables_destroy);
553        struct ast_variable *field;
554        int res = AST_TEST_PASS;
555
556        switch (cmd) {
557        case TEST_INIT:
558                info->name = "object_diff";
559                info->category = "/main/sorcery/";
560                info->summary = "sorcery object diff unit test";
561                info->description =
562                        "Test object diffing in sorcery";
563                return AST_TEST_NOT_RUN;
564        case TEST_EXECUTE:
565                break;
566        }
567
568        if (!(sorcery = alloc_and_initialize_sorcery())) {
569                ast_test_status_update(test, "Failed to open sorcery structure\n");
570                return AST_TEST_FAIL;
571        }
572
573        if (!(obj1 = ast_sorcery_alloc(sorcery, "test", "blah"))) {
574                ast_test_status_update(test, "Failed to allocate a known object type\n");
575                return AST_TEST_FAIL;
576        }
577
578        obj1->bob = 99;
579        obj1->joe = 55;
580
581        if (!(obj2 = ast_sorcery_alloc(sorcery, "test", "blah2"))) {
582                ast_test_status_update(test, "Failed to allocate a second known object type\n");
583                return AST_TEST_FAIL;
584        }
585
586        obj2->bob = 99;
587        obj2->joe = 42;
588
589        if (ast_sorcery_diff(sorcery, obj1, obj2, &changes)) {
590                ast_test_status_update(test, "Failed to diff obj1 and obj2\n");
591        } else if (!changes) {
592                ast_test_status_update(test, "Failed to produce a diff of two objects, despite there being differences\n");
593                return AST_TEST_FAIL;
594        }
595
596        for (field = changes; field; field = field->next) {
597                if (!strcmp(field->name, "joe")) {
598                        if (strcmp(field->value, "42")) {
599                                ast_test_status_update(test, "Object diff produced unexpected value '%s' for joe\n", field->value);
600                                res = AST_TEST_FAIL;
601                        }
602                } else {
603                        ast_test_status_update(test, "Object diff produced unexpected field '%s'\n", field->name);
604                        res = AST_TEST_FAIL;
605                }
606        }
607
608        return res;
609 }
610
611 AST_TEST_DEFINE(objectset_create)
612 {
613         int res = AST_TEST_PASS;
614         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
615         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
616         RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
617         struct ast_variable *field;
618
619         switch (cmd) {
620         case TEST_INIT:
621                 info->name = "objectset_create";
622                 info->category = "/main/sorcery/";
623                 info->summary = "sorcery object set creation unit test";
624                 info->description =
625                         "Test object set creation in sorcery";
626                 return AST_TEST_NOT_RUN;
627         case TEST_EXECUTE:
628                 break;
629         }
630
631         if (!(sorcery = alloc_and_initialize_sorcery())) {
632                 ast_test_status_update(test, "Failed to open sorcery structure\n");
633                 return AST_TEST_FAIL;
634         }
635
636         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
637                 ast_test_status_update(test, "Failed to allocate a known object type\n");
638                 return AST_TEST_FAIL;
639         }
640
641         if (!(objset = ast_sorcery_objectset_create(sorcery, obj))) {
642                 ast_test_status_update(test, "Failed to create an object set for a known sane object\n");
643                 return AST_TEST_FAIL;
644         }
645
646         for (field = objset; field; field = field->next) {
647                 if (!strcmp(field->name, "bob")) {
648                         if (strcmp(field->value, "5")) {
649                                 ast_test_status_update(test, "Object set failed to create proper value for 'bob'\n");
650                                 res = AST_TEST_FAIL;
651                         }
652                 } else if (!strcmp(field->name, "joe")) {
653                         if (strcmp(field->value, "10")) {
654                                 ast_test_status_update(test, "Object set failed to create proper value for 'joe'\n");
655                                 res = AST_TEST_FAIL;
656                         }
657                 } else {
658                         ast_test_status_update(test, "Object set created field '%s' which is unknown\n", field->name);
659                         res = AST_TEST_FAIL;
660                 }
661         }
662
663         return res;
664 }
665
666 AST_TEST_DEFINE(objectset_apply)
667 {
668         int res = AST_TEST_PASS;
669         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
670         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
671         RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
672
673         switch (cmd) {
674         case TEST_INIT:
675                 info->name = "objectset_apply";
676                 info->category = "/main/sorcery/";
677                 info->summary = "sorcery object apply unit test";
678                 info->description =
679                         "Test object set applying in sorcery";
680                 return AST_TEST_NOT_RUN;
681         case TEST_EXECUTE:
682                 break;
683         }
684
685         if (!(sorcery = alloc_and_initialize_sorcery())) {
686                 ast_test_status_update(test, "Failed to open sorcery structure\n");
687                 return AST_TEST_FAIL;
688         }
689
690         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
691                 ast_test_status_update(test, "Failed to allocate a known object type\n");
692                 return AST_TEST_FAIL;
693         }
694
695         if (!(objset = ast_variable_new("joe", "25", ""))) {
696                 ast_test_status_update(test, "Failed to create an object set, test could not occur\n");
697                 res = AST_TEST_FAIL;
698         } else if (ast_sorcery_objectset_apply(sorcery, obj, objset)) {
699                 ast_test_status_update(test, "Failed to apply valid object set to object\n");
700                 res = AST_TEST_FAIL;
701         } else if (obj->joe != 25) {
702                 ast_test_status_update(test, "Object set was not actually applied to object despite it returning success\n");
703                 res = AST_TEST_FAIL;
704         }
705
706         return res;
707 }
708
709 AST_TEST_DEFINE(objectset_apply_handler)
710 {
711         int res = AST_TEST_PASS;
712         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
713         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
714         RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
715
716         switch (cmd) {
717         case TEST_INIT:
718                 info->name = "objectset_apply_handler";
719                 info->category = "/main/sorcery/";
720                 info->summary = "sorcery object apply handler unit test";
721                 info->description =
722                         "Test object set apply handler call in sorcery";
723                 return AST_TEST_NOT_RUN;
724         case TEST_EXECUTE:
725                 break;
726         }
727
728         if (!(sorcery = ast_sorcery_open())) {
729                 ast_test_status_update(test, "Failed to open sorcery structure\n");
730                 return AST_TEST_FAIL;
731         }
732
733         if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) ||
734             ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, test_apply_handler)) {
735                 ast_test_status_update(test, "Failed to register 'test' object type\n");
736                 return AST_TEST_FAIL;
737         }
738
739         ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
740         ast_sorcery_object_field_register(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
741
742         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
743                 ast_test_status_update(test, "Failed to allocate a known object type\n");
744                 return AST_TEST_FAIL;
745         }
746
747         apply_handler_called = 0;
748
749         if (!(objset = ast_variable_new("joe", "25", ""))) {
750                 ast_test_status_update(test, "Failed to create an object set, test could not occur\n");
751                 res = AST_TEST_FAIL;
752         } else if (ast_sorcery_objectset_apply(sorcery, obj, objset)) {
753                 ast_test_status_update(test, "Failed to apply valid object set to object\n");
754                 res = AST_TEST_FAIL;
755         } else if (!apply_handler_called) {
756                 ast_test_status_update(test, "Apply handler was not called when it should have been\n");
757                 res = AST_TEST_FAIL;
758         }
759
760         return res;
761 }
762
763 AST_TEST_DEFINE(objectset_apply_invalid)
764 {
765         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
766         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
767         RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
768
769         switch (cmd) {
770         case TEST_INIT:
771                 info->name = "objectset_apply_invalid";
772                 info->category = "/main/sorcery/";
773                 info->summary = "sorcery object invalid apply unit test";
774                 info->description =
775                         "Test object set applying of an invalid set in sorcery";
776                 return AST_TEST_NOT_RUN;
777         case TEST_EXECUTE:
778                 break;
779         }
780
781         if (!(sorcery = alloc_and_initialize_sorcery())) {
782                 ast_test_status_update(test, "Failed to open sorcery structure\n");
783                 return AST_TEST_FAIL;
784         }
785
786         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
787                 ast_test_status_update(test, "Failed to allocate a known object type\n");
788                 return AST_TEST_FAIL;
789         }
790
791         if (!(objset = ast_variable_new("fred", "99", ""))) {
792                 ast_test_status_update(test, "Failed to create an object set, test could not occur\n");
793                 return AST_TEST_FAIL;
794         } else if (!ast_sorcery_objectset_apply(sorcery, obj, objset)) {
795                 ast_test_status_update(test, "Successfully applied an invalid object set\n");
796                 return AST_TEST_FAIL;
797         } else if ((obj->bob != 5) || (obj->joe != 10)) {
798                 ast_test_status_update(test, "Object set modified object fields when it should not have\n");
799                 return AST_TEST_FAIL;
800         }
801
802         return AST_TEST_PASS;
803 }
804
805 AST_TEST_DEFINE(objectset_transform)
806 {
807         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
808         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
809         RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
810
811         switch (cmd) {
812         case TEST_INIT:
813                 info->name = "objectset_transform";
814                 info->category = "/main/sorcery/";
815                 info->summary = "sorcery object set transformation unit test";
816                 info->description =
817                         "Test object set transformation in sorcery";
818                 return AST_TEST_NOT_RUN;
819         case TEST_EXECUTE:
820                 break;
821         }
822
823         if (!(sorcery = ast_sorcery_open())) {
824                 ast_test_status_update(test, "Failed to open sorcery structure\n");
825                 return AST_TEST_FAIL;
826         }
827
828         if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
829                 ast_test_status_update(test, "Failed to set a known wizard as a default\n");
830                 return AST_TEST_FAIL;
831         }
832
833         if (ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, test_sorcery_transform, NULL)) {
834                 ast_test_status_update(test, "Failed to register object type\n");
835                 return AST_TEST_FAIL;
836         }
837
838         ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
839         ast_sorcery_object_field_register(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
840
841         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
842                 ast_test_status_update(test, "Failed to allocate a known object type\n");
843                 return AST_TEST_FAIL;
844         }
845
846         if (!(objset = ast_sorcery_objectset_create(sorcery, obj))) {
847                 ast_test_status_update(test, "Failed to create an object set for a known sane object\n");
848                 return AST_TEST_FAIL;
849         }
850
851         if (ast_sorcery_objectset_apply(sorcery, obj, objset)) {
852                 ast_test_status_update(test, "Failed to apply properly created object set against object\n");
853                 return AST_TEST_FAIL;
854         }
855
856         if (obj->bob != 5) {
857                 ast_test_status_update(test, "Application of object set produced incorrect value on 'bob'\n");
858                 return AST_TEST_FAIL;
859         } else if (obj->joe == 10) {
860                 ast_test_status_update(test, "Transformation callback did not change value of 'joe' from provided value\n");
861                 return AST_TEST_FAIL;
862         } else if (obj->joe != 5000) {
863                 ast_test_status_update(test, "Value of 'joe' differs from default AND from transformation value\n");
864                 return AST_TEST_FAIL;
865         }
866
867         return AST_TEST_PASS;
868 }
869
870 AST_TEST_DEFINE(changeset_create)
871 {
872         int res = AST_TEST_PASS;
873         RAII_VAR(struct ast_variable *, original, NULL, ast_variables_destroy);
874         RAII_VAR(struct ast_variable *, modified, NULL, ast_variables_destroy);
875         RAII_VAR(struct ast_variable *, changes, NULL, ast_variables_destroy);
876         struct ast_variable *tmp;
877
878         switch (cmd) {
879         case TEST_INIT:
880                 info->name = "changeset_create";
881                 info->category = "/main/sorcery/";
882                 info->summary = "sorcery changeset creation unit test";
883                 info->description =
884                         "Test changeset creation in sorcery";
885                 return AST_TEST_NOT_RUN;
886         case TEST_EXECUTE:
887                 break;
888         }
889
890         if (!(tmp = ast_variable_new("bananas", "purple", ""))) {
891                 ast_test_status_update(test, "Failed to create first field for original objectset\n");
892                 return AST_TEST_FAIL;
893         }
894         tmp->next = original;
895         original = tmp;
896
897         if (!(tmp = ast_variable_new("apples", "orange", ""))) {
898                 ast_test_status_update(test, "Failed to create second field for original objectset\n");
899                 return AST_TEST_FAIL;
900         }
901         tmp->next = original;
902         original = tmp;
903
904         if (!(tmp = ast_variable_new("bananas", "green", ""))) {
905                 ast_test_status_update(test, "Failed to create first field for modified objectset\n");
906                 return AST_TEST_FAIL;
907         }
908         tmp->next = modified;
909         modified = tmp;
910
911         if (!(tmp = ast_variable_new("apples", "orange", ""))) {
912                 ast_test_status_update(test, "Failed to create second field for modified objectset\n");
913                 return AST_TEST_FAIL;
914         }
915         tmp->next = modified;
916         modified = tmp;
917
918         if (ast_sorcery_changeset_create(original, modified, &changes)) {
919                 ast_test_status_update(test, "Failed to create a changeset due to an error\n");
920                 return AST_TEST_FAIL;
921         } else if (!changes) {
922                 ast_test_status_update(test, "Failed to produce a changeset when there should be one\n");
923                 return AST_TEST_FAIL;
924         }
925
926         for (tmp = changes; tmp; tmp = tmp->next) {
927                 if (!strcmp(tmp->name, "bananas")) {
928                         if (strcmp(tmp->value, "green")) {
929                                 ast_test_status_update(test, "Changeset produced had unexpected value '%s' for bananas\n", tmp->value);
930                                 res = AST_TEST_FAIL;
931                         }
932                 } else {
933                         ast_test_status_update(test, "Changeset produced had unexpected field '%s'\n", tmp->name);
934                         res = AST_TEST_FAIL;
935                 }
936         }
937
938         return res;
939 }
940
941 AST_TEST_DEFINE(changeset_create_unchanged)
942 {
943         RAII_VAR(struct ast_variable *, original, NULL, ast_variables_destroy);
944         RAII_VAR(struct ast_variable *, changes, NULL, ast_variables_destroy);
945         RAII_VAR(struct ast_variable *, same, NULL, ast_variables_destroy);
946         struct ast_variable *tmp;
947
948         switch (cmd) {
949         case TEST_INIT:
950                 info->name = "changeset_create_unchanged";
951                 info->category = "/main/sorcery/";
952                 info->summary = "sorcery changeset creation unit test when no changes exist";
953                 info->description =
954                         "Test changeset creation in sorcery when no changes actually exist";
955                 return AST_TEST_NOT_RUN;
956         case TEST_EXECUTE:
957                 break;
958         }
959
960         if (!(tmp = ast_variable_new("bananas", "purple", ""))) {
961                 ast_test_status_update(test, "Failed to create first field for original objectset\n");
962                 return AST_TEST_FAIL;
963         }
964         tmp->next = original;
965         original = tmp;
966
967         if (!(tmp = ast_variable_new("apples", "orange", ""))) {
968                 ast_test_status_update(test, "Failed to create second field for original objectset\n");
969                 return AST_TEST_FAIL;
970         }
971         tmp->next = original;
972         original = tmp;
973
974         if (ast_sorcery_changeset_create(original, original, &changes)) {
975                 ast_test_status_update(test, "Failed to create a changeset due to an error\n");
976                 return AST_TEST_FAIL;
977         } else if (changes) {
978                 ast_test_status_update(test, "Created a changeset when no changes actually exist\n");
979                 return AST_TEST_FAIL;
980         }
981
982         if (!(tmp = ast_variable_new("bananas", "purple", ""))) {
983                 ast_test_status_update(test, "Failed to create first field for same objectset\n");
984                 return AST_TEST_FAIL;
985         }
986         tmp->next = same;
987         same = tmp;
988
989         if (!(tmp = ast_variable_new("apples", "orange", ""))) {
990                 ast_test_status_update(test, "Failed to create second field for same objectset\n");
991                 return AST_TEST_FAIL;
992         }
993         tmp->next = same;
994         same = tmp;
995
996         if (ast_sorcery_changeset_create(original, same, &changes)) {
997                 ast_test_status_update(test, "Failed to create a changeset due to an error\n");
998                 return AST_TEST_FAIL;
999         } else if (changes) {
1000                 ast_test_status_update(test, "Created a changeset between two different objectsets when no changes actually exist\n");
1001                 return AST_TEST_FAIL;
1002         }
1003
1004         return AST_TEST_PASS;
1005 }
1006
1007 AST_TEST_DEFINE(object_create)
1008 {
1009         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1010         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1011
1012         switch (cmd) {
1013         case TEST_INIT:
1014                 info->name = "object_create";
1015                 info->category = "/main/sorcery/";
1016                 info->summary = "sorcery object creation unit test";
1017                 info->description =
1018                         "Test object creation in sorcery";
1019                 return AST_TEST_NOT_RUN;
1020         case TEST_EXECUTE:
1021                 break;
1022         }
1023
1024         if (!(sorcery = alloc_and_initialize_sorcery())) {
1025                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1026                 return AST_TEST_FAIL;
1027         }
1028
1029         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1030                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1031                 return AST_TEST_FAIL;
1032         }
1033
1034         if (ast_sorcery_create(sorcery, obj)) {
1035                 ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
1036                 return AST_TEST_FAIL;
1037         }
1038
1039         return AST_TEST_PASS;
1040 }
1041
1042 AST_TEST_DEFINE(object_retrieve_id)
1043 {
1044         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1045         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1046
1047         switch (cmd) {
1048         case TEST_INIT:
1049                 info->name = "object_retrieve_id";
1050                 info->category = "/main/sorcery/";
1051                 info->summary = "sorcery object retrieval using id unit test";
1052                 info->description =
1053                         "Test object retrieval using id in sorcery";
1054                 return AST_TEST_NOT_RUN;
1055         case TEST_EXECUTE:
1056                 break;
1057         }
1058
1059         if (!(sorcery = alloc_and_initialize_sorcery())) {
1060                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1061                 return AST_TEST_FAIL;
1062         }
1063
1064         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1065                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1066                 return AST_TEST_FAIL;
1067         }
1068
1069         if (ast_sorcery_create(sorcery, obj)) {
1070                 ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
1071                 return AST_TEST_FAIL;
1072         }
1073
1074         ao2_cleanup(obj);
1075
1076         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah2"))) {
1077                 ast_test_status_update(test, "Failed to allocate second instance of a known object type\n");
1078                 return AST_TEST_FAIL;
1079         }
1080
1081         if (ast_sorcery_create(sorcery, obj)) {
1082                 ast_test_status_update(test, "Failed to create second object using in-memory wizard\n");
1083                 return AST_TEST_FAIL;
1084         }
1085
1086         ao2_cleanup(obj);
1087
1088         if (!(obj = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
1089                 ast_test_status_update(test, "Failed to retrieve properly created object using id of 'blah'\n");
1090                 return AST_TEST_FAIL;
1091         } else if (strcmp(ast_sorcery_object_get_id(obj), "blah")) {
1092                 ast_test_status_update(test, "Retrieved object does not have correct id\n");
1093                 return AST_TEST_FAIL;
1094         }
1095
1096         return AST_TEST_PASS;
1097 }
1098
1099 AST_TEST_DEFINE(object_retrieve_field)
1100 {
1101         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1102         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1103         RAII_VAR(struct ast_variable *, fields, ast_variable_new("joe", "42", ""), ast_variables_destroy);
1104
1105         switch (cmd) {
1106         case TEST_INIT:
1107                 info->name = "object_retrieve_field";
1108                 info->category = "/main/sorcery/";
1109                 info->summary = "sorcery object retrieval using a specific field unit test";
1110                 info->description =
1111                         "Test object retrieval using a specific field in sorcery";
1112                 return AST_TEST_NOT_RUN;
1113         case TEST_EXECUTE:
1114                 break;
1115         }
1116
1117         if (!fields) {
1118                 ast_test_status_update(test, "Failed to create fields for object retrieval attempt\n");
1119                 return AST_TEST_FAIL;
1120         }
1121
1122         if (!(sorcery = alloc_and_initialize_sorcery())) {
1123                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1124                 return AST_TEST_FAIL;
1125         }
1126
1127         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1128                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1129                 return AST_TEST_FAIL;
1130         }
1131
1132         obj->joe = 42;
1133
1134         if (ast_sorcery_create(sorcery, obj)) {
1135                 ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
1136                 return AST_TEST_FAIL;
1137         }
1138
1139         ao2_cleanup(obj);
1140
1141         if (!(obj = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_DEFAULT, fields))) {
1142                 ast_test_status_update(test, "Failed to retrieve properly created object using 'joe' field\n");
1143                 return AST_TEST_FAIL;
1144         }
1145
1146         ao2_cleanup(obj);
1147         ast_variables_destroy(fields);
1148
1149         if (!(fields = ast_variable_new("joe", "49", ""))) {
1150                 ast_test_status_update(test, "Failed to create fields for object retrieval attempt\n");
1151                 return AST_TEST_FAIL;
1152         }
1153
1154         if ((obj = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_DEFAULT, fields))) {
1155                 ast_test_status_update(test, "Retrieved an object using a field with an in-correct value... that should not happen\n");
1156                 return AST_TEST_FAIL;
1157         }
1158
1159         return AST_TEST_PASS;
1160 }
1161
1162 AST_TEST_DEFINE(object_retrieve_multiple_all)
1163 {
1164         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1165         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1166         RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
1167
1168         switch (cmd) {
1169         case TEST_INIT:
1170                 info->name = "object_retrieve_multiple_all";
1171                 info->category = "/main/sorcery/";
1172                 info->summary = "sorcery multiple object retrieval unit test";
1173                 info->description =
1174                         "Test multiple object retrieval in sorcery";
1175                 return AST_TEST_NOT_RUN;
1176         case TEST_EXECUTE:
1177                 break;
1178         }
1179
1180         if (!(sorcery = alloc_and_initialize_sorcery())) {
1181                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1182                 return AST_TEST_FAIL;
1183         }
1184
1185         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1186                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1187                 return AST_TEST_FAIL;
1188         }
1189
1190         if (ast_sorcery_create(sorcery, obj)) {
1191                 ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
1192                 return AST_TEST_FAIL;
1193         }
1194
1195         ao2_cleanup(obj);
1196
1197         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah2"))) {
1198                 ast_test_status_update(test, "Failed to allocate second instance of a known object type\n");
1199                 return AST_TEST_FAIL;
1200         }
1201
1202         if (ast_sorcery_create(sorcery, obj)) {
1203                 ast_test_status_update(test, "Failed to create second object using in-memory wizard\n");
1204                 return AST_TEST_FAIL;
1205         }
1206
1207         if (!(objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL))) {
1208                 ast_test_status_update(test, "Failed to retrieve a container of all objects\n");
1209                 return AST_TEST_FAIL;
1210         } else if (ao2_container_count(objects) != 2) {
1211                 ast_test_status_update(test, "Received a container with no objects in it when there should be some\n");
1212                 return AST_TEST_FAIL;
1213         }
1214
1215         return AST_TEST_PASS;
1216 }
1217
1218 AST_TEST_DEFINE(object_retrieve_multiple_field)
1219 {
1220         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1221         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1222         RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
1223         RAII_VAR(struct ast_variable *, fields, ast_variable_new("joe", "6", ""), ast_variables_destroy);
1224
1225         switch (cmd) {
1226         case TEST_INIT:
1227                 info->name = "object_retrieve_multiple_field";
1228                 info->category = "/main/sorcery/";
1229                 info->summary = "sorcery multiple object retrieval unit test";
1230                 info->description =
1231                         "Test multiple object retrieval in sorcery using fields";
1232                 return AST_TEST_NOT_RUN;
1233         case TEST_EXECUTE:
1234                 break;
1235         }
1236
1237         if (!fields) {
1238                 ast_test_status_update(test, "Failed to create fields for multiple retrieve\n");
1239                 return AST_TEST_FAIL;
1240         }
1241
1242         if (!(sorcery = alloc_and_initialize_sorcery())) {
1243                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1244                 return AST_TEST_FAIL;
1245         }
1246
1247         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1248                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1249                 return AST_TEST_FAIL;
1250         }
1251
1252         obj->joe = 6;
1253
1254         if (ast_sorcery_create(sorcery, obj)) {
1255                 ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
1256                 return AST_TEST_FAIL;
1257         }
1258
1259         if (!(objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE, fields))) {
1260                 ast_test_status_update(test, "Failed to retrieve a container of all objects\n");
1261                 return AST_TEST_FAIL;
1262         } else if (ao2_container_count(objects) != 1) {
1263                 ast_test_status_update(test, "Received a container with no objects in it when there should be some\n");
1264                 return AST_TEST_FAIL;
1265         }
1266
1267         ao2_cleanup(objects);
1268         ast_variables_destroy(fields);
1269
1270         if (!(fields = ast_variable_new("joe", "7", ""))) {
1271                 ast_test_status_update(test, "Failed to create fields for multiple retrieval\n");
1272                 return AST_TEST_FAIL;
1273         } else if (!(objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE, fields))) {
1274                 ast_test_status_update(test, "Failed to retrieve an empty container when retrieving multiple\n");
1275                 return AST_TEST_FAIL;
1276         } else if (ao2_container_count(objects)) {
1277                 ast_test_status_update(test, "Received a container with objects when there should be none in it\n");
1278                 return AST_TEST_FAIL;
1279         }
1280
1281         return AST_TEST_PASS;
1282 }
1283
1284 AST_TEST_DEFINE(object_update)
1285 {
1286         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1287         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1288         RAII_VAR(struct test_sorcery_object *, obj2, NULL, ao2_cleanup);
1289
1290         switch (cmd) {
1291         case TEST_INIT:
1292                 info->name = "object_update";
1293                 info->category = "/main/sorcery/";
1294                 info->summary = "sorcery object update unit test";
1295                 info->description =
1296                         "Test object updating in sorcery";
1297                 return AST_TEST_NOT_RUN;
1298         case TEST_EXECUTE:
1299                 break;
1300         }
1301
1302         if (!(sorcery = alloc_and_initialize_sorcery())) {
1303                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1304                 return AST_TEST_FAIL;
1305         }
1306
1307         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1308                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1309                 return AST_TEST_FAIL;
1310         }
1311
1312         if (ast_sorcery_create(sorcery, obj)) {
1313                 ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
1314                 return AST_TEST_FAIL;
1315         }
1316
1317         if (!(obj2 = ast_sorcery_copy(sorcery, obj))) {
1318                 ast_test_status_update(test, "Failed to allocate a known object type for updating\n");
1319                 return AST_TEST_FAIL;
1320         }
1321
1322         ao2_cleanup(obj);
1323
1324         if (ast_sorcery_update(sorcery, obj2)) {
1325                 ast_test_status_update(test, "Failed to update sorcery with new object\n");
1326                 return AST_TEST_FAIL;
1327         }
1328
1329         if (!(obj = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
1330                 ast_test_status_update(test, "Failed to retrieve properly updated object\n");
1331                 return AST_TEST_FAIL;
1332         } else if (obj != obj2) {
1333                 ast_test_status_update(test, "Object retrieved is not the updated object\n");
1334                 return AST_TEST_FAIL;
1335         }
1336
1337         return AST_TEST_PASS;
1338 }
1339
1340 AST_TEST_DEFINE(object_update_uncreated)
1341 {
1342         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1343         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1344
1345         switch (cmd) {
1346         case TEST_INIT:
1347                 info->name = "object_update_uncreated";
1348                 info->category = "/main/sorcery/";
1349                 info->summary = "sorcery object update unit test";
1350                 info->description =
1351                         "Test updating of an uncreated object in sorcery";
1352                 return AST_TEST_NOT_RUN;
1353         case TEST_EXECUTE:
1354                 break;
1355         }
1356
1357         if (!(sorcery = alloc_and_initialize_sorcery())) {
1358                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1359                 return AST_TEST_FAIL;
1360         }
1361
1362         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1363                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1364                 return AST_TEST_FAIL;
1365         }
1366
1367         if (!ast_sorcery_update(sorcery, obj)) {
1368                 ast_test_status_update(test, "Successfully updated an object which has not been created yet\n");
1369                 return AST_TEST_FAIL;
1370         }
1371
1372         return AST_TEST_PASS;
1373 }
1374
1375 AST_TEST_DEFINE(object_delete)
1376 {
1377         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1378         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1379
1380         switch (cmd) {
1381         case TEST_INIT:
1382                 info->name = "object_delete";
1383                 info->category = "/main/sorcery/";
1384                 info->summary = "sorcery object deletion unit test";
1385                 info->description =
1386                         "Test object deletion in sorcery";
1387                 return AST_TEST_NOT_RUN;
1388         case TEST_EXECUTE:
1389                 break;
1390         }
1391
1392         if (!(sorcery = alloc_and_initialize_sorcery())) {
1393                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1394                 return AST_TEST_FAIL;
1395         }
1396
1397         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1398                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1399                 return AST_TEST_FAIL;
1400         }
1401
1402         if (ast_sorcery_create(sorcery, obj)) {
1403                 ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
1404                 return AST_TEST_FAIL;
1405         }
1406
1407         if (ast_sorcery_delete(sorcery, obj)) {
1408                 ast_test_status_update(test, "Failed to delete object using in-memory wizard\n");
1409                 return AST_TEST_FAIL;
1410         }
1411
1412         ao2_cleanup(obj);
1413
1414         if ((obj = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
1415                 ast_test_status_update(test, "Retrieved deleted object that should not be there\n");
1416                 return AST_TEST_FAIL;
1417         }
1418
1419         return AST_TEST_PASS;
1420 }
1421
1422 AST_TEST_DEFINE(object_delete_uncreated)
1423 {
1424         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1425         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1426
1427         switch (cmd) {
1428         case TEST_INIT:
1429                 info->name = "object_delete_uncreated";
1430                 info->category = "/main/sorcery/";
1431                 info->summary = "sorcery object deletion unit test";
1432                 info->description =
1433                         "Test object deletion of an uncreated object in sorcery";
1434                 return AST_TEST_NOT_RUN;
1435         case TEST_EXECUTE:
1436                 break;
1437         }
1438
1439         if (!(sorcery = alloc_and_initialize_sorcery())) {
1440                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1441                 return AST_TEST_FAIL;
1442         }
1443
1444         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1445                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1446                 return AST_TEST_FAIL;
1447         }
1448
1449         if (!ast_sorcery_delete(sorcery, obj)) {
1450                 ast_test_status_update(test, "Successfully deleted an object which was never created\n");
1451                 return AST_TEST_FAIL;
1452         }
1453
1454         return AST_TEST_PASS;
1455 }
1456
1457 AST_TEST_DEFINE(caching_wizard_behavior)
1458 {
1459         struct ast_flags flags = { CONFIG_FLAG_NOCACHE };
1460         struct ast_config *config;
1461         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1462         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1463         RAII_VAR(struct test_sorcery_object *, obj2, NULL, ao2_cleanup);
1464         int res = AST_TEST_FAIL;
1465
1466         switch (cmd) {
1467         case TEST_INIT:
1468                 info->name = "caching_wizard_behavior";
1469                 info->category = "/main/sorcery/";
1470                 info->summary = "sorcery caching wizard behavior unit test";
1471                 info->description =
1472                         "Test internal behavior of caching wizards";
1473                 return AST_TEST_NOT_RUN;
1474         case TEST_EXECUTE:
1475                 break;
1476         }
1477
1478         if (!(config = ast_config_load2("sorcery.conf", "test_sorcery_cache", flags))) {
1479                 ast_test_status_update(test, "Sorcery configuration file not present - skipping caching_wizard_behavior test\n");
1480                 return AST_TEST_NOT_RUN;
1481         }
1482
1483         if (!ast_category_get(config, "test_sorcery_cache")) {
1484                 ast_test_status_update(test, "Sorcery configuration file does not contain 'test_sorcery_cache' section\n");
1485                 ast_config_destroy(config);
1486                 return AST_TEST_NOT_RUN;
1487         }
1488
1489         ast_config_destroy(config);
1490
1491         if (ast_sorcery_wizard_register(&test_wizard)) {
1492                 ast_test_status_update(test, "Failed to register a perfectly valid sorcery wizard\n");
1493                 return AST_TEST_FAIL;
1494         }
1495
1496         if (!(sorcery = ast_sorcery_open())) {
1497                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1498                 goto end;
1499         }
1500
1501         if (ast_sorcery_apply_config(sorcery, "test_sorcery_cache")) {
1502                 ast_test_status_update(test, "Failed to apply configured object mappings\n");
1503                 goto end;
1504         }
1505
1506         if (ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
1507                 ast_test_status_update(test, "Failed to register object type\n");
1508                 goto end;
1509         }
1510
1511         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1512                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1513                 goto end;
1514         }
1515
1516         if (ast_sorcery_create(sorcery, obj)) {
1517                 ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
1518                 goto end;
1519         }
1520
1521         ao2_cleanup(obj);
1522
1523         if (!(obj = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
1524                 ast_test_status_update(test, "Failed to retrieve just created object\n");
1525                 goto end;
1526         } else if (!cache.created) {
1527                 ast_test_status_update(test, "Caching wizard was not told to cache just created object\n");
1528                 goto end;
1529         } else if (!(obj2 = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
1530                 ast_test_status_update(test, "Failed to retrieve just cached object\n");
1531                 goto end;
1532         } else if (obj == obj2) {
1533                 ast_test_status_update(test, "Returned object is *NOT* a cached object\n");
1534                 goto end;
1535         } else if (ast_sorcery_update(sorcery, obj)) {
1536                 ast_test_status_update(test, "Failed to update a known stored object\n");
1537                 goto end;
1538         } else if (!cache.updated) {
1539                 ast_test_status_update(test, "Caching wizard was not told to update object\n");
1540                 goto end;
1541         } else if (ast_sorcery_delete(sorcery, obj)) {
1542                 ast_test_status_update(test, "Failed to delete a known stored object\n");
1543                 goto end;
1544         } else if (!cache.deleted) {
1545                 ast_test_status_update(test, "Caching wizard was not told to delete object\n");
1546                 goto end;
1547         }
1548
1549         ao2_cleanup(obj2);
1550
1551         if ((obj2 = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
1552                 ast_test_status_update(test, "Retrieved an object that should have been deleted\n");
1553                 goto end;
1554         }
1555
1556         res = AST_TEST_PASS;
1557
1558 end:
1559         ast_sorcery_unref(sorcery);
1560         sorcery = NULL;
1561
1562         if (ast_sorcery_wizard_unregister(&test_wizard)) {
1563                 ast_test_status_update(test, "Failed to unregister test sorcery wizard\n");
1564                 return AST_TEST_FAIL;
1565         }
1566
1567         return res;
1568 }
1569
1570 AST_TEST_DEFINE(configuration_file_wizard)
1571 {
1572         struct ast_flags flags = { CONFIG_FLAG_NOCACHE };
1573         struct ast_config *config;
1574         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1575         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1576
1577         switch (cmd) {
1578         case TEST_INIT:
1579                 info->name = "configuration_file_wizard";
1580                 info->category = "/main/sorcery/";
1581                 info->summary = "sorcery configuration file wizard unit test";
1582                 info->description =
1583                         "Test the configuration file wizard in sorcery";
1584                 return AST_TEST_NOT_RUN;
1585         case TEST_EXECUTE:
1586                 break;
1587         }
1588
1589         if (!(config = ast_config_load2("test_sorcery.conf", "test_sorcery", flags))) {
1590                 ast_test_status_update(test, "Test sorcery configuration file wizard file not present - skipping configuration_file_wizard test\n");
1591                 return AST_TEST_NOT_RUN;
1592         }
1593
1594         ast_config_destroy(config);
1595
1596         if (!(sorcery = ast_sorcery_open())) {
1597                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1598                 return AST_TEST_FAIL;
1599         }
1600
1601         if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf")) {
1602                 ast_test_status_update(test, "Could not set a default wizard of the 'config' type, so skipping since it may not be loaded\n");
1603                 return AST_TEST_NOT_RUN;
1604         }
1605
1606         if (ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
1607                 ast_test_status_update(test, "Failed to register object type\n");
1608                 return AST_TEST_FAIL;
1609         }
1610
1611         ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
1612         ast_sorcery_object_field_register(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
1613
1614         ast_sorcery_load(sorcery);
1615
1616         if ((obj = ast_sorcery_retrieve_by_id(sorcery, "test", "hey2"))) {
1617                 ast_test_status_update(test, "Retrieved object which has an unknown field\n");
1618                 return AST_TEST_FAIL;
1619         } else if (!(obj = ast_sorcery_retrieve_by_id(sorcery, "test", "hey"))) {
1620                 ast_test_status_update(test, "Failed to retrieve a known object that has been configured in the configuration file\n");
1621                 return AST_TEST_FAIL;
1622         } else if (obj->bob != 98) {
1623                 ast_test_status_update(test, "Value of 'bob' on object is not what is configured in configuration file\n");
1624                 return AST_TEST_FAIL;
1625         } else if (obj->joe != 41) {
1626                 ast_test_status_update(test, "Value of 'joe' on object is not what is configured in configuration file\n");
1627                 return AST_TEST_FAIL;
1628         }
1629
1630         return AST_TEST_PASS;
1631 }
1632
1633 AST_TEST_DEFINE(configuration_file_wizard_with_file_integrity)
1634 {
1635         struct ast_flags flags = { CONFIG_FLAG_NOCACHE };
1636         struct ast_config *config;
1637         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1638         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1639
1640         switch (cmd) {
1641         case TEST_INIT:
1642                 info->name = "configuration_file_wizard_with_file_integrity";
1643                 info->category = "/main/sorcery/";
1644                 info->summary = "sorcery configuration file wizard file integrity unit test";
1645                 info->description =
1646                         "Test the configuration file wizard with file integrity turned on in sorcery";
1647                 return AST_TEST_NOT_RUN;
1648         case TEST_EXECUTE:
1649                 break;
1650         }
1651
1652         if (!(config = ast_config_load2("test_sorcery.conf", "test_sorcery", flags))) {
1653                 ast_test_status_update(test, "Test sorcery configuration file wizard file not present - skipping configuration_file_wizard_with_file_integrity test\n");
1654                 return AST_TEST_NOT_RUN;
1655         }
1656
1657         ast_config_destroy(config);
1658
1659         if (!(sorcery = ast_sorcery_open())) {
1660                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1661                 return AST_TEST_FAIL;
1662         }
1663
1664         if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf,integrity=file")) {
1665                 ast_test_status_update(test, "Could not set a default wizard of the 'config' type, so skipping since it may not be loaded\n");
1666                 return AST_TEST_NOT_RUN;
1667         }
1668
1669         if (ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
1670                 ast_test_status_update(test, "Failed to register object type\n");
1671                 return AST_TEST_FAIL;
1672         }
1673
1674         ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
1675         ast_sorcery_object_field_register(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
1676
1677         ast_sorcery_load(sorcery);
1678
1679         if ((obj = ast_sorcery_retrieve_by_id(sorcery, "test", "hey"))) {
1680                 ast_test_status_update(test, "Retrieved object which has an unknown field\n");
1681                 return AST_TEST_FAIL;
1682         }
1683
1684         return AST_TEST_PASS;
1685 }
1686
1687 AST_TEST_DEFINE(configuration_file_wizard_with_criteria)
1688 {
1689         struct ast_flags flags = { CONFIG_FLAG_NOCACHE };
1690         struct ast_config *config;
1691         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1692         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1693
1694         switch (cmd) {
1695         case TEST_INIT:
1696                 info->name = "configuration_file_wizard_with_criteria";
1697                 info->category = "/main/sorcery/";
1698                 info->summary = "sorcery configuration file wizard with criteria unit test";
1699                 info->description =
1700                         "Test the configuration file wizard with criteria matching in sorcery";
1701                 return AST_TEST_NOT_RUN;
1702         case TEST_EXECUTE:
1703                 break;
1704         }
1705
1706         if (!(config = ast_config_load2("test_sorcery.conf", "test_sorcery", flags))) {
1707                 ast_test_status_update(test, "Test sorcery configuration file wizard file not present - skipping configuration_file_wizard_with_criteria test\n");
1708                 return AST_TEST_NOT_RUN;
1709         }
1710
1711         ast_config_destroy(config);
1712
1713         if (!(sorcery = ast_sorcery_open())) {
1714                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1715                 return AST_TEST_FAIL;
1716         }
1717
1718         if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf,criteria=type=zombies")) {
1719                 ast_test_status_update(test, "Could not set a default wizard of the 'config' type, so skipping since it may not be loaded\n");
1720                 return AST_TEST_NOT_RUN;
1721         }
1722
1723         if (ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
1724                 ast_test_status_update(test, "Failed to register object type\n");
1725                 return AST_TEST_FAIL;
1726         }
1727
1728         ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
1729         ast_sorcery_object_field_register(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
1730         ast_sorcery_object_field_register(sorcery, "test", "type", NULL, OPT_NOOP_T, 0, NULL);
1731
1732         ast_sorcery_load(sorcery);
1733
1734         if ((obj = ast_sorcery_retrieve_by_id(sorcery, "test", "hey"))) {
1735                 ast_test_status_update(test, "Retrieved object which did not match criteria\n");
1736                 return AST_TEST_FAIL;
1737         } else if (!(obj = ast_sorcery_retrieve_by_id(sorcery, "test", "hey2"))) {
1738                 ast_test_status_update(test, "Failed to retrieve a known object which matches criteria\n");
1739                 return AST_TEST_FAIL;
1740         }
1741
1742         return AST_TEST_PASS;
1743 }
1744
1745 AST_TEST_DEFINE(configuration_file_wizard_retrieve_field)
1746 {
1747         struct ast_flags flags = { CONFIG_FLAG_NOCACHE };
1748         struct ast_config *config;
1749         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1750         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1751         RAII_VAR(struct ast_variable *, fields, ast_variable_new("joe", "41", ""), ast_variables_destroy);
1752
1753         switch (cmd) {
1754         case TEST_INIT:
1755                 info->name = "configuration_file_wizard_retrieve_field";
1756                 info->category = "/main/sorcery/";
1757                 info->summary = "sorcery configuration file wizard field retrieval unit test";
1758                 info->description =
1759                         "Test the configuration file wizard retrieval using field in sorcery";
1760                 return AST_TEST_NOT_RUN;
1761         case TEST_EXECUTE:
1762                 break;
1763         }
1764
1765         if (!(config = ast_config_load2("test_sorcery.conf", "test_sorcery", flags))) {
1766                 ast_test_status_update(test, "Test sorcery configuration file wizard file not present - skipping configuration_file_wizard_retrieve_field test\n");
1767                 return AST_TEST_NOT_RUN;
1768         }
1769
1770         ast_config_destroy(config);
1771
1772         if (!(sorcery = ast_sorcery_open())) {
1773                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1774                 return AST_TEST_FAIL;
1775         }
1776
1777         if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf")) {
1778                 ast_test_status_update(test, "Could not set a default wizard of the 'config' type, so skipping since it may not be loaded\n");
1779                 return AST_TEST_NOT_RUN;
1780         }
1781
1782         if (ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
1783                 ast_test_status_update(test, "Failed to register object type\n");
1784                 return AST_TEST_FAIL;
1785         }
1786
1787         ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
1788         ast_sorcery_object_field_register(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
1789
1790         ast_sorcery_load(sorcery);
1791
1792         if (!(obj = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_DEFAULT, fields))) {
1793                 ast_test_status_update(test, "Failed to retrieve a known object that has been configured with the correct field\n");
1794                 return AST_TEST_FAIL;
1795         } else if (strcmp(ast_sorcery_object_get_id(obj), "hey")) {
1796                 ast_test_status_update(test, "Retrieved object has incorrect object id of '%s'\n", ast_sorcery_object_get_id(obj));
1797                 return AST_TEST_FAIL;
1798         }
1799
1800         return AST_TEST_PASS;
1801 }
1802
1803 AST_TEST_DEFINE(configuration_file_wizard_retrieve_multiple)
1804 {
1805         struct ast_flags flags = { CONFIG_FLAG_NOCACHE };
1806         struct ast_config *config;
1807         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1808         RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
1809         RAII_VAR(struct ast_variable *, fields, ast_variable_new("joe", "99", ""), ast_variables_destroy);
1810
1811         switch (cmd) {
1812         case TEST_INIT:
1813                 info->name = "configuration_file_wizard_retrieve_multiple";
1814                 info->category = "/main/sorcery/";
1815                 info->summary = "sorcery configuration file wizard multiple retrieval unit test";
1816                 info->description =
1817                         "Test the configuration file wizard multiple retrieval in sorcery";
1818                 return AST_TEST_NOT_RUN;
1819         case TEST_EXECUTE:
1820                 break;
1821         }
1822
1823         if (!(config = ast_config_load2("test_sorcery.conf", "test_sorcery", flags))) {
1824                 ast_test_status_update(test, "Test sorcery configuration file wizard file not present - skipping configuration_file_wizard_retrieve_multiple test\n");
1825                 return AST_TEST_NOT_RUN;
1826         }
1827
1828         ast_config_destroy(config);
1829
1830         if (!fields) {
1831                 ast_test_status_update(test, "Failed to create fields for multiple retrieve\n");
1832                 return AST_TEST_FAIL;
1833         }
1834
1835         if (!(sorcery = ast_sorcery_open())) {
1836                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1837                 return AST_TEST_FAIL;
1838         }
1839
1840         if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf")) {
1841                 ast_test_status_update(test, "Could not set a default wizard of the 'config' type, so skipping since it may not be loaded\n");
1842                 return AST_TEST_NOT_RUN;
1843         }
1844
1845         if (ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
1846                 ast_test_status_update(test, "Failed to register object type\n");
1847                 return AST_TEST_FAIL;
1848         }
1849
1850         ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
1851         ast_sorcery_object_field_register(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
1852
1853         ast_sorcery_load(sorcery);
1854
1855         if (!(objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE, fields))) {
1856                 ast_test_status_update(test, "Failed to retrieve an empty container when retrieving multiple\n");
1857                 return AST_TEST_FAIL;
1858         } else if (ao2_container_count(objects)) {
1859                 ast_test_status_update(test, "Received a container with objects when there should be none in it\n");
1860                 return AST_TEST_FAIL;
1861         }
1862
1863         ao2_cleanup(objects);
1864         ast_variables_destroy(fields);
1865
1866         if (!(fields = ast_variable_new("joe", "41", ""))) {
1867                 ast_test_status_update(test, "Failed to create fields for multiple retrieve\n");
1868                 return AST_TEST_FAIL;
1869         } else if (!(objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE, fields))) {
1870                 ast_test_status_update(test, "Failed to retrieve a container when retrieving multiple\n");
1871                 return AST_TEST_FAIL;
1872         } else if (ao2_container_count(objects) != 1) {
1873                 ast_test_status_update(test, "Received a container with no objects in it when there should be\n");
1874                 return AST_TEST_FAIL;
1875         }
1876
1877         return AST_TEST_PASS;
1878 }
1879
1880 AST_TEST_DEFINE(configuration_file_wizard_retrieve_multiple_all)
1881 {
1882         struct ast_flags flags = { CONFIG_FLAG_NOCACHE };
1883         struct ast_config *config;
1884         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1885         RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
1886
1887         switch (cmd) {
1888         case TEST_INIT:
1889                 info->name = "configuration_file_wizard_retrieve_multiple_all";
1890                 info->category = "/main/sorcery/";
1891                 info->summary = "sorcery configuration file wizard multiple retrieve all unit test";
1892                 info->description =
1893                         "Test the configuration file wizard multiple retrieve all in sorcery";
1894                 return AST_TEST_NOT_RUN;
1895         case TEST_EXECUTE:
1896                 break;
1897         }
1898
1899         if (!(config = ast_config_load2("test_sorcery.conf", "test_sorcery", flags))) {
1900                 ast_test_status_update(test, "Test sorcery configuration file wizard file not present - skipping configuration_file_wizard_retrieve_multiple_all test\n");
1901                 return AST_TEST_NOT_RUN;
1902         }
1903
1904         ast_config_destroy(config);
1905
1906         if (!(sorcery = ast_sorcery_open())) {
1907                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1908                 return AST_TEST_FAIL;
1909         }
1910
1911         if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf")) {
1912                 ast_test_status_update(test, "Could not set a default wizard of the 'config' type, so skipping since it may not be loaded\n");
1913                 return AST_TEST_NOT_RUN;
1914         }
1915
1916         if (ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
1917                 ast_test_status_update(test, "Failed to register object type\n");
1918                 return AST_TEST_FAIL;
1919         }
1920
1921         ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
1922         ast_sorcery_object_field_register(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
1923
1924         ast_sorcery_load(sorcery);
1925
1926         if (!(objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL))) {
1927                 ast_test_status_update(test, "Failed to retrieve a container with all objects when there should be one\n");
1928                 return AST_TEST_FAIL;
1929         } else if (ao2_container_count(objects) != 2) {
1930                 ast_test_status_update(test, "Returned container does not have the correct number of objects in it\n");
1931                 return AST_TEST_FAIL;
1932         }
1933
1934         return AST_TEST_PASS;
1935 }
1936
1937 static int unload_module(void)
1938 {
1939         AST_TEST_UNREGISTER(wizard_registration);
1940         AST_TEST_UNREGISTER(sorcery_open);
1941         AST_TEST_UNREGISTER(apply_default);
1942         AST_TEST_UNREGISTER(apply_config);
1943         AST_TEST_UNREGISTER(object_register);
1944         AST_TEST_UNREGISTER(object_register_without_mapping);
1945         AST_TEST_UNREGISTER(object_field_register);
1946         AST_TEST_UNREGISTER(object_alloc_with_id);
1947         AST_TEST_UNREGISTER(object_alloc_without_id);
1948         AST_TEST_UNREGISTER(object_copy);
1949         AST_TEST_UNREGISTER(object_diff);
1950         AST_TEST_UNREGISTER(objectset_create);
1951         AST_TEST_UNREGISTER(objectset_apply);
1952         AST_TEST_UNREGISTER(objectset_apply_handler);
1953         AST_TEST_UNREGISTER(objectset_apply_invalid);
1954         AST_TEST_UNREGISTER(objectset_transform);
1955         AST_TEST_UNREGISTER(changeset_create);
1956         AST_TEST_UNREGISTER(changeset_create_unchanged);
1957         AST_TEST_UNREGISTER(object_create);
1958         AST_TEST_UNREGISTER(object_retrieve_id);
1959         AST_TEST_UNREGISTER(object_retrieve_field);
1960         AST_TEST_UNREGISTER(object_retrieve_multiple_all);
1961         AST_TEST_UNREGISTER(object_retrieve_multiple_field);
1962         AST_TEST_UNREGISTER(object_update);
1963         AST_TEST_UNREGISTER(object_update_uncreated);
1964         AST_TEST_UNREGISTER(object_delete);
1965         AST_TEST_UNREGISTER(object_delete_uncreated);
1966         AST_TEST_UNREGISTER(caching_wizard_behavior);
1967         AST_TEST_UNREGISTER(configuration_file_wizard);
1968         AST_TEST_UNREGISTER(configuration_file_wizard_with_file_integrity);
1969         AST_TEST_UNREGISTER(configuration_file_wizard_with_criteria);
1970         AST_TEST_UNREGISTER(configuration_file_wizard_retrieve_field);
1971         AST_TEST_UNREGISTER(configuration_file_wizard_retrieve_multiple);
1972         AST_TEST_UNREGISTER(configuration_file_wizard_retrieve_multiple_all);
1973         return 0;
1974 }
1975
1976 static int load_module(void)
1977 {
1978         AST_TEST_REGISTER(wizard_registration);
1979         AST_TEST_REGISTER(sorcery_open);
1980         AST_TEST_REGISTER(apply_default);
1981         AST_TEST_REGISTER(apply_config);
1982         AST_TEST_REGISTER(object_register);
1983         AST_TEST_REGISTER(object_register_without_mapping);
1984         AST_TEST_REGISTER(object_field_register);
1985         AST_TEST_REGISTER(object_alloc_with_id);
1986         AST_TEST_REGISTER(object_alloc_without_id);
1987         AST_TEST_REGISTER(object_copy);
1988         AST_TEST_REGISTER(object_diff);
1989         AST_TEST_REGISTER(objectset_create);
1990         AST_TEST_REGISTER(objectset_apply);
1991         AST_TEST_REGISTER(objectset_apply_handler);
1992         AST_TEST_REGISTER(objectset_apply_invalid);
1993         AST_TEST_REGISTER(objectset_transform);
1994         AST_TEST_REGISTER(changeset_create);
1995         AST_TEST_REGISTER(changeset_create_unchanged);
1996         AST_TEST_REGISTER(object_create);
1997         AST_TEST_REGISTER(object_retrieve_id);
1998         AST_TEST_REGISTER(object_retrieve_field);
1999         AST_TEST_REGISTER(object_retrieve_multiple_all);
2000         AST_TEST_REGISTER(object_retrieve_multiple_field);
2001         AST_TEST_REGISTER(object_update);
2002         AST_TEST_REGISTER(object_update_uncreated);
2003         AST_TEST_REGISTER(object_delete);
2004         AST_TEST_REGISTER(object_delete_uncreated);
2005         AST_TEST_REGISTER(caching_wizard_behavior);
2006         AST_TEST_REGISTER(configuration_file_wizard);
2007         AST_TEST_REGISTER(configuration_file_wizard_with_file_integrity);
2008         AST_TEST_REGISTER(configuration_file_wizard_with_criteria);
2009         AST_TEST_REGISTER(configuration_file_wizard_retrieve_field);
2010         AST_TEST_REGISTER(configuration_file_wizard_retrieve_multiple);
2011         AST_TEST_REGISTER(configuration_file_wizard_retrieve_multiple_all);
2012         return AST_MODULE_LOAD_SUCCESS;
2013 }
2014
2015 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Sorcery test module");