EXIT_WITH_FAILURE;
}
- if (fr_dict_autoload(unit_test_module_dict) < 0) {
- fr_perror("%s", config->name);
- EXIT_WITH_FAILURE;
+ /*
+ * Manually load the protocol dictionary, unless it's "test"
+ */
+ if (strcmp(PROTOCOL_NAME, "test") != 0) {
+ if (fr_dict_autoload(unit_test_module_dict) < 0) {
+ fr_perror("%s", config->name);
+ EXIT_WITH_FAILURE;
+ }
+ } else {
+ dict_protocol = fr_dict_internal();
+ dict_freeradius = dict_protocol;
}
+
if (fr_dict_attr_autoload(unit_test_module_dict_attr) < 0) {
fr_perror("%s", config->name);
EXIT_WITH_FAILURE;
* Do some sanity checking.
*/
dict_check = virtual_server_dict_by_name("default");
+
if (!dict_check || (dict_check != dict_protocol)) {
ERROR("Virtual server namespace does not match requested namespace '%s'", PROTOCOL_NAME);
EXIT_WITH_FAILURE;
/*
* Free our explicitly loaded internal dictionary
*/
- if (fr_dict_free(&dict, __FILE__) < 0) {
+ if ((dict_protocol != dict_freeradius) &&
+ (fr_dict_free(&dict, __FILE__) < 0)) {
fr_perror("unit_test_module - dict");
ret = EXIT_FAILURE;
}
--- /dev/null
+/*
+ * 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
+ */
+
+/**
+ * $Id$
+ * @file src/process/arp/base.c
+ * @brief ARP processing.
+ *
+ * @copyright 2020 Network RADIUS SAS (legal@networkradius.com)
+ */
+#include <freeradius-devel/server/protocol.h>
+#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/arp/arp.h>
+
+static fr_dict_t const *dict_freeradius;
+
+extern fr_dict_autoload_t process_test_dict[];
+fr_dict_autoload_t process_test_dict[] = {
+ { .out = &dict_freeradius, .proto = "freeradius" },
+ { NULL }
+};
+
+static fr_dict_attr_t const *attr_packet_type;
+
+extern fr_dict_attr_autoload_t process_test_dict_attr[];
+fr_dict_attr_autoload_t process_test_dict_attr[] = {
+ { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_freeradius},
+ { NULL }
+};
+
+typedef struct {
+ uint64_t nothing; // so that the next field isn't at offset 0
+
+ CONF_SECTION *recv_request;
+ CONF_SECTION *send_reply;
+} process_test_sections_t;
+
+typedef struct {
+ bool test;
+
+ process_test_sections_t sections;
+} process_test_t;
+
+typedef enum {
+ FR_TEST_INVALID = 0,
+ FR_TEST_REQUEST,
+ FR_TEST_REPLY,
+} fr_test_packet_code_t;
+#define FR_TEST_CODE_MAX (2)
+
+#define FR_TEST_PACKET_CODE_VALID(_code) ((_code == FR_TEST_REQUEST) || (_code == FR_TEST_REPLY))
+
+#define PROCESS_PACKET_TYPE fr_test_packet_code_t
+#define PROCESS_CODE_MAX FR_TEST_CODE_MAX
+#define PROCESS_PACKET_CODE_VALID FR_TEST_PACKET_CODE_VALID
+#define PROCESS_INST process_test_t
+#include <freeradius-devel/server/process.h>
+
+static fr_process_state_t const process_state[] = {
+ [ FR_TEST_REQUEST ] = {
+ .default_reply = FR_TEST_REPLY,
+ .rcode = RLM_MODULE_NOOP,
+ .recv = recv_generic,
+ .resume = resume_recv_generic,
+ .section_offset = offsetof(process_test_sections_t, recv_request),
+ },
+ [ FR_TEST_REPLY ] = {
+ .default_reply = FR_TEST_REPLY,
+ .rcode = RLM_MODULE_NOOP,
+ .send = send_generic,
+ .resume = resume_send_generic,
+ .section_offset = offsetof(process_test_sections_t, send_reply),
+ },
+};
+
+static unlang_action_t mod_process(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
+{
+ fr_process_state_t const *state;
+
+ PROCESS_TRACE;
+
+ (void)talloc_get_type_abort_const(mctx->inst->data, process_test_t);
+ fr_assert(PROCESS_PACKET_CODE_VALID(request->packet->code));
+
+ request->component = "test";
+ request->module = NULL;
+ fr_assert(request->dict == dict_freeradius);
+
+ UPDATE_STATE(packet);
+
+ return state->recv(p_result, mctx, request);
+}
+
+static const virtual_server_compile_t compile_list[] = {
+ {
+ .name = "recv",
+ .name2 = "Request",
+ .component = MOD_POST_AUTH,
+ .offset = PROCESS_CONF_OFFSET(recv_request),
+ },
+ {
+ .name = "send",
+ .name2 = "Reply",
+ .component = MOD_POST_AUTH,
+ .offset = PROCESS_CONF_OFFSET(send_reply),
+ },
+
+ COMPILE_TERMINATOR
+};
+
+
+extern fr_process_module_t process_test;
+fr_process_module_t process_test = {
+ .common = {
+ .magic = MODULE_MAGIC_INIT,
+ .name = "test",
+ .inst_size = sizeof(process_test_t),
+ },
+ .process = mod_process,
+ .compile_list = compile_list,
+ .dict = &dict_freeradius,
+};
--- /dev/null
+#
+# Unit tests for process state machines
+#
+
+
+#
+# Test name
+#
+TEST := test.process
+
+#
+# The test files are files without extensions.
+# The list is unordered. The order is added in the next step by looking
+# at precursors.
+#
+# * search ALL_TGTS
+# * for process_foo targets
+# * strip add "process_" prefix
+# * strip off ".whatever" suffix
+# * add directory name and wildcard file
+# * use wildcard to find existing files
+# * strip off directory name
+# * filter out files we don't care about
+#
+# We're left with a set of files to run the tests on.
+#
+FILES := $(filter-out %.ignore %.conf %.md %.attrs %.mk %~ %.rej,$(subst $(DIR)/,,$(wildcard $(patsubst %,$(DIR)/%/*,$(basename $(subst process_,,$(filter process%,$(ALL_TGTS))))))))
+
+$(eval $(call TEST_BOOTSTRAP))
+
+# -S parse_new_conditions=yes -S use_new_conditions=yes -S forbid_update=yes
+
+
+#
+# For sheer laziness, allow "make test.process.foo"
+#
+define PROCESS_TEST
+test.process.${1}: $(addprefix $(OUTPUT)/,${1})
+
+test.process.help: TEST_PROCESS_HELP += test.process.${1}
+
+#
+# The output depends on the process_foo state machine,
+# and on the "test" process state machine.
+#
+# With filenames added for the output files
+#
+$(OUTPUT)/${1}: $(patsubst %,${BUILD_DIR}/lib/local/process_%.la,$(subst /,,$(dir ${1})) test)
+endef
+$(foreach x,$(FILES),$(eval $(call PROCESS_TEST,$x)))
+
+#
+# Files in the output dir depend on the unit tests
+#
+# src/tests/process/radius/FOO unlang for the test
+# build/tests/process/radius/FOO updated if the test succeeds
+# build/tests/process/radius/FOO.log debug output for the test
+#
+# 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.
+#
+# NOTE: Grepping for $< is not safe cross platform, as on Linux it
+# expands to the full absolute path, and on macOS it appears to be relative.
+#
+# To quickly find all failing tests, run:
+#
+# (make -k test.process 2>&1) | grep 'PROCESS=' | sed 's/PROCESS=//;s/ .*$//'
+#
+PROCESS_ARGS := -p test
+PROCESS_ARGS += -D share/dictionary -d $(DIR)/
+PROCESS_ARGS += -S parse_new_conditions=yes -S use_new_conditions=yes -S forbid_update=yes
+PROCESS_ARGS += -i $(DIR)/test.attrs
+
+$(OUTPUT)/%: $(DIR)/% $(TEST_BIN_DIR)/unit_test_module $(DIR)/unit_test_module.conf
+ $(eval CMD:=PROCESS=$< $(TEST_BIN)/unit_test_module $(PROCESS_ARGS) -r "$@" -xx)
+ @echo PROCESS-TEST $(notdir $@)
+ @mkdir -p $(dir $@)
+ @if ! $(CMD) > "$@.log" 2>&1 || ! test -f "$@"; then \
+ cat $@.log; \
+ echo "# $@.log"; \
+ echo $(CMD); \
+ exit 1; \
+ fi
+
+$(TEST):
+ @touch $(BUILD_DIR)/tests/$@
+
+$(TEST).help:
+ @echo make $(TEST_PROCESS_HELP)