diff --git a/lib/luatest_server.py b/lib/luatest_server.py index 0793601d..f6520ad0 100644 --- a/lib/luatest_server.py +++ b/lib/luatest_server.py @@ -3,6 +3,7 @@ import re import sys +from subprocess import PIPE from subprocess import Popen from threading import Timer @@ -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): @@ -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. # @@ -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