summaryrefslogtreecommitdiffstats
path: root/src/tests/keywords/if
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/tests/keywords/if10
-rw-r--r--src/tests/keywords/if-bob15
-rw-r--r--src/tests/keywords/if-else15
-rw-r--r--src/tests/keywords/if-elsif19
-rw-r--r--src/tests/keywords/if-multivalue173
-rw-r--r--src/tests/keywords/if-paircmp27
-rw-r--r--src/tests/keywords/if-rcode-error11
-rw-r--r--src/tests/keywords/if-regex-bad-attribute21
-rw-r--r--src/tests/keywords/if-regex-error12
-rw-r--r--src/tests/keywords/if-regex-match183
-rw-r--r--src/tests/keywords/if-regex-match-comp149
-rw-r--r--src/tests/keywords/if-regex-match-comp.attrs7
-rw-r--r--src/tests/keywords/if-regex-match-named117
-rw-r--r--src/tests/keywords/if-regex-match-named.attrs6
-rw-r--r--src/tests/keywords/if-regex-match.attrs7
-rw-r--r--src/tests/keywords/if-regex-multivalue26
-rw-r--r--src/tests/keywords/if-skip42
17 files changed, 840 insertions, 0 deletions
diff --git a/src/tests/keywords/if b/src/tests/keywords/if
new file mode 100644
index 0000000..a146029
--- /dev/null
+++ b/src/tests/keywords/if
@@ -0,0 +1,10 @@
+#
+# PRE: update
+#
+# Static if condition
+#
+if (1) {
+ update reply {
+ Filter-Id := "filter"
+ }
+}
diff --git a/src/tests/keywords/if-bob b/src/tests/keywords/if-bob
new file mode 100644
index 0000000..4e8ae3c
--- /dev/null
+++ b/src/tests/keywords/if-bob
@@ -0,0 +1,15 @@
+# PRE: if
+#
+# Matching "if" conditions
+#
+if (User-Name == "bob") {
+ update reply {
+ Filter-Id := "filter"
+ }
+}
+
+if (User-Name != "bob") {
+ update reply {
+ Filter-Id := "not bob"
+ }
+} \ No newline at end of file
diff --git a/src/tests/keywords/if-else b/src/tests/keywords/if-else
new file mode 100644
index 0000000..788d606
--- /dev/null
+++ b/src/tests/keywords/if-else
@@ -0,0 +1,15 @@
+#
+# PRE: if
+#
+# Matching "if" conditions
+#
+if (User-Name != "bob") {
+ update reply {
+ Filter-Id := "not bob"
+ }
+}
+else {
+ update reply {
+ Filter-Id := "filter"
+ }
+}
diff --git a/src/tests/keywords/if-elsif b/src/tests/keywords/if-elsif
new file mode 100644
index 0000000..c0a41ed
--- /dev/null
+++ b/src/tests/keywords/if-elsif
@@ -0,0 +1,19 @@
+# PRE: if if-else
+#
+# Matching "if" conditions
+#
+if (User-Name != "bob") {
+ update reply {
+ Filter-Id := "not bob"
+ }
+}
+elsif (User-Name == "bob") {
+ update reply {
+ Filter-Id := "filter"
+ }
+}
+else {
+ update reply {
+ Filter-Id := "last else"
+ }
+}
diff --git a/src/tests/keywords/if-multivalue b/src/tests/keywords/if-multivalue
new file mode 100644
index 0000000..f12d6fe
--- /dev/null
+++ b/src/tests/keywords/if-multivalue
@@ -0,0 +1,173 @@
+#
+# PRE: update if
+#
+update {
+ control:Cleartext-Password := 'hello'
+ reply:Filter-Id := 'filter'
+}
+
+update request {
+ Tmp-String-0 := 'foo'
+ Tmp-String-0 += 'bar'
+ Tmp-String-0 += 'baz'
+
+ Tmp-String-1 := 'GROUP ADMINISTRATORS'
+ Tmp-String-1 += 'GROUP STUDENTS'
+ Tmp-String-1 += 'GROUP PEONS'
+
+ Tmp-String-2 := 'PEONS'
+ Tmp-String-2 += 'STUDENTS'
+ Tmp-String-2 += 'ADMINISTRATORS'
+
+ Tmp-String-3 := 'no'
+ Tmp-String-3 += 'no'
+ Tmp-String-3 += 'yes'
+
+ Tmp-Integer-0 := 1
+ Tmp-Integer-0 += 2
+ Tmp-Integer-0 += 5
+}
+
+update control {
+ Tmp-String-0 := 'foo'
+ Tmp-String-0 += 'bar'
+ Tmp-String-0 += 'baz'
+
+ Tmp-String-1 := 'boink'
+ Tmp-String-1 += 'tard'
+ Tmp-String-1 += 'dink'
+ Tmp-String-1 += 'slink'
+
+ Tmp-Integer-0 := 01
+ Tmp-Integer-0 += 02
+ Tmp-Integer-0 += 05
+ Tmp-Integer-0 += 04
+
+ Tmp-Integer-1 := 10
+ Tmp-Integer-1 += 20
+ Tmp-Integer-1 += 30
+}
+
+#
+# Mmmm O(N^2)
+#
+if (&request:Tmp-String-0[*] != &control:Tmp-String-0[*]) {
+ update reply {
+ Filter-Id += 'fail 0'
+ }
+}
+
+if (&request:Tmp-String-0[*] == &control:Tmp-String-1[*]) {
+ update reply {
+ Filter-Id += 'fail 1'
+ }
+}
+
+if (&request:Tmp-String-1[*] == &control:Tmp-String-0[*]) {
+ update reply {
+ Filter-Id += 'fail 2'
+ }
+}
+
+#
+# Integer comparison and normalisation
+#
+if (&request:Tmp-Integer-0 != &control:Tmp-Integer-0) {
+ update reply {
+ Filter-Id += 'fail 3'
+ }
+}
+
+#
+# if any value of request:Tmp-Integer-0 > any value of
+# request:Tmp-Integer-1 then evaluate to true
+#
+if (&request:Tmp-Integer-0[*] > &control:Tmp-Integer-1[*]) {
+ update reply {
+ Filter-Id += 'fail 4'
+ }
+}
+
+#
+# Compiled regex comparisons
+#
+if (&request:Tmp-String-1[*] !~ /PEONS$/) {
+ update reply {
+ Filter-Id += 'fail 5'
+ }
+}
+
+if (&control:Tmp-String-1 =~ /PEONS$/) {
+ update reply {
+ Filter-Id += 'fail 6'
+ }
+}
+
+if (&control:Tmp-String-1 =~ /DINKS$/) {
+ update reply {
+ Filter-Id += 'fail 7'
+ }
+}
+
+#
+# Dynamic regex comparisons
+#
+if (&request:Tmp-String-1[*] !~ /%{Tmp-String-2[0]}$/) {
+ update reply {
+ Filter-Id += 'fail 8'
+ }
+}
+
+if (&request:Tmp-String-1 =~ /%{Tmp-String-2[1]}$/) {
+ update reply {
+ Filter-Id += 'fail 9'
+ }
+}
+
+if (&request:Tmp-String-1 !~ /%{Tmp-String-2[2]}$/) {
+ update reply {
+ Filter-Id += 'fail 10'
+ }
+}
+
+if (&request:Tmp-String-1 =~ /%{Tmp-String-2[#]}$/) {
+ update reply {
+ Filter-Id += 'fail 11'
+ }
+}
+
+#
+# XLAT virtual comparisons
+#
+if (&control:Tmp-Integer-0[*] != "%{control:Tmp-Integer-0[#]}") {
+ update reply {
+ Filter-Id += 'fail 12'
+ }
+}
+
+#
+# Literal comparisons
+#
+if (&control:Tmp-String-1[*] != 'boink') {
+ update reply {
+ Filter-Id += 'fail 13'
+ }
+}
+
+if (&control:Tmp-String-1[*] == 'foo') {
+ update reply {
+ Filter-Id += 'fail 14'
+ }
+}
+
+if (&request:Tmp-Integer-0[*] > 10) {
+ update reply {
+ Filter-Id += 'fail 15'
+ }
+}
+
+if (!(&request:Tmp-Integer-0[*] < 10)) {
+ update reply {
+ Filter-Id += 'fail 16'
+ }
+}
diff --git a/src/tests/keywords/if-paircmp b/src/tests/keywords/if-paircmp
new file mode 100644
index 0000000..6ed06e3
--- /dev/null
+++ b/src/tests/keywords/if-paircmp
@@ -0,0 +1,27 @@
+#
+# PRE: update if
+#
+update {
+ control:Cleartext-Password := 'hello'
+ reply:Filter-Id := 'filter'
+}
+
+#
+# Paircmp
+#
+
+#
+# Passing 'yes' causes the test paircmp to return match
+# Passing 'no' causes the test paircmp to return a non-match
+#
+if (&Test-Paircmp != 'yes') {
+ update reply {
+ Filter-Id += 'fail 1'
+ }
+}
+
+if (&Test-Paircmp == 'no') {
+ update reply {
+ Filter-Id += 'fail 2'
+ }
+}
diff --git a/src/tests/keywords/if-rcode-error b/src/tests/keywords/if-rcode-error
new file mode 100644
index 0000000..fed8a49
--- /dev/null
+++ b/src/tests/keywords/if-rcode-error
@@ -0,0 +1,11 @@
+# PRE: if
+#
+# return code in an "if" section.
+#
+if (User-Name == "bob") {
+ update reply {
+ Filter-Id := "filter"
+ }
+
+ ok = reject # ERROR
+}
diff --git a/src/tests/keywords/if-regex-bad-attribute b/src/tests/keywords/if-regex-bad-attribute
new file mode 100644
index 0000000..f330fde
--- /dev/null
+++ b/src/tests/keywords/if-regex-bad-attribute
@@ -0,0 +1,21 @@
+#
+# PRE: if-regex-match if-regex-error
+#
+
+#
+# This should parse
+#
+if (&User-Name =~ /%{User-Name}/) {
+ update reply {
+ Filter-Id := "filter"
+ }
+}
+
+#
+# Check regexes which refer to unknown attributes
+#
+if (&User-Name =~ /%{What-The-Heck-Is-This-Thing}/) { # ERROR
+ update reply {
+ Filter-Id := "filter"
+ }
+}
diff --git a/src/tests/keywords/if-regex-error b/src/tests/keywords/if-regex-error
new file mode 100644
index 0000000..f618e82
--- /dev/null
+++ b/src/tests/keywords/if-regex-error
@@ -0,0 +1,12 @@
+#
+# PRE: if-regex-match
+#
+
+#
+# Check that bad regular expressions will fail
+#
+if (&User-Name =~ /[a-3]/) { # ERROR
+ update reply {
+ Filter-Id := "filter"
+ }
+}
diff --git a/src/tests/keywords/if-regex-match b/src/tests/keywords/if-regex-match
new file mode 100644
index 0000000..458e455
--- /dev/null
+++ b/src/tests/keywords/if-regex-match
@@ -0,0 +1,183 @@
+# PRE: if
+#
+update request {
+ Tmp-Integer-0 := '123456789'
+}
+
+# Non matching on attribute ref
+if (User-Name !~ /^([0-9])_([0-9])?_([0-9]*)_([0-9]+)_([^_])_(6)_([7-8])%{Tmp-String-0}/) {
+ update reply {
+ Filter-Id += 'Fail 0'
+ }
+}
+
+# Matching on xlat expanded value
+if ("%{User-Name}" !~ /^([0-9])_([0-9])?_([0-9]*)_([0-9]+)_([^_])_(6)_([7-8])%{Tmp-String-0}/) {
+ update reply {
+ Filter-Id += 'Fail 1'
+ }
+}
+
+# Matching on attribute ref with capture groups
+if (User-Name =~ /^([0-9])_([0-9])?_([0-9]*)_([0-9]+)_([^_])_(6)_([7-8])%{Tmp-String-0}/) {
+ # Test all the capture groups
+ update {
+ reply:User-Name := "%{7}_%{6}_%{5}_%{4}_%{3}_%{2}_%{1}_%{0}"
+ }
+}
+else {
+ update reply {
+ Filter-Id += 'Fail 2'
+ }
+}
+
+# Checking capture groups are cleared out correctly
+if (User-Name =~ /^([0-9])_%{Tmp-String-0}/) {
+ if ("%{0}%{1}%{2}%{3}%{4}%{5}%{6}%{7}" != '1_1') {
+ update reply {
+ Filter-Id += 'Fail 3'
+ }
+ }
+}
+else {
+ update reply {
+ Filter-Id += 'Fail 3.5'
+ }
+}
+
+# Checking capture groups are cleared out correctly when there are no matches
+if (User-Name =~ /^.%{Tmp-String-0}/) {
+ if ("%{0}%{1}%{2}%{3}%{4}%{5}%{6}%{7}" != '1') {
+ update reply {
+ Filter-Id += 'Fail 4'
+ }
+ }
+}
+else {
+ update reply {
+ Filter-Id += 'Fail 4.5'
+ }
+}
+
+# Checking full capture group range
+if ('a_b_c_d_e_f_g_h_i_j_k_l_m_n_o_p_q_r_s_t_u_v_w_x_y_z_A_B_C_D_E_F' =~ /^(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)$/) {
+ if ("%{0}%{1}%{2}%{3}%{4}%{5}%{6}%{7}%{8}%{9}%{10}%{11}%{12}%{13}%{14}%{15}%{16}%{17}%{18}%{19}%{20}%{21}%{22}%{23}%{24}%{25}%{26}%{27}%{28}%{29}%{30}%{31}%{32}" != 'a_b_c_d_e_f_g_h_i_j_k_l_m_n_o_p_q_r_s_t_u_v_w_x_y_z_A_B_C_D_E_FabcdefghijklmnopqrstuvwxyzABCDEF') {
+ update reply {
+ Filter-Id += 'Fail 6'
+ }
+ }
+}
+else {
+ update reply {
+ Filter-Id += 'Fail 6.5'
+ }
+}
+
+# Checking full capture group overun
+if ('a_b_c_d_e_f_g_h_i_j_k_l_m_n_o_p_q_r_s_t_u_v_w_x_y_z_A_B_C_D_E_F_G' =~ /^(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)_(.)$/) {
+ if ("%{0}%{1}%{2}%{3}%{4}%{5}%{6}%{7}%{8}%{9}%{10}%{11}%{12}%{13}%{14}%{15}%{16}%{17}%{18}%{19}%{20}%{21}%{22}%{23}%{24}%{25}%{26}%{27}%{28}%{29}%{30}%{31}%{32}" != 'a_b_c_d_e_f_g_h_i_j_k_l_m_n_o_p_q_r_s_t_u_v_w_x_y_z_A_B_C_D_E_F_GabcdefghijklmnopqrstuvwxyzABCDEF') {
+ update reply {
+ Filter-Id += 'Fail 7'
+ }
+ }
+}
+else {
+ update reply {
+ Filter-Id += 'Fail 7.5'
+ }
+}
+
+# uncompiled - ref - insensitive
+if (Calling-Station-Id !~ /:roamyroam%{Tmp-String-0}$/i) {
+ update reply {
+ Filter-Id += 'Fail 8'
+ }
+}
+
+# uncompiled - expansion - insensitive
+if ("%{Calling-Station-Id}" !~ /:roamyroam%{Tmp-String-0}$/i) {
+ update reply {
+ Filter-Id += 'Fail 9'
+ }
+}
+
+# uncompiled - enum - ref - insensitive
+if (Service-Type !~ /^framed-user%{Tmp-String-0}$/i) {
+ update reply {
+ Filter-Id += 'Fail 10'
+ }
+}
+
+# uncompiled - enum - expansion - insensitive
+if ("%{Service-Type}" !~ /^framed-user%{Tmp-String-0}$/i) {
+ update reply {
+ Filter-Id += 'Fail 11'
+ }
+}
+
+# uncompiled - enum - ref
+if (Service-Type =~ /^framed-user%{Tmp-String-0}$/) {
+ update reply {
+ Filter-Id += 'Fail 12'
+ }
+}
+
+# uncompiled - integer - ref
+if (Tmp-Integer-0 !~ /%{Tmp-Integer-0}/) {
+ update reply {
+ Filter-Id += 'Fail 13'
+ }
+}
+
+update request {
+ Tmp-String-0 := "foo\nbar"
+}
+
+# uncompiled - ref - multiline
+if (&Tmp-String-0 !~ /^foo$%{Tmp-String-8}/m) {
+ update reply {
+ Filter-Id += 'Fail 14'
+ }
+}
+
+# uncompiled - ref - non-multiline
+if (&Tmp-String-0 =~ /^foo$%{Tmp-String-8}/) {
+ update reply {
+ Filter-Id += 'Fail 15'
+ }
+}
+
+# uncompiled - ref - non-multiline
+if (&Tmp-String-0 !~ /^foo\nbar%{Tmp-String-8}$/) {
+ update reply {
+ Filter-Id += 'Fail 16'
+ }
+}
+
+# uncompiled - ref - multiline
+if (&Tmp-String-0 !~ /^bar%{Tmp-String-8}$/m) {
+ update reply {
+ Filter-Id += 'Fail 17'
+ }
+}
+
+# uncompiled - ref - multiline - sensitive
+if (&Tmp-String-0 =~ /^BAR%{Tmp-String-8}$/m) {
+ update reply {
+ Filter-Id += 'Fail 18'
+ }
+}
+
+# uncompiled - ref - multiline - insensitive
+if (&Tmp-String-0 !~ /^BAR%{Tmp-String-8}$/mi) {
+ update reply {
+ Filter-Id += 'Fail 19'
+ }
+}
+
+# uncompiled - ref - multiline - insensitive (flag order reversed)
+if (&Tmp-String-0 !~ /^BAR%{Tmp-String-8}$/im) {
+ update reply {
+ Filter-Id += 'Fail 20'
+ }
+}
diff --git a/src/tests/keywords/if-regex-match-comp b/src/tests/keywords/if-regex-match-comp
new file mode 100644
index 0000000..c9c2d15
--- /dev/null
+++ b/src/tests/keywords/if-regex-match-comp
@@ -0,0 +1,149 @@
+# PRE: if
+#
+
+# Non matching on attribute ref
+if (User-Name !~ /^([0-9])_([0-9])?_([0-9]*)_([0-9]+)_([^_])_(6)_([7-8])/) {
+ update reply {
+ Filter-Id += 'Fail 0'
+ }
+}
+
+# Matching on xlat expanded value
+if ("%{User-Name}" !~ /^([0-9])_([0-9])?_([0-9]*)_([0-9]+)_([^_])_(6)_([7-8])/) {
+ update reply {
+ Filter-Id += 'Fail 1'
+ }
+}
+
+# Matching on attribute ref with capture groups
+if (User-Name =~ /^([0-9])_([0-9])?_([0-9]*)_([0-9]+)_([^_])_(6)_([7-8])/) {
+ # Test all the capture groups
+ update {
+ reply:User-Name := "%{7}_%{6}_%{5}_%{4}_%{3}_%{2}_%{1}_%{0}"
+ }
+}
+else {
+ update reply {
+ Filter-Id += 'Fail 2'
+ }
+}
+
+# Checking capture groups are cleared out correctly
+if (User-Name =~ /^([0-9])_/) {
+ if ("%{0}%{1}%{2}%{3}%{4}%{5}%{6}%{7}" != '1_1') {
+ update reply {
+ Filter-Id += 'Fail 3'
+ }
+ }
+}
+else {
+ update reply {
+ Filter-Id += 'Fail 3.5'
+ }
+}
+
+# Checking capture groups are cleared out correctly when there are no matches
+if (User-Name =~ /^./) {
+ if ("%{0}%{1}%{2}%{3}%{4}%{5}%{6}%{7}" != '1') {
+ update reply {
+ Filter-Id += 'Fail 4'
+ }
+ }
+}
+else {
+ update reply {
+ Filter-Id += 'Fail 4.5'
+ }
+}
+
+# compiled - ref - insensitive
+if (Calling-Station-Id !~ /:roamyroam$/i) {
+ update reply {
+ Filter-Id += 'Fail 5'
+ }
+}
+
+# compiled - expansion - insensitive
+if ("%{Calling-Station-Id}" !~ /:roamyroam$/i) {
+ update reply {
+ Filter-Id += 'Fail 6'
+ }
+}
+
+# compiled - enum - ref - insensitive
+if (Service-Type !~ /^framed-user$/i) {
+ update reply {
+ Filter-Id += 'Fail 7'
+ }
+}
+
+# compiled - enum - expansion - insensitive
+if ("%{Service-Type}" !~ /^framed-user$/i) {
+ update reply {
+ Filter-Id += 'Fail 8'
+ }
+}
+
+# compiled - enum - ref
+if (Service-Type =~ /^framed-user$/) {
+ update reply {
+ Filter-Id += 'Fail 9'
+ }
+}
+
+update request {
+ Tmp-String-0 := "foo\nbar"
+}
+
+# compiled - ref - multiline
+if (&Tmp-String-0 !~ /^foo$/m) {
+ update reply {
+ Filter-Id += 'Fail 14'
+ }
+}
+
+# compiled - ref - non-multiline
+if (&Tmp-String-0 =~ /^foo$/) {
+ update reply {
+ Filter-Id += 'Fail 15'
+ }
+}
+
+# compiled - ref - non-multiline
+
+# Not all POSIX implementations support the \n character classes
+# so only run this test if the server was built with libpcre.
+if (("${feature.regex-pcre}" == 'yes') && (&Tmp-String-0 !~ /^foo\nbar$/)) {
+ update reply {
+ Filter-Id += 'Fail 16'
+ }
+}
+
+# compiled - ref - multiline
+if (&Tmp-String-0 !~ /^bar$/m) {
+ update reply {
+ Filter-Id += 'Fail 17'
+ }
+}
+
+# compiled - ref - multiline - sensitive
+if (&Tmp-String-0 =~ /^BAR$/m) {
+ update reply {
+ Filter-Id += 'Fail 17'
+ }
+}
+
+# compiled - ref - multiline - insensitive
+if (&Tmp-String-0 !~ /^BAR$/mi) {
+ update reply {
+ Filter-Id += 'Fail 17'
+ }
+}
+
+# compiled - ref - multiline - insensitive (flag order reversed)
+if (&Tmp-String-0 !~ /^BAR$/im) {
+ update reply {
+ Filter-Id += 'Fail 18'
+ }
+}
+
diff --git a/src/tests/keywords/if-regex-match-comp.attrs b/src/tests/keywords/if-regex-match-comp.attrs
new file mode 100644
index 0000000..ba7188d
--- /dev/null
+++ b/src/tests/keywords/if-regex-match-comp.attrs
@@ -0,0 +1,7 @@
+User-Name = '1_2_3_4_5_6_7'
+User-Password = 'hello'
+Service-Type := 'Framed-User'
+Calling-Station-ID := '00:11:22:33:44:55:66:ROAMYROAM'
+
+Response-Packet-Type == Access-Accept
+User-Name == '7_6_5_4_3_2_1_1_2_3_4_5_6_7'
diff --git a/src/tests/keywords/if-regex-match-named b/src/tests/keywords/if-regex-match-named
new file mode 100644
index 0000000..2aa665f
--- /dev/null
+++ b/src/tests/keywords/if-regex-match-named
@@ -0,0 +1,117 @@
+# PRE: if
+#
+if ('${feature.regex-pcre}' == 'yes') {
+update request {
+ Tmp-Integer-0 := '123456789'
+ Tmp-Integer-1 := 1
+}
+
+# Check failures when no previous capture
+if ("%{regex:}" != "") {
+ update reply {
+ Filter-Id += 'Fail 0.1'
+ }
+}
+
+if ("%{regex:foo}" != "") {
+ update reply {
+ Filter-Id += 'Fail 0.2'
+ }
+}
+
+if ("%{regex:%{Tmp-Integer-1}}" != "") {
+ update reply {
+ Filter-Id += 'Fail 0.3'
+ }
+}
+
+if ("%{regex:1}" != "") {
+ update reply {
+ Filter-Id += 'Fail 0.4'
+ }
+}
+
+# uncompiled - ref - named capture groups
+if (User-Name =~ /^(?<one>[0-9])_(?<two>[0-9])?_(?<three>[0-9]*)_(?<four>[0-9]+)_(?<five>[^_])_(?<six>6)_(?<seven>[7-8])%{Tmp-String-0}/) {
+ if ("%{regex:seven}_%{regex:six}_%{regex:five}_%{regex:four}_%{regex:three}_%{regex:two}_%{regex:one}_%{0}" != '7_6_5_4_3_2_1_1_2_3_4_5_6_7') {
+ update reply {
+ Filter-Id += 'Fail 1'
+ }
+ }
+}
+else {
+ update reply {
+ Filter-Id += 'Fail 1.5'
+ }
+}
+
+# Checking capture groups are cleared out correctly
+if (User-Name =~ /^(?<one>[0-9])_%{Tmp-String-0}/) {
+ if ("%{0}%{regex:one}%{regex:two}%{regex:three}%{regex:four}%{regex:five}%{regex:six}%{regex:seven}" != '1_1') {
+ update reply {
+ Filter-Id += 'Fail 2'
+ }
+ }
+}
+else {
+ update reply {
+ Filter-Id += 'Fail 2.5'
+ }
+}
+
+# Checking capture groups are cleared out correctly when there are no matches
+if (User-Name =~ /^.%{Tmp-String-0}/) {
+ if ("%{0}%{regex:one}%{regex:two}%{regex:three}%{regex:four}%{regex:five}%{regex:six}%{regex:seven}" != '1') {
+ update reply {
+ Filter-Id += 'Fail 3'
+ }
+ }
+}
+else {
+ update reply {
+ Filter-Id += 'Fail 3.5'
+ }
+}
+
+# compiled - ref - named capture groups
+if (User-Name =~ /^(?<one>[0-9])_(?<two>[0-9])?_(?<three>[0-9]*)_(?<four>[0-9]+)_(?<five>[^_])_(?<six>6)_(?<seven>[7-8])/) {
+ if ("%{regex:seven}_%{regex:six}_%{regex:five}_%{regex:four}_%{regex:three}_%{regex:two}_%{regex:one}_%{0}" != '7_6_5_4_3_2_1_1_2_3_4_5_6_7') {
+ update reply {
+ Filter-Id += 'Fail 4'
+ }
+ }
+}
+else {
+ update reply {
+ Filter-Id += 'Fail 4.5'
+ }
+}
+
+# compiled - xlat - named capture groups
+if ('1_2_3_4_5_6_7' =~ /^(?<one>[0-9])_(?<two>[0-9])?_(?<three>[0-9]*)_(?<four>[0-9]+)_(?<five>[^_])_(?<six>6)_(?<seven>[7-8])/) {
+ if ("%{regex:seven}_%{regex:six}_%{regex:five}_%{regex:four}_%{regex:three}_%{regex:two}_%{regex:one}_%{0}" != '7_6_5_4_3_2_1_1_2_3_4_5_6_7') {
+ update reply {
+ Filter-Id += 'Fail 5'
+ }
+ }
+}
+else {
+ update reply {
+ Filter-Id += 'Fail 5.5'
+ }
+}
+
+# compiled - ref - named capture groups (numeric indexes)
+if (User-Name =~ /^(?<one>[0-9])_(?<two>[0-9])?_(?<three>[0-9]*)_(?<four>[0-9]+)_(?<five>[^_])_(?<six>6)_(?<seven>[7-8])/) {
+ if ("%{7}_%{6}_%{5}_%{4}_%{3}_%{2}_%{1}_%{0}" != '7_6_5_4_3_2_1_1_2_3_4_5_6_7') {
+ update reply {
+ Filter-Id += 'Fail 4'
+ }
+ }
+}
+else {
+ update reply {
+ Filter-Id += 'Fail 4.5'
+ }
+}
+}
diff --git a/src/tests/keywords/if-regex-match-named.attrs b/src/tests/keywords/if-regex-match-named.attrs
new file mode 100644
index 0000000..867ed23
--- /dev/null
+++ b/src/tests/keywords/if-regex-match-named.attrs
@@ -0,0 +1,6 @@
+User-Name = '1_2_3_4_5_6_7'
+User-Password = 'hello'
+Service-Type := 'Framed-User'
+Calling-Station-ID := '00:11:22:33:44:55:66:ROAMYROAM'
+
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/keywords/if-regex-match.attrs b/src/tests/keywords/if-regex-match.attrs
new file mode 100644
index 0000000..ba7188d
--- /dev/null
+++ b/src/tests/keywords/if-regex-match.attrs
@@ -0,0 +1,7 @@
+User-Name = '1_2_3_4_5_6_7'
+User-Password = 'hello'
+Service-Type := 'Framed-User'
+Calling-Station-ID := '00:11:22:33:44:55:66:ROAMYROAM'
+
+Response-Packet-Type == Access-Accept
+User-Name == '7_6_5_4_3_2_1_1_2_3_4_5_6_7'
diff --git a/src/tests/keywords/if-regex-multivalue b/src/tests/keywords/if-regex-multivalue
new file mode 100644
index 0000000..7358c93
--- /dev/null
+++ b/src/tests/keywords/if-regex-multivalue
@@ -0,0 +1,26 @@
+#
+# PRE: update if
+#
+update {
+ control:Cleartext-Password := 'hello'
+ reply:Filter-Id := 'filter'
+}
+
+update request {
+ Cisco-AVPair := 'foo=bar'
+ Cisco-AVPair += 'bar=baz'
+ Cisco-AVPair += 'baz=foo'
+}
+
+if (&Cisco-AVPair[*] =~ /bar=(.*)/) {
+ if ("%{1}" != 'baz') {
+ update reply {
+ Filter-Id += 'Fail 1'
+ }
+ }
+}
+else {
+ update reply {
+ Filter-Id += 'Fail 2'
+ }
+}
diff --git a/src/tests/keywords/if-skip b/src/tests/keywords/if-skip
new file mode 100644
index 0000000..0e74f22
--- /dev/null
+++ b/src/tests/keywords/if-skip
@@ -0,0 +1,42 @@
+# PRE: if
+#
+# Conditions which statically evaluate to "false"
+# have their entire contents skipped on load.
+#
+# Conditions which statically evaluate to "true"
+# have the following "else" sections skipped, too.
+#
+# i.e. we can reference things which don't exist,
+# and they'll get ignored.
+#
+if (0) {
+ no-such-module
+}
+
+if (0) {
+ no-such-module
+}
+else {
+ ok
+}
+
+if (1) {
+ ok
+}
+else {
+ no-such-module
+}
+
+if (1) {
+ ok
+}
+elsif ("%{foo:bar}") { # no pass2
+ no-such-module
+}
+else {
+ no-such-module
+}
+
+update reply {
+ Filter-Id := "filter"
+}