Asterisk - The Open Source Telephony Project  18.5.0
test_sorcery_realtime.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Joshua Colp <[email protected]>
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 <[email protected]>
24  *
25  */
26 
27 /*** MODULEINFO
28  <depend>TEST_FRAMEWORK</depend>
29  <support_level>core</support_level>
30  ***/
31 
32 #include "asterisk.h"
33 
34 #include "asterisk/test.h"
35 #include "asterisk/module.h"
36 #include "asterisk/sorcery.h"
37 #include "asterisk/astdb.h"
38 #include "asterisk/logger.h"
39 
40 /*! \brief Configuration structure which contains all stored objects */
42 
43 static struct ast_variable *realtime_sorcery(const char *database, const char *table, const struct ast_variable *fields)
44 {
45  char *object_id = NULL;
46 
47  while ((object_id = ast_category_browse(realtime_objects, object_id))) {
48  if (!ast_variable_lists_match(ast_category_root(realtime_objects, object_id), fields, 0)) {
49  continue;
50  }
51 
52  return ast_variables_dup(ast_category_root(realtime_objects, object_id));
53  }
54 
55  return NULL;
56 }
57 
58 static struct ast_config *realtime_sorcery_multi(const char *database, const char *table, const struct ast_variable *fields)
59 {
60  struct ast_config *objects;
61  char *object_id = NULL;
62 
63  if (!(objects = ast_config_new())) {
64  return NULL;
65  }
66 
67  while ((object_id = ast_category_browse(realtime_objects, object_id))) {
68  struct ast_category *object;
69  const struct ast_variable *object_fields = ast_category_root(realtime_objects, object_id);
70 
71  if (!ast_variable_lists_match(object_fields, fields, 0)) {
72  continue;
73  }
74 
75  if (!(object = ast_category_new("", "", 0))) {
76  ast_config_destroy(objects);
77  return NULL;
78  }
79 
80  ast_variable_append(object, ast_variables_dup(ast_category_root(realtime_objects, object_id)));
81  ast_category_append(objects, object);
82  }
83 
84  return objects;
85 }
86 
87 static int realtime_sorcery_update(const char *database, const char *table, const char *keyfield, const char *entity, const struct ast_variable *fields)
88 {
89  struct ast_category *object, *found;
90 
91  if (!(found = ast_category_get(realtime_objects, entity, NULL))) {
92  return 0;
93  } else if (!(object = ast_category_new(entity, "", 0))) {
94  return -1;
95  }
96 
97  ast_category_delete(realtime_objects, found);
98  ast_variable_append(object, ast_variables_dup((struct ast_variable*)fields));
99  ast_variable_append(object, ast_variable_new(keyfield, entity, ""));
100  ast_category_append(realtime_objects, object);
101 
102  return 1;
103 }
104 
105 static int realtime_sorcery_store(const char *database, const char *table, const struct ast_variable *fields)
106 {
107  /* The key field is explicit within res_sorcery_realtime */
108  const struct ast_variable *keyfield = ast_variable_find_variable_in_list(fields, "id");
109  struct ast_category *object;
110 
111  if (!keyfield || ast_category_exist(realtime_objects, keyfield->value, NULL) || !(object = ast_category_new(keyfield->value, "", 0))) {
112  return -1;
113  }
114 
115  ast_variable_append(object, ast_variables_dup((struct ast_variable*)fields));
116  ast_category_append(realtime_objects, object);
117 
118  return 1;
119 }
120 
121 static int realtime_sorcery_destroy(const char *database, const char *table, const char *keyfield, const char *entity, const struct ast_variable *fields)
122 {
123  struct ast_category *found;
124  if (!(found = ast_category_get(realtime_objects, entity, NULL))) {
125  return 0;
126  }
127 
128  ast_category_delete(realtime_objects, found);
129 
130  return 1;
131 }
132 
134  .name = "sorcery_realtime_test",
135  .realtime_func = realtime_sorcery,
136  .realtime_multi_func = realtime_sorcery_multi,
137  .update_func = realtime_sorcery_update,
138  .store_func = realtime_sorcery_store,
139  .destroy_func = realtime_sorcery_destroy,
140 };
141 
142 /*! \brief Dummy sorcery object */
143 struct test_sorcery_object {
144  SORCERY_OBJECT(details);
145  unsigned int bob;
146  unsigned int joe;
147 };
148 
149 /*! \brief Internal function to allocate a test object */
150 static void *test_sorcery_object_alloc(const char *id)
151 {
152  return ast_sorcery_generic_alloc(sizeof(struct test_sorcery_object), NULL);
153 }
154 
156 {
157  struct ast_sorcery *sorcery;
158 
159  if (!(sorcery = ast_sorcery_open())) {
160  return NULL;
161  }
162 
163  if ((ast_sorcery_apply_default(sorcery, "test", "realtime", table) != AST_SORCERY_APPLY_SUCCESS) ||
165  !(realtime_objects = ast_config_new())) {
166  ast_sorcery_unref(sorcery);
167  return NULL;
168  }
169 
170  ast_sorcery_object_field_register_nodoc(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
171  ast_sorcery_object_field_register_nodoc(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
172 
173  return sorcery;
174 }
175 
177 {
178  ast_config_destroy(realtime_objects);
179  realtime_objects = NULL;
180  ast_sorcery_unref(sorcery);
181 }
182 
183 AST_TEST_DEFINE(object_create)
184 {
186  RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
187 
188  switch (cmd) {
189  case TEST_INIT:
190  info->name = "object_create";
191  info->category = "/res/sorcery_realtime/";
192  info->summary = "sorcery realtime object creation unit test";
193  info->description =
194  "Test object creation in sorcery using realtime wizard";
195  return AST_TEST_NOT_RUN;
196  case TEST_EXECUTE:
197  break;
198  }
199 
200  if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
201  ast_test_status_update(test, "Failed to open sorcery structure\n");
202  return AST_TEST_FAIL;
203  }
204 
205  if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
206  ast_test_status_update(test, "Failed to allocate a known object type\n");
207  return AST_TEST_FAIL;
208  }
209 
210  if (ast_sorcery_create(sorcery, obj)) {
211  ast_test_status_update(test, "Failed to create object using realtime wizard\n");
212  return AST_TEST_FAIL;
213  }
214 
215  return AST_TEST_PASS;
216 }
217 
218 AST_TEST_DEFINE(object_retrieve_id)
219 {
221  RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
222 
223  switch (cmd) {
224  case TEST_INIT:
225  info->name = "object_retrieve_id";
226  info->category = "/res/sorcery_realtime/";
227  info->summary = "sorcery object retrieval using id unit test";
228  info->description =
229  "Test object retrieval using id in sorcery with realtime wizard";
230  return AST_TEST_NOT_RUN;
231  case TEST_EXECUTE:
232  break;
233  }
234 
235  if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
236  ast_test_status_update(test, "Failed to open sorcery structure\n");
237  return AST_TEST_FAIL;
238  }
239 
240  if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
241  ast_test_status_update(test, "Failed to allocate a known object type\n");
242  return AST_TEST_FAIL;
243  }
244 
245  if (ast_sorcery_create(sorcery, obj)) {
246  ast_test_status_update(test, "Failed to create object using astdb wizard\n");
247  return AST_TEST_FAIL;
248  }
249 
250  ao2_cleanup(obj);
251 
252  if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah2"))) {
253  ast_test_status_update(test, "Failed to allocate second instance of a known object type\n");
254  return AST_TEST_FAIL;
255  }
256 
257  if (ast_sorcery_create(sorcery, obj)) {
258  ast_test_status_update(test, "Failed to create second object using astdb wizard\n");
259  return AST_TEST_FAIL;
260  }
261 
262  ao2_cleanup(obj);
263 
264  if (!(obj = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
265  ast_test_status_update(test, "Failed to retrieve properly created object using id of 'blah'\n");
266  return AST_TEST_FAIL;
267  } else if (strcmp(ast_sorcery_object_get_id(obj), "blah")) {
268  ast_test_status_update(test, "Retrieved object does not have correct id\n");
269  return AST_TEST_FAIL;
270  }
271 
272  return AST_TEST_PASS;
273 }
274 
275 AST_TEST_DEFINE(object_retrieve_field)
276 {
278  RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
279  RAII_VAR(struct ast_variable *, fields, ast_variable_new("joe", "42", ""), ast_variables_destroy);
280 
281  switch (cmd) {
282  case TEST_INIT:
283  info->name = "object_retrieve_field";
284  info->category = "/res/sorcery_realtime/";
285  info->summary = "sorcery object retrieval using a specific field unit test";
286  info->description =
287  "Test object retrieval using a specific field in sorcery with realtime wizard";
288  return AST_TEST_NOT_RUN;
289  case TEST_EXECUTE:
290  break;
291  }
292 
293  if (!fields) {
294  ast_test_status_update(test, "Failed to create fields for object retrieval attempt\n");
295  return AST_TEST_FAIL;
296  }
297 
298  if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
299  ast_test_status_update(test, "Failed to open sorcery structure\n");
300  return AST_TEST_FAIL;
301  }
302 
303  if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
304  ast_test_status_update(test, "Failed to allocate a known object type\n");
305  return AST_TEST_FAIL;
306  }
307 
308  obj->joe = 42;
309 
310  if (ast_sorcery_create(sorcery, obj)) {
311  ast_test_status_update(test, "Failed to create object using realtime wizard\n");
312  return AST_TEST_FAIL;
313  }
314 
315  ao2_cleanup(obj);
316 
317  if (!(obj = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_DEFAULT, fields))) {
318  ast_test_status_update(test, "Failed to retrieve properly created object using 'joe' field\n");
319  return AST_TEST_FAIL;
320  }
321 
322  ao2_cleanup(obj);
323  ast_variables_destroy(fields);
324 
325  if (!(fields = ast_variable_new("joe", "49", ""))) {
326  ast_test_status_update(test, "Failed to create fields for object retrieval attempt\n");
327  return AST_TEST_FAIL;
328  }
329 
330  if ((obj = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_DEFAULT, fields))) {
331  ast_test_status_update(test, "Retrieved an object using a field with an in-correct value... that should not happen\n");
332  return AST_TEST_FAIL;
333  }
334 
335  return AST_TEST_PASS;
336 }
337 
338 AST_TEST_DEFINE(object_retrieve_multiple_all)
339 {
341  RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
342  RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
343 
344  switch (cmd) {
345  case TEST_INIT:
346  info->name = "object_retrieve_multiple_all";
347  info->category = "/res/sorcery_realtime/";
348  info->summary = "sorcery multiple object retrieval unit test";
349  info->description =
350  "Test multiple object retrieval in sorcery using realtime wizard";
351  return AST_TEST_NOT_RUN;
352  case TEST_EXECUTE:
353  break;
354  }
355 
356  if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
357  ast_test_status_update(test, "Failed to open sorcery structure\n");
358  return AST_TEST_FAIL;
359  }
360 
361  if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
362  ast_test_status_update(test, "Failed to allocate a known object type\n");
363  return AST_TEST_FAIL;
364  }
365 
366  if (ast_sorcery_create(sorcery, obj)) {
367  ast_test_status_update(test, "Failed to create object using realtime wizard\n");
368  return AST_TEST_FAIL;
369  }
370 
371  ao2_cleanup(obj);
372 
373  if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah2"))) {
374  ast_test_status_update(test, "Failed to allocate second instance of a known object type\n");
375  return AST_TEST_FAIL;
376  }
377 
378  if (ast_sorcery_create(sorcery, obj)) {
379  ast_test_status_update(test, "Failed to create second object using realtime wizard\n");
380  return AST_TEST_FAIL;
381  }
382 
384  ast_test_status_update(test, "Failed to retrieve a container of all objects\n");
385  return AST_TEST_FAIL;
386  } else if (ao2_container_count(objects) != 2) {
387  ast_test_status_update(test, "Received a container with no objects in it when there should be some\n");
388  return AST_TEST_FAIL;
389  }
390 
391  return AST_TEST_PASS;
392 }
393 
394 AST_TEST_DEFINE(object_retrieve_multiple_all_nofetch)
395 {
397  RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
398  RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
399 
400  switch (cmd) {
401  case TEST_INIT:
402  info->name = "object_retrieve_multiple_all_nofetch";
403  info->category = "/res/sorcery_realtime/";
404  info->summary = "sorcery multiple object retrieval unit test";
405  info->description =
406  "Test multiple object retrieval in sorcery using realtime wizard";
407  return AST_TEST_NOT_RUN;
408  case TEST_EXECUTE:
409  break;
410  }
411 
412  if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test,allow_unqualified_fetch=no"))) {
413  ast_test_status_update(test, "Failed to open sorcery structure\n");
414  return AST_TEST_FAIL;
415  }
416 
417  if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
418  ast_test_status_update(test, "Failed to allocate a known object type\n");
419  return AST_TEST_FAIL;
420  }
421 
422  if (ast_sorcery_create(sorcery, obj)) {
423  ast_test_status_update(test, "Failed to create object using realtime wizard\n");
424  return AST_TEST_FAIL;
425  }
426 
427  ao2_cleanup(obj);
428 
429  if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah2"))) {
430  ast_test_status_update(test, "Failed to allocate second instance of a known object type\n");
431  return AST_TEST_FAIL;
432  }
433 
434  if (ast_sorcery_create(sorcery, obj)) {
435  ast_test_status_update(test, "Failed to create second object using realtime wizard\n");
436  return AST_TEST_FAIL;
437  }
438 
440  ast_test_status_update(test, "Failed to retrieve a container of all objects\n");
441  return AST_TEST_FAIL;
442  } else if (ao2_container_count(objects) != 0) {
443  ast_test_status_update(test, "Received a container with objects in it when there should be none\n");
444  return AST_TEST_FAIL;
445  }
446 
447  return AST_TEST_PASS;
448 }
449 
450 
451 AST_TEST_DEFINE(object_retrieve_multiple_field)
452 {
454  RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
455  RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
456  RAII_VAR(struct ast_variable *, fields, ast_variable_new("joe", "6", ""), ast_variables_destroy);
457 
458  switch (cmd) {
459  case TEST_INIT:
460  info->name = "object_retrieve_multiple_field";
461  info->category = "/res/sorcery_realtime/";
462  info->summary = "sorcery multiple object retrieval unit test";
463  info->description =
464  "Test multiple object retrieval in sorcery using fields using realtime wizard";
465  return AST_TEST_NOT_RUN;
466  case TEST_EXECUTE:
467  break;
468  }
469 
470  if (!fields) {
471  ast_test_status_update(test, "Failed to create fields for multiple retrieve\n");
472  return AST_TEST_FAIL;
473  }
474 
475  if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
476  ast_test_status_update(test, "Failed to open sorcery structure\n");
477  return AST_TEST_FAIL;
478  }
479 
480  if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
481  ast_test_status_update(test, "Failed to allocate a known object type\n");
482  return AST_TEST_FAIL;
483  }
484 
485  obj->joe = 6;
486 
487  if (ast_sorcery_create(sorcery, obj)) {
488  ast_test_status_update(test, "Failed to create object using realtime wizard\n");
489  return AST_TEST_FAIL;
490  }
491 
492  if (!(objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE, fields))) {
493  ast_test_status_update(test, "Failed to retrieve a container of all objects\n");
494  return AST_TEST_FAIL;
495  } else if (ao2_container_count(objects) != 1) {
496  ast_test_status_update(test, "Received a container with no objects in it when there should be some\n");
497  return AST_TEST_FAIL;
498  }
499 
500  ao2_cleanup(objects);
501  ast_variables_destroy(fields);
502 
503  if (!(fields = ast_variable_new("joe", "7", ""))) {
504  ast_test_status_update(test, "Failed to create fields for multiple retrieval\n");
505  return AST_TEST_FAIL;
506  } else if (!(objects = ast_sorcery_retrieve_by_fields(sorcery, "test", AST_RETRIEVE_FLAG_MULTIPLE, fields))) {
507  ast_test_status_update(test, "Failed to retrieve an empty container when retrieving multiple\n");
508  return AST_TEST_FAIL;
509  } else if (ao2_container_count(objects)) {
510  ast_test_status_update(test, "Received a container with objects when there should be none in it\n");
511  return AST_TEST_FAIL;
512  }
513 
514  return AST_TEST_PASS;
515 }
516 
517 AST_TEST_DEFINE(object_retrieve_regex)
518 {
520  RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
521  RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
522 
523  switch (cmd) {
524  case TEST_INIT:
525  info->name = "object_retrieve_regex";
526  info->category = "/res/sorcery_realtime/";
527  info->summary = "sorcery multiple object retrieval using regex unit test";
528  info->description =
529  "Test multiple object retrieval in sorcery using regular expression for matching using realtime wizard";
530  return AST_TEST_NOT_RUN;
531  case TEST_EXECUTE:
532  break;
533  }
534 
535  if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
536  ast_test_status_update(test, "Failed to open sorcery structure\n");
537  return AST_TEST_FAIL;
538  }
539 
540  if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah-98joe"))) {
541  ast_test_status_update(test, "Failed to allocate a known object type\n");
542  return AST_TEST_FAIL;
543  }
544 
545  if (ast_sorcery_create(sorcery, obj)) {
546  ast_test_status_update(test, "Failed to create object using realtime wizard\n");
547  return AST_TEST_FAIL;
548  }
549 
550  ao2_cleanup(obj);
551 
552  if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah-93joe"))) {
553  ast_test_status_update(test, "Failed to allocate second instance of a known object type\n");
554  return AST_TEST_FAIL;
555  }
556 
557  if (ast_sorcery_create(sorcery, obj)) {
558  ast_test_status_update(test, "Failed to create second object using astdb wizard\n");
559  return AST_TEST_FAIL;
560  }
561 
562  ao2_cleanup(obj);
563 
564  if (!(obj = ast_sorcery_alloc(sorcery, "test", "neener-93joe"))) {
565  ast_test_status_update(test, "Failed to allocate third instance of a known object type\n");
566  return AST_TEST_FAIL;
567  }
568 
569  if (ast_sorcery_create(sorcery, obj)) {
570  ast_test_status_update(test, "Failed to create third object using astdb wizard\n");
571  return AST_TEST_FAIL;
572  }
573 
574  if (!(objects = ast_sorcery_retrieve_by_regex(sorcery, "test", "blah-"))) {
575  ast_test_status_update(test, "Failed to retrieve a container of objects\n");
576  return AST_TEST_FAIL;
577  } else if (ao2_container_count(objects) != 2) {
578  ast_test_status_update(test, "Received a container with incorrect number of objects in it: %d instead of 2\n", ao2_container_count(objects));
579  return AST_TEST_FAIL;
580  }
581 
582  return AST_TEST_PASS;
583 }
584 
585 AST_TEST_DEFINE(object_retrieve_regex_nofetch)
586 {
588  RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
589  RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
590 
591  switch (cmd) {
592  case TEST_INIT:
593  info->name = "object_retrieve_regex_nofetch";
594  info->category = "/res/sorcery_realtime/";
595  info->summary = "sorcery multiple object retrieval using regex unit test";
596  info->description =
597  "Test multiple object retrieval in sorcery using regular expression for matching using realtime wizard";
598  return AST_TEST_NOT_RUN;
599  case TEST_EXECUTE:
600  break;
601  }
602 
603  if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test,allow_unqualified_fetch=no"))) {
604  ast_test_status_update(test, "Failed to open sorcery structure\n");
605  return AST_TEST_FAIL;
606  }
607 
608  if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah-98joe"))) {
609  ast_test_status_update(test, "Failed to allocate a known object type\n");
610  return AST_TEST_FAIL;
611  }
612 
613  if (ast_sorcery_create(sorcery, obj)) {
614  ast_test_status_update(test, "Failed to create object using realtime wizard\n");
615  return AST_TEST_FAIL;
616  }
617 
618  ao2_cleanup(obj);
619 
620  if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah-93joe"))) {
621  ast_test_status_update(test, "Failed to allocate second instance of a known object type\n");
622  return AST_TEST_FAIL;
623  }
624 
625  if (ast_sorcery_create(sorcery, obj)) {
626  ast_test_status_update(test, "Failed to create second object using astdb wizard\n");
627  return AST_TEST_FAIL;
628  }
629 
630  ao2_cleanup(obj);
631 
632  if (!(obj = ast_sorcery_alloc(sorcery, "test", "neener-93joe"))) {
633  ast_test_status_update(test, "Failed to allocate third instance of a known object type\n");
634  return AST_TEST_FAIL;
635  }
636 
637  if (ast_sorcery_create(sorcery, obj)) {
638  ast_test_status_update(test, "Failed to create third object using astdb wizard\n");
639  return AST_TEST_FAIL;
640  }
641 
642  if (!(objects = ast_sorcery_retrieve_by_regex(sorcery, "test", ""))) {
643  ast_test_status_update(test, "Failed to retrieve a container of objects\n");
644  return AST_TEST_FAIL;
645  } else if (ao2_container_count(objects) != 0) {
646  ast_test_status_update(test, "Received a container with incorrect number of objects in it: %d instead of 0\n", ao2_container_count(objects));
647  return AST_TEST_FAIL;
648  }
649 
650  return AST_TEST_PASS;
651 }
652 
653 AST_TEST_DEFINE(object_update)
654 {
656  RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
657  RAII_VAR(struct test_sorcery_object *, obj2, NULL, ao2_cleanup);
658 
659  switch (cmd) {
660  case TEST_INIT:
661  info->name = "object_update";
662  info->category = "/res/sorcery_realtime/";
663  info->summary = "sorcery object update unit test";
664  info->description =
665  "Test object updating in sorcery using realtime wizard";
666  return AST_TEST_NOT_RUN;
667  case TEST_EXECUTE:
668  break;
669  }
670 
671  if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
672  ast_test_status_update(test, "Failed to open sorcery structure\n");
673  return AST_TEST_FAIL;
674  }
675 
676  if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
677  ast_test_status_update(test, "Failed to allocate a known object type\n");
678  return AST_TEST_FAIL;
679  }
680 
681  if (ast_sorcery_create(sorcery, obj)) {
682  ast_test_status_update(test, "Failed to create object using realtime wizard\n");
683  return AST_TEST_FAIL;
684  }
685 
686  if (!(obj2 = ast_sorcery_copy(sorcery, obj))) {
687  ast_test_status_update(test, "Failed to allocate a known object type for updating\n");
688  return AST_TEST_FAIL;
689  }
690 
691  ao2_cleanup(obj);
692 
693  obj2->bob = 1000;
694  obj2->joe = 2000;
695 
696  if (ast_sorcery_update(sorcery, obj2)) {
697  ast_test_status_update(test, "Failed to update sorcery with new object\n");
698  return AST_TEST_FAIL;
699  }
700 
701  if (!(obj = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
702  ast_test_status_update(test, "Failed to retrieve properly updated object\n");
703  return AST_TEST_FAIL;
704  } else if ((obj->bob != obj2->bob) || (obj->joe != obj2->joe)) {
705  ast_test_status_update(test, "Object retrieved is not the updated object\n");
706  return AST_TEST_FAIL;
707  }
708 
709  return AST_TEST_PASS;
710 }
711 
712 AST_TEST_DEFINE(object_delete)
713 {
715  RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
716 
717  switch (cmd) {
718  case TEST_INIT:
719  info->name = "object_delete";
720  info->category = "/res/sorcery_realtime/";
721  info->summary = "sorcery object deletion unit test";
722  info->description =
723  "Test object deletion in sorcery using realtime wizard";
724  return AST_TEST_NOT_RUN;
725  case TEST_EXECUTE:
726  break;
727  }
728 
729  if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
730  ast_test_status_update(test, "Failed to open sorcery structure\n");
731  return AST_TEST_FAIL;
732  }
733 
734  if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
735  ast_test_status_update(test, "Failed to allocate a known object type\n");
736  return AST_TEST_FAIL;
737  }
738 
739  if (ast_sorcery_create(sorcery, obj)) {
740  ast_test_status_update(test, "Failed to create object using realtime wizard\n");
741  return AST_TEST_FAIL;
742  }
743 
744  if (ast_sorcery_delete(sorcery, obj)) {
745  ast_test_status_update(test, "Failed to delete object using realtime wizard\n");
746  return AST_TEST_FAIL;
747  }
748 
749  ao2_cleanup(obj);
750 
751  if ((obj = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
752  ast_test_status_update(test, "Retrieved deleted object that should not be there\n");
753  return AST_TEST_FAIL;
754  }
755 
756  return AST_TEST_PASS;
757 }
758 
759 AST_TEST_DEFINE(object_delete_uncreated)
760 {
762  RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
763 
764  switch (cmd) {
765  case TEST_INIT:
766  info->name = "object_delete_uncreated";
767  info->category = "/res/sorcery_realtime/";
768  info->summary = "sorcery object deletion unit test";
769  info->description =
770  "Test object deletion of an uncreated object in sorcery using realtime wizard";
771  return AST_TEST_NOT_RUN;
772  case TEST_EXECUTE:
773  break;
774  }
775 
776  if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
777  ast_test_status_update(test, "Failed to open sorcery structure\n");
778  return AST_TEST_FAIL;
779  }
780 
781  if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
782  ast_test_status_update(test, "Failed to allocate a known object type\n");
783  return AST_TEST_FAIL;
784  }
785 
786  if (!ast_sorcery_delete(sorcery, obj)) {
787  ast_test_status_update(test, "Successfully deleted an object which was never created\n");
788  return AST_TEST_FAIL;
789  }
790 
791  return AST_TEST_PASS;
792 }
793 
794 AST_TEST_DEFINE(object_allocate_on_retrieval)
795 {
797  RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
798  struct ast_category *cat;
799 
800  switch (cmd) {
801  case TEST_INIT:
802  info->name = "object_allocate_on_retrieval";
803  info->category = "/res/sorcery_realtime/";
804  info->summary = "sorcery object allocation upon retrieval unit test";
805  info->description =
806  "This test creates data in a realtime backend, not through sorcery. Sorcery is then\n"
807  "instructed to retrieve an object with the id of the object that was created in the\n"
808  "realtime backend. Sorcery should be able to allocate the object appropriately";
809  return AST_TEST_NOT_RUN;
810  case TEST_EXECUTE:
811  break;
812  }
813 
814  if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
815  ast_test_status_update(test, "Failed to open sorcery structure\n");
816  return AST_TEST_FAIL;
817  }
818 
819  cat = ast_category_new("blah", "", 0);
820  ast_variable_append(cat, ast_variable_new("id", "blah", ""));
821  ast_variable_append(cat, ast_variable_new("bob", "42", ""));
822  ast_variable_append(cat, ast_variable_new("joe", "93", ""));
823  ast_category_append(realtime_objects, cat);
824 
825  if (!(obj = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
826  ast_test_status_update(test, "Failed to allocate object 'blah' base on realtime data\n");
827  return AST_TEST_FAIL;
828  }
829 
830  if (obj->bob != 42) {
831  ast_test_status_update(test, "Object's 'bob' field does not have expected value: %u != 42\n",
832  obj->bob);
833  return AST_TEST_FAIL;
834  } else if (obj->joe != 93) {
835  ast_test_status_update(test, "Object's 'joe' field does not have expected value: %u != 93\n",
836  obj->joe);
837  return AST_TEST_FAIL;
838  }
839 
840  return AST_TEST_PASS;
841 }
842 
843 
844 AST_TEST_DEFINE(object_filter)
845 {
847  RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
848  struct ast_category *cat;
849 
850  switch (cmd) {
851  case TEST_INIT:
852  info->name = "object_filter";
853  info->category = "/res/sorcery_realtime/";
854  info->summary = "sorcery object field filter unit test";
855  info->description =
856  "This test creates data in a realtime backend, not through sorcery. In addition to\n"
857  "the object fields that have been registered with sorcery, there is data in the\n"
858  "realtime backend that is unknown to sorcery. When sorcery attempts to retrieve\n"
859  "the object from the realtime backend, the data unknown to sorcery should be\n"
860  "filtered out of the returned objectset, and the object should be successfully\n"
861  "allocated by sorcery";
862  return AST_TEST_NOT_RUN;
863  case TEST_EXECUTE:
864  break;
865  }
866 
867  if (!(sorcery = alloc_and_initialize_sorcery("sorcery_realtime_test"))) {
868  ast_test_status_update(test, "Failed to open sorcery structure\n");
869  return AST_TEST_FAIL;
870  }
871 
872  cat = ast_category_new("blah", "", 0);
873  ast_variable_append(cat, ast_variable_new("id", "blah", ""));
874  ast_variable_append(cat, ast_variable_new("bob", "42", ""));
875  ast_variable_append(cat, ast_variable_new("joe", "93", ""));
876  ast_variable_append(cat, ast_variable_new("fred", "50", ""));
877  ast_category_append(realtime_objects, cat);
878 
879  if (!(obj = ast_sorcery_retrieve_by_id(sorcery, "test", "blah"))) {
880  ast_test_status_update(test, "Failed to retrieve properly created object using id of 'blah'\n");
881  return AST_TEST_FAIL;
882  }
883 
884  if (obj->bob != 42) {
885  ast_test_status_update(test, "Object's 'bob' field does not have expected value: %u != 42\n",
886  obj->bob);
887  return AST_TEST_FAIL;
888  } else if (obj->joe != 93) {
889  ast_test_status_update(test, "Object's 'joe' field does not have expected value: %u != 93\n",
890  obj->joe);
891  return AST_TEST_FAIL;
892  }
893  return AST_TEST_PASS;
894 }
895 
896 static int unload_module(void)
897 {
898  ast_config_engine_deregister(&sorcery_config_engine);
899  AST_TEST_UNREGISTER(object_create);
900  AST_TEST_UNREGISTER(object_retrieve_id);
901  AST_TEST_UNREGISTER(object_retrieve_field);
902  AST_TEST_UNREGISTER(object_retrieve_multiple_all);
903  AST_TEST_UNREGISTER(object_retrieve_multiple_all_nofetch);
904  AST_TEST_UNREGISTER(object_retrieve_multiple_field);
905  AST_TEST_UNREGISTER(object_retrieve_regex);
906  AST_TEST_UNREGISTER(object_retrieve_regex_nofetch);
907  AST_TEST_UNREGISTER(object_update);
908  AST_TEST_UNREGISTER(object_delete);
909  AST_TEST_UNREGISTER(object_delete_uncreated);
910  AST_TEST_UNREGISTER(object_allocate_on_retrieval);
911  AST_TEST_UNREGISTER(object_filter);
912 
913  return 0;
914 }
915 
916 static int load_module(void)
917 {
918  ast_config_engine_register(&sorcery_config_engine);
919  ast_realtime_append_mapping("sorcery_realtime_test", "sorcery_realtime_test", "test", "test", 1);
920  AST_TEST_REGISTER(object_create);
921  AST_TEST_REGISTER(object_retrieve_id);
922  AST_TEST_REGISTER(object_retrieve_field);
923  AST_TEST_REGISTER(object_retrieve_multiple_all);
924  AST_TEST_REGISTER(object_retrieve_multiple_all_nofetch);
925  AST_TEST_REGISTER(object_retrieve_multiple_field);
926  AST_TEST_REGISTER(object_retrieve_regex);
927  AST_TEST_REGISTER(object_retrieve_regex_nofetch);
928  AST_TEST_REGISTER(object_update);
929  AST_TEST_REGISTER(object_delete);
930  AST_TEST_REGISTER(object_delete_uncreated);
931  AST_TEST_REGISTER(object_allocate_on_retrieval);
932  AST_TEST_REGISTER(object_filter);
933 
935 }
936 
937 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Sorcery Realtime Wizard test module");
struct ast_category * ast_category_new(const char *name, const char *in_file, int lineno)
Create a category.
Definition: extconf.c:2790
static struct ast_variable * realtime_sorcery(const char *database, const char *table, const struct ast_variable *fields)
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:567
#define ast_sorcery_object_field_register_nodoc(sorcery, type, name, default_val, opt_type, flags,...)
Register a field within an object without documentation.
Definition: sorcery.h:987
Asterisk main include file. File version handling, generic pbx functions.
Dummy sorcery object.
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1263
struct ast_category * ast_category_delete(struct ast_config *cfg, struct ast_category *category)
Delete a category.
Definition: main/config.c:1478
static int unload_module(void)
static int entity
Definition: isdn_lib.c:259
Structure for variables, used for configurations and for channel variables.
Test Framework API.
Perform no matching, return all objects.
Definition: sorcery.h:123
static struct ast_sorcery * alloc_and_initialize_sorcery(char *table)
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
int ast_category_exist(const struct ast_config *config, const char *category_name, const char *filter)
Check for category duplicates.
Definition: main/config.c:1061
Full structure for sorcery.
Definition: sorcery.c:230
int ast_config_engine_deregister(struct ast_config_engine *del)
Deregister config engine.
Definition: main/config.c:3006
int ast_config_engine_register(struct ast_config_engine *newconfig)
Register config engine.
Definition: main/config.c:2990
Return all matching objects.
Definition: sorcery.h:120
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3328
#define NULL
Definition: resample.c:96
struct ao2_container * ast_sorcery_retrieve_by_regex(const struct ast_sorcery *sorcery, const char *type, const char *regex)
Retrieve multiple objects using a regular expression on their id.
Definition: sorcery.c:1949
Configuration engine structure, used to define realtime drivers.
void * ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *type, const char *id)
Retrieve an object using its unique identifier.
Definition: sorcery.c:1853
#define ast_sorcery_unref(sorcery)
Decrease the reference count of a sorcery structure.
Definition: sorcery.h:1502
static char * table
Definition: cdr_odbc.c:58
struct ast_variable * ast_category_root(struct ast_config *config, char *cat)
returns the root ast_variable of a config
Definition: main/config.c:1162
int ast_sorcery_create(const struct ast_sorcery *sorcery, void *object)
Create and potentially persist an object using an available wizard.
Definition: sorcery.c:2057
static int load_module(void)
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:911
Type for default option handler for unsigned integers.
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
unsigned int joe
Definition: test_sorcery.c:47
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: extconf.c:1290
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
#define ast_variable_new(name, value, filename)
struct ast_config_engine sorcery_config_engine
struct ast_config * ast_config_new(void)
Create a new base configuration structure.
Definition: extconf.c:3276
Default retrieval flags.
Definition: sorcery.h:117
int ast_realtime_append_mapping(const char *name, const char *driver, const char *database, const char *table, int priority)
Add an explicit mapping for a family.
Definition: main/config.c:2868
static int realtime_sorcery_destroy(const char *database, const char *table, const char *keyfield, const char *entity, const struct ast_variable *fields)
int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object)
Delete an object.
Definition: sorcery.c:2233
static struct ast_config * realtime_sorcery_multi(const char *database, const char *table, const struct ast_variable *fields)
#define ast_sorcery_internal_object_register(sorcery, type, alloc, transform, apply)
Register an internal, hidden object type.
Definition: sorcery.h:868
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
def info(msg)
#define ast_sorcery_apply_default(sorcery, type, name, data)
Definition: sorcery.h:477
static void * test_sorcery_object_alloc(const char *id)
Internal function to allocate a test object.
void * ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, const char *id)
Allocate an object.
Definition: sorcery.c:1744
unsigned int bob
Definition: test_sorcery.c:46
Support for logging to various files, console and syslog Configuration in file logger.conf.
void * ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const char *type, unsigned int flags, struct ast_variable *fields)
Retrieve an object or multiple objects using specific fields.
Definition: sorcery.c:1897
void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
Definition: extconf.c:1178
void ast_category_append(struct ast_config *config, struct ast_category *cat)
Appends a category to a config.
Definition: extconf.c:2835
static struct ast_config * realtime_objects
Configuration structure which contains all stored objects.
static struct ast_sorcery * sorcery
int ast_variable_lists_match(const struct ast_variable *left, const struct ast_variable *right, int exact_match)
Tests 2 variable lists to see if they match.
Definition: main/config.c:772
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
void * ast_sorcery_copy(const struct ast_sorcery *sorcery, const void *object)
Create a copy of an object.
Definition: sorcery.c:1778
const struct ast_variable * ast_variable_find_variable_in_list(const struct ast_variable *list, const char *variable_name)
Gets a variable from a variable list by name.
Definition: main/config.c:740
#define ast_sorcery_open()
Definition: sorcery.h:408
static int realtime_sorcery_update(const char *database, const char *table, const char *keyfield, const char *entity, const struct ast_variable *fields)
Generic container type.
struct ast_category * ast_category_get(const struct ast_config *config, const char *category_name, const char *filter)
Retrieve a category if it exists.
Definition: main/config.c:1022
void * ast_sorcery_generic_alloc(size_t size, ao2_destructor_fn destructor)
Allocate a generic sorcery capable object.
Definition: sorcery.c:1728
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
Persistant data storage (akin to *doze registry)
struct ast_variable * ast_variables_dup(struct ast_variable *var)
Duplicate variable list.
Definition: main/config.c:545
AST_TEST_DEFINE(object_create)
static void deinitialize_sorcery(struct ast_sorcery *sorcery)
Sorcery Data Access Layer API.
int ast_sorcery_update(const struct ast_sorcery *sorcery, void *object)
Update an object.
Definition: sorcery.c:2145
static int realtime_sorcery_store(const char *database, const char *table, const struct ast_variable *fields)