Asterisk - The Open Source Telephony Project  18.5.0
test_astobj2_weaken.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2015, CFWare, LLC
5  *
6  * Corey Farrell <[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 astobj2 weakproxy test module
22  *
23  * \author Corey Farrell <[email protected]>
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 {
44 }
45 
46 static void weakproxy_destructor(void *obj)
47 {
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 
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) {
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 
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 
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 
349  weakproxy_str_hash_fn, NULL, weakproxy_str_cmp_fn);
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 
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 
435 }
436 
437 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "ASTOBJ2 Weak Reference Unit Tests");
#define ao2_t_ref(o, delta, tag)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:463
static void test_obj_destructor(void *obj)
static int weakproxydestroyed
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:567
#define ao2_t_cleanup(obj, tag)
Definition: astobj2.h:1959
#define ao2_weakproxy_set_object(weakproxy, obj, flags)
Definition: astobj2.h:583
Asterisk main include file. File version handling, generic pbx functions.
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
#define ao2_weakproxy_ref_object(weakproxy, delta, flags)
Definition: astobj2.h:609
#define ao2_t_alloc(data_size, destructor_fn, debug_msg)
Definition: astobj2.h:409
int ao2_weakproxy_unsubscribe(void *weakproxy, ao2_weakproxy_notification_cb cb, void *data, int flags)
Remove notification of real object destruction.
Definition: astobj2.c:970
AST_TEST_DEFINE(astobj2_weak1)
AO2_STRING_FIELD_CMP_FN(weakproxy_str, value)
#define ao2_callback(c, flags, cb_fn, arg)
Definition: astobj2.h:1716
void ao2_iterator_cleanup(struct ao2_iterator *iter)
int ao2_container_dup_weakproxy_objs(struct ao2_container *dest, struct ao2_container *src, enum search_flags flags)
Copy object references associated with src container weakproxies into the dest container.
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Definition: astobj2.h:1335
#define ao2_t_get_weakproxy(obj, tag)
Definition: astobj2.h:695
Test Framework API.
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
int ao2_weakproxy_subscribe(void *weakproxy, ao2_weakproxy_notification_cb cb, void *data, int flags)
Request notification when weakproxy points to NULL.
Definition: astobj2.c:931
#define NULL
Definition: resample.c:96
int value
Definition: syslog.c:37
Utility functions.
static int unload_module(void)
#define WEAKFIND_CHECK(c, key, var, expected)
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
AO2_STRING_FIELD_SORT_FN(strong_str, value)
#define ao2_weakproxy_alloc(data_size, destructor_fn)
Definition: astobj2.h:554
static void weakproxy_destructor(void *obj)
#define ao2_ref(o, delta)
Definition: astobj2.h:464
static int load_module(void)
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Definition: astobj2.h:1310
static int destructor_called
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
#define ao2_t_weakproxy_set_object(weakproxy, obj, flags, tag)
Definition: astobj2.h:586
def info(msg)
static void * cleanup(void *unused)
Definition: pbx_realtime.c:124
#define ao2_t_weakproxy_get_object(weakproxy, flags, tag)
Definition: astobj2.h:628
static struct strong_str * alloc_str(struct ao2_container *weakcontainer, const char *value)
#define ao2_t_weakproxy_ref_object(weakproxy, delta, flags, tag)
Definition: astobj2.h:605
#define ao2_replace(dst, src)
Definition: astobj2.h:517
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1841
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
#define ao2_t_weakproxy_alloc(data_size, destructor_fn, tag)
Definition: astobj2.h:557
#define ITERATOR_CHECK_NEXT(iter, var, expected)
Generic container type.
static void test_obj_destroy_notify(void *obj, void *data)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
AO2_STRING_FIELD_HASH_FN(weakproxy_str, value)
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_link(container, obj)
Definition: astobj2.h:1549