From b620082e168c4b8b76f1ae6063123f6b2dcc70ec Mon Sep 17 00:00:00 2001
From: Konstantin Osipov <kostja@tarantool.org>
Date: Thu, 14 Jun 2012 13:07:27 +0400
Subject: [PATCH] Implement a simple unit testing framework. Add the first unit
 test.

Extend queue.h with STAILQ_SPLICE and STAILQ_REVERSE.
Test the new functions in a simple unit testing framework.
---
 .gitignore                  |   1 +
 test/CMakeLists.txt         |   1 +
 test/lib/server.py          |   6 +-
 test/lib/unittest_server.py |  36 ++++++++
 test/unit/CMakeLists.txt    |   1 +
 test/unit/queue.c           | 166 ++++++++++++++++++++++++++++++++++++
 test/unit/queue.result      |  37 ++++++++
 test/unit/queue.test        |   1 +
 test/unit/suite.ini         |  10 +++
 test/unit/unit.h            |  35 ++++++++
 third_party/queue.h         |  25 ++++++
 11 files changed, 316 insertions(+), 3 deletions(-)
 create mode 100644 test/lib/unittest_server.py
 create mode 100644 test/unit/CMakeLists.txt
 create mode 100644 test/unit/queue.c
 create mode 100644 test/unit/queue.result
 create mode 100644 test/unit/queue.test
 create mode 100644 test/unit/suite.ini
 create mode 100644 test/unit/unit.h

diff --git a/.gitignore b/.gitignore
index 1f82f83581..e7129f79d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,7 @@ test/lib/*.pyc
 test/lib/*/*.pyc
 test/box/protocol
 test/box/connector
