Skip to content

Make it possible to execute luatest suites #310

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@
[submodule "lib/tarantool-python"]
path = lib/tarantool-python
url = https://github.com/tarantool/tarantool-python.git
[submodule "lib/checks"]
path = lib/checks
url = https://github.com/tarantool/checks.git
[submodule "lib/luatest"]
path = lib/luatest
url = https://github.com/tarantool/luatest.git
4 changes: 3 additions & 1 deletion .luacheckrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
globals = {"box", "_TARANTOOL", "tonumber64"}
globals = {"box", "_TARANTOOL", "tonumber64", "os"}
ignore = {
-- Accessing an undefined field of a global variable <debug>.
"143/debug",
Expand Down Expand Up @@ -27,4 +27,6 @@ include_files = {
exclude_files = {
"lib/tarantool-python",
"test/test-tarantool/*.test.lua",
"lib/luatest/**",
"lib/checks/**",
}
15 changes: 14 additions & 1 deletion lib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from lib.tarantool_server import TarantoolServer
from lib.unittest_server import UnittestServer
from lib.app_server import AppServer
from lib.luatest_server import LuatestServer
from lib.utils import warn_unix_sockets_at_start


Expand All @@ -31,6 +32,7 @@ def module_init():
# If script executed with (python test-run.py) dirname is ''
# so we need to make it .
path = os.path.dirname(sys.argv[0])
os.environ['TEST_RUN_DIR'] = os.path.dirname(os.path.realpath(sys.argv[0]))
if not path:
path = '.'
os.chdir(path)
Expand All @@ -56,14 +58,25 @@ def module_init():
os.environ["SOURCEDIR"] = SOURCEDIR
os.environ["BUILDDIR"] = BUILDDIR
soext = sys.platform == 'darwin' and 'dylib' or 'so'
os.environ["LUA_PATH"] = SOURCEDIR+"/?.lua;"+SOURCEDIR+"/?/init.lua;;"

os.environ['LUA_PATH'] = (
SOURCEDIR + '/?.lua;' + SOURCEDIR + '/?/init.lua;'
+ os.environ['TEST_RUN_DIR'] + '/lib/checks/?.lua;'
+ os.environ['TEST_RUN_DIR'] + '/lib/luatest/?/init.lua;'
+ os.environ['TEST_RUN_DIR'] + '/lib/luatest/?.lua;;'
)

os.environ["LUA_CPATH"] = BUILDDIR+"/?."+soext+";;"
os.environ["REPLICATION_SYNC_TIMEOUT"] = str(args.replication_sync_timeout)
os.environ['MEMTX_ALLOCATOR'] = args.memtx_allocator

os.environ['LUATEST_BIN'] = os.path.join(
os.environ['TEST_RUN_DIR'], 'lib/luatest/bin/luatest')

TarantoolServer.find_exe(args.builddir)
UnittestServer.find_exe(args.builddir)
AppServer.find_exe(args.builddir)
LuatestServer.find_exe(args.builddir)

Options().check_schema_upgrade_option(TarantoolServer.debug)

Expand Down
1 change: 1 addition & 0 deletions lib/checks
Submodule checks added at eb49c1
3 changes: 3 additions & 0 deletions lib/error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class TestRunInitError(Exception):
def __init__(self, *args, **kwargs):
super(TestRunInitError, self).__init__(*args, **kwargs)
1 change: 1 addition & 0 deletions lib/luatest
Submodule luatest added at 10590e
133 changes: 133 additions & 0 deletions lib/luatest_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import glob
import os
import re
import sys

from subprocess import Popen, PIPE
from subprocess import STDOUT

from lib.error import TestRunInitError
from lib.sampler import sampler
from lib.server import Server
from lib.tarantool_server import Test
from lib.tarantool_server import TarantoolServer


class LuatestTest(Test):
""" Handle *_test.lua.

Provide method for executing luatest <name>_test.lua test.
"""

def __init__(self, *args, **kwargs):
super(LuatestTest, self).__init__(*args, **kwargs)
self.valgrind = kwargs.get('valgrind', False)

def execute(self, server):
"""Execute test by luatest command

Execute 'luatest -c -v <name>_test.lua -o tap --shuffle none'
Provide a verbose output in the tap format.
Use shuffle option in none mode for avoiding mixing tests.
Use capture mode.
"""
server.current_test = self
script = os.path.join(os.path.basename(server.testdir), self.name)
command = [os.environ['LUATEST_BIN'], '-c', '-v', script, '-o', 'tap',
'--shuffle', 'none']

# Tarantool's build directory is added to PATH in
# TarantoolServer.find_exe().
#
# We start luatest from the project source directory, it
# is the usual way to use luatest.
#
# VARDIR (${BUILDDIR}/test/var/001_foo) will be used for
# write ahead logs, snapshots, logs, unix domain sockets
# and so on.
os.environ['VARDIR'] = server.vardir
project_dir = os.environ['SOURCEDIR']
proc = Popen(command, cwd=project_dir, stdout=PIPE, stderr=STDOUT)
sampler.register_process(proc.pid, self.id, server.name)
sys.stdout.write_bytes(proc.communicate()[0])


class LuatestServer(Server):
"""A dummy server implementation for luatest server tests"""

def __new__(cls, ini=None, *args, **kwargs):
cls = Server.get_mixed_class(cls, ini)
return object.__new__(cls)

def __init__(self, _ini=None, test_suite=None):
if _ini is None:
_ini = {}
ini = {'vardir': None}
ini.update(_ini)
super(LuatestServer, self).__init__(ini, test_suite)
self.testdir = os.path.abspath(os.curdir)
self.vardir = ini['vardir']
self.builddir = ini['builddir']
self.name = 'luatest_server'

@property
def logfile(self):
return self.current_test.tmp_result

@property
def binary(self):
return LuatestServer.prepare_args(self)[0]

def deploy(self, vardir=None, silent=True, wait=True):
self.vardir = vardir
if not os.access(self.vardir, os.F_OK):
os.makedirs(self.vardir)

@classmethod
def find_exe(cls, builddir):
cls.builddir = builddir
cls.binary = TarantoolServer.binary
cls.debug = bool(re.findall(r'-Debug', str(cls.version()),
re.I))

@classmethod
def verify_luatest_exe(cls):
"""Verify that luatest executable is available."""
try:
# Just check that the command returns zero exit code.
with open(os.devnull, 'w') as devnull:
returncode = Popen([os.environ['LUATEST_BIN'], '--version'],
stdout=devnull,
stderr=devnull).wait()
if returncode != 0:
raise TestRunInitError('Unable to run `luatest --version`',
{'returncode': returncode})
except OSError as e:
# Python 2 raises OSError if the executable is not
# found or if it has no executable bit. Python 3
# raises FileNotFoundError and PermissionError in
# those cases, which are childs of OSError anyway.
raise TestRunInitError('Unable to find luatest executable', e)

@staticmethod
def find_tests(test_suite, suite_path):
"""Looking for *_test.lua, which are can be executed by luatest."""

def patterned(test, patterns):
answer = []
for i in patterns:
if test.name.find(i) != -1:
answer.append(test)
return answer

test_suite.ini['suite'] = suite_path
tests = glob.glob(os.path.join(suite_path, '*_test.lua'))

tests = Server.exclude_tests(tests, test_suite.args.exclude)
test_suite.tests = [LuatestTest(k, test_suite.args, test_suite.ini)
for k in sorted(tests)]
test_suite.tests = sum([patterned(x, test_suite.args.tests)
for x in test_suite.tests], [])

def print_log(self, lines):
pass
8 changes: 7 additions & 1 deletion lib/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,13 @@ def flush(self):


def get_filename_by_test(postfix, test_name):
rg = re.compile(r'\.test.*')
"""For <..>/<name>_test.* or <..>/<name>.test.* return <name> + postfix

Examples:
postfix='.result', test_name='foo/bar.test.lua' => return 'bar.result'
postfix='.reject', test_name='bar_test.lua' => return 'bar.reject'
"""
rg = re.compile(r'[._]test.*')
return os.path.basename(rg.sub(postfix, test_name))


Expand Down
9 changes: 8 additions & 1 deletion lib/test_suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from lib import Options
from lib.app_server import AppServer
from lib.luatest_server import LuatestServer
from lib.colorer import color_stdout
from lib.inspector import TarantoolInspector
from lib.server import Server
Expand Down Expand Up @@ -146,12 +147,17 @@ def __init__(self, suite_path, args):
# rid of all other side effects.
self.tests_are_collected = False

if self.ini['core'] == 'luatest':
LuatestServer.verify_luatest_exe()

def collect_tests(self):
if self.tests_are_collected:
return self.tests

if self.ini['core'] == 'tarantool':
TarantoolServer.find_tests(self, self.suite_path)
elif self.ini['core'] == 'luatest':
LuatestServer.find_tests(self, self.suite_path)
elif self.ini['core'] == 'app':
AppServer.find_tests(self, self.suite_path)
elif self.ini['core'] == 'unittest':
Expand All @@ -162,7 +168,8 @@ def collect_tests(self):
self.tests_are_collected = True
return self.tests
else:
raise ValueError('Cannot collect tests of unknown type')
raise ValueError(
'Cannot collect tests of unknown type: %s' % self.ini['core'])

# In given cases, this large output looks redundant.
if not Options().args.reproduce and not Options().args.show_tags:
Expand Down
16 changes: 11 additions & 5 deletions test-run.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
from lib import Options
from lib.colorer import color_stdout
from lib.utils import find_tags
from lib.error import TestRunInitError
from lib.utils import print_tail_n
from lib.utils import PY3
from lib.worker import get_task_groups
Expand All @@ -70,6 +71,7 @@
EXIT_INTERRUPTED = 2
EXIT_FAILED_TEST = 3
EXIT_NOTDONE_TEST = 4
EXIT_INIT_ERROR = 5
EXIT_UNKNOWN_ERROR = 50


Expand Down Expand Up @@ -293,10 +295,14 @@ def open_as_utf8(*args, **kwargs):
show_tags()
exit(status)

force_parallel = bool(Options().args.reproduce)
if not force_parallel and Options().args.jobs == -1:
status = main_consistent()
else:
status = main_parallel()
try:
force_parallel = bool(Options().args.reproduce)
if not force_parallel and Options().args.jobs == -1:
status = main_consistent()
else:
status = main_parallel()
except TestRunInitError as e:
color_stdout(str(e), '\n', schema='error')
status = EXIT_INIT_ERROR

exit(status)
19 changes: 19 additions & 0 deletions test/instances/default.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env tarantool

local helpers = require('test.luatest_helpers')

box.cfg(helpers.box_cfg())
box.schema.user.grant('guest', 'super', nil, nil, {if_not_exists = true})

-- luatest_helpers.Server:start() unblocks only when this variable
-- becomes true.
--
-- Set it when the instance is fully operable:
--
-- * The server listens for requests.
-- * The database is bootstrapped.
-- * Permissions are granted.
--
-- Use luatest_helpers.Server:start({wait_for_readiness = false})
-- to don't wait for setting of this variable.
_G.ready = true
44 changes: 44 additions & 0 deletions test/luatest_helpers.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
local fun = require('fun')
local json = require('json')
local fio = require('fio')

local luatest_helpers = {
SOCKET_DIR = fio.abspath(os.getenv('VARDIR') or 'test/var')
}

luatest_helpers.Server = require('test.luatest_helpers.server')

local function default_cfg()
return {
work_dir = os.getenv('TARANTOOL_WORKDIR'),
listen = os.getenv('TARANTOOL_LISTEN'),
}
end

local function env_cfg()
local src = os.getenv('TARANTOOL_BOX_CFG')
if src == nil then
return {}
end
local res = json.decode(src)
assert(type(res) == 'table')
return res
end

-- Collect box.cfg table from values passed through
-- luatest_helpers.Server({<...>}) and from the given argument.
--
-- Use it from inside an instance script.
function luatest_helpers.box_cfg(cfg)
return fun.chain(default_cfg(), env_cfg(), cfg or {}):tomap()
end

function luatest_helpers.instance_uri(alias, instance_id)
if instance_id == nil then
instance_id = ''
end
instance_id = tostring(instance_id)
return ('%s/%s%s.iproto'):format(luatest_helpers.SOCKET_DIR, alias, instance_id);
end

return luatest_helpers
Loading