From 81942fe05711a95dcc4445805a2ba69d2f3cc0ab Mon Sep 17 00:00:00 2001
From: Oskar Viljasaar <oskar.viljasaar@iopsys.eu>
Date: Thu, 17 Sep 2020 14:00:32 +0200
Subject: [PATCH] initial test framework

---
 Makefile                    |  28 +++++
 src/Makefile                |  15 ++-
 test/cmocka/Makefile        |  28 +++++
 test/cmocka/unit_test_qos.c | 197 ++++++++++++++++++++++++++++++++++++
 4 files changed, 264 insertions(+), 4 deletions(-)
 create mode 100644 test/cmocka/Makefile
 create mode 100644 test/cmocka/unit_test_qos.c

diff --git a/Makefile b/Makefile
index c342593..b0f6618 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,7 @@
 # Makefile for qosmngr
+include common.mk
+
+QOSMNGR_LIB=libqosmngr.so
 
 subdirs ?= src
 
@@ -6,3 +9,28 @@ subdirs ?= src
 
 all clean:
 	@for i in $(subdirs) ; do [ -d $$i ] && $(MAKE) -C $$i $@; done
+
+qosmngr:
+	$(MAKE) all -C src
+
+${QOSMNGR_LIB}:
+	$(MAKE) CFLAGS+=-fPIC lib -C src
+
+test: ${QOSMNGR_LIB}
+
+unit-test: coverage
+	$(MAKE) -C test/cmocka unit-test QOSMNGR_LIB_DIR=$(PWD)
+
+coverage: CFLAGS  += -g -O0 -fprofile-arcs -ftest-coverage -fPIC
+coverage: LDFLAGS += --coverage
+coverage: test qosmngr
+	$(foreach testprog, $(CODECOVERAGE_SRC), $(GCOV) $(testprog);)
+
+clean:
+	rm -f *.o libqosmngr.so $(PROG)
+	rm -f *.xml *.html
+	find -name '*.gcda' -exec rm {} -fv \;
+	find -name '*.gcno' -exec rm {} -fv \;
+	find -name '*.gcov' -exec rm {} -fv \;
+	make -C test/cmocka clean
+	make -C src clean
\ No newline at end of file
diff --git a/src/Makefile b/src/Makefile
index c923b31..9b2ba4c 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,6 +1,8 @@
 include ../common.mk
 
 PROG = $(PROJECT_TOPLEVEL)/qosmngr
+SHARED_LIB = $(PROJECT_TOPLEVEL)/libqosmngr.so
+
 OBJS = main.o qosmngr.o
 
 PROG_CFLAGS = $(CFLAGS) -I$(INCLUDE_DIR) -Wall -fstrict-aliasing
@@ -10,9 +12,14 @@ PROG_LIBS = -luci -lubus -lubox -ljson-c -lblobmsg_json -lnl-genl-3 -lnl-3 -lgco
 %.o: %.c
 	$(CC) $(PROG_CFLAGS) $(FPIC) -c -o $@ $<
 
-.PHONY: version
+.PHONY: clean
+
+all: $(PROG)
+
+lib: $(SHARED_LIB)
 
-all: version $(PROG)
+$(SHARED_LIB): $(OBJS) $(INCLUDE_DIR)/version.h
+	$(CC) $(PROG_LDFLAGS) -shared -o $@ $^ $(PROG_LIBS)
 
 version: $(INCLUDE_DIR)/version.h
 
@@ -25,8 +32,8 @@ $(INCLUDE_DIR)/version.h: $(PROJECT_TOPLEVEL)/VERSION
 	echo "const char *qosmngr_xtra_version = \"$$xver$$dirty\";" >> $@; \
 	)
 
-$(PROG): $(OBJS)
+$(PROG): $(INCLUDE_DIR)/version.h $(OBJS)
 	$(CC) $(PROG_LDFLAGS) -o $@ $^ $(PROG_LIBS)
 
 clean:
