Diff between 3fe68046fe4ceccba8650dd62c48e193a374f71c and b0c402bc695edeefe59ed93a5f30c3a81536dea6

Changed Files

File Additions Deletions Status
src/shared/tester.c +684 -0 added
src/shared/tester.h +68 -0 added

Full Patch

diff --git a/src/shared/tester.c b/src/shared/tester.c
new file mode 100644
index 0000000..e4a1093
--- /dev/null
+++ b/src/shared/tester.c
@@ -0,0 +1,684 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  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 <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+
+#include <glib.h>
+
+#include "src/shared/tester.h"
+
+#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_CYAN	"\x1B[0;36m"
+#define COLOR_WHITE	"\x1B[0;37m"
+#define COLOR_HIGHLIGHT	"\x1B[1;39m"
+
+#define print_text(color, fmt, args...) \
+		printf(color fmt COLOR_OFF "\n", ## args)
+
+#define print_summary(label, color, value) \
+		printf("%-45s " color "%-10s" COLOR_OFF "\n", label, value)
+ 
+#define print_progress(name, color, fmt, args...) \
+		printf(COLOR_HIGHLIGHT "%s" COLOR_OFF " - " \
+				color fmt COLOR_OFF "\n", name, ##args)
+
+enum test_result {
+	TEST_RESULT_NOT_RUN,
+	TEST_RESULT_PASSED,
+	TEST_RESULT_FAILED,
+};
+
+enum test_stage {
+	TEST_STAGE_INVALID,
+	TEST_STAGE_PRE_SETUP,
+	TEST_STAGE_SETUP,
+	TEST_STAGE_RUN,
+	TEST_STAGE_TEARDOWN,
+	TEST_STAGE_POST_TEARDOWN,
+};
+
+struct test_case {
+	char *name;
+	enum test_result result;
+	enum test_stage stage;
+	const void *test_data;
+	tester_data_func_t pre_setup_func;
+	tester_data_func_t setup_func;
+	tester_data_func_t test_func;
+	tester_data_func_t teardown_func;
+	tester_data_func_t post_teardown_func;
+};
+
+static GMainLoop *main_loop;
+
+static GList *test_list;
+static GList *test_current;
+
+static void test_destroy(gpointer data)
+{
+	struct test_case *test = data;
+
+	g_free(test->name);
+	g_free(test);
+}
+
+void tester_print(const char *format, ...)
+{
+	va_list ap;
+
+	if (tester_use_quiet())
+		return;
+
+	printf("  %s", COLOR_WHITE);
+	va_start(ap, format);
+	vprintf(format, ap);
+	va_end(ap);
+	printf("%s\n", COLOR_OFF);
+}
+
+void tester_warn(const char *format, ...)
+{
+	va_list ap;
+
+	printf("  %s", COLOR_WHITE);
+	va_start(ap, format);
+	vprintf(format, ap);
+	va_end(ap);
+	printf("%s\n", COLOR_OFF);
+}
+
+static void default_pre_setup(const void *test_data)
+{
+	tester_pre_setup_complete();
+}
+
+static void default_setup(const void *test_data)
+{
+	tester_setup_complete();
+}
+
+static void default_teardown(const void *test_data)
+{
+	tester_teardown_complete();
+}
+
+static void default_post_teardown(const void *test_data)
+{
+	tester_post_teardown_complete();
+}
+
+void tester_add_full(const char *name, const void *test_data,
+					tester_data_func_t pre_setup_func,
+					tester_data_func_t setup_func,
+					tester_data_func_t test_func,
+					tester_data_func_t teardown_func,
+					tester_data_func_t post_teardown_func)
+{
+	struct test_case *test;
+
+	if (!test_func)
+		return;
+
+	test = g_new0(struct test_case, 1);
+
+	test->name = g_strdup(name);
+	test->result = TEST_RESULT_NOT_RUN;
+	test->stage = TEST_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_list = g_list_append(test_list, test);
+}
+
+void tester_add(const char *name, const void *test_data,
+					tester_data_func_t setup_func,
+					tester_data_func_t test_func,
+					tester_data_func_t teardown_func)
+{
+	tester_add_full(name, test_data, NULL, setup_func, test_func,
+							teardown_func, NULL);
+}
+
+static void tester_summarize(void)
+{
+	unsigned int not_run = 0, passed = 0, failed = 0;
+	GList *list;
+
+	printf("\n");
+	print_text(COLOR_HIGHLIGHT, "");
+	print_text(COLOR_HIGHLIGHT, "Test Summary");
+	print_text(COLOR_HIGHLIGHT, "------------");
+
+	for (list = g_list_first(test_list); list; list = g_list_next(list)) {
+		struct test_case *test = list->data;
+
+		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");
+			passed++;
+			break;
+		case TEST_RESULT_FAILED:
+			print_summary(test->name, COLOR_RED, "Failed");
+			failed++;
+			break;
+		}
+        }
+
+	printf("\nTotal: %d, "
+		COLOR_GREEN "Passed: %d (%.1f%%)" COLOR_OFF ", "
+		COLOR_RED "Failed: %d" COLOR_OFF ", "
+		COLOR_YELLOW "Not Run: %d" COLOR_OFF "\n",
+			not_run + passed + failed, passed,
+			(float) passed * 100 / (not_run + passed + failed),
+			failed, not_run);
+
+}
+
+static void next_test_case(void)
+{
+	struct test_case *test;
+
+	if (test_current)
+		test_current = g_list_next(test_current);
+	else
+		test_current = test_list;
+
+	if (!test_current) {
+		g_main_loop_quit(main_loop);
+		return;
+	}
+
+	test = test_current->data;
+
+	printf("\n");
+	print_progress(test->name, COLOR_BLACK, "init");
+
+	test->stage = TEST_STAGE_PRE_SETUP;
+
+	test->pre_setup_func(test->test_data);
+}
+
+static gboolean setup_callback(gpointer user_data)
+{
+	struct test_case *test = user_data;
+
+	test->stage = TEST_STAGE_SETUP;
+
+	print_progress(test->name, COLOR_BLUE, "setup");
+	test->setup_func(test->test_data);
+
+	return FALSE;
+}
+
+static gboolean run_callback(gpointer user_data)
+{
+	struct test_case *test = user_data;
+
+	test->stage = TEST_STAGE_RUN;
+
+	print_progress(test->name, COLOR_BLACK, "run");
+	test->test_func(test->test_data);
+
+	return FALSE;
+}
+
+static gboolean teardown_callback(gpointer user_data)
+{
+	struct test_case *test = user_data;
+
+	test->stage = TEST_STAGE_TEARDOWN;
+
+	print_progress(test->name, COLOR_MAGENTA, "teardown");
+	test->teardown_func(test->test_data);
+
+	return FALSE;
+}
+
+static gboolean done_callback(gpointer user_data)
+{
+	struct test_case *test = user_data;
+
+	print_progress(test->name, COLOR_BLACK, "done");
+	next_test_case();
+
+	return FALSE;
+}
+
+void tester_pre_setup_complete(void)
+{
+	struct test_case *test;
+
+	if (!test_current)
+		return;
+
+	test = test_current->data;
+
+	if (test->stage != TEST_STAGE_PRE_SETUP)
+		return;
+
+	g_idle_add(setup_callback, test);
+}
+
+void tester_pre_setup_failed(void)
+{
+	struct test_case *test;
+
+	if (!test_current)
+		return;
+
+	test = test_current->data;
+
+	if (test->stage != TEST_STAGE_PRE_SETUP)
+		return;
+
+	test->stage = TEST_STAGE_SETUP;
+
+	tester_setup_failed();
+}
+
+void tester_setup_complete(void)
+{
+	struct test_case *test;
+
+	if (!test_current)
+		return;
+
+	test = test_current->data;
+
+	if (test->stage != TEST_STAGE_SETUP)
+		return;
+
+	print_progress(test->name, COLOR_BLUE, "setup complete");
+
+	g_idle_add(run_callback, test);
+}
+
+void tester_setup_failed(void)
+{
+	struct test_case *test;
+
+	if (!test_current)
+		return;
+
+	test = test_current->data;
+
+	if (test->stage != TEST_STAGE_SETUP)
+		return;
+
+	print_progress(test->name, COLOR_RED, "setup failed");
+
+	g_idle_add(done_callback, test);
+}
+
+void tester_test_passed(void)
+{
+	struct test_case *test;
+
+	if (!test_current)
+		return;
+
+	test = test_current->data;
+
+	if (test->stage != TEST_STAGE_RUN)
+		return;
+
+	test->result = TEST_RESULT_PASSED;
+	print_progress(test->name, COLOR_GREEN, "test passed");
+
+	g_idle_add(teardown_callback, test);
+}
+
+void tester_test_failed(void)
+{
+	struct test_case *test;
+
+	if (!test_current)
+		return;
+
+	test = test_current->data;
+
+	if (test->stage != TEST_STAGE_RUN)
+		return;
+
+	test->result = TEST_RESULT_FAILED;
+	print_progress(test->name, COLOR_RED, "test failed");
+
+	g_idle_add(teardown_callback, test);
+}
+
+void tester_teardown_complete(void)
+{
+	struct test_case *test;
+
+	if (!test_current)
+		return;
+
+	test = test_current->data;
+
+	if (test->stage != TEST_STAGE_TEARDOWN)
+		return;
+
+	test->stage = TEST_STAGE_POST_TEARDOWN;
+
+	test->post_teardown_func(test->test_data);
+}
+
+void tester_teardown_failed(void)
+{
+	struct test_case *test;
+
+	if (!test_current)
+		return;
+
+	test = test_current->data;
+
+	if (test->stage != TEST_STAGE_TEARDOWN)
+		return;
+
+	test->stage = TEST_STAGE_POST_TEARDOWN;
+
+	tester_post_teardown_failed();
+}
+
+void tester_post_teardown_complete(void)
+{
+	struct test_case *test;
+
+	if (!test_current)
+		return;
+
+	test = test_current->data;
+
+	if (test->stage != TEST_STAGE_POST_TEARDOWN)
+		return;
+
+	print_progress(test->name, COLOR_MAGENTA, "teardown complete");
+
+	g_idle_add(done_callback, test);
+}
+
+void tester_post_teardown_failed(void)
+{
+	struct test_case *test;
+
+	if (!test_current)
+		return;
+
+	test = test_current->data;
+
+	if (test->stage != TEST_STAGE_POST_TEARDOWN)
+		return;
+
+	print_progress(test->name, COLOR_RED, "teardown failed");
+
+	g_idle_add(done_callback, test);
+}
+
+static gboolean start_tester(gpointer user_data)
+{
+	next_test_case();
+
+	return FALSE;
+}
+
+struct wait_data {
+	unsigned int seconds;
+	struct test_case *test;
+	tester_wait_func_t func;
+	void *user_data;
+};
+
+static gboolean wait_callback(gpointer 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 sec left",
+								wait->seconds);
+		return TRUE;
+	}
+
+	print_progress(test->name, COLOR_BLACK, "waiting done");
+
+	wait->func(wait->user_data);
+
+	g_free(wait);
+
+	return FALSE;
+}
+
+void tester_wait(unsigned int seconds, tester_wait_func_t func,
+							void *user_data)
+{
+	struct test_case *test;
+	struct wait_data *wait;
+
+	if (!func || seconds < 1)
+		return;
+
+	if (!test_current)
+		return;
+
+	test = test_current->data;
+
+	print_progress(test->name, COLOR_BLACK, "waiting %u sec", seconds);
+
+	wait = g_new0(struct wait_data, 1);
+
+	wait->seconds = seconds;
+	wait->test = test;
+	wait->func = func;
+	wait->user_data = user_data;
+
+	g_timeout_add_seconds(1, wait_callback, wait);
+}
+
+static gboolean signal_handler(GIOChannel *channel, GIOCondition condition,
+							gpointer user_data)
+{
+	static unsigned int __terminated = 0;
+	struct signalfd_siginfo si;
+	ssize_t result;
+	int fd;
+
+	if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+		g_main_loop_quit(main_loop);
+		return FALSE;
+	}
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	result = read(fd, &si, sizeof(si));
+	if (result != sizeof(si))
+		return FALSE;
+
+	switch (si.ssi_signo) {
+	case SIGINT:
+	case SIGTERM:
+		if (__terminated == 0)
+			g_main_loop_quit(main_loop);
+
+		__terminated = 1;
+		break;
+	}
+
+	return TRUE;
+}
+
+static guint setup_signalfd(void)
+{
+	GIOChannel *channel;
+	guint source;
+	sigset_t mask;
+	int fd;
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGINT);
+	sigaddset(&mask, SIGTERM);
+
+	if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
+		perror("Failed to set signal mask");
+		return 0;
+	}
+
+	fd = signalfd(-1, &mask, 0);
+	if (fd < 0) {
+		perror("Failed to create signal descriptor");
+		return 0;
+	}
+
+	channel = g_io_channel_unix_new(fd);
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+	g_io_channel_set_encoding(channel, NULL, NULL);
+	g_io_channel_set_buffered(channel, FALSE);
+
+	source = g_io_add_watch(channel,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				signal_handler, NULL);
+
+	g_io_channel_unref(channel);
+
+	return source;
+}
+
+static gboolean option_version = FALSE;
+static gboolean option_quiet = FALSE;
+static gboolean option_debug = FALSE;
+
+bool tester_use_quiet(void)
+{
+	return option_quiet == TRUE ? true : false;
+}
+
+bool tester_use_debug(void)
+{
+	return option_debug == TRUE ? true : false;
+}
+
+static GOptionEntry options[] = {
+	{ "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
+				"Show version information and exit" },
+	{ "quiet", 'q', 0, G_OPTION_ARG_NONE, &option_quiet,
+				"Run tests without logging" },
+	{ "debug", 'd', 0, G_OPTION_ARG_NONE, &option_debug,
+				"Run tests with debug output" },
+	{ NULL },
+};
+
+void tester_init(int *argc, char ***argv)
+{
+	GOptionContext *context;
+	GError *error = NULL;
+
+	context = g_option_context_new(NULL);
+	g_option_context_add_main_entries(context, options, NULL);
+
+	if (g_option_context_parse(context, argc, argv, &error) == FALSE) {
+		if (error != NULL) {
+			g_printerr("%s\n", error->message);
+			g_error_free(error);
+		} else
+			g_printerr("An unknown error occurred\n");
+		exit(1);
+	}
+
+	g_option_context_free(context);
+
+	if (option_version == TRUE) {
+		g_print("%s\n", VERSION);
+		exit(EXIT_SUCCESS);
+	}
+
+	main_loop = g_main_loop_new(NULL, FALSE);
+
+	test_list = NULL;
+	test_current = NULL;
+}
+
+int tester_run(void)
+{
+	guint signal;
+
+	if (!main_loop)
+		return EXIT_FAILURE;
+
+	signal = setup_signalfd();
+
+	g_idle_add(start_tester, NULL);
+	g_main_loop_run(main_loop);
+
+	g_source_remove(signal);
+
+	g_main_loop_unref(main_loop);
+
+	tester_summarize();
+
+	g_list_free_full(test_list, test_destroy);
+
+	return EXIT_SUCCESS;
+}
diff --git a/src/shared/tester.h b/src/shared/tester.h
new file mode 100644
index 0000000..7c23340
--- /dev/null
+++ b/src/shared/tester.h
@@ -0,0 +1,68 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  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
+ *
+ */
+
+#include <stdbool.h>
+
+void tester_init(int *argc, char ***argv);
+int tester_run(void);
+
+bool tester_use_quiet(void);
+bool tester_use_debug(void);
+
+void tester_print(const char *format, ...)
+				__attribute__((format(printf, 1, 2)));
+void tester_warn(const char *format, ...)
+				__attribute__((format(printf, 1, 2)));
+
+typedef void (*tester_data_func_t)(const void *test_data);
+
+void tester_add_full(const char *name, const void *test_data,
+					tester_data_func_t pre_setup_func,
+					tester_data_func_t setup_func,
+					tester_data_func_t test_func,
+					tester_data_func_t teardown_func,
+					tester_data_func_t post_teardown_func);
+void tester_add(const char *name, const void *test_data,
+					tester_data_func_t setup_func,
+					tester_data_func_t test_func,
+					tester_data_func_t teardown_func);
+
+void tester_pre_setup_complete(void);
+void tester_pre_setup_failed(void);
+
+void tester_setup_complete(void);
+void tester_setup_failed(void);
+
+void tester_test_passed(void);
+void tester_test_failed(void);
+
+void tester_teardown_complete(void);
+void tester_teardown_failed(void);
+
+void tester_post_teardown_complete(void);
+void tester_post_teardown_failed(void);
+
+typedef void (*tester_wait_func_t)(void *user_data);
+
+void tester_wait(unsigned int seconds, tester_wait_func_t func,
+							void *user_data);