diff --git a/.gitignore b/.gitignore
index b99d0325dc4025dc0980b3a4613e1327d3684316..cd299e2268dcf7fae5489505bbc3a1c15cbbac12 100644
--- a/.gitignore
+++ b/.gitignore
@@ -60,18 +60,7 @@ test/connector_c/update
 test/connector_c/xlog
 test/lib/*.pyc
 test/lib/*/*.pyc
-test/unit/base64
-test/unit/bit_test
-test/unit/bitset_basic_test
-test/unit/bitset_iterator_test
-test/unit/bitset_index_test
-test/unit/mhash
-test/unit/rlist
-test/unit/rope
-test/unit/rope_avl
-test/unit/rope_basic
-test/unit/rope_stress
-test/unit/queue
+test/unit/*.test
 test/var
 third_party/luajit/src/luajit
 third_party/luajit/lib/vmdef.lua
diff --git a/src/box/box_lua.cc b/src/box/box_lua.cc
index d426361ffddc1471f0ff087bd62010d9d15aa67a..4a22d69801b7d4cc0380573cc947adba44a46ed8 100644
--- a/src/box/box_lua.cc
+++ b/src/box/box_lua.cc
@@ -142,7 +142,8 @@ lbox_tuple_slice(struct lua_State *L)
 {
 	struct tuple *tuple = lua_checktuple(L, 1);
 	int argc = lua_gettop(L) - 1;
-	int start, end;
+	uint32_t start, end;
+	int offset;
 
 	/*
 	 * Prepare the range. The second argument is optional.
@@ -151,36 +152,45 @@ lbox_tuple_slice(struct lua_State *L)
 	 */
 	if (argc == 0 || argc > 2)
 		luaL_error(L, "tuple.slice(): bad arguments");
-	start = lua_tointeger(L, 2);
-	if (start < 0)
-		start += tuple->field_count;
+
+	offset = lua_tointeger(L, 2);
+	if (offset >= 0 && offset < tuple->field_count) {
+		start = offset;
+	} else if (offset < 0 && -offset <= tuple->field_count) {
+		start = offset + tuple->field_count;
+	} else {
+		luaL_error(L, "tuple.slice(): start >= field count");
+	}
+
 	if (argc == 2) {
-		end = lua_tointeger(L, 3);
-		if (end < 0)
-			end += tuple->field_count;
-		else if (end > tuple->field_count)
-			end = tuple->field_count;
+		offset = lua_tointeger(L, 3);
+		if (offset > 0 && offset <= tuple->field_count) {
+			end = offset;
+		} else if (offset < 0 && -offset < tuple->field_count) {
+			end = offset + tuple->field_count;
+		} else {
+			luaL_error(L, "tuple.slice(): end > field count");
+		}
 	} else {
 		end = tuple->field_count;
 	}
 	if (end <= start)
 		luaL_error(L, "tuple.slice(): start must be less than end");
 
-	u32 stop = end - 1;
-
 	struct tuple_iterator it;
 	tuple_rewind(&it, tuple);
 	const char *field;
 	uint32_t len;
-	uint32_t field_no = 0;
-	while ((field = tuple_next(&it, &len))) {
-		if (field_no >= start) {
-			lua_pushlstring(L, field, len);
-			if (field_no == stop)
-				break;
-		}
+
+	assert(start < tuple->field_count);
+	uint32_t field_no = start;
+	field = tuple_seek(&it, start, &len);
+	while (field && field_no < end) {
+		lua_pushlstring(L, field, len);
 		++field_no;
+		field = tuple_next(&it, &len);
 	}
+	assert(field_no == end);
 	return end - start;
 }
 
@@ -1176,17 +1186,11 @@ static void
 port_add_lua_ret(struct port *port, struct lua_State *L, int index)
 {
 	struct tuple *tuple = lua_totuple(L, index);
-	try {
-		port_add_tuple(port, tuple, BOX_RETURN_TUPLE);
-
-		if (tuple->refs == 0)
-			tuple_free(tuple);
-	} catch (...) {
+	auto scoped_guard = make_scoped_guard([=] {
 		if (tuple->refs == 0)
 			tuple_free(tuple);
-
-		throw;
-	}
+	});
+	port_add_tuple(port, tuple, BOX_RETURN_TUPLE);
 }
 
 /**
diff --git a/src/box/port.h b/src/box/port.h
index ba6e7333dae72929b0a09024f7e0d7a40821723a..cd3a8166952878a852368583ad2147d9c97639fe 100644
--- a/src/box/port.h
+++ b/src/box/port.h
@@ -33,6 +33,26 @@
 struct tuple;
 struct port;
 
+/**
+ * A single port represents a destination of box_process output.
+ * One such destination can be a Lua stack, or the binary
+ * protocol.
+ * An instance of a port is usually short lived, as it is created
+ * for every server request. State of the instance is represented
+ * by the tuples added to it. E.g.:
+ *
+ * struct port_iproto *port = port_iproto_new(...)
+ * for (tuple in tuples)
+ *	port_add_tuple(tuple);
+ * port_eof(port);	// end of request
+ *
+ * Beginning with Tarantool 1.5, tuple can have different internal
+ * structure and port_add_tuple() requires a double
+ * dispatch: first, by the type of the port the tuple is being
+ * added to, second, by the type of the tuple format, since the
+ * format defines the internal structure of the tuple.
+ */
+
 struct port_vtab
 {
 	void (*add_tuple)(struct port *port, struct tuple *tuple, u32 flags);
diff --git a/src/iproto.cc b/src/iproto.cc
index 50fb27c9b872e8324d7a48bc22229f4b8ed593ff..d49410ccf5cbdddcd64c580c9780803b2d3b7ade 100644
--- a/src/iproto.cc
+++ b/src/iproto.cc
@@ -132,6 +132,12 @@ port_iproto_eof(struct port *ptr)
 	}
 }
 
+static inline void
+tuple_to_iproto(struct port_iproto *port, struct tuple *tuple)
+{
+	obuf_dup(port->buf, &tuple->bsize, tuple_len(tuple));
+}
+
 static void
 port_iproto_add_tuple(struct port *ptr, struct tuple *tuple, u32 flags)
 {
@@ -141,7 +147,7 @@ port_iproto_add_tuple(struct port *ptr, struct tuple *tuple, u32 flags)
 		port->svp = obuf_book(port->buf, sizeof(port->reply));
 	}
 	if (flags & BOX_RETURN_TUPLE) {
-		obuf_dup(port->buf, &tuple->bsize, tuple_len(tuple));
+		tuple_to_iproto(port, tuple);
 	}
 }
 
diff --git a/test/.gitattributes b/test/.gitattributes
new file mode 100644
index 0000000000000000000000000000000000000000..120b07e8db944c0d3dd22357e61acba1622d6666
--- /dev/null
+++ b/test/.gitattributes
@@ -0,0 +1,2 @@
+*.result diff merge=text
+
diff --git a/test/box/lua.result b/test/box/lua.result
index 7a927c5d5bb0977bb88842597357505538b0e512..fcef60120cd3c72ca4617ee5853253f01ad7e998 100644
--- a/test/box/lua.result
+++ b/test/box/lua.result
@@ -1419,12 +1419,12 @@ lua floor(1.1)
 
 # Test box.tuple:slice()
 
-lua t=box.space[0]:insert(0, '1', '2', '3', '4', '5', '6', '7')
+lua t=box.tuple.new({'0', '1', '2', '3', '4', '5', '6', '7'})
 ---
 ...
 lua t:slice(0)
 ---
- - 
+ - 0
  - 1
  - 2
  - 3
@@ -1473,26 +1473,75 @@ lua t:slice(7)
 ---
  - 7
 ...
+lua t:slice(8)
+---
+error: 'tuple.slice(): start >= field count'
+...
 lua t:slice(9)
 ---
-error: 'tuple.slice(): start must be less than end'
+error: 'tuple.slice(): start >= field count'
+...
+lua t:slice(100500)
+---
+error: 'tuple.slice(): start >= field count'
 ...
 lua t:slice(9, -1)
 ---
-error: 'tuple.slice(): start must be less than end'
+error: 'tuple.slice(): start >= field count'
 ...
 lua t:slice(6, -1)
 ---
  - 6
 ...
-lua t:slice(9, 10)
+lua t:slice(4, 4)
 ---
 error: 'tuple.slice(): start must be less than end'
 ...
-lua t:slice(500, 700)
+lua t:slice(6, 4)
 ---
 error: 'tuple.slice(): start must be less than end'
 ...
