[PATCH] tester: Fix includes
by Inga Stotland
Include proper header files, only those that are required.
---
ell/tester.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/ell/tester.c b/ell/tester.c
index ebebc1d..eedd66b 100644
--- a/ell/tester.c
+++ b/ell/tester.c
@@ -24,6 +24,7 @@
#include <config.h>
#endif
+#define _GNU_SOURCE
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
@@ -31,10 +32,12 @@
#include <sys/time.h>
-#include <ell/ell.h>
-
+#include "idle.h"
#include "log.h"
#include "private.h"
+#include "queue.h"
+#include "time.h"
+#include "timeout.h"
#include "useful.h"
#include "tester.h"
--
2.26.2
3 weeks, 3 days
[PATCH] tester: Add missing include for useful.h
by Inga Stotland
---
ell/tester.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/ell/tester.c b/ell/tester.c
index 5c5386e..ebebc1d 100644
--- a/ell/tester.c
+++ b/ell/tester.c
@@ -35,6 +35,7 @@
#include "log.h"
#include "private.h"
+#include "useful.h"
#include "tester.h"
/**
--
2.26.2
3 weeks, 4 days
[PATCH v2] [RFC PATCH] tester: Add framework for serialized test execution
by Inga Stotland
This adds a framework for running series of noninteractive tests
that are capable of executing more complex scenarios than a usual unit
test.
The framework enables staging of a test case processing:
- test pre-setup: can be used to prepare an initial testing environment
potentially common to between several test cases, e.g.,
put an emulator in a certain well known state.
- test setup: used to setup conditions specific for the test case
- test run: execute actual test case
- test teardown: Cleans up after test setup
- test post-teardown: Cleans up after test pre-setup
Additional command line options allow to run a selected group of tests
based either on a group prefix of the test name or a substring in test
description.
---
Makefile.am | 6 +-
ell/ell.h | 1 +
ell/ell.sym | 21 ++
ell/tester.c | 780 +++++++++++++++++++++++++++++++++++++++++++++++++++
ell/tester.h | 97 +++++++
5 files changed, 903 insertions(+), 2 deletions(-)
create mode 100644 ell/tester.c
create mode 100644 ell/tester.h
diff --git a/Makefile.am b/Makefile.am
index 2f9a4ce..4c65e9d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -58,7 +58,8 @@ pkginclude_HEADERS = ell/ell.h \
ell/gpio.h \
ell/path.h \
ell/icmp6.h \
- ell/acd.h
+ ell/acd.h \
+ ell/tester.h
lib_LTLIBRARIES = ell/libell.la
@@ -141,7 +142,8 @@ ell_libell_la_SOURCES = $(linux_headers) \
ell/path.c \
ell/icmp6.c \
ell/icmp6-private.h \
- ell/acd.c
+ ell/acd.c \
+ ell/tester.c
ell_libell_la_LDFLAGS = -Wl,--no-undefined \
-Wl,--version-script=$(top_srcdir)/ell/ell.sym \
diff --git a/ell/ell.h b/ell/ell.h
index 22fddf7..d47d5cf 100644
--- a/ell/ell.h
+++ b/ell/ell.h
@@ -64,3 +64,4 @@
#include <ell/gpio.h>
#include <ell/path.h>
#include <ell/acd.h>
+#include <ell/tester.h>
diff --git a/ell/ell.sym b/ell/ell.sym
index 9938216..606513c 100644
--- a/ell/ell.sym
+++ b/ell/ell.sym
@@ -665,6 +665,27 @@ global:
l_acd_set_debug;
l_acd_set_skip_probes;
l_acd_set_defend_policy;
+ /* tester */
+ l_tester_new;
+ l_tester_destroy;
+ l_tester_start;
+ l_tester_summarize;
+ l_tester_test_add;
+ l_tester_test_add_full;
+ l_tester_test_get_stage;
+ l_tester_get_data;
+ l_tester_pre_setup_complete;
+ l_tester_pre_setup_failed;
+ l_tester_setup_complete;
+ l_tester_setup_failed;
+ l_tester_test_passed;
+ l_tester_test_failed;
+ l_tester_test_abort;
+ l_tester_teardown_complete;
+ l_tester_teardown_failed;
+ l_tester_post_teardown_complete;
+ l_tester_post_teardown_failed;
+ l_tester_wait;
local:
*;
};
diff --git a/ell/tester.c b/ell/tester.c
new file mode 100644
index 0000000..5c5386e
--- /dev/null
+++ b/ell/tester.c
@@ -0,0 +1,780 @@
+/*
+ *
+ * Embedded library
+ *
+ * Copyright (C) 2021 Intel Corporation. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+
+#include <sys/time.h>
+
+#include <ell/ell.h>
+
+#include "log.h"
+#include "private.h"
+#include "tester.h"
+
+/**
+ * SECTION:tester
+ * @short_description: Non-interactive test framework
+ *
+ * Non-interactive test framework
+ */
+
+#define COLOR_OFF "\x1B[0m"
+#define COLOR_BLACK "\x1B[0;30m"
+#define COLOR_RED "\x1B[0;31m"
+#define COLOR_GREEN "\x1B[0;32m"
+#define COLOR_YELLOW "\x1B[0;33m"
+#define COLOR_BLUE "\x1B[0;34m"
+#define COLOR_MAGENTA "\x1B[0;35m"
+#define COLOR_HIGHLIGHT "\x1B[1;39m"
+
+#define print_text(color, fmt, args...) \
+ l_info(color fmt COLOR_OFF, ## args)
+
+#define print_summary(label, color, value, fmt, args...) \
+ l_info("%-52s " color "%-10s" COLOR_OFF fmt, \
+ label, value, ## args)
+
+#define print_progress(name, color, fmt, args...) \
+ l_info(COLOR_HIGHLIGHT "%s" COLOR_OFF " - " \
+ color fmt COLOR_OFF, name, ## args)
+
+enum test_result {
+ TEST_RESULT_NOT_RUN,
+ TEST_RESULT_PASSED,
+ TEST_RESULT_FAILED,
+ TEST_RESULT_TIMED_OUT,
+};
+
+struct l_tester {
+ uint64_t start_time;
+ struct l_queue *tests;
+ const struct l_queue_entry *test_entry;
+ bool list_cases;
+ const char *prefix;
+ const char *substring;
+ l_tester_finish_func_t finish_callback;
+};
+
+struct test_case {
+ uint64_t start_time;
+ uint64_t end_time;
+ char *name;
+ enum test_result result;
+ enum l_tester_stage stage;
+ const void *test_data;
+ l_tester_data_func_t pre_setup_func;
+ l_tester_data_func_t setup_func;
+ l_tester_data_func_t test_func;
+ l_tester_data_func_t teardown_func;
+ l_tester_data_func_t post_teardown_func;
+ unsigned int timeout;
+ struct l_timeout *run_timer;
+ l_tester_destroy_func_t destroy;
+ void *user_data;
+ bool teardown;
+};
+
+static void destroy_test(void *data)
+{
+ struct test_case *test = data;
+
+ l_timeout_remove(test->run_timer);
+
+ if (test->destroy)
+ test->destroy(test->user_data);
+
+ l_free(test->name);
+ l_free(test);
+}
+
+static uint64_t get_elapsed_time(uint64_t base)
+{
+ uint64_t now;
+
+ now = l_time_now();
+
+ return l_time_diff(base, now);
+}
+
+static void teardown_callback(void *user_data)
+{
+ struct l_tester *tester = user_data;
+ struct test_case *test;
+
+ printf("teardown %p\n", tester->test_entry);
+
+ test = tester->test_entry->data;
+ printf("teardown data %p\n", tester->test_entry->data);
+ test->stage = L_TESTER_STAGE_TEARDOWN;
+ test->teardown = false;
+
+ printf("Test name %p\n", test->name);
+ printf("Test name %s\n", test->name);
+ print_progress(test->name, COLOR_MAGENTA, "teardown");
+ printf("teardown\n");
+
+
+ if (test->teardown_func)
+ test->teardown_func(test->test_data);
+ else
+ l_tester_teardown_complete(tester);
+}
+
+static void test_timeout(struct l_timeout *timer, void *user_data)
+{
+ struct l_tester *tester = user_data;
+ struct test_case *test;
+
+ test = tester->test_entry->data;
+
+ l_timeout_remove(timer);
+ test->run_timer = NULL;
+
+ test->result = TEST_RESULT_TIMED_OUT;
+ print_progress(test->name, COLOR_RED, "test timed out");
+
+ l_idle_oneshot(teardown_callback, tester, NULL);
+}
+
+static void next_test_case(struct l_tester *tester)
+{
+ struct test_case *test;
+
+ if (tester->test_entry)
+ tester->test_entry = tester->test_entry->next;
+ else
+ tester->test_entry = l_queue_get_entries(tester->tests);
+
+ if (!tester->test_entry) {
+ if (tester->finish_callback)
+ tester->finish_callback(tester);
+ return;
+ }
+
+ test = tester->test_entry->data;
+
+ print_progress(test->name, COLOR_BLACK, "init");
+
+ test->start_time = get_elapsed_time(tester->start_time);
+
+ if (test->timeout > 0)
+ test->run_timer = l_timeout_create(test->timeout, test_timeout,
+ test, NULL);
+
+ test->stage = L_TESTER_STAGE_PRE_SETUP;
+
+ if (test->pre_setup_func)
+ test->pre_setup_func(test->test_data);
+ else
+ l_tester_pre_setup_complete(tester);
+}
+
+static void setup_callback(void *user_data)
+{
+ struct l_tester *tester = user_data;
+ struct test_case *test = tester->test_entry->data;
+
+ test->stage = L_TESTER_STAGE_SETUP;
+
+ print_progress(test->name, COLOR_BLUE, "setup");
+
+ if (test->setup_func)
+ test->setup_func(test->test_data);
+ else
+ l_tester_setup_complete(tester);
+}
+
+static void run_callback(void *user_data)
+{
+ struct l_tester *tester = user_data;
+ struct test_case *test = tester->test_entry->data;
+
+ test->stage = L_TESTER_STAGE_RUN;
+
+ print_progress(test->name, COLOR_BLACK, "run");
+ test->test_func(test->test_data);
+}
+
+static void done_callback(void *user_data)
+{
+ struct l_tester *tester = user_data;
+ struct test_case *test = tester->test_entry->data;
+
+ test->end_time = get_elapsed_time(tester->start_time);
+
+ print_progress(test->name, COLOR_BLACK, "done");
+ next_test_case(tester);
+}
+
+LIB_EXPORT void *l_tester_get_data(struct l_tester *tester)
+{
+ struct test_case *test;
+
+ if (unlikely(!tester))
+ return NULL;
+
+ if (!tester->test_entry)
+ return NULL;
+
+ test = tester->test_entry->data;
+
+ return test->user_data;
+}
+
+LIB_EXPORT void l_tester_pre_setup_complete(struct l_tester *tester)
+{
+ struct test_case *test;
+
+ if (unlikely(!tester))
+ return;
+
+ if (!tester->test_entry)
+ return;
+
+ test = tester->test_entry->data;
+
+ if (test->stage != L_TESTER_STAGE_PRE_SETUP)
+ return;
+
+ l_idle_oneshot(setup_callback, tester, NULL);
+}
+
+LIB_EXPORT void l_tester_pre_setup_failed(struct l_tester *tester)
+{
+ struct test_case *test;
+
+ if (unlikely(!tester))
+ return;
+
+ if (!tester->test_entry)
+ return;
+
+ test = tester->test_entry->data;
+
+ if (test->stage != L_TESTER_STAGE_PRE_SETUP)
+ return;
+
+ print_progress(test->name, COLOR_RED, "pre setup failed");
+
+ l_idle_oneshot(done_callback, tester, NULL);
+}
+
+LIB_EXPORT void l_tester_setup_complete(struct l_tester *tester)
+{
+ struct test_case *test;
+
+ if (unlikely(!tester))
+ return;
+
+ if (!tester->test_entry)
+ return;
+
+ test = tester->test_entry->data;
+
+ if (test->stage != L_TESTER_STAGE_SETUP)
+ return;
+
+ print_progress(test->name, COLOR_BLUE, "setup complete");
+
+ l_idle_oneshot(run_callback, tester, NULL);
+}
+
+LIB_EXPORT void l_tester_setup_failed(struct l_tester *tester)
+{
+ struct test_case *test;
+
+ if (unlikely(!tester))
+ return;
+
+ if (!tester->test_entry)
+ return;
+
+ test = tester->test_entry->data;
+
+ if (test->stage != L_TESTER_STAGE_SETUP)
+ return;
+
+ test->stage = L_TESTER_STAGE_POST_TEARDOWN;
+
+ l_timeout_remove(test->run_timer);
+ test->run_timer = NULL;
+
+ print_progress(test->name, COLOR_RED, "setup failed");
+ print_progress(test->name, COLOR_MAGENTA, "teardown");
+
+ test->post_teardown_func(test->test_data);
+}
+
+static void test_result(struct l_tester *tester, enum test_result result)
+{
+ struct test_case *test;
+
+ if (unlikely(!tester))
+ return;
+
+ if (!tester->test_entry)
+ return;
+
+ test = tester->test_entry->data;
+
+ if (test->stage != L_TESTER_STAGE_RUN)
+ return;
+
+ l_timeout_remove(test->run_timer);
+ test->run_timer = NULL;
+
+ test->result = result;
+ switch (result) {
+ case TEST_RESULT_PASSED:
+ print_progress(test->name, COLOR_GREEN, "test passed");
+ break;
+ case TEST_RESULT_FAILED:
+ print_progress(test->name, COLOR_RED, "test failed");
+ break;
+ case TEST_RESULT_NOT_RUN:
+ print_progress(test->name, COLOR_YELLOW, "test not run");
+ break;
+ case TEST_RESULT_TIMED_OUT:
+ print_progress(test->name, COLOR_RED, "test timed out");
+ break;
+ }
+
+ if (test->teardown)
+ return;
+
+ test->teardown = true;
+
+ l_idle_oneshot(teardown_callback, tester, NULL);
+}
+
+LIB_EXPORT void l_tester_test_passed(struct l_tester *tester)
+{
+ if (unlikely(!tester))
+ return;
+
+ test_result(tester, TEST_RESULT_PASSED);
+}
+
+LIB_EXPORT void l_tester_test_failed(struct l_tester *tester)
+{
+ if (unlikely(!tester))
+ return;
+
+ test_result(tester, TEST_RESULT_FAILED);
+}
+
+LIB_EXPORT void l_tester_test_abort(struct l_tester *tester)
+{
+ if (unlikely(!tester))
+ return;
+
+ test_result(tester, TEST_RESULT_NOT_RUN);
+}
+
+LIB_EXPORT void l_tester_teardown_complete(struct l_tester *tester)
+{
+ struct test_case *test;
+
+ if (unlikely(!tester))
+ return;
+
+ if (!tester->test_entry)
+ return;
+
+ test = tester->test_entry->data;
+
+ if (test->stage != L_TESTER_STAGE_TEARDOWN)
+ return;
+
+ test->stage = L_TESTER_STAGE_POST_TEARDOWN;
+
+ if (test->post_teardown_func)
+ test->post_teardown_func(test->test_data);
+ else
+ l_tester_post_teardown_complete(tester);
+}
+
+LIB_EXPORT void l_tester_teardown_failed(struct l_tester *tester)
+{
+ struct test_case *test;
+
+ if (unlikely(!tester))
+ return;
+
+ if (!tester->test_entry)
+ return;
+
+ test = tester->test_entry->data;
+
+ if (test->stage != L_TESTER_STAGE_TEARDOWN)
+ return;
+
+ test->stage = L_TESTER_STAGE_POST_TEARDOWN;
+
+ l_tester_post_teardown_failed(tester);
+}
+
+LIB_EXPORT void l_tester_post_teardown_complete(struct l_tester *tester)
+{
+ struct test_case *test;
+
+ if (unlikely(!tester))
+ return;
+
+ if (!tester->test_entry)
+ return;
+
+ test = tester->test_entry->data;
+
+ if (test->stage != L_TESTER_STAGE_POST_TEARDOWN)
+ return;
+
+ print_progress(test->name, COLOR_MAGENTA, "teardown complete");
+
+ l_idle_oneshot(done_callback, tester, NULL);
+}
+
+LIB_EXPORT void l_tester_post_teardown_failed(struct l_tester *tester)
+{
+ struct test_case *test;
+
+ if (unlikely(!tester))
+ return;
+
+ if (!tester->test_entry)
+ return;
+
+ test = tester->test_entry->data;
+
+ if (test->stage != L_TESTER_STAGE_POST_TEARDOWN)
+ return;
+
+ print_progress(test->name, COLOR_RED, "teardown failed");
+
+ l_idle_oneshot(done_callback, tester, NULL);
+}
+
+struct wait_data {
+ unsigned int seconds;
+ struct test_case *test;
+ l_tester_wait_func_t func;
+ void *user_data;
+};
+
+static void wait_callback(struct l_timeout *timer, void *user_data)
+{
+ struct wait_data *wait = user_data;
+ struct test_case *test = wait->test;
+
+ wait->seconds--;
+
+ if (wait->seconds > 0) {
+ print_progress(test->name, COLOR_BLACK, "%u seconds left",
+ wait->seconds);
+ return;
+ }
+
+ print_progress(test->name, COLOR_BLACK, "waiting done");
+
+ wait->func(wait->user_data);
+
+ free(wait);
+
+ l_timeout_remove(timer);
+}
+
+LIB_EXPORT void l_tester_wait(struct l_tester *tester, unsigned int seconds,
+ l_tester_wait_func_t func, void *user_data)
+{
+ struct test_case *test;
+ struct wait_data *wait;
+
+ if (unlikely(!tester))
+ return;
+
+ if (!func || seconds < 1)
+ return;
+
+ if (!tester->test_entry)
+ return;
+
+ test = tester->test_entry->data;
+
+ wait = l_new(struct wait_data, 1);
+ wait->seconds = seconds;
+ wait->test = test;
+ wait->func = func;
+ wait->user_data = user_data;
+
+ l_timeout_create(seconds, wait_callback, wait, NULL);
+
+ print_progress(test->name, COLOR_BLACK, "waiting %u seconds", seconds);
+}
+
+/**
+ * l_tester_add_full:
+ * @tester: tester instance
+ * @name: test case name
+ * @test_data: test data
+ * @pre_setup_func: test pre-setup function
+ * @setup_func: test setup function
+ * @test_func: test function
+ * @teardown_func: test teardown function
+ * @teardown_func: test post-teardown function
+ * @timeout: test teardown function
+ * @user_data: user data
+ * @destroy: user data destroy function
+ *
+ * Add a new test case.
+ **/
+LIB_EXPORT void l_tester_add_full(struct l_tester *tester, const char *name,
+ const void *test_data,
+ l_tester_data_func_t pre_setup_func,
+ l_tester_data_func_t setup_func,
+ l_tester_data_func_t test_func,
+ l_tester_data_func_t teardown_func,
+ l_tester_data_func_t post_teardown_func,
+ unsigned int timeout,
+ void *user_data,
+ l_tester_destroy_func_t destroy)
+{
+ struct test_case *test;
+
+ if (unlikely(!tester || !test_func))
+ return;
+
+ if (tester->prefix && !l_str_has_prefix(name, tester->prefix)) {
+ if (destroy)
+ destroy(user_data);
+ return;
+ }
+
+ if (tester->substring && !strstr(name, tester->substring)) {
+ if (destroy)
+ destroy(user_data);
+ return;
+ }
+
+ if (tester->list_cases) {
+ l_info("%s", name);
+
+ if (destroy)
+ destroy(user_data);
+ return;
+ }
+
+ test = l_new(struct test_case, 1);
+ test->name = l_strdup(name);
+ test->result = TEST_RESULT_NOT_RUN;
+ test->stage = L_TESTER_STAGE_INVALID;
+
+ test->test_data = test_data;
+ test->pre_setup_func = pre_setup_func;
+ test->setup_func = setup_func;
+ test->test_func = test_func;
+ test->teardown_func = teardown_func;
+ test->post_teardown_func = post_teardown_func;
+ test->timeout = timeout;
+ test->destroy = destroy;
+ test->user_data = user_data;
+
+ l_queue_push_tail(tester->tests, test);
+}
+
+/**
+ * l_tester_add:
+ * @tester: tester instance
+ * @name: test case name
+ * @test_data: test data
+ * @setup_func: test setup function
+ * @test_func: test function
+ * @teardown_func: test teardown function
+ *
+ * Add a new test with default settings for timeout and no pre-setup procedure.
+ **/
+LIB_EXPORT void l_tester_add(struct l_tester *tester, const char *name,
+ const void *test_data,
+ l_tester_data_func_t setup_func,
+ l_tester_data_func_t test_func,
+ l_tester_data_func_t teardown_func)
+{
+ l_tester_add_full(tester, name, test_data, NULL, setup_func, test_func,
+ teardown_func, NULL, 0, NULL, NULL);
+}
+
+/**
+ * l_tester_new:
+ *
+ * Initialize tester framework.
+ *
+ * Returns: new tester instance
+ **/
+LIB_EXPORT struct l_tester *l_tester_new(const char *prefix,
+ const char *substring, bool list_cases)
+{
+ struct l_tester *tester = l_new(struct l_tester, 1);
+
+ tester->prefix = prefix;
+ tester->substring = substring;
+ tester->list_cases = list_cases;
+ tester->tests = l_queue_new();
+ return tester;
+}
+
+/**
+ * l_tester_start:
+ * @tester: tester instance
+ *
+ * Kick off execution of the test queue
+ *
+ **/
+LIB_EXPORT void l_tester_start(struct l_tester *tester,
+ l_tester_finish_func_t finish_func)
+{
+ if (unlikely(!tester))
+ return;
+
+ if (!tester->tests)
+ return;
+
+ tester->finish_callback = finish_func;
+
+ tester->start_time = l_time_now();
+ next_test_case(tester);
+}
+
+/**
+ * l_tester_summarize:
+ * @tester: tester instance
+ *
+ * Print summary of all added test cases.
+ *
+ * Returns: true, if all the tests passed
+ * false, if any of the tests failed
+ **/
+LIB_EXPORT bool l_tester_summarize(struct l_tester *tester)
+{
+ unsigned int not_run = 0, passed = 0, failed = 0;
+ double execution_time;
+ const struct l_queue_entry *entry;
+
+ if (unlikely(!tester))
+ return false;
+
+ l_info(COLOR_HIGHLIGHT "%s" COLOR_OFF,
+ "\n\nTest Summary\n------------");
+
+ entry = l_queue_get_entries(tester->tests);
+
+ for (; entry; entry = entry->next) {
+ struct test_case *test = entry->data;
+ double exec_time;
+
+ exec_time = (test->end_time - test->start_time) /
+ (double)L_USEC_PER_SEC;
+
+ switch (test->result) {
+ case TEST_RESULT_NOT_RUN:
+ print_summary(test->name, COLOR_YELLOW, "Not Run", "");
+ not_run++;
+ break;
+ case TEST_RESULT_PASSED:
+ print_summary(test->name, COLOR_GREEN, "Passed",
+ "%8.3f seconds", exec_time);
+ passed++;
+ break;
+ case TEST_RESULT_FAILED:
+ print_summary(test->name, COLOR_RED, "Failed",
+ "%8.3f seconds", exec_time);
+ failed++;
+ break;
+ case TEST_RESULT_TIMED_OUT:
+ print_summary(test->name, COLOR_RED, "Timed out",
+ "%8.3f seconds", exec_time);
+ failed++;
+ break;
+ }
+ }
+
+ l_info("Total: %d, "
+ COLOR_GREEN "Passed: %d (%.1f%%)" COLOR_OFF ", "
+ COLOR_RED "Failed: %d" COLOR_OFF ", "
+ COLOR_YELLOW "Not Run: %d" COLOR_OFF,
+ not_run + passed + failed, passed,
+ (not_run + passed + failed) ?
+ (float) passed * 100 / (not_run + passed + failed) : 0,
+ failed, not_run);
+
+ execution_time = get_elapsed_time(tester->start_time);
+
+ l_info("Overall execution time: %8.3f seconds",
+ execution_time / (double)L_USEC_PER_SEC);
+
+ return failed;
+}
+
+/**
+ * l_tester_destroy:
+ * @tester: tester instance
+ *
+ * Free up the teter framework resources
+ *
+ **/
+LIB_EXPORT void l_tester_destroy(struct l_tester *tester)
+{
+ if (unlikely(!tester))
+ return;
+
+ l_queue_destroy(tester->tests, destroy_test);
+ l_free(tester);
+}
+
+/**
+ * l_tester_get_stage:
+ * @tester: tester instance
+ *
+ * Get the current test stage
+ *
+ * Returns: the stage of the current test that is being processing.
+ *
+ **/
+LIB_EXPORT enum l_tester_stage l_tester_get_stage(struct l_tester *tester)
+{
+ struct test_case *test;
+
+ if (unlikely(!tester))
+ return L_TESTER_STAGE_INVALID;
+
+ if (!tester->test_entry)
+ return L_TESTER_STAGE_INVALID;
+
+ test = tester->test_entry->data;
+
+ return test->stage;
+}
diff --git a/ell/tester.h b/ell/tester.h
new file mode 100644
index 0000000..b5aa1be
--- /dev/null
+++ b/ell/tester.h
@@ -0,0 +1,97 @@
+/*
+ *
+ * Embedded Linux library
+ *
+ * Copyright (C) 2021 Intel Corporation. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __ELL_TESTER_H
+#define __ELL_TESTER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+struct l_tester;
+
+enum l_tester_stage {
+ L_TESTER_STAGE_INVALID,
+ L_TESTER_STAGE_PRE_SETUP,
+ L_TESTER_STAGE_SETUP,
+ L_TESTER_STAGE_RUN,
+ L_TESTER_STAGE_TEARDOWN,
+ L_TESTER_STAGE_POST_TEARDOWN,
+};
+
+typedef void (*l_tester_destroy_func_t)(void *user_data);
+typedef void (*l_tester_data_func_t)(const void *test_data);
+typedef void (*l_tester_finish_func_t)(struct l_tester *tester);
+typedef void (*l_tester_wait_func_t)(void *user_data);
+
+struct l_tester *l_tester_new(const char *prefix, const char *substring,
+ bool list_cases);
+void l_tester_destroy(struct l_tester *tester);
+void l_tester_start(struct l_tester *tester,
+ l_tester_finish_func_t finish_func);
+
+bool l_tester_summarize(struct l_tester *tester);
+
+void l_tester_add_full(struct l_tester *tester, const char *name,
+ const void *test_data,
+ l_tester_data_func_t pre_setup_func,
+ l_tester_data_func_t setup_func,
+ l_tester_data_func_t test_func,
+ l_tester_data_func_t teardown_func,
+ l_tester_data_func_t post_teardown_func,
+ unsigned int timeout,
+ void *user_data,
+ l_tester_destroy_func_t destroy);
+
+void l_tester_add(struct l_tester *tester, const char *name,
+ const void *test_data,
+ l_tester_data_func_t setup_func,
+ l_tester_data_func_t test_func,
+ l_tester_data_func_t teardown_func);
+
+void l_tester_pre_setup_complete(struct l_tester *tester);
+void l_tester_pre_setup_failed(struct l_tester *tester);
+void l_tester_setup_complete(struct l_tester *tester);
+void l_tester_setup_failed(struct l_tester *tester);
+void l_tester_test_passed(struct l_tester *tester);
+void l_tester_test_failed(struct l_tester *tester);
+void l_tester_test_abort(struct l_tester *tester);
+void l_tester_teardown_complete(struct l_tester *tester);
+void l_tester_teardown_failed(struct l_tester *tester);
+void l_tester_post_teardown_complete(struct l_tester *tester);
+void l_tester_post_teardown_failed(struct l_tester *tester);
+
+enum l_tester_stage l_tester_get_stage(struct l_tester *tester);
+void *l_tester_get_data(struct l_tester *tester);
+
+void l_tester_wait(struct l_tester *tester, unsigned int seconds,
+ l_tester_wait_func_t func, void *user_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ELL_TESTER_H */
--
2.26.2
3 weeks, 5 days
[PATCH] genl: Return false, not NULL, for boolean return.
by Ossama Othman
From: Ossama Othman <ossama.othman(a)gmail.com>
The return type of the l_genl_request_family() function is bool, not a
pointer. Return false instead of NULL to match the return type.
---
ell/genl.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/ell/genl.c b/ell/genl.c
index 7e3f800..97457cf 100644
--- a/ell/genl.c
+++ b/ell/genl.c
@@ -1538,11 +1538,11 @@ LIB_EXPORT bool l_genl_request_family(struct l_genl *genl, const char *name,
struct family_request *req;
if (unlikely(!genl) || unlikely(!name))
- return NULL;
+ return false;
len = strlen(name);
if (unlikely(strlen(name) >= GENL_NAMSIZ))
- return NULL;
+ return false;
req = l_new(struct family_request, 1);
req->appeared_func = appeared_func;
--
2.25.1
4 weeks, 1 day
[PATCH] pem: Write encrypted flag in load_container with .crt format
by Andrew Zaborowski
Make sure a value is written to *out_encrypted whenever
l_cert_load_container_file() returns success. Specifically we were
forgetting to do this with raw DER-encoded certificates. Fixes the
following valgrind warning (sometimes test failure) in test-pem:
==1142== Conditional jump or move depends on uninitialised value(s)
==1142== at 0x10B427: test_load_file (test-pem.c:369)
==1142== by 0x10CF53: l_test_run (test.c:83)
==1142== by 0x10BC19: main (test-pem.c:523)
==1142==
test-pem: unit/test-pem.c:369: test_load_file: Assertion `encrypted == params->expect_encrypted' failed.
---
ell/cert.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/ell/cert.c b/ell/cert.c
index 8463a1d..c489f60 100644
--- a/ell/cert.c
+++ b/ell/cert.c
@@ -1413,6 +1413,9 @@ static int cert_try_load_der_format(const uint8_t *content, size_t content_len,
if (out_privkey)
*out_privkey = NULL;
+ if (out_encrypted)
+ *out_encrypted = false;
+
return 0;
}
--
2.27.0
1 month
[PATCH] [RFC PATCH] tester: Add framework for serialized test execution
by Inga Stotland
This adds a framework for running series of noninteractive tests
that are capable of executing more complex scenarios than a usual unit
test.
The framework enables staging of a test case processing:
- test pre-setup: can be used to prepare an initial testing environment
potentially common to between several test cases, e.g.,
put an emulator in a certain well known state.
- test setup: used to setup conditions specific for the test case
- test run: execute actual test case
- test teardown: Cleans up after test setup
- test post-teardown: Cleans up after test pre-setup
Additional command line options allow to run a selected group of tests
based either on a group prefix of the test name or a substring in test
description.
---
Makefile.am | 6 +-
ell/tester.c | 786 +++++++++++++++++++++++++++++++++++++++++++++++++++
ell/tester.h | 94 ++++++
3 files changed, 884 insertions(+), 2 deletions(-)
create mode 100644 ell/tester.c
create mode 100644 ell/tester.h
diff --git a/Makefile.am b/Makefile.am
index 2f9a4ce..4c65e9d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -58,7 +58,8 @@ pkginclude_HEADERS = ell/ell.h \
ell/gpio.h \
ell/path.h \
ell/icmp6.h \
- ell/acd.h
+ ell/acd.h \
+ ell/tester.h
lib_LTLIBRARIES = ell/libell.la
@@ -141,7 +142,8 @@ ell_libell_la_SOURCES = $(linux_headers) \
ell/path.c \
ell/icmp6.c \
ell/icmp6-private.h \
- ell/acd.c
+ ell/acd.c \
+ ell/tester.c
ell_libell_la_LDFLAGS = -Wl,--no-undefined \
-Wl,--version-script=$(top_srcdir)/ell/ell.sym \
diff --git a/ell/tester.c b/ell/tester.c
new file mode 100644
index 0000000..887dee9
--- /dev/null
+++ b/ell/tester.c
@@ -0,0 +1,786 @@
+/*
+ *
+ * Embedded library
+ *
+ * Copyright (C) 2021 Intel Corporation. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+
+#include <sys/time.h>
+
+#include <ell/ell.h>
+
+#include "log.h"
+#include "private.h"
+#include "tester.h"
+
+/**
+ * SECTION:tester
+ * @short_description: Non-interactive test framework
+ *
+ * Non-interactive test framework
+ */
+
+#define COLOR_OFF "\x1B[0m"
+#define COLOR_BLACK "\x1B[0;30m"
+#define COLOR_RED "\x1B[0;31m"
+#define COLOR_GREEN "\x1B[0;32m"
+#define COLOR_YELLOW "\x1B[0;33m"
+#define COLOR_BLUE "\x1B[0;34m"
+#define COLOR_MAGENTA "\x1B[0;35m"
+#define COLOR_HIGHLIGHT "\x1B[1;39m"
+
+#define print_text(color, fmt, args...) \
+ l_info(color fmt COLOR_OFF, ## args)
+
+#define print_summary(label, color, value, fmt, args...) \
+ l_info("%-52s " color "%-10s" COLOR_OFF fmt, \
+ label, value, ## args)
+
+#define print_progress(name, color, fmt, args...) \
+ l_info(COLOR_HIGHLIGHT "%s" COLOR_OFF " - " \
+ color fmt COLOR_OFF, name, ## args)
+
+enum test_result {
+ TEST_RESULT_NOT_RUN,
+ TEST_RESULT_PASSED,
+ TEST_RESULT_FAILED,
+ TEST_RESULT_TIMED_OUT,
+};
+
+struct test_case {
+ char *name;
+ enum test_result result;
+ enum l_tester_stage stage;
+ const void *test_data;
+ l_tester_data_func_t pre_setup_func;
+ l_tester_data_func_t setup_func;
+ l_tester_data_func_t test_func;
+ l_tester_data_func_t teardown_func;
+ l_tester_data_func_t post_teardown_func;
+ double start_time;
+ double end_time;
+ unsigned int timeout;
+ struct l_timeout *run_timer;
+ l_tester_destroy_func_t destroy;
+ void *user_data;
+ bool teardown;
+};
+
+static char *tester_name;
+
+static struct l_queue *test_list;
+static const struct l_queue_entry *test_entry;
+static struct timeval tester_start;
+
+static bool option_list;
+static const char *option_prefix;
+static const char *option_string;
+
+static bool terminated;
+
+static void test_destroy(void *data)
+{
+ struct test_case *test = data;
+
+ l_timeout_remove(test->run_timer);
+
+ if (test->destroy)
+ test->destroy(test->user_data);
+
+ l_free(test->name);
+ l_free(test);
+}
+
+static void default_pre_setup(const void *test_data)
+{
+ l_tester_pre_setup_complete();
+}
+
+static void default_setup(const void *test_data)
+{
+ l_tester_setup_complete();
+}
+
+static void default_teardown(const void *test_data)
+{
+ l_tester_teardown_complete();
+}
+
+static void default_post_teardown(const void *test_data)
+{
+ l_tester_post_teardown_complete();
+}
+
+void *l_tester_get_data(void)
+{
+ struct test_case *test;
+
+ if (!test_entry)
+ return NULL;
+
+ test = test_entry->data;
+
+ return test->user_data;
+}
+
+static double get_elapsed_time(struct timeval *base)
+{
+ static struct timeval now, elapsed;
+
+ gettimeofday(&now, NULL);
+ timersub(&now, base, &elapsed);
+
+ return elapsed.tv_sec + ((double) elapsed.tv_usec) / 1000000;
+}
+
+static int tester_summarize(void)
+{
+ unsigned int not_run = 0, passed = 0, failed = 0;
+ double execution_time;
+ const struct l_queue_entry *entry;
+
+ l_info(COLOR_HIGHLIGHT "%s" COLOR_OFF,
+ "\n\nTest Summary\n------------");
+
+ entry = l_queue_get_entries(test_list);
+
+ for (; entry; entry = entry->next) {
+ struct test_case *test = entry->data;
+ double exec_time;
+
+ exec_time = test->end_time - test->start_time;
+
+ switch (test->result) {
+ case TEST_RESULT_NOT_RUN:
+ print_summary(test->name, COLOR_YELLOW, "Not Run", "");
+ not_run++;
+ break;
+ case TEST_RESULT_PASSED:
+ print_summary(test->name, COLOR_GREEN, "Passed",
+ "%8.3f seconds", exec_time);
+ passed++;
+ break;
+ case TEST_RESULT_FAILED:
+ print_summary(test->name, COLOR_RED, "Failed",
+ "%8.3f seconds", exec_time);
+ failed++;
+ break;
+ case TEST_RESULT_TIMED_OUT:
+ print_summary(test->name, COLOR_RED, "Timed out",
+ "%8.3f seconds", exec_time);
+ failed++;
+ break;
+ }
+ }
+
+ l_info("Total: %d, "
+ COLOR_GREEN "Passed: %d (%.1f%%)" COLOR_OFF ", "
+ COLOR_RED "Failed: %d" COLOR_OFF ", "
+ COLOR_YELLOW "Not Run: %d" COLOR_OFF,
+ not_run + passed + failed, passed,
+ (not_run + passed + failed) ?
+ (float) passed * 100 / (not_run + passed + failed) : 0,
+ failed, not_run);
+
+ execution_time = get_elapsed_time(&tester_start);
+ l_info("Overall execution time: %.3g seconds", execution_time);
+
+ return failed;
+}
+
+static void teardown_callback(void *user_data)
+{
+ struct test_case *test = user_data;
+
+ test->stage = L_TESTER_STAGE_TEARDOWN;
+ test->teardown = false;
+
+ print_progress(test->name, COLOR_MAGENTA, "teardown");
+ test->teardown_func(test->test_data);
+}
+
+static void test_timeout(struct l_timeout *timer, void *user_data)
+{
+ struct test_case *test = user_data;
+
+ l_timeout_remove(timer);
+ test->run_timer = NULL;
+
+ test->result = TEST_RESULT_TIMED_OUT;
+ print_progress(test->name, COLOR_RED, "test timed out");
+
+ l_idle_oneshot(teardown_callback, test, NULL);
+}
+
+static void next_test_case(void)
+{
+ struct test_case *test;
+
+ if (test_entry)
+ test_entry = test_entry->next;
+ else
+ test_entry = l_queue_get_entries(test_list);
+
+ if (!test_entry) {
+ l_main_quit();
+ return;
+ }
+
+ test = test_entry->data;
+
+ l_info("");
+ print_progress(test->name, COLOR_BLACK, "init");
+
+ test->start_time = get_elapsed_time(&tester_start);
+
+ if (test->timeout > 0)
+ test->run_timer = l_timeout_create(test->timeout, test_timeout,
+ test, NULL);
+
+ test->stage = L_TESTER_STAGE_PRE_SETUP;
+
+ test->pre_setup_func(test->test_data);
+}
+
+static void setup_callback(void *user_data)
+{
+ struct test_case *test = user_data;
+
+ test->stage = L_TESTER_STAGE_SETUP;
+
+ print_progress(test->name, COLOR_BLUE, "setup");
+ test->setup_func(test->test_data);
+}
+
+static void run_callback(void *user_data)
+{
+ struct test_case *test = user_data;
+
+ test->stage = L_TESTER_STAGE_RUN;
+
+ print_progress(test->name, COLOR_BLACK, "run");
+ test->test_func(test->test_data);
+}
+
+static void done_callback(void *user_data)
+{
+ struct test_case *test = user_data;
+
+ test->end_time = get_elapsed_time(&tester_start);
+
+ print_progress(test->name, COLOR_BLACK, "done");
+ next_test_case();
+}
+
+LIB_EXPORT void l_tester_pre_setup_complete(void)
+{
+ struct test_case *test;
+
+ if (!test_entry)
+ return;
+
+ test = test_entry->data;
+
+ if (test->stage != L_TESTER_STAGE_PRE_SETUP)
+ return;
+
+ l_idle_oneshot(setup_callback, test, NULL);
+}
+
+LIB_EXPORT void l_tester_pre_setup_failed(void)
+{
+ struct test_case *test;
+
+ if (!test_entry)
+ return;
+
+ test = test_entry->data;
+
+ if (test->stage != L_TESTER_STAGE_PRE_SETUP)
+ return;
+
+ print_progress(test->name, COLOR_RED, "pre setup failed");
+
+ l_idle_oneshot(done_callback, test, NULL);
+}
+
+LIB_EXPORT void l_tester_setup_complete(void)
+{
+ struct test_case *test;
+
+ if (!test_entry)
+ return;
+
+ test = test_entry->data;
+
+ if (test->stage != L_TESTER_STAGE_SETUP)
+ return;
+
+ print_progress(test->name, COLOR_BLUE, "setup complete");
+
+ l_idle_oneshot(run_callback, test, NULL);
+}
+
+LIB_EXPORT void l_tester_setup_failed(void)
+{
+ struct test_case *test;
+
+ if (!test_entry)
+ return;
+
+ test = test_entry->data;
+
+ if (test->stage != L_TESTER_STAGE_SETUP)
+ return;
+
+ test->stage = L_TESTER_STAGE_POST_TEARDOWN;
+
+ l_timeout_remove(test->run_timer);
+ test->run_timer = NULL;
+
+ print_progress(test->name, COLOR_RED, "setup failed");
+ print_progress(test->name, COLOR_MAGENTA, "teardown");
+
+ test->post_teardown_func(test->test_data);
+}
+
+static void test_result(enum test_result result)
+{
+ struct test_case *test;
+
+ if (!test_entry)
+ return;
+
+ test = test_entry->data;
+
+ if (test->stage != L_TESTER_STAGE_RUN)
+ return;
+
+ l_timeout_remove(test->run_timer);
+ test->run_timer = NULL;
+
+ test->result = result;
+ switch (result) {
+ case TEST_RESULT_PASSED:
+ print_progress(test->name, COLOR_GREEN, "test passed");
+ break;
+ case TEST_RESULT_FAILED:
+ print_progress(test->name, COLOR_RED, "test failed");
+ break;
+ case TEST_RESULT_NOT_RUN:
+ print_progress(test->name, COLOR_YELLOW, "test not run");
+ break;
+ case TEST_RESULT_TIMED_OUT:
+ print_progress(test->name, COLOR_RED, "test timed out");
+ break;
+ }
+
+ if (test->teardown)
+ return;
+
+ test->teardown = true;
+
+ l_idle_oneshot(teardown_callback, test, NULL);
+}
+
+LIB_EXPORT void l_tester_test_passed(void)
+{
+ test_result(TEST_RESULT_PASSED);
+}
+
+LIB_EXPORT void l_tester_test_failed(void)
+{
+ test_result(TEST_RESULT_FAILED);
+}
+
+LIB_EXPORT void l_tester_test_abort(void)
+{
+ test_result(TEST_RESULT_NOT_RUN);
+}
+
+LIB_EXPORT void l_tester_teardown_complete(void)
+{
+ struct test_case *test;
+
+ if (!test_entry)
+ return;
+
+ test = test_entry->data;
+
+ if (test->stage != L_TESTER_STAGE_TEARDOWN)
+ return;
+
+ test->stage = L_TESTER_STAGE_POST_TEARDOWN;
+
+ test->post_teardown_func(test->test_data);
+}
+
+LIB_EXPORT void l_tester_teardown_failed(void)
+{
+ struct test_case *test;
+
+ if (!test_entry)
+ return;
+
+ test = test_entry->data;
+
+ if (test->stage != L_TESTER_STAGE_TEARDOWN)
+ return;
+
+ test->stage = L_TESTER_STAGE_POST_TEARDOWN;
+
+ l_tester_post_teardown_failed();
+}
+
+LIB_EXPORT void l_tester_post_teardown_complete(void)
+{
+ struct test_case *test;
+
+ if (!test_entry)
+ return;
+
+ test = test_entry->data;
+
+ if (test->stage != L_TESTER_STAGE_POST_TEARDOWN)
+ return;
+
+ print_progress(test->name, COLOR_MAGENTA, "teardown complete");
+
+ l_idle_oneshot(done_callback, test, NULL);
+}
+
+LIB_EXPORT void l_tester_post_teardown_failed(void)
+{
+ struct test_case *test;
+
+ if (!test_entry)
+ return;
+
+ test = test_entry->data;
+
+ if (test->stage != L_TESTER_STAGE_POST_TEARDOWN)
+ return;
+
+ print_progress(test->name, COLOR_RED, "teardown failed");
+
+ l_idle_oneshot(done_callback, test, NULL);
+}
+
+static void start_tester(void *user_data)
+{
+ gettimeofday(&tester_start, NULL);
+ next_test_case();
+}
+
+struct wait_data {
+ unsigned int seconds;
+ struct test_case *test;
+ l_tester_wait_func_t func;
+ void *user_data;
+};
+
+static void wait_callback(struct l_timeout *timer, void *user_data)
+{
+ struct wait_data *wait = user_data;
+ struct test_case *test = wait->test;
+
+ wait->seconds--;
+
+ if (wait->seconds > 0) {
+ print_progress(test->name, COLOR_BLACK, "%u seconds left",
+ wait->seconds);
+ return;
+ }
+
+ print_progress(test->name, COLOR_BLACK, "waiting done");
+
+ wait->func(wait->user_data);
+
+ free(wait);
+
+ l_timeout_remove(timer);
+}
+
+LIB_EXPORT void l_tester_wait(unsigned int seconds, l_tester_wait_func_t func,
+ void *user_data)
+{
+ struct test_case *test;
+ struct wait_data *wait;
+
+ if (!func || seconds < 1)
+ return;
+
+ if (!test_entry)
+ return;
+
+ test = test_entry->data;
+
+ wait = l_new(struct wait_data, 1);
+ wait->seconds = seconds;
+ wait->test = test;
+ wait->func = func;
+ wait->user_data = user_data;
+
+ l_timeout_create(seconds, wait_callback, wait, NULL);
+
+ print_progress(test->name, COLOR_BLACK, "waiting %u seconds", seconds);
+}
+
+static void signal_callback(unsigned int signum, void *user_data)
+{
+ switch (signum) {
+ case SIGINT:
+ case SIGTERM:
+ if (!terminated)
+ l_main_quit();
+
+ terminated = true;
+ break;
+ }
+}
+
+static const struct option options[] = {
+ { "version", no_argument, NULL, 'v' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "debug", no_argument, NULL, 'd' },
+ { "monitor", no_argument, NULL, 'm' },
+ { "list", no_argument, NULL, 'l' },
+ { "prefix", required_argument, NULL, 'p' },
+ { "string", required_argument, NULL, 's' },
+ { }
+};
+
+static void usage(void)
+{
+ fprintf(stderr,
+ "Usage:\n"
+ "\%s [options]\n", tester_name);
+ fprintf(stderr,
+ "Options:\n"
+ "\t-v, --version Show version information and exit\n"
+ "\t-d, --debug Run tests with debug output\n"
+ "\t-l, --list Only list the tests to be run\n"
+ "\t-p, --prefix Run tests matching the provided prefix\n"
+ "\t-s, --string Run tests matching the provided string\n");
+}
+
+/**
+ * l_tester_add_full:
+ * @name: test name
+ * @test_data: test data
+ * @pre_setup_func: test pre-setup function
+ * @setup_func: test setup function
+ * @test_func: test function
+ * @teardown_func: test teardown function
+ * @teardown_func: test post-teardown function
+ * @timeout: test teardown function
+ * @user_data: user data
+ * @destroy: user data destroy function
+ *
+ * Add a new test case.
+ **/
+LIB_EXPORT void l_tester_add_full(const char *name, const void *test_data,
+ l_tester_data_func_t pre_setup_func,
+ l_tester_data_func_t setup_func,
+ l_tester_data_func_t test_func,
+ l_tester_data_func_t teardown_func,
+ l_tester_data_func_t post_teardown_func,
+ unsigned int timeout,
+ void *user_data,
+ l_tester_destroy_func_t destroy)
+{
+ struct test_case *test;
+
+ if (!test_func)
+ return;
+
+ if (option_prefix && !l_str_has_prefix(name, option_prefix)) {
+ if (destroy)
+ destroy(user_data);
+ return;
+ }
+
+ if (option_string && !strstr(name, option_string)) {
+ if (destroy)
+ destroy(user_data);
+ return;
+ }
+
+ if (option_list) {
+ l_info("%s", name);
+ if (destroy)
+ destroy(user_data);
+ return;
+ }
+
+ test = l_new(struct test_case, 1);
+ test->name = l_strdup(name);
+ test->result = TEST_RESULT_NOT_RUN;
+ test->stage = L_TESTER_STAGE_INVALID;
+
+ test->test_data = test_data;
+
+ if (pre_setup_func)
+ test->pre_setup_func = pre_setup_func;
+ else
+ test->pre_setup_func = default_pre_setup;
+
+ if (setup_func)
+ test->setup_func = setup_func;
+ else
+ test->setup_func = default_setup;
+
+ test->test_func = test_func;
+
+ if (teardown_func)
+ test->teardown_func = teardown_func;
+ else
+ test->teardown_func = default_teardown;
+
+ if (post_teardown_func)
+ test->post_teardown_func = post_teardown_func;
+ else
+ test->post_teardown_func = default_post_teardown;
+
+ test->timeout = timeout;
+
+ test->destroy = destroy;
+ test->user_data = user_data;
+
+ l_queue_push_tail(test_list, test);
+}
+
+/**
+ * l_tester_add:
+ * @name: test name
+ * @test_data: test data
+ * @setup_func: test setup function
+ * @test_func: test function
+ * @teardown_func: test teardown function
+ *
+ * Add a new test with default settings for timeout and no pre-setup procedure.
+ **/
+LIB_EXPORT void l_tester_add(const char *name, const void *test_data,
+ l_tester_data_func_t setup_func,
+ l_tester_data_func_t test_func,
+ l_tester_data_func_t teardown_func)
+{
+ l_tester_add_full(name, test_data, NULL, setup_func, test_func,
+ teardown_func, NULL, 0, NULL, NULL);
+}
+
+/**
+ * l_tester_init:
+ * @argc: pointer to @argc parameter of main() function
+ * @argv: pointer to @argv parameter of main() function
+ *
+ * Initialize tester framework.
+ **/
+LIB_EXPORT void l_tester_init(int *argc, char ***argv)
+{
+ tester_name = strrchr(*argv[0], '/');
+ if (!tester_name)
+ tester_name = strdup(*argv[0]);
+ else
+ tester_name = strdup(++tester_name);
+
+ for (;;) {
+ int opt;
+
+ opt = getopt_long(*argc, *argv, "ps:vdl", options, NULL);
+ if (opt < 0)
+ break;
+
+ switch (opt) {
+ case 'v':
+ printf("%s\n", VERSION);
+ exit(EXIT_SUCCESS);
+ case 'l':
+ option_list = true;
+ break;
+ case 'p':
+ option_prefix = optarg;
+ break;
+ case 's':
+ option_string = optarg;
+ break;
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ l_main_init();
+
+ test_list = l_queue_new();
+}
+
+/**
+ * l_test_run:
+ *
+ * Run all added test cases.
+ *
+ * Returns: EXIT_SUCCESS (0) on success and
+ * EXIT_FAILURE (1) on failure
+ **/
+LIB_EXPORT int l_tester_run(void)
+{
+ int ret;
+
+ if (option_list)
+ return EXIT_SUCCESS;
+
+ l_idle_oneshot(start_tester, NULL, NULL);
+
+ l_main_run_with_signal(signal_callback, NULL);
+
+ ret = tester_summarize();
+
+ l_queue_destroy(test_list, test_destroy);
+
+ return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+/**
+ * l_test_get_stage:
+ *
+ * Get the test stage
+ *
+ * Returns: the stage of the current test that is being processing.
+ *
+ **/
+LIB_EXPORT enum l_tester_stage l_tester_get_stage(void)
+{
+ struct test_case *test;
+
+ if (!test_entry)
+ return L_TESTER_STAGE_INVALID;
+
+ test = test_entry->data;
+
+ return test->stage;
+}
diff --git a/ell/tester.h b/ell/tester.h
new file mode 100644
index 0000000..125f0b8
--- /dev/null
+++ b/ell/tester.h
@@ -0,0 +1,94 @@
+/*
+ *
+ * Embedded Linux library
+ *
+ * Copyright (C) 2021 Intel Corporation. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __ELL_TESTER_H
+#define __ELL_TESTER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+enum l_tester_stage {
+ L_TESTER_STAGE_INVALID,
+ L_TESTER_STAGE_PRE_SETUP,
+ L_TESTER_STAGE_SETUP,
+ L_TESTER_STAGE_RUN,
+ L_TESTER_STAGE_TEARDOWN,
+ L_TESTER_STAGE_POST_TEARDOWN,
+};
+
+void l_tester_init(int *argc, char ***argv);
+int l_tester_run(void);
+
+enum l_tester_stage l_tester_get_stage(void);
+
+typedef void (*l_tester_destroy_func_t)(void *user_data);
+typedef void (*l_tester_data_func_t)(const void *test_data);
+
+void l_tester_add_full(const char *name, const void *test_data,
+ l_tester_data_func_t pre_setup_func,
+ l_tester_data_func_t setup_func,
+ l_tester_data_func_t test_func,
+ l_tester_data_func_t teardown_func,
+ l_tester_data_func_t post_teardown_func,
+ unsigned int timeout,
+ void *user_data,
+ l_tester_destroy_func_t destroy);
+
+void l_tester_add(const char *name, const void *test_data,
+ l_tester_data_func_t setup_func,
+ l_tester_data_func_t test_func,
+ l_tester_data_func_t teardown_func);
+
+void *l_tester_get_data(void);
+
+void l_tester_pre_setup_complete(void);
+void l_tester_pre_setup_failed(void);
+void l_tester_pre_setup_continue(void);
+
+void l_tester_setup_complete(void);
+void l_tester_setup_failed(void);
+
+void l_tester_test_passed(void);
+void l_tester_test_failed(void);
+void l_tester_test_abort(void);
+
+void l_tester_teardown_complete(void);
+void l_tester_teardown_failed(void);
+
+void l_tester_post_teardown_complete(void);
+void l_tester_post_teardown_failed(void);
+
+typedef void (*l_tester_wait_func_t)(void *user_data);
+
+void l_tester_wait(unsigned int seconds, l_tester_wait_func_t func,
+ void *user_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ELL_TESTER_H */
--
2.26.2
1 month
[PATCH] util: Add L_AUTO_FREE_OBJ and l_steal_ptr macros
by Andrew Zaborowski
L_AUTO_FREE_OBJ can be used for ell object types that define a
destructor named l_<type>_free such as l_settings_free(). The "FREE" in
the name hopefully is enough to communicate this.
Some of our classes use l_<type>_destroy and similar names and some of
those functions, like l_queue_destroy, take additional parameters so for
those classes if a similar "AUTO" macros is wanted, it may be better
defined directly in that class's header.
l_steal_ptr is similar to g_steal_pointer except for being a macro.
Both macros can be used like in this example:
struct l_settings *load_settings(const char *path)
{
L_AUTO_FREE_OBJ(l_settings, s) = l_settings_new();
if (!l_settings_load_from_file(s, path))
return NULL;
return l_steal_ptr(s);
}
---
This is an RFC. L_AUTO_FREE_OBJ is based on James's idea.
ell/util.h | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/ell/util.h b/ell/util.h
index b112878..94a4178 100644
--- a/ell/util.h
+++ b/ell/util.h
@@ -235,6 +235,11 @@ static inline void l_put_be64(uint64_t val, void *ptr)
#define L_AUTO_FREE_VAR(vartype,varname) \
vartype varname __attribute__((cleanup(auto_free)))
+#define L_AUTO_FREE_OBJ(l_type,varname) \
+ void varname ## _auto_free(void *_ptr) \
+ { l_type ## _free(*(void **) _ptr); } \
+ L_AUTO_CLEANUP_VAR(struct l_type *, varname, varname ## _auto_free)
+
#define L_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
void *l_malloc(size_t size) __attribute__ ((warn_unused_result, malloc));
@@ -276,6 +281,9 @@ static inline size_t minsize(size_t a, size_t b)
__p; \
}))
+#define l_steal_ptr(ptr) \
+ (__extension__ ({ typeof(ptr) *_tmp = (ptr); (ptr) = NULL; _tmp; }))
+
char *l_strdup(const char *str);
char *l_strndup(const char *str, size_t max);
char *l_strdup_printf(const char *format, ...)
--
2.27.0
1 month
[PATCH 1/2] dhcp-server: Stricter start_ip/end_ip checks
by Andrew Zaborowski
Add basic checks that start_ip/end_ip are in the subnet defined by
server->address and server->netmask. Exclude server->address from
allowed client addresses since we don't prohibit a start_ip/end_ip
combination that contains server->address (and that's Ok). Slightly
modify how the default end_ip is derived from server->address to work
with any prefix_length -- that seems to have been the only place that
made assumptions about the prefix length.
---
ell/dhcp-server.c | 34 ++++++++++++++++++++++++++++++----
1 file changed, 30 insertions(+), 4 deletions(-)
diff --git a/ell/dhcp-server.c b/ell/dhcp-server.c
index 3bc1eac..68b6f13 100644
--- a/ell/dhcp-server.c
+++ b/ell/dhcp-server.c
@@ -323,7 +323,10 @@ static bool check_requested_ip(struct l_dhcp_server *server,
if (ntohl(requested_nip) < server->start_ip)
return false;
- if (htonl(requested_nip) > server->end_ip)
+ if (ntohl(requested_nip) > server->end_ip)
+ return false;
+
+ if (requested_nip == server->address)
return false;
lease = find_lease_by_ip(server->lease_list, requested_nip);
@@ -361,6 +364,9 @@ static uint32_t find_free_or_expired_ip(struct l_dhcp_server *server,
if ((ip_addr & 0xff) == 0xff)
continue;
+ if (ip_nl == server->address)
+ continue;
+
/*
* Search both active and expired leases. If this exausts all
* IP's in the range pop the expired list (oldest expired lease)
@@ -778,13 +784,33 @@ LIB_EXPORT bool l_dhcp_server_start(struct l_dhcp_server *server)
/*
* Assign a default ip range if not already. This will default to
- * server->address + 1 ... 254
+ * server->address + 1 ... subnet end address - 1
*/
if (!server->start_ip) {
- server->start_ip = L_BE32_TO_CPU(server->address) + 1;
- server->end_ip = (server->start_ip & 0xffffff00) | 0xfe;
+ server->start_ip = ntohl(server->address) + 1;
+ server->end_ip = ntohl(server->address) |
+ (~ntohl(server->netmask));
+ } else {
+ if ((server->start_ip ^ ntohl(server->address)) &
+ ntohl(server->netmask))
+ return false;
+
+ if ((server->end_ip ^ ntohl(server->address)) &
+ ntohl(server->netmask))
+ return false;
}
+ /*
+ * We skip over IPs ending in 0 or 255 when selecting a free address
+ * later on but make sure end_ip is not 0xffffffff so we can use
+ * "<= server->end_ip" safely in the loop condition.
+ */
+ if ((server->end_ip & 0xff) == 255)
+ server->end_ip--;
+
+ if (server->start_ip > server->end_ip)
+ return false;
+
if (!server->ifname) {
server->ifname = l_net_get_name(server->ifindex);
--
2.27.0
1 month, 1 week
[PATCH] netlink: Allow command handler to use l_netlink_cancel
by Andrew Zaborowski
---
ell/netlink.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/ell/netlink.c b/ell/netlink.c
index 33d0e5b..4664762 100644
--- a/ell/netlink.c
+++ b/ell/netlink.c
@@ -162,12 +162,15 @@ static void process_message(struct l_netlink *netlink, struct nlmsghdr *nlmsg)
{
const void *data = nlmsg;
struct command *command;
+ uint32_t id;
command = l_hashmap_remove(netlink->command_pending,
L_UINT_TO_PTR(nlmsg->nlmsg_seq));
if (!command)
return;
+ id = command->id;
+
if (!command->handler)
goto done;
@@ -189,9 +192,8 @@ static void process_message(struct l_netlink *netlink, struct nlmsghdr *nlmsg)
}
done:
- l_hashmap_remove(netlink->command_lookup, L_UINT_TO_PTR(command->id));
-
- destroy_command(command);
+ if (l_hashmap_remove(netlink->command_lookup, L_UINT_TO_PTR(id)))
+ destroy_command(command);
}
static void process_multi(struct l_netlink *netlink, struct nlmsghdr *nlmsg)
--
2.27.0
1 month, 1 week
[PATCH] queue: Be more specific in l_queue_insert comment
by Andrew Zaborowski
Specify that entries for which the compare function returns a 0 come
before the new entry.
---
ell/queue.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/ell/queue.c b/ell/queue.c
index 86c52ed..3ef892a 100644
--- a/ell/queue.c
+++ b/ell/queue.c
@@ -271,8 +271,9 @@ LIB_EXPORT void *l_queue_peek_tail(struct l_queue *queue)
* @user_data: user data given to compare function
*
* Inserts @data pointer at a position in the queue determined by the
- * compare @function. @function should return > 0 if the @data (first
- * parameter) should be inserted after the current entry (second parameter).
+ * compare @function. @function should return >= 0 if the @data (first
+ * parameter) should be inserted after the current entry (second parameter)
+ * and should return < 0 if before it.
*
* Returns: #true when data has been added and #false in case of failure
**/
--
2.27.0
1 month, 1 week