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