+lua t:slice(0, 0)
+---
+error: 'tuple.slice(): end > field count'
+...
+lua t:slice(9, 10)
+---
+error: 'tuple.slice(): start >= field count'
+...
+lua t:slice(-7)
+---
+ - 1
+ - 2
+ - 3
+ - 4
+ - 5
+ - 6
+ - 7
+...
+lua t:slice(-8)
+---
+ - 0
+ - 1
+ - 2
+ - 3
+ - 4
+ - 5
+ - 6
+ - 7
+...
+lua t:slice(-9)
+---
+error: 'tuple.slice(): start >= field count'
+...
+lua t:slice(-100500)
+---
+error: 'tuple.slice(): start >= field count'
+...
+lua t:slice(500, 700)
+---
+error: 'tuple.slice(): start >= field count'
+...
 lua box.space[0]:truncate()
 ---
 ...
diff --git a/test/box/lua.test b/test/box/lua.test
index b4d775bfa704d7d9996a68ef7ce2135d89d31ef4..c5f711ff0cb46229545901c8b1d2d2e41d52602d 100644
--- a/test/box/lua.test
+++ b/test/box/lua.test
@@ -490,7 +490,7 @@ server.deploy(init_lua=None)
 print """
 # Test box.tuple:slice()
 """
-exec admin "lua t=box.space[0]:insert(0, '1', '2', '3', '4', '5', '6', '7')"
+exec admin "lua t=box.tuple.new({'0', '1', '2', '3', '4', '5', '6', '7'})"
 exec admin "lua t:slice(0)"
 exec admin "lua t:slice(-1)"
 exec admin "lua t:slice(1)"
@@ -499,10 +499,19 @@ exec admin "lua t:slice(-1, 1)"
 exec admin "lua t:slice(1, -1)"
 exec admin "lua t:slice(1, 3)"
 exec admin "lua t:slice(7)"
+exec admin "lua t:slice(8)"
 exec admin "lua t:slice(9)"
+exec admin "lua t:slice(100500)"
 exec admin "lua t:slice(9, -1)"
 exec admin "lua t:slice(6, -1)"
+exec admin "lua t:slice(4, 4)"
+exec admin "lua t:slice(6, 4)"
+exec admin "lua t:slice(0, 0)"
 exec admin "lua t:slice(9, 10)"
+exec admin "lua t:slice(-7)"
+exec admin "lua t:slice(-8)"
+exec admin "lua t:slice(-9)"
+exec admin "lua t:slice(-100500)"
 exec admin "lua t:slice(500, 700)"
 exec admin "lua box.space[0]:truncate()"
 
@@ -672,3 +681,4 @@ exec admin  "lua box.tuple.new({false})"
 exec admin "lua t = box.tuple.new('abc')"
 exec admin "lua t"
 exec admin "lua t:bsize()"
+exec admin silent "lua box.delete(0, 8989)"
diff --git a/test/connector_c/xlog_rpl.test b/test/connector_c/xlog_rpl.test
index f53063e350344442202df0b32f00c40e1cc2b1a9..5f774f785bc7ec7e841b009056badff89581d3d4 100644
--- a/test/connector_c/xlog_rpl.test
+++ b/test/connector_c/xlog_rpl.test
@@ -2,7 +2,7 @@ import subprocess
 import sys
 import os
 
-from lib.tarantool_box_server import TarantoolBoxServer
+from lib.tarantool_server import TarantoolServer
 
 p = subprocess.Popen([os.path.join(builddir, "test/connector_c/xlog"),
 		              os.path.abspath("connector_c/connector.xlog")],
diff --git a/test/lib/server.py b/test/lib/server.py
index 5730ffe979aa4a1a138528f5ee11b3b85420e8c9..8f292b14cb33b00a38aadc3ae36d96b467f1d8ca 100644
--- a/test/lib/server.py
+++ b/test/lib/server.py
@@ -57,17 +57,16 @@ class Server(object):
     replication slaves. The server is started once at the beginning
     of each suite, and stopped at the end."""
 
-    def __new__(cls, core=None, module=None):
+    def __new__(cls, core=None):
         if core  == None:
             return super(Server, cls).__new__(cls)
         mdlname = "lib.{0}_server".format(core)
         clsname = "{0}Server".format(core.title())
         corecls = __import__(mdlname, fromlist=clsname).__dict__[clsname]
-        return corecls.__new__(corecls, core, module)
+        return corecls.__new__(corecls, core)
 
-    def __init__(self, core, module):
+    def __init__(self, core):
         self.core = core
-        self.module = module
         self.re_vardir_cleanup = ['*.core.*', 'core']
         self.process = None
         self.default_config_name = None
@@ -75,7 +74,7 @@ class Server(object):
         self.config = None
         self.vardir = None
         self.valgrind_log = "valgrind.log"
-        self.valgrind_sup = os.path.join("share/", "%s_%s.sup" % (core, module))
+        self.valgrind_sup = os.path.join("share/", "%s.sup" % (core))
         self.init_lua = None
         self.default_suppression_name = "valgrind.sup"
         self.pidfile = None
@@ -89,14 +88,13 @@ class Server(object):
 
     def find_exe(self, builddir, silent=True):
         "Locate server executable in the build dir or in the PATH."
-        exe_name = self.default_bin_name()
         path = builddir + os.pathsep + os.environ["PATH"]
 
         if not silent:
             print "  Looking for server binary in {0} ...".format(path)
 
         for dir in path.split(os.pathsep):
-            exe = os.path.join(dir, exe_name)
+            exe = os.path.join(dir, self.default_bin_name)
             if os.access(exe, os.X_OK):
                 return exe
 
diff --git a/test/lib/tarantool_box_server.py b/test/lib/tarantool_box_server.py
deleted file mode 100644
index 1e68c5a6e953aa9179fb46af928ef80d66327485..0000000000000000000000000000000000000000
--- a/test/lib/tarantool_box_server.py
+++ /dev/null
@@ -1,62 +0,0 @@
-import time
-import shutil
-import subprocess
-import yaml
-import ConfigParser
-from tarantool_server import TarantoolServer, TarantoolConfigFile
-from admin_connection import AdminConnection
-from box_connection import BoxConnection
-from memcached_connection import MemcachedConnection
-import time
-
-class TarantoolBoxServer(TarantoolServer):
-    def __new__(cls, core="tarantool", module="box"):
-        return TarantoolServer.__new__(cls)
-
-    def __init__(self, core="tarantool", module="box"):
-        TarantoolServer.__init__(self, core, module)
-
-    def configure(self, config):
-        TarantoolServer.configure(self, config)
-        with open(self.config) as fp:
-            dummy_section_name = "tarantool"
-            config = ConfigParser.ConfigParser()
-            config.readfp(TarantoolConfigFile(fp, dummy_section_name))
-
-            self.primary_port = self.get_option_int(config, dummy_section_name, "primary_port")
-            self.admin_port = self.get_option_int(config, dummy_section_name, "admin_port")
-            self.memcached_port = self.get_option_int(config, dummy_section_name, "memcached_port")
-
-        self.port = self.admin_port
-        self.admin = AdminConnection("localhost", self.admin_port)
-        self.sql = BoxConnection("localhost", self.primary_port)
-        if self.memcached_port != 0:
-            # Run memcached client
-            self.memcached = MemcachedConnection('localhost', self.memcached_port)
-
-    def get_option_int(self, config, section, option):
-        if config.has_option(section, option):
-            return config.getint(section, option)
-        else:
-            return 0
-
-    def init(self):
-        # init storage
-        subprocess.check_call([self.binary, "--init-storage"],
-                              cwd = self.vardir,
-                              # catch stdout/stderr to not clutter output
-                              stdout = subprocess.PIPE,
-                              stderr = subprocess.PIPE)
-
-    def get_param(self, param):
-        data = self.admin.execute("show info", silent = True)
-        info = yaml.load(data)["info"]
-        return info[param]
-
-    def wait_lsn(self, lsn):
-        while True:
-            curr_lsn = int(self.get_param("lsn"))
-            if (curr_lsn >= lsn):
-                break
-            time.sleep(0.01)
-
diff --git a/test/lib/tarantool_server.py b/test/lib/tarantool_server.py
index ec9c3b527a3962adb303f3564cf08ab5617fab85..5ae0eb9f10dce8c85e29b92a932ae85352aed928 100644
--- a/test/lib/tarantool_server.py
+++ b/test/lib/tarantool_server.py
@@ -1,9 +1,67 @@
 import os
+import sys
+import glob
+import time
+import yaml
 import shutil
-import subprocess
 import pexpect
+import traceback
+import subprocess
 import ConfigParser
+import pprint
+
+import tarantool_preprocessor
+
 from server import Server
+from box_connection import BoxConnection
+from test_suite import FilteredStream, Test
+from admin_connection import AdminConnection
+from memcached_connection import MemcachedConnection
+
+
+class FuncTest(Test):
+    def __init__(self, name, args, suite_ini):
+        Test.__init__(self, name, args, suite_ini)
+        self.name = name
+        self.result = name.replace(".test", ".result")
+        self.skip_cond = name.replace(".test", ".skipcond")
+        self.tmp_result = os.path.join(self.args.vardir,
+                os.path.basename(self.result))
+        self.reject = "{0}/test/{1}".format(self.args.builddir,
+                name.replace(".test", ".reject"))
+
+    def execute(self, server):
+        diagnostics = "unknown"
+        builddir = self.args.builddir
+        save_stdout = sys.stdout
+        try:
+            self.skip = 0
+            if os.path.exists(self.skip_cond):
+                sys.stdout = FilteredStream(self.tmp_result)
+                stdout_fileno = sys.stdout.stream.fileno()
+                execfile(self.skip_cond, dict(locals(), **server.__dict__))
+                sys.stdout.close()
+                sys.stdout = save_stdout
+            if not self.skip:
+                sys.stdout = FilteredStream(self.tmp_result)
+                stdout_fileno = sys.stdout.stream.fileno()
+                execfile(self.name, dict(locals(), **server.__dict__))
+            self.is_executed_ok = True
+        except Exception as e:
+            traceback.print_exc(e)
+            diagnostics = str(e)
+        finally:
+            if sys.stdout and sys.stdout != save_stdout:
+                sys.stdout.close()
+            sys.stdout = save_stdout;
+        self.is_executed = True
+
+    def __repr__(self):
+        return str([self.name, self.result, self.skip_cond, self.tmp_result,
+        self.reject])
+
+    __str__ = __repr__
+
 
 class TarantoolConfigFile:
     """ConfigParser can't read files without sections, work it around"""
@@ -19,19 +77,13 @@ class TarantoolConfigFile:
         # tarantool.cfg puts string values in quote
         return self.fp.readline().replace("\"", '')
 
-
 class TarantoolServer(Server):
-    def __new__(cls, core=None, module=None):
-        if module  == None:
-            return super(Server, cls).__new__(cls)
-        mdlname = "lib.{0}_{1}_server".format(core, module)
-        clsname = "{0}{1}Server".format(core.title(), module.title())
-        modulecls = __import__(mdlname, fromlist=clsname).__dict__[clsname]
+    def __new__(cls, core="tarantool"):
+        return super(Server, cls).__new__(cls)
 
-        return modulecls.__new__(modulecls, core, module)
-
-    def __init__(self, core, module):
-        Server.__init__(self, core, module)
+    def __init__(self, core="tarantool"):
+        Server.__init__(self, core)
+        self.default_bin_name = "tarantool_box"
         self.default_config_name = "tarantool.cfg"
         self.default_init_lua_name = "init.lua"
         # append additional cleanup patterns
@@ -43,7 +95,7 @@ class TarantoolServer(Server):
                                    '*.lua']
 
     def find_exe(self, builddir, silent=True):
