From 950075281f5fa0cf54d89e37372aff1ae7e91fd7 Mon Sep 17 00:00:00 2001 From: Shubham <shubham.sharma@iopsys.eu> Date: Mon, 1 Feb 2021 10:14:13 +0000 Subject: [PATCH] Test, coverage and documentation fixes - all test scenarios added for functional testing - wrong method for qos object - strncpy fixed and ports uci file needed for testing - compilation fix for functional test - Added dummy test stats for test platform - removed reload from documentation - unnecessary test removal --- .gitlab-ci.yml | 15 -- Makefile | 5 +- docs/api/qos.md | 79 +---------- gitlab-ci/functional-api-test.sh | 2 + gitlab-ci/functional-test.sh | 3 +- gitlab-ci/install-dependencies.sh | 5 +- gitlab-ci/unit-test.sh | 13 -- schemas/ubus/qos.json | 23 --- src/qosmngr.c | 4 +- test/api/json/qos.json | 40 +++--- test/cmocka/Makefile | 11 +- test/cmocka/functional_test_qos.c | 224 +++++++++++++++++++++++++++--- test/cmocka/unit_test_qos.c | 137 ------------------ test/files/etc/config/ports | 22 +++ 14 files changed, 261 insertions(+), 322 deletions(-) delete mode 100755 gitlab-ci/unit-test.sh delete mode 100644 test/cmocka/unit_test_qos.c create mode 100644 test/files/etc/config/ports diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b9b68c5..6c87429 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -30,21 +30,6 @@ run_api_test: - api-test-memory-report.xml - timestamp.log -run_unit_test: - stage: unit_test - image: iopsys/code-analysis-dev - allow_failure: false - script: - - "./gitlab-ci/install-dependencies.sh" - - "./gitlab-ci/setup.sh" - - "./gitlab-ci/unit-test.sh" - - artifacts: - when: always - paths: - - unit-test-coverage.xml - - timestamp.log - run_functional_test: stage: functional_test image: iopsys/code-analysis-dev diff --git a/Makefile b/Makefile index 4d475ae..60fa796 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ QOSMNGR_LIB=libqosmngr.so GCOV=gcov SRC_DIR = src -CODECOVERAGE_SRC = $(addprefix $(SRC_DIR), qosmngr.c ) +CODECOVERAGE_SRC = $(addprefix $(SRC_DIR)/, qosmngr.c ) PROG = $(PROJECT_TOPLEVEL)/qosmngr SHARED_LIB = $(PROJECT_TOPLEVEL)/libqosmngr.so OBJS = $(addprefix $(SRC_DIR)/, main.o qosmngr.o ) @@ -45,9 +45,6 @@ coverage: LDFLAGS += --coverage coverage: test $(PROG) $(foreach testprog, $(CODECOVERAGE_SRC), $(GCOV) $(testprog);) -unit-test: test - $(MAKE) -C test/cmocka unit-test - functional-test: test $(MAKE) -C test/cmocka functional-test diff --git a/docs/api/qos.md b/docs/api/qos.md index 596e228..8ed4f37 100644 --- a/docs/api/qos.md +++ b/docs/api/qos.md @@ -13,7 +13,6 @@ http://example.com/root.json | List of Methods | | --------------------------- | | [queue_stats](#queue_stats) | Method | qos (this schema) | -| [reload](#reload) | Method | qos (this schema) | | `*` | any | Additional | Yes | this schema _allows_ additional properties | ## queue_stats @@ -44,7 +43,7 @@ http://example.com/root.json | Property | Type | Required | | -------- | ------- | ------------ | -| `ifname` | string | **Required** | +| `ifname` | string | Optional | | `qid` | integer | Optional | #### ifname @@ -252,79 +251,3 @@ All items must be of the type: `object` with following properties: } ``` -## reload - -`reload` - -- type: `Method` - -### reload Type - -`object` with following properties: - -| Property | Type | Required | -| -------- | ------ | -------- | -| `input` | object | Optional | -| `output` | object | Optional | - -#### input - -`input` - -- is optional -- type: `object` - -##### input Type - -`object` with following properties: - -| Property | Type | Required | -| --------- | ------ | ------------ | -| `section` | string | **Required** | - -#### section - -`section` - -- is **required** -- type: `string` - -##### section Type - -`string` - -- minimum length: 1 characters -- maximum length: 16 characters - -### Ubus CLI Example - -``` -ubus call qos reload {"section":"aliquip repr"} -``` - -### JSONRPC Example - -```json -{ "jsonrpc": "2.0", "id": 0, "method": "call", "params": ["<SID>", "qos", "reload", { "section": "aliquip repr" }] } -``` - -#### output - -`output` - -- is optional -- type: `object` - -##### output Type - -`object` with following properties: - -| Property | Type | Required | -| -------- | ---- | -------- | -| None | None | None | - -### Output Example - -```json -{} -``` diff --git a/gitlab-ci/functional-api-test.sh b/gitlab-ci/functional-api-test.sh index 8bc036a..0d9dc0e 100755 --- a/gitlab-ci/functional-api-test.sh +++ b/gitlab-ci/functional-api-test.sh @@ -10,7 +10,9 @@ sleep 3 supervisorctl status all # run API validation +echo "Validating ubus api with ubs-api-validator" ubus-api-validator -d ./test/api/json/ > ./api-result.log +echo "Please check test result under pipeline test tab." supervisorctl stop all supervisorctl status diff --git a/gitlab-ci/functional-test.sh b/gitlab-ci/functional-test.sh index 1546ec5..f5231f6 100755 --- a/gitlab-ci/functional-test.sh +++ b/gitlab-ci/functional-test.sh @@ -1,8 +1,9 @@ #!/bin/bash -echo "preparation script" +echo "Functional Test" pwd +make coverage -C ./ supervisorctl status all supervisorctl update sleep 3 diff --git a/gitlab-ci/install-dependencies.sh b/gitlab-ci/install-dependencies.sh index c89057c..04c4a39 100755 --- a/gitlab-ci/install-dependencies.sh +++ b/gitlab-ci/install-dependencies.sh @@ -9,14 +9,13 @@ cd /opt/dev rm -fr easy-soc-libs git clone https://dev.iopsys.eu/iopsys/easy-soc-libs.git cd easy-soc-libs -git checkout wip/libqos/subject-to-rebase cd libeasy make CFLAGS+="-I/usr/include/libnl3" mkdir -p /usr/include/easy -cp easy.h event.h utils.h /usr/include/easy +cp easy.h event.h utils.h if_utils.h debug.h /usr/include/easy cp -a libeasy*.so* /usr/lib cd ../libqos -make PLATFORM=TEST +make PLATFORM=TEST cp qos.h /usr/include cp -a libqos.so* /usr/lib sudo ldconfig diff --git a/gitlab-ci/unit-test.sh b/gitlab-ci/unit-test.sh deleted file mode 100755 index b0207da..0000000 --- a/gitlab-ci/unit-test.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -echo "preparation script" -pwd - -make unit-test -C ./ - -#report part -#GitLab-CI output -gcovr -r . -# Artefact -gcovr -r . --xml -o ./unit-test-coverage.xml -date +%s > timestamp.log \ No newline at end of file diff --git a/schemas/ubus/qos.json b/schemas/ubus/qos.json index 453c232..8849de6 100644 --- a/schemas/ubus/qos.json +++ b/schemas/ubus/qos.json @@ -13,29 +13,6 @@ "title": "qos", "object": "qos", "properties": { - "reload": { - "type": "object", - "properties": { - "input": { - "type": "object", - "required": [ - "section" - ], - "properties": { - "section": { - "type": "string", - "minLength": 1, - "maxLength": 16 - } - } - }, - "output": { - "type": "object", - "required": [], - "properties": {} - } - } - }, "queue_stats": { "type": "object", "properties": { diff --git a/src/qosmngr.c b/src/qosmngr.c index e6b044f..b167ffc 100644 --- a/src/qosmngr.c +++ b/src/qosmngr.c @@ -36,6 +36,8 @@ #define MIN_INDEX 48 #define MAX_INDEX 57 +int test_flag = 0; + static int init_flag = 1; static struct qos_stats **q_stat = {0}; @@ -288,7 +290,7 @@ static int get_stats_for_all_intf(struct blob_buf *b, struct qos_stats *stats, v struct uci_section *uci_sec = uci_to_section(uci_elmnt); if (uci_sec) { struct uci_option *uci_opn = uci_lookup_option(uci_ctx, uci_sec, "ifname"); - strcpy(ifname, uci_opn->v.string); + strncpy(ifname, uci_opn->v.string, sizeof(ifname) - 1); ret = get_stats_by_ifname(b, stats, dd, ifname, QOS_QUEUE_ANY); if (ret != 0) { syslog(LOG_ERR, "get_stats_by_ifname : ret %d\n", ret); diff --git a/test/api/json/qos.json b/test/api/json/qos.json index 1932eea..c95a1d3 100644 --- a/test/api/json/qos.json +++ b/test/api/json/qos.json @@ -1,21 +1,25 @@ { - "object": "qos", - "methods": [ - { - "method": "get_status", - "args": { - "ifname": "test" - }, - "rc": 0 - }, - { - "method": "get_status", - "args": { - "ifname": "test", - "qid": 0 - }, - "rc": 0 - } - ] + "object": "qos", + "methods": [ + { + "method": "queue_stats", + "rc": 0 + }, + { + "method": "queue_stats", + "args": { + "ifname": "eth0" + }, + "rc": 0 + }, + { + "method": "queue_stats", + "args": { + "ifname": "eth0", + "qid": 0 + }, + "rc": 0 + } + ] } diff --git a/test/cmocka/Makefile b/test/cmocka/Makefile index 5f5240e..0051154 100644 --- a/test/cmocka/Makefile +++ b/test/cmocka/Makefile @@ -3,9 +3,8 @@ include ../../common.mk QOSMNGR_LIB_DIR = $(PROJECT_TOPLEVEL) CMOCKA_LIB = -lcmocka LIBS = $(QOSMNGR_LDFLAGS) $(CMOCKA_LIB) -lpthread $(COMMON_LDFLAGS) -ljson-validator -ljson-schema-validator -ljson-editor -CFLAGS = -g -Wall -O0 -fprofile-arcs -ftest-coverage -fPIC -I$(INCLUDE_DIR) +CFLAGS = -g -Wall -O0 -fPIC -I$(INCLUDE_DIR) LDFLAGS = $(LIBS) -Wl,-rpath=$(QOSMNGR_LIB_DIR) -I$(QOSMNGR_LIB_DIR) --coverage -UNIT_TESTS = unit_test_qos FUNCTIONAL_TESTS = functional_test_qos VALGRIND = valgrind --leak-check=full --show-reachable=no \ @@ -15,19 +14,13 @@ VALGRIND = valgrind --leak-check=full --show-reachable=no \ %.o: %.c $(CC) $(CFLAGS) $(FPIC) -c -o $@ $< -unit_test_qos: unit_test_qos.o - $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) - functional_test_qos: functional_test_qos.o $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) -unit-test: $(UNIT_TESTS) - $(foreach testprog, $(UNIT_TESTS), sudo $(VALGRIND) ./$(testprog);) - functional-test: $(FUNCTIONAL_TESTS) $(foreach testprog, $(FUNCTIONAL_TESTS), sudo $(VALGRIND) ./$(testprog);) .PHONY: clean clean: - rm $(UNIT_TESTS) $(FUNCTIONAL_TESTS) *.o *.gcno *.gcda -fv + rm $(FUNCTIONAL_TESTS) *.o *.gcno *.gcda -fv diff --git a/test/cmocka/functional_test_qos.c b/test/cmocka/functional_test_qos.c index a307218..70d49b7 100644 --- a/test/cmocka/functional_test_qos.c +++ b/test/cmocka/functional_test_qos.c @@ -1,6 +1,12 @@ +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> #include <stdarg.h> #include <stddef.h> #include <setjmp.h> +#include <errno.h> #include <cmocka.h> #include <libubus.h> @@ -10,48 +16,226 @@ #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 { - FILE *fp; -}; +#define UBUS_OUTPUT_FILE "/tmp/qosmngr_ubus_output.txt" -int qosmngr_event_main(const char *evmap_file); +struct json_object *json_output = NULL; +/** + * group_setup function to setup the test modules + * @param state input parameter pointer to a pointer to test state + */ static int group_setup(void **state) { - struct test_ctx *ctx = calloc(1, sizeof(struct test_ctx)); - - if (!ctx) - return -1; + // Start ubusd if it is not running + system("pidof ubusd || ubusd &"); - remove("/tmp/test.log"); + // Restart qosmngr + system("kill $(pidof qosmngr) 2>/dev/null; ../../qosmngr &"); - *state = ctx; return 0; } +/** + * group_teardown function to cleanup the test modules + * @param state input parameter pointer to a pointer to test state + */ static int group_teardown(void **state) { - struct test_ctx *ctx = (struct test_ctx *) *state; + // Stop qosmngr + system("kill $(pidof qosmngr) 2>/dev/null"); + + // Delete test logs + unlink(UBUS_OUTPUT_FILE); + + return 0; +} - free(ctx); - remove("/tmp/test.log"); +/** + * teardown function to cleanup the test modules + * @param state input parameter pointer to a pointer to test state + */ +static int teardown(void **state) +{ + if (json_output) { + json_object_put(json_output); // Free the output JSON object + json_output = NULL; + } - /* TODO: fix event poll file */ - //if (ctx->fp) - //fclose(ctx->fp); return 0; } +/** + * validate_queues function to validate all queues received under queues + * @param queues input parameter pointer to json_object containg all queues to + * be validated + */ +static void validate_queues(struct json_object *queues) +{ + int i; + int len; + struct json_object *queue; + + assert_non_null(queues); + len = json_object_array_length(queues); + + // Assert on length as well, should be greater than 0 + assert_true(len >= 0); + + for (i = 0; i < len; i++) { + queue = json_object_array_get_idx(queues, i); + json_object_object_foreach(queue, key, val) { + int val_type = json_object_get_type(val); + switch(val_type) { + case json_type_int: + assert_true(json_object_get_int(val) >= 0); + break; + case json_type_string: + assert_true(!strncmp(json_object_get_string(val), + "eth", strlen("eth"))); + break; + } + } + } +} + +/** + * validate_stats function to validate json_object received under json_obj + * @param json_obj input parameter pointer to json_object containg ubus output + */ +static void validate_stats(struct json_object *json_obj) +{ + assert_non_null(json_obj); + + json_object_object_foreach(json_obj, key, val) { + int val_type = json_object_get_type(val); + + if (val_type == json_type_array) { + switch (val_type) { + case json_type_array: + validate_queues(val); + break; + } + } + } +} + +/** + * get_ubus_call_output function to fill global json output variable with ubus call output + * @param ubus_cmd input parameter pointer to char string containg ubus call + * @param negative_case input parameter of type bool to identify negative scenarios + */ +static void get_ubus_call_output(const char *ubus_cmd, bool negative_case) +{ + char cmd[256]; + int fd = -1; + char *str = NULL; + + // Put the output of ubus call into a string + snprintf(cmd, sizeof(cmd), "%s > %s", ubus_cmd, UBUS_OUTPUT_FILE); + int rc = system(cmd); + assert_return_code(rc, errno); + + fd = open(UBUS_OUTPUT_FILE, O_RDONLY); + assert_return_code(fd, errno); + + struct stat st; + rc = fstat(fd, &st); + + if(negative_case) { + assert_false(rc == 0 && st.st_size > 0); + return; + } else + assert_true(rc == 0 && st.st_size > 0); + + str = calloc(1, (size_t)st.st_size + 1); + assert_non_null(str); + + ssize_t read_len = read(fd, str, (size_t)st.st_size); + assert_int_equal((int)read_len, (int)st.st_size); + + // Parse the string to a json_object + //printf("JSON_OBJECT GET : %s\n", str); + json_output = json_tokener_parse(str); + + if (fd >= 0) + close(fd); + if (str) + free(str); +} + +/** + * test_qos_stats_no_param function to test qos_stats without any parameter + * @param state input parameter pointer to a pointer to test state + */ +static void test_qos_stats_no_param(void **state) +{ + get_ubus_call_output("ubus call qos queue_stats", false); + validate_stats(json_output); +} + +/** + * test_qos_stats_one_param function to test qos_stats with just ifname + * @param state input parameter pointer to a pointer to test state + */ +static void test_qos_stats_one_param(void **state) +{ + get_ubus_call_output("ubus call qos queue_stats '{\"ifname\":\"eth0\"}'", false); + validate_stats(json_output); +} + +/** + * test_qos_stats_all_param function to test qos_stats with all valid params + * @param state input parameter pointer to a pointer to test state + */ +static void test_qos_stats_all_param(void **state) +{ + get_ubus_call_output("ubus call qos queue_stats '{\"ifname\":\"eth0\", \"qid\":0}'", false); + validate_stats(json_output); +} + +/** + * test_qos_stats_missing_param_ifname function to test missing parameter i.e., ifname + * @param state input parameter pointer to a pointer to test state + */ +static void test_qos_stats_missing_param_ifname(void **state) +{ + get_ubus_call_output("ubus call qos queue_stats '{\"qid\":0}'", true); +} + +/** + * test_qos_stats_invalid_param_port function to test invalid parameter as port + * @param state input parameter pointer to a pointer to test state + */ +static void test_qos_stats_invalid_param_port(void **state) +{ + get_ubus_call_output("ubus call qos queue_stats '{\"port\":\"eth0\"}'", true); +} + +/** + * test_qos_stats_invalid_param_pid function to test invalid parameter as pid + * @param state input parameter pointer to a pointer to test state + */ +static void test_qos_stats_invalid_param_pid(void **state) +{ + get_ubus_call_output("ubus call qos queue_stats '{\"ifname\":\"eth0\", \"pid\":0}'", true); +} + int main(void) { - const struct CMUnitTest tests[] = { }; + const struct CMUnitTest tests[] = { + cmocka_unit_test_teardown(test_qos_stats_no_param, teardown), + cmocka_unit_test_teardown(test_qos_stats_one_param, teardown), + cmocka_unit_test_teardown(test_qos_stats_all_param, teardown), + + // -ve scenarios + cmocka_unit_test_teardown(test_qos_stats_missing_param_ifname, teardown), + cmocka_unit_test_teardown(test_qos_stats_invalid_param_port, teardown), + cmocka_unit_test_teardown(test_qos_stats_invalid_param_pid, teardown), + }; return cmocka_run_group_tests(tests, group_setup, group_teardown); } diff --git a/test/cmocka/unit_test_qos.c b/test/cmocka/unit_test_qos.c deleted file mode 100644 index e301988..0000000 --- a/test/cmocka/unit_test_qos.c +++ /dev/null @@ -1,137 +0,0 @@ -#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 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; -} - - -char *multi_tok(char *input, char *delimiter) -{ - static char *string; - char *end, *temp; - - if (input) - string = input; - - if (!string) - return string; - - end = strstr(string, delimiter); - - if (!end) { - char *temp = string; - - string = NULL; - return temp; - } - - temp = string; - - *end = '\0'; - string = end + strlen(delimiter); - - return temp; -} - - -/* 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; - - 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 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; -} - -int main(void) -{ - const struct CMUnitTest tests[] = { - }; - - return cmocka_run_group_tests(tests, group_setup, group_teardown); -} diff --git a/test/files/etc/config/ports b/test/files/etc/config/ports new file mode 100644 index 0000000..1b6a7f3 --- /dev/null +++ b/test/files/etc/config/ports @@ -0,0 +1,22 @@ + +config ethport 'LAN1' + option enabled '1' + option name 'LAN1' + option ifname 'eth1' + option speed '1000' + option duplex 'full' + option autoneg '1' + option eee '0' + option pause '0' + +config ethport 'WAN' + option enabled '1' + option name 'WAN' + option ifname 'eth0' + option speed '1000' + option duplex 'full' + option autoneg '1' + option eee '0' + option pause '1' + option uplink '1' + -- GitLab