-	rm -f *.o libqosmngr.so $(PROG) $(INCLUDE_DIR)/version.h
+	rm -f *.o $(SHARED_LIB) $(PROG) $(INCLUDE_DIR)/version.h
diff --git a/test/cmocka/Makefile b/test/cmocka/Makefile
new file mode 100644
index 0000000..6529742
--- /dev/null
+++ b/test/cmocka/Makefile
@@ -0,0 +1,28 @@
+include ../../common.mk
+
+CC		= gcc
+QOSMNGR_LIB_DIR	?= $(shell dirname $(PWD))
+QOSMNGR_LIB	= -lqosmngr -L$(QOSMNGR_LIB_DIR)
+CMOCKA_LIB	= -l cmocka
+LIBS		= $(QOSMNGR_LIB) $(CMOCKA_LIB) -lqos -pthread -luci -lubus -lubox -ljson-c -lblobmsg_json -lnl-genl-3 -lnl-3 -ljson-validator -ljson-schema-validator -ljson-editor
+CFLAGS		= -g -Wall -I$(INCLUDE_DIR)
+LDFLAGS		= $(LIBS) -Wl,-rpath=$(QOSMNGR_LIB_DIR) -I$(QOSMNGR_LIB_DIR)
+UNIT_TESTS	= unit_test_qos
+
+VALGRIND	= valgrind --leak-check=full --show-reachable=no \
+	--show-leak-kinds=all --errors-for-leak-kinds=all \
+	--error-exitcode=1 --track-origins=yes
+
+%.o: %.c
+	$(CC) $(CFLAGS) $(FPIC) -c -o $@ $<
+
+unit_test_qos: unit_test_qos.o
+	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
+
+unit-test: $(UNIT_TESTS)
+	$(foreach testprog, $(UNIT_TESTS), sudo $(VALGRIND) ./$(testprog);)
+
+.PHONY: clean
+
+clean:
+	rm $(UNIT_TESTS) *.o -fv
diff --git a/test/cmocka/unit_test_qos.c b/test/cmocka/unit_test_qos.c
new file mode 100644
index 0000000..617665a
--- /dev/null
+++ b/test/cmocka/unit_test_qos.c
@@ -0,0 +1,197 @@
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <libubus.h>
+#include <libubox/blobmsg_json.h>
+#include <libubox/blobmsg.h>
+
+#include <json-validator.h>
+#include <json-c/json.h>
+#include <json-editor.h>
+
+#include <json-c/json_tokener.h>
+#include <easy/easy.h>
+#include <qos.h>
+
+#include <qosmngr.h>
+
+struct test_ctx {
+	struct blob_buf bb;
+	struct ubus_object qos;
+	FILE *fp;
+};
+
+/* declare qosmngr functions */
+int wl_radio_status(struct ubus_context *ctx, struct ubus_object *obj,
+		struct ubus_request_data *req, const char *method,
+		struct blob_attr *msg);
+
+
+int wl_radio_get_param(struct ubus_context *ctx, struct ubus_object *obj,
+		  struct ubus_request_data *req, const char *method,
+		  struct blob_attr *msg);
+
+int sta_disconnect(struct ubus_context *ctx, struct ubus_object *obj,
+		      struct ubus_request_data *req, const char *method,
+		      struct blob_attr *msg);
+
+int nbr_transition(struct ubus_context *ctx, struct ubus_object *obj,
+		      struct ubus_request_data *req, const char *method,
+		      struct blob_attr *msg);
+
+int nbr_request(struct ubus_context *ctx, struct ubus_object *obj,
+		      struct ubus_request_data *req, const char *method,
+		      struct blob_attr *msg);
+
+int nbr_add(struct ubus_context *ctx, struct ubus_object *obj,
+		      struct ubus_request_data *req, const char *method,
+		      struct blob_attr *msg);
+
+int wps_start(struct ubus_context *ctx, struct ubus_object *obj,
+		  struct ubus_request_data *req, const char *method,
+		  struct blob_attr *msg);
+
+int wps_stop(struct ubus_context *ctx, struct ubus_object *obj,
+		  struct ubus_request_data *req, const char *method,
+		  struct blob_attr *msg);
+
+int vsie_add(struct ubus_context *ctx, struct ubus_object *obj,
+		      struct ubus_request_data *ureq, const char *method,
+		      struct blob_attr *msg);
+
+int vsie_del(struct ubus_context *ctx, struct ubus_object *obj,
+		      struct ubus_request_data *ureq, const char *method,
+		      struct blob_attr *msg);
+
+int nbr_del(struct ubus_context *ctx, struct ubus_object *obj,
+		      struct ubus_request_data *req, const char *method,
+		      struct blob_attr *msg);
+
+int sta_monitor(struct ubus_context *ctx, struct ubus_object *obj,
+		      struct ubus_request_data *req, const char *method,
+		      struct blob_attr *msg);
+
+int wl_scan(struct ubus_context *ctx, struct ubus_object *obj,
+		  struct ubus_request_data *req, const char *method,
+		  struct blob_attr *msg);
+
+int wl_autochannel(struct ubus_context *ctx, struct ubus_object *obj,
+		struct ubus_request_data *req, const char *method,
+		struct blob_attr *msg);
+
+int wps_set_ap_pin(struct ubus_context *ctx, struct ubus_object *obj,
+		  struct ubus_request_data *req, const char *method,
+		  struct blob_attr *msg);
+
+int qosmngr_event_main(const char *evmap_file);
+
+/* overload ubus_send_reply to prevent segfault*/
+int ubus_send_reply(struct ubus_context *ctx, struct ubus_request_data *req,
+		    struct blob_attr *msg)
+{
+	return 0;
+}
+
+
+/* TODO: how to fix events without fopen and fclose every poll? */
+struct json_object *poll_test_log(FILE *fp, const char *prefix)
+{
+	char line[256] = {0};
+	char msg[256] = {0};
+	struct json_object *obj = NULL;
+
+	//if (!fp) {
+    fp = fopen("/tmp/test.log", "r");
+    if (!fp)
+        return NULL;
+	//}
+
+	while (fgets(line, 256, fp))  {
+		char *ptr, *s;
+		char appended[32];
+
+		snprintf(appended, sizeof(appended), "%s:", prefix);
+
+		ptr = strstr(line, appended);
+		if (!ptr)
+			continue;
+
+		s = multi_tok(ptr, appended);
+		s = multi_tok(NULL, appended);
+		strncpy(msg, s, sizeof(msg));
+	}
+
+	if (strlen(msg))
+		obj = json_tokener_parse(msg);
+
+	fclose(fp);
+	return obj;
+}
+
+static int group_setup(void **state)
+{
+	struct test_ctx *ctx = calloc(1, sizeof(struct test_ctx));
+
+	if (!ctx)
+		return -1;
+
+	remove("/tmp/test.log");
+
+	ctx->qos.name = "qos";
+
+	memset(&ctx->bb, 0, sizeof(struct blob_buf));
+
+	*state = ctx;
+	return 0;
+}
+
+static void test_api_get_stats(void **state)
+{
+	struct test_ctx *ctx = (struct test_ctx *) *state;
+	struct blob_buf *bb = &ctx->bb;
+	struct ubus_object *obj = &ctx->qos;
+	struct json_object *jobj, *tmp;
+
+	blobmsg_add_string(bb, "ifname", "test");
+
+	tmp = json_object_get_by_string(jobj, "iface");
+	assert_string_equal(json_object_get_string(tmp), "test");
+
+	json_object_put(jobj);
+    
+	return;
+}
+
+static int group_teardown(void **state)
+{
+	struct test_ctx *ctx = (struct test_ctx *) *state;
+
+	blob_buf_free(&ctx->bb);
+	free(ctx);
+	remove("/tmp/test.log");
+
+	/* TODO: fix event poll file */
+	//if (ctx->fp)
+		//fclose(ctx->fp);
+	return 0;
+}
+
+static int setup(void **state)
+{
+	struct test_ctx *ctx = (struct test_ctx *) *state;
+
+	blob_buf_init(&ctx->bb, 0);
+
+	return 0;
+}
+
+int main(void)
+{
+	const struct CMUnitTest tests[] = {
+		cmocka_unit_test_setup(test_api_get_stats, setup),
+	};
+
+	return cmocka_run_group_tests(tests, group_setup, group_teardown);
+}
-- 
GitLab