Prevent duplicate sorcery wizards from being applied to sorcery object types.
[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         <depend>func_sorcery</depend>
30         <support_level>core</support_level>
31  ***/
32
33 #include "asterisk.h"
34
35 ASTERISK_FILE_VERSION(__FILE__, "")
36
37 #include "asterisk/test.h"
38 #include "asterisk/module.h"
39 #include "asterisk/astobj2.h"
40 #include "asterisk/pbx.h"
41 #include "asterisk/sorcery.h"
42 #include "asterisk/logger.h"
43 #include "asterisk/json.h"
44
45 /*! \brief Dummy sorcery object */
46 struct test_sorcery_object {
47         SORCERY_OBJECT(details);
48         unsigned int bob;
49         unsigned int joe;
50         struct ast_variable *jim;
51         struct ast_variable *jack;
52 };
53
54 /*! \brief Internal function to destroy a test object */
55 static void test_sorcery_object_destroy(void *obj)
56 {
57         struct test_sorcery_object *tobj = obj;
58         ast_variables_destroy(tobj->jim);
59         ast_variables_destroy(tobj->jack);
60 }
61
62 /*! \brief Internal function to allocate a test object */
63 static void *test_sorcery_object_alloc(const char *id)
64 {
65         return ast_sorcery_generic_alloc(sizeof(struct test_sorcery_object), test_sorcery_object_destroy);
66 }
67
68 /*! \brief Internal function for object set transformation */
69 static struct ast_variable *test_sorcery_transform(struct ast_variable *set)
70 {
71         struct ast_variable *field, *transformed = NULL;
72
73         for (field = set; field; field = field->next) {
74                 struct ast_variable *transformed_field;
75
76                 if (!strcmp(field->name, "joe")) {
77                         transformed_field = ast_variable_new(field->name, "5000", "");
78                 } else {
79                         transformed_field = ast_variable_new(field->name, field->value, "");
80                 }
81
82                 if (!transformed_field) {
83                         ast_variables_destroy(transformed);
84                         return NULL;
85                 }
86
87                 transformed_field->next = transformed;
88                 transformed = transformed_field;
89         }
90
91         return transformed;
92 }
93
94 /*! \brief Internal function which copies pre-defined data into an object, natively */
95 static int test_sorcery_copy(const void *src, void *dst)
96 {
97         struct test_sorcery_object *obj = dst;
98         obj->bob = 10;
99         obj->joe = 20;
100         obj->jim = ast_variable_new("jim", "444", "");
101         obj->jack = ast_variable_new("jack", "999,000", "");
102         return 0;
103 }
104
105 /*! \brief Internal function which creates a pre-defined diff natively */
106 static int test_sorcery_diff(const void *original, const void *modified, struct ast_variable **changes)
107 {
108         *changes = ast_variable_new("yes", "itworks", "");
109         return 0;
110 }
111
112 /*! \brief Internal function which sets some values */
113 static int test_sorcery_regex_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
114 {
115         struct test_sorcery_object *test = obj;
116
117         test->bob = 256;
118
119         return 0;
120 }
121
122 /*! \brief Internal function which creates some ast_variable structures */
123 static int test_sorcery_regex_fields(const void *obj, struct ast_variable **fields)
124 {
125         *fields = ast_variable_new("toast-bob", "10", "");
126
127         return 0;
128 }
129
130 /*! \brief Test structure for caching */
131 struct sorcery_test_caching {
132         /*! \brief Whether the object has been created in the cache or not */
133         unsigned int created:1;
134
135         /*! \brief Whether the object has been updated in the cache or not */
136         unsigned int updated:1;
137
138         /*! \brief Whether the object has been deleted from the cache or not */
139         unsigned int deleted:1;
140
141         /*! \brief Object to return when asked */
142         struct test_sorcery_object object;
143 };
144
145 /*! \brief Test structure for observer */
146 struct sorcery_test_observer {
147         /*! \brief Lock for notification */
148         ast_mutex_t lock;
149
150         /*! \brief Condition for notification */
151         ast_cond_t cond;
152
153         /*! \brief Pointer to the created object */
154         const void *created;
155
156         /*! \brief Pointer to the update object */
157         const void *updated;
158
159         /*! \brief Pointer to the deleted object */
160         const void *deleted;
161
162         /*! \brief Whether the type has been loaded */
163         unsigned int loaded:1;
164 };
165
166 /*! \brief Global scope apply handler integer to make sure it executed */
167 static int apply_handler_called;
168
169 /*! \brief Simple apply handler which sets global scope integer to 1 if called */
170 static int test_apply_handler(const struct ast_sorcery *sorcery, void *obj)
171 {
172         apply_handler_called = 1;
173         return 0;
174 }
175
176 /*! \brief Global scope caching structure for testing */
177 static struct sorcery_test_caching cache = { 0, };
178
179 /*! \brief Global scope observer structure for testing */
180 static struct sorcery_test_observer observer;
181
182 static int sorcery_test_create(const struct ast_sorcery *sorcery, void *data, void *object)
183 {
184         cache.created = 1;
185         cache.updated = 0;
186         cache.deleted = 0;
187         return 0;
188 }
189
190 static void *sorcery_test_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type, const char *id)
191 {
192         return (cache.created && !cache.deleted) ? ast_sorcery_alloc(sorcery, type, id) : NULL;
193 }
194
195 static int sorcery_test_update(const struct ast_sorcery *sorcery, void *data, void *object)
196 {
197         cache.updated = 1;
198         return 0;
199 }
200
201 static int sorcery_test_delete(const struct ast_sorcery *sorcery, void *data, void *object)
202 {
203         cache.deleted = 1;
204         return 0;
205 }
206
207 /*! \brief Dummy sorcery wizard, not actually used so we only populate the name and nothing else */
208 static struct ast_sorcery_wizard test_wizard = {
209         .name = "test",
210         .create = sorcery_test_create,
211         .retrieve_id = sorcery_test_retrieve_id,
212         .update = sorcery_test_update,
213         .delete = sorcery_test_delete,
214 };
215
216 static void sorcery_observer_created(const void *object)
217 {
218         SCOPED_MUTEX(lock, &observer.lock);
219         observer.created = object;
220         ast_cond_signal(&observer.cond);
221 }
222
223 static void sorcery_observer_updated(const void *object)
224 {
225         SCOPED_MUTEX(lock, &observer.lock);
226         observer.updated = object;
227         ast_cond_signal(&observer.cond);
228 }
229
230 static void sorcery_observer_deleted(const void *object)
231 {
232         SCOPED_MUTEX(lock, &observer.lock);
233         observer.deleted = object;
234         ast_cond_signal(&observer.cond);
235 }
236
237 static void sorcery_observer_loaded(const char *object_type)
238 {
239         SCOPED_MUTEX(lock, &observer.lock);
240         observer.loaded = 1;
241         ast_cond_signal(&observer.cond);
242 }
243
244 /*! \brief Test sorcery observer implementation */
245 static const struct ast_sorcery_observer test_observer = {
246         .created = sorcery_observer_created,
247         .updated = sorcery_observer_updated,
248         .deleted = sorcery_observer_deleted,
249         .loaded = sorcery_observer_loaded,
250 };
251
252 /*  This handler takes a simple value and creates new list entry for it*/
253 static int jim_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
254 {
255         struct test_sorcery_object *tobj = obj;
256
257         ast_variable_list_append(&tobj->jim, ast_variables_dup(var));
258
259         return 0;
260 }
261
262 /*  This handler takes a CSV string and creates new a new list entry for each value */
263 static int jack_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
264 {
265         struct test_sorcery_object *tobj = obj;
266
267         char *jacks = ast_strdupa(var->value);
268         char *val;
269
270         while ((val = strsep(&jacks, ","))) {
271                 ast_variable_list_append(&tobj->jack, ast_variable_new("jack", val, ""));
272         }
273         return 0;
274 }
275
276 static int jim_vl(const void *obj, struct ast_variable **fields)
277 {
278         const struct test_sorcery_object *tobj = obj;
279         if (tobj->jim) {
280                 *fields = ast_variables_dup(tobj->jim);
281         }
282         return 0;
283 }
284
285 static int jack_str(const void *obj, const intptr_t *args, char **buf)
286 {
287         const struct test_sorcery_object *tobj = obj;
288         struct ast_variable *curr = tobj->jack;
289         RAII_VAR(struct ast_str *, str, ast_str_create(128), ast_free);
290
291         while(curr) {
292                 ast_str_append(&str, 0, "%s,", curr->value);
293                 curr = curr->next;
294         }
295         ast_str_truncate(str, -1);
296         *buf = ast_strdup(ast_str_buffer(str));
297         str = NULL;
298         return 0;
299 }
300
301 static struct ast_sorcery *alloc_and_initialize_sorcery(void)
302 {
303         struct ast_sorcery *sorcery;
304
305         if (!(sorcery = ast_sorcery_open())) {
306                 return NULL;
307         }
308
309         if ((ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) ||
310                 ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
311                 ast_sorcery_unref(sorcery);
312                 return NULL;
313         }
314
315         ast_sorcery_object_field_register_nodoc(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
316         ast_sorcery_object_field_register_nodoc(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
317         ast_sorcery_object_field_register_custom_nodoc(sorcery, "test", "jim", "444", jim_handler, NULL, jim_vl, 0, 0);
318         ast_sorcery_object_field_register_custom_nodoc(sorcery, "test", "jack", "888,999", jack_handler, jack_str, NULL, 0, 0);
319
320         return sorcery;
321 }
322
323 AST_TEST_DEFINE(wizard_registration)
324 {
325         switch (cmd) {
326         case TEST_INIT:
327                 info->name = "wizard_registration";
328                 info->category = "/main/sorcery/";
329                 info->summary = "sorcery wizard registration and unregistration unit test";
330                 info->description =
331                         "Test registration and unregistration of a sorcery wizard";
332                 return AST_TEST_NOT_RUN;
333         case TEST_EXECUTE:
334                 break;
335         }
336
337         if (ast_sorcery_wizard_register(&test_wizard)) {
338                 ast_test_status_update(test, "Failed to register a perfectly valid sorcery wizard\n");
339                 return AST_TEST_FAIL;
340         }
341
342         if (!ast_sorcery_wizard_register(&test_wizard)) {
343                 ast_test_status_update(test, "Successfully registered a sorcery wizard twice, which is bad\n");
344                 return AST_TEST_FAIL;
345         }
346
347         if (ast_sorcery_wizard_unregister(&test_wizard)) {
348                 ast_test_status_update(test, "Failed to unregister a perfectly valid sorcery wizard\n");
349                 return AST_TEST_FAIL;
350         }
351
352         if (!ast_sorcery_wizard_unregister(&test_wizard)) {
353                 ast_test_status_update(test, "Successfully unregistered a sorcery wizard twice, which is bad\n");
354                 return AST_TEST_FAIL;
355         }
356
357         return AST_TEST_PASS;
358 }
359
360 AST_TEST_DEFINE(sorcery_open)
361 {
362         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
363         RAII_VAR(struct ast_sorcery *, sorcery2, NULL, ast_sorcery_unref);
364         int refcount;
365
366         switch (cmd) {
367         case TEST_INIT:
368                 info->name = "open";
369                 info->category = "/main/sorcery/";
370                 info->summary = "sorcery open/close unit test";
371                 info->description =
372                         "Test opening of sorcery and registry operations";
373                 return AST_TEST_NOT_RUN;
374         case TEST_EXECUTE:
375                 break;
376         }
377
378         if ((sorcery = ast_sorcery_retrieve_by_module_name(AST_MODULE))) {
379                 ast_test_status_update(test, "There should NOT have been an existing sorcery instance\n");
380                 return AST_TEST_FAIL;
381         }
382
383         if (!(sorcery = ast_sorcery_open())) {
384                 ast_test_status_update(test, "Failed to open new sorcery structure\n");
385                 return AST_TEST_FAIL;
386         }
387
388         if (!(sorcery2 = ast_sorcery_retrieve_by_module_name(AST_MODULE))) {
389                 ast_test_status_update(test, "Failed to find sorcery structure in registry\n");
390                 return AST_TEST_FAIL;
391         }
392
393         if (sorcery2 != sorcery) {
394                 ast_test_status_update(test, "Should have gotten same sorcery on retrieve\n");
395                 return AST_TEST_FAIL;
396         }
397         ast_sorcery_unref(sorcery2);
398
399         if ((refcount = ao2_ref(sorcery, 0)) != 2) {
400                 ast_test_status_update(test, "Should have been 2 references to sorcery instead of %d\n", refcount);
401                 return AST_TEST_FAIL;
402         }
403
404         if (!(sorcery2 = ast_sorcery_open())) {
405                 ast_test_status_update(test, "Failed to open second sorcery structure\n");
406                 return AST_TEST_FAIL;
407         }
408
409         if (sorcery2 != sorcery) {
410                 ast_test_status_update(test, "Should have gotten same sorcery on 2nd open\n");
411                 return AST_TEST_FAIL;
412         }
413
414         if ((refcount = ao2_ref(sorcery, 0)) != 3) {
415                 ast_test_status_update(test, "Should have been 3 references to sorcery instead of %d\n", refcount);
416                 return AST_TEST_FAIL;
417         }
418
419         ast_sorcery_unref(sorcery);
420         ast_sorcery_unref(sorcery2);
421
422         sorcery2 = NULL;
423
424         if ((sorcery = ast_sorcery_retrieve_by_module_name(AST_MODULE))) {
425                 ast_sorcery_unref(sorcery);
426                 sorcery = NULL;
427                 ast_test_status_update(test, "Should NOT have found sorcery structure in registry\n");
428                 return AST_TEST_FAIL;
429         }
430
431         return AST_TEST_PASS;
432 }
433
434 AST_TEST_DEFINE(apply_default)
435 {
436         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
437
438         switch (cmd) {
439         case TEST_INIT:
440                 info->name = "apply_default";
441                 info->category = "/main/sorcery/";
442                 info->summary = "sorcery default wizard unit test";
443                 info->description =
444                         "Test setting default type wizard in sorcery";
445                 return AST_TEST_NOT_RUN;
446         case TEST_EXECUTE:
447                 break;
448         }
449
450         if (!(sorcery = ast_sorcery_open())) {
451                 ast_test_status_update(test, "Failed to open sorcery structure\n");
452                 return AST_TEST_FAIL;
453         }
454
455         if (ast_sorcery_apply_default(sorcery, "test", "dummy", NULL) != AST_SORCERY_APPLY_FAIL) {
456                 ast_test_status_update(test, "Successfully set a default wizard that doesn't exist\n");
457                 return AST_TEST_FAIL;
458         }
459
460         if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) {
461                 ast_test_status_update(test, "Failed to set a known wizard as a default\n");
462                 return AST_TEST_FAIL;
463         }
464
465         if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_DEFAULT_UNNECESSARY) {
466                 ast_test_status_update(test, "Successfully set a default wizard on a type twice\n");
467                 return AST_TEST_FAIL;
468         }
469
470         return AST_TEST_PASS;
471 }
472
473 AST_TEST_DEFINE(apply_config)
474 {
475         struct ast_flags flags = { CONFIG_FLAG_NOCACHE };
476         struct ast_config *config;
477         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
478
479         switch (cmd) {
480         case TEST_INIT:
481                 info->name = "apply_config";
482                 info->category = "/main/sorcery/";
483                 info->summary = "sorcery object mapping configuration unit test";
484                 info->description =
485                         "Test configured object mapping in sorcery";
486                 return AST_TEST_NOT_RUN;
487         case TEST_EXECUTE:
488                 break;
489         }
490
491         if (!(config = ast_config_load2("sorcery.conf", "test_sorcery", flags))) {
492                 ast_test_status_update(test, "Sorcery configuration file not present - skipping apply_config test\n");
493                 return AST_TEST_NOT_RUN;
494         }
495
496         if (!ast_category_get(config, "test_sorcery_section")) {
497                 ast_test_status_update(test, "Sorcery configuration file does not have test_sorcery section\n");
498                 ast_config_destroy(config);
499                 return AST_TEST_NOT_RUN;
500         }
501
502         ast_config_destroy(config);
503
504         if (!(sorcery = ast_sorcery_open())) {
505                 ast_test_status_update(test, "Failed to open sorcery structure\n");
506                 return AST_TEST_FAIL;
507         }
508
509         if (ast_sorcery_apply_config(sorcery, "test_sorcery_section") != AST_SORCERY_APPLY_SUCCESS) {
510                 ast_test_status_update(test, "Failed to apply configured object mappings\n");
511                 return AST_TEST_FAIL;
512         }
513
514         return AST_TEST_PASS;
515 }
516
517 AST_TEST_DEFINE(object_register)
518 {
519         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
520
521         switch (cmd) {
522         case TEST_INIT:
523                 info->name = "object_register";
524                 info->category = "/main/sorcery/";
525                 info->summary = "sorcery object type registration unit test";
526                 info->description =
527                         "Test object type registration in sorcery";
528                 return AST_TEST_NOT_RUN;
529         case TEST_EXECUTE:
530                 break;
531         }
532
533         if (!(sorcery = ast_sorcery_open())) {
534                 ast_test_status_update(test, "Failed to open structure\n");
535                 return AST_TEST_FAIL;
536         }
537
538         if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) {
539                 ast_test_status_update(test, "Failed to set a known wizard as a default\n");
540                 return AST_TEST_FAIL;
541         }
542
543         if (ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
544                 ast_test_status_update(test, "Failed to register object type\n");
545                 return AST_TEST_FAIL;
546         }
547
548         if (!ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
549                 ast_test_status_update(test, "Registered object type a second time, despite it being registered already\n");
550                 return AST_TEST_FAIL;
551         }
552
553         return AST_TEST_PASS;
554 }
555
556 AST_TEST_DEFINE(object_register_without_mapping)
557 {
558         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
559
560         switch (cmd) {
561         case TEST_INIT:
562                 info->name = "object_register_without_mapping";
563                 info->category = "/main/sorcery/";
564                 info->summary = "sorcery object type registration (without mapping) unit test";
565                 info->description =
566                         "Test object type registration when no mapping exists in sorcery";
567                 return AST_TEST_NOT_RUN;
568         case TEST_EXECUTE:
569                 break;
570         }
571
572         if (!(sorcery = ast_sorcery_open())) {
573                 ast_test_status_update(test, "Failed to open sorcery structure\n");
574                 return AST_TEST_FAIL;
575         }
576
577         if (!ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
578                 ast_test_status_update(test, "Registered object type when no object mapping exists\n");
579                 return AST_TEST_FAIL;
580         }
581
582         return AST_TEST_PASS;
583 }
584
585 AST_TEST_DEFINE(object_field_register)
586 {
587         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
588
589         switch (cmd) {
590         case TEST_INIT:
591                 info->name = "object_field_register";
592                 info->category = "/main/sorcery/";
593                 info->summary = "sorcery object field registration unit test";
594                 info->description =
595                         "Test object field registration in sorcery with a provided id";
596                 return AST_TEST_NOT_RUN;
597         case TEST_EXECUTE:
598                 break;
599         }
600
601         if (!(sorcery = ast_sorcery_open())) {
602                 ast_test_status_update(test, "Failed to open sorcery structure\n");
603                 return AST_TEST_FAIL;
604         }
605
606         if (!ast_sorcery_object_field_register_nodoc(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob))) {
607                 ast_test_status_update(test, "Registered an object field successfully when no mappings or object types exist\n");
608                 return AST_TEST_FAIL;
609         }
610
611         if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) {
612                 ast_test_status_update(test, "Failed to set a known wizard as a default\n");
613                 return AST_TEST_FAIL;
614         }
615
616         if (!ast_sorcery_object_field_register_nodoc(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob))) {
617                 ast_test_status_update(test, "Registered an object field successfully when object type does not exist\n");
618                 return AST_TEST_FAIL;
619         }
620
621         if (ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
622                 ast_test_status_update(test, "Failed to register object type\n");
623                 return AST_TEST_FAIL;
624         }
625
626         if (ast_sorcery_object_field_register_nodoc(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob))) {
627                 ast_test_status_update(test, "Could not successfully register object field when mapping and object type exists\n");
628                 return AST_TEST_FAIL;
629         }
630
631         return AST_TEST_PASS;
632 }
633
634 AST_TEST_DEFINE(object_fields_register)
635 {
636         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
637
638         switch (cmd) {
639         case TEST_INIT:
640                 info->name = "object_fields_register";
641                 info->category = "/main/sorcery/";
642                 info->summary = "sorcery object regex fields registration unit test";
643                 info->description =
644                         "Test object regex fields registration in sorcery with a provided id";
645                 return AST_TEST_NOT_RUN;
646         case TEST_EXECUTE:
647                 break;
648         }
649
650         if (!(sorcery = ast_sorcery_open())) {
651                 ast_test_status_update(test, "Failed to open sorcery structure\n");
652                 return AST_TEST_FAIL;
653         }
654
655         if (!ast_sorcery_object_fields_register(sorcery, "test", "^toast-", test_sorcery_regex_handler, test_sorcery_regex_fields)) {
656                 ast_test_status_update(test, "Registered a regex object field successfully when no mappings or object types exist\n");
657                 return AST_TEST_FAIL;
658         }
659
660         if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) {
661                 ast_test_status_update(test, "Failed to set a known wizard as a default\n");
662                 return AST_TEST_FAIL;
663         }
664
665         if (!ast_sorcery_object_fields_register(sorcery, "test", "^toast-", test_sorcery_regex_handler, test_sorcery_regex_fields)) {
666                 ast_test_status_update(test, "Registered a regex object field successfully when object type does not exist\n");
667                 return AST_TEST_FAIL;
668         }
669
670         if (ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
671                 ast_test_status_update(test, "Failed to register object type\n");
672                 return AST_TEST_FAIL;
673         }
674
675         if (ast_sorcery_object_fields_register(sorcery, "test", "^toast-", test_sorcery_regex_handler, test_sorcery_regex_fields)) {
676                 ast_test_status_update(test, "Registered a regex object field successfully when no mappings or object types exist\n");
677                 return AST_TEST_FAIL;
678         }
679
680         return AST_TEST_PASS;
681 }
682
683 AST_TEST_DEFINE(object_alloc_with_id)
684 {
685         int res = AST_TEST_PASS;
686         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
687         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
688
689         switch (cmd) {
690         case TEST_INIT:
691                 info->name = "object_alloc_with_id";
692                 info->category = "/main/sorcery/";
693                 info->summary = "sorcery object allocation (with id) unit test";
694                 info->description =
695                         "Test object allocation in sorcery with a provided id";
696                 return AST_TEST_NOT_RUN;
697         case TEST_EXECUTE:
698                 break;
699         }
700
701         if (!(sorcery = alloc_and_initialize_sorcery())) {
702                 ast_test_status_update(test, "Failed to open sorcery structure\n");
703                 return AST_TEST_FAIL;
704         }
705
706         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
707                 ast_test_status_update(test, "Failed to allocate a known object type\n");
708                 res = AST_TEST_FAIL;
709         } else if (ast_strlen_zero(ast_sorcery_object_get_id(obj))) {
710                 ast_test_status_update(test, "Allocated object has empty id when it should not\n");
711                 res = AST_TEST_FAIL;
712         } else if (strcmp(ast_sorcery_object_get_id(obj), "blah")) {
713                 ast_test_status_update(test, "Allocated object does not have correct id\n");
714                 res = AST_TEST_FAIL;
715         } else if (ast_strlen_zero(ast_sorcery_object_get_type(obj))) {
716                 ast_test_status_update(test, "Allocated object has empty type when it should not\n");
717                 res = AST_TEST_FAIL;
718         } else if (strcmp(ast_sorcery_object_get_type(obj), "test")) {
719                 ast_test_status_update(test, "Allocated object does not have correct type\n");
720                 res = AST_TEST_FAIL;
721         } else if ((obj->bob != 5) || (obj->joe != 10)) {
722                 ast_test_status_update(test, "Allocated object does not have defaults set as it should\n");
723                 res = AST_TEST_FAIL;
724         }
725
726         return res;
727 }
728
729 AST_TEST_DEFINE(object_alloc_without_id)
730 {
731         int res = AST_TEST_PASS;
732         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
733         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
734
735         switch (cmd) {
736         case TEST_INIT:
737                 info->name = "object_alloc_without_id";
738                 info->category = "/main/sorcery/";
739                 info->summary = "sorcery object allocation (without id) unit test";
740                 info->description =
741                         "Test object allocation in sorcery with no provided id";
742                 return AST_TEST_NOT_RUN;
743         case TEST_EXECUTE:
744                 break;
745         }
746
747         if (!(sorcery = alloc_and_initialize_sorcery())) {
748                 ast_test_status_update(test, "Failed to open sorcery structure\n");
749                 return AST_TEST_FAIL;
750         }
751
752         if (!(obj = ast_sorcery_alloc(sorcery, "test", NULL))) {
753                 ast_test_status_update(test, "Failed to allocate a known object type\n");
754                 res = AST_TEST_FAIL;
755         } else if (ast_strlen_zero(ast_sorcery_object_get_id(obj))) {
756                 ast_test_status_update(test, "Allocated object has empty id when it should not\n");
757                 res = AST_TEST_FAIL;
758         }
759
760         return res;
761 }
762
763
764 AST_TEST_DEFINE(object_copy)
765 {
766         int res = AST_TEST_PASS;
767         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
768         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
769         RAII_VAR(struct test_sorcery_object *, copy, NULL, ao2_cleanup);
770
771         switch (cmd) {
772         case TEST_INIT:
773                 info->name = "object_copy";
774                 info->category = "/main/sorcery/";
775                 info->summary = "sorcery object copy unit test";
776                 info->description =
777                         "Test object copy in sorcery";
778                 return AST_TEST_NOT_RUN;
779         case TEST_EXECUTE:
780                 break;
781         }
782
783         if (!(sorcery = alloc_and_initialize_sorcery())) {
784                 ast_test_status_update(test, "Failed to open sorcery structure\n");
785                 return AST_TEST_FAIL;
786         }
787
788         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
789                 ast_test_status_update(test, "Failed to allocate a known object type\n");
790                 return AST_TEST_FAIL;
791         }
792
793         obj->bob = 50;
794         obj->joe = 100;
795         jim_handler(NULL, ast_variable_new("jim", "444", ""), obj);
796         jim_handler(NULL, ast_variable_new("jim", "555", ""), obj);
797
798         if (!(copy = ast_sorcery_copy(sorcery, obj))) {
799                 ast_test_status_update(test, "Failed to create a copy of a known valid object\n");
800                 res = AST_TEST_FAIL;
801         } else if (copy == obj) {
802                 ast_test_status_update(test, "Created copy is actually the original object\n");
803                 res = AST_TEST_FAIL;
804         } else if (copy->bob != obj->bob) {
805                 ast_test_status_update(test, "Value of 'bob' on newly created copy is not the same as original\n");
806                 res = AST_TEST_FAIL;
807         } else if (copy->joe != obj->joe) {
808                 ast_test_status_update(test, "Value of 'joe' on newly created copy is not the same as original\n");
809                 res = AST_TEST_FAIL;
810         } else if (!copy->jim) {
811                 ast_test_status_update(test, "A new ast_variable was not created for 'jim'\n");
812                 res = AST_TEST_FAIL;
813         } else if (copy->jim == obj->jim) {
814                 ast_test_status_update(test, "Created copy of 'jim' is actually the ogirinal 'jim'\n");
815                 res = AST_TEST_FAIL;
816         } else if (strcmp(copy->jim->value, obj->jim->value)) {
817                 ast_test_status_update(test, "Value of 1st 'jim' on newly created copy is not the same as original\n");
818                 res = AST_TEST_FAIL;
819         } else if (!copy->jim->next) {
820                 ast_test_status_update(test, "A new ast_variable was not created for 2nd 'jim'\n");
821                 res = AST_TEST_FAIL;
822         } else if (strcmp(copy->jim->next->value, obj->jim->next->value)) {
823                 ast_test_status_update(test, "Value of 2nd 'jim' (%s %s) on newly created copy is not the same as original (%s %s)\n",
824                         copy->jim->value, copy->jim->next->value, obj->jim->value, obj->jim->next->value);
825                 res = AST_TEST_FAIL;
826         }
827
828         return res;
829 }
830
831 AST_TEST_DEFINE(object_copy_native)
832 {
833         int res = AST_TEST_PASS;
834         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
835         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
836         RAII_VAR(struct test_sorcery_object *, copy, NULL, ao2_cleanup);
837
838         switch (cmd) {
839         case TEST_INIT:
840                 info->name = "object_copy_native";
841                 info->category = "/main/sorcery/";
842                 info->summary = "sorcery object native copy unit test";
843                 info->description =
844                         "Test object native copy in sorcery";
845                 return AST_TEST_NOT_RUN;
846         case TEST_EXECUTE:
847                 break;
848         }
849
850         if (!(sorcery = alloc_and_initialize_sorcery())) {
851                 ast_test_status_update(test, "Failed to open sorcery structure\n");
852                 return AST_TEST_FAIL;
853         }
854
855         ast_sorcery_object_set_copy_handler(sorcery, "test", test_sorcery_copy);
856
857         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
858                 ast_test_status_update(test, "Failed to allocate a known object type\n");
859                 return AST_TEST_FAIL;
860         }
861
862         obj->bob = 50;
863         obj->joe = 100;
864
865         if (!(copy = ast_sorcery_copy(sorcery, obj))) {
866                 ast_test_status_update(test, "Failed to create a copy of a known valid object\n");
867                 res = AST_TEST_FAIL;
868         } else if (copy == obj) {
869                 ast_test_status_update(test, "Created copy is actually the original object\n");
870                 res = AST_TEST_FAIL;
871         } else if (copy->bob != 10) {
872                 ast_test_status_update(test, "Value of 'bob' on newly created copy is not the predefined native copy value\n");
873                 res = AST_TEST_FAIL;
874         } else if (copy->joe != 20) {
875                 ast_test_status_update(test, "Value of 'joe' on newly created copy is not the predefined native copy value\n");
876                 res = AST_TEST_FAIL;
877         } else if (!copy->jim) {
878                 ast_test_status_update(test, "A new ast_variable was not created for 'jim'\n");
879                 res = AST_TEST_FAIL;
880         } else if (strcmp(copy->jim->value, "444")) {
881                 ast_test_status_update(test, "Value of 'jim' on newly created copy is not the predefined native copy value\n");
882                 res = AST_TEST_FAIL;
883         }
884
885         return res;
886 }
887
888 AST_TEST_DEFINE(object_diff)
889 {
890        RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
891        RAII_VAR(struct test_sorcery_object *, obj1, NULL, ao2_cleanup);
892        RAII_VAR(struct test_sorcery_object *, obj2, NULL, ao2_cleanup);
893        RAII_VAR(struct ast_variable *, changes, NULL, ast_variables_destroy);
894        struct ast_variable *field;
895        int res = AST_TEST_PASS;
896        int jims = 0;
897
898        switch (cmd) {
899        case TEST_INIT:
900                info->name = "object_diff";
901                info->category = "/main/sorcery/";
902                info->summary = "sorcery object diff unit test";
903                info->description =
904                        "Test object diffing in sorcery";
905                return AST_TEST_NOT_RUN;
906        case TEST_EXECUTE:
907                break;
908        }
909
910        if (!(sorcery = alloc_and_initialize_sorcery())) {
911                ast_test_status_update(test, "Failed to open sorcery structure\n");
912                return AST_TEST_FAIL;
913        }
914
915        if (!(obj1 = ast_sorcery_alloc(sorcery, "test", "blah"))) {
916                ast_test_status_update(test, "Failed to allocate a known object type\n");
917                return AST_TEST_FAIL;
918        }
919
920        obj1->bob = 99;
921        obj1->joe = 55;
922        jim_handler(NULL, ast_variable_new("jim", "444", ""), obj1);
923        jim_handler(NULL, ast_variable_new("jim", "555", ""), obj1);
924
925        if (!(obj2 = ast_sorcery_alloc(sorcery, "test", "blah2"))) {
926                ast_test_status_update(test, "Failed to allocate a second known object type\n");
927                return AST_TEST_FAIL;
928        }
929
930        obj2->bob = 99;
931        obj2->joe = 42;
932        jim_handler(NULL, ast_variable_new("jim", "444", ""), obj2);
933        jim_handler(NULL, ast_variable_new("jim", "666", ""), obj2);
934        jim_handler(NULL, ast_variable_new("jim", "777", ""), obj2);
935
936        if (ast_sorcery_diff(sorcery, obj1, obj2, &changes)) {
937                ast_test_status_update(test, "Failed to diff obj1 and obj2\n");
938        } else if (!changes) {
939                ast_test_status_update(test, "Failed to produce a diff of two objects, despite there being differences\n");
940                return AST_TEST_FAIL;
941        }
942
943         for (field = changes; field; field = field->next) {
944                 if (!strcmp(field->name, "joe")) {
945                         if (strcmp(field->value, "42")) {
946                                 ast_test_status_update(test,
947                                         "Object diff produced unexpected value '%s' for joe\n", field->value);
948                                 res = AST_TEST_FAIL;
949                         }
950                 } else if (!strcmp(field->name, "jim")) {
951                         jims++;
952                         if (!strcmp(field->value, "555")) {
953                                 ast_test_status_update(test,
954                                         "Object diff produced unexpected value '%s' for jim\n", field->value);
955                                 res = AST_TEST_FAIL;
956                         }
957                 } else {
958                         ast_test_status_update(test, "Object diff produced unexpected field '%s'\n",
959                                 field->name);
960                         res = AST_TEST_FAIL;
961                 }
962         }
963
964        if (jims != 2) {
965                ast_test_status_update(test, "Object diff didn't produce 2 jims\n");
966                res = AST_TEST_FAIL;
967        }
968
969        return res;
970 }
971
972 AST_TEST_DEFINE(object_diff_native)
973 {
974        RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
975        RAII_VAR(struct test_sorcery_object *, obj1, NULL, ao2_cleanup);
976        RAII_VAR(struct test_sorcery_object *, obj2, NULL, ao2_cleanup);
977        RAII_VAR(struct ast_variable *, changes, NULL, ast_variables_destroy);
978        struct ast_variable *field;
979        int res = AST_TEST_PASS;
980
981        switch (cmd) {
982        case TEST_INIT:
983                info->name = "object_diff_native";
984                info->category = "/main/sorcery/";
985                info->summary = "sorcery object native diff unit test";
986                info->description =
987                        "Test native object diffing in sorcery";
988                return AST_TEST_NOT_RUN;
989        case TEST_EXECUTE:
990                break;
991        }
992
993        if (!(sorcery = alloc_and_initialize_sorcery())) {
994                ast_test_status_update(test, "Failed to open sorcery structure\n");
995                return AST_TEST_FAIL;
996        }
997
998        ast_sorcery_object_set_diff_handler(sorcery, "test", test_sorcery_diff);
999
1000        if (!(obj1 = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1001                ast_test_status_update(test, "Failed to allocate a known object type\n");
1002                return AST_TEST_FAIL;
1003        }
1004
1005        obj1->bob = 99;
1006        obj1->joe = 55;
1007
1008        if (!(obj2 = ast_sorcery_alloc(sorcery, "test", "blah2"))) {
1009                ast_test_status_update(test, "Failed to allocate a second known object type\n");
1010                return AST_TEST_FAIL;
1011        }
1012
1013        obj2->bob = 99;
1014        obj2->joe = 42;
1015
1016        if (ast_sorcery_diff(sorcery, obj1, obj2, &changes)) {
1017                ast_test_status_update(test, "Failed to diff obj1 and obj2\n");
1018        } else if (!changes) {
1019                ast_test_status_update(test, "Failed to produce a diff of two objects, despite there being differences\n");
1020                return AST_TEST_FAIL;
1021        }
1022
1023        for (field = changes; field; field = field->next) {
1024                if (!strcmp(field->name, "yes")) {
1025                        if (strcmp(field->value, "itworks")) {
1026                                ast_test_status_update(test, "Object diff produced unexpected value '%s' for joe\n", field->value);
1027                                res = AST_TEST_FAIL;
1028                        }
1029                } else {
1030                        ast_test_status_update(test, "Object diff produced unexpected field '%s'\n", field->name);
1031                        res = AST_TEST_FAIL;
1032                }
1033        }
1034
1035        return res;
1036 }
1037
1038 AST_TEST_DEFINE(objectset_create)
1039 {
1040         int res = AST_TEST_PASS;
1041         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1042         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1043         RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
1044         struct ast_variable *field;
1045
1046         switch (cmd) {
1047         case TEST_INIT:
1048                 info->name = "objectset_create";
1049                 info->category = "/main/sorcery/";
1050                 info->summary = "sorcery object set creation unit test";
1051                 info->description =
1052                         "Test object set creation in sorcery";
1053                 return AST_TEST_NOT_RUN;
1054         case TEST_EXECUTE:
1055                 break;
1056         }
1057
1058         if (!(sorcery = alloc_and_initialize_sorcery())) {
1059                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1060                 return AST_TEST_FAIL;
1061         }
1062
1063         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1064                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1065                 return AST_TEST_FAIL;
1066         }
1067
1068         if (!(objset = ast_sorcery_objectset_create(sorcery, obj))) {
1069                 ast_test_status_update(test, "Failed to create an object set for a known sane object\n");
1070                 return AST_TEST_FAIL;
1071         }
1072
1073         for (field = objset; field; field = field->next) {
1074                 if (!strcmp(field->name, "bob")) {
1075                         if (strcmp(field->value, "5")) {
1076                                 ast_test_status_update(test, "Object set failed to create proper value for 'bob'\n");
1077                                 res = AST_TEST_FAIL;
1078                         }
1079                 } else if (!strcmp(field->name, "joe")) {
1080                         if (strcmp(field->value, "10")) {
1081                                 ast_test_status_update(test, "Object set failed to create proper value for 'joe'\n");
1082                                 res = AST_TEST_FAIL;
1083                         }
1084                 } else if (!strcmp(field->name, "jim")) {
1085                         if (strcmp(field->value, "444")) {
1086                                 ast_test_status_update(test, "Object set failed to create proper value for 'jim'\n");
1087                                 res = AST_TEST_FAIL;
1088                         }
1089                 } else if (!strcmp(field->name, "jack")) {
1090                         if (strcmp(field->value, "888,999")) {
1091                                 ast_test_status_update(test, "Object set failed to create proper value (%s) for 'jack'\n", field->value);
1092                                 res = AST_TEST_FAIL;
1093                         }
1094                 } else {
1095                         ast_test_status_update(test, "Object set created field '%s' which is unknown\n", field->name);
1096                         res = AST_TEST_FAIL;
1097                 }
1098         }
1099
1100         return res;
1101 }
1102
1103 AST_TEST_DEFINE(objectset_json_create)
1104 {
1105         int res = AST_TEST_PASS;
1106         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1107         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1108         RAII_VAR(struct ast_json *, objset, NULL, ast_json_unref);
1109         struct ast_json_iter *field;
1110
1111         switch (cmd) {
1112         case TEST_INIT:
1113                 info->name = "objectset_json_create";
1114                 info->category = "/main/sorcery/";
1115                 info->summary = "sorcery json object set creation unit test";
1116                 info->description =
1117                         "Test object set creation (for JSON format) in sorcery";
1118                 return AST_TEST_NOT_RUN;
1119         case TEST_EXECUTE:
1120                 break;
1121         }
1122
1123         if (!(sorcery = alloc_and_initialize_sorcery())) {
1124                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1125                 return AST_TEST_FAIL;
1126         }
1127
1128         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1129                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1130                 return AST_TEST_FAIL;
1131         }
1132
1133         if (!(objset = ast_sorcery_objectset_json_create(sorcery, obj))) {
1134                 ast_test_status_update(test, "Failed to create an object set for a known sane object\n");
1135                 return AST_TEST_FAIL;
1136         }
1137
1138         for (field = ast_json_object_iter(objset); field; field = ast_json_object_iter_next(objset, field)) {
1139                 struct ast_json *value = ast_json_object_iter_value(field);
1140
1141                 if (!strcmp(ast_json_object_iter_key(field), "bob")) {
1142                         if (strcmp(ast_json_string_get(value), "5")) {
1143                                 ast_test_status_update(test, "Object set failed to create proper value for 'bob'\n");
1144                                 res = AST_TEST_FAIL;
1145                         }
1146                 } else if (!strcmp(ast_json_object_iter_key(field), "joe")) {
1147                         if (strcmp(ast_json_string_get(value), "10")) {
1148                                 ast_test_status_update(test, "Object set failed to create proper value for 'joe'\n");
1149                                 res = AST_TEST_FAIL;
1150                         }
1151                 } else if (!strcmp(ast_json_object_iter_key(field), "jim")) {
1152                         if (strcmp(ast_json_string_get(value), "444")) {
1153                                 ast_test_status_update(test, "Object set failed to create proper value for 'jim'\n");
1154                                 res = AST_TEST_FAIL;
1155                         }
1156                 } else if (!strcmp(ast_json_object_iter_key(field), "jack")) {
1157                         if (strcmp(ast_json_string_get(value), "888,999")) {
1158                                 ast_test_status_update(test, "Object set failed to create proper value for 'jack'\n");
1159                                 res = AST_TEST_FAIL;
1160                         }
1161                 } else {
1162                         ast_test_status_update(test, "Object set created field '%s' which is unknown\n", ast_json_object_iter_key(field));
1163                         res = AST_TEST_FAIL;
1164                 }
1165         }
1166
1167         return res;
1168 }
1169
1170 AST_TEST_DEFINE(objectset_create_regex)
1171 {
1172         int res = AST_TEST_PASS;
1173         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1174         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1175         RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
1176         struct ast_variable *field;
1177
1178         switch (cmd) {
1179         case TEST_INIT:
1180                 info->name = "objectset_create_regex";
1181                 info->category = "/main/sorcery/";
1182                 info->summary = "sorcery object set creation with regex fields unit test";
1183                 info->description =
1184                         "Test object set creation with regex fields in sorcery";
1185                 return AST_TEST_NOT_RUN;
1186         case TEST_EXECUTE:
1187                 break;
1188         }
1189
1190         if (!(sorcery = ast_sorcery_open())) {
1191                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1192                 return AST_TEST_FAIL;
1193         }
1194
1195         if ((ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) ||
1196             ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, test_apply_handler)) {
1197                 ast_test_status_update(test, "Failed to register 'test' object type\n");
1198                 return AST_TEST_FAIL;
1199         }
1200
1201         ast_sorcery_object_fields_register(sorcery, "test", "^toast-", test_sorcery_regex_handler, test_sorcery_regex_fields);
1202
1203         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1204                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1205                 return AST_TEST_FAIL;
1206         }
1207
1208         if (!(objset = ast_sorcery_objectset_create(sorcery, obj))) {
1209                 ast_test_status_update(test, "Failed to create an object set for a known sane object\n");
1210                 return AST_TEST_FAIL;
1211         }
1212
1213         for (field = objset; field; field = field->next) {
1214                 if (!strcmp(field->name, "toast-bob")) {
1215                         if (strcmp(field->value, "10")) {
1216                                 ast_test_status_update(test, "Object set failed to create proper value for 'bob'\n");
1217                                 res = AST_TEST_FAIL;
1218                         }
1219                 } else {
1220                         ast_test_status_update(test, "Object set created field '%s' which is unknown\n", field->name);
1221                         res = AST_TEST_FAIL;
1222                 }
1223         }
1224
1225         return res;
1226 }
1227
1228 AST_TEST_DEFINE(objectset_apply)
1229 {
1230         int res = AST_TEST_PASS;
1231         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1232         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1233         RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
1234
1235         switch (cmd) {
1236         case TEST_INIT:
1237                 info->name = "objectset_apply";
1238                 info->category = "/main/sorcery/";
1239                 info->summary = "sorcery object apply unit test";
1240                 info->description =
1241                         "Test object set applying in sorcery";
1242                 return AST_TEST_NOT_RUN;
1243         case TEST_EXECUTE:
1244                 break;
1245         }
1246
1247         if (!(sorcery = alloc_and_initialize_sorcery())) {
1248                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1249                 return AST_TEST_FAIL;
1250         }
1251
1252         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1253                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1254                 return AST_TEST_FAIL;
1255         }
1256
1257         if (!(objset = ast_variable_new("joe", "25", ""))) {
1258                 ast_test_status_update(test, "Failed to create an object set, test could not occur\n");
1259                 res = AST_TEST_FAIL;
1260         } else if (ast_sorcery_objectset_apply(sorcery, obj, objset)) {
1261                 ast_test_status_update(test, "Failed to apply valid object set to object\n");
1262                 res = AST_TEST_FAIL;
1263         } else if (obj->joe != 25) {
1264                 ast_test_status_update(test, "Object set was not actually applied to object despite it returning success\n");
1265                 res = AST_TEST_FAIL;
1266         }
1267
1268         return res;
1269 }
1270
1271 AST_TEST_DEFINE(objectset_apply_handler)
1272 {
1273         int res = AST_TEST_PASS;
1274         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1275         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1276         RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
1277
1278         switch (cmd) {
1279         case TEST_INIT:
1280                 info->name = "objectset_apply_handler";
1281                 info->category = "/main/sorcery/";
1282                 info->summary = "sorcery object apply handler unit test";
1283                 info->description =
1284                         "Test object set apply handler call in sorcery";
1285                 return AST_TEST_NOT_RUN;
1286         case TEST_EXECUTE:
1287                 break;
1288         }
1289
1290         if (!(sorcery = ast_sorcery_open())) {
1291                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1292                 return AST_TEST_FAIL;
1293         }
1294
1295         if ((ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) ||
1296             ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, test_apply_handler)) {
1297                 ast_test_status_update(test, "Failed to register 'test' object type\n");
1298                 return AST_TEST_FAIL;
1299         }
1300
1301         ast_sorcery_object_field_register_nodoc(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
1302         ast_sorcery_object_field_register_nodoc(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
1303
1304         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1305                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1306                 return AST_TEST_FAIL;
1307         }
1308
1309         apply_handler_called = 0;
1310
1311         if (!(objset = ast_variable_new("joe", "25", ""))) {
1312                 ast_test_status_update(test, "Failed to create an object set, test could not occur\n");
1313                 res = AST_TEST_FAIL;
1314         } else if (ast_sorcery_objectset_apply(sorcery, obj, objset)) {
1315                 ast_test_status_update(test, "Failed to apply valid object set to object\n");
1316                 res = AST_TEST_FAIL;
1317         } else if (!apply_handler_called) {
1318                 ast_test_status_update(test, "Apply handler was not called when it should have been\n");
1319                 res = AST_TEST_FAIL;
1320         }
1321
1322         return res;
1323 }
1324
1325 AST_TEST_DEFINE(objectset_apply_invalid)
1326 {
1327         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1328         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1329         RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
1330
1331         switch (cmd) {
1332         case TEST_INIT:
1333                 info->name = "objectset_apply_invalid";
1334                 info->category = "/main/sorcery/";
1335                 info->summary = "sorcery object invalid apply unit test";
1336                 info->description =
1337                         "Test object set applying of an invalid set in sorcery";
1338                 return AST_TEST_NOT_RUN;
1339         case TEST_EXECUTE:
1340                 break;
1341         }
1342
1343         if (!(sorcery = alloc_and_initialize_sorcery())) {
1344                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1345                 return AST_TEST_FAIL;
1346         }
1347
1348         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1349                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1350                 return AST_TEST_FAIL;
1351         }
1352
1353         if (!(objset = ast_variable_new("fred", "99", ""))) {
1354                 ast_test_status_update(test, "Failed to create an object set, test could not occur\n");
1355                 return AST_TEST_FAIL;
1356         } else if (!ast_sorcery_objectset_apply(sorcery, obj, objset)) {
1357                 ast_test_status_update(test, "Successfully applied an invalid object set\n");
1358                 return AST_TEST_FAIL;
1359         } else if ((obj->bob != 5) || (obj->joe != 10)) {
1360                 ast_test_status_update(test, "Object set modified object fields when it should not have\n");
1361                 return AST_TEST_FAIL;
1362         }
1363
1364         return AST_TEST_PASS;
1365 }
1366
1367 AST_TEST_DEFINE(objectset_transform)
1368 {
1369         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1370         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1371         RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
1372
1373         switch (cmd) {
1374         case TEST_INIT:
1375                 info->name = "objectset_transform";
1376                 info->category = "/main/sorcery/";
1377                 info->summary = "sorcery object set transformation unit test";
1378                 info->description =
1379                         "Test object set transformation in sorcery";
1380                 return AST_TEST_NOT_RUN;
1381         case TEST_EXECUTE:
1382                 break;
1383         }
1384
1385         if (!(sorcery = ast_sorcery_open())) {
1386                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1387                 return AST_TEST_FAIL;
1388         }
1389
1390         if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) {
1391                 ast_test_status_update(test, "Failed to set a known wizard as a default\n");
1392                 return AST_TEST_FAIL;
1393         }
1394
1395         if (ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, test_sorcery_transform, NULL)) {
1396                 ast_test_status_update(test, "Failed to register object type\n");
1397                 return AST_TEST_FAIL;
1398         }
1399
1400         ast_sorcery_object_field_register_nodoc(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
1401         ast_sorcery_object_field_register_nodoc(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
1402
1403         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1404                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1405                 return AST_TEST_FAIL;
1406         }
1407
1408         if (!(objset = ast_sorcery_objectset_create(sorcery, obj))) {
1409                 ast_test_status_update(test, "Failed to create an object set for a known sane object\n");
1410                 return AST_TEST_FAIL;
1411         }
1412
1413         if (ast_sorcery_objectset_apply(sorcery, obj, objset)) {
1414                 ast_test_status_update(test, "Failed to apply properly created object set against object\n");
1415                 return AST_TEST_FAIL;
1416         }
1417
1418         if (obj->bob != 5) {
1419                 ast_test_status_update(test, "Application of object set produced incorrect value on 'bob'\n");
1420                 return AST_TEST_FAIL;
1421         } else if (obj->joe == 10) {
1422                 ast_test_status_update(test, "Transformation callback did not change value of 'joe' from provided value\n");
1423                 return AST_TEST_FAIL;
1424         } else if (obj->joe != 5000) {
1425                 ast_test_status_update(test, "Value of 'joe' differs from default AND from transformation value\n");
1426                 return AST_TEST_FAIL;
1427         }
1428
1429         return AST_TEST_PASS;
1430 }
1431
1432 AST_TEST_DEFINE(objectset_apply_fields)
1433 {
1434         int res = AST_TEST_PASS;
1435         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1436         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1437         RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
1438
1439         switch (cmd) {
1440         case TEST_INIT:
1441                 info->name = "objectset_apply_fields";
1442                 info->category = "/main/sorcery/";
1443                 info->summary = "sorcery object apply regex fields unit test";
1444                 info->description =
1445                         "Test object set apply with regex fields in sorcery";
1446                 return AST_TEST_NOT_RUN;
1447         case TEST_EXECUTE:
1448                 break;
1449         }
1450
1451         if (!(sorcery = ast_sorcery_open())) {
1452                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1453                 return AST_TEST_FAIL;
1454         }
1455
1456         if ((ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) ||
1457             ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, test_apply_handler)) {
1458                 ast_test_status_update(test, "Failed to register 'test' object type\n");
1459                 return AST_TEST_FAIL;
1460         }
1461
1462         ast_sorcery_object_fields_register(sorcery, "test", "^toast-", test_sorcery_regex_handler, test_sorcery_regex_fields);
1463
1464         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1465                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1466                 return AST_TEST_FAIL;
1467         }
1468
1469         if (!(objset = ast_variable_new("toast-bob", "20", ""))) {
1470                 ast_test_status_update(test, "Failed to create an object set, test could not occur\n");
1471                 res = AST_TEST_FAIL;
1472         } else if (ast_sorcery_objectset_apply(sorcery, obj, objset)) {
1473                 ast_test_status_update(test, "Failed to apply valid object set to object\n");
1474                 res = AST_TEST_FAIL;
1475         } else if (obj->bob != 256) {
1476                 ast_test_status_update(test, "Regex field handler was not called when it should have been\n");
1477                 res = AST_TEST_FAIL;
1478         }
1479
1480         return res;
1481 }
1482
1483 AST_TEST_DEFINE(extended_fields)
1484 {
1485         int res = AST_TEST_PASS;
1486         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1487         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1488         RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
1489         const char *value;
1490
1491         switch (cmd) {
1492         case TEST_INIT:
1493                 info->name = "extended_fields";
1494                 info->category = "/main/sorcery/";
1495                 info->summary = "sorcery object extended fields unit test";
1496                 info->description =
1497                         "Test extended fields support in sorcery";
1498                 return AST_TEST_NOT_RUN;
1499         case TEST_EXECUTE:
1500                 break;
1501         }
1502
1503         if (!(sorcery = alloc_and_initialize_sorcery())) {
1504                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1505                 return AST_TEST_FAIL;
1506         }
1507
1508         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1509                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1510                 return AST_TEST_FAIL;
1511         }
1512
1513         if (!(objset = ast_variable_new("@testing", "toast", ""))) {
1514                 ast_test_status_update(test, "Failed to create an object set, test could not occur\n");
1515                 res = AST_TEST_FAIL;
1516         } else if (ast_sorcery_objectset_apply(sorcery, obj, objset)) {
1517                 ast_test_status_update(test, "Failed to apply valid object set to object\n");
1518                 res = AST_TEST_FAIL;
1519         } else if (!(value = ast_sorcery_object_get_extended(obj, "testing"))) {
1520                 ast_test_status_update(test, "Extended field, which was set using object set, could not be found\n");
1521                 res = AST_TEST_FAIL;
1522         } else if (strcmp(value, "toast")) {
1523                 ast_test_status_update(test, "Extended field does not contain expected value\n");
1524                 res = AST_TEST_FAIL;
1525         } else if (ast_sorcery_object_set_extended(obj, "@tacos", "supreme")) {
1526                 ast_test_status_update(test, "Extended field could not be set\n");
1527                 res = AST_TEST_FAIL;
1528         } else if (!(value = ast_sorcery_object_get_extended(obj, "tacos"))) {
1529                 ast_test_status_update(test, "Extended field, which was set using the API, could not be found\n");
1530                 res = AST_TEST_FAIL;
1531         } else if (strcmp(value, "supreme")) {
1532                 ast_test_status_update(test, "Extended field does not contain expected value\n");
1533                 res = AST_TEST_FAIL;
1534         } else if (ast_sorcery_object_set_extended(obj, "@tacos", "canadian")) {
1535                 ast_test_status_update(test, "Extended field could not be set a second time\n");
1536                 res = AST_TEST_FAIL;
1537         } else if (!(value = ast_sorcery_object_get_extended(obj, "tacos"))) {
1538                 ast_test_status_update(test, "Extended field, which was set using the API, could not be found\n");
1539                 res = AST_TEST_FAIL;
1540         } else if (strcmp(value, "canadian")) {
1541                 ast_test_status_update(test, "Extended field does not contain expected value\n");
1542                 res = AST_TEST_FAIL;
1543         }
1544
1545         return res;
1546 }
1547
1548 AST_TEST_DEFINE(changeset_create)
1549 {
1550         int res = AST_TEST_PASS;
1551         RAII_VAR(struct ast_variable *, original, NULL, ast_variables_destroy);
1552         RAII_VAR(struct ast_variable *, modified, NULL, ast_variables_destroy);
1553         RAII_VAR(struct ast_variable *, changes, NULL, ast_variables_destroy);
1554         struct ast_variable *tmp;
1555
1556         switch (cmd) {
1557         case TEST_INIT:
1558                 info->name = "changeset_create";
1559                 info->category = "/main/sorcery/";
1560                 info->summary = "sorcery changeset creation unit test";
1561                 info->description =
1562                         "Test changeset creation in sorcery";
1563                 return AST_TEST_NOT_RUN;
1564         case TEST_EXECUTE:
1565                 break;
1566         }
1567
1568         if (!(tmp = ast_variable_new("bananas", "purple", ""))) {
1569                 ast_test_status_update(test, "Failed to create first field for original objectset\n");
1570                 return AST_TEST_FAIL;
1571         }
1572         tmp->next = original;
1573         original = tmp;
1574
1575         if (!(tmp = ast_variable_new("apples", "orange", ""))) {
1576                 ast_test_status_update(test, "Failed to create second field for original objectset\n");
1577                 return AST_TEST_FAIL;
1578         }
1579         tmp->next = original;
1580         original = tmp;
1581
1582         if (!(tmp = ast_variable_new("bananas", "green", ""))) {
1583                 ast_test_status_update(test, "Failed to create first field for modified objectset\n");
1584                 return AST_TEST_FAIL;
1585         }
1586         tmp->next = modified;
1587         modified = tmp;
1588
1589         if (!(tmp = ast_variable_new("apples", "orange", ""))) {
1590                 ast_test_status_update(test, "Failed to create second field for modified objectset\n");
1591                 return AST_TEST_FAIL;
1592         }
1593         tmp->next = modified;
1594         modified = tmp;
1595
1596         if (ast_sorcery_changeset_create(original, modified, &changes)) {
1597                 ast_test_status_update(test, "Failed to create a changeset due to an error\n");
1598                 return AST_TEST_FAIL;
1599         } else if (!changes) {
1600                 ast_test_status_update(test, "Failed to produce a changeset when there should be one\n");
1601                 return AST_TEST_FAIL;
1602         }
1603
1604         for (tmp = changes; tmp; tmp = tmp->next) {
1605                 if (!strcmp(tmp->name, "bananas")) {
1606                         if (strcmp(tmp->value, "green")) {
1607                                 ast_test_status_update(test, "Changeset produced had unexpected value '%s' for bananas\n", tmp->value);
1608                                 res = AST_TEST_FAIL;
1609                         }
1610                 } else {
1611                         ast_test_status_update(test, "Changeset produced had unexpected field '%s'\n", tmp->name);
1612                         res = AST_TEST_FAIL;
1613                 }
1614         }
1615
1616         return res;
1617 }
1618
1619 AST_TEST_DEFINE(changeset_create_unchanged)
1620 {
1621         RAII_VAR(struct ast_variable *, original, NULL, ast_variables_destroy);
1622         RAII_VAR(struct ast_variable *, changes, NULL, ast_variables_destroy);
1623         RAII_VAR(struct ast_variable *, same, NULL, ast_variables_destroy);
1624         struct ast_variable *tmp;
1625
1626         switch (cmd) {
1627         case TEST_INIT:
1628                 info->name = "changeset_create_unchanged";
1629                 info->category = "/main/sorcery/";
1630                 info->summary = "sorcery changeset creation unit test when no changes exist";
1631                 info->description =
1632                         "Test changeset creation in sorcery when no changes actually exist";
1633                 return AST_TEST_NOT_RUN;
1634         case TEST_EXECUTE:
1635                 break;
1636         }
1637
1638         if (!(tmp = ast_variable_new("bananas", "purple", ""))) {
1639                 ast_test_status_update(test, "Failed to create first field for original objectset\n");
1640                 return AST_TEST_FAIL;
1641         }
1642         tmp->next = original;
1643         original = tmp;
1644
1645         if (!(tmp = ast_variable_new("apples", "orange", ""))) {
1646                 ast_test_status_update(test, "Failed to create second field for original objectset\n");
1647                 return AST_TEST_FAIL;
1648         }
1649         tmp->next = original;
1650         original = tmp;
1651
1652         if (ast_sorcery_changeset_create(original, original, &changes)) {
1653                 ast_test_status_update(test, "Failed to create a changeset due to an error\n");
1654                 return AST_TEST_FAIL;
1655         } else if (changes) {
1656                 ast_test_status_update(test, "Created a changeset when no changes actually exist\n");
1657                 return AST_TEST_FAIL;
1658         }
1659
1660         if (!(tmp = ast_variable_new("bananas", "purple", ""))) {
1661                 ast_test_status_update(test, "Failed to create first field for same objectset\n");
1662                 return AST_TEST_FAIL;
1663         }
1664         tmp->next = same;
1665         same = tmp;
1666
1667         if (!(tmp = ast_variable_new("apples", "orange", ""))) {
1668                 ast_test_status_update(test, "Failed to create second field for same objectset\n");
1669                 return AST_TEST_FAIL;
1670         }
1671         tmp->next = same;
1672         same = tmp;
1673
1674         if (ast_sorcery_changeset_create(original, same, &changes)) {
1675                 ast_test_status_update(test, "Failed to create a changeset due to an error\n");
1676                 return AST_TEST_FAIL;
1677         } else if (changes) {
1678                 ast_test_status_update(test, "Created a changeset between two different objectsets when no changes actually exist\n");
1679                 return AST_TEST_FAIL;
1680         }
1681
1682         return AST_TEST_PASS;
1683 }
1684
1685 AST_TEST_DEFINE(object_create)
1686 {
1687         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1688         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1689
1690         switch (cmd) {
1691         case TEST_INIT:
1692                 info->name = "object_create";
1693                 info->category = "/main/sorcery/";
1694                 info->summary = "sorcery object creation unit test";
1695                 info->description =
1696                         "Test object creation in sorcery";
1697                 return AST_TEST_NOT_RUN;
1698         case TEST_EXECUTE:
1699                 break;
1700         }
1701
1702         if (!(sorcery = alloc_and_initialize_sorcery())) {
1703                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1704                 return AST_TEST_FAIL;
1705         }
1706
1707         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1708                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1709                 return AST_TEST_FAIL;
1710         }
1711
1712         if (ast_sorcery_create(sorcery, obj)) {
1713                 ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
1714                 return AST_TEST_FAIL;
1715         }
1716
1717         return AST_TEST_PASS;
1718 }
1719
1720 AST_TEST_DEFINE(object_retrieve_id)
1721 {
1722         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1723         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1724
1725         switch (cmd) {
1726         case TEST_INIT:
1727                 info->name = "object_retrieve_id";
1728                 info->category = "/main/sorcery/";
1729                 info->summary = "sorcery object retrieval using id unit test";
1730                 info->description =
1731                         "Test object retrieval using id in sorcery";
1732                 return AST_TEST_NOT_RUN;
1733         case TEST_EXECUTE:
1734                 break;
1735         }
1736
1737         if (!(sorcery = alloc_and_initialize_sorcery())) {
1738                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1739                 return AST_TEST_FAIL;
1740         }
1741
1742         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1743                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1744                 return AST_TEST_FAIL;
1745         }
1746
1747         if (ast_sorcery_create(sorcery, obj)) {
1748                 ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
1749                 return AST_TEST_FAIL;
1750         }
1751
1752         ao2_cleanup(obj);
1753
1754         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah2"))) {
1755                 ast_test_status_update(test, "Failed to allocate second instance of a known object type\n");
1756                 return AST_TEST_FAIL;
1757         }
1758
1759         if (ast_sorcery_create(sorcery, obj)) {
1760                 ast_test_status_update(test, "Failed to create second object using in-memory wizard\n");
1761                 return AST_TEST_FAIL;
1762         }
1763
1764         ao2_cleanup(obj);
1765
1766         if (!(obj = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
1767                 ast_test_status_update(test, "Failed to retrieve properly created object using id of 'blah'\n");
1768                 return AST_TEST_FAIL;
1769         } else if (strcmp(ast_sorcery_object_get_id(obj), "blah")) {
1770                 ast_test_status_update(test, "Retrieved object does not have correct id\n");
1771                 return AST_TEST_FAIL;
1772         }
1773
1774         return AST_TEST_PASS;
1775 }
1776
1777 AST_TEST_DEFINE(object_retrieve_field)
1778 {
1779         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1780         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1781         RAII_VAR(struct ast_variable *, fields, ast_variable_new("joe", "42", ""), ast_variables_destroy);
1782
1783         switch (cmd) {
1784         case TEST_INIT:
1785                 info->name = "object_retrieve_field";
1786                 info->category = "/main/sorcery/";
1787                 info->summary = "sorcery object retrieval using a specific field unit test";
1788                 info->description =
1789                         "Test object retrieval using a specific field in sorcery";
1790                 return AST_TEST_NOT_RUN;
1791         case TEST_EXECUTE:
1792                 break;
1793         }
1794
1795         if (!fields) {
1796                 ast_test_status_update(test, "Failed to create fields for object retrieval attempt\n");
1797                 return AST_TEST_FAIL;
1798         }
1799
1800         if (!(sorcery = alloc_and_initialize_sorcery())) {
1801                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1802                 return AST_TEST_FAIL;
1803         }
1804
1805         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1806                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1807                 return AST_TEST_FAIL;
1808         }
1809
1810         obj->joe = 42;
1811
1812         if (ast_sorcery_create(sorcery, obj)) {
1813                 ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
1814                 return AST_TEST_FAIL;
1815         }
1816
1817         ao2_cleanup(obj);
1818
1819         if (!(obj = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_DEFAULT, fields))) {
1820                 ast_test_status_update(test, "Failed to retrieve properly created object using 'joe' field\n");
1821                 return AST_TEST_FAIL;
1822         }
1823
1824         ao2_cleanup(obj);
1825         ast_variables_destroy(fields);
1826
1827         if (!(fields = ast_variable_new("joe", "49", ""))) {
1828                 ast_test_status_update(test, "Failed to create fields for object retrieval attempt\n");
1829                 return AST_TEST_FAIL;
1830         }
1831
1832         if ((obj = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_DEFAULT, fields))) {
1833                 ast_test_status_update(test, "Retrieved an object using a field with an in-correct value... that should not happen\n");
1834                 return AST_TEST_FAIL;
1835         }
1836
1837         return AST_TEST_PASS;
1838 }
1839
1840 AST_TEST_DEFINE(object_retrieve_multiple_all)
1841 {
1842         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1843         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1844         RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
1845
1846         switch (cmd) {
1847         case TEST_INIT:
1848                 info->name = "object_retrieve_multiple_all";
1849                 info->category = "/main/sorcery/";
1850                 info->summary = "sorcery multiple object retrieval unit test";
1851                 info->description =
1852                         "Test multiple object retrieval in sorcery";
1853                 return AST_TEST_NOT_RUN;
1854         case TEST_EXECUTE:
1855                 break;
1856         }
1857
1858         if (!(sorcery = alloc_and_initialize_sorcery())) {
1859                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1860                 return AST_TEST_FAIL;
1861         }
1862
1863         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1864                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1865                 return AST_TEST_FAIL;
1866         }
1867
1868         if (ast_sorcery_create(sorcery, obj)) {
1869                 ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
1870                 return AST_TEST_FAIL;
1871         }
1872
1873         ao2_cleanup(obj);
1874
1875         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah2"))) {
1876                 ast_test_status_update(test, "Failed to allocate second instance of a known object type\n");
1877                 return AST_TEST_FAIL;
1878         }
1879
1880         if (ast_sorcery_create(sorcery, obj)) {
1881                 ast_test_status_update(test, "Failed to create second object using in-memory wizard\n");
1882                 return AST_TEST_FAIL;
1883         }
1884
1885         if (!(objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL))) {
1886                 ast_test_status_update(test, "Failed to retrieve a container of all objects\n");
1887                 return AST_TEST_FAIL;
1888         } else if (ao2_container_count(objects) != 2) {
1889                 ast_test_status_update(test, "Received a container with no objects in it when there should be some\n");
1890                 return AST_TEST_FAIL;
1891         }
1892
1893         return AST_TEST_PASS;
1894 }
1895
1896 AST_TEST_DEFINE(object_retrieve_multiple_field)
1897 {
1898         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1899         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1900         RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
1901         RAII_VAR(struct ast_variable *, fields, ast_variable_new("joe", "6", ""), ast_variables_destroy);
1902
1903         switch (cmd) {
1904         case TEST_INIT:
1905                 info->name = "object_retrieve_multiple_field";
1906                 info->category = "/main/sorcery/";
1907                 info->summary = "sorcery multiple object retrieval unit test";
1908                 info->description =
1909                         "Test multiple object retrieval in sorcery using fields";
1910                 return AST_TEST_NOT_RUN;
1911         case TEST_EXECUTE:
1912                 break;
1913         }
1914
1915         if (!fields) {
1916                 ast_test_status_update(test, "Failed to create fields for multiple retrieve\n");
1917                 return AST_TEST_FAIL;
1918         }
1919
1920         if (!(sorcery = alloc_and_initialize_sorcery())) {
1921                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1922                 return AST_TEST_FAIL;
1923         }
1924
1925         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
1926                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1927                 return AST_TEST_FAIL;
1928         }
1929
1930         obj->joe = 6;
1931
1932         if (ast_sorcery_create(sorcery, obj)) {
1933                 ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
1934                 return AST_TEST_FAIL;
1935         }
1936
1937         if (!(objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE, fields))) {
1938                 ast_test_status_update(test, "Failed to retrieve a container of all objects\n");
1939                 return AST_TEST_FAIL;
1940         } else if (ao2_container_count(objects) != 1) {
1941                 ast_test_status_update(test, "Received a container with no objects in it when there should be some\n");
1942                 return AST_TEST_FAIL;
1943         }
1944
1945         ao2_cleanup(objects);
1946         ast_variables_destroy(fields);
1947
1948         if (!(fields = ast_variable_new("joe", "7", ""))) {
1949                 ast_test_status_update(test, "Failed to create fields for multiple retrieval\n");
1950                 return AST_TEST_FAIL;
1951         } else if (!(objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE, fields))) {
1952                 ast_test_status_update(test, "Failed to retrieve an empty container when retrieving multiple\n");
1953                 return AST_TEST_FAIL;
1954         } else if (ao2_container_count(objects)) {
1955                 ast_test_status_update(test, "Received a container with objects when there should be none in it\n");
1956                 return AST_TEST_FAIL;
1957         }
1958
1959         return AST_TEST_PASS;
1960 }
1961
1962 AST_TEST_DEFINE(object_retrieve_regex)
1963 {
1964         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
1965         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
1966         RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
1967
1968         switch (cmd) {
1969         case TEST_INIT:
1970                 info->name = "object_retrieve_regex";
1971                 info->category = "/main/sorcery/";
1972                 info->summary = "sorcery multiple object retrieval using regex unit test";
1973                 info->description =
1974                         "Test multiple object retrieval in sorcery using regular expression for matching";
1975                 return AST_TEST_NOT_RUN;
1976         case TEST_EXECUTE:
1977                 break;
1978         }
1979
1980         if (!(sorcery = alloc_and_initialize_sorcery())) {
1981                 ast_test_status_update(test, "Failed to open sorcery structure\n");
1982                 return AST_TEST_FAIL;
1983         }
1984
1985         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah-98joe"))) {
1986                 ast_test_status_update(test, "Failed to allocate a known object type\n");
1987                 return AST_TEST_FAIL;
1988         }
1989
1990         if (ast_sorcery_create(sorcery, obj)) {
1991                 ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
1992                 return AST_TEST_FAIL;
1993         }
1994
1995         ao2_cleanup(obj);
1996
1997         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah-93joe"))) {
1998                 ast_test_status_update(test, "Failed to allocate second instance of a known object type\n");
1999                 return AST_TEST_FAIL;
2000         }
2001
2002         if (ast_sorcery_create(sorcery, obj)) {
2003                 ast_test_status_update(test, "Failed to create second object using in-memory wizard\n");
2004                 return AST_TEST_FAIL;
2005         }
2006
2007         ao2_cleanup(obj);
2008
2009         if (!(obj = ast_sorcery_alloc(sorcery, "test", "neener-93joe"))) {
2010                 ast_test_status_update(test, "Failed to allocate third instance of a known object type\n");
2011                 return AST_TEST_FAIL;
2012         }
2013
2014         if (ast_sorcery_create(sorcery, obj)) {
2015                 ast_test_status_update(test, "Failed to create third object using in-memory wizard\n");
2016                 return AST_TEST_FAIL;
2017         }
2018
2019         if (!(objects = ast_sorcery_retrieve_by_regex(sorcery, "test", "^blah-"))) {
2020                 ast_test_status_update(test, "Failed to retrieve a container of objects\n");
2021                 return AST_TEST_FAIL;
2022         } else if (ao2_container_count(objects) != 2) {
2023                 ast_test_status_update(test, "Received a container with incorrect number of objects in it\n");
2024                 return AST_TEST_FAIL;
2025         }
2026
2027         return AST_TEST_PASS;
2028 }
2029
2030 AST_TEST_DEFINE(object_update)
2031 {
2032         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
2033         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
2034         RAII_VAR(struct test_sorcery_object *, obj2, NULL, ao2_cleanup);
2035
2036         switch (cmd) {
2037         case TEST_INIT:
2038                 info->name = "object_update";
2039                 info->category = "/main/sorcery/";
2040                 info->summary = "sorcery object update unit test";
2041                 info->description =
2042                         "Test object updating in sorcery";
2043                 return AST_TEST_NOT_RUN;
2044         case TEST_EXECUTE:
2045                 break;
2046         }
2047
2048         if (!(sorcery = alloc_and_initialize_sorcery())) {
2049                 ast_test_status_update(test, "Failed to open sorcery structure\n");
2050                 return AST_TEST_FAIL;
2051         }
2052
2053         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
2054                 ast_test_status_update(test, "Failed to allocate a known object type\n");
2055                 return AST_TEST_FAIL;
2056         }
2057
2058         if (ast_sorcery_create(sorcery, obj)) {
2059                 ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
2060                 return AST_TEST_FAIL;
2061         }
2062
2063         if (!(obj2 = ast_sorcery_copy(sorcery, obj))) {
2064                 ast_test_status_update(test, "Failed to allocate a known object type for updating\n");
2065                 return AST_TEST_FAIL;
2066         }
2067
2068         ao2_cleanup(obj);
2069
2070         if (ast_sorcery_update(sorcery, obj2)) {
2071                 ast_test_status_update(test, "Failed to update sorcery with new object\n");
2072                 return AST_TEST_FAIL;
2073         }
2074
2075         if (!(obj = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
2076                 ast_test_status_update(test, "Failed to retrieve properly updated object\n");
2077                 return AST_TEST_FAIL;
2078         } else if (obj != obj2) {
2079                 ast_test_status_update(test, "Object retrieved is not the updated object\n");
2080                 return AST_TEST_FAIL;
2081         }
2082
2083         return AST_TEST_PASS;
2084 }
2085
2086 AST_TEST_DEFINE(object_update_uncreated)
2087 {
2088         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
2089         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
2090
2091         switch (cmd) {
2092         case TEST_INIT:
2093                 info->name = "object_update_uncreated";
2094                 info->category = "/main/sorcery/";
2095                 info->summary = "sorcery object update unit test";
2096                 info->description =
2097                         "Test updating of an uncreated object in sorcery";
2098                 return AST_TEST_NOT_RUN;
2099         case TEST_EXECUTE:
2100                 break;
2101         }
2102
2103         if (!(sorcery = alloc_and_initialize_sorcery())) {
2104                 ast_test_status_update(test, "Failed to open sorcery structure\n");
2105                 return AST_TEST_FAIL;
2106         }
2107
2108         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
2109                 ast_test_status_update(test, "Failed to allocate a known object type\n");
2110                 return AST_TEST_FAIL;
2111         }
2112
2113         if (!ast_sorcery_update(sorcery, obj)) {
2114                 ast_test_status_update(test, "Successfully updated an object which has not been created yet\n");
2115                 return AST_TEST_FAIL;
2116         }
2117
2118         return AST_TEST_PASS;
2119 }
2120
2121 AST_TEST_DEFINE(object_delete)
2122 {
2123         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
2124         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
2125
2126         switch (cmd) {
2127         case TEST_INIT:
2128                 info->name = "object_delete";
2129                 info->category = "/main/sorcery/";
2130                 info->summary = "sorcery object deletion unit test";
2131                 info->description =
2132                         "Test object deletion in sorcery";
2133                 return AST_TEST_NOT_RUN;
2134         case TEST_EXECUTE:
2135                 break;
2136         }
2137
2138         if (!(sorcery = alloc_and_initialize_sorcery())) {
2139                 ast_test_status_update(test, "Failed to open sorcery structure\n");
2140                 return AST_TEST_FAIL;
2141         }
2142
2143         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
2144                 ast_test_status_update(test, "Failed to allocate a known object type\n");
2145                 return AST_TEST_FAIL;
2146         }
2147
2148         if (ast_sorcery_create(sorcery, obj)) {
2149                 ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
2150                 return AST_TEST_FAIL;
2151         }
2152
2153         if (ast_sorcery_delete(sorcery, obj)) {
2154                 ast_test_status_update(test, "Failed to delete object using in-memory wizard\n");
2155                 return AST_TEST_FAIL;
2156         }
2157
2158         ao2_cleanup(obj);
2159
2160         if ((obj = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
2161                 ast_test_status_update(test, "Retrieved deleted object that should not be there\n");
2162                 return AST_TEST_FAIL;
2163         }
2164
2165         return AST_TEST_PASS;
2166 }
2167
2168 AST_TEST_DEFINE(object_delete_uncreated)
2169 {
2170         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
2171         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
2172
2173         switch (cmd) {
2174         case TEST_INIT:
2175                 info->name = "object_delete_uncreated";
2176                 info->category = "/main/sorcery/";
2177                 info->summary = "sorcery object deletion unit test";
2178                 info->description =
2179                         "Test object deletion of an uncreated object in sorcery";
2180                 return AST_TEST_NOT_RUN;
2181         case TEST_EXECUTE:
2182                 break;
2183         }
2184
2185         if (!(sorcery = alloc_and_initialize_sorcery())) {
2186                 ast_test_status_update(test, "Failed to open sorcery structure\n");
2187                 return AST_TEST_FAIL;
2188         }
2189
2190         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
2191                 ast_test_status_update(test, "Failed to allocate a known object type\n");
2192                 return AST_TEST_FAIL;
2193         }
2194
2195         if (!ast_sorcery_delete(sorcery, obj)) {
2196                 ast_test_status_update(test, "Successfully deleted an object which was never created\n");
2197                 return AST_TEST_FAIL;
2198         }
2199
2200         return AST_TEST_PASS;
2201 }
2202
2203 AST_TEST_DEFINE(caching_wizard_behavior)
2204 {
2205         struct ast_flags flags = { CONFIG_FLAG_NOCACHE };
2206         struct ast_config *config;
2207         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
2208         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
2209         RAII_VAR(struct test_sorcery_object *, obj2, NULL, ao2_cleanup);
2210         int res = AST_TEST_FAIL;
2211
2212         switch (cmd) {
2213         case TEST_INIT:
2214                 info->name = "caching_wizard_behavior";
2215                 info->category = "/main/sorcery/";
2216                 info->summary = "sorcery caching wizard behavior unit test";
2217                 info->description =
2218                         "Test internal behavior of caching wizards";
2219                 return AST_TEST_NOT_RUN;
2220         case TEST_EXECUTE:
2221                 break;
2222         }
2223
2224         if (!(config = ast_config_load2("sorcery.conf", "test_sorcery_cache", flags))) {
2225                 ast_test_status_update(test, "Sorcery configuration file not present - skipping caching_wizard_behavior test\n");
2226                 return AST_TEST_NOT_RUN;
2227         }
2228
2229         if (!ast_category_get(config, "test_sorcery_cache")) {
2230                 ast_test_status_update(test, "Sorcery configuration file does not contain 'test_sorcery_cache' section\n");
2231                 ast_config_destroy(config);
2232                 return AST_TEST_NOT_RUN;
2233         }
2234
2235         ast_config_destroy(config);
2236
2237         if (ast_sorcery_wizard_register(&test_wizard)) {
2238                 ast_test_status_update(test, "Failed to register a perfectly valid sorcery wizard\n");
2239                 return AST_TEST_FAIL;
2240         }
2241
2242         if (!(sorcery = ast_sorcery_open())) {
2243                 ast_test_status_update(test, "Failed to open sorcery structure\n");
2244                 goto end;
2245         }
2246
2247         if (ast_sorcery_apply_config(sorcery, "test_sorcery_cache") != AST_SORCERY_APPLY_SUCCESS) {
2248                 ast_test_status_update(test, "Failed to apply configured object mappings\n");
2249                 goto end;
2250         }
2251
2252         if (ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
2253                 ast_test_status_update(test, "Failed to register object type\n");
2254                 goto end;
2255         }
2256
2257         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
2258                 ast_test_status_update(test, "Failed to allocate a known object type\n");
2259                 goto end;
2260         }
2261
2262         if (ast_sorcery_create(sorcery, obj)) {
2263                 ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
2264                 goto end;
2265         }
2266
2267         ao2_cleanup(obj);
2268
2269         if (!(obj = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
2270                 ast_test_status_update(test, "Failed to retrieve just created object\n");
2271                 goto end;
2272         } else if (!cache.created) {
2273                 ast_test_status_update(test, "Caching wizard was not told to cache just created object\n");
2274                 goto end;
2275         } else if (!(obj2 = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
2276                 ast_test_status_update(test, "Failed to retrieve just cached object\n");
2277                 goto end;
2278         } else if (obj == obj2) {
2279                 ast_test_status_update(test, "Returned object is *NOT* a cached object\n");
2280                 goto end;
2281         } else if (ast_sorcery_update(sorcery, obj)) {
2282                 ast_test_status_update(test, "Failed to update a known stored object\n");
2283                 goto end;
2284         } else if (!cache.updated) {
2285                 ast_test_status_update(test, "Caching wizard was not told to update object\n");
2286                 goto end;
2287         } else if (ast_sorcery_delete(sorcery, obj)) {
2288                 ast_test_status_update(test, "Failed to delete a known stored object\n");
2289                 goto end;
2290         } else if (!cache.deleted) {
2291                 ast_test_status_update(test, "Caching wizard was not told to delete object\n");
2292                 goto end;
2293         }
2294
2295         ao2_cleanup(obj2);
2296
2297         if ((obj2 = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
2298                 ast_test_status_update(test, "Retrieved an object that should have been deleted\n");
2299                 goto end;
2300         }
2301
2302         res = AST_TEST_PASS;
2303
2304 end:
2305         ast_sorcery_unref(sorcery);
2306         sorcery = NULL;
2307
2308         if (ast_sorcery_wizard_unregister(&test_wizard)) {
2309                 ast_test_status_update(test, "Failed to unregister test sorcery wizard\n");
2310                 return AST_TEST_FAIL;
2311         }
2312
2313         return res;
2314 }
2315
2316 AST_TEST_DEFINE(object_type_observer)
2317 {
2318         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
2319         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
2320         int res = AST_TEST_FAIL;
2321
2322         switch (cmd) {
2323         case TEST_INIT:
2324                 info->name = "object_type_observer";
2325                 info->category = "/main/sorcery/";
2326                 info->summary = "sorcery object type observer unit test";
2327                 info->description =
2328                         "Test that object type observers get called when they should";
2329                 return AST_TEST_NOT_RUN;
2330         case TEST_EXECUTE:
2331                 break;
2332         }
2333
2334         if (!(sorcery = alloc_and_initialize_sorcery())) {
2335                 ast_test_status_update(test, "Failed to open sorcery structure\n");
2336                 return AST_TEST_FAIL;
2337         }
2338
2339         if (!ast_sorcery_observer_add(sorcery, "test", NULL)) {
2340                 ast_test_status_update(test, "Successfully added a NULL observer when it should not be possible\n");
2341                 return AST_TEST_FAIL;
2342         }
2343
2344         if (ast_sorcery_observer_add(sorcery, "test", &test_observer)) {
2345                 ast_test_status_update(test, "Failed to add a proper observer\n");
2346                 return AST_TEST_FAIL;
2347         }
2348
2349         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
2350                 ast_test_status_update(test, "Failed to allocate a known object type\n");
2351                 goto end;
2352         }
2353
2354         ast_mutex_init(&observer.lock);
2355         ast_cond_init(&observer.cond, NULL);
2356         observer.created = NULL;
2357         observer.updated = NULL;
2358         observer.deleted = NULL;
2359
2360         if (ast_sorcery_create(sorcery, obj)) {
2361                 ast_test_status_update(test, "Failed to create object using in-memory wizard\n");
2362                 goto end;
2363         }
2364
2365         ast_mutex_lock(&observer.lock);
2366         while (!observer.created) {
2367         struct timeval start = ast_tvnow();
2368         struct timespec end = {
2369                 .tv_sec = start.tv_sec + 10,
2370                 .tv_nsec = start.tv_usec * 1000,
2371         };
2372                 if (ast_cond_timedwait(&observer.cond, &observer.lock, &end) == ETIMEDOUT) {
2373                         break;
2374                 }
2375         }
2376         ast_mutex_unlock(&observer.lock);
2377
2378         if (!observer.created) {
2379                 ast_test_status_update(test, "Failed to receive observer notification for object creation within suitable timeframe\n");
2380                 goto end;
2381         }
2382
2383         if (ast_sorcery_update(sorcery, obj)) {
2384                 ast_test_status_update(test, "Failed to update object using in-memory wizard\n");
2385                 goto end;
2386         }
2387
2388         ast_mutex_lock(&observer.lock);
2389         while (!observer.updated) {
2390         struct timeval start = ast_tvnow();
2391         struct timespec end = {
2392                 .tv_sec = start.tv_sec + 10,
2393                 .tv_nsec = start.tv_usec * 1000,
2394         };
2395                 if (ast_cond_timedwait(&observer.cond, &observer.lock, &end) == ETIMEDOUT) {
2396                         break;
2397                 }
2398         }
2399         ast_mutex_unlock(&observer.lock);
2400
2401         if (!observer.updated) {
2402                 ast_test_status_update(test, "Failed to receive observer notification for object updating within suitable timeframe\n");
2403                 goto end;
2404         }
2405
2406         if (ast_sorcery_delete(sorcery, obj)) {
2407                 ast_test_status_update(test, "Failed to delete object using in-memory wizard\n");
2408                 goto end;
2409         }
2410
2411         ast_mutex_lock(&observer.lock);
2412         while (!observer.deleted) {
2413         struct timeval start = ast_tvnow();
2414         struct timespec end = {
2415                 .tv_sec = start.tv_sec + 10,
2416                 .tv_nsec = start.tv_usec * 1000,
2417         };
2418                 if (ast_cond_timedwait(&observer.cond, &observer.lock, &end) == ETIMEDOUT) {
2419                         break;
2420                 }
2421         }
2422         ast_mutex_unlock(&observer.lock);
2423
2424         if (!observer.deleted) {
2425                 ast_test_status_update(test, "Failed to receive observer notification for object deletion within suitable timeframe\n");
2426                 goto end;
2427         }
2428
2429         ast_sorcery_reload(sorcery);
2430
2431         ast_mutex_lock(&observer.lock);
2432         while (!observer.loaded) {
2433         struct timeval start = ast_tvnow();
2434         struct timespec end = {
2435                 .tv_sec = start.tv_sec + 10,
2436                 .tv_nsec = start.tv_usec * 1000,
2437         };
2438                 if (ast_cond_timedwait(&observer.cond, &observer.lock, &end) == ETIMEDOUT) {
2439                         break;
2440                 }
2441         }
2442         ast_mutex_unlock(&observer.lock);
2443
2444         if (!observer.loaded) {
2445                 ast_test_status_update(test, "Failed to receive observer notification for object type load within suitable timeframe\n");
2446                 goto end;
2447         }
2448
2449         res = AST_TEST_PASS;
2450
2451 end:
2452         observer.created = NULL;
2453         observer.updated = NULL;
2454         observer.deleted = NULL;
2455         ast_mutex_destroy(&observer.lock);
2456         ast_cond_destroy(&observer.cond);
2457
2458         return res;
2459 }
2460
2461 AST_TEST_DEFINE(configuration_file_wizard)
2462 {
2463         struct ast_flags flags = { CONFIG_FLAG_NOCACHE };
2464         struct ast_config *config;
2465         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
2466         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
2467
2468         switch (cmd) {
2469         case TEST_INIT:
2470                 info->name = "configuration_file_wizard";
2471                 info->category = "/main/sorcery/";
2472                 info->summary = "sorcery configuration file wizard unit test";
2473                 info->description =
2474                         "Test the configuration file wizard in sorcery";
2475                 return AST_TEST_NOT_RUN;
2476         case TEST_EXECUTE:
2477                 break;
2478         }
2479
2480         if (!(config = ast_config_load2("test_sorcery.conf", "test_sorcery", flags))) {
2481                 ast_test_status_update(test, "Test sorcery configuration file wizard file not present - skipping configuration_file_wizard test\n");
2482                 return AST_TEST_NOT_RUN;
2483         }
2484
2485         ast_config_destroy(config);
2486
2487         if (!(sorcery = ast_sorcery_open())) {
2488                 ast_test_status_update(test, "Failed to open sorcery structure\n");
2489                 return AST_TEST_FAIL;
2490         }
2491
2492         if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf") != AST_SORCERY_APPLY_SUCCESS) {
2493                 ast_test_status_update(test, "Could not set a default wizard of the 'config' type, so skipping since it may not be loaded\n");
2494                 return AST_TEST_NOT_RUN;
2495         }
2496
2497         if (ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
2498                 ast_test_status_update(test, "Failed to register object type\n");
2499                 return AST_TEST_FAIL;
2500         }
2501
2502         ast_sorcery_object_field_register_nodoc(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
2503         ast_sorcery_object_field_register_nodoc(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
2504
2505         ast_sorcery_load(sorcery);
2506
2507         if ((obj = ast_sorcery_retrieve_by_id(sorcery, "test", "hey2"))) {
2508                 ast_test_status_update(test, "Retrieved object which has an unknown field\n");
2509                 return AST_TEST_FAIL;
2510         } else if (!(obj = ast_sorcery_retrieve_by_id(sorcery, "test", "hey"))) {
2511                 ast_test_status_update(test, "Failed to retrieve a known object that has been configured in the configuration file\n");
2512                 return AST_TEST_FAIL;
2513         } else if (obj->bob != 98) {
2514                 ast_test_status_update(test, "Value of 'bob' on object is not what is configured in configuration file\n");
2515                 return AST_TEST_FAIL;
2516         } else if (obj->joe != 41) {
2517                 ast_test_status_update(test, "Value of 'joe' on object is not what is configured in configuration file\n");
2518                 return AST_TEST_FAIL;
2519         }
2520
2521         return AST_TEST_PASS;
2522 }
2523
2524 AST_TEST_DEFINE(configuration_file_wizard_with_file_integrity)
2525 {
2526         struct ast_flags flags = { CONFIG_FLAG_NOCACHE };
2527         struct ast_config *config;
2528         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
2529         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
2530
2531         switch (cmd) {
2532         case TEST_INIT:
2533                 info->name = "configuration_file_wizard_with_file_integrity";
2534                 info->category = "/main/sorcery/";
2535                 info->summary = "sorcery configuration file wizard file integrity unit test";
2536                 info->description =
2537                         "Test the configuration file wizard with file integrity turned on in sorcery";
2538                 return AST_TEST_NOT_RUN;
2539         case TEST_EXECUTE:
2540                 break;
2541         }
2542
2543         if (!(config = ast_config_load2("test_sorcery.conf", "test_sorcery", flags))) {
2544                 ast_test_status_update(test, "Test sorcery configuration file wizard file not present - skipping configuration_file_wizard_with_file_integrity test\n");
2545                 return AST_TEST_NOT_RUN;
2546         }
2547
2548         ast_config_destroy(config);
2549
2550         if (!(sorcery = ast_sorcery_open())) {
2551                 ast_test_status_update(test, "Failed to open sorcery structure\n");
2552                 return AST_TEST_FAIL;
2553         }
2554
2555         if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf,integrity=file") != AST_SORCERY_APPLY_SUCCESS) {
2556                 ast_test_status_update(test, "Could not set a default wizard of the 'config' type, so skipping since it may not be loaded\n");
2557                 return AST_TEST_NOT_RUN;
2558         }
2559
2560         if (ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
2561                 ast_test_status_update(test, "Failed to register object type\n");
2562                 return AST_TEST_FAIL;
2563         }
2564
2565         ast_sorcery_object_field_register_nodoc(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
2566         ast_sorcery_object_field_register_nodoc(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
2567
2568         ast_sorcery_load(sorcery);
2569
2570         if ((obj = ast_sorcery_retrieve_by_id(sorcery, "test", "hey"))) {
2571                 ast_test_status_update(test, "Retrieved object which has an unknown field\n");
2572                 return AST_TEST_FAIL;
2573         }
2574
2575         return AST_TEST_PASS;
2576 }
2577
2578 AST_TEST_DEFINE(configuration_file_wizard_with_criteria)
2579 {
2580         struct ast_flags flags = { CONFIG_FLAG_NOCACHE };
2581         struct ast_config *config;
2582         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
2583         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
2584
2585         switch (cmd) {
2586         case TEST_INIT:
2587                 info->name = "configuration_file_wizard_with_criteria";
2588                 info->category = "/main/sorcery/";
2589                 info->summary = "sorcery configuration file wizard with criteria unit test";
2590                 info->description =
2591                         "Test the configuration file wizard with criteria matching in sorcery";
2592                 return AST_TEST_NOT_RUN;
2593         case TEST_EXECUTE:
2594                 break;
2595         }
2596
2597         if (!(config = ast_config_load2("test_sorcery.conf", "test_sorcery", flags))) {
2598                 ast_test_status_update(test, "Test sorcery configuration file wizard file not present - skipping configuration_file_wizard_with_criteria test\n");
2599                 return AST_TEST_NOT_RUN;
2600         }
2601
2602         ast_config_destroy(config);
2603
2604         if (!(sorcery = ast_sorcery_open())) {
2605                 ast_test_status_update(test, "Failed to open sorcery structure\n");
2606                 return AST_TEST_FAIL;
2607         }
2608
2609         if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf,criteria=type=zombies") != AST_SORCERY_APPLY_SUCCESS) {
2610                 ast_test_status_update(test, "Could not set a default wizard of the 'config' type, so skipping since it may not be loaded\n");
2611                 return AST_TEST_NOT_RUN;
2612         }
2613
2614         if (ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
2615                 ast_test_status_update(test, "Failed to register object type\n");
2616                 return AST_TEST_FAIL;
2617         }
2618
2619         ast_sorcery_object_field_register_nodoc(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
2620         ast_sorcery_object_field_register_nodoc(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
2621         ast_sorcery_object_field_register_nodoc(sorcery, "test", "type", NULL, OPT_NOOP_T, 0, NULL);
2622
2623         ast_sorcery_load(sorcery);
2624
2625         if ((obj = ast_sorcery_retrieve_by_id(sorcery, "test", "hey"))) {
2626                 ast_test_status_update(test, "Retrieved object which did not match criteria\n");
2627                 return AST_TEST_FAIL;
2628         } else if (!(obj = ast_sorcery_retrieve_by_id(sorcery, "test", "hey2"))) {
2629                 ast_test_status_update(test, "Failed to retrieve a known object which matches criteria\n");
2630                 return AST_TEST_FAIL;
2631         }
2632
2633         return AST_TEST_PASS;
2634 }
2635
2636 AST_TEST_DEFINE(configuration_file_wizard_retrieve_field)
2637 {
2638         struct ast_flags flags = { CONFIG_FLAG_NOCACHE };
2639         struct ast_config *config;
2640         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
2641         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
2642         RAII_VAR(struct ast_variable *, fields, ast_variable_new("joe", "41", ""), ast_variables_destroy);
2643
2644         switch (cmd) {
2645         case TEST_INIT:
2646                 info->name = "configuration_file_wizard_retrieve_field";
2647                 info->category = "/main/sorcery/";
2648                 info->summary = "sorcery configuration file wizard field retrieval unit test";
2649                 info->description =
2650                         "Test the configuration file wizard retrieval using field in sorcery";
2651                 return AST_TEST_NOT_RUN;
2652         case TEST_EXECUTE:
2653                 break;
2654         }
2655
2656         if (!(config = ast_config_load2("test_sorcery.conf", "test_sorcery", flags))) {
2657                 ast_test_status_update(test, "Test sorcery configuration file wizard file not present - skipping configuration_file_wizard_retrieve_field test\n");
2658                 return AST_TEST_NOT_RUN;
2659         }
2660
2661         ast_config_destroy(config);
2662
2663         if (!(sorcery = ast_sorcery_open())) {
2664                 ast_test_status_update(test, "Failed to open sorcery structure\n");
2665                 return AST_TEST_FAIL;
2666         }
2667
2668         if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf") != AST_SORCERY_APPLY_SUCCESS) {
2669                 ast_test_status_update(test, "Could not set a default wizard of the 'config' type, so skipping since it may not be loaded\n");
2670                 return AST_TEST_NOT_RUN;
2671         }
2672
2673         if (ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
2674                 ast_test_status_update(test, "Failed to register object type\n");
2675                 return AST_TEST_FAIL;
2676         }
2677
2678         ast_sorcery_object_field_register_nodoc(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
2679         ast_sorcery_object_field_register_nodoc(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
2680
2681         ast_sorcery_load(sorcery);
2682
2683         if (!(obj = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_DEFAULT, fields))) {
2684                 ast_test_status_update(test, "Failed to retrieve a known object that has been configured with the correct field\n");
2685                 return AST_TEST_FAIL;
2686         } else if (strcmp(ast_sorcery_object_get_id(obj), "hey")) {
2687                 ast_test_status_update(test, "Retrieved object has incorrect object id of '%s'\n", ast_sorcery_object_get_id(obj));
2688                 return AST_TEST_FAIL;
2689         }
2690
2691         return AST_TEST_PASS;
2692 }
2693
2694 AST_TEST_DEFINE(configuration_file_wizard_retrieve_multiple)
2695 {
2696         struct ast_flags flags = { CONFIG_FLAG_NOCACHE };
2697         struct ast_config *config;
2698         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
2699         RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
2700         RAII_VAR(struct ast_variable *, fields, ast_variable_new("joe", "99", ""), ast_variables_destroy);
2701
2702         switch (cmd) {
2703         case TEST_INIT:
2704                 info->name = "configuration_file_wizard_retrieve_multiple";
2705                 info->category = "/main/sorcery/";
2706                 info->summary = "sorcery configuration file wizard multiple retrieval unit test";
2707                 info->description =
2708                         "Test the configuration file wizard multiple retrieval in sorcery";
2709                 return AST_TEST_NOT_RUN;
2710         case TEST_EXECUTE:
2711                 break;
2712         }
2713
2714         if (!(config = ast_config_load2("test_sorcery.conf", "test_sorcery", flags))) {
2715                 ast_test_status_update(test, "Test sorcery configuration file wizard file not present - skipping configuration_file_wizard_retrieve_multiple test\n");
2716                 return AST_TEST_NOT_RUN;
2717         }
2718
2719         ast_config_destroy(config);
2720
2721         if (!fields) {
2722                 ast_test_status_update(test, "Failed to create fields for multiple retrieve\n");
2723                 return AST_TEST_FAIL;
2724         }
2725
2726         if (!(sorcery = ast_sorcery_open())) {
2727                 ast_test_status_update(test, "Failed to open sorcery structure\n");
2728                 return AST_TEST_FAIL;
2729         }
2730
2731         if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf") != AST_SORCERY_APPLY_SUCCESS) {
2732                 ast_test_status_update(test, "Could not set a default wizard of the 'config' type, so skipping since it may not be loaded\n");
2733                 return AST_TEST_NOT_RUN;
2734         }
2735
2736         if (ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
2737                 ast_test_status_update(test, "Failed to register object type\n");
2738                 return AST_TEST_FAIL;
2739         }
2740
2741         ast_sorcery_object_field_register_nodoc(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
2742         ast_sorcery_object_field_register_nodoc(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
2743
2744         ast_sorcery_load(sorcery);
2745
2746         if (!(objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE, fields))) {
2747                 ast_test_status_update(test, "Failed to retrieve an empty container when retrieving multiple\n");
2748                 return AST_TEST_FAIL;
2749         } else if (ao2_container_count(objects)) {
2750                 ast_test_status_update(test, "Received a container with objects when there should be none in it\n");
2751                 return AST_TEST_FAIL;
2752         }
2753
2754         ao2_cleanup(objects);
2755         ast_variables_destroy(fields);
2756
2757         if (!(fields = ast_variable_new("joe", "41", ""))) {
2758                 ast_test_status_update(test, "Failed to create fields for multiple retrieve\n");
2759                 return AST_TEST_FAIL;
2760         } else if (!(objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE, fields))) {
2761                 ast_test_status_update(test, "Failed to retrieve a container when retrieving multiple\n");
2762                 return AST_TEST_FAIL;
2763         } else if (ao2_container_count(objects) != 1) {
2764                 ast_test_status_update(test, "Received a container with no objects in it when there should be\n");
2765                 return AST_TEST_FAIL;
2766         }
2767
2768         return AST_TEST_PASS;
2769 }
2770
2771 AST_TEST_DEFINE(configuration_file_wizard_retrieve_multiple_all)
2772 {
2773         struct ast_flags flags = { CONFIG_FLAG_NOCACHE };
2774         struct ast_config *config;
2775         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
2776         RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
2777
2778         switch (cmd) {
2779         case TEST_INIT:
2780                 info->name = "configuration_file_wizard_retrieve_multiple_all";
2781                 info->category = "/main/sorcery/";
2782                 info->summary = "sorcery configuration file wizard multiple retrieve all unit test";
2783                 info->description =
2784                         "Test the configuration file wizard multiple retrieve all in sorcery";
2785                 return AST_TEST_NOT_RUN;
2786         case TEST_EXECUTE:
2787                 break;
2788         }
2789
2790         if (!(config = ast_config_load2("test_sorcery.conf", "test_sorcery", flags))) {
2791                 ast_test_status_update(test, "Test sorcery configuration file wizard file not present - skipping configuration_file_wizard_retrieve_multiple_all test\n");
2792                 return AST_TEST_NOT_RUN;
2793         }
2794
2795         ast_config_destroy(config);
2796
2797         if (!(sorcery = ast_sorcery_open())) {
2798                 ast_test_status_update(test, "Failed to open sorcery structure\n");
2799                 return AST_TEST_FAIL;
2800         }
2801
2802         if (ast_sorcery_apply_default(sorcery, "test", "config", "test_sorcery.conf") != AST_SORCERY_APPLY_SUCCESS) {
2803                 ast_test_status_update(test, "Could not set a default wizard of the 'config' type, so skipping since it may not be loaded\n");
2804                 return AST_TEST_NOT_RUN;
2805         }
2806
2807         if (ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
2808                 ast_test_status_update(test, "Failed to register object type\n");
2809                 return AST_TEST_FAIL;
2810         }
2811
2812         ast_sorcery_object_field_register_nodoc(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
2813         ast_sorcery_object_field_register_nodoc(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
2814
2815         ast_sorcery_load(sorcery);
2816
2817         if (!(objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL))) {
2818                 ast_test_status_update(test, "Failed to retrieve a container with all objects when there should be one\n");
2819                 return AST_TEST_FAIL;
2820         } else if (ao2_container_count(objects) != 2) {
2821                 ast_test_status_update(test, "Returned container does not have the correct number of objects in it\n");
2822                 return AST_TEST_FAIL;
2823         }
2824
2825         return AST_TEST_PASS;
2826 }
2827
2828 AST_TEST_DEFINE(dialplan_function)
2829 {
2830         RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
2831         RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
2832         RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
2833         struct ast_str *buf;
2834         char expression[256];
2835
2836         switch (cmd) {
2837         case TEST_INIT:
2838                 info->name = "dialplan_function";
2839                 info->category = "/main/sorcery/";
2840                 info->summary = "AST_SORCERY dialplan function";
2841                 info->description =
2842                         "Test the AST_SORCERY dialplan function";
2843                 return AST_TEST_NOT_RUN;
2844         case TEST_EXECUTE:
2845                 break;
2846         }
2847
2848         if (!(sorcery = alloc_and_initialize_sorcery())) {
2849                 ast_test_status_update(test, "Failed to open sorcery structure\n");
2850                 return AST_TEST_FAIL;
2851         }
2852
2853         if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
2854                 ast_test_status_update(test, "Failed to allocate a known object type\n");
2855                 return AST_TEST_FAIL;
2856         }
2857
2858         if (ast_sorcery_create(sorcery, obj)) {
2859                 ast_test_status_update(test, "Failed to create a known object type\n");
2860                 return AST_TEST_FAIL;
2861         }
2862
2863         if (!(buf = ast_str_create(16))) {
2864                 ast_test_status_update(test, "Failed to allocate return buffer\n");
2865                 return AST_TEST_FAIL;
2866         }
2867
2868         ast_str_reset(buf);
2869         snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s)", "notest_sorcery", "test", "blah", "bob");
2870         if (!ast_func_read2(NULL, expression, &buf, 16)) {
2871                 ast_free(buf);
2872                 ast_test_status_update(test, "Retrieved a non-existent module\n");
2873                 return AST_TEST_FAIL;
2874         }
2875
2876         ast_str_reset(buf);
2877         snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s)", "test_sorcery", "notest", "blah", "bob");
2878         if (!ast_func_read2(NULL, expression, &buf, 16)) {
2879                 ast_free(buf);
2880                 ast_test_status_update(test, "Retrieved a non-existent type\n");
2881                 return AST_TEST_FAIL;
2882         }
2883
2884         ast_str_reset(buf);
2885         snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s)", "test_sorcery", "test", "noid", "bob");
2886         if (!ast_func_read2(NULL, expression, &buf, 16)) {
2887                 ast_free(buf);
2888                 ast_test_status_update(test, "Retrieved a non-existent id\n");
2889                 return AST_TEST_FAIL;
2890         }
2891
2892         ast_str_reset(buf);
2893         snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s)", "test_sorcery", "test", "blah", "nobob");
2894         if (!ast_func_read2(NULL, expression, &buf, 16)) {
2895                 ast_free(buf);
2896                 ast_test_status_update(test, "Retrieved a non-existent field\n");
2897                 return AST_TEST_FAIL;
2898         }
2899
2900         ast_str_reset(buf);
2901         snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s)", "test_sorcery", "test", "blah", "bob");
2902         if (ast_func_read2(NULL, expression, &buf, 16)) {
2903                 ast_free(buf);
2904                 ast_test_status_update(test, "Failed retrieve field 'bob'\n");
2905                 return AST_TEST_FAIL;
2906         }
2907         if (strcmp(ast_str_buffer(buf), "5")) {
2908                 ast_free(buf);
2909                 ast_test_status_update(test, "Failed retrieve field.  Got '%d', should be '5'\n", obj->bob);
2910                 return AST_TEST_FAIL;
2911         }
2912
2913         ast_str_reset(buf);
2914         snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s,single,1)", "test_sorcery", "test", "blah", "bob");
2915         if (ast_func_read2(NULL, expression, &buf, 16)) {
2916                 ast_free(buf);
2917                 ast_test_status_update(test, "Failed retrieve field 'bob'\n");
2918                 return AST_TEST_FAIL;
2919         }
2920         if (strcmp(ast_str_buffer(buf), "5")) {
2921                 ast_free(buf);
2922                 ast_test_status_update(test, "Failed retrieve field.  Got '%d', should be '5'\n", obj->bob);
2923                 return AST_TEST_FAIL;
2924         }
2925
2926         ast_str_reset(buf);
2927         snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s,single,2)", "test_sorcery", "test", "blah", "bob");
2928         if (!ast_func_read2(NULL, expression, &buf, 16)) {
2929                 ast_free(buf);
2930                 ast_test_status_update(test, "Got a second 'bob' and shouldn't have\n");
2931                 return AST_TEST_FAIL;
2932         }
2933
2934         /* 444 is already the first item in the list */
2935         jim_handler(NULL, ast_variable_new("jim", "555", ""), obj);
2936         jim_handler(NULL, ast_variable_new("jim", "666", ""), obj);
2937
2938         ast_str_reset(buf);
2939         snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s)", "test_sorcery", "test", "blah", "jim");
2940         if (ast_func_read2(NULL, expression, &buf, 16)) {
2941                 ast_free(buf);
2942                 ast_test_status_update(test, "Couldn't retrieve 'jim'\n");
2943                 return AST_TEST_FAIL;
2944         }
2945         if (strcmp(ast_str_buffer(buf), "444,555,666")) {
2946                 ast_free(buf);
2947                 ast_test_status_update(test, "Failed retrieve jim.  Got '%s', should be '444,555,666'\n", ast_str_buffer(buf));
2948                 return AST_TEST_FAIL;
2949         }
2950
2951         ast_str_reset(buf);
2952         snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s,single,2)", "test_sorcery", "test", "blah", "jim");
2953         if (ast_func_read2(NULL, expression, &buf, 16)) {
2954                 ast_free(buf);
2955                 ast_test_status_update(test, "Couldn't retrieve 2nd jim\n");
2956                 return AST_TEST_FAIL;
2957         }
2958         if (strcmp(ast_str_buffer(buf), "555")) {
2959                 ast_free(buf);
2960                 ast_test_status_update(test, "Failed retrieve 2nd jim.  Got '%s', should be '555'\n", ast_str_buffer(buf));
2961                 return AST_TEST_FAIL;
2962         }
2963
2964         ast_str_reset(buf);
2965         snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s,concat,|)", "test_sorcery", "test", "blah", "jim");
2966         if (ast_func_read2(NULL, expression, &buf, 16)) {
2967                 ast_free(buf);
2968                 ast_test_status_update(test, "Couldn't retrieve any 'jim'\n");
2969                 return AST_TEST_FAIL;
2970         }
2971         if (strcmp(ast_str_buffer(buf), "444|555|666")) {
2972                 ast_free(buf);
2973                 ast_test_status_update(test, "Failed retrieve 'jim'.  Got '%s', should be '444|555|666'\n", ast_str_buffer(buf));
2974                 return AST_TEST_FAIL;
2975         }
2976
2977         ast_str_reset(buf);
2978         snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s,noconcat,3)", "test_sorcery", "test", "blah", "jim");
2979         if (!ast_func_read2(NULL, expression, &buf, 16)) {
2980                 ast_free(buf);
2981                 ast_test_status_update(test, "Should have failed with invalid retrieval_type\n");
2982                 return AST_TEST_FAIL;
2983         }
2984
2985         ast_str_reset(buf);
2986         snprintf(expression, sizeof(expression), "AST_SORCERY(%s,%s,%s,%s,single,|)", "test_sorcery", "test", "blah", "jim");
2987         if (!ast_func_read2(NULL, expression, &buf, 16)) {
2988                 ast_free(buf);
2989                 ast_test_status_update(test, "Should have failed with invalid occurrence_number\n");
2990                 return AST_TEST_FAIL;
2991         }
2992
2993         ast_free(buf);
2994
2995         return AST_TEST_PASS;
2996 }
2997
2998 static int unload_module(void)
2999 {
3000         AST_TEST_UNREGISTER(wizard_registration);
3001         AST_TEST_UNREGISTER(sorcery_open);
3002         AST_TEST_UNREGISTER(apply_default);
3003         AST_TEST_UNREGISTER(apply_config);
3004         AST_TEST_UNREGISTER(object_register);
3005         AST_TEST_UNREGISTER(object_register_without_mapping);
3006         AST_TEST_UNREGISTER(object_field_register);
3007         AST_TEST_UNREGISTER(object_fields_register);
3008         AST_TEST_UNREGISTER(object_alloc_with_id);
3009         AST_TEST_UNREGISTER(object_alloc_without_id);
3010         AST_TEST_UNREGISTER(object_copy);
3011         AST_TEST_UNREGISTER(object_copy_native);
3012         AST_TEST_UNREGISTER(object_diff);
3013         AST_TEST_UNREGISTER(object_diff_native);
3014         AST_TEST_UNREGISTER(objectset_create);
3015         AST_TEST_UNREGISTER(objectset_json_create);
3016         AST_TEST_UNREGISTER(objectset_create_regex);
3017         AST_TEST_UNREGISTER(objectset_apply);
3018         AST_TEST_UNREGISTER(objectset_apply_handler);
3019         AST_TEST_UNREGISTER(objectset_apply_invalid);
3020         AST_TEST_UNREGISTER(objectset_transform);
3021         AST_TEST_UNREGISTER(objectset_apply_fields);
3022         AST_TEST_UNREGISTER(extended_fields);
3023         AST_TEST_UNREGISTER(changeset_create);
3024         AST_TEST_UNREGISTER(changeset_create_unchanged);
3025         AST_TEST_UNREGISTER(object_create);
3026         AST_TEST_UNREGISTER(object_retrieve_id);
3027         AST_TEST_UNREGISTER(object_retrieve_field);
3028         AST_TEST_UNREGISTER(object_retrieve_multiple_all);
3029         AST_TEST_UNREGISTER(object_retrieve_multiple_field);
3030         AST_TEST_UNREGISTER(object_retrieve_regex);
3031         AST_TEST_UNREGISTER(object_update);
3032         AST_TEST_UNREGISTER(object_update_uncreated);
3033         AST_TEST_UNREGISTER(object_delete);
3034         AST_TEST_UNREGISTER(object_delete_uncreated);
3035         AST_TEST_UNREGISTER(caching_wizard_behavior);
3036         AST_TEST_UNREGISTER(object_type_observer);
3037         AST_TEST_UNREGISTER(configuration_file_wizard);
3038         AST_TEST_UNREGISTER(configuration_file_wizard_with_file_integrity);
3039         AST_TEST_UNREGISTER(configuration_file_wizard_with_criteria);
3040         AST_TEST_UNREGISTER(configuration_file_wizard_retrieve_field);
3041         AST_TEST_UNREGISTER(configuration_file_wizard_retrieve_multiple);
3042         AST_TEST_UNREGISTER(configuration_file_wizard_retrieve_multiple_all);
3043         AST_TEST_UNREGISTER(dialplan_function);
3044         return 0;
3045 }
3046
3047 static int load_module(void)
3048 {
3049         AST_TEST_REGISTER(wizard_registration);
3050         AST_TEST_REGISTER(sorcery_open);
3051         AST_TEST_REGISTER(apply_default);
3052         AST_TEST_REGISTER(apply_config);
3053         AST_TEST_REGISTER(object_register);
3054         AST_TEST_REGISTER(object_register_without_mapping);
3055         AST_TEST_REGISTER(object_field_register);
3056         AST_TEST_REGISTER(object_fields_register);
3057         AST_TEST_REGISTER(object_alloc_with_id);
3058         AST_TEST_REGISTER(object_alloc_without_id);
3059         AST_TEST_REGISTER(object_copy);
3060         AST_TEST_REGISTER(object_copy_native);
3061         AST_TEST_REGISTER(object_diff);
3062         AST_TEST_REGISTER(object_diff_native);
3063         AST_TEST_REGISTER(objectset_create);
3064         AST_TEST_REGISTER(objectset_json_create);
3065         AST_TEST_REGISTER(objectset_create_regex);
3066         AST_TEST_REGISTER(objectset_apply);
3067         AST_TEST_REGISTER(objectset_apply_handler);
3068         AST_TEST_REGISTER(objectset_apply_invalid);
3069         AST_TEST_REGISTER(objectset_transform);
3070         AST_TEST_REGISTER(objectset_apply_fields);
3071         AST_TEST_REGISTER(extended_fields);
3072         AST_TEST_REGISTER(changeset_create);
3073         AST_TEST_REGISTER(changeset_create_unchanged);
3074         AST_TEST_REGISTER(object_create);
3075         AST_TEST_REGISTER(object_retrieve_id);
3076         AST_TEST_REGISTER(object_retrieve_field);
3077         AST_TEST_REGISTER(object_retrieve_multiple_all);
3078         AST_TEST_REGISTER(object_retrieve_multiple_field);
3079         AST_TEST_REGISTER(object_retrieve_regex);
3080         AST_TEST_REGISTER(object_update);
3081         AST_TEST_REGISTER(object_update_uncreated);
3082         AST_TEST_REGISTER(object_delete);
3083         AST_TEST_REGISTER(object_delete_uncreated);
3084         AST_TEST_REGISTER(caching_wizard_behavior);
3085         AST_TEST_REGISTER(object_type_observer);
3086         AST_TEST_REGISTER(configuration_file_wizard);
3087         AST_TEST_REGISTER(configuration_file_wizard_with_file_integrity);
3088         AST_TEST_REGISTER(configuration_file_wizard_with_criteria);
3089         AST_TEST_REGISTER(configuration_file_wizard_retrieve_field);
3090         AST_TEST_REGISTER(configuration_file_wizard_retrieve_multiple);
3091         AST_TEST_REGISTER(configuration_file_wizard_retrieve_multiple_all);
3092         AST_TEST_REGISTER(dialplan_function);
3093         return AST_MODULE_LOAD_SUCCESS;
3094 }
3095
3096 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Sorcery test module");