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