diff --git a/test/par-test-run.py b/test/lib/parallel.py
old mode 100755
new mode 100644
similarity index 88%
rename from test/par-test-run.py
rename to test/lib/parallel.py
index ecf3e68a0f689f48e739aa00210853936e4ce170..a42e3ab9d85bf2886da8a581af9f60d77f09288c
--- a/test/par-test-run.py
+++ b/test/lib/parallel.py
@@ -228,32 +228,51 @@ class TestStatus(object):
         pass
 
 class Supervisor(object):
-    def __init__(self):
-        self.name      = 'parallel'
-        self.server    = TarantoolServer({
-            'core'  : 'tarantool',
-            'script': 'parallel/box.lua',
-            'vardir': 'var_parallel'
-        })
+    def __init__(self, suite_path, args):
+        self.args = args
+        self.tests = []
+        self.suite_path = suite_path
+        self.ini = {
+                'core': 'tarantool',
+                'script': os.path.join(suite_path, 'parallel.lua'),
+        }
+
+        # read suite config
+        config = ConfigParser.ConfigParser()
+        config.read(os.path.join(suite_path, "suite.ini"))
+        self.ini.update(dict(config.items("default")))
+        self.ini.update(self.args.__dict__)
+        self.jobs = int(self.ini.get('jobs', 1))
+        self.count = int(self.ini.get('count', 0))
+
+        for i in ["script"]:
+            self.ini[i] = os.path.join(suite_path, self.ini[i]) if i in self.ini else None
+        self.server = TarantoolServer(self.ini)
         self.pool = None
         self.iterator = None
 
     def search_tests(self):
-        self.tests  = []
         self.tests += [Parallel_PythonTest(k) \
-                for k in sorted(glob.glob(os.path.join(self.name, "*.test.py" )))]
-#        self.tests += [Parallel_LuaTest(k)    \
-#                for k in sorted(glob.glob(os.path.join(self.name, "*.test.lua")))]
+                for k in sorted(glob.glob(os.path.join(self.suite_path, "*.test.py" )))]
 
     def take_rand(self):
-        while True:
-            sql = self.server.sql.ret_copy()
-            admin = self.server.admin.ret_copy()
-            yield [random.choice(self.tests), [sql, admin]]
+        if self.count != 0:
+            for test in self.tests:
+                sql = self.server.sql.ret_copy()
+                admin = self.server.admin.ret_copy()
+                yield [test, [sql, admin]]
+        else:
+            while True:
+                sql = self.server.sql.ret_copy()
+                admin = self.server.admin.ret_copy()
+                yield [random.choice(self.tests), [sql, admin]]
 
-    def run(self, jobs):
+    def run(self):
         self.search_tests()
-        self.pool = Parallel_Pool(processes = jobs)
+        if self.count != 0:
+            self.tests *= self.count
+            random.shuffle(self.tests)
+        self.pool = Parallel_Pool(processes = self.jobs)
         self.iterator = self.pool.run()
         self.filler = self.pool.fill(self.take_rand())
         try:
@@ -279,7 +298,7 @@ class Supervisor(object):
                             if stat.status != 3:
                                 logger.info('>>>> Test %s finished' % repr(task.name))
                             else:
-                                logger.info('>>>> Test %s failed with %s' %
+                                logger.error('>>>> Test %s failed with %s' %
                                         (repr(task.name), stat.message))
                         except (QueueEmpty, StopIteration):
                             break
@@ -373,5 +392,4 @@ class Parallel_PythonTest(Parallel_FuncTest): pass
 
 if __name__ == '__main__':
     TarantoolServer.find_exe('..')
-    sup = Supervisor()
-    sup.run(1)
+    Supervisor().run()
diff --git a/test/lib/test_suite.py b/test/lib/test_suite.py
index 566bf391b172f0d283a236ed8c2089e5454e1174..647379a830e0bbcb3ce78e715b4f9828f7f80da6 100644
--- a/test/lib/test_suite.py
+++ b/test/lib/test_suite.py
@@ -44,14 +44,15 @@ class TestSuite:
         self.ini["core"] = "tarantool"
 
         if os.access(suite_path, os.F_OK) == False:
-            raise RuntimeError("Suite \"" + suite_path + \
-                               "\" doesn't exist")
+            raise RuntimeError("Suite %s doesn't exist" % repr(suite_path))
 
         # read the suite config
         config = ConfigParser.ConfigParser()
         config.read(os.path.join(suite_path, "suite.ini"))
         self.ini.update(dict(config.items("default")))
         self.ini.update(self.args.__dict__)
+        if self.args.stress is None and self.ini['core'] == 'stress':
+            return
 
         for i in ["script"]:
             self.ini[i] = os.path.join(suite_path, self.ini[i]) if i in self.ini else None
@@ -59,10 +60,9 @@ class TestSuite:
             self.ini[i] = dict.fromkeys(self.ini[i].split()) if i in self.ini else dict()
         for i in ["lua_libs"]:
             self.ini[i] = map(lambda x: os.path.join(suite_path, x),
-                    dict.fromkeys(self.ini[i].split()) if i in self.ini else
-                    dict())
+                    dict.fromkeys(self.ini[i].split()) if i in self.ini else dict())
         try:
