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