The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
tmpl_tests.c
Go to the documentation of this file.
1/*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2.1 of the License, or (at your option) any later version.
6 *
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Tests for tmpl parsing and printing
18 *
19 * @file src/lib/server/tmpl_tests.c
20 *
21 * @copyright 2026 Network RADIUS SAS (legal@networkradius.com)
22 */
23
24static void test_init(void);
25# define TEST_INIT test_init()
26
27#include <freeradius-devel/util/test/acutest.h>
28#include <freeradius-devel/util/test/acutest_helpers.h>
29#include <freeradius-devel/util/dict_test.h>
30#include <freeradius-devel/server/tmpl.h>
31#include <freeradius-devel/server/tmpl_dcursor.h>
32#include <freeradius-devel/server/pair.h>
33
34static TALLOC_CTX *autofree;
36
37DIAG_OFF(declaration-after-statement)
38
39/** Global initialisation
40 */
41static void test_init(void)
42{
44 if (!autofree) {
45 error:
46 fr_perror("tmpl_tests");
47 fr_exit_now(EXIT_FAILURE);
48 }
49
50 /*
51 * Mismatch between the binary and the libraries it depends on
52 */
53 if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) goto error;
54
55 if (fr_dict_test_init(autofree, &test_dict, NULL) < 0) goto error;
56
57 if (request_global_init() < 0) goto error;
58}
59
61{
62 request_t *request;
63
64 /*
65 * Create and initialize the new request.
66 */
68
69 request->packet = fr_packet_alloc(request, false);
70 TEST_ASSERT(request->packet != NULL);
71
72 request->reply = fr_packet_alloc(request, false);
73 TEST_ASSERT(request->reply != NULL);
74
75 return request;
76}
77
78/** Default tmpl rules for tests
79 */
80#define test_rules() \
81 (&(tmpl_rules_t){ \
82 .attr = { \
83 .dict_def = test_dict, \
84 .list_def = request_attr_request, \
85 } \
86 })
87
88/*
89 * === Tokenization: tmpl_afrom_attr_str ===
90 */
91
92static void test_parse_attr_simple(void)
93{
94 tmpl_t *vpt = NULL;
96 ssize_t slen;
97
98 slen = tmpl_afrom_attr_str(autofree, &err, &vpt, "Test-String-0", test_rules());
99 TEST_CHECK(slen > 0);
100 TEST_ASSERT(vpt != NULL);
101
105}
106
107static void test_parse_attr_index(void)
108{
109 tmpl_t *vpt = NULL;
111 ssize_t slen;
112
113 slen = tmpl_afrom_attr_str(autofree, &err, &vpt, "Test-Int32-0[0]", test_rules());
114 TEST_CHECK(slen > 0);
115 TEST_ASSERT(vpt != NULL);
116
121}
122
123static void test_parse_attr_all(void)
124{
125 tmpl_t *vpt = NULL;
127 ssize_t slen;
128
129 slen = tmpl_afrom_attr_str(autofree, &err, &vpt, "Test-Int32-0[*]", test_rules());
130 TEST_CHECK(slen > 0);
131 TEST_ASSERT(vpt != NULL);
132
136}
137
138static void test_parse_attr_count(void)
139{
140 tmpl_t *vpt = NULL;
142 ssize_t slen;
143
144 slen = tmpl_afrom_attr_str(autofree, &err, &vpt, "Test-Int32-0[#]", test_rules());
145 TEST_CHECK(slen > 0);
146 TEST_ASSERT(vpt != NULL);
147
151}
152
153static void test_parse_attr_last(void)
154{
155 tmpl_t *vpt = NULL;
157 ssize_t slen;
158
159 slen = tmpl_afrom_attr_str(autofree, &err, &vpt, "Test-Int32-0[n]", test_rules());
160 TEST_CHECK(slen > 0);
161 TEST_ASSERT(vpt != NULL);
162
166}
167
168static void test_parse_attr_nested(void)
169{
170 tmpl_t *vpt = NULL;
172 ssize_t slen;
173
175 "Test-Nested-Top-TLV-0.Child-TLV.Leaf-String", test_rules());
176 TEST_CHECK(slen > 0);
177 TEST_ASSERT(vpt != NULL);
178
181 TEST_MSG("Expected at least 3 attr refs in chain, got %zu", tmpl_attr_num_elements(vpt));
184}
185
186static void test_parse_attr_missing(void)
187{
188 tmpl_t *vpt = NULL;
190 ssize_t slen;
191 tmpl_rules_t rules = {
192 .attr = {
194 .list_def = request_attr_request,
195 .allow_unresolved = true,
196 }
197 };
198
199 slen = tmpl_afrom_attr_str(autofree, &err, &vpt, "Non-Existent-Attr", &rules);
200 TEST_CHECK(slen > 0);
201 TEST_ASSERT(vpt != NULL);
202
205}
206
207static void test_parse_attr_invalid(void)
208{
209 tmpl_t *vpt = NULL;
211 ssize_t slen;
212
213 slen = tmpl_afrom_attr_str(autofree, &err, &vpt, "aaa^442", test_rules());
214 TEST_CHECK(slen < 0);
215 TEST_MSG("Expected negative slen for invalid attribute, got %zd", slen);
216 if (vpt) talloc_free(vpt);
217}
218
220{
221 tmpl_t *vpt = NULL;
223 ssize_t slen;
224 tmpl_rules_t rules = {
225 .attr = {
227 .list_def = request_attr_request,
228 .allow_unresolved = false,
229 }
230 };
231
232 slen = tmpl_afrom_attr_str(autofree, &err, &vpt, "", &rules);
233 TEST_CHECK(slen == -1);
234 TEST_MSG("Expected no data when no input, got slen=%zd", slen);
235 TEST_CHECK(vpt == NULL);
236}
237
238
239/*
240 * Tokenization: tmpl_afrom_substr()
241 */
242
244{
245 tmpl_t *vpt = NULL;
246 ssize_t slen;
247
248 slen = tmpl_afrom_substr(autofree, &vpt, &FR_SBUFF_IN_STR("Test-String-0"),
249 T_BARE_WORD, NULL, test_rules());
250 TEST_CHECK(slen > 0);
251 TEST_ASSERT(vpt != NULL);
252
256}
257
259{
260 tmpl_t *vpt = NULL;
261 ssize_t slen;
262
263 slen = tmpl_afrom_substr(autofree, &vpt, &FR_SBUFF_IN_STR("hello world"),
265 TEST_CHECK(slen > 0);
266 TEST_ASSERT(vpt != NULL);
267
268 /*
269 * Single-quoted strings without a cast produce
270 * TMPL_TYPE_DATA_UNRESOLVED. The string is stored in
271 * vpt->data.unescaped, and not in the value box.
272 */
275}
276
278{
279 tmpl_t *vpt = NULL;
280 ssize_t slen;
281
282 slen = tmpl_afrom_substr(autofree, &vpt, &FR_SBUFF_IN_STR("hello world"),
284 TEST_CHECK(slen > 0);
285 TEST_ASSERT(vpt != NULL);
286
287 /*
288 * Double quoted strings without expansions end up as
289 * data or unresolved.
290 * A literal "hello world" should result in data unresolved.
291 */
294}
295
297{
298 tmpl_t *vpt = NULL;
299 ssize_t slen;
300 tmpl_rules_t rules = {
301 .attr = {
303 .list_def = request_attr_request,
304 .allow_unresolved = true,
305 }
306 };
307
308 slen = tmpl_afrom_substr(autofree, &vpt, &FR_SBUFF_IN_STR("Hello %{User-Name}"),
309 T_DOUBLE_QUOTED_STRING, NULL, &rules);
310 TEST_CHECK(slen > 0);
311 TEST_ASSERT(vpt != NULL);
312
313 /*
314 * Double quoted strings with expansions end up as
315 * xlats.
316 */
319}
320
321/*
322 * tmpl_afrom_value_box()
323 */
324
326{
327 tmpl_t *vpt = NULL;
328 fr_value_box_t box;
329 int ret;
330
331 fr_value_box_init(&box, FR_TYPE_STRING, NULL, false);
332 fr_value_box_strdup_shallow(&box, NULL, "test", false);
333
334 ret = tmpl_afrom_value_box(autofree, &vpt, &box, false);
335 TEST_CHECK(ret == 0);
336 TEST_ASSERT(vpt != NULL);
337
340 TEST_CHECK(strcmp(tmpl_value(vpt)->vb_strvalue, "test") == 0);
341
343}
344
346{
347 tmpl_t *vpt = NULL;
348 fr_value_box_t box;
349 int ret;
350
351 fr_value_box_init(&box, FR_TYPE_UINT32, NULL, false);
352 box.vb_uint32 = 42;
353
354 ret = tmpl_afrom_value_box(autofree, &vpt, &box, false);
355 TEST_CHECK(ret == 0);
356 TEST_ASSERT(vpt != NULL);
357
361
363}
364
366{
367 tmpl_t *vpt = NULL;
368 fr_value_box_t box;
369 int ret;
370
371 fr_value_box_init(&box, FR_TYPE_IPV4_ADDR, NULL, false);
372 box.vb_ip.af = AF_INET;
373 box.vb_ip.prefix = 32;
374 box.vb_ip.addr.v4.s_addr = htonl(INADDR_LOOPBACK);
375
376 ret = tmpl_afrom_value_box(autofree, &vpt, &box, false);
377 TEST_CHECK(ret == 0);
378 TEST_ASSERT(vpt != NULL);
379
382
384}
385
386/*
387 * tmpl_cast_in_place()
388 */
389
391{
392 tmpl_t *vpt = NULL;
393 ssize_t slen;
394 int ret;
395 tmpl_rules_t rules = *test_rules();
396
397 /*
398 * Parse "42" as single-quoted. Without a cast, single-quoted
399 * strings produce TMPL_TYPE_DATA_UNRESOLVED.
400 */
402 T_SINGLE_QUOTED_STRING, NULL, &rules);
403 TEST_CHECK(slen > 0);
404 TEST_ASSERT(vpt != NULL);
405
407
409 TEST_CHECK(ret == 0);
410 TEST_MSG("tmpl_cast_in_place failed: %s", fr_strerror());
411
415
417}
418
420{
421 tmpl_t *vpt = NULL;
422 ssize_t slen;
423 int ret;
424
425 slen = tmpl_afrom_substr(autofree, &vpt, &FR_SBUFF_IN_STR("hello"),
427 TEST_CHECK(slen > 0);
428 TEST_ASSERT(vpt != NULL);
429
431 TEST_CHECK(ret == 0);
432
435 TEST_CHECK(strcmp(tmpl_value(vpt)->vb_strvalue, "hello") == 0);
436
438}
439
441{
442 tmpl_t *vpt = NULL;
443 fr_value_box_t box;
444 int ret;
445
446 fr_value_box_init(&box, FR_TYPE_UINT32, NULL, false);
447 box.vb_uint32 = 100;
448
449 ret = tmpl_afrom_value_box(autofree, &vpt, &box, false);
450 TEST_CHECK(ret == 0);
451 TEST_ASSERT(vpt != NULL);
452
454 TEST_CHECK(ret == 0);
455 TEST_MSG("tmpl_cast_in_place failed: %s", fr_strerror());
456
460
462}
463
464static void test_cast_invalid(void)
465{
466 tmpl_t *vpt = NULL;
467 ssize_t slen;
468 int ret;
469
470 slen = tmpl_afrom_substr(autofree, &vpt, &FR_SBUFF_IN_STR("not_a_number"),
472 TEST_CHECK(slen > 0);
473 TEST_ASSERT(vpt != NULL);
474
476 TEST_CHECK(ret == -1);
477 TEST_MSG("Expected failure casting 'not_a_number' to uint32");
478
480}
481
482/*
483 * tmpl_copy_foo()
484 */
485
486static void test_copy_data(void)
487{
488 tmpl_t *vpt = NULL;
489 tmpl_t *copy;
490 fr_value_box_t box;
491 int ret;
492
493 fr_value_box_init(&box, FR_TYPE_STRING, NULL, false);
494 fr_value_box_strdup_shallow(&box, NULL, "copy_test", false);
495
496 ret = tmpl_afrom_value_box(autofree, &vpt, &box, false);
497 TEST_CHECK(ret == 0);
498 TEST_ASSERT(vpt != NULL);
499
500 copy = tmpl_copy(autofree, vpt);
501 TEST_ASSERT(copy != NULL);
502
503 TEST_CHECK(copy != vpt);
506 TEST_CHECK(strcmp(tmpl_value(copy)->vb_strvalue, "copy_test") == 0);
507
509 talloc_free(copy);
510}
511
512static void test_copy_attr(void)
513{
514 tmpl_t *vpt = NULL;
515 tmpl_t *copy;
517 ssize_t slen;
518
519 slen = tmpl_afrom_attr_str(autofree, &err, &vpt, "Test-String-0", test_rules());
520 TEST_CHECK(slen > 0);
521 TEST_ASSERT(vpt != NULL);
522
523 copy = tmpl_copy(autofree, vpt);
524 TEST_ASSERT(copy != NULL);
525
526 TEST_CHECK(copy != vpt);
529
531 talloc_free(copy);
532}
533
535{
536 tmpl_t *vpt = NULL;
537 tmpl_t *copy;
538 ssize_t slen;
539
540 slen = tmpl_afrom_substr(autofree, &vpt, &FR_SBUFF_IN_STR("hello world"),
542 TEST_CHECK(slen > 0);
543 TEST_ASSERT(vpt != NULL);
544
545 copy = tmpl_copy(autofree, vpt);
546 TEST_ASSERT(copy != NULL);
547
548 TEST_CHECK(copy != vpt);
549 TEST_CHECK(copy->type == vpt->type);
550
552 talloc_free(copy);
553}
554
555/*
556 * tmpl_eval() and friends
557 */
558
559static void test_eval_data(void)
560{
561 tmpl_t *vpt = NULL;
562 fr_value_box_t box;
563 fr_value_box_list_t out;
564 fr_value_box_t *result;
565 request_t *request = request_fake_alloc();
566 int ret;
567
568 fr_value_box_list_init(&out);
569
570 fr_value_box_init(&box, FR_TYPE_UINT32, NULL, false);
571 box.vb_uint32 = 42;
572
573 ret = tmpl_afrom_value_box(autofree, &vpt, &box, false);
574 TEST_CHECK(ret == 0);
575 TEST_ASSERT(vpt != NULL);
576
577 ret = tmpl_eval(autofree, &out, request, vpt);
578 TEST_CHECK(ret == 0);
579 TEST_MSG("tmpl_eval failed: %s", fr_strerror());
580
581 result = fr_value_box_list_head(&out);
582 TEST_ASSERT(result != NULL);
583 TEST_CHECK(result->type == FR_TYPE_UINT32);
584 TEST_CHECK(result->vb_uint32 == 42);
585
587 fr_value_box_list_talloc_free(&out);
588 talloc_free(request);
589}
590
591static void test_eval_attr_found(void)
592{
593 tmpl_t *vpt = NULL;
595 fr_value_box_list_t out;
596 fr_value_box_t *result;
597 request_t *request = request_fake_alloc();
598 fr_pair_t *string_vp;
599 ssize_t slen;
600 int ret;
601
602 fr_value_box_list_init(&out);
603
605 TEST_ASSERT(string_vp != NULL);
606 ret = fr_pair_value_strdup(string_vp, "hello", false); /* @todo - shallow copy here crashes! */
607 TEST_CHECK(ret == 0);
608
609 slen = tmpl_afrom_attr_str(autofree, &err, &vpt, "Test-String-0", test_rules());
610 TEST_CHECK(slen > 0);
611 TEST_ASSERT(vpt != NULL);
612
613 ret = tmpl_eval(autofree, &out, request, vpt);
614 TEST_CHECK(ret == 0);
615 TEST_MSG("tmpl_eval failed: %s", fr_strerror());
616
617 result = fr_value_box_list_head(&out);
618 TEST_ASSERT(result != NULL);
619 TEST_CHECK(result->type == FR_TYPE_STRING);
620 TEST_CHECK(strcmp(result->vb_strvalue, "hello") == 0);
621
623 fr_value_box_list_talloc_free(&out);
624 talloc_free(request);
625}
626
627static void test_eval_attr_missing(void)
628{
629 tmpl_t *vpt = NULL;
631 fr_value_box_list_t out;
632 fr_value_box_t *result;
633 request_t *request = request_fake_alloc();
634 ssize_t slen;
635 int ret;
636
637 fr_value_box_list_init(&out);
638
639 slen = tmpl_afrom_attr_str(autofree, &err, &vpt, "Test-Int16-0", test_rules());
640 TEST_CHECK(slen > 0);
641 TEST_ASSERT(vpt != NULL);
642
643 ret = tmpl_eval(autofree, &out, request, vpt);
644 /*
645 * When the attribute is missing, tmpl_eval returns 0 with an empty list.
646 */
647 TEST_CHECK(ret == 0);
648
649 result = fr_value_box_list_head(&out);
650 TEST_CHECK(result == NULL);
651 TEST_MSG("Expected empty result list for missing attribute");
652
654 fr_value_box_list_talloc_free(&out);
655 talloc_free(request);
656}
657
658static void test_eval_attr_multiple(void)
659{
660 tmpl_t *vpt = NULL;
662 fr_value_box_list_t out;
663 fr_value_box_t *result;
664 request_t *request = request_fake_alloc();
665 fr_pair_t *vp1, *vp2;
666 ssize_t slen;
667 int ret;
668
669 fr_value_box_list_init(&out);
670
672 vp1->vp_int32 = 10;
673
675 vp2->vp_int32 = 20;
676
677 slen = tmpl_afrom_attr_str(autofree, &err, &vpt, "Test-Int32-0[*]", test_rules());
678 TEST_CHECK(slen > 0);
679 TEST_ASSERT(vpt != NULL);
680
681 ret = tmpl_eval(autofree, &out, request, vpt);
682 TEST_CHECK(ret == 0);
683 TEST_MSG("tmpl_eval failed: %s", fr_strerror());
684
685 result = fr_value_box_list_head(&out);
686 TEST_ASSERT(result != NULL);
687 TEST_CHECK(result->type == FR_TYPE_INT32);
688 TEST_CHECK(result->vb_int32 == 10);
689
690 result = fr_value_box_list_next(&out, result);
691 TEST_ASSERT(result != NULL);
692 TEST_CHECK(result->vb_int32 == 20);
693
695 fr_value_box_list_talloc_free(&out);
696 talloc_free(request);
697}
698
699static void test_eval_attr_count(void)
700{
701 tmpl_t *vpt = NULL;
703 fr_value_box_list_t out;
704 fr_value_box_t *result;
705 request_t *request = request_fake_alloc();
706 fr_pair_t *vp1, *vp2;
707 ssize_t slen;
708 int ret;
709
710 fr_value_box_list_init(&out);
711
713 vp1->vp_int32 = 10;
714
716 vp2->vp_int32 = 20;
717
718 slen = tmpl_afrom_attr_str(autofree, &err, &vpt, "Test-Int32-0[#]", test_rules());
719 TEST_CHECK(slen > 0);
720 TEST_ASSERT(vpt != NULL);
721
722 ret = tmpl_eval(autofree, &out, request, vpt);
723 TEST_CHECK(ret == 0);
724 TEST_MSG("tmpl_eval failed: %s", fr_strerror());
725
726 result = fr_value_box_list_head(&out);
727 TEST_ASSERT(result != NULL);
728 TEST_CHECK(result->type == FR_TYPE_UINT32);
729 TEST_CHECK(result->vb_uint32 == 2);
730 TEST_MSG("Expected count=2, got %" PRIu32, result->vb_uint32);
731
733 fr_value_box_list_talloc_free(&out);
734 talloc_free(request);
735}
736
737/*
738 * tmpl_find_vp() and tmpl_find_or_add_vp()
739 */
740
741static void test_find_vp_found(void)
742{
743 tmpl_t *vpt = NULL;
745 request_t *request = request_fake_alloc();
746 fr_pair_t *string_vp;
747 fr_pair_t *found = NULL;
748 ssize_t slen;
749 int ret;
750
752 fr_pair_value_strdup(string_vp, "findme", false);
753
754 slen = tmpl_afrom_attr_str(autofree, &err, &vpt, "Test-String-0", test_rules());
755 TEST_CHECK(slen > 0);
756 TEST_ASSERT(vpt != NULL);
757
758 ret = tmpl_find_vp(&found, request, vpt);
759 TEST_CHECK(ret == 0);
760 TEST_CHECK(found == string_vp);
761
763 talloc_free(request);
764}
765
766static void test_find_vp_missing(void)
767{
768 tmpl_t *vpt = NULL;
770 request_t *request = request_fake_alloc();
771 fr_pair_t *found = NULL;
772 ssize_t slen;
773 int ret;
774
775 slen = tmpl_afrom_attr_str(autofree, &err, &vpt, "Test-Int16-0", test_rules());
776 TEST_CHECK(slen > 0);
777 TEST_ASSERT(vpt != NULL);
778
779 ret = tmpl_find_vp(&found, request, vpt);
780 TEST_CHECK(ret != 0);
781 TEST_CHECK(found == NULL);
782
784 talloc_free(request);
785}
786
788{
789 tmpl_t *vpt = NULL;
791 request_t *request = request_fake_alloc();
792 fr_pair_t *string_vp;
793 fr_pair_t *found = NULL;
794 ssize_t slen;
795 int ret;
796
798 fr_pair_value_strdup(string_vp, "existing", false);
799
800 slen = tmpl_afrom_attr_str(autofree, &err, &vpt, "Test-String-0", test_rules());
801 TEST_CHECK(slen > 0);
802 TEST_ASSERT(vpt != NULL);
803
804 ret = tmpl_find_or_add_vp(&found, request, vpt);
805 TEST_CHECK(ret == 0);
806 TEST_MSG("Expected 0 (found existing), got %d", ret);
807 TEST_CHECK(found == string_vp);
808
810 talloc_free(request);
811}
812
813static void test_find_or_add_new(void)
814{
815 tmpl_t *vpt = NULL;
817 request_t *request = request_fake_alloc();
818 fr_pair_t *found = NULL;
819 ssize_t slen;
820 int ret;
821
822 slen = tmpl_afrom_attr_str(autofree, &err, &vpt, "Test-Int16-0", test_rules());
823 TEST_CHECK(slen > 0);
824 TEST_ASSERT(vpt != NULL);
825
826 ret = tmpl_find_or_add_vp(&found, request, vpt);
827 TEST_CHECK(ret == 1);
828 TEST_MSG("Expected 1 (created new), got %d", ret);
829 TEST_CHECK(found != NULL);
830 TEST_MSG("Expected non-NULL vp from find_or_add");
831
833 talloc_free(request);
834}
835
836/*
837 * tmpl_print() and friends.
838 */
839
840static void test_print_attr(void)
841{
842 tmpl_t *vpt = NULL;
844 ssize_t slen;
845 char *str = NULL;
846
847 slen = tmpl_afrom_attr_str(autofree, &err, &vpt, "Test-String-0", test_rules());
848 TEST_CHECK(slen > 0);
849 TEST_ASSERT(vpt != NULL);
850
851 slen = tmpl_aprint(autofree, &str, vpt, NULL);
852 TEST_CHECK(slen > 0);
853 TEST_CHECK(str != NULL);
854 if (str) {
855 TEST_CHECK(strcmp(str, "Test-String-0") == 0);
856 TEST_MSG("Expected 'Test-String-0', got '%s'", str);
857 talloc_free(str);
858 }
859
861}
862
863static void test_print_data_string(void)
864{
865 tmpl_t *vpt = NULL;
866 fr_value_box_t box;
867 int ret;
868 ssize_t slen;
869 char *str = NULL;
870
871 fr_value_box_init(&box, FR_TYPE_STRING, NULL, false);
872 fr_value_box_strdup_shallow(&box, NULL, "hello", false);
873
874 ret = tmpl_afrom_value_box(autofree, &vpt, &box, false);
875 TEST_CHECK(ret == 0);
876 TEST_ASSERT(vpt != NULL);
877
878 slen = tmpl_aprint(autofree, &str, vpt, NULL);
879 TEST_CHECK(slen > 0);
880 TEST_CHECK(str != NULL);
881 if (str) {
882 TEST_CHECK(strcmp(str, "hello") == 0);
883 TEST_MSG("Expected 'hello', got '%s'", str);
884 talloc_free(str);
885 }
886
888}
889
890static void test_print_data_uint32(void)
891{
892 tmpl_t *vpt = NULL;
893 fr_value_box_t box;
894 int ret;
895 ssize_t slen;
896 char *str = NULL;
897
898 fr_value_box_init(&box, FR_TYPE_UINT32, NULL, false);
899 box.vb_uint32 = 42;
900
901 ret = tmpl_afrom_value_box(autofree, &vpt, &box, false);
902 TEST_CHECK(ret == 0);
903 TEST_ASSERT(vpt != NULL);
904
905 slen = tmpl_aprint(autofree, &str, vpt, NULL);
906 TEST_CHECK(slen > 0);
907 TEST_CHECK(str != NULL);
908 if (str) {
909 TEST_CHECK(strcmp(str, "42") == 0);
910 TEST_MSG("Expected '42', got '%s'", str);
911 talloc_free(str);
912 }
913
915}
916
917static void test_print_quoted(void)
918{
919 tmpl_t *vpt = NULL;
920 fr_value_box_t box;
921 int ret;
922 ssize_t slen;
923 char *str = NULL;
924
925 fr_value_box_init(&box, FR_TYPE_STRING, NULL, false);
926 fr_value_box_strdup_shallow(&box, NULL, "hello", false);
927
928 ret = tmpl_afrom_value_box(autofree, &vpt, &box, false);
929 TEST_CHECK(ret == 0);
930 TEST_ASSERT(vpt != NULL);
931
932 /*
933 * String values from tmpl_afrom_value_box get T_SINGLE_QUOTED_STRING,
934 * so tmpl_print_quoted should wrap with single quotes.
935 */
936 slen = tmpl_aprint_quoted(autofree, &str, vpt);
937 TEST_CHECK(slen > 0);
938 TEST_CHECK(str != NULL);
939 if (str) {
940 TEST_CHECK(strcmp(str, "'hello'") == 0);
941 TEST_MSG("Expected \"'hello'\", got '%s'", str);
942 talloc_free(str);
943 }
944
946}
947
948/*
949 * Type checking macros.
950 */
951
952static void test_type_checking(void)
953{
954 tmpl_t *data_vpt = NULL;
955 tmpl_t *attr_vpt = NULL;
956 tmpl_t *unresolved_vpt = NULL;
958 fr_value_box_t box;
959 ssize_t slen;
960 int ret;
961
962 /* Create a DATA tmpl */
963 fr_value_box_init(&box, FR_TYPE_UINT32, NULL, false);
964 box.vb_uint32 = 1;
965 ret = tmpl_afrom_value_box(autofree, &data_vpt, &box, false);
966 TEST_CHECK(ret == 0);
967 TEST_ASSERT(data_vpt != NULL);
968 TEST_CHECK(tmpl_is_data(data_vpt));
969 TEST_CHECK(!tmpl_is_attr(data_vpt));
971
972 /* Create an ATTR tmpl */
973 slen = tmpl_afrom_attr_str(autofree, &err, &attr_vpt, "Test-String-0", test_rules());
974 TEST_CHECK(slen > 0);
975 TEST_ASSERT(attr_vpt != NULL);
976 TEST_CHECK(tmpl_is_attr(attr_vpt));
977 TEST_CHECK(!tmpl_is_data(attr_vpt));
980
981 /* Create a DATA_UNRESOLVED tmpl */
982 slen = tmpl_afrom_substr(autofree, &unresolved_vpt, &FR_SBUFF_IN_STR("hello"),
984 TEST_CHECK(slen > 0);
985 TEST_ASSERT(unresolved_vpt != NULL);
986
987 /*
988 * Double-quoted "hello" (no expansions) is DATA_UNRESOLVED.
989 */
990 if (tmpl_is_data_unresolved(unresolved_vpt)) {
991 TEST_CHECK(tmpl_needs_resolving(unresolved_vpt));
992 TEST_CHECK(!tmpl_is_attr(unresolved_vpt));
993 }
994
995 talloc_free(data_vpt);
996 talloc_free(attr_vpt);
997 talloc_free(unresolved_vpt);
998}
999
1000/*
1001 * Error cases.
1002 */
1003
1005{
1006 tmpl_t *vpt = NULL;
1008 ssize_t slen;
1009
1010 slen = tmpl_afrom_attr_str(autofree, &err, &vpt, "", test_rules());
1011 TEST_CHECK(slen <= 0);
1012 TEST_CHECK(vpt == NULL);
1013 TEST_MSG("Expected error for empty attr ref '&', got slen=%zd", slen);
1015 TEST_MSG("Expected non-zero error code, got %d", err);
1016}
1017
1019{
1020 tmpl_t *vpt = NULL;
1022 ssize_t slen;
1023 tmpl_rules_t rules = {
1024 .attr = {
1026 .list_def = request_attr_request,
1027 .allow_unresolved = false,
1028 }
1029 };
1030
1031 slen = tmpl_afrom_attr_str(autofree, &err, &vpt, "No-Such-Attr", &rules);
1032 TEST_CHECK(slen < 0);
1033 TEST_MSG("Expected error when unresolved not allowed, got slen=%zd", slen);
1034 TEST_CHECK(vpt == NULL);
1035}
1036
1038 /* Tokenization: tmpl_afrom_attr_str */
1039 { "test_parse_attr_simple", test_parse_attr_simple },
1040 { "test_parse_attr_index", test_parse_attr_index },
1041 { "test_parse_attr_all", test_parse_attr_all },
1042 { "test_parse_attr_count", test_parse_attr_count },
1043 { "test_parse_attr_last", test_parse_attr_last },
1044 { "test_parse_attr_nested", test_parse_attr_nested },
1045 { "test_parse_attr_missing", test_parse_attr_missing },
1046 { "test_parse_attr_invalid", test_parse_attr_invalid },
1047 { "test_parse_attr_emptystring", test_parse_attr_emptystring },
1048
1049 /* Tokenization: tmpl_afrom_substr */
1050 { "test_parse_bareword_attr", test_parse_bareword_attr },
1051 { "test_parse_single_quoted", test_parse_single_quoted },
1052 { "test_parse_double_quoted_literal", test_parse_double_quoted_literal },
1053 { "test_parse_double_quoted_xlat", test_parse_double_quoted_xlat },
1054
1055 /* tmpl_afrom_value_box */
1056 { "test_from_value_box_string", test_from_value_box_string },
1057 { "test_from_value_box_uint32", test_from_value_box_uint32 },
1058 { "test_from_value_box_ipaddr", test_from_value_box_ipaddr },
1059
1060 /* tmpl_cast_in_place */
1061 { "test_cast_unresolved_to_uint32", test_cast_unresolved_to_uint32 },
1062 { "test_cast_unresolved_to_string", test_cast_unresolved_to_string },
1063 { "test_cast_uint32_to_uint64", test_cast_uint32_to_uint64 },
1064 { "test_cast_invalid", test_cast_invalid },
1065
1066 /* tmpl_copy */
1067 { "test_copy_data", test_copy_data },
1068 { "test_copy_attr", test_copy_attr },
1069 { "test_copy_data_unresolved", test_copy_data_unresolved },
1070
1071 /* tmpl_eval and tmpl_eval_pair */
1072 { "test_eval_data", test_eval_data },
1073 { "test_eval_attr_found", test_eval_attr_found },
1074 { "test_eval_attr_missing", test_eval_attr_missing },
1075 { "test_eval_attr_multiple", test_eval_attr_multiple },
1076 { "test_eval_attr_count", test_eval_attr_count },
1077
1078 /* tmpl_find_vp and tmpl_find_or_add_vp */
1079 { "test_find_vp_found", test_find_vp_found },
1080 { "test_find_vp_missing", test_find_vp_missing },
1081 { "test_find_or_add_existing", test_find_or_add_existing },
1082 { "test_find_or_add_new", test_find_or_add_new },
1083
1084 /* tmpl_print and tmpl_print_quoted */
1085 { "test_print_attr", test_print_attr },
1086 { "test_print_data_string", test_print_data_string },
1087 { "test_print_data_uint32", test_print_data_uint32 },
1088 { "test_print_quoted", test_print_quoted },
1089
1090 /* Type checking macros */
1091 { "test_type_checking", test_type_checking },
1092
1093 /* Error cases */
1094 { "test_parse_attr_empty_ref", test_parse_attr_empty_ref },
1095 { "test_parse_attr_unresolved_disallowed", test_parse_attr_unresolved_disallowed },
1096
1098};
1099
1100DIAG_ON(declaration-after-statement)
#define TEST_CHECK(cond)
Definition acutest.h:87
#define TEST_ASSERT(cond)
Definition acutest.h:110
#define TEST_TERMINATOR
Definition acutest.h:64
#define TEST_MSG(...)
Definition acutest.h:217
#define DIAG_ON(_x)
Definition build.h:462
#define DIAG_OFF(_x)
Definition build.h:461
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
Definition debug.h:226
static fr_slen_t err
Definition dict.h:884
fr_dict_attr_t const * fr_dict_attr_test_nested_leaf_string
Definition dict_test.c:78
fr_dict_attr_t const * fr_dict_attr_test_int32
Definition dict_test.c:52
fr_dict_attr_t const * fr_dict_attr_test_string
Definition dict_test.c:30
int fr_dict_test_init(TALLOC_CTX *ctx, fr_dict_t **dict_p, fr_dict_test_attr_t const *test_defs)
Initialise a test dictionary and add our test_defs to it.
Definition dict_test.c:248
talloc_free(hp)
fr_packet_t * fr_packet_alloc(TALLOC_CTX *ctx, bool new_vector)
Allocate a new fr_packet_t.
Definition packet.c:38
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_INT32
32 Bit signed integer.
@ FR_TYPE_UINT64
64 Bit unsigned integer.
long int ssize_t
int fr_pair_value_strdup(fr_pair_t *vp, char const *src, bool tainted)
Copy data into an "string" data type.
Definition pair.c:2651
fr_dict_attr_t const * request_attr_request
Definition request.c:43
int request_global_init(void)
Definition request.c:596
#define request_local_alloc_external(_ctx, _args)
Allocate a new external request outside of the request pool.
Definition request.h:335
Optional arguments for initialising requests.
Definition request.h:287
#define FR_SBUFF_IN_STR(_start)
#define pair_append_request(_attr, _da)
Allocate and append a fr_pair_t to the request list.
Definition pair.h:37
int tmpl_find_vp(fr_pair_t **out, request_t *request, tmpl_t const *vpt))
Returns the first VP matching a tmpl_t.
Definition tmpl_eval.c:776
static int16_t tmpl_attr_tail_num(tmpl_t const *vpt)
Return the last attribute reference's attribute number.
Definition tmpl.h:885
#define tmpl_is_attr_unresolved(vpt)
Definition tmpl.h:219
#define NUM_LAST
Definition tmpl.h:397
static fr_slen_t tmpl_aprint_quoted(TALLOC_CTX *ctx, char **out, tmpl_t const *vpt) 1(tmpl_print_quoted
#define tmpl_value(_tmpl)
Definition tmpl.h:937
int tmpl_afrom_value_box(TALLOC_CTX *ctx, tmpl_t **out, fr_value_box_t *data, bool steal)
Create a tmpl_t from a fr_value_box_t.
#define tmpl_is_xlat_unresolved(vpt)
Definition tmpl.h:220
#define tmpl_is_attr(vpt)
Definition tmpl.h:208
#define NUM_ALL
Definition tmpl.h:395
static fr_slen_t tmpl_aprint(TALLOC_CTX *ctx, char **out, tmpl_t const *vpt, fr_sbuff_escape_rules_t const *e_rules) 1(tmpl_print
ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_t **out, char const *name, tmpl_rules_t const *rules))
Parse a string into a TMPL_TYPE_ATTR_* type tmpl_t.
#define NUM_COUNT
Definition tmpl.h:396
#define tmpl_contains_attr(vpt)
Definition tmpl.h:225
int tmpl_eval(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *request, tmpl_t const *vpt)
Gets the value of a tmpl.
Definition tmpl_eval.c:1103
tmpl_t * tmpl_copy(TALLOC_CTX *ctx, tmpl_t const *in)
Copy a tmpl.
ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_token_t quote, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Convert an arbitrary string into a tmpl_t.
int tmpl_cast_in_place(tmpl_t *vpt, fr_type_t type, fr_dict_attr_t const *enumv))
Convert tmpl_t of type TMPL_TYPE_DATA_UNRESOLVED or TMPL_TYPE_DATA to TMPL_TYPE_DATA of type specifie...
#define tmpl_is_data(vpt)
Definition tmpl.h:206
static fr_slen_t vpt
Definition tmpl.h:1269
static size_t tmpl_attr_num_elements(tmpl_t const *vpt)
The number of attribute references contained within a tmpl.
Definition tmpl.h:896
#define tmpl_value_type(_tmpl)
Definition tmpl.h:939
tmpl_attr_error_t
Definition tmpl.h:1004
@ TMPL_ATTR_ERROR_NONE
No error.
Definition tmpl.h:1005
#define tmpl_is_data_unresolved(vpt)
Definition tmpl.h:217
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:339
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition tmpl.h:801
int tmpl_find_or_add_vp(fr_pair_t **out, request_t *request, tmpl_t const *vpt)
Returns the first VP matching a tmpl_t, or if no VPs match, creates a new one.
Definition tmpl_eval.c:805
#define tmpl_needs_resolving(vpt)
Definition tmpl.h:223
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:336
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:273
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
#define talloc_autofree_context
The original function is deprecated, so replace it with our version.
Definition talloc.h:51
static void test_eval_attr_found(void)
Definition tmpl_tests.c:591
static void test_eval_attr_missing(void)
Definition tmpl_tests.c:627
TEST_LIST
static void test_cast_unresolved_to_uint32(void)
Definition tmpl_tests.c:390
static void test_parse_attr_missing(void)
Definition tmpl_tests.c:186
static void test_parse_double_quoted_literal(void)
Definition tmpl_tests.c:277
static void test_cast_uint32_to_uint64(void)
Definition tmpl_tests.c:440
static TALLOC_CTX * autofree
Definition tmpl_tests.c:34
static request_t * request_fake_alloc(void)
Definition tmpl_tests.c:60
static void test_parse_attr_empty_ref(void)
static void test_from_value_box_ipaddr(void)
Definition tmpl_tests.c:365
static void test_parse_attr_invalid(void)
Definition tmpl_tests.c:207
static void test_type_checking(void)
Definition tmpl_tests.c:952
static void test_parse_attr_simple(void)
Definition tmpl_tests.c:92
static void test_parse_attr_last(void)
Definition tmpl_tests.c:153
static void test_cast_invalid(void)
Definition tmpl_tests.c:464
static void test_copy_attr(void)
Definition tmpl_tests.c:512
static void test_from_value_box_uint32(void)
Definition tmpl_tests.c:345
static void test_parse_attr_unresolved_disallowed(void)
static void test_parse_attr_nested(void)
Definition tmpl_tests.c:168
static void test_find_or_add_existing(void)
Definition tmpl_tests.c:787
static void test_copy_data_unresolved(void)
Definition tmpl_tests.c:534
static void test_parse_attr_index(void)
Definition tmpl_tests.c:107
static void test_print_data_string(void)
Definition tmpl_tests.c:863
static fr_dict_t * test_dict
Definition tmpl_tests.c:35
static void test_print_data_uint32(void)
Definition tmpl_tests.c:890
static void test_print_quoted(void)
Definition tmpl_tests.c:917
static void test_parse_attr_emptystring(void)
Definition tmpl_tests.c:219
static void test_parse_attr_all(void)
Definition tmpl_tests.c:123
static void test_copy_data(void)
Definition tmpl_tests.c:486
static void test_eval_attr_count(void)
Definition tmpl_tests.c:699
static void test_parse_bareword_attr(void)
Definition tmpl_tests.c:243
static void test_eval_data(void)
Definition tmpl_tests.c:559
static void test_parse_attr_count(void)
Definition tmpl_tests.c:138
#define test_rules()
Default tmpl rules for tests.
Definition tmpl_tests.c:80
static void test_find_or_add_new(void)
Definition tmpl_tests.c:813
static void test_find_vp_found(void)
Definition tmpl_tests.c:741
static void test_from_value_box_string(void)
Definition tmpl_tests.c:325
static void test_find_vp_missing(void)
Definition tmpl_tests.c:766
static void test_parse_double_quoted_xlat(void)
Definition tmpl_tests.c:296
static void test_init(void)
Global initialisation.
Definition tmpl_tests.c:41
static void test_print_attr(void)
Definition tmpl_tests.c:840
static void test_parse_single_quoted(void)
Definition tmpl_tests.c:258
static void test_eval_attr_multiple(void)
Definition tmpl_tests.c:658
static void test_cast_unresolved_to_string(void)
Definition tmpl_tests.c:419
@ T_SINGLE_QUOTED_STRING
Definition token.h:122
@ T_BARE_WORD
Definition token.h:120
@ T_DOUBLE_QUOTED_STRING
Definition token.h:121
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:553
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
Definition strerror.c:732
int fr_check_lib_magic(uint64_t magic)
Check if the application linking to the library has the correct magic number.
Definition version.c:40
#define RADIUSD_MAGIC_NUMBER
Definition version.h:81
void fr_value_box_strdup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Assign a buffer containing a nul terminated string to a box, but don't copy it.
Definition value.c:4744
#define vb_strvalue
Definition value.h:258
#define vb_uint32
Definition value.h:274
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition value.h:610
#define vb_uint64
Definition value.h:275
static size_t char ** out
Definition value.h:1030