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