Newer
Older
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2012 - 2013, Digium, Inc.
*
* Joshua Colp <jcolp@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*!
* \file
* \brief Sorcery Unit Tests
*
* \author Joshua Colp <jcolp@digium.com>
*
*/
/*** MODULEINFO
<depend>TEST_FRAMEWORK</depend>
<depend>func_sorcery</depend>
<support_level>core</support_level>
***/
#include "asterisk.h"
#include "asterisk/test.h"
#include "asterisk/module.h"
#include "asterisk/sorcery.h"
#include "asterisk/logger.h"
#include "asterisk/json.h"
/*! \brief Dummy sorcery object */
struct test_sorcery_object {
SORCERY_OBJECT(details);
unsigned int bob;
unsigned int joe;
struct ast_variable *jim;
struct ast_variable *jack;
/*! \brief Internal function to destroy a test object */
static void test_sorcery_object_destroy(void *obj)
{
struct test_sorcery_object *tobj = obj;
ast_variables_destroy(tobj->jim);
ast_variables_destroy(tobj->jack);
}
/*! \brief Internal function to allocate a test object */
static void *test_sorcery_object_alloc(const char *id)
{
return ast_sorcery_generic_alloc(sizeof(struct test_sorcery_object), test_sorcery_object_destroy);
}
/*! \brief Internal function for object set transformation */
static struct ast_variable *test_sorcery_transform(struct ast_variable *set)
{
struct ast_variable *field, *transformed = NULL;
for (field = set; field; field = field->next) {
struct ast_variable *transformed_field;
if (!strcmp(field->name, "joe")) {
transformed_field = ast_variable_new(field->name, "5000", "");
} else {
transformed_field = ast_variable_new(field->name, field->value, "");
}
if (!transformed_field) {
ast_variables_destroy(transformed);
return NULL;
}
transformed_field->next = transformed;
transformed = transformed_field;
}
return transformed;
}
/*! \brief Internal function which copies pre-defined data into an object, natively */
static int test_sorcery_copy(const void *src, void *dst)
{
struct test_sorcery_object *obj = dst;
obj->bob = 10;
obj->joe = 20;
obj->jim = ast_variable_new("jim", "444", "");
obj->jack = ast_variable_new("jack", "999,000", "");
return 0;
}
/*! \brief Internal function which creates a pre-defined diff natively */
static int test_sorcery_diff(const void *original, const void *modified, struct ast_variable **changes)
{
*changes = ast_variable_new("yes", "itworks", "");
return 0;
}
Joshua Colp
committed
/*! \brief Internal function which sets some values */
static int test_sorcery_regex_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
{
struct test_sorcery_object *test = obj;
test->bob = 256;
return 0;
}
/*! \brief Internal function which creates some ast_variable structures */
static int test_sorcery_regex_fields(const void *obj, struct ast_variable **fields)
{
*fields = ast_variable_new("toast-bob", "10", "");
return 0;
}
/*! \brief Test structure for caching */
struct sorcery_test_caching {
/*! \brief Whether the object has been created in the cache or not */
unsigned int created:1;
/*! \brief Whether the object has been updated in the cache or not */
unsigned int updated:1;
/*! \brief Whether the object has been deleted from the cache or not */
unsigned int deleted:1;
/*! \brief Whether the object is stale or not */
unsigned int is_stale:1;
/*! \brief Object to return when asked */
struct test_sorcery_object object;
};
/*! \brief Test structure for observer */
struct sorcery_test_observer {
/*! \brief Lock for notification */
ast_mutex_t lock;
/*! \brief Condition for notification */
ast_cond_t cond;
/*! \brief Pointer to the created object */
const void *created;
/*! \brief Pointer to the update object */
const void *updated;
/*! \brief Pointer to the deleted object */
const void *deleted;
/*! \brief Whether the type has been loaded */
unsigned int loaded:1;
};
Joshua Colp
committed
/*! \brief Global scope apply handler integer to make sure it executed */
static int apply_handler_called;
/*! \brief Simple apply handler which sets global scope integer to 1 if called */
static int test_apply_handler(const struct ast_sorcery *sorcery, void *obj)
Joshua Colp
committed
{
apply_handler_called = 1;
Joshua Colp
committed
}
/*! \brief Global scope caching structure for testing */
static struct sorcery_test_caching cache = { 0, };
/*! \brief Global scope observer structure for testing */
static struct sorcery_test_observer observer;
static void *wizard2_data;
static void *sorcery_test_open(const char *data)
{
wizard2_data = (void *)data;
return wizard2_data;
}
static void sorcery_test_close(void *data)
{
}
static int sorcery_test_create(const struct ast_sorcery *sorcery, void *data, void *object)
{
cache.created = 1;
cache.updated = 0;
cache.deleted = 0;
return 0;
}
static void *sorcery_test_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type, const char *id)
{
return (cache.created && !cache.deleted) ? ast_sorcery_alloc(sorcery, type, id) : NULL;
}
static int sorcery_test_update(const struct ast_sorcery *sorcery, void *data, void *object)
{
cache.updated = 1;
return 0;
}
static int sorcery_test_delete(const struct ast_sorcery *sorcery, void *data, void *object)
{
cache.deleted = 1;
return 0;
}
static int sorcery_test_is_stale(const struct ast_sorcery *sorcery, void *data, void *object)
{
cache.is_stale = 1;
return 1;
}
/*! \brief Dummy sorcery wizards, not actually used so we only populate the name and nothing else */
static struct ast_sorcery_wizard test_wizard = {
.name = "test",
.create = sorcery_test_create,
.retrieve_id = sorcery_test_retrieve_id,
.update = sorcery_test_update,
.delete = sorcery_test_delete,
};
static struct ast_sorcery_wizard test_wizard2 = {
.name = "test2",
.open = sorcery_test_open,
.close = sorcery_test_close,
.create = sorcery_test_create,
.retrieve_id = sorcery_test_retrieve_id,
.update = sorcery_test_update,
.delete = sorcery_test_delete,
.is_stale = sorcery_test_is_stale,
};
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
static void sorcery_observer_created(const void *object)
{
SCOPED_MUTEX(lock, &observer.lock);
observer.created = object;
ast_cond_signal(&observer.cond);
}
static void sorcery_observer_updated(const void *object)
{
SCOPED_MUTEX(lock, &observer.lock);
observer.updated = object;
ast_cond_signal(&observer.cond);
}
static void sorcery_observer_deleted(const void *object)
{
SCOPED_MUTEX(lock, &observer.lock);
observer.deleted = object;
ast_cond_signal(&observer.cond);
}
static void sorcery_observer_loaded(const char *object_type)
{
SCOPED_MUTEX(lock, &observer.lock);
observer.loaded = 1;
ast_cond_signal(&observer.cond);
}
/*! \brief Test sorcery observer implementation */
Richard Mudgett
committed
static const struct ast_sorcery_observer test_observer = {
.created = sorcery_observer_created,
.updated = sorcery_observer_updated,
.deleted = sorcery_observer_deleted,
.loaded = sorcery_observer_loaded,
};
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
/* This handler takes a simple value and creates new list entry for it*/
static int jim_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
{
struct test_sorcery_object *tobj = obj;
ast_variable_list_append(&tobj->jim, ast_variables_dup(var));
return 0;
}
/* This handler takes a CSV string and creates new a new list entry for each value */
static int jack_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
{
struct test_sorcery_object *tobj = obj;
char *jacks = ast_strdupa(var->value);
char *val;
while ((val = strsep(&jacks, ","))) {
ast_variable_list_append(&tobj->jack, ast_variable_new("jack", val, ""));
}
return 0;
}
static int jim_vl(const void *obj, struct ast_variable **fields)
{
const struct test_sorcery_object *tobj = obj;
if (tobj->jim) {
*fields = ast_variables_dup(tobj->jim);
}
return 0;
}
static int jack_str(const void *obj, const intptr_t *args, char **buf)
{
const struct test_sorcery_object *tobj = obj;
struct ast_variable *curr = tobj->jack;
RAII_VAR(struct ast_str *, str, ast_str_create(128), ast_free);
while(curr) {
ast_str_append(&str, 0, "%s,", curr->value);
curr = curr->next;
}
ast_str_truncate(str, -1);
*buf = ast_strdup(ast_str_buffer(str));
str = NULL;
return 0;
}
static struct ast_sorcery *alloc_and_initialize_sorcery(void)
{
struct ast_sorcery *sorcery;
if (!(sorcery = ast_sorcery_open())) {
Mark Michelson
committed
if ((ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) ||
ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
ast_sorcery_unref(sorcery);
return NULL;
ast_sorcery_object_field_register_nodoc(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
ast_sorcery_object_field_register_nodoc(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
ast_sorcery_object_field_register_custom_nodoc(sorcery, "test", "jim", "444", jim_handler, NULL, jim_vl, 0, 0);
ast_sorcery_object_field_register_custom_nodoc(sorcery, "test", "jack", "888,999", jack_handler, jack_str, NULL, 0, 0);
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
return sorcery;
}
AST_TEST_DEFINE(wizard_registration)
{
switch (cmd) {
case TEST_INIT:
info->name = "wizard_registration";
info->category = "/main/sorcery/";
info->summary = "sorcery wizard registration and unregistration unit test";
info->description =
"Test registration and unregistration of a sorcery wizard";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
if (ast_sorcery_wizard_register(&test_wizard)) {
ast_test_status_update(test, "Failed to register a perfectly valid sorcery wizard\n");
return AST_TEST_FAIL;
}
if (!ast_sorcery_wizard_register(&test_wizard)) {
ast_test_status_update(test, "Successfully registered a sorcery wizard twice, which is bad\n");
return AST_TEST_FAIL;
}
if (ast_sorcery_wizard_unregister(&test_wizard)) {
ast_test_status_update(test, "Failed to unregister a perfectly valid sorcery wizard\n");
return AST_TEST_FAIL;
}
if (!ast_sorcery_wizard_unregister(&test_wizard)) {
ast_test_status_update(test, "Successfully unregistered a sorcery wizard twice, which is bad\n");
return AST_TEST_FAIL;
}
return AST_TEST_PASS;
}
Joshua Colp
committed
AST_TEST_DEFINE(sorcery_open)
{
RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
RAII_VAR(struct ast_sorcery *, sorcery2, NULL, ast_sorcery_unref);
int refcount;
switch (cmd) {
case TEST_INIT:
info->name = "open";
info->category = "/main/sorcery/";
info->summary = "sorcery open/close unit test";
"Test opening of sorcery and registry operations";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
if ((sorcery = ast_sorcery_retrieve_by_module_name(AST_MODULE))) {
ast_test_status_update(test, "There should NOT have been an existing sorcery instance\n");
return AST_TEST_FAIL;
}
if (!(sorcery = ast_sorcery_open())) {
ast_test_status_update(test, "Failed to open new sorcery structure\n");
return AST_TEST_FAIL;
}
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
if (!(sorcery2 = ast_sorcery_retrieve_by_module_name(AST_MODULE))) {
ast_test_status_update(test, "Failed to find sorcery structure in registry\n");
return AST_TEST_FAIL;
}
if (sorcery2 != sorcery) {
ast_test_status_update(test, "Should have gotten same sorcery on retrieve\n");
return AST_TEST_FAIL;
}
ast_sorcery_unref(sorcery2);
if ((refcount = ao2_ref(sorcery, 0)) != 2) {
ast_test_status_update(test, "Should have been 2 references to sorcery instead of %d\n", refcount);
return AST_TEST_FAIL;
}
if (!(sorcery2 = ast_sorcery_open())) {
ast_test_status_update(test, "Failed to open second sorcery structure\n");
return AST_TEST_FAIL;
}
if (sorcery2 != sorcery) {
ast_test_status_update(test, "Should have gotten same sorcery on 2nd open\n");
return AST_TEST_FAIL;
}
if ((refcount = ao2_ref(sorcery, 0)) != 3) {
ast_test_status_update(test, "Should have been 3 references to sorcery instead of %d\n", refcount);
return AST_TEST_FAIL;
}
ast_sorcery_unref(sorcery);
ast_sorcery_unref(sorcery2);
sorcery2 = NULL;
if ((sorcery = ast_sorcery_retrieve_by_module_name(AST_MODULE))) {
ast_sorcery_unref(sorcery);
sorcery = NULL;
ast_test_status_update(test, "Should NOT have found sorcery structure in registry\n");
return AST_TEST_FAIL;
}
return AST_TEST_PASS;
}
AST_TEST_DEFINE(apply_default)
{
RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
switch (cmd) {
case TEST_INIT:
info->name = "apply_default";
info->category = "/main/sorcery/";
info->summary = "sorcery default wizard unit test";
info->description =
"Test setting default type wizard in sorcery";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
if (!(sorcery = ast_sorcery_open())) {
ast_test_status_update(test, "Failed to open sorcery structure\n");
return AST_TEST_FAIL;
}
Mark Michelson
committed
if (ast_sorcery_apply_default(sorcery, "test", "dummy", NULL) != AST_SORCERY_APPLY_FAIL) {
ast_test_status_update(test, "Successfully set a default wizard that doesn't exist\n");
return AST_TEST_FAIL;
}
Mark Michelson
committed
if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) {
ast_test_status_update(test, "Failed to set a known wizard as a default\n");
return AST_TEST_FAIL;
}
Mark Michelson
committed
if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_DEFAULT_UNNECESSARY) {
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
ast_test_status_update(test, "Successfully set a default wizard on a type twice\n");
return AST_TEST_FAIL;
}
return AST_TEST_PASS;
}
AST_TEST_DEFINE(apply_config)
{
struct ast_flags flags = { CONFIG_FLAG_NOCACHE };
struct ast_config *config;
RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
switch (cmd) {
case TEST_INIT:
info->name = "apply_config";
info->category = "/main/sorcery/";
info->summary = "sorcery object mapping configuration unit test";
info->description =
"Test configured object mapping in sorcery";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
if (!(config = ast_config_load2("sorcery.conf", "test_sorcery", flags))) {
ast_test_status_update(test, "Sorcery configuration file not present - skipping apply_config test\n");
return AST_TEST_NOT_RUN;
}
if (!ast_category_get(config, "test_sorcery_section", NULL)) {
ast_test_status_update(test, "Sorcery configuration file does not have test_sorcery section\n");
ast_config_destroy(config);
return AST_TEST_NOT_RUN;
}
ast_config_destroy(config);
if (!(sorcery = ast_sorcery_open())) {
ast_test_status_update(test, "Failed to open sorcery structure\n");
return AST_TEST_FAIL;
}
Mark Michelson
committed
if (ast_sorcery_apply_config(sorcery, "test_sorcery_section") != AST_SORCERY_APPLY_SUCCESS) {
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
ast_test_status_update(test, "Failed to apply configured object mappings\n");
return AST_TEST_FAIL;
}
return AST_TEST_PASS;
}
AST_TEST_DEFINE(object_register)
{
RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
switch (cmd) {
case TEST_INIT:
info->name = "object_register";
info->category = "/main/sorcery/";
info->summary = "sorcery object type registration unit test";
info->description =
"Test object type registration in sorcery";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
if (!(sorcery = ast_sorcery_open())) {
ast_test_status_update(test, "Failed to open structure\n");
return AST_TEST_FAIL;
}
Mark Michelson
committed
if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) {
ast_test_status_update(test, "Failed to set a known wizard as a default\n");
return AST_TEST_FAIL;
}
if (ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
ast_test_status_update(test, "Failed to register object type\n");
return AST_TEST_FAIL;
}
if (!ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
ast_test_status_update(test, "Registered object type a second time, despite it being registered already\n");
return AST_TEST_FAIL;
}
return AST_TEST_PASS;
}
AST_TEST_DEFINE(object_register_without_mapping)
{
RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
switch (cmd) {
case TEST_INIT:
info->name = "object_register_without_mapping";
info->category = "/main/sorcery/";
info->summary = "sorcery object type registration (without mapping) unit test";
info->description =
"Test object type registration when no mapping exists in sorcery";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
if (!(sorcery = ast_sorcery_open())) {
ast_test_status_update(test, "Failed to open sorcery structure\n");
return AST_TEST_FAIL;
}
if (!ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
ast_test_status_update(test, "Registered object type when no object mapping exists\n");
return AST_TEST_FAIL;
}
return AST_TEST_PASS;
}
AST_TEST_DEFINE(object_field_register)
{
RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
switch (cmd) {
case TEST_INIT:
info->name = "object_field_register";
info->category = "/main/sorcery/";
info->summary = "sorcery object field registration unit test";
info->description =
"Test object field registration in sorcery with a provided id";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
if (!(sorcery = ast_sorcery_open())) {
ast_test_status_update(test, "Failed to open sorcery structure\n");
return AST_TEST_FAIL;
}
if (!ast_sorcery_object_field_register_nodoc(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob))) {
ast_test_status_update(test, "Registered an object field successfully when no mappings or object types exist\n");
return AST_TEST_FAIL;
}
Mark Michelson
committed
if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) {
ast_test_status_update(test, "Failed to set a known wizard as a default\n");
return AST_TEST_FAIL;
}
if (!ast_sorcery_object_field_register_nodoc(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob))) {
ast_test_status_update(test, "Registered an object field successfully when object type does not exist\n");
return AST_TEST_FAIL;
}
if (ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
ast_test_status_update(test, "Failed to register object type\n");
return AST_TEST_FAIL;
}
if (ast_sorcery_object_field_register_nodoc(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob))) {
ast_test_status_update(test, "Could not successfully register object field when mapping and object type exists\n");
return AST_TEST_FAIL;
}
return AST_TEST_PASS;
}
Joshua Colp
committed
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
AST_TEST_DEFINE(object_fields_register)
{
RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
switch (cmd) {
case TEST_INIT:
info->name = "object_fields_register";
info->category = "/main/sorcery/";
info->summary = "sorcery object regex fields registration unit test";
info->description =
"Test object regex fields registration in sorcery with a provided id";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
if (!(sorcery = ast_sorcery_open())) {
ast_test_status_update(test, "Failed to open sorcery structure\n");
return AST_TEST_FAIL;
}
if (!ast_sorcery_object_fields_register(sorcery, "test", "^toast-", test_sorcery_regex_handler, test_sorcery_regex_fields)) {
ast_test_status_update(test, "Registered a regex object field successfully when no mappings or object types exist\n");
return AST_TEST_FAIL;
}
Mark Michelson
committed
if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) {
Joshua Colp
committed
ast_test_status_update(test, "Failed to set a known wizard as a default\n");
return AST_TEST_FAIL;
}
if (!ast_sorcery_object_fields_register(sorcery, "test", "^toast-", test_sorcery_regex_handler, test_sorcery_regex_fields)) {
ast_test_status_update(test, "Registered a regex object field successfully when object type does not exist\n");
return AST_TEST_FAIL;
}
if (ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
Joshua Colp
committed
ast_test_status_update(test, "Failed to register object type\n");
return AST_TEST_FAIL;
}
if (ast_sorcery_object_fields_register(sorcery, "test", "^toast-", test_sorcery_regex_handler, test_sorcery_regex_fields)) {
ast_test_status_update(test, "Registered a regex object field successfully when no mappings or object types exist\n");
return AST_TEST_FAIL;
}
return AST_TEST_PASS;
}
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
AST_TEST_DEFINE(object_alloc_with_id)
{
int res = AST_TEST_PASS;
RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
switch (cmd) {
case TEST_INIT:
info->name = "object_alloc_with_id";
info->category = "/main/sorcery/";
info->summary = "sorcery object allocation (with id) unit test";
info->description =
"Test object allocation in sorcery with a provided id";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
if (!(sorcery = alloc_and_initialize_sorcery())) {
ast_test_status_update(test, "Failed to open sorcery structure\n");
return AST_TEST_FAIL;
}
if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
ast_test_status_update(test, "Failed to allocate a known object type\n");
res = AST_TEST_FAIL;
} else if (ast_strlen_zero(ast_sorcery_object_get_id(obj))) {
ast_test_status_update(test, "Allocated object has empty id when it should not\n");
res = AST_TEST_FAIL;
} else if (strcmp(ast_sorcery_object_get_id(obj), "blah")) {
ast_test_status_update(test, "Allocated object does not have correct id\n");
res = AST_TEST_FAIL;
} else if (ast_strlen_zero(ast_sorcery_object_get_type(obj))) {
ast_test_status_update(test, "Allocated object has empty type when it should not\n");
res = AST_TEST_FAIL;
} else if (strcmp(ast_sorcery_object_get_type(obj), "test")) {
ast_test_status_update(test, "Allocated object does not have correct type\n");
res = AST_TEST_FAIL;
} else if ((obj->bob != 5) || (obj->joe != 10)) {
ast_test_status_update(test, "Allocated object does not have defaults set as it should\n");
res = AST_TEST_FAIL;
}
return res;
}
AST_TEST_DEFINE(object_alloc_without_id)
{
int res = AST_TEST_PASS;
RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
switch (cmd) {
case TEST_INIT:
info->name = "object_alloc_without_id";
info->category = "/main/sorcery/";
info->summary = "sorcery object allocation (without id) unit test";
info->description =
"Test object allocation in sorcery with no provided id";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
if (!(sorcery = alloc_and_initialize_sorcery())) {
ast_test_status_update(test, "Failed to open sorcery structure\n");
return AST_TEST_FAIL;
}
if (!(obj = ast_sorcery_alloc(sorcery, "test", NULL))) {
ast_test_status_update(test, "Failed to allocate a known object type\n");
res = AST_TEST_FAIL;
} else if (ast_strlen_zero(ast_sorcery_object_get_id(obj))) {
ast_test_status_update(test, "Allocated object has empty id when it should not\n");
res = AST_TEST_FAIL;
}
return res;
}
AST_TEST_DEFINE(object_copy)
{
int res = AST_TEST_PASS;
RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
RAII_VAR(struct test_sorcery_object *, copy, NULL, ao2_cleanup);
switch (cmd) {
case TEST_INIT:
info->name = "object_copy";
info->category = "/main/sorcery/";
info->summary = "sorcery object copy unit test";
info->description =
"Test object copy in sorcery";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
if (!(sorcery = alloc_and_initialize_sorcery())) {
ast_test_status_update(test, "Failed to open sorcery structure\n");
return AST_TEST_FAIL;
}
if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
ast_test_status_update(test, "Failed to allocate a known object type\n");
return AST_TEST_FAIL;
}
obj->bob = 50;
obj->joe = 100;
jim_handler(NULL, ast_variable_new("jim", "444", ""), obj);
jim_handler(NULL, ast_variable_new("jim", "555", ""), obj);
if (!(copy = ast_sorcery_copy(sorcery, obj))) {
ast_test_status_update(test, "Failed to create a copy of a known valid object\n");
res = AST_TEST_FAIL;
} else if (copy == obj) {
ast_test_status_update(test, "Created copy is actually the original object\n");
res = AST_TEST_FAIL;
} else if (copy->bob != obj->bob) {
ast_test_status_update(test, "Value of 'bob' on newly created copy is not the same as original\n");
res = AST_TEST_FAIL;
} else if (copy->joe != obj->joe) {
ast_test_status_update(test, "Value of 'joe' on newly created copy is not the same as original\n");
res = AST_TEST_FAIL;
} else if (!copy->jim) {
ast_test_status_update(test, "A new ast_variable was not created for 'jim'\n");
res = AST_TEST_FAIL;
} else if (copy->jim == obj->jim) {
ast_test_status_update(test, "Created copy of 'jim' is actually the ogirinal 'jim'\n");
res = AST_TEST_FAIL;
} else if (strcmp(copy->jim->value, obj->jim->value)) {
ast_test_status_update(test, "Value of 1st 'jim' on newly created copy is not the same as original\n");
res = AST_TEST_FAIL;
} else if (!copy->jim->next) {
ast_test_status_update(test, "A new ast_variable was not created for 2nd 'jim'\n");
res = AST_TEST_FAIL;
} else if (strcmp(copy->jim->next->value, obj->jim->next->value)) {
ast_test_status_update(test, "Value of 2nd 'jim' (%s %s) on newly created copy is not the same as original (%s %s)\n",
copy->jim->value, copy->jim->next->value, obj->jim->value, obj->jim->next->value);
res = AST_TEST_FAIL;
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
AST_TEST_DEFINE(object_copy_native)
{
int res = AST_TEST_PASS;
RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
RAII_VAR(struct test_sorcery_object *, copy, NULL, ao2_cleanup);
switch (cmd) {
case TEST_INIT:
info->name = "object_copy_native";
info->category = "/main/sorcery/";
info->summary = "sorcery object native copy unit test";
info->description =
"Test object native copy in sorcery";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
if (!(sorcery = alloc_and_initialize_sorcery())) {
ast_test_status_update(test, "Failed to open sorcery structure\n");
return AST_TEST_FAIL;
}
ast_sorcery_object_set_copy_handler(sorcery, "test", test_sorcery_copy);
if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
ast_test_status_update(test, "Failed to allocate a known object type\n");
return AST_TEST_FAIL;
}
obj->bob = 50;
obj->joe = 100;
if (!(copy = ast_sorcery_copy(sorcery, obj))) {
ast_test_status_update(test, "Failed to create a copy of a known valid object\n");
res = AST_TEST_FAIL;
} else if (copy == obj) {
ast_test_status_update(test, "Created copy is actually the original object\n");
res = AST_TEST_FAIL;
} else if (copy->bob != 10) {
ast_test_status_update(test, "Value of 'bob' on newly created copy is not the predefined native copy value\n");
res = AST_TEST_FAIL;
} else if (copy->joe != 20) {
ast_test_status_update(test, "Value of 'joe' on newly created copy is not the predefined native copy value\n");
res = AST_TEST_FAIL;
} else if (!copy->jim) {
ast_test_status_update(test, "A new ast_variable was not created for 'jim'\n");
res = AST_TEST_FAIL;
} else if (strcmp(copy->jim->value, "444")) {
ast_test_status_update(test, "Value of 'jim' on newly created copy is not the predefined native copy value\n");
res = AST_TEST_FAIL;
}
return res;
}
AST_TEST_DEFINE(object_diff)
{
RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
RAII_VAR(struct test_sorcery_object *, obj1, NULL, ao2_cleanup);
RAII_VAR(struct test_sorcery_object *, obj2, NULL, ao2_cleanup);
RAII_VAR(struct ast_variable *, changes, NULL, ast_variables_destroy);
struct ast_variable *field;
int res = AST_TEST_PASS;
switch (cmd) {
case TEST_INIT:
info->name = "object_diff";
info->category = "/main/sorcery/";
info->summary = "sorcery object diff unit test";
info->description =
"Test object diffing in sorcery";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
if (!(sorcery = alloc_and_initialize_sorcery())) {
ast_test_status_update(test, "Failed to open sorcery structure\n");
return AST_TEST_FAIL;
}
if (!(obj1 = ast_sorcery_alloc(sorcery, "test", "blah"))) {
ast_test_status_update(test, "Failed to allocate a known object type\n");
return AST_TEST_FAIL;
}
obj1->bob = 99;
obj1->joe = 55;
jim_handler(NULL, ast_variable_new("jim", "444", ""), obj1);
jim_handler(NULL, ast_variable_new("jim", "555", ""), obj1);
if (!(obj2 = ast_sorcery_alloc(sorcery, "test", "blah2"))) {
ast_test_status_update(test, "Failed to allocate a second known object type\n");
return AST_TEST_FAIL;
}
obj2->bob = 99;
obj2->joe = 42;
jim_handler(NULL, ast_variable_new("jim", "444", ""), obj2);
jim_handler(NULL, ast_variable_new("jim", "666", ""), obj2);
jim_handler(NULL, ast_variable_new("jim", "777", ""), obj2);
if (ast_sorcery_diff(sorcery, obj1, obj2, &changes)) {
ast_test_status_update(test, "Failed to diff obj1 and obj2\n");
} else if (!changes) {
ast_test_status_update(test, "Failed to produce a diff of two objects, despite there being differences\n");
return AST_TEST_FAIL;
}
for (field = changes; field; field = field->next) {
if (!strcmp(field->name, "joe")) {
if (strcmp(field->value, "42")) {
ast_test_status_update(test,
"Object diff produced unexpected value '%s' for joe\n", field->value);
res = AST_TEST_FAIL;
}
} else if (!strcmp(field->name, "jim")) {
jims++;
if (!strcmp(field->value, "555")) {
ast_test_status_update(test,
"Object diff produced unexpected value '%s' for jim\n", field->value);
res = AST_TEST_FAIL;
}
} else {
ast_test_status_update(test, "Object diff produced unexpected field '%s'\n",
field->name);
res = AST_TEST_FAIL;
}
}
if (jims != 2) {
ast_test_status_update(test, "Object diff didn't produce 2 jims\n");
res = AST_TEST_FAIL;