Skip to content

luatest: allow to run test cases in parallel #433

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
Jun 8, 2024
Merged
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
93 changes: 80 additions & 13 deletions lib/luatest_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import re
import sys

from subprocess import PIPE
from subprocess import Popen
from threading import Timer

Expand All @@ -14,6 +15,8 @@
from lib.tarantool_server import Test
from lib.tarantool_server import TestExecutionError
from lib.tarantool_server import TarantoolServer
from lib.utils import bytes_to_str
from lib.utils import find_tags


def timeout_handler(process, test_timeout):
Expand Down Expand Up @@ -52,6 +55,10 @@ def execute(self, server):
for p in Options().args.pattern:
command.extend(['--pattern', p])

# Run a specific test case. See find_tests() for details.
if 'test_case' in self.run_params:
command.extend(['--run-test-case', self.run_params['test_case']])

# We start luatest from the project source directory, it
# is the usual way to use luatest.
#
Expand Down Expand Up @@ -137,22 +144,82 @@ def verify_luatest_exe(cls):
# those cases, which are childs of OSError anyway.
raise TestRunInitError('Unable to find luatest executable', e)

@classmethod
def test_cases(cls, test_name):
p = Popen([cls.luatest, test_name, '--list-test-cases'], stdout=PIPE)
output = bytes_to_str(p.stdout.read()).rstrip()
p.wait()

# Exclude the first line if it is a tarantool version
# report.
res = output.split('\n')
if len(res) > 0 and res[0].startswith('Tarantool version is'):
return res[1:]

return res

@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

# TODO: Investigate why this old hack is needed and drop
# it if possible (move the assignment to test_suite.py).
#
# cdc70f94701f suggests that it is related to the out of
# source build.
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], [])
# A pattern here means just a substring to find in a test
# name.
include_patterns = Options().args.tests
exclude_patterns = Options().args.exclude

accepted_tags = Options().args.tags

tests = []
for test_name in glob.glob(os.path.join(suite_path, '*_test.lua')):
# If neither of the include patterns are substrings of
# the given test name, skip the test.
if not any(p in test_name for p in include_patterns):
continue

# If at least one of the exclude patterns is a
# substring of the given test name, skip the test.
if any(p in test_name for p in exclude_patterns):
continue

tags = find_tags(test_name)

# If --tags <...> CLI option is provided...
if accepted_tags:
# ...and the test has neither of the given tags,
# skip the test.
if not any(t in accepted_tags for t in tags):
continue

# Add the test to the execution list otherwise.
if 'parallel' in tags:
# If the test has the 'parallel' tag, split the
# test to test cases to run in separate tasks in
# parallel.
test_cases = LuatestServer.test_cases(test_name)

# Display shorter test case names on the screen:
# strip the common prefix.
prefix_len = len(os.path.commonprefix(test_cases))

for test_case in test_cases:
tests.append(LuatestTest(test_name, test_suite.args, test_suite.ini,
params={"test_case": test_case},
conf_name=test_case[prefix_len:]))
else:
# If the test has no 'parallel' tag, run all the
# test cases as one task.
tests.append(LuatestTest(test_name, test_suite.args, test_suite.ini))

tests.sort(key=lambda t: t.name)

# TODO: Don't modify a test suite object's field from
# another object directly. It is much better to just
# return a list of tests from this method.
test_suite.tests = tests