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