Skip to content

Commit af29141

Browse files
committed
Add luatest server
Provide luatest server, which can find tests by luatest pattern *_test.lua and execute them luatest -c -v <name>_test.lua -o tap --shuffle none. Use luatest bin from a submodule. Was changed get_filename_by_test for right working not only with *.test.*, but also with *_test.*. Smoke check lua-test was added with starting server and checks of box_cfg args. Show a test suite type when it is unknown. Part of: #304
1 parent 79c9942 commit af29141

File tree

12 files changed

+406
-9
lines changed

12 files changed

+406
-9
lines changed

.luacheckrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
globals = {"box", "_TARANTOOL", "tonumber64"}
1+
globals = {"box", "_TARANTOOL", "tonumber64", "os"}
22
ignore = {
33
-- Accessing an undefined field of a global variable <debug>.
44
"143/debug",

lib/__init__.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from lib.tarantool_server import TarantoolServer
77
from lib.unittest_server import UnittestServer
88
from lib.app_server import AppServer
9+
from lib.luatest_server import LuatestServer
910
from lib.utils import warn_unix_sockets_at_start
1011

1112

@@ -31,6 +32,7 @@ def module_init():
3132
# If script executed with (python test-run.py) dirname is ''
3233
# so we need to make it .
3334
path = os.path.dirname(sys.argv[0])
35+
os.environ['TEST_RUN_DIR'] = os.path.dirname(os.path.realpath(sys.argv[0]))
3436
if not path:
3537
path = '.'
3638
os.chdir(path)
@@ -56,14 +58,25 @@ def module_init():
5658
os.environ["SOURCEDIR"] = SOURCEDIR
5759
os.environ["BUILDDIR"] = BUILDDIR
5860
soext = sys.platform == 'darwin' and 'dylib' or 'so'
59-
os.environ["LUA_PATH"] = SOURCEDIR+"/?.lua;"+SOURCEDIR+"/?/init.lua;;"
61+
62+
os.environ['LUA_PATH'] = (
63+
SOURCEDIR + '/?.lua;' + SOURCEDIR + '/?/init.lua;'
64+
+ os.environ['TEST_RUN_DIR'] + '/lib/checks/?.lua;'
65+
+ os.environ['TEST_RUN_DIR'] + '/lib/luatest/?/init.lua;'
66+
+ os.environ['TEST_RUN_DIR'] + '/lib/luatest/?.lua;;'
67+
)
68+
6069
os.environ["LUA_CPATH"] = BUILDDIR+"/?."+soext+";;"
6170
os.environ["REPLICATION_SYNC_TIMEOUT"] = str(args.replication_sync_timeout)
6271
os.environ['MEMTX_ALLOCATOR'] = args.memtx_allocator
6372

73+
os.environ['LUATEST_BIN'] = os.path.join(
74+
os.environ['TEST_RUN_DIR'], 'lib/luatest/bin/luatest')
75+
6476
TarantoolServer.find_exe(args.builddir)
6577
UnittestServer.find_exe(args.builddir)
6678
AppServer.find_exe(args.builddir)
79+
LuatestServer.find_exe(args.builddir)
6780

6881
Options().check_schema_upgrade_option(TarantoolServer.debug)
6982

lib/error.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class TestRunInitError(Exception):
2+
def __init__(self, *args, **kwargs):
3+
super(TestRunInitError, self).__init__(*args, **kwargs)

lib/luatest_server.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import glob
2+
import os
3+
import re
4+
import sys
5+
6+
from subprocess import Popen, PIPE
7+
from subprocess import STDOUT
8+
9+
from lib.error import TestRunInitError
10+
from lib.sampler import sampler
11+
from lib.server import Server
12+
from lib.tarantool_server import Test
13+
from lib.tarantool_server import TarantoolServer
14+
15+
16+
class LuatestTest(Test):
17+
""" Handle *_test.lua.
18+
19+
Provide method for executing luatest <name>_test.lua test.
20+
"""
21+
22+
def __init__(self, *args, **kwargs):
23+
super(LuatestTest, self).__init__(*args, **kwargs)
24+
self.valgrind = kwargs.get('valgrind', False)
25+
26+
def execute(self, server):
27+
"""Execute test by luatest command
28+
29+
Execute 'luatest -c -v <name>_test.lua -o tap --shuffle none'
30+
Provide a verbose output in the tap format.
31+
Use shuffle option in none mode for avoiding mixing tests.
32+
Use capture mode.
33+
"""
34+
server.current_test = self
35+
script = os.path.join(os.path.basename(server.testdir), self.name)
36+
command = [os.environ['LUATEST_BIN'], '-c', '-v', script, '-o', 'tap',
37+
'--shuffle', 'none']
38+
39+
# Tarantool's build directory is added to PATH in
40+
# TarantoolServer.find_exe().
41+
#
42+
# We start luatest from the project source directory, it
43+
# is the usual way to use luatest.
44+
#
45+
# VARDIR (${BUILDDIR}/test/var/001_foo) will be used for
46+
# write ahead logs, snapshots, logs, unix domain sockets
47+
# and so on.
48+
os.environ['VARDIR'] = server.vardir
49+
project_dir = os.environ['SOURCEDIR']
50+
proc = Popen(command, cwd=project_dir, stdout=PIPE, stderr=STDOUT)
51+
sampler.register_process(proc.pid, self.id, server.name)
52+
sys.stdout.write_bytes(proc.communicate()[0])
53+
54+
55+
class LuatestServer(Server):
56+
"""A dummy server implementation for luatest server tests"""
57+
58+
def __new__(cls, ini=None, *args, **kwargs):
59+
cls = Server.get_mixed_class(cls, ini)
60+
return object.__new__(cls)
61+
62+
def __init__(self, _ini=None, test_suite=None):
63+
if _ini is None:
64+
_ini = {}
65+
ini = {'vardir': None}
66+
ini.update(_ini)
67+
super(LuatestServer, self).__init__(ini, test_suite)
68+
self.testdir = os.path.abspath(os.curdir)
69+
self.vardir = ini['vardir']
70+
self.builddir = ini['builddir']
71+
self.name = 'luatest_server'
72+
73+
@property
74+
def logfile(self):
75+
return self.current_test.tmp_result
76+
77+
@property
78+
def binary(self):
79+
return LuatestServer.prepare_args(self)[0]
80+
81+
def deploy(self, vardir=None, silent=True, wait=True):
82+
self.vardir = vardir
83+
if not os.access(self.vardir, os.F_OK):
84+
os.makedirs(self.vardir)
85+
86+
@classmethod
87+
def find_exe(cls, builddir):
88+
cls.builddir = builddir
89+
cls.binary = TarantoolServer.binary
90+
cls.debug = bool(re.findall(r'-Debug', str(cls.version()),
91+
re.I))
92+
93+
@classmethod
94+
def verify_luatest_exe(cls):
95+
"""Verify that luatest executable is available."""
96+
try:
97+
# Just check that the command returns zero exit code.
98+
with open(os.devnull, 'w') as devnull:
99+
returncode = Popen([os.environ['LUATEST_BIN'], '--version'],
100+
stdout=devnull,
101+
stderr=devnull).wait()
102+
if returncode != 0:
103+
raise TestRunInitError('Unable to run `luatest --version`',
104+
{'returncode': returncode})
105+
except OSError as e:
106+
# Python 2 raises OSError if the executable is not
107+
# found or if it has no executable bit. Python 3
108+
# raises FileNotFoundError and PermissionError in
109+
# those cases, which are childs of OSError anyway.
110+
raise TestRunInitError('Unable to find luatest executable', e)
111+
112+
@staticmethod
113+
def find_tests(test_suite, suite_path):
114+
"""Looking for *_test.lua, which are can be executed by luatest."""
115+
116+
def patterned(test, patterns):
117+
answer = []
118+
for i in patterns:
119+
if test.name.find(i) != -1:
120+
answer.append(test)
121+
return answer
122+
123+
test_suite.ini['suite'] = suite_path
124+
tests = glob.glob(os.path.join(suite_path, '*_test.lua'))
125+
126+
tests = Server.exclude_tests(tests, test_suite.args.exclude)
127+
test_suite.tests = [LuatestTest(k, test_suite.args, test_suite.ini)
128+
for k in sorted(tests)]
129+
test_suite.tests = sum([patterned(x, test_suite.args.tests)
130+
for x in test_suite.tests], [])
131+
132+
def print_log(self, lines):
133+
pass

lib/test.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,13 @@ def flush(self):
9494

9595

9696
def get_filename_by_test(postfix, test_name):
97-
rg = re.compile(r'\.test.*')
97+
"""For <..>/<name>_test.* or <..>/<name>.test.* return <name> + postfix
98+
99+
Examples:
100+
postfix='.result', test_name='foo/bar.test.lua' => return 'bar.result'
101+
postfix='.reject', test_name='bar_test.lua' => return 'bar.reject'
102+
"""
103+
rg = re.compile(r'[._]test.*')
98104
return os.path.basename(rg.sub(postfix, test_name))
99105

100106

lib/test_suite.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
from lib import Options
1515
from lib.app_server import AppServer
16+
from lib.luatest_server import LuatestServer
1617
from lib.colorer import color_stdout
1718
from lib.inspector import TarantoolInspector
1819
from lib.server import Server
@@ -146,12 +147,17 @@ def __init__(self, suite_path, args):
146147
# rid of all other side effects.
147148
self.tests_are_collected = False
148149

150+
if self.ini['core'] == 'luatest':
151+
LuatestServer.verify_luatest_exe()
152+
149153
def collect_tests(self):
150154
if self.tests_are_collected:
151155
return self.tests
152156

153157
if self.ini['core'] == 'tarantool':
154158
TarantoolServer.find_tests(self, self.suite_path)
159+
elif self.ini['core'] == 'luatest':
160+
LuatestServer.find_tests(self, self.suite_path)
155161
elif self.ini['core'] == 'app':
156162
AppServer.find_tests(self, self.suite_path)
157163
elif self.ini['core'] == 'unittest':
@@ -162,7 +168,8 @@ def collect_tests(self):
162168
self.tests_are_collected = True
163169
return self.tests
164170
else:
165-
raise ValueError('Cannot collect tests of unknown type')
171+
raise ValueError(
172+
'Cannot collect tests of unknown type: %s' % self.ini['core'])
166173

167174
# In given cases, this large output looks redundant.
168175
if not Options().args.reproduce and not Options().args.show_tags:

test-run.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
from lib import Options
5757
from lib.colorer import color_stdout
5858
from lib.utils import find_tags
59+
from lib.error import TestRunInitError
5960
from lib.utils import print_tail_n
6061
from lib.utils import PY3
6162
from lib.worker import get_task_groups
@@ -70,6 +71,7 @@
7071
EXIT_INTERRUPTED = 2
7172
EXIT_FAILED_TEST = 3
7273
EXIT_NOTDONE_TEST = 4
74+
EXIT_INIT_ERROR = 5
7375
EXIT_UNKNOWN_ERROR = 50
7476

7577

@@ -293,10 +295,14 @@ def open_as_utf8(*args, **kwargs):
293295
show_tags()
294296
exit(status)
295297

296-
force_parallel = bool(Options().args.reproduce)
297-
if not force_parallel and Options().args.jobs == -1:
298-
status = main_consistent()
299-
else:
300-
status = main_parallel()
298+
try:
299+
force_parallel = bool(Options().args.reproduce)
300+
if not force_parallel and Options().args.jobs == -1:
301+
status = main_consistent()
302+
else:
303+
status = main_parallel()
304+
except TestRunInitError as e:
305+
color_stdout(str(e), '\n', schema='error')
306+
status = EXIT_INIT_ERROR
301307

302308
exit(status)

test/instances/default.lua

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/env tarantool
2+
3+
local helpers = require('test.luatest_helpers')
4+
5+
box.cfg(helpers.box_cfg())
6+
box.schema.user.grant('guest', 'super', nil, nil, {if_not_exists = true})
7+
8+
-- luatest_helpers.Server:start() unblocks only when this variable
9+
-- becomes true.
10+
--
11+
-- Set it when the instance is fully operable:
12+
--
13+
-- * The server listens for requests.
14+
-- * The database is bootstrapped.
15+
-- * Permissions are granted.
16+
--
17+
-- Use luatest_helpers.Server:start({wait_for_readiness = false})
18+
-- to don't wait for setting of this variable.
19+
_G.ready = true

test/luatest_helpers.lua

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
local fun = require('fun')
2+
local json = require('json')
3+
4+
local luatest_helpers = {}
5+
6+
luatest_helpers.Server = require('test.luatest_helpers.server')
7+
8+
local function default_cfg()
9+
return {
10+
work_dir = os.getenv('TARANTOOL_WORKDIR'),
11+
listen = os.getenv('TARANTOOL_LISTEN'),
12+
}
13+
end
14+
15+
local function env_cfg()
16+
local src = os.getenv('TARANTOOL_BOX_CFG')
17+
if src == nil then
18+
return {}
19+
end
20+
local res = json.decode(src)
21+
assert(type(res) == 'table')
22+
return res
23+
end
24+
25+
-- Collect box.cfg table from values passed through
26+
-- luatest_helpers.Server({<...>}) and from the given argument.
27+
--
28+
-- Use it from inside an instance script.
29+
function luatest_helpers.box_cfg(cfg)
30+
return fun.chain(default_cfg(), env_cfg(), cfg or {}):tomap()
31+
end
32+
33+
return luatest_helpers

0 commit comments

Comments
 (0)