[PATCH 16/22] unit: Add basic unit test for the blueatchat module

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch adds first few basic unit test for blueatchat's AT parsing
functionality.
---
 Makefile.am            |    9 ++
 unit/test-blueatchat.c |  334 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 343 insertions(+)
 create mode 100644 unit/test-blueatchat.c

diff --git a/Makefile.am b/Makefile.am
index 079405f..76bcc75 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -289,6 +289,15 @@ unit_test_cbuffer_SOURCES = unit/test-cbuffer.c \
 				src/shared/cbuffer.h src/shared/cbuffer.c
 unit_test_cbuffer_LDADD = @GLIB_LIBS@
 
+unit_tests += unit/test-blueatchat
+
+unit_test_blueatchat_SOURCES = unit/test-blueatchat.c \
+				src/shared/util.h src/shared/util.c \
+				src/shared/cbuffer.h src/shared/cbuffer.c \
+				src/shared/blueatchat.h src/shared/blueatchat.c \
+				src/shared/dparser.h src/shared/dparser.c
+unit_test_blueatchat_LDADD = @GLIB_LIBS@
+
 noinst_PROGRAMS += $(unit_tests)
 
 TESTS = $(unit_tests)
diff --git a/unit/test-blueatchat.c b/unit/test-blueatchat.c
new file mode 100644
index 0000000..ae10ff7
--- /dev/null
+++ b/unit/test-blueatchat.c
@@ -0,0 +1,334 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+#include "src/shared/cbuffer.h"
+#include "src/shared/blueatchat.h"
+
+/* use power of 2 for the buffer size */
+#define BUFFER_SIZE (1<<8)
+
+/* #define BLUEATCHAT_DEBUG */
+
+/* storage used by unit testing framework */
+struct blueat_fixture {
+	GMainLoop *loop;
+	char *msg;
+	struct blueatchat_session *session;
+	GIOChannel *gio_read, *gio_write;
+	bool issent;
+};
+
+/* stores callback calls count for each test case if data was parsed */
+volatile int called;
+/* stores callback calls for unknown commands or with wring data */
+volatile int unknown;
+
+/******************************************************************************
+ * Add test cases below:
+ *	- create test case for sending commands data
+ *	- if sending new commands - remember to register this command
+ *		and its callback which verifies parsed data
+ *	- add prepared test case in the main function at the very bottom
+ ******************************************************************************/
+/* Unknown command test case and callback that verifies (non)parsed data */
+static bool tc_blueat_unknown_cb(GSList *data)
+{
+	unknown += 1;
+
+	/* In this test case there should be no parsed data */
+	g_assert(data);
+	g_assert(data->data);
+	g_message("Unknown command callback: %s",
+			g_strescape((char *)data->data, NULL));
+
+	return true;
+}
+static void tc_blueat_unknown(struct blueat_fixture *fix,
+					gconstpointer test_data)
+{
+	/* Defined test case */
+	fix->msg = "\r\nunknown command and garbage\r\n";
+
+	/* start the loop for IO ops execution */
+	g_main_loop_run(fix->loop);
+
+	/* one unknown command should be found */
+	g_assert_cmpint(called, ==, 0);
+	g_assert_cmpint(unknown, ==, 1);
+}
+
+/* OK test case and callback that verifies parsed data */
+static bool tc_blueat_ok_cb(GSList *data)
+{
+	called += 1;
+	g_message("OK callback");
+	g_assert(!data);
+	return true;
+}
+static void tc_blueat_ok(struct blueat_fixture *fix, gconstpointer test_data)
+{
+	fix->msg = "\r\nOK\r\n";
+
+	g_main_loop_run(fix->loop);
+
+	/* one ok command should be found */
+	g_assert_cmpint(called, ==, 1);
+}
+static void tc_blueat_garbagemix(struct blueat_fixture *fix,
+					gconstpointer test_data)
+{
+	/* Define test case */
+	fix->msg = "garbage\n\r"		/* garbage */
+			"\r\nunknown\r\n"	/* unknown */
+			"\r\nOK\r\n"		/* ok */
+			"\r\n\n\rgarbage\r\n"	/* unknown */
+			"\r\rgarbageggegg\n\n"	/* garbage */
+			"\r\nOK\r\n";		/* ok */
+
+	g_main_loop_run(fix->loop);
+
+	/* 2 OK and 2 unknown commands should be found */
+	g_assert_cmpint(called, ==, 2);
+	g_assert_cmpint(unknown, ==, 2);
+}
+
+/* AT+BRSF test case and callback that verifies parsed data */
+static bool tc_blueat_atbrsf_cb(GSList *data)
+{
+	called += 1;
+	g_message("AT+BRSF= callback");
+
+	g_assert(data);
+	g_assert(data->data);
+	g_assert_cmpint(*(int *)data->data, ==, 128);
+
+	return true;
+}
+static void tc_blueat_atbrsf(struct blueat_fixture *fix,
+					gconstpointer test_data)
+{
+	/* Define test case */
+	fix->msg = "\r\nAT+BRSF=128\r\n";
+
+	g_main_loop_run(fix->loop);
+
+	/* 1 callback should be called */
+	g_assert_cmpint(called, ==, 1);
+}
+
+static void tc_blueat_garbage(struct blueat_fixture *fix,
+					gconstpointer test_data)
+{
+	/* Define test case */
+	fix->msg = "dfg5745j46\74845r4\68r\48un468n4wn\4r4hgnn\r\nOKjf\rkfhn"
+			"\r\n\nlsfg\aargarbage\r;\nsrdgfhdnOK\r\n";
+
+	/* start the loop for IO ops execution */
+	g_main_loop_run(fix->loop);
+
+	/* no commands should be found in this garbage */
+	g_assert_cmpint(called, ==, 0);
+}
+
+static bool tc_blueat_cnum_cb(GSList *data)
+{
+	GSList *item = NULL;
+
+	called += 1;
+	g_message("+CNUM: callback");
+
+	g_assert(data);
+	item = g_slist_reverse(data);
+
+	g_assert(item);
+	/* no optional parameter */
+	g_assert((char *)item->data == NULL);
+
+	g_assert((item = g_slist_next(item)));
+	g_assert((char *)item->data != NULL);
+	g_assert_cmpint(strcmp((char *)item->data, "5551212"), ==, 0);
+
+	g_assert((item = g_slist_next(item)));
+	g_assert((int *)item->data != NULL);
+	g_assert_cmpint(*(int *)item->data, ==, 129);
+
+	/* no optional parameter */
+	g_assert((item = g_slist_next(item)));
+	g_assert((int *)item->data == NULL);
+
+	g_assert((item = g_slist_next(item)));
+	g_assert((int *)item->data != NULL);
+	g_assert_cmpint(*(int *)item->data, ==, 4);
+	return true;
+}
+static void tc_blueat_cnum(struct blueat_fixture *fix, gconstpointer test_data)
+{
+	/* Define test case */
+	/* two optional command parameters absent */
+	fix->msg = "\r\n+CNUM: ,\"5551212\",129,,4\r\n";
+
+	/* start the loop for IO ops execution */
+	g_main_loop_run(fix->loop);
+
+	/* 1 cnum command should be found */
+	g_assert_cmpint(called, ==, 1);
+}
+
+/******************************************************************************
+ * Parser configuration and command registration
+ ******************************************************************************/
+static struct blueatchat_config cfg = {
+	.buff_size = BUFFER_SIZE,
+	.cmd_prefix = "\r\n",
+	.cmd_postfix = "\r\n"
+};
+
+/* Commands that should be recognised by the parser.
+ * !IMPORTANT: unknown command callback should always be last.
+ */
+static struct blueatchat_cmd_descriptor commands[] = {
+	/* recognised command | syntax descriptor   |   callback */
+	{"OK",		NULL,				tc_blueat_ok_cb},
+	{"AT+BRSF=",	"u",				tc_blueat_atbrsf_cb},
+	{"+CNUM: ",	"oq,q,u,ou,u*",			tc_blueat_cnum_cb},
+	{NULL,		"s",				tc_blueat_unknown_cb},
+};
+
+/* Callback sending messages to the blueatchat module */
+static gboolean gio_out_cb(GIOChannel *gio, GIOCondition condition,
+					gpointer data)
+{
+	struct blueat_fixture *fix = data;
+
+	if (condition & G_IO_HUP)
+		g_error("Write end of pipe died!\n");
+
+	if (!fix->issent) {
+		gsize len;
+		GIOStatus ret;
+		GError *err = NULL;
+		const gchar *msg = fix->msg;
+
+		ret = g_io_channel_write_chars(gio, msg, -1, &len,
+						&err);
+		if (ret == G_IO_STATUS_ERROR)
+			g_error("Error writing: %s\n", err->message);
+		fix->issent = true;
+		return TRUE;
+	}
+
+	/* It will end up here at second io write,
+	 * when message has already been written.
+	 */
+	g_main_loop_quit(fix->loop);
+	return FALSE;
+}
+
+/* Make blueatchat silent */
+static void blueatchat_debug(const char *str, void *user_data)
+{
+#ifdef BLUEATCHAT_DEBUG
+	const char *prefix = user_data;
+	g_message("%s: %s", prefix, g_strescape(str, NULL));
+#endif
+}
+
+static gboolean blueat_read_cb(GIOChannel *gio, GIOCondition condition,
+				gpointer data)
+{
+	struct blueatchat_session *session = (struct blueatchat_session *)data;
+
+	if (!session)
+		g_error("Session died!\n");
+
+	g_message("Read started");
+
+	if (condition & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
+		g_message("Read end of pipe died!");
+		return FALSE;
+	}
+
+	if (!blueatchat_read(session, gio)) {
+		g_message("AT chat died!");
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static void blueat_fix_setup(struct blueat_fixture *fix, gconstpointer tdata)
+{
+	int fd[2], ret;
+
+	called = 0;
+	unknown = 0;
+	fix->issent = false;
+	fix->loop = g_main_loop_new(NULL, FALSE);
+
+	ret = pipe(fd);
+	if (ret == -1)
+		g_error("Creating pipe failed: %s\n", strerror(errno));
+
+	fix->gio_read = g_io_channel_unix_new(fd[0]);
+	fix->gio_write = g_io_channel_unix_new(fd[1]);
+
+	g_io_channel_set_encoding(fix->gio_write, NULL, NULL);
+	g_io_channel_set_buffered(fix->gio_write, FALSE);
+
+	g_io_channel_set_encoding(fix->gio_read, NULL, NULL);
+	g_io_channel_set_buffered(fix->gio_read, FALSE);
+	g_io_channel_set_flags(fix->gio_read, G_IO_FLAG_NONBLOCK, NULL);
+
+	fix->session =
+		blueatchat_init_session(&cfg, commands, blueatchat_debug);
+
+	if (!fix->session)
+		g_error("Cannot initialize the gat_parser!\n");
+
+	if (!fix->gio_read || !fix->gio_write)
+		g_error("Cannot create new GIOChannel!\n");
+
+	g_io_add_watch_full(fix->gio_read, G_PRIORITY_DEFAULT,
+					(G_IO_IN | G_IO_ERR | G_IO_NVAL),
+					blueat_read_cb,
+					fix->session, NULL);
+	if (!g_io_add_watch(fix->gio_write, G_IO_OUT | G_IO_HUP,
+					gio_out_cb, fix))
+		g_error("Cannot add watch on GIOChannel!\n");
+}
+
+static void blueat_fix_teardown(struct blueat_fixture *fix, gconstpointer tdata)
+{
+	/* channel clean up */
+	blueatchat_cleanup_session(fix->session);
+	g_io_channel_unref(fix->gio_read);
+	g_io_channel_unref(fix->gio_write);
+	g_main_loop_unref(fix->loop);
+}
+
+int main(int argc, char **argv)
+{
+	g_test_init(&argc, &argv, NULL);
+	g_test_add("/blueatchat/unknown", struct blueat_fixture, 0,
+				blueat_fix_setup, tc_blueat_unknown,
+				blueat_fix_teardown);
+	g_test_add("/blueatchat/ok", struct blueat_fixture, 0,
+				blueat_fix_setup, tc_blueat_ok,
+				blueat_fix_teardown);
+	g_test_add("/blueatchat/garbagemix", struct blueat_fixture, 0,
+				blueat_fix_setup, tc_blueat_garbagemix,
+				blueat_fix_teardown);
+	g_test_add("/blueatchat/atbrsf", struct blueat_fixture, 0,
+				blueat_fix_setup, tc_blueat_atbrsf,
+				blueat_fix_teardown);
+	g_test_add("/blueatchat/garbage", struct blueat_fixture, 0,
+				blueat_fix_setup, tc_blueat_garbage,
+				blueat_fix_teardown);
+	g_test_add("/blueatchat/cnum", struct blueat_fixture, 0,
+				blueat_fix_setup, tc_blueat_cnum,
+				blueat_fix_teardown);
+	return g_test_run();
+}
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux