* [PATCH] [RFC PATCH] tester: Add framework for serialized test execution
@ 2021-03-05 21:37 Inga Stotland
2021-03-08 22:19 ` Denis Kenzior
0 siblings, 1 reply; 3+ messages in thread
From: Inga Stotland @ 2021-03-05 21:37 UTC (permalink / raw)
To: ell
[-- Attachment #1: Type: text/plain, Size: 22632 bytes --]
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
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] [RFC PATCH] tester: Add framework for serialized test execution
2021-03-05 21:37 [PATCH] [RFC PATCH] tester: Add framework for serialized test execution Inga Stotland
@ 2021-03-08 22:19 ` Denis Kenzior
2021-03-09 2:59 ` Stotland, Inga
0 siblings, 1 reply; 3+ messages in thread
From: Denis Kenzior @ 2021-03-08 22:19 UTC (permalink / raw)
To: ell
[-- Attachment #1: Type: text/plain, Size: 2496 bytes --]
Hi Inga,
On 3/5/21 3:37 PM, Inga Stotland wrote:
> 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 ++++++
Hmm, ell.sym entries?
> 3 files changed, 884 insertions(+), 2 deletions(-)
> create mode 100644 ell/tester.c
> create mode 100644 ell/tester.h
>
<snip>
> +
> +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;
> +
Curious. This tester framework is part of the main ell library, so I would have
assumed it is a 'class' so to speak. But you use static vars here. So is
l_tester only used from a dedicated binary?
If not, do the l_tester_* signatures need to be updated to take an l_tester
instance and appropriate constructors / destructors defined?
<snip>
> +
> +void *l_tester_get_data(void)
LIB_EXPORT?
> +{
> + 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);
> +
Have you considered using l_time* from ell/time.h?
> + return elapsed.tv_sec + ((double) elapsed.tv_usec) / 1000000;
> +}
> +
<snip>
The rest of the code seems straightforward and nothing else really jumped out at me.
Regards,
-Denis
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] [RFC PATCH] tester: Add framework for serialized test execution
2021-03-08 22:19 ` Denis Kenzior
@ 2021-03-09 2:59 ` Stotland, Inga
0 siblings, 0 replies; 3+ messages in thread
From: Stotland, Inga @ 2021-03-09 2:59 UTC (permalink / raw)
To: ell
[-- Attachment #1: Type: text/plain, Size: 2730 bytes --]
Hi Denis,
On Mon, 2021-03-08 at 16:19 -0600, Denis Kenzior wrote:
Hi Inga,
On 3/5/21 3:37 PM, Inga Stotland wrote:
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 ++++++
Hmm, ell.sym entries?
3 files changed, 884 insertions(+), 2 deletions(-)
create mode 100644 ell/tester.c
create mode 100644 ell/tester.h
<snip>
+
+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;
+
Curious. This tester framework is part of the main ell library, so I would have
assumed it is a 'class' so to speak. But you use static vars here. So is
l_tester only used from a dedicated binary?
If not, do the l_tester_* signatures need to be updated to take an l_tester
instance and appropriate constructors / destructors defined?
<snip>
+
+void *l_tester_get_data(void)
LIB_EXPORT?
+{
+ 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);
+
Have you considered using l_time* from ell/time.h?
+ return elapsed.tv_sec + ((double) elapsed.tv_usec) / 1000000;
+}
+
<snip>
The rest of the code seems straightforward and nothing else really jumped out at me.
Regards,
-Denis
Thanks for reviewing. Will work on v2.
Regards,
Inga
[-- Attachment #2: attachment.htm --]
[-- Type: text/html, Size: 4815 bytes --]
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2021-03-09 2:59 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-05 21:37 [PATCH] [RFC PATCH] tester: Add framework for serialized test execution Inga Stotland
2021-03-08 22:19 ` Denis Kenzior
2021-03-09 2:59 ` Stotland, Inga
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.