-            if self.ini['core'] == 'tarantool':
+            if self.ini['core'] in ['tarantool', 'stress']:
                 self.server = TarantoolServer(self.ini)
             else:
                 self.server = Server(self.ini)
@@ -98,7 +98,6 @@ class TestSuite:
         color_stdout(shortsep, "\n", schema='separator')
         failed_tests = []
         try:
-            self.server.cleanup()
             for test in self.tests:
                 color_stdout(test.name.ljust(48), schema='t_name')
                 # for better diagnostics in case of a long-running test
@@ -119,6 +118,8 @@ class TestSuite:
             color_stdout("\n%s\n" % shortsep, schema='separator')
             self.server.stop(silent=False)
             raise
+        finally:
+            self.server.cleanup()
 
         if failed_tests:
             color_stdout("Failed {0} tests: {1}.\n".format(len(failed_tests),
diff --git a/test/parallel/call.result b/test/parallel/call.result
index c76e7645410d0ce21bc2624f83f99e8527bb9aa3..c312c9b6583eb8024c9074698c5add8c0ecc6823 100644
--- a/test/parallel/call.result
+++ b/test/parallel/call.result
@@ -188,6 +188,14 @@ space = box.schema.create_space('tweedledum', { id = 0 })
 ...
 space:create_index('primary', { type = 'hash' })
 ---
+- unique: true
+  parts:
+  - type: NUM
+    fieldno: 1
+  id: 0
+  space_id: 0
+  name: primary
+  type: HASH
 ...
 function myreplace(...) return space:replace{...} end
 ---
diff --git a/test/parallel/box.lua b/test/parallel/parallel.lua
similarity index 100%
rename from test/parallel/box.lua
rename to test/parallel/parallel.lua
diff --git a/test/parallel/sql.result b/test/parallel/sql.result
index 0cbf7f8f9c6e70bcc87284521500d955b3ed58ad..a267722d508e444e55e4c52a8448febc21a63134 100644
--- a/test/parallel/sql.result
+++ b/test/parallel/sql.result
@@ -9,9 +9,25 @@ s = box.schema.create_space('tweedledum', { id = 0 })
 ...
 s:create_index('primary', { type = 'tree', parts = { 1, 'str'} })
 ---
+- unique: true
+  parts:
+  - type: STR
+    fieldno: 1
+  id: 0
+  space_id: 0
+  name: primary
+  type: TREE
 ...
 s:create_index('secondary', { type = 'tree', unique = false, parts = {2, 'str'}})
 ---
+- unique: false
+  parts:
+  - type: STR
+    fieldno: 2
+  id: 1
+  space_id: 0
+  name: secondary
+  type: TREE
 ...
 #
 # A test case for Bug#729758
diff --git a/test/parallel/suite.ini b/test/parallel/suite.ini
new file mode 100644
index 0000000000000000000000000000000000000000..7cf67d04dcbf0e8cd12a6885d7cf989fbbc6878a
--- /dev/null
+++ b/test/parallel/suite.ini
@@ -0,0 +1,7 @@
+[default]
+core = stress
+description = parallel example
+script = parallel.lua
+jobs = 1
+count = 10
+vardir = 'test/parallel/vardir'
diff --git a/test/run b/test/run
deleted file mode 100755
index fcedec5eddb61a5167c329ead6fdfb22fd62d20b..0000000000000000000000000000000000000000
--- a/test/run
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/sh
-
-/usr/bin/env python2 <<__EOB__
-import sys
-if sys.hexversion < 0x02060000 or sys.hexversion >= 0x03000000:
-    sys.stderr.write("ERROR: test harness must use python >= 2.6 but not 3.x\n")
-    sys.exit(1)
-else:
-    sys.exit(0)
-__EOB__
-
-[ $? -eq 0 ] && ./test-run.py $*
-
diff --git a/test/run.sh b/test/run.sh
deleted file mode 100755
index fcedec5eddb61a5167c329ead6fdfb22fd62d20b..0000000000000000000000000000000000000000
--- a/test/run.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/sh
-
-/usr/bin/env python2 <<__EOB__
-import sys
-if sys.hexversion < 0x02060000 or sys.hexversion >= 0x03000000:
-    sys.stderr.write("ERROR: test harness must use python >= 2.6 but not 3.x\n")
-    sys.exit(1)
-else:
-    sys.exit(0)
-__EOB__
-
-[ $? -eq 0 ] && ./test-run.py $*
-
diff --git a/test/test-run.py b/test/test-run.py
index 77b651401e3f47b999053f79a28d69225a8386ed..0f3a3aa5250c38e4c0efd7e4d6e50c80d2ba2071 100755
--- a/test/test-run.py
+++ b/test/test-run.py
@@ -32,11 +32,11 @@ import shutil
 import os.path
 import argparse
 
-from lib.colorer import Colorer
-from lib.test_suite import TestSuite
+from lib.colorer          import Colorer
+from lib.parallel         import Supervisor
+from lib.test_suite       import TestSuite
 from lib.tarantool_server import TarantoolServer
-from lib.unittest_server import UnittestServer
-
+from lib.unittest_server  import UnittestServer
 color_stdout = Colorer()
 #
 # Run a collection of tests.
@@ -113,7 +113,7 @@ class Options:
 
         parser.add_argument(
                 "--stress",
-                dest = "builddir",
+                dest = "stress",
                 default = None,
                 help = """Name of streess TestSuite to run""")
 
@@ -159,25 +159,27 @@ def main():
     failed_tests = []
 
     try:
+        TarantoolServer.find_exe(options.args.builddir)
+        UnittestServer.find_exe(options.args.builddir)
+
         color_stdout("Started {0}\n".format(" ".join(sys.argv)), schema='tr_text')
-        suite_names = []
-        if options.args.suites != []:
-            suite_names = options.args.suites
-        else:
+        suite_names = options.args.suites
+        if suite_names == []:
             for root, dirs, names in os.walk(os.getcwd()):
                 if "suite.ini" in names:
                     suite_names.append(os.path.basename(root))
 
-        suites = [TestSuite(suite_name, options.args) for suite_name in sorted(suite_names)]
-
-
-        TarantoolServer.find_exe(options.args.builddir)
-        UnittestServer.find_exe(options.args.builddir)
-
-        for suite in suites:
-            failed_tests.extend(suite.run_all())
+        if options.args.stress is None:
+            suites = [TestSuite(suite_name, options.args) for suite_name in sorted(suite_names)]
+            for suite in suites:
+                failed_tests.extend(suite.run_all())
+        else:
+            suite_names = [suite_name for suite_name in suite_names if suite_name.find(options.args.stress) != -1]
+            suites = [Supervisor(suite_name, options.args) for suite_name in sorted(suite_names)]
+            for suite in suites:
+                failed_tests.extend(suite.run())
     except RuntimeError as e:
-        color_stdout("\nFatal error: {0}. Execution aborted.\n".format(e), schema='error')
+        color_stdout("\nFatal error: %s. Execution aborted.\n" % e, schema='error')
         if options.args.gdb:
             time.sleep(100)
         return (-1)
@@ -185,9 +187,9 @@ def main():
         os.chdir(oldcwd)
 
     if failed_tests and options.args.is_force:
-        color_stdout("\n===== {0} tests failed:".format(len(failed_tests))+"\n", schema='error')
+        color_stdout("\n===== %d tests failed:\n" % len(failed_tests), schema='error')
         for test in failed_tests:
-             color_stdout("----- "+test+"\n", schema='info')
+             color_stdout("----- %s\n" % test, schema='info')
 
     return (-1 if failed_tests else 0)