-        return Server.find_exe(self, "{0}/src/{1}".format(builddir, self.module), silent)
+        return Server.find_exe(self, "{0}/src/box/".format(builddir), silent)
 
     def configure(self, config):
         Server.configure(self, config)
@@ -52,7 +104,24 @@ class TarantoolServer(Server):
             dummy_section_name = "tarantool"
             config = ConfigParser.ConfigParser()
             config.readfp(TarantoolConfigFile(fp, dummy_section_name))
+
             self.pidfile = config.get(dummy_section_name, "pid_file")
+            self.primary_port = self.get_option_int(config, dummy_section_name, "primary_port")
+            self.admin_port = self.get_option_int(config, dummy_section_name, "admin_port")
+            self.memcached_port = self.get_option_int(config, dummy_section_name, "memcached_port")
+
+        self.port = self.admin_port
+        self.admin = AdminConnection("localhost", self.admin_port)
+        self.sql = BoxConnection("localhost", self.primary_port)
+        if self.memcached_port != 0:
+            # Run memcached client
+            self.memcached = MemcachedConnection('localhost', self.memcached_port)
+
+    def find_tests(self, test_suite, suite_path):
+        for test_name in sorted(glob.glob(os.path.join(suite_path, "*.test"))):
+            for test_pattern in test_suite.args.tests:
+                if test_name.find(test_pattern) != -1:
+                    test_suite.tests.append(FuncTest(test_name, test_suite.args, test_suite.ini))
 
     def reconfigure(self, config, silent=False):
         if config == None:
@@ -62,6 +131,32 @@ class TarantoolServer(Server):
             shutil.copy(self.config, os.path.join(self.vardir, self.default_config_name))
         self.admin.execute("reload configuration", silent=silent)
 
