Merge "Revert "app_voicemail: Remove need to subscribe to stasis""
[asterisk/asterisk.git] / tests / test_astobj2_weaken.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2015, CFWare, LLC
5  *
6  * Corey Farrell <git@cfware.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 astobj2 weakproxy test module
22  *
23  * \author Corey Farrell <git@cfware.com>
24  */
25
26 /*** MODULEINFO
27         <depend>TEST_FRAMEWORK</depend>
28         <support_level>core</support_level>
29  ***/
30
31 #include "asterisk.h"
32
33 #include "asterisk/utils.h"
34 #include "asterisk/module.h"
35 #include "asterisk/test.h"
36 #include "asterisk/astobj2.h"
37
38 static int destructor_called;
39 static int weakproxydestroyed;
40
41 static void test_obj_destructor(void *obj)
42 {
43         destructor_called++;
44 }
45
46 static void weakproxy_destructor(void *obj)
47 {
48         weakproxydestroyed++;
49 }
50
51 static void test_obj_destroy_notify(void *obj, void *data)
52 {
53         int *i = data;
54
55         ++*i;
56 }
57
58 struct my_weakproxy {
59         AO2_WEAKPROXY();
60         int f1;
61 };
62
63 AST_TEST_DEFINE(astobj2_weak1)
64 {
65         void *obj1 = NULL;
66         void *obj2 = NULL;
67         void *obj3 = NULL;
68         void *strong1 = NULL;
69         struct my_weakproxy *weakref1 = NULL;
70         struct my_weakproxy *weakref2 = NULL;
71         int notify0_called = 0;
72         int notify1_called = 0;
73         int notify2_called = 0;
74         int notify3_called = 0;
75
76         switch (cmd) {
77         case TEST_INIT:
78                 info->name = "astobj2_weak1";
79                 info->category = "/main/astobj2/";
80                 info->summary = "Test ao2 weak objects";
81                 info->description = "Test ao2 weak objects.";
82                 return AST_TEST_NOT_RUN;
83         case TEST_EXECUTE:
84                 break;
85         }
86
87         destructor_called = weakproxydestroyed = 0;
88         obj1 = ao2_t_alloc(0, test_obj_destructor, "obj1");
89         if (!obj1) {
90                 return AST_TEST_FAIL;
91         }
92
93         weakref1 = ao2_t_weakproxy_alloc(sizeof(*weakref1), weakproxy_destructor, "weakref1");
94         if (!weakref1) {
95                 ast_test_status_update(test, "Failed to allocate weakref1.\n");
96                 goto fail_cleanup;
97         }
98         weakref1->f1 = 5315;
99
100         if (ao2_weakproxy_subscribe(weakref1, test_obj_destroy_notify, &notify0_called, 0)) {
101                 ast_test_status_update(test, "Failed to subscribe to weakref1.\n");
102                 goto fail_cleanup;
103         }
104         if (!notify0_called) {
105                 ast_test_status_update(test, "Subscribe failed to immediately run callback for empty weakproxy.\n");
106                 goto fail_cleanup;
107         }
108
109         if (ao2_t_weakproxy_set_object(weakref1, obj1, 0, "set weakref1 to obj1")) {
110                 ast_test_status_update(test, "Failed to set obj1 on weakref1.\n");
111                 goto fail_cleanup;
112         }
113
114         if (ao2_weakproxy_subscribe(weakref1, test_obj_destroy_notify, &notify1_called, 0)) {
115                 ast_test_status_update(test, "Failed to add a subscription to weakref1.\n");
116                 goto fail_cleanup;
117         }
118
119         weakref2 = ao2_t_get_weakproxy(obj1, "get weakref2 from obj1");
120         if (weakref1 != weakref2) {
121                 ast_test_status_update(test, "weakref1 != weakref2.\n");
122                 goto fail_cleanup;
123         }
124
125         if (ao2_weakproxy_subscribe(weakref2, test_obj_destroy_notify, &notify2_called, 0)) {
126                 ast_test_status_update(test, "Failed to add a subscription to weakref2.\n");
127                 goto fail_cleanup;
128         }
129
130         if (ao2_weakproxy_subscribe(weakref2, test_obj_destroy_notify, &notify2_called, 0)) {
131                 ast_test_status_update(test, "Failed to add a duplicate subscription to weakref2.\n");
132                 goto fail_cleanup;
133         }
134
135         if (ao2_weakproxy_subscribe(weakref2, test_obj_destroy_notify, &notify2_called, 0)) {
136                 ast_test_status_update(test, "Failed to add a second duplicate subscription to weakref2.\n");
137                 goto fail_cleanup;
138         }
139
140         if (ao2_weakproxy_unsubscribe(weakref2, test_obj_destroy_notify, &notify2_called, 0) != 1) {
141                 ast_test_status_update(test, "Failed to remove a subscription to weakref2.\n");
142                 goto fail_cleanup;
143         }
144
145         ao2_t_cleanup(weakref1, "weakref1");
146         ao2_t_cleanup(weakref2, "weakref2");
147
148         weakref2 = ao2_t_get_weakproxy(obj1, "get weakref2 from obj1");
149         if (weakref1 != weakref2) {
150                 weakref1 = NULL;
151                 ast_test_status_update(test, "weakref1 != weakref2.\n");
152                 goto fail_cleanup;
153         }
154         weakref1 = NULL;
155
156         obj2 = ao2_t_alloc(0, NULL, "obj2");
157         if (obj2) {
158                 int ret = ao2_t_weakproxy_set_object(weakref2, obj2, 0, "set weakref2 to obj2");
159
160                 ao2_ref(obj2, -1);
161                 if (!ret) {
162                         ast_test_status_update(test, "Set obj2 to weakref2 when it already had an object.\n");
163                         goto fail_cleanup;
164                 }
165         }
166
167         if (ao2_weakproxy_subscribe(weakref2, test_obj_destroy_notify, &notify3_called, 0)) {
168                 ast_test_status_update(test, "Failed to add a subscription to weakref2.\n");
169                 goto fail_cleanup;
170         }
171
172         if (ao2_weakproxy_subscribe(weakref2, test_obj_destroy_notify, &notify3_called, 0)) {
173                 ast_test_status_update(test, "Failed to add a duplicate subscription to weakref2.\n");
174                 goto fail_cleanup;
175         }
176
177         if (ao2_weakproxy_unsubscribe(weakref2, test_obj_destroy_notify, &notify3_called, OBJ_MULTIPLE) != 2) {
178                 ast_test_status_update(test, "Failed to remove the correct number of subscriptions to weakref2.\n");
179                 goto fail_cleanup;
180         }
181
182         if (destructor_called || notify1_called || notify2_called || notify3_called) {
183                 ast_test_status_update(test, "Destructor or notifications called early.\n");
184                 goto fail_cleanup;
185         }
186
187         strong1 = ao2_t_weakproxy_get_object(weakref2, 0, "get strong1 from weakref2");
188         ao2_t_cleanup(strong1, "strong1");
189
190         if (obj1 != strong1) {
191                 ast_test_status_update(test, "obj1 != strong1.\n");
192                 goto fail_cleanup;
193         }
194
195         if (destructor_called || notify1_called || notify2_called || notify3_called) {
196                 ast_test_status_update(test, "Destructor or notification called early.\n");
197                 goto fail_cleanup;
198         }
199
200         ao2_t_ref(obj1, -1, "obj1");
201         obj1 = NULL;
202
203         if (destructor_called != 1 || notify1_called != 1 || notify2_called != 2 || notify3_called != 0) {
204                 ast_test_status_update(test, "Destructor or notification not called the expected number of times.\n");
205                 goto fail_cleanup;
206         }
207
208         if (ao2_t_weakproxy_get_object(weakref2, 0, "impossible get of weakref2") != NULL) {
209                 ast_test_status_update(test, "Get object on weakref2 worked when it shouldn't\n");
210                 goto fail_cleanup;
211         }
212
213         obj3 = ao2_t_alloc(0, test_obj_destructor, "obj3");
214         if (!obj3) {
215                 ast_test_status_update(test, "Failed to allocate obj3.\n");
216                 goto fail_cleanup;
217         }
218
219         if (ao2_t_weakproxy_set_object(weakref2, obj3, 0, "set weakref2 to obj3")) {
220                 ast_test_status_update(test, "Failed to set obj3 to weakref2.\n");
221                 goto fail_cleanup;
222         }
223
224         if (ao2_t_weakproxy_ref_object(obj3, +1, 0, "ao2_ref should never see this") != -2) {
225                 ast_test_status_update(test,
226                         "Expected -2 from ao2_t_weakproxy_ref_object against normal ao2 object.\n");
227                 goto fail_cleanup;
228         }
229
230         if (ao2_t_weakproxy_ref_object(weakref2, +1, 0, "weakref2 ref_object") != 2) {
231                 ast_test_status_update(test, "Expected 2 from weakref2 ref_object.\n");
232                 goto fail_cleanup;
233         }
234
235         if (ao2_t_ref(obj3, -1, "balance weakref2 ref_object") != 3) {
236                 ast_test_status_update(test, "Expected 3 from obj3 ao2_t_ref.\n");
237                 goto fail_cleanup;
238         }
239
240         ao2_ref(obj3, -1);
241
242         if (ao2_weakproxy_ref_object(weakref2, +1, 0) != -1) {
243                 ast_test_status_update(test, "Expected -1 from weakref2 ref_object because obj3 is gone.\n");
244                 goto fail_cleanup;
245         }
246
247         ao2_t_ref(weakref2, -1, "weakref2");
248
249         if (!weakproxydestroyed) {
250                 ast_test_status_update(test, "Destructor never called for weakproxy, likely a leak.\n");
251                 return AST_TEST_FAIL;
252         }
253
254         return AST_TEST_PASS;
255
256 fail_cleanup:
257         ao2_cleanup(obj1);
258         ao2_cleanup(obj3);
259         ao2_cleanup(weakref1);
260         ao2_cleanup(weakref2);
261
262         return AST_TEST_FAIL;
263 }
264
265 struct strong_str {
266         char *value;
267 };
268
269 struct weakproxy_str {
270         AO2_WEAKPROXY();
271         char value[0];
272 };
273
274 static struct strong_str *alloc_str(struct ao2_container *weakcontainer, const char *value)
275 {
276         struct strong_str *strong = ao2_t_alloc(sizeof(*strong), NULL, value);
277         struct weakproxy_str *weak = ao2_weakproxy_alloc(sizeof(*weak) + strlen(value) + 1, NULL);
278
279         if (!weak || !strong) {
280                 goto error_return;
281         }
282
283         strcpy(weak->value, value); /*SAFE*/
284         strong->value = weak->value;
285
286         if (ao2_weakproxy_set_object(weak, strong, 0)) {
287                 goto error_return;
288         }
289
290         if (!ao2_link(weakcontainer, weak)) {
291                 goto error_return;
292         }
293
294         ao2_ref(weak, -1);
295         return strong;
296
297 error_return:
298         ao2_cleanup(weak);
299         ao2_cleanup(strong);
300
301         return NULL;
302 }
303
304 AO2_STRING_FIELD_HASH_FN(weakproxy_str, value);
305 AO2_STRING_FIELD_CMP_FN(weakproxy_str, value);
306 AO2_STRING_FIELD_SORT_FN(strong_str, value);
307
308 #define ITERATOR_CHECK_NEXT(iter, var, expected) \
309         do { \
310                 var = ao2_iterator_next(iter); \
311                 ast_test_validate_cleanup(test, var == expected, ret, cleanup); \
312                 ao2_cleanup(var); \
313         } while (0)
314
315 #define WEAKFIND_CHECK(c, key, var, expected) \
316         do { \
317                 var = ao2_weakproxy_find(c, key, OBJ_SEARCH_KEY, ""); \
318                 ast_test_validate_cleanup(test, var == expected, ret, cleanup); \
319                 ao2_cleanup(var); \
320         } while (0)
321
322 AST_TEST_DEFINE(astobj2_weak_container)
323 {
324         int ret = AST_TEST_FAIL;
325
326         struct strong_str *strong1 = NULL;
327         struct strong_str *strong2 = NULL;
328         struct strong_str *strong3 = NULL;
329
330         struct strong_str *strong = NULL;
331
332         struct ao2_container *weakcontainer = NULL;
333         struct ao2_container *dupcontainer = NULL;
334
335         struct ao2_iterator iter;
336
337         switch (cmd) {
338         case TEST_INIT:
339                 info->name = "astobj2_weak_container";
340                 info->category = "/main/astobj2/";
341                 info->summary = "Test ao2 weak containers";
342                 info->description = "Test ao2 weak containers.";
343                 return AST_TEST_NOT_RUN;
344         case TEST_EXECUTE:
345                 break;
346         }
347
348         weakcontainer = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, 7,
349                 weakproxy_str_hash_fn, NULL, weakproxy_str_cmp_fn);
350         dupcontainer = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0,
351                 strong_str_sort_fn, NULL);
352
353         if (!weakcontainer || !dupcontainer) {
354                 goto cleanup;
355         }
356
357         strong1 = alloc_str(weakcontainer, "obj1");
358         strong2 = alloc_str(weakcontainer, "obj2");
359         strong3 = alloc_str(weakcontainer, "obj3");
360
361         if (!strong1 || !strong2 || !strong3) {
362                 goto cleanup;
363         }
364
365         if (ao2_container_dup_weakproxy_objs(dupcontainer, weakcontainer, 0)) {
366                 goto cleanup;
367         }
368
369         iter = ao2_iterator_init(dupcontainer, 0);
370         ITERATOR_CHECK_NEXT(&iter, strong, strong1);
371         ITERATOR_CHECK_NEXT(&iter, strong, strong2);
372         ITERATOR_CHECK_NEXT(&iter, strong, strong3);
373         ITERATOR_CHECK_NEXT(&iter, strong, NULL);
374         ao2_iterator_cleanup(&iter);
375
376         ao2_callback(dupcontainer, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, NULL, NULL);
377
378         WEAKFIND_CHECK(weakcontainer, "obj1", strong, strong1);
379         WEAKFIND_CHECK(weakcontainer, "obj2", strong, strong2);
380         WEAKFIND_CHECK(weakcontainer, "obj3", strong, strong3);
381         WEAKFIND_CHECK(weakcontainer, "unknown", strong, NULL);
382
383         /* This will orphan "obj2" in weakcontainer. */
384         ao2_replace(strong2, NULL);
385
386         if (ao2_container_dup_weakproxy_objs(dupcontainer, weakcontainer, 0)) {
387                 goto cleanup;
388         }
389
390         ast_test_validate_cleanup(test,
391                 ao2_container_count(weakcontainer) == ao2_container_count(dupcontainer) + 1,
392                 ret,
393                 cleanup);
394
395         iter = ao2_iterator_init(dupcontainer, 0);
396         ITERATOR_CHECK_NEXT(&iter, strong, strong1);
397         ITERATOR_CHECK_NEXT(&iter, strong, strong3);
398         ITERATOR_CHECK_NEXT(&iter, strong, NULL);
399         ao2_iterator_cleanup(&iter);
400
401         WEAKFIND_CHECK(weakcontainer, "obj1", strong, strong1);
402         WEAKFIND_CHECK(weakcontainer, "obj2", strong, NULL);
403         WEAKFIND_CHECK(weakcontainer, "obj3", strong, strong3);
404         WEAKFIND_CHECK(weakcontainer, "unknown", strong, NULL);
405
406         ret = AST_TEST_PASS;
407
408 cleanup:
409         ao2_cleanup(strong1);
410         ao2_cleanup(strong2);
411         ao2_cleanup(strong3);
412
413         ao2_cleanup(weakcontainer);
414         ao2_cleanup(dupcontainer);
415
416         ao2_cleanup(strong);
417
418         return ret;
419 }
420
421 static int unload_module(void)
422 {
423         AST_TEST_UNREGISTER(astobj2_weak1);
424         AST_TEST_UNREGISTER(astobj2_weak_container);
425
426         return 0;
427 }
428
429 static int load_module(void)
430 {
431         AST_TEST_REGISTER(astobj2_weak1);
432         AST_TEST_REGISTER(astobj2_weak_container);
433
434         return AST_MODULE_LOAD_SUCCESS;
435 }
436
437 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "ASTOBJ2 Weak Reference Unit Tests");