mirror of
https://github.com/yuzu-emu/mbedtls.git
synced 2024-11-23 05:25:39 +01:00
Make test function parsing robust
This commit enhances parsing of the test function in generate_test_code.py for cases where return type and function name are on separate lines.
This commit is contained in:
parent
4084ec7ae5
commit
fcdf685302
@ -185,11 +185,10 @@ END_CASE_REGEX = r'/\*\s*END_CASE\s*\*/'
|
|||||||
|
|
||||||
DEPENDENCY_REGEX = r'depends_on:(?P<dependencies>.*)'
|
DEPENDENCY_REGEX = r'depends_on:(?P<dependencies>.*)'
|
||||||
C_IDENTIFIER_REGEX = r'!?[a-z_][a-z0-9_]*'
|
C_IDENTIFIER_REGEX = r'!?[a-z_][a-z0-9_]*'
|
||||||
TEST_FUNCTION_VALIDATION_REGEX = r'\s*void\s+(\w+)\s*\('
|
TEST_FUNCTION_VALIDATION_REGEX = r'\s*void\s+(?P<func_name>\w+)\s*\('
|
||||||
INT_CHECK_REGEX = r'int\s+.*'
|
INT_CHECK_REGEX = r'int\s+.*'
|
||||||
CHAR_CHECK_REGEX = r'char\s*\*\s*.*'
|
CHAR_CHECK_REGEX = r'char\s*\*\s*.*'
|
||||||
DATA_T_CHECK_REGEX = r'data_t\s*\*\s*.*'
|
DATA_T_CHECK_REGEX = r'data_t\s*\*\s*.*'
|
||||||
FUNCTION_ARG_LIST_START_REGEX = r'.*?\s+(\w+)\s*\('
|
|
||||||
FUNCTION_ARG_LIST_END_REGEX = r'.*\)'
|
FUNCTION_ARG_LIST_END_REGEX = r'.*\)'
|
||||||
EXIT_LABEL_REGEX = r'^exit:'
|
EXIT_LABEL_REGEX = r'^exit:'
|
||||||
|
|
||||||
@ -451,7 +450,7 @@ def parse_function_dependencies(line):
|
|||||||
return dependencies
|
return dependencies
|
||||||
|
|
||||||
|
|
||||||
def parse_function_signature(line):
|
def parse_function_arguments(line):
|
||||||
"""
|
"""
|
||||||
Parses test function signature for validation and generates
|
Parses test function signature for validation and generates
|
||||||
a dispatch wrapper function that translates input test vectors
|
a dispatch wrapper function that translates input test vectors
|
||||||
@ -459,19 +458,15 @@ def parse_function_signature(line):
|
|||||||
|
|
||||||
:param line: Line from .function file that has a function
|
:param line: Line from .function file that has a function
|
||||||
signature.
|
signature.
|
||||||
:return: function name, argument list, local variables for
|
:return: argument list, local variables for
|
||||||
wrapper function and argument dispatch code.
|
wrapper function and argument dispatch code.
|
||||||
"""
|
"""
|
||||||
args = []
|
args = []
|
||||||
local_vars = ''
|
local_vars = ''
|
||||||
args_dispatch = []
|
args_dispatch = []
|
||||||
# Check if the test function returns void.
|
|
||||||
match = re.search(TEST_FUNCTION_VALIDATION_REGEX, line, re.I)
|
|
||||||
if not match:
|
|
||||||
raise ValueError("Test function should return 'void'\n%s" % line)
|
|
||||||
name = match.group(1)
|
|
||||||
line = line[len(match.group(0)):]
|
|
||||||
arg_idx = 0
|
arg_idx = 0
|
||||||
|
# Remove characters before arguments
|
||||||
|
line = line[line.find('(') + 1:]
|
||||||
# Process arguments, ex: <type> arg1, <type> arg2 )
|
# Process arguments, ex: <type> arg1, <type> arg2 )
|
||||||
# This script assumes that the argument list is terminated by ')'
|
# This script assumes that the argument list is terminated by ')'
|
||||||
# i.e. the test functions will not have a function pointer
|
# i.e. the test functions will not have a function pointer
|
||||||
@ -501,7 +496,7 @@ def parse_function_signature(line):
|
|||||||
"'char *' or 'data_t'\n%s" % line)
|
"'char *' or 'data_t'\n%s" % line)
|
||||||
arg_idx += 1
|
arg_idx += 1
|
||||||
|
|
||||||
return name, args, local_vars, args_dispatch
|
return args, local_vars, args_dispatch
|
||||||
|
|
||||||
|
|
||||||
def parse_function_code(funcs_f, dependencies, suite_dependencies):
|
def parse_function_code(funcs_f, dependencies, suite_dependencies):
|
||||||
@ -514,30 +509,38 @@ def parse_function_code(funcs_f, dependencies, suite_dependencies):
|
|||||||
:param suite_dependencies: List of test suite dependencies
|
:param suite_dependencies: List of test suite dependencies
|
||||||
:return: Function name, arguments, function code and dispatch code.
|
:return: Function name, arguments, function code and dispatch code.
|
||||||
"""
|
"""
|
||||||
code = '#line %d "%s"\n' % (funcs_f.line_no + 1, funcs_f.name)
|
line_directive = '#line %d "%s"\n' % (funcs_f.line_no + 1, funcs_f.name)
|
||||||
|
code = ''
|
||||||
has_exit_label = False
|
has_exit_label = False
|
||||||
for line in funcs_f:
|
for line in funcs_f:
|
||||||
# Check function signature. This script expects function name
|
# Check function signature. Function signature may be split
|
||||||
# and return type to be specified at the same line.
|
# across multiple lines. Here we try to find the start of
|
||||||
match = re.match(FUNCTION_ARG_LIST_START_REGEX, line, re.I)
|
# arguments list, then remove '\n's and apply the regex to
|
||||||
|
# detect function start.
|
||||||
|
up_to_arg_list_start = code + line[:line.find('(') + 1]
|
||||||
|
match = re.match(TEST_FUNCTION_VALIDATION_REGEX,
|
||||||
|
up_to_arg_list_start.replace('\n', ' '), re.I)
|
||||||
if match:
|
if match:
|
||||||
# check if we have full signature i.e. split in more lines
|
# check if we have full signature i.e. split in more lines
|
||||||
|
name = match.group('func_name')
|
||||||
if not re.match(FUNCTION_ARG_LIST_END_REGEX, line):
|
if not re.match(FUNCTION_ARG_LIST_END_REGEX, line):
|
||||||
for lin in funcs_f:
|
for lin in funcs_f:
|
||||||
line += lin
|
line += lin
|
||||||
if re.search(FUNCTION_ARG_LIST_END_REGEX, line):
|
if re.search(FUNCTION_ARG_LIST_END_REGEX, line):
|
||||||
break
|
break
|
||||||
name, args, local_vars, args_dispatch = parse_function_signature(
|
args, local_vars, args_dispatch = parse_function_arguments(
|
||||||
line)
|
line)
|
||||||
code += line.replace(name, 'test_' + name, 1)
|
code += line
|
||||||
name = 'test_' + name
|
|
||||||
break
|
break
|
||||||
else:
|
|
||||||
code += line
|
code += line
|
||||||
else:
|
else:
|
||||||
raise GeneratorInputError("file: %s - Test functions not found!" %
|
raise GeneratorInputError("file: %s - Test functions not found!" %
|
||||||
funcs_f.name)
|
funcs_f.name)
|
||||||
|
|
||||||
|
# Prefix test function name with 'test_'
|
||||||
|
code = code.replace(name, 'test_' + name, 1)
|
||||||
|
name = 'test_' + name
|
||||||
|
|
||||||
for line in funcs_f:
|
for line in funcs_f:
|
||||||
if re.search(END_CASE_REGEX, line):
|
if re.search(END_CASE_REGEX, line):
|
||||||
break
|
break
|
||||||
@ -557,7 +560,8 @@ def parse_function_code(funcs_f, dependencies, suite_dependencies):
|
|||||||
;
|
;
|
||||||
}""".join(split_code)
|
}""".join(split_code)
|
||||||
|
|
||||||
code += gen_function_wrapper(name, local_vars, args_dispatch)
|
code = line_directive + code + gen_function_wrapper(name, local_vars,
|
||||||
|
args_dispatch)
|
||||||
preprocessor_check_start, preprocessor_check_end = \
|
preprocessor_check_start, preprocessor_check_end = \
|
||||||
gen_dependencies(dependencies)
|
gen_dependencies(dependencies)
|
||||||
dispatch_code = gen_dispatch(name, suite_dependencies + dependencies)
|
dispatch_code = gen_dispatch(name, suite_dependencies + dependencies)
|
||||||
|
@ -31,7 +31,7 @@ from generate_test_code import gen_function_wrapper, gen_dispatch
|
|||||||
from generate_test_code import parse_until_pattern, GeneratorInputError
|
from generate_test_code import parse_until_pattern, GeneratorInputError
|
||||||
from generate_test_code import parse_suite_dependencies
|
from generate_test_code import parse_suite_dependencies
|
||||||
from generate_test_code import parse_function_dependencies
|
from generate_test_code import parse_function_dependencies
|
||||||
from generate_test_code import parse_function_signature, parse_function_code
|
from generate_test_code import parse_function_arguments, parse_function_code
|
||||||
from generate_test_code import parse_functions, END_HEADER_REGEX
|
from generate_test_code import parse_functions, END_HEADER_REGEX
|
||||||
from generate_test_code import END_SUITE_HELPERS_REGEX, escaped_split
|
from generate_test_code import END_SUITE_HELPERS_REGEX, escaped_split
|
||||||
from generate_test_code import parse_test_data, gen_dep_check
|
from generate_test_code import parse_test_data, gen_dep_check
|
||||||
@ -476,7 +476,7 @@ class ParseFuncDependencies(TestCase):
|
|||||||
|
|
||||||
class ParseFuncSignature(TestCase):
|
class ParseFuncSignature(TestCase):
|
||||||
"""
|
"""
|
||||||
Test Suite for parse_function_signature().
|
Test Suite for parse_function_arguments().
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def test_int_and_char_params(self):
|
def test_int_and_char_params(self):
|
||||||
@ -485,8 +485,7 @@ class ParseFuncSignature(TestCase):
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
line = 'void entropy_threshold( char * a, int b, int result )'
|
line = 'void entropy_threshold( char * a, int b, int result )'
|
||||||
name, args, local, arg_dispatch = parse_function_signature(line)
|
args, local, arg_dispatch = parse_function_arguments(line)
|
||||||
self.assertEqual(name, 'entropy_threshold')
|
|
||||||
self.assertEqual(args, ['char*', 'int', 'int'])
|
self.assertEqual(args, ['char*', 'int', 'int'])
|
||||||
self.assertEqual(local, '')
|
self.assertEqual(local, '')
|
||||||
self.assertEqual(arg_dispatch, ['(char *) params[0]',
|
self.assertEqual(arg_dispatch, ['(char *) params[0]',
|
||||||
@ -499,8 +498,7 @@ class ParseFuncSignature(TestCase):
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
line = 'void entropy_threshold( char * a, data_t * h, int result )'
|
line = 'void entropy_threshold( char * a, data_t * h, int result )'
|
||||||
name, args, local, arg_dispatch = parse_function_signature(line)
|
args, local, arg_dispatch = parse_function_arguments(line)
|
||||||
self.assertEqual(name, 'entropy_threshold')
|
|
||||||
self.assertEqual(args, ['char*', 'hex', 'int'])
|
self.assertEqual(args, ['char*', 'hex', 'int'])
|
||||||
self.assertEqual(local,
|
self.assertEqual(local,
|
||||||
' data_t data1 = {(uint8_t *) params[1], '
|
' data_t data1 = {(uint8_t *) params[1], '
|
||||||
@ -509,21 +507,13 @@ class ParseFuncSignature(TestCase):
|
|||||||
'&data1',
|
'&data1',
|
||||||
'*( (int *) params[3] )'])
|
'*( (int *) params[3] )'])
|
||||||
|
|
||||||
def test_non_void_function(self):
|
|
||||||
"""
|
|
||||||
Test invalid signature (non void).
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
line = 'int entropy_threshold( char * a, data_t * h, int result )'
|
|
||||||
self.assertRaises(ValueError, parse_function_signature, line)
|
|
||||||
|
|
||||||
def test_unsupported_arg(self):
|
def test_unsupported_arg(self):
|
||||||
"""
|
"""
|
||||||
Test unsupported arguments (not among int, char * and data_t)
|
Test unsupported arguments (not among int, char * and data_t)
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
line = 'int entropy_threshold( char * a, data_t * h, int * result )'
|
line = 'void entropy_threshold( char * a, data_t * h, char result )'
|
||||||
self.assertRaises(ValueError, parse_function_signature, line)
|
self.assertRaises(ValueError, parse_function_arguments, line)
|
||||||
|
|
||||||
def test_no_params(self):
|
def test_no_params(self):
|
||||||
"""
|
"""
|
||||||
@ -531,8 +521,7 @@ class ParseFuncSignature(TestCase):
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
line = 'void entropy_threshold()'
|
line = 'void entropy_threshold()'
|
||||||
name, args, local, arg_dispatch = parse_function_signature(line)
|
args, local, arg_dispatch = parse_function_arguments(line)
|
||||||
self.assertEqual(name, 'entropy_threshold')
|
|
||||||
self.assertEqual(args, [])
|
self.assertEqual(args, [])
|
||||||
self.assertEqual(local, '')
|
self.assertEqual(local, '')
|
||||||
self.assertEqual(arg_dispatch, [])
|
self.assertEqual(arg_dispatch, [])
|
||||||
@ -554,8 +543,9 @@ test
|
|||||||
function
|
function
|
||||||
'''
|
'''
|
||||||
stream = StringIOWrapper('test_suite_ut.function', data)
|
stream = StringIOWrapper('test_suite_ut.function', data)
|
||||||
self.assertRaises(GeneratorInputError, parse_function_code, stream, [],
|
err_msg = 'file: test_suite_ut.function - Test functions not found!'
|
||||||
[])
|
self.assertRaisesRegexp(GeneratorInputError, err_msg,
|
||||||
|
parse_function_code, stream, [], [])
|
||||||
|
|
||||||
def test_no_end_case_comment(self):
|
def test_no_end_case_comment(self):
|
||||||
"""
|
"""
|
||||||
@ -568,17 +558,19 @@ void test_func()
|
|||||||
}
|
}
|
||||||
'''
|
'''
|
||||||
stream = StringIOWrapper('test_suite_ut.function', data)
|
stream = StringIOWrapper('test_suite_ut.function', data)
|
||||||
self.assertRaises(GeneratorInputError, parse_function_code, stream, [],
|
err_msg = r'file: test_suite_ut.function - '\
|
||||||
[])
|
'end case pattern .*? not found!'
|
||||||
|
self.assertRaisesRegexp(GeneratorInputError, err_msg,
|
||||||
|
parse_function_code, stream, [], [])
|
||||||
|
|
||||||
@patch("generate_test_code.parse_function_signature")
|
@patch("generate_test_code.parse_function_arguments")
|
||||||
def test_function_called(self,
|
def test_function_called(self,
|
||||||
parse_function_signature_mock):
|
parse_function_arguments_mock):
|
||||||
"""
|
"""
|
||||||
Test parse_function_code()
|
Test parse_function_code()
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
parse_function_signature_mock.return_value = ('test_func', [], '', [])
|
parse_function_arguments_mock.return_value = ([], '', [])
|
||||||
data = '''
|
data = '''
|
||||||
void test_func()
|
void test_func()
|
||||||
{
|
{
|
||||||
@ -587,14 +579,14 @@ void test_func()
|
|||||||
stream = StringIOWrapper('test_suite_ut.function', data)
|
stream = StringIOWrapper('test_suite_ut.function', data)
|
||||||
self.assertRaises(GeneratorInputError, parse_function_code,
|
self.assertRaises(GeneratorInputError, parse_function_code,
|
||||||
stream, [], [])
|
stream, [], [])
|
||||||
self.assertTrue(parse_function_signature_mock.called)
|
self.assertTrue(parse_function_arguments_mock.called)
|
||||||
parse_function_signature_mock.assert_called_with('void test_func()\n')
|
parse_function_arguments_mock.assert_called_with('void test_func()\n')
|
||||||
|
|
||||||
@patch("generate_test_code.gen_dispatch")
|
@patch("generate_test_code.gen_dispatch")
|
||||||
@patch("generate_test_code.gen_dependencies")
|
@patch("generate_test_code.gen_dependencies")
|
||||||
@patch("generate_test_code.gen_function_wrapper")
|
@patch("generate_test_code.gen_function_wrapper")
|
||||||
@patch("generate_test_code.parse_function_signature")
|
@patch("generate_test_code.parse_function_arguments")
|
||||||
def test_return(self, parse_function_signature_mock,
|
def test_return(self, parse_function_arguments_mock,
|
||||||
gen_function_wrapper_mock,
|
gen_function_wrapper_mock,
|
||||||
gen_dependencies_mock,
|
gen_dependencies_mock,
|
||||||
gen_dispatch_mock):
|
gen_dispatch_mock):
|
||||||
@ -602,7 +594,7 @@ void test_func()
|
|||||||
Test generated code.
|
Test generated code.
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
parse_function_signature_mock.return_value = ('func', [], '', [])
|
parse_function_arguments_mock.return_value = ([], '', [])
|
||||||
gen_function_wrapper_mock.return_value = ''
|
gen_function_wrapper_mock.return_value = ''
|
||||||
gen_dependencies_mock.side_effect = gen_dependencies
|
gen_dependencies_mock.side_effect = gen_dependencies
|
||||||
gen_dispatch_mock.side_effect = gen_dispatch
|
gen_dispatch_mock.side_effect = gen_dispatch
|
||||||
@ -617,8 +609,8 @@ void func()
|
|||||||
stream = StringIOWrapper('test_suite_ut.function', data)
|
stream = StringIOWrapper('test_suite_ut.function', data)
|
||||||
name, arg, code, dispatch_code = parse_function_code(stream, [], [])
|
name, arg, code, dispatch_code = parse_function_code(stream, [], [])
|
||||||
|
|
||||||
self.assertTrue(parse_function_signature_mock.called)
|
self.assertTrue(parse_function_arguments_mock.called)
|
||||||
parse_function_signature_mock.assert_called_with('void func()\n')
|
parse_function_arguments_mock.assert_called_with('void func()\n')
|
||||||
gen_function_wrapper_mock.assert_called_with('test_func', '', [])
|
gen_function_wrapper_mock.assert_called_with('test_func', '', [])
|
||||||
self.assertEqual(name, 'test_func')
|
self.assertEqual(name, 'test_func')
|
||||||
self.assertEqual(arg, [])
|
self.assertEqual(arg, [])
|
||||||
@ -638,8 +630,8 @@ exit:
|
|||||||
@patch("generate_test_code.gen_dispatch")
|
@patch("generate_test_code.gen_dispatch")
|
||||||
@patch("generate_test_code.gen_dependencies")
|
@patch("generate_test_code.gen_dependencies")
|
||||||
@patch("generate_test_code.gen_function_wrapper")
|
@patch("generate_test_code.gen_function_wrapper")
|
||||||
@patch("generate_test_code.parse_function_signature")
|
@patch("generate_test_code.parse_function_arguments")
|
||||||
def test_with_exit_label(self, parse_function_signature_mock,
|
def test_with_exit_label(self, parse_function_arguments_mock,
|
||||||
gen_function_wrapper_mock,
|
gen_function_wrapper_mock,
|
||||||
gen_dependencies_mock,
|
gen_dependencies_mock,
|
||||||
gen_dispatch_mock):
|
gen_dispatch_mock):
|
||||||
@ -647,7 +639,7 @@ exit:
|
|||||||
Test when exit label is present.
|
Test when exit label is present.
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
parse_function_signature_mock.return_value = ('func', [], '', [])
|
parse_function_arguments_mock.return_value = ([], '', [])
|
||||||
gen_function_wrapper_mock.return_value = ''
|
gen_function_wrapper_mock.return_value = ''
|
||||||
gen_dependencies_mock.side_effect = gen_dependencies
|
gen_dependencies_mock.side_effect = gen_dependencies
|
||||||
gen_dispatch_mock.side_effect = gen_dispatch
|
gen_dispatch_mock.side_effect = gen_dispatch
|
||||||
@ -675,6 +667,66 @@ exit:
|
|||||||
yes sir yes sir
|
yes sir yes sir
|
||||||
3 bags full
|
3 bags full
|
||||||
}
|
}
|
||||||
|
'''
|
||||||
|
self.assertEqual(code, expected)
|
||||||
|
|
||||||
|
def test_non_void_function(self):
|
||||||
|
"""
|
||||||
|
Test invalid signature (non void).
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
data = 'int entropy_threshold( char * a, data_t * h, int result )'
|
||||||
|
err_msg = 'file: test_suite_ut.function - Test functions not found!'
|
||||||
|
stream = StringIOWrapper('test_suite_ut.function', data)
|
||||||
|
self.assertRaisesRegexp(GeneratorInputError, err_msg,
|
||||||
|
parse_function_code, stream, [], [])
|
||||||
|
|
||||||
|
@patch("generate_test_code.gen_dispatch")
|
||||||
|
@patch("generate_test_code.gen_dependencies")
|
||||||
|
@patch("generate_test_code.gen_function_wrapper")
|
||||||
|
@patch("generate_test_code.parse_function_arguments")
|
||||||
|
def test_functio_name_on_newline(self, parse_function_arguments_mock,
|
||||||
|
gen_function_wrapper_mock,
|
||||||
|
gen_dependencies_mock,
|
||||||
|
gen_dispatch_mock):
|
||||||
|
"""
|
||||||
|
Test when exit label is present.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
parse_function_arguments_mock.return_value = ([], '', [])
|
||||||
|
gen_function_wrapper_mock.return_value = ''
|
||||||
|
gen_dependencies_mock.side_effect = gen_dependencies
|
||||||
|
gen_dispatch_mock.side_effect = gen_dispatch
|
||||||
|
data = '''
|
||||||
|
void
|
||||||
|
|
||||||
|
|
||||||
|
func()
|
||||||
|
{
|
||||||
|
ba ba black sheep
|
||||||
|
have you any wool
|
||||||
|
exit:
|
||||||
|
yes sir yes sir
|
||||||
|
3 bags full
|
||||||
|
}
|
||||||
|
/* END_CASE */
|
||||||
|
'''
|
||||||
|
stream = StringIOWrapper('test_suite_ut.function', data)
|
||||||
|
_, _, code, _ = parse_function_code(stream, [], [])
|
||||||
|
|
||||||
|
expected = '''#line 1 "test_suite_ut.function"
|
||||||
|
|
||||||
|
void
|
||||||
|
|
||||||
|
|
||||||
|
test_func()
|
||||||
|
{
|
||||||
|
ba ba black sheep
|
||||||
|
have you any wool
|
||||||
|
exit:
|
||||||
|
yes sir yes sir
|
||||||
|
3 bags full
|
||||||
|
}
|
||||||
'''
|
'''
|
||||||
self.assertEqual(code, expected)
|
self.assertEqual(code, expected)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user