+    def get_option_int(self, config, section, option):
+        if config.has_option(section, option):
+            return config.getint(section, option)
+        else:
+            return 0
+
+    def init(self):
+        # init storage
+        subprocess.check_call([self.binary, "--init-storage"],
+                              cwd = self.vardir,
+                              # catch stdout/stderr to not clutter output
+                              stdout = subprocess.PIPE,
+                              stderr = subprocess.PIPE)
+
+    def get_param(self, param):
+        data = self.admin.execute("show info", silent = True)
+        info = yaml.load(data)["info"]
+        return info[param]
+
+    def wait_lsn(self, lsn):
+        while True:
+            curr_lsn = int(self.get_param("lsn"))
+            if (curr_lsn >= lsn):
+                break
+            time.sleep(0.01)
+
     def version(self):
         p = subprocess.Popen([self.binary, "--version"],
                              cwd = self.vardir,
@@ -83,7 +178,3 @@ class TarantoolServer(Server):
                 raise RuntimeError("'--gdb' and '--start-and-exit' can't be defined together")
             self.server = pexpect.spawn(args[0], args[1:], cwd = self.vardir)
             self.server.wait()
-
-    def default_bin_name(self):
-        return "{0}_{1}".format(self.core, self.module)
-
diff --git a/test/lib/test_suite.py b/test/lib/test_suite.py
index f3dfceb55d665b356eed08884dc716bddb547fa4..768086630dfa318925523a38cdc215647789633b 100644
--- a/test/lib/test_suite.py
+++ b/test/lib/test_suite.py
@@ -10,6 +10,7 @@ import difflib
 import filecmp
 import shlex
 import time
+
 from server import Server
 import tarantool_preprocessor
 import re
@@ -35,7 +36,7 @@ class FilteredStream:
             for pattern, replacement in self.filters:
                 line = re.sub(pattern, replacement, line)
                 # don't write lines that are completely filtered out:
-                skipped = original_len and len(line.strip()) == 0
+                skipped = original_len and not line.strip()
                 if skipped:
                     break
             if not skipped:
@@ -87,8 +88,8 @@ class Test:
         self.skip_cond = name.replace(".test", ".skipcond")
         self.tmp_result = os.path.join(self.args.vardir,
                                        os.path.basename(self.result))
-	self.reject = "{0}/test/{1}".format(self.args.builddir,
-			                    name.replace(".test", ".reject"))
+        self.reject = "{0}/test/{1}".format(self.args.builddir,
+                                            name.replace(".test", ".reject"))
         self.is_executed = False
         self.is_executed_ok = None
         self.is_equal_result = None
@@ -109,31 +110,8 @@ class Test:
 
 
         diagnostics = "unknown"
-        save_stdout = sys.stdout
         builddir = self.args.builddir
-        try:
-            self.skip = 0
-            if os.path.exists(self.skip_cond):
-                sys.stdout = FilteredStream(self.tmp_result)
-                stdout_fileno = sys.stdout.stream.fileno()
-                execfile(self.skip_cond, dict(locals(), **server.__dict__))
-                sys.stdout.close()
-                sys.stdout = save_stdout
-
-
-            if not self.skip:
-                sys.stdout = FilteredStream(self.tmp_result)
-                stdout_fileno = sys.stdout.stream.fileno()
-                execfile(self.name, dict(locals(), **server.__dict__))
-            self.is_executed_ok = True
-        except Exception as e:
-            traceback.print_exc(e)
-            diagnostics = str(e)
-        finally:
-            if sys.stdout and sys.stdout != save_stdout:
-                sys.stdout.close()
-            sys.stdout = save_stdout;
-
+        self.execute(server)
         self.is_executed = True
 
         if not self.skip:
@@ -148,7 +126,8 @@ class Test:
 
         if self.skip:
             print "[ skip ]"
-
+            if os.path.exists(self.tmp_result):
+                os.remove(self.tmp_result)
         elif self.is_executed_ok and self.is_equal_result and self.is_valgrind_clean:
             print "[ pass ]"
             if os.path.exists(self.tmp_result):
@@ -223,7 +202,6 @@ class TestSuite:
         self.ini = {}
 
         self.ini["core"] = "tarantool"
-        self.ini["module"] = "box"
 
         if os.access(suite_path, os.F_OK) == False:
             raise RuntimeError("Suite \"" + suite_path + \
@@ -233,68 +211,49 @@ class TestSuite:
         config = ConfigParser.ConfigParser()
         config.read(os.path.join(suite_path, "suite.ini"))
         self.ini.update(dict(config.items("default")))
-        self.ini["config"] = os.path.join(suite_path, self.ini["config"])
-
-        if self.ini.has_key("init_lua"):
-            self.ini["init_lua"] = os.path.join(suite_path,
-                                                self.ini["init_lua"])
-        else:
-            self.ini["init_lua"] = None
 
-        if self.ini.has_key("disabled"):
-            self.ini["disabled"] = dict.fromkeys(self.ini["disabled"].split(" "))
-        else:
-            self.ini["disabled"] = dict()
-
-        if self.ini.has_key("valgrind_disabled"):
-            self.ini["valgrind_disabled"] = dict.fromkeys(self.ini["valgrind_disabled"].split(" "))
-        else:
-            self.ini["valgrind_disabled"] = dict()
+        for i in ["config", "init_lua"]:
+            self.ini[i] = os.path.join(suite_path, self.ini[i]) if i in self.ini else None
+        for i in ["disabled", "valgrind_disabled", "release_disabled"]:
+            self.ini[i] = dict.fromkeys(self.ini[i].split()) if i in self.ini else dict()
 
-        if self.ini.has_key("release_disabled"):
-            self.ini["release_disabled"] = dict.fromkeys(self.ini["release_disabled"].split(" "))
-        else:
-            self.ini["release_disabled"] = dict()
+        try:
+            self.server = Server(self.ini["core"])
+            self.ini["server"] = self.server
+        except Exception as e:
+            print e
+            raise RuntimeError("Unknown server: core = {0}".format(
+                               self.ini["core"]))
 
         print "Collecting tests in \"" + suite_path + "\": " +\
             self.ini["description"] + "."
-
-        for test_name in glob.glob(os.path.join(suite_path, "*.test")):
-            for test_pattern in self.args.tests:
-                if test_name.find(test_pattern) != -1:
-                    self.tests.append(Test(test_name, self.args, self.ini))
+        self.server.find_tests(self, suite_path);
         print "Found " + str(len(self.tests)) + " tests."
 
     def run_all(self):
         """For each file in the test suite, run client program
         assuming each file represents an individual test."""
-        try:
-            server = Server(self.ini["core"], self.ini["module"])
-        except Exception as e:
-            print e
-            raise RuntimeError("Unknown server: core = {0}, module = {1}".format(
-                               self.ini["core"], self.ini["module"]))
 
-        if len(self.tests) == 0:
+        if not self.tests:
             # noting to test, exit
             return 0
 
-        server.deploy(self.ini["config"],
-                      server.find_exe(self.args.builddir, silent=False),
+        self.server.deploy(self.ini["config"],
+                      self.server.find_exe(self.args.builddir, silent=False),
                       self.args.vardir, self.args.mem, self.args.start_and_exit,
                       self.args.gdb, self.args.valgrind,
                       init_lua=self.ini["init_lua"], silent=False)
+
         if self.args.start_and_exit:
             print "  Start and exit requested, exiting..."
             exit(0)
 
-        longsep = "=============================================================================="
-        shortsep = "------------------------------------------------------------"
+        longsep = '='*70
+        shortsep = '-'*60
         print longsep
         print string.ljust("TEST", 48), "RESULT"
         print shortsep
         failed_tests = []
-        self.ini["server"] = server
 
         for test in self.tests:
             sys.stdout.write(string.ljust(test.name, 48))
@@ -302,27 +261,26 @@ class TestSuite:
             sys.stdout.flush()
 
             test_name = os.path.basename(test.name)
-            if test_name in self.ini["disabled"]:
-                print "[ disabled ]"
-            elif not server.debug and test_name in self.ini["release_disabled"]:
-                print "[ disabled ]"
-            elif self.args.valgrind and test_name in self.ini["valgrind_disabled"]:
+
+            if (test_name in self.ini["disabled"]
+                or not self.server.debug and test_name in self.ini["release_disabled"]
+                or self.args.valgrind and test_name in self.ini["valgrind_disabled"]):
                 print "[ disabled ]"
             else:
-                test.run(server)
+                test.run(self.server)
                 if not test.passed():
                     failed_tests.append(test.name)
 
         print shortsep
-        if len(failed_tests):
+        if failed_tests:
             print "Failed {0} tests: {1}.".format(len(failed_tests),
                                                 ", ".join(failed_tests))
-        server.stop(silent=False)
-        server.cleanup()
+        self.server.stop(silent=False)
+        self.server.cleanup()
 
-        if self.args.valgrind and check_valgrind_log(server.valgrind_log):
+        if self.args.valgrind and check_valgrind_log(self.server.valgrind_log):
             print "  Error! There were warnings/errors in valgrind log file:"
-            print_tail_n(server.valgrind_log, 20)
+            print_tail_n(self.server.valgrind_log, 20)
             return 1
         return len(failed_tests)
 
diff --git a/test/lib/unittest_server.py b/test/lib/unittest_server.py
index f63b33be1f44b6b54eebf0e3972e94cdef4e40a6..24ec2597d193e81acdb8254068c5e86d48c15818 100644
--- a/test/lib/unittest_server.py
+++ b/test/lib/unittest_server.py
@@ -1,16 +1,70 @@
-from server import Server
-import subprocess
-import sys
 import os
+import re
+import sys
+import traceback
+import subprocess
+
+from subprocess import Popen, PIPE
+
+import tarantool_preprocessor
+
+from server import Server
+from test_suite import FilteredStream, Test
+
+
+class UnitTest(Test):
+    def __init__(self, name, args, suite_ini):
+        Test.__init__(self, name, args, suite_ini)
+        self.name = name + ".test"
+        self.result = name + ".result"
+        self.skip_cond = name + ".skipcond"
+        self.tmp_result = os.path.join(self.args.vardir,
+                                       os.path.basename(self.result))
+        self.reject = "{0}/test/{1}".format(self.args.builddir, name + ".reject")
+
+    def execute(self, server):
+        diagnostics = "unknown"
+        builddir = self.args.builddir
+        save_stdout = sys.stdout
+        try:
+            self.skip = 0
+            if os.path.exists(self.skip_cond):
+                sys.stdout = FilteredStream(self.tmp_result)
+                stdout_fileno = sys.stdout.stream.fileno()
+                execfile(self.skip_cond, dict(locals(), **server.__dict__))
+                sys.stdout.close()
+                sys.stdout = save_stdout
+            if not self.skip:
+                sys.stdout = FilteredStream(self.tmp_result)
+                stdout_fileno = sys.stdout.stream.fileno()
+                execs = [os.path.join(server.builddir, "test", self.name)]
+                proc = Popen(execs, stdout=PIPE)
+                sys.stdout.write(proc.communicate()[0])
+            self.is_executed_ok = True
+        except Exception as e:
+            traceback.print_exc(e)
+            diagnostics = str(e)
+        finally:
+            if sys.stdout and sys.stdout != save_stdout:
+                sys.stdout.close()
+            sys.stdout = save_stdout;
+        self.is_executed = True
+
+    def __repr__(self):
+        return str([self.name, self.result, self.skip_cond, self.tmp_result,
+        self.reject])
+
+    __str__ = __repr__
+
 
 class UnittestServer(Server):
     """A dummy server implementation for unit test suite"""
-    def __new__(cls, core="unittest", module="dummy"):
+    def __new__(cls, core="unittest"):
         return Server.__new__(cls)
 
 
-    def __init__(self, core="unittest", module="dummy"):
-        Server.__init__(self, core, module)
+    def __init__(self, core="unittest"):
+        Server.__init__(self, core)
         self.debug = False
 
     def configure(self, config):
@@ -18,14 +72,8 @@ class UnittestServer(Server):
     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
+        self.vardir = vardir
         if not os.access(self.vardir, os.F_OK):
             if (self.mem == True and check_tmpfs_exists() and
                 os.path.basename(self.vardir) == self.vardir):
@@ -39,5 +87,21 @@ class UnittestServer(Server):
     def find_exe(self, builddir, silent=False):
         self.builddir = builddir
 
+    def find_tests(self, test_suite, suite_path):
+        def patterned(name):
+            for i in test_suite.args.tests:
+                if name.find(i) != -1:
+                    return True
+            return False
+
+        regexp = re.compile('([a-zA-Z0-9_]*).test')
+        for f in sorted(os.listdir(suite_path)):
+            if regexp.match(f):
+                f = os.path.join(suite_path, regexp.match(f).groups()[0]) + '.test'
+                if os.access(f, os.X_OK) and os.path.isfile(f) and patterned(f):
+                    test_suite.tests.append(UnitTest(f[:-5], test_suite.args,
+                                test_suite.ini));
+
+
     def init(self):
         pass
diff --git a/test/memcached/tarantool.cfg b/test/memcached/cfg/master.cfg
similarity index 79%
rename from test/memcached/tarantool.cfg
rename to test/memcached/cfg/master.cfg
index b3c09590bda0ac3e1ee14513a9863ebd7514adb5..0f83dcb1823bc095c9669de5274e848d81c8d448 100644
--- a/test/memcached/tarantool.cfg
+++ b/test/memcached/cfg/master.cfg
@@ -1,15 +1,18 @@
 slab_alloc_arena = 0.1
 
 pid_file = "box.pid"
-
 logger="cat - >> tarantool.log"
+bind_ipaddr="INADDR_ANY"
+custom_proc_title="master"
 
 primary_port = 33013
 secondary_port = 33014
 admin_port = 33015
 memcached_port = 33016
 
-rows_per_wal = 50
+replication_port = 33017
+
+rows_per_wal = 200
 
 space[0].enabled = 1
 space[0].index[0].type = "HASH"
diff --git a/test/memcached/tarantool_memcached_bad.cfg b/test/memcached/cfg/tarantool_memcached_bad.cfg
similarity index 100%
rename from test/memcached/tarantool_memcached_bad.cfg
rename to test/memcached/cfg/tarantool_memcached_bad.cfg
diff --git a/test/memcached/tarantool_memcached_off.cfg b/test/memcached/cfg/tarantool_memcached_off.cfg
similarity index 100%
rename from test/memcached/tarantool_memcached_off.cfg
rename to test/memcached/cfg/tarantool_memcached_off.cfg
diff --git a/test/memcached/expirations.test b/test/memcached/expirations.test
index c941333f8fa1a538ef4ce59603fb7c236c419f39..5197d08aa7756a9c7209aa1aeaade2964911db3a 100644
--- a/test/memcached/expirations.test
+++ b/test/memcached/expirations.test
@@ -1,5 +1,24 @@
 # encoding: tarantool
 import time
+import yaml
+
+###################################
+def wait_for_next_lsn(lsn, serv):
+    serv_admin = serv.admin
+    while True:
+        if get_lsn(serv) != lsn:
+            return lsn
+        time.sleep(0.01)
+
+def get_lsn(serv):
+    serv_admin = serv.admin
+    resp = exec serv_admin silent "lua box.info.lsn"
+    return yaml.load(resp)[0]
+
+def wait(serv = server):
+    lsn = get_lsn(serv)
+    return wait_for_next_lsn(lsn, serv)
+###################################
 
 print """# expire: after 1 second"""
 
@@ -9,7 +28,7 @@ exec memcached "set foo 0 1 6\r\nfooval\r\n"
 print """# foo shoud be exists"""
 exec memcached "get foo\r\n"
 
-time.sleep(1.5)
+wait()
 print """# foo shoud expired"""
 exec memcached "get foo\r\n"
 
@@ -33,11 +52,10 @@ exec memcached silent "set foo 0 %d 6\r\nfooval\r\n" % expire
 print """# foo shoud be exists"""
 exec memcached "get foo\r\n"
 
-time.sleep(2.2)
+wait()
 print """# foo shoud expired"""
 exec memcached "get foo\r\n"
 
-
 print """# expire: time - 20 second"""
 expire = time.time() - 20
 
@@ -47,7 +65,6 @@ exec memcached silent "set boo 0 %d 6\r\nbooval\r\n" % expire
 print """# foo shoud expired"""
 exec memcached "get boo\r\n"
 
-
 print """# expire: after 2 second"""
 
 print """# add add"""
@@ -56,7 +73,7 @@ exec memcached "add add 0 1 6\r\naddval\r\n"
 print """# readd add - shoud be fail"""
 exec memcached "add add 0 1 7\r\naddval1\r\n"
 
-time.sleep(2.2)
+wait()
 print """# readd add - shoud be success"""
 exec memcached "add add 0 1 7\r\naddval2\r\n"
 
diff --git a/test/memcached/flush-all.result b/test/memcached/flush-all.result
index 6722b889b4bca9cdbb3583ef7ea9bc1277ad468f..245d0f0f7421ceaef53c819f28dc51c2f9dd1bc0 100644
--- a/test/memcached/flush-all.result
+++ b/test/memcached/flush-all.result
@@ -19,7 +19,7 @@ VALUE foo 0 3
 new
 END
 # and the other form, specifying a flush_all time... 
-flush_all time + 2
+flush_all time + 1
 OK
 
 get foo
diff --git a/test/memcached/flush-all.test b/test/memcached/flush-all.test
index d7957749b7e09376e59b68c8558c1002d8a95ac2..41693ed020500708599c612e9e19197ad75a935e 100644
--- a/test/memcached/flush-all.test
+++ b/test/memcached/flush-all.test
@@ -1,5 +1,20 @@
 # encoding: tarantool
 import time
+import yaml
+
+###################################
+def get_memcached_len(serv):
+    serv_admin = serv.admin
+    resp = exec serv_admin silent "lua box.space[box.cfg.memcached_space]:len()"
+    return yaml.load(resp)[0]
+
+def wait_for_empty_space(serv = server):
+    serv_admin = serv.admin
+    while True:
+        if get_memcached_len(serv) == 0:
+            return
+        time.sleep(0.01)
+###################################
 
 print """# Test flush_all with zero delay. """
 exec memcached "set foo 0 0 6\r\nfooval\r\n"
@@ -12,14 +27,14 @@ exec memcached "set foo 0 0 3\r\nnew\r\n"
 exec memcached "get foo\r\n"
 
 print """# and the other form, specifying a flush_all time... """
-expire = time.time() + 2
-print "flush_all time + 2"
+expire = time.time() + 1
+print "flush_all time + 1"
 print exec memcached silent "flush_all %d\r\n" % expire
 exec memcached "get foo\r\n"
 
 exec memcached "set foo 0 0 3\r\n123\r\n"
 exec memcached "get foo\r\n"
-time.sleep(2.2)
+wait_for_empty_space()
 exec memcached "get foo\r\n"
 
 # resore default suite config
diff --git a/test/memcached/off.test b/test/memcached/off.test
index c886dede1c5f479b6cd859cf64dd8fc2fd7676d8..d7859dabc14f667bddaf664e91d5e9c5cf70fe73 100644
--- a/test/memcached/off.test
+++ b/test/memcached/off.test
@@ -15,7 +15,7 @@ print """
 # stop current server
 server.stop()
 # start server with memcached off
-server.deploy("memcached/tarantool_memcached_off.cfg")
+server.deploy("memcached/cfg/tarantool_memcached_off.cfg")
 # check values
 exec admin "show configuration"
 
@@ -23,7 +23,7 @@ exec admin "show configuration"
 server.stop()
 # start server with memcached space conflict
 sys.stdout.push_filter("(/\S+)+/tarantool", "tarantool")
-server.test_option("-c " + os.path.join(os.getcwd(), "memcached/tarantool_memcached_bad.cfg"))
+server.test_option("-c " + os.path.join(os.getcwd(), "memcached/cfg/tarantool_memcached_bad.cfg"))
 sys.stdout.pop_filter()
 
 # restore default server
diff --git a/test/memcached/suite.ini b/test/memcached/suite.ini
index abd820de1872dcd76f93f0274895e73ac37a76fa..3c8a675998a5d0f4ceb239807d7756b892380f4d 100644
--- a/test/memcached/suite.ini
+++ b/test/memcached/suite.ini
@@ -1,6 +1,6 @@
 [default]
 description = tarantool/box memcached tests
-config = tarantool.cfg
+config = cfg/master.cfg
 disabled = cas.test
 # put disabled in valgrind test here
-valgrind_disabled = expirations.test
+valgrind_disabled = expirations.test, repl.test
diff --git a/test/replication/cfg/hot_standby.cfg b/test/replication/cfg/hot_standby.cfg
index fc74b3539f9fe30a6b173f2f691567e6ad56d7ce..580e073cfecfae5cab5e873d8f398feda38e884b 100644
--- a/test/replication/cfg/hot_standby.cfg
+++ b/test/replication/cfg/hot_standby.cfg
@@ -2,8 +2,8 @@ slab_alloc_arena = 0.1
 
 pid_file = "tarantool.pid"
 logger="cat - >> tarantool.log"
-
 bind_ipaddr="INADDR_ANY"
+custom_proc_title="hot_standby"
 
 wal_dir="../"
 snap_dir="../"
@@ -11,12 +11,15 @@ snap_dir="../"
 primary_port = 33013
 secondary_port = 33024
 admin_port = 33025
+memcached_port = 33026
+replication_port=33017
 
-replication_port=33016
-custom_proc_title="hot_standby"
 
 space[0].enabled = 1
 space[0].index[0].type = "HASH"
 space[0].index[0].unique = 1
 space[0].index[0].key_field[0].fieldno = 0
 space[0].index[0].key_field[0].type = "NUM"
+
+memcached_space = 2
+memcached_expire=true
diff --git a/test/replication/cfg/master.cfg b/test/replication/cfg/master.cfg
index 9118e6d22ee79571a91fe71a07062b6d105ad913..8069ab062c50ed765d90a02df4351c134716113d 100644
--- a/test/replication/cfg/master.cfg
+++ b/test/replication/cfg/master.cfg
@@ -2,15 +2,14 @@ slab_alloc_arena = 0.1
 
 pid_file = "tarantool.pid"
 logger="cat - >> tarantool.log"
-
 bind_ipaddr="INADDR_ANY"
+custom_proc_title="master"
 
 primary_port = 33013
 secondary_port = 33014
 admin_port = 33015
-
-replication_port=33016
-custom_proc_title="master"
+memcached_port = 33016
+replication_port = 33017
 
 space[0].enabled = 1
 space[0].index[0].type = "HASH"
@@ -18,3 +17,5 @@ space[0].index[0].unique = 1
 space[0].index[0].key_field[0].fieldno = 0
 space[0].index[0].key_field[0].type = "NUM"
 
+memcached_space = 2
+memcached_expire=true
diff --git a/test/replication/cfg/master_to_replica.cfg b/test/replication/cfg/master_to_replica.cfg
index 5d2ae2420cc0ee1f3924074f48c8335dfa60f3e2..2428d2c393bd01bbbb12e610287351b62664d6b5 100644
--- a/test/replication/cfg/master_to_replica.cfg
+++ b/test/replication/cfg/master_to_replica.cfg
@@ -2,15 +2,17 @@ slab_alloc_arena = 0.1
 
 pid_file = "tarantool.pid"
 logger="cat - >> tarantool.log"
-
 bind_ipaddr="INADDR_ANY"
+custom_proc_title="master"
 
 primary_port = 33013
 secondary_port = 33014
 admin_port = 33015
+memcached_port = 33016
+
+replication_port=33017
+replication_source = "127.0.0.1:33117"
 
-replication_port=33016
-custom_proc_title="master"
 
 space[0].enabled = 1
 space[0].index[0].type = "HASH"
@@ -18,4 +20,5 @@ space[0].index[0].unique = 1
 space[0].index[0].key_field[0].fieldno = 0
 space[0].index[0].key_field[0].type = "NUM"
 
-replication_source = "127.0.0.1:33116"
+memcached_space = 2
+memcached_expire=true
diff --git a/test/replication/cfg/replica.cfg b/test/replication/cfg/replica.cfg
index 271da4823e6615b384911e577df6dfffd1a34766..c214a3aef4f29c1cc19426c17a2a40d7982f14d7 100644
--- a/test/replication/cfg/replica.cfg
+++ b/test/replication/cfg/replica.cfg
@@ -2,15 +2,16 @@ slab_alloc_arena = 0.1
 
 pid_file = "tarantool.pid"
 logger="cat - >> tarantool.log"
-
 bind_ipaddr="INADDR_ANY"
+custom_proc_title="replica"
 
 primary_port = 33113
 secondary_port = 33114
 admin_port = 33115
+memcached_port = 33116
 
-replication_port=33116
-custom_proc_title="replica"
+replication_port=33117
+replication_source = 127.0.0.1:33017
 
 space[0].enabled = 1
 space[0].index[0].type = "HASH"
@@ -18,4 +19,5 @@ space[0].index[0].unique = 1
 space[0].index[0].key_field[0].fieldno = 0
 space[0].index[0].key_field[0].type = "NUM"
 
-replication_source = 127.0.0.1:33016
+memcached_space = 2
+memcached_expire=true
diff --git a/test/replication/cfg/replica_to_master.cfg b/test/replication/cfg/replica_to_master.cfg
index df97d6e341536dd90078f7c98ffa4daa4c0b03b0..1f7c10c31ea5ebb90cb2d2a92a3546520ac2969a 100644
--- a/test/replication/cfg/replica_to_master.cfg
+++ b/test/replication/cfg/replica_to_master.cfg
@@ -2,18 +2,20 @@ slab_alloc_arena = 0.1
 
 pid_file = "tarantool.pid"
 logger="cat - >> tarantool.log"
-
 bind_ipaddr="INADDR_ANY"
+custom_proc_title="replica"
 
 primary_port = 33113
 secondary_port = 33114
 admin_port = 33115
-
-replication_port=33116
-custom_proc_title="replica"
+memcached_port = 33116
+replication_port = 33117
 
 space[0].enabled = 1
 space[0].index[0].type = "HASH"
 space[0].index[0].unique = 1
 space[0].index[0].key_field[0].fieldno = 0
 space[0].index[0].key_field[0].type = "NUM"
+
+memcached_space = 2
+memcached_expire=true
diff --git a/test/replication/consistent.result b/test/replication/consistent.result
index 24538e947d46da37728521d8564f4b7140df283c..d254e5c90726e1c8ca41df47d1e884c837232f3d 100644
--- a/test/replication/consistent.result
+++ b/test/replication/consistent.result
@@ -684,4 +684,4 @@ Found 1 tuple:
 master lsn = 61
 replica lsn = 61
 insert into t0 values (0, 'replica is read only')
-An error occurred: ER_NONMASTER, 'Can't modify data on a replication slave. My master is: 127.0.0.1:33016'
+An error occurred: ER_NONMASTER, 'Can't modify data on a replication slave. My master is: 127.0.0.1:33017'
diff --git a/test/replication/consistent.test b/test/replication/consistent.test
index e978c417326c3edcc6b9937e957d04027e6514d7..b537f7baf9e215e443addb6ce09c033c6248b35c 100644
--- a/test/replication/consistent.test
+++ b/test/replication/consistent.test
@@ -1,7 +1,7 @@
 # encoding: tarantool
 import os
 import time
-from lib.tarantool_box_server import TarantoolBoxServer
+from lib.tarantool_server import TarantoolServer
 
 ID_BEGIN = 0
 ID_STEP = 10
@@ -22,7 +22,7 @@ def select_tuples(server, begin, end):
 master = server
 
 # replica server
-replica = TarantoolBoxServer()
+replica = TarantoolServer()
 replica.deploy("replication/cfg/replica.cfg",
                replica.find_exe(self.args.builddir),
                os.path.join(self.args.vardir, "replica"))
diff --git a/test/replication/hot_standby.test b/test/replication/hot_standby.test
index 4689c4bca79301f13d156dda12531624443690aa..8f8e68cf62147f5561d544e8b3a9c083512950cb 100644
--- a/test/replication/hot_standby.test
+++ b/test/replication/hot_standby.test
@@ -1,21 +1,21 @@
 # encoding: tarantool
 import os
 import time
-from lib.tarantool_box_server import TarantoolBoxServer
+from lib.tarantool_server import TarantoolServer
 
 # master server
 master = server
 master_sql = master.sql
 
 # hot standby server
-hot_standby = TarantoolBoxServer()
+hot_standby = TarantoolServer()
 hot_standby.deploy("replication/cfg/hot_standby.cfg",
                    hot_standby.find_exe(self.args.builddir),
                    os.path.join(self.args.vardir, "hot_standby"), need_init=False)
 hot_standby_sql = hot_standby.sql
 
 # replica server
-replica = TarantoolBoxServer()
+replica = TarantoolServer()
 replica.deploy("replication/cfg/replica.cfg",
                replica.find_exe(self.args.builddir),
                os.path.join(self.args.vardir, "replica"))
diff --git a/test/replication/memcached.result b/test/replication/memcached.result
new file mode 100644
index 0000000000000000000000000000000000000000..fb1629897d9356a340f447a1c8b9563aac0da59c
--- /dev/null
+++ b/test/replication/memcached.result
@@ -0,0 +1,52 @@
+# set initial k-v pairs
+# wait and get last k-v pair from replica
+get 9
+VALUE 9 0 5
+good9
+END
+# make multiple cnanges with master
+# wait and get k-v's from replicas
+get 1 2 3 4 5 7 8 9 10
+VALUE 1 0 8
+good1afk
+VALUE 2 0 8
+kfagood2
+VALUE 3 0 2
+95
+VALUE 4 0 2
+45
+VALUE 5 0 5
+good5
+VALUE 7 0 21
+The expense of spirit
+VALUE 8 0 19
+in a waste of shame
+VALUE 9 0 18
+Is lust in action;
+VALUE 10 0 21
+and till action, lust
+END
+# get deleted value
+get 6
+END
+# flush all k-v on master and try to get them from replica
+get 10
+END
+# check that expiration is working properly on replica
+get 1
+VALUE 1 0 21
+The expense of spirit
+END
+get 1
+END
+# check that expiration is working properly, when replica becomes master
+reload configuration
+---
+ok
+...
+get 1
+VALUE 1 0 21
+The expense of spirit
+END
+get 1
+END
diff --git a/test/replication/memcached.test b/test/replication/memcached.test
new file mode 100644
index 0000000000000000000000000000000000000000..0152df2e3cd20200b7f89813956dfe3cd8c5e15f
--- /dev/null
+++ b/test/replication/memcached.test
@@ -0,0 +1,123 @@
+# encoding: tarantool
+import os
+import sys
+import time
+import yaml
+
+from lib.memcached_connection import MemcachedConnection
+from lib.tarantool_server import TarantoolServer
+
+sonet = """The expense of spirit
+in a waste of shame
+Is lust in action;
+and till action, lust""".split('\n')
+
+master = server
+master_memcached = master.memcached
+
+replica = TarantoolServer()
+replica.deploy("replication/cfg/replica.cfg",
+           replica.find_exe(self.args.builddir),
+           os.path.join(self.args.vardir, "replica"))
+replica_memcached = replica.memcached
+
+###################################
+def wait_for_lsn(lsn, serv):
+    serv_admin = serv.admin
+    while True:
+        if get_lsn(serv) == lsn:
+            return lsn
+        time.sleep(0.01)
+
+def wait_for_next_lsn(lsn, serv):
+    serv_admin = serv.admin
+    while True:
+        if get_lsn(serv) != lsn:
+            return lsn
+        time.sleep(0.01)
+
+def get_lsn(serv):
+    serv_admin = serv.admin
+    resp = exec serv_admin silent "lua box.info.lsn"
+    return yaml.load(resp)[0]
+
+def wait(next = False, serv_master = master, serv_replica = replica):
+    if next:
+        lsn = get_lsn(serv_replica)
+        return wait_for_next_lsn(lsn, serv_replica)
+    else:
+        lsn = get_lsn(serv_master)
+        return wait_for_lsn(lsn, serv_replica)
+
+def get_memcached_len(serv):
+    serv_admin = serv.admin
+    resp = exec serv_admin silent "lua box.space[box.cfg.memcached_space]:len()"
+    return yaml.load(resp)[0]
+
+
+def wait_for_empty_space(serv):
+    serv_admin = serv.admin
+    while True:
+        if get_memcached_len(serv) == 0:
+            return
+        time.sleep(0.01)
+
+###################################
+
+print """# set initial k-v pairs"""
+for i in xrange(10):
+    exec master_memcached silent "set %d 0 0 5\r\ngood%d\r\n" % (i, i)
+
+print """# wait and get last k-v pair from replica"""
+wait()
+exec replica_memcached "get 9\r\n"
+
+print """# make multiple cnanges with master"""
+answer = exec master_memcached silent "gets 9\r\n"
+cas = int(answer.split()[4])
+exec master_memcached silent "append 1 0 0 3\r\nafk\r\n"
+exec master_memcached silent "prepend 2 0 0 3\r\nkfa\r\n"
+exec master_memcached silent "set 3 0 0 2\r\n80\r\n"
+exec master_memcached silent "set 4 0 0 2\r\n60\r\n"
+exec master_memcached silent "delete 6\r\n"
+exec master_memcached silent "replace 7 0 0 %d\r\n%s\r\n" % (len(sonet[0]), sonet[0])
+exec master_memcached silent "replace 8 0 0 %d\r\n%s\r\n" % (len(sonet[1]), sonet[1])
+exec master_memcached silent "cas 9 0 0 %d %d\r\n%s\r\n" % (len(sonet[2]), cas, sonet[2])
+exec master_memcached silent "add 10 0 0 %d\r\n%s\r\n" % (len(sonet[3]), sonet[3])
+exec master_memcached silent "incr 3 15\r\n"
+exec master_memcached silent "decr 4 15\r\n"
+
+print """# wait and get k-v's from replicas"""
+wait()
+exec replica_memcached "get 1 2 3 4 5 7 8 9 10\r\n"
+
+print """# get deleted value"""
+exec replica_memcached "get 6\r\n"
+
+print """# flush all k-v on master and try to get them from replica"""
+exec master_memcached silent "flush_all\r\n"
+wait_for_empty_space(replica)
+exec replica_memcached "get 10\r\n"
+
+
+print """# check that expiration is working properly on replica"""
+exec master_memcached silent "set 1 0 1 %d\r\n%s\r\n" % (len(sonet[0]), sonet[0])
+wait()
+exec replica_memcached "get 1\r\n"
+wait(True)
+exec replica_memcached "get 1\r\n"
+
+print """# check that expiration is working properly, when replica becomes master"""
+exec master_memcached silent "set 1 0 1 %d\r\n%s\r\n" % (len(sonet[0]), sonet[0])
+replica.reconfigure("replication/cfg/replica_to_master.cfg")
+exec replica_memcached "get 1\r\n"
+wait(True)
+exec replica_memcached "get 1\r\n"
+
+
+# resore default suite config
+replica.stop()
+replica.cleanup(True)
+master.stop()
+master.deploy(self.suite_ini["config"])
+# vim: syntax=python
diff --git a/test/replication/swap.test b/test/replication/swap.test
index 7f47bbe5007aec017a3bf94d1f243a040a0663fd..82afc13cc29f06f27e8c4da79e66adc3c0940785 100644
--- a/test/replication/swap.test
+++ b/test/replication/swap.test
@@ -1,7 +1,7 @@
 # encoding: tarantool
 import os
 import time
-from lib.tarantool_box_server import TarantoolBoxServer
+from lib.tarantool_server import TarantoolServer
 
 REPEAT = 20
 ID_BEGIN = 0
@@ -23,7 +23,7 @@ def select_tuples(server, begin, end):
 master = server
 
 # replica server
-replica = TarantoolBoxServer()
+replica = TarantoolServer()
 replica.deploy("replication/cfg/replica.cfg",
                replica.find_exe(self.args.builddir),
                os.path.join(self.args.vardir, "replica"))
diff --git a/test/share/tarantool_box.sup b/test/share/tarantool.sup
similarity index 100%
rename from test/share/tarantool_box.sup
rename to test/share/tarantool.sup
diff --git a/test/test-run.py b/test/test-run.py
index 2d8428376befa4cabf27529fe7c58d074639fc27..392ac2273d8834eba8228132a74e26f8c6f0ee8f 100755
--- a/test/test-run.py
+++ b/test/test-run.py
@@ -24,12 +24,14 @@ __author__ = "Konstantin Osipov <kostja.osipov@gmail.com>"
 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 # SUCH DAMAGE.
 
-import argparse
-import os.path
 import os
-import time
 import sys
+import time
 import string
+import shutil
+import os.path
+import argparse
+
 from lib.test_suite import TestSuite
 
 #
@@ -58,14 +60,6 @@ class Options:
                 suites, "box/show" will only enable tests starting with "show" in
                 "box" suite. Default: run all tests in all specified suites.""")
 
-        parser.add_argument(
-                "--module",
-                dest = 'modules',
-                metavar = "module",
-                nargs="*",
-                default = ["box"],
-                help = "List of modules to test. Default: \"box\"")
-
         parser.add_argument(
                 "--suite",
                 dest = 'suites',
@@ -162,8 +156,14 @@ def main():
     options = Options()
     oldcwd = os.getcwd()
     # Change the current working directory to where all test
-    # collections are supposed to reside.
-    os.chdir(os.path.dirname(sys.argv[0]))
+    # collections are supposed to reside
+    # If script executed with (python test-run.py) dirname is ''
+    # so we need to make it .
+    path = os.path.dirname(sys.argv[0])
+    if not path:
+        path = '.'
+    os.chdir(path)
+
     failed_tests = 0
 
     try:
@@ -175,13 +175,9 @@ def main():
             for root, dirs, names in os.walk(os.getcwd()):
                 if "suite.ini" in names:
                     suite_names.append(os.path.basename(root))
-        suites = []
-        for suite_name in suite_names:
-            suite = TestSuite(suite_name, options.args)
-            if suite.ini["module"] not in options.args.modules:
-                continue
-            suites.append(suite)
 
+        suites = [TestSuite(suite_name, options.args) for suite_name in sorted(suite_names)]
+        
         for suite in suites:
             failed_tests += suite.run_all()
     except RuntimeError as e:
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
index 934e68d42dae60701eb64d45b6349016d578f32d..1b3a87cfe96f354ff9e86035b5be8d866ef0b56f 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -1,19 +1,19 @@
 file(GLOB all_sources *.c *.m *.mm)
 set_source_files_compile_flags(${all_sources})
 
-add_executable(rlist rlist.c test.c)
-add_executable(queue queue.c)
-add_executable(mhash mhash.c)
-add_executable(rope_basic rope_basic.c ${CMAKE_SOURCE_DIR}/src/rope.c)
-add_executable(rope_avl rope_avl.c ${CMAKE_SOURCE_DIR}/src/rope.c)
-add_executable(rope_stress rope_stress.c ${CMAKE_SOURCE_DIR}/src/rope.c)
-add_executable(rope rope.c ${CMAKE_SOURCE_DIR}/src/rope.c)
-add_executable(bit_test bit.c bit.c)
-target_link_libraries(bit_test bit)
-add_executable(bitset_basic_test bitset_basic.c)
-target_link_libraries(bitset_basic_test bitset)
-add_executable(bitset_iterator_test bitset_iterator.c)
-target_link_libraries(bitset_iterator_test bitset)
-add_executable(bitset_index_test bitset_index.c)
-target_link_libraries(bitset_index_test bitset)
-add_executable(base64 base64.c ${CMAKE_SOURCE_DIR}/third_party/base64.c)
+add_executable(rlist.test rlist.c test.c)
+add_executable(queue.test queue.c)
+add_executable(mhash.test mhash.c)
+add_executable(rope_basic.test rope_basic.c ${CMAKE_SOURCE_DIR}/src/rope.c)
+add_executable(rope_avl.test rope_avl.c ${CMAKE_SOURCE_DIR}/src/rope.c)
+add_executable(rope_stress.test rope_stress.c ${CMAKE_SOURCE_DIR}/src/rope.c)
+add_executable(rope.test rope.c ${CMAKE_SOURCE_DIR}/src/rope.c)
+add_executable(bit.test bit.c bit.c)
+target_link_libraries(bit.test bit)
+add_executable(bitset_basic.test bitset_basic.c)
+target_link_libraries(bitset_basic.test bitset)
+add_executable(bitset_iterator.test bitset_iterator.c)
+target_link_libraries(bitset_iterator.test bitset)
+add_executable(bitset_index.test bitset_index.c)
+target_link_libraries(bitset_index.test bitset)
+add_executable(base64.test base64.c ${CMAKE_SOURCE_DIR}/third_party/base64.c)
diff --git a/test/unit/base64.test b/test/unit/base64.test
deleted file mode 100644
index 0ed8d1b67d7103560b29ab48c9c8becec98619b8..0000000000000000000000000000000000000000
--- a/test/unit/base64.test
+++ /dev/null
@@ -1 +0,0 @@
-run_test("base64")
diff --git a/test/unit/bit.test b/test/unit/bit.test
deleted file mode 100644
index bd42398b072ea067924ad5ff0977c8109be9636c..0000000000000000000000000000000000000000
--- a/test/unit/bit.test
+++ /dev/null
@@ -1 +0,0 @@
-run_test("bit_test")
diff --git a/test/unit/bitset_basic.test b/test/unit/bitset_basic.test
deleted file mode 100644
index 080353944e138e8ed1fd5bb5d855c87de892c30a..0000000000000000000000000000000000000000
--- a/test/unit/bitset_basic.test
+++ /dev/null
@@ -1 +0,0 @@
-run_test("bitset_basic_test")
diff --git a/test/unit/bitset_index.test b/test/unit/bitset_index.test
deleted file mode 100644
index a7d9a5ec934971c94e78e1968aa7014b3111d0ce..0000000000000000000000000000000000000000
--- a/test/unit/bitset_index.test
+++ /dev/null
@@ -1 +0,0 @@
-run_test("bitset_index_test")
diff --git a/test/unit/bitset_iterator.test b/test/unit/bitset_iterator.test
deleted file mode 100644
index eb272521cb9be9cba2cf0389725ecafe2fc7e4b2..0000000000000000000000000000000000000000
--- a/test/unit/bitset_iterator.test
+++ /dev/null
@@ -1 +0,0 @@
-run_test("bitset_iterator_test")
diff --git a/test/unit/mhash.test b/test/unit/mhash.test
deleted file mode 100644
index a537d0bcbbeb6938ea3f119d7401096c81d194f3..0000000000000000000000000000000000000000
--- a/test/unit/mhash.test
+++ /dev/null
@@ -1 +0,0 @@
-run_test("mhash")
diff --git a/test/unit/queue.test b/test/unit/queue.test
deleted file mode 100644
index 78ef0b17533ec9c2d5085770247fc97a6f609193..0000000000000000000000000000000000000000
--- a/test/unit/queue.test
+++ /dev/null
@@ -1 +0,0 @@
-run_test("queue")
diff --git a/test/unit/rlist.test b/test/unit/rlist.test
deleted file mode 100644
index 5c8699160c2e47c699d67a94854f57394554b6d5..0000000000000000000000000000000000000000
--- a/test/unit/rlist.test
+++ /dev/null
@@ -1 +0,0 @@
-run_test("rlist")
diff --git a/test/unit/rope.test b/test/unit/rope.test
deleted file mode 100644
index 69d20903c14769eaf16b11fcfa5ee42027e8d2e1..0000000000000000000000000000000000000000
--- a/test/unit/rope.test
+++ /dev/null
@@ -1 +0,0 @@
-run_test("rope")
diff --git a/test/unit/rope_avl.test b/test/unit/rope_avl.test
deleted file mode 100644
index 416fc384267bd0b10d321702514f93cc2aa1905d..0000000000000000000000000000000000000000
--- a/test/unit/rope_avl.test
+++ /dev/null
@@ -1 +0,0 @@
-run_test("rope_avl")
diff --git a/test/unit/rope_basic.test b/test/unit/rope_basic.test
deleted file mode 100644
index ff8723e8a0cbdc70299965f88f8f8356c14faf71..0000000000000000000000000000000000000000
--- a/test/unit/rope_basic.test
+++ /dev/null
@@ -1 +0,0 @@
-run_test("rope_basic")
diff --git a/test/unit/rope_stress.test b/test/unit/rope_stress.test
deleted file mode 100644
index 6c3a3e7fc6ebeb21eaeb4d3f049c867695fac7ff..0000000000000000000000000000000000000000
--- a/test/unit/rope_stress.test
+++ /dev/null
@@ -1 +0,0 @@
-run_test("rope_stress")
diff --git a/test/unit/suite.ini b/test/unit/suite.ini
index 47106bc06f6f84d1cf4e8cae77974fae77fc076c..4d69f51aa5f459f513515612149f647ea337d22c 100644
--- a/test/unit/suite.ini
+++ b/test/unit/suite.ini
@@ -1,10 +1,4 @@
 [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
+description = unit tests
+