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