57edd37ceaec96c90d43b56d9bd4318ece145715
[asterisk/asterisk.git] / res / parking / parking_tests.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Jonathan Rose <jrose@digium.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 /*! \file
20  *
21  * \brief Call Parking Unit Tests
22  *
23  * \author Jonathan Rose <jrose@digium.com>
24  */
25
26 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #include "res_parking.h"
31 #include "asterisk/utils.h"
32 #include "asterisk/module.h"
33 #include "asterisk/astobj2.h"
34 #include "asterisk/test.h"
35 #include "asterisk/stringfields.h"
36 #include "asterisk/time.h"
37 #include "asterisk/causes.h"
38 #include "asterisk/pbx.h"
39
40 #if defined(TEST_FRAMEWORK)
41
42 #define TEST_CATEGORY "/res/parking/"
43
44 #define CHANNEL_TECH_NAME "ParkingTestChannel"
45
46 static const struct ast_party_caller alice_callerid = {
47         .id.name.str = "Alice",
48         .id.name.valid = 1,
49         .id.number.str = "100",
50         .id.number.valid = 1,
51 };
52
53 /*! \brief Create a \ref test_cdr_chan_tech for Alice */
54 static struct ast_channel *create_alice_channel(void)
55 {
56         struct ast_channel *alice = ast_channel_alloc(0, AST_STATE_DOWN,
57                 "100", "Alice", "100", "100", "default", NULL, 0,
58                 CHANNEL_TECH_NAME "/Alice");
59
60         if (!alice) {
61                 return NULL;
62         }
63
64         ast_channel_set_caller(alice, &alice_callerid, NULL);
65
66         return alice;
67 }
68
69 /*! \brief Hang up a test channel safely */
70 static struct ast_channel *hangup_channel(struct ast_channel *chan, int hangup_cause)
71 {
72         ast_channel_hangupcause_set(chan, hangup_cause);
73         ast_hangup(chan);
74         return NULL;
75 }
76
77 static void safe_channel_release(struct ast_channel *chan)
78 {
79         if (!chan) {
80                 return;
81         }
82         ast_channel_release(chan);
83 }
84
85 static int fake_fixup(struct ast_channel *clonechan, struct ast_channel *original)
86 {
87         return 0;
88 }
89
90 static const struct ast_channel_tech fake_tech = {
91         .fixup = fake_fixup, /* silence warning from masquerade... though those shouldn't be happening now */
92 };
93
94 #define TEST_LOT_NAME "unit_tests_res_parking_test_lot"
95
96 static struct parking_lot *generate_test_parking_lot(const char *name, int low_space, int high_space, const char *park_exten, const char *park_context, struct ast_test *test)
97 {
98         RAII_VAR(struct parking_lot_cfg *, test_cfg, NULL, ao2_cleanup);
99         struct parking_lot *test_lot;
100
101         test_cfg = parking_lot_cfg_create(name);
102
103         if (!test_cfg) {
104                 return NULL;
105         }
106
107         test_cfg->parking_start = low_space;
108         test_cfg->parking_stop = high_space;
109         test_cfg->parkingtime = 10;
110         test_cfg->comebackdialtime = 10;
111         test_cfg->parkfindnext = 1;
112         test_cfg->parkext_exclusive = 1;
113         ast_string_field_set(test_cfg, parkext, park_exten);
114         ast_string_field_set(test_cfg, parking_con, park_context);
115         ast_string_field_set(test_cfg, comebackcontext, "unit_test_res_parking_create_lot_comeback");
116
117         if (parking_lot_cfg_create_extensions(test_cfg)) {
118                 ast_test_status_update(test, "Extensions for parking lot '%s' could not be registered. Extension Creation failed.\n", name);
119                 return NULL;
120         }
121
122         test_lot = parking_lot_build_or_update(test_cfg, 1);
123
124         if (!test_lot) {
125                 return NULL;
126         }
127
128         return test_lot;
129 }
130
131 static int dispose_test_lot(struct parking_lot *test_lot, int expect_destruction)
132 {
133         RAII_VAR(struct parking_lot *, found_lot, NULL, ao2_cleanup);
134
135         test_lot->mode = PARKINGLOT_DISABLED;
136         parking_lot_remove_if_unused(test_lot);
137
138         found_lot = parking_lot_find_by_name(test_lot->name);
139
140         if ((expect_destruction && !found_lot) || (!expect_destruction && found_lot)) {
141                 return 0;
142         }
143
144         return -1;
145 }
146
147 AST_TEST_DEFINE(create_lot)
148 {
149         RAII_VAR(struct parking_lot *, test_lot, NULL, ao2_cleanup);
150         RAII_VAR(struct parking_lot *, found_copy, NULL, ao2_cleanup);
151
152         switch (cmd) {
153         case TEST_INIT:
154                 info->name = "create_lot";
155                 info->category = TEST_CATEGORY;
156                 info->summary = "Parking lot creation";
157                 info->description =
158                         "Creates a parking lot and then disposes of it.";
159                 return AST_TEST_NOT_RUN;
160         case TEST_EXECUTE:
161                 break;
162         }
163
164         ast_test_status_update(test, "Creating test parking lot '%s'\n", TEST_LOT_NAME);
165
166         test_lot = generate_test_parking_lot(TEST_LOT_NAME, 701, 703, NULL, "unit_test_res_parking_create_lot_con", test);
167         if (!test_lot) {
168                 ast_test_status_update(test, "Failed to create test parking lot. Test Failed\n");
169                 return AST_TEST_FAIL;
170         }
171
172         ast_test_status_update(test, "Successfully created parking lot. Retrieving test parking lot from container.\n");
173
174         found_copy = parking_lot_find_by_name(TEST_LOT_NAME);
175         if (!found_copy) {
176                 ast_test_status_update(test, "Failed to find parking lot in the parking lot container. Test failed.\n");
177                 dispose_test_lot(test_lot, 1);
178                 return AST_TEST_FAIL;
179         }
180
181         ast_test_status_update(test, "Successfully retrieved parking lot. Removing test parking lot from container.\n");
182
183         if (dispose_test_lot(found_copy, 1)) {
184                 ast_test_status_update(test, "Found parking lot in container after attempted removal. Test failed.\n");
185         }
186
187         ast_test_status_update(test, "Parking lot was successfully removed from the container. Test complete.\n");
188
189         return AST_TEST_PASS;
190 }
191
192 AST_TEST_DEFINE(park_call)
193 {
194         RAII_VAR(struct parking_lot *, test_lot, NULL, ao2_cleanup);
195         RAII_VAR(struct ast_channel *, chan_alice, NULL, safe_channel_release);
196         RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
197
198         struct ast_bridge_features chan_features;
199         struct timespec to_sleep = {1, 0};
200
201         switch (cmd) {
202         case TEST_INIT:
203                 info->name = "park_channel";
204                 info->category = TEST_CATEGORY;
205                 info->summary = "Park a Channel";
206                 info->description =
207                         "Creates a parking lot, parks a channel in it, then removes it from the parking lot bridge.";
208                 return AST_TEST_NOT_RUN;
209         case TEST_EXECUTE:
210                 break;
211         }
212
213         ast_test_status_update(test, "Creating test parking lot '%s'\n", TEST_LOT_NAME);
214
215         test_lot = generate_test_parking_lot(TEST_LOT_NAME, 701, 703, NULL, "unit_test_res_parking_create_lot_con", test);
216         if (!test_lot) {
217                 ast_test_status_update(test, "Failed to create test parking lot. Test failed.\n");
218                 return AST_TEST_FAIL;
219         }
220
221         chan_alice = create_alice_channel();
222
223         if (!chan_alice) {
224                 ast_test_status_update(test, "Failed to create test channel to park. Test failed.\n");
225                 dispose_test_lot(test_lot, 1);
226                 return AST_TEST_FAIL;
227         }
228
229         ast_channel_state_set(chan_alice, AST_STATE_UP);
230
231         pbx_builtin_setvar_helper(chan_alice, "BLINDTRANSFER", ast_channel_name(chan_alice));
232
233         parking_bridge = park_application_setup(chan_alice, chan_alice, TEST_LOT_NAME, NULL);
234
235         if (!parking_bridge) {
236                 ast_test_status_update(test, "Failed to get the parking bridge for '%s'. Test failed.\n", TEST_LOT_NAME);
237                 dispose_test_lot(test_lot, 1);
238                 return AST_TEST_FAIL;
239         }
240
241         if (ast_bridge_features_init(&chan_features)) {
242                 ast_bridge_features_cleanup(&chan_features);
243                 ast_test_status_update(test, "Failed to initialize bridge features. Test failed.\n");
244                 dispose_test_lot(test_lot, 1);
245                 return AST_TEST_FAIL;
246         }
247
248         ast_bridge_impart(parking_bridge, chan_alice, NULL, NULL, 0);
249
250         while ((nanosleep(&to_sleep, &to_sleep) == -1) && (errno == EINTR));
251
252         ast_bridge_depart(chan_alice);
253
254         chan_alice = hangup_channel(chan_alice, AST_CAUSE_NORMAL);
255
256         if (dispose_test_lot(test_lot, 1)) {
257                 ast_test_status_update(test, "Found parking lot in container after attempted removal. Test failed.\n");
258         }
259
260         return AST_TEST_PASS;
261
262 }
263
264 static int parked_users_match(const struct parked_user *actual, const struct parked_user *expected, struct ast_test *test)
265 {
266         if (expected->parking_space != actual->parking_space) {
267                 ast_test_status_update(test, "parking_space expected: %d - got: %d\n", expected->parking_space, actual->parking_space);
268                 return 0;
269         }
270
271         if (strcmp(expected->parker_dial_string, actual->parker_dial_string)) {
272                 ast_test_status_update(test, "parker_dial_string expected: %s - got: %s\n", expected->parker_dial_string, actual->parker_dial_string);
273                 return 0;
274         }
275
276         if (expected->time_limit != actual->time_limit) {
277                 ast_test_status_update(test, "time_limit expected: %d - got: %d\n", expected->time_limit, actual->time_limit);
278                 return 0;
279         }
280
281         if (expected->resolution != actual->resolution) {
282                 ast_test_status_update(test, "resolution expected: %d - got: %d\n", expected->resolution, actual->resolution);
283                 return 0;
284         }
285
286         return 1;
287 }
288
289 static int parking_lot_cfgs_match(const struct parking_lot_cfg *actual, const struct parking_lot_cfg *expected, struct ast_test *test)
290 {
291         if (expected->parking_start != actual->parking_start) {
292                 ast_test_status_update(test, "parking_start expected: %d - got: %d\n", expected->parking_start, actual->parking_start);
293                 return 0;
294         }
295
296         if (expected->parking_stop != actual->parking_stop) {
297                 ast_test_status_update(test, "parking_stop expected: %d - got: %d\n", expected->parking_stop, actual->parking_stop);
298                 return 0;
299         }
300
301         if (expected->parkingtime != actual->parkingtime) {
302                 ast_test_status_update(test, "parkingtime expected: %d - got: %d\n", expected->parkingtime, actual->parkingtime);
303                 return 0;
304         }
305
306         if (expected->comebackdialtime != actual->comebackdialtime) {
307                 ast_test_status_update(test, "comebackdialtime expected: %d - got: %d\n", expected->comebackdialtime, actual->comebackdialtime);
308                 return 0;
309         }
310
311         if (expected->parkfindnext != actual->parkfindnext) {
312                 ast_test_status_update(test, "parkfindnext expected: %d - got: %d\n", expected->parkfindnext, actual->parkfindnext);
313                 return 0;
314         }
315
316         if (expected->parkext_exclusive != actual->parkext_exclusive) {
317                 ast_test_status_update(test, "parkext_exclusive expected: %d - got: %d\n", expected->parkext_exclusive, actual->parkext_exclusive);
318                 return 0;
319         }
320
321         if (strcmp(expected->parkext, actual->parkext)) {
322                 ast_test_status_update(test, "parkext expected: %s - got: %s\n", expected->parkext, actual->parkext);
323                 return 0;
324         }
325
326         if (strcmp(expected->parking_con, actual->parking_con)) {
327                 ast_test_status_update(test, "parking_con expected: %s - got: %s\n", expected->parking_con, actual->parking_con);
328                 return 0;
329         }
330
331         if (strcmp(expected->comebackcontext, actual->comebackcontext)) {
332                 ast_test_status_update(test, "comebackcontext expected: %s - got: %s\n", expected->comebackcontext, actual->comebackcontext);
333                 return 0;
334         }
335
336         return 1;
337 }
338
339 AST_TEST_DEFINE(retrieve_call)
340 {
341         RAII_VAR(struct parking_lot *, test_lot, NULL, ao2_cleanup);
342         RAII_VAR(struct ast_channel *, chan_alice, NULL, safe_channel_release);
343         RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
344         RAII_VAR(struct parked_user *, retrieved_user, NULL, ao2_cleanup);
345
346         struct timespec to_sleep = {1, 0};
347         int failure = 0;
348
349         static const struct parked_user expected_user = {
350                 .parking_space = 701,
351                 .parker_dial_string = "ParkingTestChannel/Alice",
352                 .time_limit = 10,
353                 .resolution = PARK_ANSWERED,
354         };
355
356         struct ast_bridge_features chan_features;
357
358         switch (cmd) {
359         case TEST_INIT:
360                 info->name = "park_retrieve";
361                 info->category = TEST_CATEGORY;
362                 info->summary = "Retrieve a parked channel";
363                 info->description =
364                         "Creates a parking lot, parks a channel in it, then removes it from the parking lot bridge.";
365                 return AST_TEST_NOT_RUN;
366         case TEST_EXECUTE:
367                 break;
368         }
369
370         ast_test_status_update(test, "Creating test parking lot '%s'\n", TEST_LOT_NAME);
371
372         test_lot = generate_test_parking_lot(TEST_LOT_NAME, 701, 703, NULL, "unit_test_res_parking_create_lot_con", test);
373         if (!test_lot) {
374                 ast_test_status_update(test, "Failed to create test parking lot. Test failed.\n");
375                 return AST_TEST_FAIL;
376         }
377
378         chan_alice = create_alice_channel();
379         if (!chan_alice) {
380                 ast_test_status_update(test, "Failed to create test channel to park. Test failed.\n");
381                 dispose_test_lot(test_lot, 1);
382                 return AST_TEST_FAIL;
383         }
384
385         ast_channel_state_set(chan_alice, AST_STATE_UP);
386
387         pbx_builtin_setvar_helper(chan_alice, "BLINDTRANSFER", ast_channel_name(chan_alice));
388
389         parking_bridge = park_application_setup(chan_alice, chan_alice, TEST_LOT_NAME, NULL);
390         if (!parking_bridge) {
391                 ast_test_status_update(test, "Failed to get the parking bridge for '%s'. Test failed.\n", TEST_LOT_NAME);
392                 dispose_test_lot(test_lot, 1);
393                 return AST_TEST_FAIL;
394         }
395
396         if (ast_bridge_features_init(&chan_features)) {
397                 ast_bridge_features_cleanup(&chan_features);
398                 ast_test_status_update(test, "Failed to initialize bridge features. Test failed.\n");
399                 dispose_test_lot(test_lot, 1);
400                 return AST_TEST_FAIL;
401         }
402
403         ast_bridge_impart(parking_bridge, chan_alice, NULL, NULL, 0);
404
405         while ((nanosleep(&to_sleep, &to_sleep) == -1) && (errno == EINTR));
406
407         retrieved_user = parking_lot_retrieve_parked_user(test_lot, 701);
408         if (!retrieved_user) {
409                 ast_test_status_update(test, "Failed to retrieve the parked user from the expected parking space. Test failed.\n");
410
411                 failure = 1;
412                 goto test_cleanup;
413         }
414
415         ast_test_status_update(test, "Successfully retrieved parked user from the parking lot. Validating user data. Test failed.\n");
416
417         if (!parked_users_match(retrieved_user, &expected_user, test)) {
418                 ast_test_status_update(test, "Parked user validation failed\n");
419                 failure = 1;
420                 goto test_cleanup;
421         }
422
423         if (retrieved_user->chan != chan_alice) {
424                 ast_test_status_update(test, "The retrieved parked channel didn't match the expected channel. Test failed.\n");
425                 failure = 1;
426                 goto test_cleanup;
427         }
428
429 test_cleanup:
430         ast_bridge_depart(chan_alice);
431         chan_alice = hangup_channel(chan_alice, AST_CAUSE_NORMAL);
432         if (dispose_test_lot(test_lot, 1)) {
433                 ast_test_status_update(test, "Found parking lot in container after attempted removal. Test failed.\n");
434                 failure = 1;
435         }
436
437         return failure ? AST_TEST_FAIL : AST_TEST_PASS;
438 }
439
440 static int check_retrieve_call_extensions(struct ast_test *test, int expected)
441 {
442         struct ast_exten *check;
443         struct pbx_find_info find_info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
444         int extens;
445         char search_buffer[4];
446
447         /* Check the parking extensions */
448         check = pbx_find_extension(NULL, NULL, &find_info, "unit_test_res_parking_create_lot_con", "700", 1, NULL, NULL, E_MATCH);
449
450         if (check ? !expected : expected) {
451                 /* extension isn't present when it should be or is present when it shouldn't be. Automatic failure. */
452                 ast_test_status_update(test, "An extension '700' was %s when it %s have been. Test failed.\n",
453                         expected ? "not present" : "present",
454                         expected ? "should" : "should not");
455                 return -1;
456         } else if (check && expected) {
457                 if (strcmp(ast_get_extension_app(check), "Park")) {
458                         ast_test_status_update(test, "An extension '700' has the wrong application associated with it. Got '%s' expected 'Park'.\n",
459                                 ast_get_extension_app(check));
460                         return -1;
461                 }
462         }
463
464
465         /* Check the parking space extensions 701-703 */
466         for (extens = 701; extens <= 703; extens++) {
467                 sprintf(search_buffer, "%d", extens);
468                 find_info.stacklen = 0; /* reset for pbx_find_extension */
469
470                 check = pbx_find_extension(NULL, NULL, &find_info, "unit_test_res_parking_create_lot_con", search_buffer, 1, NULL, NULL, E_MATCH);
471
472                 if (check ? !expected : expected) {
473                         /* extension isn't present when it should be or is present when it shouldn't be. Automatic failure. */
474                         ast_test_status_update(test, "An extension '%s' was %s when it %s have been. Test failed.\n",
475                                 search_buffer,
476                                 expected ? "not present" : "present",
477                                 expected ? "should" : "should not");
478                         return -1;
479                 } else if (check && expected) {
480                         if (strcmp(ast_get_extension_app(check), "ParkedCall")) {
481                                 ast_test_status_update(test, "An extension '%s' has the wrong application associated with it. Got '%s', expected 'ParkedCall'.\n",
482                                         search_buffer,
483                                         ast_get_extension_app(check));
484                                 return -1;
485                         }
486                 }
487         }
488
489         return 0;
490
491 }
492
493 AST_TEST_DEFINE(park_extensions)
494 {
495         RAII_VAR(struct parking_lot *, test_lot, NULL, ao2_cleanup);
496
497         switch (cmd) {
498         case TEST_INIT:
499                 info->name = "park_extensions";
500                 info->category = TEST_CATEGORY;
501                 info->summary = "Parking lot extension creation tests";
502                 info->description =
503                         "Creates parking lots and checks that they registered the expected extensions, then removes them.";
504                 return AST_TEST_NOT_RUN;
505         case TEST_EXECUTE:
506                 break;
507         }
508
509         test_lot = generate_test_parking_lot(TEST_LOT_NAME, 701, 703, "700", "unit_test_res_parking_create_lot_con", test);
510         if (!test_lot) {
511                 ast_test_status_update(test, "Failed to create test parking lot. Test Failed.\n");
512                 return AST_TEST_FAIL;
513         }
514
515         if (check_retrieve_call_extensions(test, 1)) {
516                 dispose_test_lot(test_lot, 1);
517                 return AST_TEST_FAIL;
518         }
519
520         ast_test_status_update(test, "Extensions for the test parking lot were verified. Cleaning up and verifying their removal.\n");
521
522         if (dispose_test_lot(test_lot, 1)) {
523                 ast_test_status_update(test, "Found parking lot in container after attempted removal. Test failed.\n");
524                 return AST_TEST_FAIL;
525         }
526         ao2_cleanup(test_lot);
527         test_lot = NULL;
528
529         if (check_retrieve_call_extensions(test, 0)) {
530                 ast_log(LOG_ERROR, "Test 'park_extensions' failed to clean up after itself properly.\n");
531                 return AST_TEST_FAIL;
532         }
533
534         ast_test_status_update(test, "Extensions for the test parking lot verified as removed. Test completed successfully.\n");
535
536         return AST_TEST_PASS;
537 }
538
539 AST_TEST_DEFINE(extension_conflicts)
540 {
541         RAII_VAR(struct parking_lot *, base_lot, NULL, ao2_cleanup);
542         RAII_VAR(struct parking_lot *, expect_fail1, NULL, ao2_cleanup); /* Failure due to overlapping parkexten */
543         RAII_VAR(struct parking_lot *, expect_fail2, NULL, ao2_cleanup); /* Failure due to overlapping spaces */
544         RAII_VAR(struct parking_lot *, expect_fail3, NULL, ao2_cleanup); /* parkexten overlaps parking spaces */
545         RAII_VAR(struct parking_lot *, expect_fail4, NULL, ao2_cleanup); /* parking spaces overlap parkexten */
546         RAII_VAR(struct parking_lot *, expect_success1, NULL, ao2_cleanup); /* Success due to being in a different context */
547         RAII_VAR(struct parking_lot *, expect_success2, NULL, ao2_cleanup); /* Success due to not having overlapping extensions */
548         RAII_VAR(struct parking_lot *, expect_success3, NULL, ao2_cleanup); /* Range of parking spaces differs by one above */
549         RAII_VAR(struct parking_lot *, expect_success4, NULL, ao2_cleanup); /* Range of parking spaces differs by one below */
550         char *cur_lot_name;
551
552         int failed = 0;
553
554         switch (cmd) {
555         case TEST_INIT:
556                 info->name = "extension_conflicts";
557                 info->category = TEST_CATEGORY;
558                 info->summary = "Tests the addition of parking lot extensions to make sure conflicts are detected";
559                 info->description =
560                         "Creates parking lots with overlapping extensions to test for conflicts";
561                 return AST_TEST_NOT_RUN;
562         case TEST_EXECUTE:
563                 break;
564         }
565
566         ast_test_status_update(test, "Creating the base lot. This should pass.\n");
567         base_lot = generate_test_parking_lot(TEST_LOT_NAME, 701, 703, "700", "unit_test_res_parking_create_lot_con", test);
568
569         if (!base_lot) {
570                 ast_test_status_update(test, "Failed to create the base parking lot. Test failed.\n");
571                 failed = 1;
572                 goto cleanup;
573         }
574
575         cur_lot_name = "unit_tests_res_parking_test_lot_fail1";
576         ast_test_status_update(test, "Creating a test lot which will overlap.\n");
577         expect_fail1 = generate_test_parking_lot(cur_lot_name,
578                 801, 803, "700", "unit_test_res_parking_create_lot_con", /* The parkexten overlaps the parkexten of the base */
579                 test);
580
581         if (expect_fail1) {
582                 ast_test_status_update(test, "%s was successfully created when it was expected to fail. Test failed.\n", cur_lot_name);
583                 failed = 1;
584                 goto cleanup;
585         }
586
587         cur_lot_name = "unit_tests_res_parking_test_lot_fail2";
588         expect_fail2 = generate_test_parking_lot(cur_lot_name,
589                 702, 705, "800", "unit_test_res_parking_create_lot_con", /* The range overlaps the range of the base */
590                 test);
591         if (expect_fail2) {
592                 ast_test_status_update(test, "%s was successfully created when it was expected to fail. Test failed.\n", cur_lot_name);
593                 failed = 1;
594                 goto cleanup;
595         }
596
597         cur_lot_name = "unit_tests_res_parking_test_lot_fail3";
598         expect_fail3 = generate_test_parking_lot(cur_lot_name,
599                 698, 700, "testfail3", "unit_test_res_parking_create_lot_con", /* The range overlaps the parkexten of the base */
600                 test);
601         if (expect_fail3) {
602                 ast_test_status_update(test, "%s was successfully created when it was expected to fail. Test failed.\n", cur_lot_name);
603                 failed = 1;
604                 goto cleanup;
605         }
606
607         cur_lot_name = "unit_tests_res_parking_test_lot_fail4";
608         expect_fail4 = generate_test_parking_lot(cur_lot_name,
609                 704, 706, "703", "unit_test_res_parking_create_lot_con", /* The parkexten overlaps the range of the base */
610                 test);
611         if (expect_fail4) {
612                 ast_test_status_update(test, "%s was successfully created when it was expected to fail. Test failed.\n", cur_lot_name);
613                 failed = 1;
614                 goto cleanup;
615         }
616
617         cur_lot_name = "unit_tests_res_parking_test_lot_success1";
618         expect_success1 = generate_test_parking_lot(cur_lot_name,
619                 701, 703, "700", "unit_test_res_parking_create_lot_con_2", /* no overlap due to different context */
620                 test);
621         if (!expect_success1) {
622                 ast_test_status_update(test, "%s failed to be created. Success was expected. Test failed.\n", cur_lot_name);
623                 failed = 1;
624                 goto cleanup;
625         }
626
627         cur_lot_name = "unit_tests_res_parking_test_lot_success2";
628         expect_success2 = generate_test_parking_lot(cur_lot_name,
629                 601, 605, "600", "unit_test_res_parking_create_lot_con", /* no overlap due to different extensions and ranges */
630                 test);
631         if (!expect_success2) {
632                 ast_test_status_update(test, "%s failed to be created. Success was expected. Test failed.\n", cur_lot_name);
633                 failed = 1;
634                 goto cleanup;
635         }
636
637         cur_lot_name = "unit_tests_res_parking_test_lot_success3";
638         expect_success3 = generate_test_parking_lot(cur_lot_name,
639                 704, 706, "testsuccess3", "unit_test_res_parking_create_lot_con", /* no overlap because the parking spaces start 1 above existing ranges */
640                 test);
641         if (!expect_success3) {
642                 ast_test_status_update(test, "%s failed to be created. Success was expected. Test failed.\n", cur_lot_name);
643                 failed = 1;
644                 goto cleanup;
645         }
646
647         cur_lot_name = "unit_tests_res_parking_test_lot_success4";
648         expect_success4 = generate_test_parking_lot(cur_lot_name,
649                 697, 699, "testsuccess4", "unit_test_res_parking_create_lot_con", /* no overlap because the parking spaces end 1 below existing ranges */
650                 test);
651         if (!expect_success4) {
652                 failed = 1;
653                 goto cleanup;
654         }
655
656 cleanup:
657         if (base_lot && dispose_test_lot(base_lot, 1)) {
658                 ast_test_status_update(test, "Found base parking lot in container after attempted removal. Test failed.\n");
659                 failed = 1;
660         }
661
662         if (expect_fail1) {
663                 dispose_test_lot(expect_fail1, 1);
664                 failed = 1;
665         }
666
667         if (expect_fail2) {
668                 dispose_test_lot(expect_fail2, 1);
669                 failed = 1;
670         }
671
672         if (expect_fail3) {
673                 dispose_test_lot(expect_fail3, 1);
674                 failed = 1;
675         }
676
677         if (expect_fail4) {
678                 dispose_test_lot(expect_fail4, 1);
679                 failed = 1;
680         }
681
682         if (expect_success1 && dispose_test_lot(expect_success1, 1)) {
683                 ast_test_status_update(test, "Found expect_success1 parking lot in container after attempted removal. Test failed.\n");
684                 failed = 1;
685         }
686
687         if (expect_success2 && dispose_test_lot(expect_success2, 1)) {
688                 ast_test_status_update(test, "Found expect_success2 parking lot in container after attempted removal. Test failed.\n");
689                 failed = 1;
690         }
691
692         if (expect_success3 && dispose_test_lot(expect_success3, 1)) {
693                 ast_test_status_update(test, "Found expect_success3 parking lot in container after attempted removal. Test failed.\n");
694                 failed = 1;
695         }
696
697         if (expect_success4 && dispose_test_lot(expect_success4, 1)) {
698                 ast_test_status_update(test, "Found expect_success4 parking lot in container after attempted removal. Test failed.\n");
699         }
700
701         return failed ? AST_TEST_FAIL : AST_TEST_PASS;
702 }
703
704 AST_TEST_DEFINE(dynamic_parking_variables)
705 {
706         RAII_VAR(struct parking_lot *, template_lot, NULL, ao2_cleanup);
707         RAII_VAR(struct parking_lot *, dynamic_lot, NULL, ao2_cleanup);
708         RAII_VAR(struct ast_channel *, chan_alice, NULL, safe_channel_release);
709         RAII_VAR(struct parking_lot_cfg *, expected_cfg, NULL, ao2_cleanup);
710
711         int failed = 0;
712
713         switch (cmd) {
714         case TEST_INIT:
715                 info->name = "dynamic_parking_variables";
716                 info->category = TEST_CATEGORY;
717                 info->summary = "Tests whether dynamic parking lot creation respects channel variables";
718                 info->description =
719                         "Creates a template parking lot, creates a channel, sets dynamic parking variables, and then creates a parking lot for that channel";
720                 return AST_TEST_NOT_RUN;
721         case TEST_EXECUTE:
722                 break;
723         }
724
725         ast_test_status_update(test, "Creating expected configuration for dynamic parking lot\n");
726
727         expected_cfg = parking_lot_cfg_create("unit_tests_res_parking_test_lot_dynamic");
728
729         if (!expected_cfg) {
730                 ast_test_status_update(test, "Failed to create expected configuration. Test failed.\n");
731                 return AST_TEST_FAIL;
732         }
733
734         expected_cfg->parking_start = 751;
735         expected_cfg->parking_stop = 760;
736         expected_cfg->parkingtime = 10;
737         expected_cfg->comebackdialtime = 10;
738         expected_cfg->parkfindnext = 1;
739         expected_cfg->parkext_exclusive = 1;
740         ast_string_field_set(expected_cfg, parkext, "750");
741         ast_string_field_set(expected_cfg, parking_con, "unit_test_res_parking_create_lot_dynamic");
742         ast_string_field_set(expected_cfg, comebackcontext, "unit_test_res_parking_create_lot_comeback");
743
744         ast_test_status_update(test, "Creating template lot\n");
745
746         template_lot = generate_test_parking_lot(TEST_LOT_NAME, 701, 703, "700", "unit_test_res_parking_create_lot_con", test);
747
748         if (!template_lot) {
749                 ast_test_status_update(test, "Failed to generate template lot. Test failed.\n");
750                 return AST_TEST_FAIL;
751         }
752
753         ast_test_status_update(test, "Creating Alice channel to test dynamic parking lot creation.\n");
754
755         chan_alice = create_alice_channel();
756
757         if (!chan_alice) {
758                 ast_test_status_update(test, "Failed to create Alice channel. Test failed.\n");
759                 failed = 1;
760                 goto cleanup;
761         }
762
763         ast_test_status_update(test, "Setting Dynamic Parking channel variables on Alice.\n");
764
765         pbx_builtin_setvar_helper(chan_alice, "PARKINGDYNAMIC", TEST_LOT_NAME);
766         pbx_builtin_setvar_helper(chan_alice, "PARKINGLOT", "unit_test_res_parking_create_lot_dynamic");
767         pbx_builtin_setvar_helper(chan_alice, "PARKINGDYNCONTEXT", "unit_test_res_parking_create_lot_dynamic");
768         pbx_builtin_setvar_helper(chan_alice, "PARKINGDYNEXTEN", "750");
769         pbx_builtin_setvar_helper(chan_alice, "PARKINGDYNPOS", "751-760");
770
771         ast_test_status_update(test, "Generating dynamic parking lot based on Alice's channel variables.");
772
773         dynamic_lot = parking_create_dynamic_lot_forced("unit_tests_res_parking_test_lot_dynamic", chan_alice);
774
775         if (!dynamic_lot) {
776                 ast_test_status_update(test, "Failed to create dynamic parking lot. Test failed.\n");
777                 failed = 1;
778                 goto cleanup;
779         }
780
781         /* Check stats */
782         if (!parking_lot_cfgs_match(dynamic_lot->cfg, expected_cfg, test)) {
783                 ast_test_status_update(test, "Dynamic parking lot configuration did not match Expectations.\n");
784                 failed = 1;
785                 goto cleanup;
786         }
787
788         ast_test_status_update(test, "Dynamic parking lot created successfully and matches expectations. Test passed.\n");
789
790 cleanup:
791         if (template_lot && dispose_test_lot(template_lot, 1)) {
792                 ast_test_status_update(test, "Found template parking lot in container after attempted removal. Test failed.\n");
793                 failed = 1;
794         }
795
796         if (dynamic_lot && dispose_test_lot(dynamic_lot, 1)) {
797                 ast_test_status_update(test, "Found dynamic parking lot in container after attempted removal. Test failed.\n");
798                 failed = 1;
799         }
800
801         return failed ? AST_TEST_FAIL : AST_TEST_PASS;
802 }
803
804 #endif /* TEST_FRAMEWORK */
805
806
807 void unload_parking_tests(void)
808 {
809 /* NOOP without test framework */
810 #if defined(TEST_FRAMEWORK)
811         AST_TEST_UNREGISTER(create_lot);
812         AST_TEST_UNREGISTER(park_call);
813         AST_TEST_UNREGISTER(retrieve_call);
814         AST_TEST_UNREGISTER(park_extensions);
815         AST_TEST_UNREGISTER(extension_conflicts);
816         AST_TEST_UNREGISTER(dynamic_parking_variables);
817 #endif
818 }
819
820 int load_parking_tests(void)
821 {
822         int res = 0;
823
824 /* NOOP without test framework */
825 #if defined(TEST_FRAMEWORK)
826         res |= AST_TEST_REGISTER(create_lot);
827         res |= AST_TEST_REGISTER(park_call);
828         res |= AST_TEST_REGISTER(retrieve_call);
829         res |= AST_TEST_REGISTER(park_extensions);
830         res |= AST_TEST_REGISTER(extension_conflicts);
831         res |= AST_TEST_REGISTER(dynamic_parking_variables);
832 #endif
833
834         return res;
835 }