44#define TEST_GLOBAL_INIT test_init()
46#include <freeradius-devel/util/test/acutest_common_init.h>
47#include <freeradius-devel/util/test/acutest_helpers.h>
48#include <freeradius-devel/util/dict_test.h>
50#include <freeradius-devel/server/pairmove.h>
51#include <freeradius-devel/server/request.h>
52#include <freeradius-devel/util/conf.h>
53#include <freeradius-devel/util/pair.h>
66 { .name =
"s0" }, { .name =
"s1" }, { .name =
"s2" },
67 { .name =
"i0" }, { .name =
"i1" },
68 { .name =
"p4" }, { .name =
"p6" },
69 { .name =
"un" }, { .name =
"up" },
134 char const *p = spec;
144 while (*p ==
' ') p++;
171 len = strcspn(p,
" ");
181#ifdef STATIC_ANALYZER
205 char const *
name = NULL;
266 return &
request->request_pairs;
274 return &
request->control_pairs;
291 size_t before, after;
296 before = talloc_total_blocks(
request);
315 after = talloc_total_blocks(
request);
317 TEST_MSG(
"Blocks before : %zu", before);
318 TEST_MSG(
"Blocks after : %zu", after);
328 for (i = 0; i < num; i++) {
340 {
"empty into empty",
"",
"",
"" },
341 {
"= into empty",
"",
"s0=a",
"s0=a" },
342 {
"= absent attribute",
"s0=a",
"s1=b",
"s0=a s1=b" },
343 {
"= present attribute",
"s0=a",
"s0=b",
"s0=a" },
344 {
"= present, others",
"s1=x s0=a",
"s0=b s2=c",
"s1=x s0=a s2=c" },
350 {
"= twice, absent",
"",
"s0=a s0=b",
"s0=a s0=b" },
364 {
":= into empty",
"",
"s0:=c",
"s0=c" },
365 {
":= absent attribute",
"s1=x",
"s0:=c",
"s1=x s0=c" },
366 {
":= replaces in place",
"s1=x s0=a i0=1",
"s0:=b",
"s1=x s0=b i0=1" },
367 {
":= replaces first only",
"s0=a s0=b",
"s0:=c",
"s0=c s0=b" },
374 {
":= twice, one instance",
"s0=a",
"s0:=b s0:=c",
"s0=b s0=c" },
375 {
":= twice, two instances",
"s0=a s0=x",
"s0:=b s0:=c",
"s0=b s0=c" },
376 {
":= thrice, two instances",
"s0=a s0=x",
"s0:=b s0:=c s0:=d",
"s0=b s0=c s0=d" },
377 {
":= integer",
"i0=1",
"i0:=2",
"i0=2" },
389 {
"+= into empty",
"",
"s0+=a",
"s0=a" },
390 {
"+= present attribute",
"s0=a",
"s0+=b",
"s0=a s0=b" },
391 {
"+= same value",
"s0=a",
"s0+=a",
"s0=a s0=a" },
392 {
"+= keeps from order",
"s0=a",
"s1+=x s2+=y s1+=z",
"s0=a s1=x s2=y s1=z" },
393 {
"+= integer",
"i0=1",
"i0+=2",
"i0=1 i0=2" },
410 {
"^= into empty",
"",
"s0^=a",
"s0=a" },
411 {
"^= one attribute",
"s0=a",
"s1^=x",
"s1=x s0=a" },
412 {
"^= present attribute",
"s0=a",
"s0^=b",
"s0=b s0=a" },
413 {
"^= two attributes",
"s0=a",
"s1^=x s2^=y",
"s2=y s1=x s0=a" },
414 {
"^= three attributes",
"",
"s0^=a s1^=b s2^=c",
"s2=c s1=b s0=a" },
415 {
"^= and +=",
"s0=a",
"s1+=x s2^=y",
"s2=y s0=a s1=x" },
429 {
"-= from empty",
"",
"s0-=a",
"" },
430 {
"-= absent attribute",
"s1=x",
"s0-=a",
"s1=x" },
431 {
"-= no matching value",
"s0=a",
"s0-=b",
"s0=a" },
432 {
"-= matching value",
"s0=a",
"s0-=a",
"" },
433 {
"-= all matching values",
"s0=a s0=b s0=a",
"s0-=a",
"s0=b" },
434 {
"-= keeps others",
"s1=x s0=a i0=1",
"s0-=a",
"s1=x i0=1" },
435 {
"-= integer",
"i0=1 i0=2",
"i0-=2",
"i0=1" },
436 {
"-= twice",
"s0=a s0=b s0=c",
"s0-=a s0-=c",
"s0=b" },
449 {
"!* from empty",
"",
"s0!*",
"" },
450 {
"!* absent attribute",
"s1=x",
"s0!*",
"s1=x" },
451 {
"!* one instance",
"s0=a",
"s0!*",
"" },
452 {
"!* all instances",
"s0=a s1=b s0=c",
"s0!*",
"s1=b" },
453 {
"!* integer",
"i0=1 i0=2",
"i0!*",
"" },
467 {
"== from empty",
"",
"s0==a",
"" },
468 {
"== absent attribute",
"s1=x",
"s0==a",
"s1=x" },
469 {
"== matching value",
"s0=a",
"s0==a",
"s0=a" },
470 {
"== no matching value",
"s0=a",
"s0==b",
"" },
471 {
"== some matching",
"s0=a s0=b s0=a",
"s0==a",
"s0=a s0=a" },
472 {
"== keeps others",
"s1=x s0=b",
"s0==a",
"s1=x" },
487 {
"<= larger value",
"i0=10",
"i0<=5",
"i0=5" },
494 {
"<= equal value",
"i0=5",
"i0<=5",
"i0=5" },
495 {
"<= smaller value",
"i0=3",
"i0<=5",
"i0=3" },
496 {
"<= absent attribute",
"i1=1",
"i0<=5",
"i1=1 i0=5" },
497 {
"<= into empty",
"",
"i0<=5",
"i0=5" },
498 {
"<= replaces in place",
"i1=1 i0=10 s0=a",
"i0<=5",
"i1=1 i0=5 s0=a" },
499 {
"<= all instances",
"i0=10 i0=20",
"i0<=5",
"i0=5 i0=20" },
501 {
">= smaller value",
"i0=3",
"i0>=5",
"i0=5" },
502 {
">= equal value",
"i0=5",
"i0>=5",
"i0=5" },
503 {
">= larger value",
"i0=9",
"i0>=5",
"i0=9" },
504 {
">= absent attribute",
"i1=1",
"i0>=5",
"i1=1 i0=5" },
505 {
">= into empty",
"",
"i0>=5",
"i0=5" },
506 {
">= replaces in place",
"i1=1 i0=3 s0=a",
"i0>=5",
"i1=1 i0=5 s0=a" },
527 {
"<= ipv4 prefix, inside",
"p4=10.0.0.0/8",
"p4<=10.1.0.0/16",
"p4=10.0.0.0/8" },
528 {
"<= ipv4 prefix, equal",
"p4=10.0.0.0/8",
"p4<=10.0.0.0/8",
"p4=10.0.0.0/8" },
529 {
"<= ipv4 prefix, contains",
"p4=10.0.0.0/8",
"p4<=0.0.0.0/0",
"p4=10.0.0.0/8" },
530 {
"<= ipv4 prefix, disjoint",
"p4=10.0.0.0/8",
"p4<=192.168.0.0/16",
"p4=10.0.0.0/8" },
531 {
">= ipv4 prefix, inside",
"p4=10.0.0.0/8",
"p4>=10.1.0.0/16",
"p4=10.1.0.0/16" },
532 {
">= ipv4 prefix, equal",
"p4=10.0.0.0/8",
"p4>=10.0.0.0/8",
"p4=10.0.0.0/8" },
533 {
">= ipv4 prefix, contains",
"p4=10.0.0.0/8",
"p4>=0.0.0.0/0",
"p4=0.0.0.0/0" },
534 {
">= ipv4 prefix, disjoint",
"p4=10.0.0.0/8",
"p4>=192.168.0.0/16",
"p4=192.168.0.0/16" },
539 {
"<= ipv6 prefix, inside",
"p6=2001:db8::/32",
"p6<=2001:db8:1::/48",
"p6=2001:db8::/32" },
540 {
"<= ipv6 prefix, equal",
"p6=2001:db8::/32",
"p6<=2001:db8::/32",
"p6=2001:db8::/32" },
541 {
"<= ipv6 prefix, contains",
"p6=2001:db8::/32",
"p6<=::/0",
"p6=2001:db8::/32" },
542 {
"<= ipv6 prefix, disjoint",
"p6=2001:db8::/32",
"p6<=fe80::/10",
"p6=2001:db8::/32" },
543 {
">= ipv6 prefix, inside",
"p6=2001:db8::/32",
"p6>=2001:db8:1::/48",
"p6=2001:db8:1::/48" },
544 {
">= ipv6 prefix, equal",
"p6=2001:db8::/32",
"p6>=2001:db8::/32",
"p6=2001:db8::/32" },
545 {
">= ipv6 prefix, contains",
"p6=2001:db8::/32",
"p6>=::/0",
"p6=::/0" },
546 {
">= ipv6 prefix, disjoint",
"p6=2001:db8::/32",
"p6>=fe80::/10",
"p6=fe80::/10" },
551 {
"<= prefix, absent",
"",
"p4<=10.0.0.0/8",
"p4=10.0.0.0/8" },
552 {
">= prefix, absent",
"",
"p6>=2001:db8::/32",
"p6=2001:db8::/32" },
565 {
"+= then :=",
"s0=a",
"s0+=x s0:=y",
"s0=y s0=x" },
566 {
"+= then -=",
"s0=a",
"s0+=a s0-=a",
"s0=a" },
567 {
"+= then !*",
"s0=a",
"s0+=b s0!*",
"s0=b" },
568 {
":= then -=",
"s0=a",
"s0:=b s0-=b",
"s0=b" },
569 {
"^= then !*",
"s0=a",
"s0^=b s0!*",
"s0=b" },
584 {
":= then =",
"s0=a",
"s0:=b s0=c",
"s0=b s0=c" },
585 {
"-= then =",
"s0=a",
"s0-=a s0=b",
"s0=b" },
586 {
"!* then =",
"s0=a s0=x",
"s0!* s0=b",
"s0=b" },
587 {
"<= then =",
"i0=10",
"i0<=5 i0=7",
"i0=5 i0=7" },
588 {
">= then =",
"i0=1",
"i0>=5 i0=7",
"i0=5 i0=7" },
593 {
":= then =, two instances",
"s0=a s0=x",
"s0:=b s0=c",
"s0=b s0=x" },
594 {
"-= then =, two instances",
"s0=a s0=x",
"s0-=a s0=c",
"s0=x" },
599 {
"other attribute then =",
"s0=a s1=x",
"s1:=y s0=c",
"s0=a s1=y" },
619 {
"+= then -=",
"s0=a",
"s1+=x s0-=a",
"s1=x" },
620 {
"+= then !*",
"s0=a s1=b",
"i0+=1 s0!*",
"s1=b i0=1" },
621 {
"+= then == (no match)",
"s0=a",
"s1+=x s0==b",
"s1=x" },
622 {
"+= then = (present)",
"s0=a",
"s1+=x s0=b",
"s0=a s1=x" },
623 {
"+= then <= (kept)",
"i0=3",
"s1+=x i0<=5",
"i0=3 s1=x" },
624 {
"+= then >= (kept)",
"i0=9",
"s1+=x i0>=5",
"i0=9 s1=x" },
625 {
"+= then two freed",
"s0=a s2=c",
"s1+=x s0-=a s2!*",
"s1=x" },
626 {
"two += then freed",
"s0=a",
"s1+=x s2+=y s0-=a",
"s1=x s2=y" },
631 {
"+= then :=",
"s0=a",
"s1+=x s0:=y",
"s0=y s1=x" },
632 {
"+= then <= (replaced)",
"i0=10",
"s1+=x i0<=5",
"i0=5 s1=x" },
633 {
"+= then >= (replaced)",
"i0=1",
"s1+=x i0>=5",
"i0=5 s1=x" },
638 {
"+= then ^=",
"s0=a",
"s1+=x s2^=y",
"s2=y s0=a s1=x" },
639 {
"^= then -=",
"s0=a s2=z",
"s1^=x s0-=a",
"s1=x s2=z" },
644 {
":= then -=",
"s0=a s2=z",
"s0:=b s1-=q",
"s0=b s2=z" },
645 {
":= then !*",
"s0=a s2=z",
"s0:=b s2!*",
"s0=b" },
650 {
"= then +=",
"",
"s0=a s1+=b",
"s0=a s1=b" },
651 {
":= (absent) then -=",
"",
"s0:=a s1-=b",
"s0=a" },
665 "s0=a s1=b s2=c i0=1 i1=2",
666 "s0:=A s1-=b s2!* i0<=0 i1+=3 un=bob",
667 "s0=A i0=0 i1=2 i1=3 un=bob" },
668 {
"different attributes",
671 "s0=a i0=1 s1=b i1=2 s2=c" },
672 {
"prepend, replace, append",
675 "i0=1 s0=a s1=B i1=2" },
693 }
const sizes[] = { { 1, 100 }, { 100, 1 }, { 100, 100 }, { 0, 100 }, { 100, 0 } };
709 before = talloc_total_blocks(
request);
713 for (j = 0; j < sizes[i].to_num; j++) {
719 for (j = 0; j < sizes[i].from_num; j++) {
727 TEST_CASE_(
"long lists %zu and %zu: %s", sizes[i].to_num, sizes[i].from_num,
730 TEST_MSG(
"Expected length : %zu", sizes[i].to_num + sizes[i].from_num);
761 {
"= (empty to)",
"",
"s0=a",
"s0=a" },
762 {
":= (empty to)",
"",
"s0:=a",
"s0=a" },
763 {
"+= (empty to)",
"",
"s0+=a",
"s0=a" },
764 {
"^= (empty to)",
"",
"s0^=a",
"s0=a" },
765 {
"-= (empty to)",
"",
"s0-=a",
"" },
766 {
"!* (empty to)",
"",
"s0!*",
"" },
767 {
"== (empty to)",
"",
"s0==a",
"" },
768 {
"<= (empty to)",
"",
"i0<=5",
"i0=5" },
769 {
">= (empty to)",
"",
"i0>=5",
"i0=5" },
770 {
"<= prefix (empty to)",
"",
"p4<=10.0.0.0/8",
"p4=10.0.0.0/8" },
771 {
">= prefix (empty to)",
"",
"p6>=2001:db8::/32",
"p6=2001:db8::/32" },
777 {
"= twice",
"",
"s0=a s0=b",
"s0=a s0=b" },
778 {
":= twice",
"",
"s0:=a s0:=b",
"s0=a s0=b" },
779 {
"+= twice",
"",
"s0+=a s0+=b",
"s0=a s0=b" },
780 {
"^= twice",
"",
"s0^=a s0^=b",
"s0=b s0=a" },
781 {
"-= twice",
"",
"s0-=a s0-=b",
"" },
782 {
"!* twice",
"",
"s0!* s0!*",
"" },
783 {
"== twice",
"",
"s0==a s0==b",
"" },
784 {
"<= twice",
"",
"i0<=5 i0<=3",
"i0=5 i0=3" },
785 {
">= twice",
"",
"i0>=5 i0>=7",
"i0=5 i0=7" },
790 {
"= then :=",
"",
"s0=a s0:=b",
"s0=a s0=b" },
791 {
":= then =",
"",
"s0:=a s0=b",
"s0=a s0=b" },
792 {
"+= then -=",
"",
"s0+=a s0-=a",
"s0=a" },
793 {
"+= then !*",
"",
"s0+=a s0!*",
"s0=a" },
794 {
"+= then ==",
"",
"s0+=a s0==b",
"s0=a" },
795 {
"^= then !*",
"",
"s0^=a s0!*",
"s0=a" },
800 {
"deletes then add",
"",
"s0-=a s1!* s2==c i0=1",
"i0=1" },
801 {
"add then deletes",
"",
"i0=1 s0-=a s1!* s2==c",
"i0=1" },
802 {
"add between deletes",
"",
"s0-=a i0=1 s1!*",
"i0=1" },
807 {
"different attributes",
"",
"s0=a i0:=1 s1+=b",
"s0=a i0=1 s1=b" },
808 {
"prepend and append",
"",
"s0=a s1^=b s2+=c",
"s1=b s0=a s2=c" },
811 "s0=a i0:=1 s1+=b s2^=c i1<=4 s0-=x s1!* un=bob",
812 "s2=c s0=a i0=1 s1=b i1=4 un=bob" },
static int const char char buffer[256]
strcpy(log_entry->msg, buffer)
#define TEST_ASSERT(cond)
#define TEST_CHECK_STRCMP(_got, _exp)
static uint8_t request[4]
#define fr_exit_now(_x)
Exit without calling atexit() handlers, producing a log message in debug builds.
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.
fr_dict_test_attr_number_t attr
Attribute number to create.
fr_dict_test_attr_number_t
Test attribute numbers.
Test enumeration attributes.
fr_packet_t * fr_packet_alloc(TALLOC_CTX *ctx, bool new_vector)
Allocate a new fr_packet_t.
@ FR_TYPE_IPV6_PREFIX
IPv6 Prefix.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_IPV4_PREFIX
IPv4 Prefix.
int fr_pair_append_by_da(TALLOC_CTX *ctx, fr_pair_t **out, fr_pair_list_t *list, fr_dict_attr_t const *da)
Alloc a new fr_pair_t (and append)
int fr_pair_value_from_str(fr_pair_t *vp, char const *value, size_t inlen, fr_sbuff_unescape_rules_t const *uerules, UNUSED bool tainted)
Convert string value to native attribute value.
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
void radius_pairmove(request_t *request, fr_pair_list_t *to, fr_pair_list_t *from)
Merge the "from" list into the "to" list, using the operator of each pair.
static void pairmove_run_cases(pairmove_case_t const *cases, size_t num)
static void test_op_cmp_eq(void)
static void test_stale_next(void)
static void test_op_le_ge_prefix(void)
static void test_op_le_ge(void)
static TALLOC_CTX * autofree
static void test_empty_to(void)
static request_t * request_make(void)
Make a request with empty pair lists.
static void test_original_list_only(void)
static char const * list_target_names[LIST_NUM]
static void test_op_cmp_false(void)
static fr_dict_test_attr_t const test_attrs[]
static void test_op_set(void)
static void test_op_eq(void)
static void test_op_prepend(void)
static void test_long_lists(void)
Merge lists of up to 100 pairs.
static void list_alloc(TALLOC_CTX *ctx, fr_pair_list_t *list, char const *spec)
Build a list of pairs from the text in spec
static void test_op_add(void)
static fr_dict_t * test_dict
static char const * list_str(fr_pair_list_t const *list)
Render a list in the text form that list_alloc() accepts.
char const * expect
Expected text form of the to list after the merge.
static void test_op_sub(void)
static void test_op_eq_after_edit(void)
static void test_mixed(void)
static struct @89 op_names[]
char const * from
Text form of the from list.
static void test_init(void)
Global initialisation.
static struct @88 attr_names[]
static void pairmove_run(pairmove_case_t const *tc, list_target_t target)
char const * to
Text form of the to list before the merge.
static fr_pair_list_t * list_target(TALLOC_CTX **ctx, request_t *request, list_target_t target)
Get the pair list and the talloc context for a target.
int request_global_init(void)
#define request_local_alloc_external(_ctx, _args)
Allocate a new external request outside of the request pool.
Optional arguments for initialising requests.
ssize_t fr_sbuff_in_strcpy(fr_sbuff_t *sbuff, char const *str)
Copy bytes into the sbuff up to the first \0.
#define FR_SBUFF_OUT(_start, _len_or_end)
#define fr_sbuff_used(_sbuff_or_marker)
#define fr_sbuff_in_char(_sbuff,...)
fr_aka_sim_id_type_t type
Stores an attribute, a value and various bits of other data.
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
#define talloc_autofree_context
The original function is deprecated, so replace it with our version.
char const * fr_tokens[T_TOKEN_LAST]
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
#define fr_pair_list_foreach(_list_head, _iter)
Iterate over the contents of a fr_pair_list_t.
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
size_t fr_pair_list_num_elements(fr_pair_list_t const *list)
Get the length of a list of fr_pair_t.
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
int fr_check_lib_magic(uint64_t magic)
Check if the application linking to the library has the correct magic number.
#define RADIUSD_MAGIC_NUMBER
ssize_t fr_value_box_print(fr_sbuff_t *out, fr_value_box_t const *data, fr_sbuff_escape_rules_t const *e_rules)
Print one boxed value to a string.
static fr_sbuff_err_t char ** out
static fr_sbuff_err_t char size_t * len