Added simple dynamic xlat tests
authorAlan T. DeKok <aland@freeradius.org>
Wed, 28 Jan 2015 16:54:35 +0000 (11:54 -0500)
committerAlan T. DeKok <aland@freeradius.org>
Wed, 28 Jan 2015 16:54:35 +0000 (11:54 -0500)
for stand-alone expansions

Makefile
src/main/unittest.c
src/tests/all.mk
src/tests/keywords/all.mk
src/tests/xlat/all.mk [new file with mode: 0644]
src/tests/xlat/expr.txt [new file with mode: 0644]
src/tests/xlat/radiusd.conf [new file with mode: 0644]

index 9f37c07..2beeaee 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -52,7 +52,7 @@ $(BUILD_DIR)/tests/radiusd-c: raddb/test.conf ${BUILD_DIR}/bin/radiusd | build.r
        @echo "ok"
        @touch $@
 
-test: ${BUILD_DIR}/bin/radiusd ${BUILD_DIR}/bin/radclient tests.unit tests.keywords tests.auth tests.modules $(BUILD_DIR)/tests/radiusd-c | build.raddb
+test: ${BUILD_DIR}/bin/radiusd ${BUILD_DIR}/bin/radclient tests.unit tests.xlat tests.keywords tests.auth tests.modules $(BUILD_DIR)/tests/radiusd-c | build.raddb
        @$(MAKE) -C src/tests tests
 
 #  Tests specifically for Travis.  We do a LOT more than just
index d151dbf..24d2494 100644 (file)
@@ -32,6 +32,8 @@ RCSID("$Id$")
 #      include <getopt.h>
 #endif
 
+#include <ctype.h>
+
 /*
  *  Global variables.
  */
