]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Added unit test framework for maps
authorAlan T. DeKok <aland@freeradius.org>
Wed, 19 Aug 2015 09:54:25 +0000 (05:54 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Wed, 19 Aug 2015 10:29:08 +0000 (06:29 -0400)
Which are run after the unit tests, and before xlat / keyword
tests.

These tests are for parsing ONLY.  They don't verify that the maps
DO anything.

16 files changed:
src/tests/all.mk
src/tests/bob [new file with mode: 0644]
src/tests/keywords/all.mk
src/tests/keywords/count-error [new file with mode: 0644]
src/tests/keywords/foreach-break-4 [new file with mode: 0644]
src/tests/keywords/switch-escape [new file with mode: 0644]
src/tests/keywords/update-delete [new file with mode: 0644]
src/tests/keywords/virtual [new file with mode: 0644]
src/tests/map/all.mk [new file with mode: 0644]
src/tests/map/base [new file with mode: 0644]
src/tests/map/base.out [new file with mode: 0644]
src/tests/map/map_tests.mk [new file with mode: 0644]
src/tests/map/map_unit.c [new file with mode: 0644]
src/tests/map/map_unit.mk [new file with mode: 0644]
src/tests/peap-client-mschapv2.conf [new file with mode: 0644]
src/tests/unit/foo [new file with mode: 0644]

index 99d131a19946122e13eaf64842cb9d2ebe6a2cc7..d87a22e08201e8f0d69a25ab765efa72da972822 100644 (file)
@@ -1,4 +1,4 @@
-SUBMAKEFILES := rbmonkey.mk unit/all.mk xlat/all.mk keywords/all.mk auth/all.mk modules/all.mk
+SUBMAKEFILES := rbmonkey.mk unit/all.mk map/all.mk xlat/all.mk keywords/all.mk auth/all.mk modules/all.mk
 
 #
 #  Include all of the autoconf definitions into the Make variable space
diff --git a/src/tests/bob b/src/tests/bob
new file mode 100644 (file)
index 0000000..f9398ad
--- /dev/null
@@ -0,0 +1,2 @@
+User-Name = "bob"
+User-Password = "bob"
index dddf78699c45695e02b66cb385689b165a3f1278..369bb7b7d70e6cf3e61daa5cb0412847b7bdacc3 100644 (file)
@@ -116,7 +116,7 @@ TESTS.KEYWORDS_FILES := $(addprefix $(BUILD_DIR)/tests/keywords/,$(KEYWORD_FILES
 #
 tests.keywords: $(TESTS.KEYWORDS_FILES)
 
-$(TESTS.KEYWORDS_FILES): $(TESTS.XLAT_FILES)
+$(TESTS.KEYWORDS_FILES): $(TESTS.XLAT_FILES) $(TESTS.MAP_FILES)
 
 .PHONY: clean.tests.keywords
 clean.tests.keywords:
diff --git a/src/tests/keywords/count-error b/src/tests/keywords/count-error
new file mode 100644 (file)
index 0000000..f0723cb
--- /dev/null
@@ -0,0 +1,11 @@
+update control {
+       Cleartext-Password := 'hello'
+}
+
+update reply {
+       Filter-Id := "filter"
+}
+
+update request {
+       Tmp-String-0 := &reply:Filter-Id[#]     # ERROR
+}
diff --git a/src/tests/keywords/foreach-break-4 b/src/tests/keywords/foreach-break-4
new file mode 100644 (file)
index 0000000..037af8e
--- /dev/null
@@ -0,0 +1,44 @@
+#
+#  PRE: foreach foreach-break-3
+#
+
+update request {
+       Calling-Station-Id := "8"
+}
+
+update control {
+       &Calling-Station-Id := "0"
+       &Calling-Station-Id += "1"
+       &Calling-Station-Id += "2"
+       &Calling-Station-Id += "3"
+       &Calling-Station-Id += "4"
+       &Calling-Station-Id += "5"
+       &Calling-Station-Id += "6"
+       &Calling-Station-Id += "7"
+       &Calling-Station-Id += "8"
+       &Calling-Station-Id += "9"
+       &Calling-Station-Id += "a"
+       &Calling-Station-Id += "b"
+       &Calling-Station-Id += "c"
+       &Calling-Station-Id += "d"
+       &Calling-Station-Id += "e"
+       &Calling-Station-Id += "f"
+       &Calling-Station-Id += "g"
+}
+
+foreach &control:Calling-Station-Id {
+       if (&request:Calling-Station-Id == "%{Foreach-Variable-0}") {
+               update reply {
+                       Filter-Id := "filter"
+               }
+
+               break
+       }
+       elsif ("%{Foreach-Variable-0}" == '9') {
+               update reply {
+                       Filter-Id := "fail-9"
+               }
+
+               reject
+       }
+}
diff --git a/src/tests/keywords/switch-escape b/src/tests/keywords/switch-escape
new file mode 100644 (file)
index 0000000..50d9fdf
--- /dev/null
@@ -0,0 +1,43 @@
+update request {
+       &Tmp-String-0 := 'foo'
+}
+
+switch "%{tolower:%{request:Tmp-String-0}}" {
+       case 'foo' {
+               update reply {
+                       Filter-Id := "filter"
+               }
+       }
+
+       case '' {
+               update reply {
+                       Filter-Id += "fail-empty-1"
+               }
+       }
+
+       case {
+               update reply {
+                       Filter-Id += "fail-default-1"
+               }
+       }
+}
+
+switch "%{request:Tmp-String-0}" {
+       case 'foo' {
+               update reply {
+                       Filter-Id := "filter"
+               }
+       }
+
+       case '' {
+               update reply {
+                       Filter-Id += "fail-empty-2"
+               }
+       }
+
+       case {
+               update reply {
+                       Filter-Id += "fail-default-2"
+               }
+       }
+}
diff --git a/src/tests/keywords/update-delete b/src/tests/keywords/update-delete
new file mode 100644 (file)
index 0000000..a5c2d5a
--- /dev/null
@@ -0,0 +1,40 @@
+#
+# PRE: update
+#
+#  Remove all attributes in a list
+#
+update {
+       control:Cleartext-Password := 'hello'
+       reply:Filter-Id := 'filter'
+}
+
+update request {
+       Tmp-String-0 := 'foobarbaz'
+       Tmp-Integer-0 := 123456789
+       Tmp-IP-Address-0 := 192.0.2.1
+}
+
+if ((Tmp-String-0 != 'foobarbaz') || (Tmp-Integer-0 != 123456789) || (Tmp-IP-Address-0 != 192.0.2.1)) {
+       update {
+               reply:Filter-Id := 'fail'
+       }
+}
+
+# Remove all attributes in the control list
+update {
+       request: !* ANY
+}
+
+# All attributes should now of been removed
+if ((Tmp-String-0 && (Tmp-String-0 == 'foobarbaz')) || \
+    (Tmp-Integer-0 && (Tmp-Integer-0 == 123456789)) || \
+    (Tmp-IP-Address-0 && (Tmp-IP-Address-0 == 192.0.2.1))) {
+       update {
+               reply:Filter-Id := 'fail'
+       }
+}
+
+# This will of been removed too
+update request {
+       User-Password := 'hello'
+}
diff --git a/src/tests/keywords/virtual b/src/tests/keywords/virtual
new file mode 100644 (file)
index 0000000..d6dbe32
--- /dev/null
@@ -0,0 +1,12 @@
+#
+#  PRE: update if
+#
+update control {
+       Cleartext-Password := 'hello'
+}
+
+if (request:Packet-Type == Access-Request) {
+       update reply {
+               Filter-Id := "filter"
+       }
+}
diff --git a/src/tests/map/all.mk b/src/tests/map/all.mk
new file mode 100644 (file)
index 0000000..fedd29d
--- /dev/null
@@ -0,0 +1 @@
+SUBMAKEFILES := map_unit.mk map_tests.mk
diff --git a/src/tests/map/base b/src/tests/map/base
new file mode 100644 (file)
index 0000000..633c32a
--- /dev/null
@@ -0,0 +1,6 @@
+update request {
+       Filter-Id := "filter"
+       User-Name := "blah"
+
+       &reply:Filter-Id += &request:Filter-Id[*]
+}
diff --git a/src/tests/map/base.out b/src/tests/map/base.out
new file mode 100644 (file)
index 0000000..34c519b
--- /dev/null
@@ -0,0 +1,5 @@
+update request {
+       &Filter-Id := "filter"
+       &User-Name := "blah"
+       &reply:Filter-Id += &Filter-Id[*]
+}
diff --git a/src/tests/map/map_tests.mk b/src/tests/map/map_tests.mk
new file mode 100644 (file)
index 0000000..886e6b2
--- /dev/null
@@ -0,0 +1,49 @@
+MAP_TESTS      := $(patsubst $(top_srcdir)/src/tests/map/%,%,$(filter-out %.conf %.md %.attrs %.c %.mk %~ %.rej %.out,$(wildcard $(top_srcdir)/src/tests/map/*)))
+MAP_OUTPUT     := $(addsuffix .out,$(addprefix $(BUILD_DIR)/tests/map/,$(MAP_TESTS)))
+MAP_UNIT       := $(BUILD_DIR)/bin/local/map_unit
+
+.PHONY: $(BUILD_DIR)/tests/map/
+$(BUILD_DIR)/tests/map/:
+       @mkdir -p $@
+
+#
+#      Re-run the tests if the test program changes
+#
+#      Create the output directory before the files
+#
+$(MAP_OUTPUT): $(MAP_UNIT) | $(BUILD_DIR)/tests/map/
+
+#
+#      Re-run the tests if the input file changes
+#
+$(BUILD_DIR)/tests/map/%.out: $(top_srcdir)/src/tests/map/%
+       @echo MAP_TEST $(notdir $<)
+       @if ! $(MAP_UNIT) -d $(top_srcdir)/raddb -D $(top_srcdir)/share $< > $@ 2>&1; then \
+               if ! grep ERROR $< 2>&1 > /dev/null; then \
+                       cat $@; \
+                       echo "# $@"; \
+                       echo FAILED: "$(MAP_UNIT) -d $(top_srcdir)/raddb -D $(top_srcdir)/share $<"; \
+                       exit 1; \
+               fi; \
+               FOUND=$$(grep $< $@ | head -1 | sed 's,^.*$(top_srcdir),,;s/:.*//;s/.*\[//;s/\].*//'); \
+               EXPECTED=$$(grep -n ERROR $< | sed 's/:.*//'); \
+               if [ "$$EXPECTED" != "$$FOUND" ]; then \
+                       cat $@; \
+                       echo "# $@"; \
+                       echo "E $$EXPECTED F $$FOUND"; \
+                       echo UNEXPECTED ERROR: "$(MAP_UNIT) -d $(top_srcdir)/raddb -D $(top_srcdir)/share $<"; \
+                       exit 1; \
+               fi; \
+       else \
+               if ! diff $<.out $@; then \
+                       echo FAILED: " diff $<.out $@"; \
+                       echo FAILED: "$(MAP_UNIT) -d $(top_srcdir)/raddb -D $(top_srcdir)/share $<"; \
+                       exit 1; \
+               fi; \
+       fi
+
+TESTS.MAP_FILES := $(MAP_OUTPUT)
+
+$(TESTS.MAP_FILES): $(TESTS.UNIT_FILES)
+
+tests.map: $(MAP_OUTPUT)
diff --git a/src/tests/map/map_unit.c b/src/tests/map/map_unit.c
new file mode 100644 (file)
index 0000000..5c0b7a4
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * radattr.c   Map debugging tool.
+ *
+ * Version:    $Id$
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright 2015  Alan DeKok <aland@freeradius.org>
+ */
+
+RCSID("$Id$")
+
+#include <freeradius-devel/libradius.h>
+
+#include <freeradius-devel/conf.h>
+#include <freeradius-devel/modpriv.h>
+#include <freeradius-devel/modcall.h>
+
+#include <ctype.h>
+
+#ifdef HAVE_GETOPT_H
+#      include <getopt.h>
+#endif
+
+#include <assert.h>
+
+#include <freeradius-devel/log.h>
+extern log_lvl_t rad_debug_lvl;
+
+#include <sys/wait.h>
+pid_t rad_fork(void);
+pid_t rad_waitpid(pid_t pid, int *status);
+
+pid_t rad_fork(void)
+{
+       return fork();
+}
+
+pid_t rad_waitpid(pid_t pid, int *status)
+{
+       return waitpid(pid, status, 0);
+}
+
+
+static void NEVER_RETURNS usage(void)
+{
+       fprintf(stderr, "usage: map_unit [OPTS] filename ...\n");
+       fprintf(stderr, "  -d <raddb>             Set user dictionary directory (defaults to " RADDBDIR ").\n");
+       fprintf(stderr, "  -D <dictdir>           Set main dictionary directory (defaults to " DICTDIR ").\n");
+       fprintf(stderr, "  -O <output_dir>        Set output directory\n");
+       fprintf(stderr, "  -x                     Debugging mode.\n");
+       fprintf(stderr, "  -M                     Show program version information.\n");
+
+       exit(1);
+}
+
+static int process_file(char const *filename)
+{
+       int rcode;
+       char const *name1, *name2;
+       CONF_SECTION *cs, *main_cs;
+       vp_map_t *head, *map;
+       char buffer[8192];
+
+       main_cs = cf_section_alloc(NULL, "main", NULL);
+       if (cf_file_read(main_cs, filename) < 0) {
+               fprintf(stderr, "map_unit: Failed parsing %s\n",
+                       filename);
+               exit(1);
+       }
+
+       /*
+        *      Always has to be an "update" section.
+        */
+       cs = cf_section_sub_find(main_cs, "update");
+       if (!cs) {
+               talloc_free(main_cs);
+               return -1;
+       }
+
+       /*
+        *      Convert the update section to a list of maps.
+        */
+       rcode = map_afrom_cs(&head, cs, PAIR_LIST_REQUEST, PAIR_LIST_REQUEST, modcall_fixup_update, NULL, 128);
+       if (rcode < 0) return -1; /* message already printed */
+       if (!head) {
+               cf_log_err_cs(cs, "'update' sections cannot be empty");
+               return -1;
+       }
+
+       buffer[0] = '\t';
+
+       name1 = cf_section_name1(cs);
+       name2 = cf_section_name2(cs);
+
+       /*
+        *      And print it all out.
+        */
+       if (!name2) {
+               printf("%s {\n", name1);
+       } else {
+               printf("%s %s {\n", name1, name2);
+       }
+
+       for (map = head; map != NULL; map = map->next) {
+               map_prints(buffer + 1, sizeof(buffer) - 1, map);
+               puts(buffer);
+       }
+       printf("}\n");
+
+       talloc_free(main_cs);
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       int c, rcode = 0;
+       bool report = false;
+       char const *radius_dir = RADDBDIR;
+       char const *dict_dir = DICTDIR;
+
+       cf_new_escape = true;   /* fix the tests */
+
+#ifndef NDEBUG
+       if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) {
+               fr_perror("radattr");
+               exit(EXIT_FAILURE);
+       }
+#endif
+
+       while ((c = getopt(argc, argv, "d:D:xMh")) != EOF) switch (c) {
+               case 'd':
+                       radius_dir = optarg;
+                       break;
+               case 'D':
+                       dict_dir = optarg;
+                       break;
+               case 'x':
+                       fr_debug_lvl++;
+                       rad_debug_lvl = fr_debug_lvl;
+                       break;
+               case 'M':
+                       report = true;
+                       break;
+               case 'h':
+               default:
+                       usage();
+       }
+       argc -= (optind - 1);
+       argv += (optind - 1);
+
+       /*
+        *      Mismatch between the binary and the libraries it depends on
+        */
+       if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
+               fr_perror("radattr");
+               return 1;
+       }
+
+       if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) {
+               fr_perror("radattr");
+               return 1;
+       }
+
+       if (dict_read(radius_dir, RADIUS_DICTIONARY) == -1) {
+               fr_perror("radattr");
+               return 1;
+       }
+
+       if (argc < 2) {
+               rcode = process_file("-");
+
+       } else {
+               rcode = process_file(argv[1]);
+       }
+
+       if (report) {
+               dict_free();
+               fr_log_talloc_report(NULL);
+       }
+
+       if (rcode < 0) rcode = 1; /* internal to Unix process return code */
+
+       return rcode;
+}
diff --git a/src/tests/map/map_unit.mk b/src/tests/map/map_unit.mk
new file mode 100644 (file)
index 0000000..88d319b
--- /dev/null
@@ -0,0 +1,5 @@
+TARGET         := map_unit
+SOURCES                := map_unit.c ${top_srcdir}/src/main/modcall.c
+
+TGT_PREREQS    := libfreeradius-server.a libfreeradius-radius.a
+TGT_LDLIBS     := $(LIBS)
diff --git a/src/tests/peap-client-mschapv2.conf b/src/tests/peap-client-mschapv2.conf
new file mode 100644 (file)
index 0000000..1c60933
--- /dev/null
@@ -0,0 +1,18 @@
+#
+#   ./eapol_test -c peap-mschapv2.conf -s testing123
+#
+network={
+       ssid="example"
+       key_mgmt=WPA-EAP
+       eap=PEAP
+       identity="bob"
+       anonymous_identity="anonymous"
+       password="bob"
+       phase2="auth=MSCHAPV2"
+       phase1="peapver=0"
+
+       ca_cert="../../raddb/certs/ca.pem"
+       client_cert="../../raddb/certs/client.crt"
+       private_key="../../raddb/certs/client.key"
+       private_key_passwd="whatever"
+}
diff --git a/src/tests/unit/foo b/src/tests/unit/foo
new file mode 100644 (file)
index 0000000..add4ab5
--- /dev/null
@@ -0,0 +1,17 @@
+#
+#  We can't look at the data here, because the encoded Tunnel-Password has a 2 byte
+#  random salt
+#
+#encode Tunnel-Password:0 := "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+#decode -
+#data Tunnel-Password:0 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+
+#
+#  1 octet for the tag.  2 octets for salt.  One octet for encrypted length.
+#  249 octets left for real data.
+#
+encode Tunnel-Password:0 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx123456789ab"
+#data x
+decode -
+data Tunnel-Password:0 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx123456789"
+fff