+test/unit/queue
 Makefile
 CMakeFiles
 CMakeCache.txt
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index ad52105be4..0d2a43e561 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -4,6 +4,7 @@ add_custom_target(test
 add_custom_target(test-force
     COMMAND ${PROJECT_SOURCE_DIR}/test/run.sh --builddir=${PROJECT_BINARY_DIR} --force --vardir=${PROJECT_BINARY_DIR}/test/var)
 
+add_subdirectory(unit)
 add_subdirectory(box)
 add_subdirectory(connector_c)
 
diff --git a/test/lib/server.py b/test/lib/server.py
index 2c2590d08e..5730ffe979 100644
--- a/test/lib/server.py
+++ b/test/lib/server.py
@@ -177,7 +177,7 @@ class Server(object):
         if start_and_exit != None: self.start_and_exit = start_and_exit
         if gdb != None: self.gdb = gdb
         if valgrind != None: self.valgrind = valgrind
-	self.debug = self.test_debug()
+        self.debug = self.test_debug()
 
         if self.is_started:
             if not silent:
@@ -269,7 +269,7 @@ class Server(object):
 
     def test_option_get(self, show, option_list_str):
         args = [self.binary] + option_list_str.split()
-	if show:
+        if show:
            print " ".join([os.path.basename(self.binary)] + args[1:])
         output = subprocess.Popen(args,
                                   cwd = self.vardir,
@@ -282,7 +282,7 @@ class Server(object):
 
     def test_debug(self):
         output = self.test_option_get(False, "-V")
-	if re.search("-Debug", output):
+        if re.search("-Debug", output):
            return True
         return False
 
diff --git a/test/lib/unittest_server.py b/test/lib/unittest_server.py
new file mode 100644
index 0000000000..5d56bbc7f0
--- /dev/null
+++ b/test/lib/unittest_server.py
@@ -0,0 +1,36 @@
+from server import Server
+import subprocess
+import sys
+import os
+
+class UnittestServer(Server):
+    """A dummy server implementation for unit test suite"""
+    def __new__(cls, core="unittest", module="dummy"):
+        return Server.__new__(cls)
+
+
+    def __init__(self, core="unittest", module="dummy"):
+        Server.__init__(self, core, module)
+        self.debug = False
+
+    def configure(self, config):
+        pass
+    def deploy(self, config=None, binary=None, vardir=None,
+               mem=None, start_and_exit=None, gdb=None, valgrind=None,
+               valgrind_sup=None, init_lua=None, silent=True, need_init=True):
+        self.vardir = vardir
+        def run_test(name):
+            p = subprocess.Popen([os.path.join(self.builddir, "test/unit", name)], stdout=subprocess.PIPE)
+            p.wait()
+            for line in p.stdout.readlines():
+                sys.stdout.write(line)
+
+        self.run_test = run_test
+
+    def start(self):
+        pass
+    def find_exe(self, builddir, silent=False):
+        self.builddir = builddir
+
+    def init(self):
+        pass
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
new file mode 100644
index 0000000000..a2961e1ba7
--- /dev/null
+++ b/test/unit/CMakeLists.txt
@@ -0,0 +1 @@
+add_executable(queue queue.c)
diff --git a/test/unit/queue.c b/test/unit/queue.c
new file mode 100644
index 0000000000..5fbfdeeff7
--- /dev/null
+++ b/test/unit/queue.c
@@ -0,0 +1,166 @@
+/*
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "third_party/queue.h"
+#include "unit.h"
+#include <stdio.h>
+#include <assert.h>
+
+struct elem
+{
+	STAILQ_ENTRY(elem) entry;
+	int val;
+};
+
+STAILQ_HEAD(elem_queue, elem);
+
+const char *
+queue2str(struct elem_queue *queue)
+{
+	static char buf[1024];
+	buf[0] = '\0';
+	struct elem *elem;
+	int n = 0;
+	STAILQ_FOREACH(elem, queue, entry) {
+		n += snprintf(buf + n, sizeof(buf) - n - 1, "%d ", elem->val);
+	}
+	return buf;
+}
+
+/** Test a queue with 0 elements. */
+void test0()
+{
+	header();
+	struct elem_queue queue = STAILQ_HEAD_INITIALIZER(queue);
+	printf("Initialized: %s\n", queue2str(&queue));
+	STAILQ_INIT(&queue);
+	printf("STAILQ_INIT: %s\n", queue2str(&queue));
+	STAILQ_REVERSE(&queue, elem, entry);
+	printf("STAILQ_REVERSE: %s\n", queue2str(&queue));
+	footer();
+}
+
+/** Test a queue with 1 element. */
+void test1()
+{
+	header();
+	struct elem el1;
+	struct elem_queue queue = STAILQ_HEAD_INITIALIZER(queue);
+	el1.val = 1;
+	STAILQ_INSERT_TAIL(&queue, &el1, entry);
+	printf("STAILQ_INIT: %s\n", queue2str(&queue));
+	STAILQ_REVERSE(&queue, elem, entry);
+	printf("STAILQ_REVERSE: %s\n", queue2str(&queue));
+	footer();
+}
+
+
+void test2()
+{
+	header();
+	struct elem el1, el2;
+	struct elem_queue queue = STAILQ_HEAD_INITIALIZER(queue);
+	el1.val = 1;
+	el2.val = 2;
+	STAILQ_INSERT_TAIL(&queue, &el1, entry);
+	STAILQ_INSERT_TAIL(&queue, &el2, entry);
+	printf("STAILQ_INIT: %s\n", queue2str(&queue));
+	STAILQ_REVERSE(&queue, elem, entry);
+	printf("STAILQ_REVERSE: %s\n", queue2str(&queue));
+	footer();
+}
+
+void test3()
+{
+	header();
+	struct elem el1, el2, el3;
+	struct elem_queue queue = STAILQ_HEAD_INITIALIZER(queue);
+	el1.val = 1;
+	el2.val = 2;
+	el3.val = 3;
+	STAILQ_INSERT_TAIL(&queue, &el1, entry);
+	STAILQ_INSERT_TAIL(&queue, &el2, entry);
+	STAILQ_INSERT_TAIL(&queue, &el3, entry);
+	printf("STAILQ_INIT: %s\n", queue2str(&queue));
+	STAILQ_REVERSE(&queue, elem, entry);
+	printf("STAILQ_REVERSE: %s\n", queue2str(&queue));
+	footer();
+}
+
+
+void test_splice()
+{
+	header();
+	struct elem el1, el2, el3;
+	struct elem_queue queue1 = STAILQ_HEAD_INITIALIZER(queue1);
+	struct elem_queue queue2 = STAILQ_HEAD_INITIALIZER(queue2);
+	STAILQ_SPLICE(&queue1, STAILQ_FIRST(&queue1), entry, &queue2);
+	printf("q1: %s\n", queue2str(&queue1));
+	printf("q2: %s\n", queue2str(&queue2));
+	STAILQ_SPLICE(&queue2, STAILQ_FIRST(&queue2), entry, &queue1);
+	printf("q1: %s\n", queue2str(&queue1));
+	printf("q2: %s\n", queue2str(&queue2));
+	el1.val = 1;
+	el2.val = 2;
+	el3.val = 3;
+	STAILQ_INSERT_TAIL(&queue1, &el1, entry);
+	STAILQ_INSERT_TAIL(&queue1, &el2, entry);
+	STAILQ_INSERT_TAIL(&queue1, &el3, entry);
+	printf("STAILQ_INIT: %s\n", queue2str(&queue1));
+	STAILQ_SPLICE(&queue1, STAILQ_FIRST(&queue1), entry, &queue2);
+	printf("q1: %s\n", queue2str(&queue1));
+	printf("q2: %s\n", queue2str(&queue2));
+	STAILQ_SPLICE(&queue2, STAILQ_FIRST(&queue2), entry, &queue1);
+	printf("q1: %s\n", queue2str(&queue1));
+	printf("q2: %s\n", queue2str(&queue2));
+	STAILQ_SPLICE(&queue1, STAILQ_NEXT(STAILQ_FIRST(&queue1), entry),
+					   entry, &queue2);
+	printf("q1: %s\n", queue2str(&queue1));
+	printf("q2: %s\n", queue2str(&queue2));
+	STAILQ_SPLICE(&queue2, STAILQ_NEXT(STAILQ_FIRST(&queue2), entry),
+		      entry, &queue1);
+	printf("q1: %s\n", queue2str(&queue1));
+	printf("q2: %s\n", queue2str(&queue2));
+	STAILQ_SPLICE(&queue2, STAILQ_FIRST(&queue2), entry, &queue1);
+	printf("q1: %s\n", queue2str(&queue1));
+	printf("q2: %s\n", queue2str(&queue2));
+	STAILQ_SPLICE(&queue2, STAILQ_FIRST(&queue2), entry, &queue1);
+	printf("q1: %s\n", queue2str(&queue1));
+	printf("q2: %s\n", queue2str(&queue2));
+	footer();
+}
+
+int main()
+{
+	test0();
+	test1();
+	test2();
+	test3();
+	test_splice();
+	return 0;
+}
diff --git a/test/unit/queue.result b/test/unit/queue.result
new file mode 100644
index 0000000000..9839056432
--- /dev/null
+++ b/test/unit/queue.result
@@ -0,0 +1,37 @@
+	*** test0 ***
+Initialized: 
+STAILQ_INIT: 
+STAILQ_REVERSE: 
+	*** test0: done ***
+ 	*** test1 ***
+STAILQ_INIT: 1 
+STAILQ_REVERSE: 1 
+	*** test1: done ***
+ 	*** test2 ***
+STAILQ_INIT: 1 2 
+STAILQ_REVERSE: 2 1 
+	*** test2: done ***
+ 	*** test3 ***
+STAILQ_INIT: 1 2 3 
+STAILQ_REVERSE: 3 2 1 
+	*** test3: done ***
+ 	*** test_splice ***
+q1: 
+q2: 
+q1: 
+q2: 
+STAILQ_INIT: 1 2 3 
+q1: 
+q2: 1 2 3 
+q1: 1 2 3 
+q2: 
+q1: 1 
+q2: 2 3 
+q1: 1 3 
+q2: 2 
+q1: 1 3 2 
+q2: 
+q1: 1 3 2 
+q2: 
+	*** test_splice: done ***
+ 
\ No newline at end of file
diff --git a/test/unit/queue.test b/test/unit/queue.test
new file mode 100644
index 0000000000..78ef0b1753
--- /dev/null
+++ b/test/unit/queue.test
@@ -0,0 +1 @@
+run_test("queue")
diff --git a/test/unit/suite.ini b/test/unit/suite.ini
new file mode 100644
index 0000000000..47106bc06f
--- /dev/null
+++ b/test/unit/suite.ini
@@ -0,0 +1,10 @@
+[default]
+core = unittest
+module = box 
+description =  unit tests
+config = tarantool.cfg 
+# put disabled tests here
+#disabled = xlog.test
+# put disabled in valgrind test here
+#valgrind_disabled = admin_coredump.test
+#release_disabled = errinj.test
diff --git a/test/unit/unit.h b/test/unit/unit.h
new file mode 100644
index 0000000000..64b133fc1f
--- /dev/null
+++ b/test/unit/unit.h
@@ -0,0 +1,35 @@
+#ifndef INCLUDES_TARANTOOL_TEST_UNIT_H
+#define INCLUDES_TARANTOOL_TEST_UNIT_H
+/*
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define header() printf("\t*** %s ***\n", __FUNCTION__)
+#define footer() printf("\t*** %s: done ***\n ", __FUNCTION__)
+
+#endif /* INCLUDES_TARANTOOL_TEST_UNIT_H */
diff --git a/third_party/queue.h b/third_party/queue.h
index 2112256024..0eead7407d 100644
--- a/third_party/queue.h
+++ b/third_party/queue.h
@@ -304,6 +304,31 @@ struct {								\
 		(head)->stqh_last = &STAILQ_FIRST((head));		\
 } while (0)
 
+/* Reverse a list in-place. */
+#define STAILQ_REVERSE(head, type, member) do {				\
+	struct type *elem = STAILQ_FIRST(head), *next;			\
+	STAILQ_INIT(head);						\
+	while (elem) {							\
+		next = STAILQ_NEXT(elem, member);			\
+		STAILQ_INSERT_HEAD(head, elem, member);			\
+		elem = next;						\
+	}								\
+} while (0)
+
+/* Concat all members of head1 starting from elem to the end of head2. */
+#define STAILQ_SPLICE(head1, elem, member, head2) do {			\
+	if (elem) {							\
+		*(head2)->stqh_last = (elem);				\
+		(head2)->stqh_last = (head1)->stqh_last;		\
+		(head1)->stqh_last = &STAILQ_FIRST(head1);		\
+		while (*(head1)->stqh_last != (elem)) {			\
+			(head1)->stqh_last = &STAILQ_NEXT(		\
+				*(head1)->stqh_last, member);		\
+		}							\
+		*(head1)->stqh_last = NULL;				\
+	}								\
+} while (0)
+
 /*
  * List declarations.
  */
-- 
GitLab