@@ -504,6 +506,102 @@ static ssize_t xlat_poke(UNUSED void *instance, REQUEST *request,
 
 
 /*
+ *     Read a file compose of xlat's and expected results
+ */
+static bool do_xlats(char const *filename, FILE *fp)
+{
+       int lineno = 0;
+       ssize_t len;
+       char *p;
+       char input[8192];
+       char output[8192];
+       REQUEST *request;
+
+       request = request_alloc(NULL);
+
+       request->log.lvl = debug_flag;
+       request->log.func = vradlog_request;
+
+       while (fgets(input, sizeof(input), fp) != NULL) {
+               lineno++;
+
+               /*
+                *      Ignore blank lines and comments
+                */
+               p = input;
+               while (isspace((int) *p)) p++;
+
+               if (*p < ' ') continue;
+               if (*p == '#') continue;
+
+               p = strchr(p, '\n');
+               if (!p) {
+                       if (!feof(fp)) {
+                               fprintf(stderr, "Line %d too long in %s\n",
+                                       lineno, filename);
+                               TALLOC_FREE(request);
+                               return false;
+                       }
+               } else {
+                       *p = '\0';
+               }
+
+               /*
+                *      Look for "xlat"
+                */
+               if (strncmp(input, "xlat ", 5) == 0) {
+                       ssize_t slen;
+                       char const *error = NULL;
+                       char *fmt = talloc_typed_strdup(NULL, input + 5);
+                       xlat_exp_t *head;
+
+                       slen = xlat_tokenize(fmt, fmt, &head, &error);
+                       if (slen <= 0) {
+                               talloc_free(fmt);
+                               snprintf(output, sizeof(output), "ERROR offset %d '%s'", (int) -slen, error);
+                               continue;
+                       }
+
+                       if (input[slen + 5] != '\0') {  
+                               talloc_free(fmt);
+                               snprintf(output, sizeof(output), "ERROR offset %d 'Too much text' ::%s::", (int) slen, input + slen + 5);
+                               continue;
+                       }
+
+                       len = radius_xlat_struct(output, sizeof(output), request, head, NULL, NULL);
+                       if (len < 0) {
+                               snprintf(output, sizeof(output), "ERROR expanding xlat: %s", fr_strerror());
+                               continue;
+                       }
+
+                       TALLOC_FREE(fmt); /* also frees 'head' */
+                       continue;
+               }
+
+               /*
+                *      Look for "data".
+                */
+               if (strncmp(input, "data ", 5) == 0) {
+                       if (strcmp(input + 5, output) != 0) {
+                               fprintf(stderr, "Mismatch at line %d of %s\n\tgot      : %s\n\texpected : %s\n",
+                                       lineno, filename, output, input + 5);
+                               TALLOC_FREE(request);
+                               return false;
+                       }
+                       continue;
+               }
+
+               fprintf(stderr, "Unknown keyword in %s[%d]\n", filename, lineno);
+               TALLOC_FREE(request);
+               return false;
+       }
+
+       TALLOC_FREE(request);
+       return true;
+}
+
+
+/*
  *     The main guy.
  */
 int main(int argc, char *argv[])
@@ -517,6 +615,7 @@ int main(int argc, char *argv[])
        REQUEST *request = NULL;
        VALUE_PAIR *vp;
        VALUE_PAIR *filter_vps = NULL;
+       bool xlat_only = false;
 
        /*
         *      If the server was built with debugging enabled always install
@@ -558,7 +657,7 @@ int main(int argc, char *argv[])
        default_log.fd = STDOUT_FILENO;
 
        /*  Process the options.  */
-       while ((argval = getopt(argc, argv, "d:D:f:hi:mMn:o:xX")) != EOF) {
+       while ((argval = getopt(argc, argv, "d:D:f:hi:mMn:o:O:xX")) != EOF) {
 
                switch (argval) {
                        case 'd':
@@ -598,6 +697,15 @@ int main(int argc, char *argv[])
                                output_file = optarg;
                                break;
 
+                       case 'O':
+                               if (strcmp(optarg, "xlat_only") == 0) {
+                                       xlat_only = true;
+                                       break;
+                               }
+
+                               fprintf(stderr, "Unknown option '%s'\n", optarg);
+                               exit(EXIT_FAILURE);
+
                        case 'X':
                                debug_flag += 2;
                                main_config.log_auth = true;
@@ -671,6 +779,15 @@ int main(int argc, char *argv[])
        }
 
        /*
+        *      For simplicity, read xlat's.
+        */
+       if (xlat_only) {
+               if (!do_xlats(input_file, fp)) rcode = EXIT_FAILURE;
+               if (input_file) fclose(fp);
+               goto finish;
+       }
+
+       /*
         *      Grab the VPs from stdin, or from the file.
         */
        request = request_setup(fp);
index fd7ee97..99d131a 100644 (file)
@@ -1,4 +1,4 @@
-SUBMAKEFILES := rbmonkey.mk unit/all.mk keywords/all.mk auth/all.mk modules/all.mk
+SUBMAKEFILES := rbmonkey.mk unit/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
index 75e37b2..ab35bc2 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.UNIT_FILES)
+$(TESTS.KEYWORDS_FILES): $(TESTS.XLAT_FILES)
 
 .PHONY: clean.tests.keywords
 clean.tests.keywords:
diff --git a/src/tests/xlat/all.mk b/src/tests/xlat/all.mk
new file mode 100644 (file)
index 0000000..c76d062
--- /dev/null
@@ -0,0 +1,58 @@
+#
+#  Unit tests for dynamic xlat expansions
+#
+
+#
+#  The test files are files without extensions.
+#  The list is unordered.  The order is added in the next step by looking
+#  at precursors.
+#
+XLAT_FILES := $(subst $(DIR)/,,$(wildcard $(DIR)/*.txt))
+
+#
+#  Create the output directory
+#
+.PHONY: $(BUILD_DIR)/tests/xlat
+$(BUILD_DIR)/tests/xlat:
+       @mkdir -p $@
+
+#
+#  Files in the output dir depend on the unit tests
+#
+#      src/tests/keywords/FOO          unlang for the test
+#      src/tests/keywords/FOO.attrs    input RADIUS and output filter
+#      build/tests/keywords/FOO        updated if the test succeeds
+#      build/tests/keywords/FOO.log    debug output for the test
+#
+#  Auto-depend on modules via $(shell grep INCLUDE $(DIR)/radiusd.conf | grep mods-enabled | sed 's/.*}/raddb/'))
+#
+#  If the test fails, then look for ERROR in the input.  No error
+#  means it's unexpected, so we die.
+#
+#  Otherwise, check the log file for a parse error which matches the
+#  ERROR line in the input.
+#
+$(BUILD_DIR)/tests/xlat/%: $(DIR)/% $(TESTBINDIR)/unittest | $(BUILD_DIR)/tests/xlat build.raddb
+       @echo XLAT-TEST $(notdir $@)
+       @if ! $(TESTBIN)/unittest -D share -d src/tests/xlat/ -i $< -xx -O xlat_only > $@.log 2>&1; then \
+               cat $@.log; \
+               echo "./$(TESTBIN)/unittest -D share -d src/tests/xlat/ -i $< -xx -O xlat_only"; \
+               exit 1; \
+       fi
+       @touch $@
+
+#
+#  Get all of the unit test output files
+#
+TESTS.XLAT_FILES := $(addprefix $(BUILD_DIR)/tests/xlat/,$(XLAT_FILES))
+
+#
+#  Depend on the output files, and create the directory first.
+#
+tests.xlat: $(TESTS.XLAT_FILES)
+
+$(TESTS.XLAT_FILES): $(TESTS.UNIT_FILES)
+
+.PHONY: clean.tests.xlat
+clean.tests.xlat:
+       @rm -rf $(BUILD_DIR)/tests/xlat/
diff --git a/src/tests/xlat/expr.txt b/src/tests/xlat/expr.txt
new file mode 100644 (file)
index 0000000..e2d9f10
--- /dev/null
@@ -0,0 +1,20 @@
+xlat %{md5:This is a string\n}
+data 9ac4dbbc3c0ad2429e61d0df5dc28add
+
+xlat %{expr: 1 + 2 + 3 + 4}
+data 10
+
+xlat %{expr: 1 & ~1}
+data 0
+
+xlat %{expr: 2 - -1}
+data 3
+
+xlat %{expr: -1 * 2}
+data -2
+
+xlat %{expr: 1 << 2 | 1}
+data 5
+
+xlat %{expr: 6 + -(1 + 3)}
+data 2
diff --git a/src/tests/xlat/radiusd.conf b/src/tests/xlat/radiusd.conf
new file mode 100644 (file)
index 0000000..89e7f24
--- /dev/null
@@ -0,0 +1,37 @@
+#
+#  Minimal radiusd.conf for testing keywords
+#
+
+raddb          = raddb
+
+modconfdir     = ${raddb}/mods-config
+
+correct_escapes        = true
+
+#  Only for testing!
+#  Setting this on a production system is a BAD IDEA.
+security {
+       allow_vulnerable_openssl = yes
+}
+
+modules {
+       $INCLUDE ${raddb}/mods-enabled/always
+
+       $INCLUDE ${raddb}/mods-enabled/pap
+
+       $INCLUDE ${raddb}/mods-enabled/expr
+}
+
+server default {
+       authorize {
+               update control {
+                       Cleartext-Password := 'hello'
+               }
+
+               pap
+       }
+
+       authenticate {
+               pap
+       }
+}