Asterisk - The Open Source Telephony Project  18.5.0
test_mwi.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2019, Sangoma Technologies Corporation
5  *
6  * Kevin Harwell <[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 /*** MODULEINFO
20  <depend>TEST_FRAMEWORK</depend>
21  <support_level>core</support_level>
22  ***/
23 
24 #include "asterisk.h"
25 
26 #include "asterisk/astobj2.h"
27 #include "asterisk/conversions.h"
28 #include "asterisk/module.h"
29 #include "asterisk/mwi.h"
30 #include "asterisk/stasis.h"
31 #include "asterisk/test.h"
32 
33 #define test_category "/mwi/"
34 
35 #define MAILBOX_PREFIX "test~" /* Hopefully sufficiently unlikely */
36 #define MAILBOX_COUNT 500
37 #define MAILBOX_SIZE 32
38 
41 
42 /*!
43  * For testing purposes each subscribed mailbox is a number. This value is
44  * the summation of all mailboxes.
45  */
46 static size_t sum_total;
47 
48 /*! Test variable that tracks the running total of mailboxes */
49 static size_t running_total;
50 
51 /*! This value is set to check if MWI data is zero before publishing */
52 static int expect_zero;
53 
54 static int num_to_mailbox(char *mailbox, size_t size, size_t num)
55 {
56  if (snprintf(mailbox, 10, MAILBOX_PREFIX "%zu", num) == -1) {
57  ast_log(LOG_ERROR, "Unable to convert mailbox to string\n");
58  return -1;
59  }
60 
61  return 0;
62 }
63 
64 static int mailbox_to_num(const char *mailbox, size_t *num)
65 {
66  const char *p = strchr(mailbox, '~');
67 
68  if (!p) {
69  ast_log(LOG_ERROR, "Prefix separator '~' not found in '%s'\n", mailbox);
70  return -1;
71  }
72 
73  if (ast_str_to_umax(++p, num)) {
74  ast_log(LOG_ERROR, "Unable to convert mailbox '%s' to numeric\n", mailbox);
75  return -1;
76  }
77 
78  return 0;
79 }
80 
81 static int validate_data(struct ast_mwi_state *mwi_state)
82 {
83  size_t num;
84  size_t val;
85 
86  if (mailbox_to_num(mwi_state->uniqueid, &num)) {
87  return -1;
88  }
89 
90  running_total += num;
91 
92  val = expect_zero ? 0 : num;
93 
94  if (mwi_state->urgent_msgs != val || mwi_state->new_msgs != val ||
95  mwi_state->old_msgs != val) {
96  ast_log(LOG_ERROR, "Unexpected MWI state data for '%s', %d != %zu\n",
97  mwi_state->uniqueid, mwi_state->urgent_msgs, val);
98  return -1;
99  }
100 
101  return num;
102 }
103 
104 static void handle_validate(const char *mailbox, struct ast_mwi_subscriber *sub)
105 {
106  struct ast_mwi_state *mwi_state = ast_mwi_subscriber_data(sub);
107 
108  if (ast_begins_with(mwi_state->uniqueid, MAILBOX_PREFIX)) {
109  validate_data(mwi_state);
110  }
111 
112  ao2_cleanup(mwi_state);
113 }
114 
117  .on_unsubscribe = handle_validate
118 };
119 
120 static void mwi_type_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
121 {
122  /* No op since we are not really testing stasis topic handling here */
123 }
124 
125 static int subscriptions_destroy(struct subscriptions *subs)
126 {
128 
130  AST_VECTOR_FREE(subs);
131 
132  ast_mwi_remove_observer(&mwi_observer);
133 
134  if (running_total != sum_total) {
135  ast_log(LOG_ERROR, "Failed to destroy all MWI subscriptions: running=%zu, sum=%zu\n",
137  return -1;
138  }
139 
140  return 0;
141 }
142 
143 static int subscriptions_create(struct subscriptions *subs)
144 {
145  size_t i;
146 
147  if (ast_mwi_add_observer(&mwi_observer) ||
149  return -1;
150  }
151 
152  sum_total = running_total = 0;
153  expect_zero = 1;
154 
155  for (i = 0; i < MAILBOX_COUNT; ++i) {
156  struct ast_mwi_subscriber *sub;
157  char mailbox[MAILBOX_SIZE];
158 
159  if (num_to_mailbox(mailbox, MAILBOX_SIZE, i)) {
160  break;
161  }
162 
163  sub = ast_mwi_subscribe_pool(mailbox, mwi_type_cb, NULL);
164  if (!sub) {
165  ast_log(LOG_ERROR, "Failed to create a MWI subscriber for mailbox '%s'\n", mailbox);
166  break;
167  }
168 
169  if (AST_VECTOR_APPEND(subs, sub)) {
170  ast_log(LOG_ERROR, "Failed to add to MWI sub to vector for mailbox '%s'\n", mailbox);
171  ao2_ref(sub, -1);
172  break;
173  }
174 
175  sum_total += i;
176  }
177 
178  if (i != MAILBOX_COUNT || running_total != sum_total) {
179  ast_log(LOG_ERROR, "Failed to create all MWI subscriptions: running=%zu, sum=%zu\n",
181  subscriptions_destroy(subs);
182  return -1;
183  }
184 
185  return 0;
186 }
187 
188 static int publishers_destroy(struct publishers *pubs)
189 {
190  size_t i;
191 
192  if (pubs) {
193  /* Remove explicit publishers */
195  AST_VECTOR_FREE(pubs);
196  return 0;
197  }
198 
199  for (i = 0; i < MAILBOX_COUNT; ++i) {
200  char mailbox[MAILBOX_SIZE];
201 
202  /* Remove implicit publishers */
203  if (num_to_mailbox(mailbox, MAILBOX_SIZE, i)) {
204  return -1;
205  }
206 
207  ast_delete_mwi_state(mailbox, NULL);
208  }
209 
210  return 0;
211 }
212 
213 static int publishers_create(struct publishers *pubs)
214 {
215  size_t i;
216 
217  if (AST_VECTOR_INIT(pubs, MAILBOX_COUNT)) {
218  return -1;
219  }
220 
221  for (i = 0; i < MAILBOX_COUNT; ++i) {
222  struct ast_mwi_publisher *pub;
223  char mailbox[MAILBOX_SIZE];
224 
225  if (num_to_mailbox(mailbox, MAILBOX_SIZE, i)) {
226  break;
227  }
228 
229  /* Create the MWI publisher */
230  pub = ast_mwi_add_publisher(mailbox);
231  if (!pub) {
232  ast_log(LOG_ERROR, "Failed to create an MWI publisher for mailbox '%s'\n", mailbox);
233  break;
234  }
235 
236  if (AST_VECTOR_APPEND(pubs, pub)) {
237  ast_log(LOG_ERROR, "Failed to add to an MWI publisher to vector for mailbox '%s'\n", mailbox);
238  ao2_ref(pub, -1);
239  break;
240  }
241  }
242 
243  if (i != MAILBOX_COUNT) {
244  ast_log(LOG_ERROR, "Failed to create all MWI publishers: count=%zu\n", i);
245  publishers_destroy(pubs);
246  return -1;
247  }
248 
249  return 0;
250 }
251 
252 static int implicit_publish_cb(struct ast_mwi_state *mwi_state, void *data)
253 {
254  size_t num;
255 
256  if (!ast_begins_with(mwi_state->uniqueid, MAILBOX_PREFIX)) {
257  /* Ignore any mailboxes not prefixed */
258  return 0;
259  }
260 
261  num = validate_data(mwi_state);
262  if (num < 0) {
263  return CMP_STOP;
264  }
265 
266  ast_mwi_publish_by_mailbox(mwi_state->uniqueid, NULL, num, num, num, NULL, NULL);
267 
268  return 0;
269 }
270 
271 static int explicit_publish_cb(struct ast_mwi_state *mwi_state, void *data)
272 {
273  struct publishers *pubs = data;
274  struct ast_mwi_publisher *pub;
275  size_t num;
276 
277  if (!ast_begins_with(mwi_state->uniqueid, MAILBOX_PREFIX)) {
278  /* Ignore any mailboxes not prefixed */
279  return 0;
280  }
281 
282  num = validate_data(mwi_state);
283  if (num < 0) {
284  return CMP_STOP;
285  }
286 
287  if (mailbox_to_num(mwi_state->uniqueid, &num)) {
288  return CMP_STOP;
289  }
290 
291  /* Mailbox number will always be the index */
292  pub = AST_VECTOR_GET(pubs, num);
293 
294  if (!pub) {
295  ast_log(LOG_ERROR, "Unable to locate MWI publisher for mailbox '%s'\n", mwi_state->uniqueid);
296  return CMP_STOP;
297  }
298 
299  ast_mwi_publish(pub, num, num, num, NULL, NULL);
300 
301  return 0;
302 }
303 
304 static int publish(on_mwi_state cb, void *user_data)
305 {
306  /* First time there is no state data */
307  expect_zero = 1;
308 
309  running_total = 0;
310  ast_mwi_state_callback_all(cb, user_data);
311 
312  if (running_total != sum_total) {
313  ast_log(LOG_ERROR, "Failed MWI state callback (1): running=%zu, sum=%zu\n",
315  return -1;
316  }
317 
318  /* Second time check valid state data exists */
320  ast_mwi_state_callback_all(cb, user_data);
321 
322  if (running_total != sum_total) {
323  ast_log(LOG_ERROR, "Failed MWI state callback (2): running=%zu, sum=%zu\n",
325  return -1;
326  }
327 
328  return 0;
329 }
330 
331 AST_TEST_DEFINE(implicit_publish)
332 {
333  struct subscriptions subs;
334  int rc = AST_TEST_PASS;
335 
336  switch (cmd) {
337  case TEST_INIT:
338  info->name = __func__;
339  info->category = test_category;
340  info->summary = "Test implicit publishing of MWI state";
341  info->description = info->summary;
342  return AST_TEST_NOT_RUN;
343  case TEST_EXECUTE:
344  break;
345  }
346 
347  ast_test_validate(test, !subscriptions_create(&subs));
348 
349  ast_test_validate_cleanup(test, !publish(implicit_publish_cb, NULL),
350  rc, cleanup);
351 
352 cleanup:
354  return AST_TEST_FAIL;
355  }
356 
357  return rc;
358 }
359 
360 AST_TEST_DEFINE(explicit_publish)
361 {
362  struct subscriptions subs;
363  struct publishers pubs;
364  int rc = AST_TEST_PASS;
365 
366  switch (cmd) {
367  case TEST_INIT:
368  info->name = __func__;
369  info->category = test_category;
370  info->summary = "Test explicit publishing of MWI state";
371  info->description = info->summary;
372  return AST_TEST_NOT_RUN;
373  case TEST_EXECUTE:
374  break;
375  }
376 
377  ast_test_validate(test, !subscriptions_create(&subs));
378  ast_test_validate_cleanup(test, !publishers_create(&pubs), rc, cleanup);
379 
380  ast_test_validate_cleanup(test, !publish(explicit_publish_cb, &pubs),
381  rc, cleanup);
382 
383 cleanup:
384  if (subscriptions_destroy(&subs) || publishers_destroy(&pubs)) {
385  return AST_TEST_FAIL;
386  }
387 
388  return rc;
389 }
390 
391 static int unload_module(void)
392 {
393  AST_TEST_UNREGISTER(implicit_publish);
394  AST_TEST_UNREGISTER(explicit_publish);
395 
396  return 0;
397 }
398 
399 static int load_module(void)
400 {
401  AST_TEST_REGISTER(implicit_publish);
402  AST_TEST_REGISTER(explicit_publish);
403 
405 }
406 
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:567
static void handle_validate(const char *mailbox, struct ast_mwi_subscriber *sub)
Definition: test_mwi.c:104
Asterisk main include file. File version handling, generic pbx functions.
static struct ao2_container * publishers
Container of active outbound extension state publishers.
static int mailbox_to_num(const char *mailbox, size_t *num)
Definition: test_mwi.c:64
Definition: ast_expr2.c:325
static int num_to_mailbox(char *mailbox, size_t size, size_t num)
Definition: test_mwi.c:54
static size_t running_total
Definition: test_mwi.c:49
int ast_str_to_umax(const char *str, uintmax_t *res)
Convert the given string to an unsigned max size integer.
Definition: conversions.c:119
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
static int unload_module(void)
Definition: test_mwi.c:391
void(* on_subscribe)(const char *mailbox, struct ast_mwi_subscriber *sub)
Raised when MWI is being subscribed.
Definition: mwi.h:259
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
Test Framework API.
static int subscriptions_create(struct subscriptions *subs)
Definition: test_mwi.c:143
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
struct ast_mwi_observer mwi_observer
Definition: test_mwi.c:115
struct ast_mwi_state * ast_mwi_subscriber_data(struct ast_mwi_subscriber *sub)
Retrieves the state data object associated with the MWI subscriber.
Definition: mwi.c:264
#define NULL
Definition: resample.c:96
static int explicit_publish_cb(struct ast_mwi_state *mwi_state, void *data)
Definition: test_mwi.c:271
int ast_mwi_add_observer(struct ast_mwi_observer *observer)
Add an observer to receive MWI state related events.
Definition: mwi.c:296
#define MAILBOX_PREFIX
Definition: test_mwi.c:35
static int publishers_destroy(struct publishers *pubs)
Definition: test_mwi.c:188
static char mailbox[AST_MAX_MAILBOX_UNIQUEID]
Definition: chan_mgcp.c:204
#define ast_log
Definition: astobj2.c:42
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113
static int load_module(void)
Definition: test_mwi.c:399
static void mwi_type_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
Definition: test_mwi.c:120
static size_t sum_total
Definition: test_mwi.c:46
#define ao2_ref(o, delta)
Definition: astobj2.h:464
Conversion utility functions.
int old_msgs
Definition: mwi.h:462
static int implicit_publish_cb(struct ast_mwi_state *mwi_state, void *data)
Definition: test_mwi.c:252
int(* on_mwi_state)(struct ast_mwi_state *mwi_state, void *data)
The delegate called for each managed mailbox state.
Definition: mwi.h:303
static int expect_zero
Definition: test_mwi.c:52
#define MAILBOX_COUNT
Definition: test_mwi.c:36
static int publish(on_mwi_state cb, void *user_data)
Definition: test_mwi.c:304
int ast_mwi_publish(struct ast_mwi_publisher *publisher, int urgent_msgs, int new_msgs, int old_msgs, const char *channel_id, struct ast_eid *eid)
Publish MWI for the given mailbox.
Definition: mwi.c:353
#define LOG_ERROR
Definition: logger.h:285
void ast_mwi_remove_observer(struct ast_mwi_observer *observer)
Remove an MWI state observer.
Definition: mwi.c:302
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
def info(msg)
static int subscriptions_destroy(struct subscriptions *subs)
Definition: test_mwi.c:125
#define ast_delete_mwi_state(mailbox, context)
Delete MWI state cached by stasis.
Definition: mwi.h:433
AST_VECTOR(subscriptions, struct ast_mwi_subscriber *)
static int publishers_create(struct publishers *pubs)
Definition: test_mwi.c:213
void ast_mwi_state_callback_all(on_mwi_state handler, void *data)
For each managed mailbox call the given handler.
Definition: mwi.c:333
void * ast_mwi_unsubscribe_and_join(struct ast_mwi_subscriber *sub)
Unsubscribe from the stasis topic, block until the final message is received, and then unsubscribe fr...
Definition: mwi.c:254
int urgent_msgs
Definition: mwi.h:466
static int validate_data(struct ast_mwi_state *mwi_state)
Definition: test_mwi.c:81
static void * cleanup(void *unused)
Definition: pbx_realtime.c:124
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:682
struct ast_mwi_publisher * ast_mwi_add_publisher(const char *mailbox)
Add an MWI state publisher to the mailbox.
Definition: mwi.c:290
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
#define MAILBOX_SIZE
Definition: test_mwi.c:37
MWI state event interface.
Definition: mwi.h:252
int new_msgs
Definition: mwi.h:461
AST_TEST_DEFINE(implicit_publish)
Definition: test_mwi.c:331
const ast_string_field uniqueid
Definition: mwi.h:460
Asterisk MWI API.
struct stasis_forward * sub
Definition: res_corosync.c:240
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Definition: strings.h:94
int ast_mwi_publish_by_mailbox(const char *mailbox, const char *context, int urgent_msgs, int new_msgs, int old_msgs, const char *channel_id, struct ast_eid *eid)
Publish MWI for the given mailbox.
Definition: mwi.c:370
The structure that contains MWI state.
Definition: mwi.h:457
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
struct ast_mwi_subscriber * ast_mwi_subscribe_pool(const char *mailbox, stasis_subscription_cb callback, void *data)
Add an MWI state subscriber, and stasis subscription to the mailbox.
Definition: mwi.c:230
#define test_category
Definition: test_mwi.c:33
#define AST_VECTOR_CALLBACK_VOID(vec, callback,...)
Execute a callback on every element in a vector disregarding callback return.
Definition: vector.h:865