mbed-os test code generator

This commit is contained in:
Mohammad Azim Khan 2017-03-28 01:48:31 +01:00 committed by Mohammad Azim Khan
parent 1f29be7241
commit fff4904e6b
6 changed files with 1821 additions and 9 deletions

View file

@ -2,7 +2,7 @@
# To compile on SunOS: add "-lsocket -lnsl" to LDFLAGS
# To compile with PKCS11: add "-lpkcs11-helper" to LDFLAGS
CFLAGS ?= -O2
CFLAGS ?= -g3 #-O2
WARNING_CFLAGS ?= -Wall -W -Wdeclaration-after-statement -Wno-unused-function -Wno-unused-value
LDFLAGS ?=
@ -184,9 +184,16 @@ $(DEP):
C_FILES := $(addsuffix .c,$(APPS))
.SECONDEXPANSION:
$(C_FILES): %.c: suites/$$(func.$$*).function suites/%.data scripts/generate_code.pl suites/helpers.function suites/main_test.function
$(C_FILES): %.c: suites/$$(func.$$*).function suites/%.data scripts/gen_mbed_code.py suites/helpers.function suites/mbed_test.function suites/desktop_test.function
echo " Gen $@"
perl scripts/generate_code.pl suites $(func.$*) $*
# perl scripts/generate_code.pl suites $(func.$*) $*
python scripts/gen_mbed_code.py -f suites/$(func.$*).function \
-d suites/$*.data \
-t suites/mbed_test.function \
-p suites/desktop_test.function \
-s suites \
--help-file suites/helpers.function \
-o .
$(BINARIES): %$(EXEXT): %.c $(DEP)
@ -199,9 +206,9 @@ all: $(BINARIES)
clean:
ifndef WINDOWS
rm -rf $(APPS) *.c TESTS
rm -rf $(APPS) *.c *.data TESTS
else
del /Q /F *.c *.exe
del /Q /F *.c *.exe *.data
rmdir /Q /S TESTS
endif
@ -221,14 +228,15 @@ MBED_APPS := $(addprefix mbed_,$(APPS))
# name is used as the test group dir.
.SECONDEXPANSION:
$(MBED_APPS): mbed_%: suites/$$(func.$$*).function suites/%.data scripts/gen_mbed_code.py suites/helpers.function suites/main_test.function
echo " Gen ./TESTS/mbedtls/$*/main.c"
$(MBED_APPS): mbed_%: suites/$$(func.$$*).function suites/%.data scripts/gen_mbed_code.py suites/helpers.function suites/mbed_test.function suites/embedded_test.function
echo " Gen ./TESTS/mbedtls/$*/$*.c"
python scripts/gen_mbed_code.py -f suites/$(func.$*).function \
-d suites/$*.data \
-t suites/mbed_test.function \
-p suites/embedded_test.function \
-s suites \
--help-file suites/helpers.function \
-o ./TESTS
-o ./TESTS/mbedtls/$*
gen-mbed-test: $(MBED_APPS)

View file

@ -0,0 +1,623 @@
"""
mbed SDK
Copyright (c) 2017-2018 ARM Limited
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import os
import re
import argparse
import shutil
"""
Generates code in following structure.
<output dir>/
|-- host_tests/
| |-- mbedtls_test.py
| |-- suites/
| | |-- *.data files
| |-- mbedtls/
| | |-- <test suite #1>/
| | | |-- main.c
| | ...
| | |-- <test suite #n>/
| | | |-- main.c
| | |
"""
BEGIN_HEADER_REGEX = '/\*\s*BEGIN_HEADER\s*\*/'
END_HEADER_REGEX = '/\*\s*END_HEADER\s*\*/'
BEGIN_DEP_REGEX = 'BEGIN_DEPENDENCIES'
END_DEP_REGEX = 'END_DEPENDENCIES'
BEGIN_CASE_REGEX = '/\*\s*BEGIN_CASE\s*(.*?)\s*\*/'
END_CASE_REGEX = '/\*\s*END_CASE\s*\*/'
class InvalidFileFormat(Exception):
"""
Exception to indicate invalid file format.
"""
pass
def gen_deps(deps):
"""
Generates dependency i.e. if def and endif code
:param deps:
:return:
"""
dep_start = ''
dep_end = ''
for dep in deps:
if dep[0] == '!':
noT = '!'
dep = dep[1:]
else:
noT = ''
dep_start += '#if %sdefined(%s)\n' % (noT, dep)
dep_end = '#endif /* %s%s */\n' % (noT, dep) + dep_end
return dep_start, dep_end
def gen_deps_one_line(deps):
"""
Generates dependency checks in one line. Useful for writing code in #else case.
:param deps:
:return:
"""
defines = []
for dep in deps:
if dep[0] == '!':
noT = '!'
dep = dep[1:]
else:
noT = ''
defines.append('%sdefined(%s)' % (noT, dep))
return '#if ' + ' && '.join(defines)
def gen_function_wrapper(name, args_dispatch):
"""
Creates test function code
:param name:
:param args_dispatch:
:return:
"""
# Then create the wrapper
wrapper = '''
void {name}_wrapper( void ** params )
{{
{unused_params}
{name}( {args} );
}}
'''.format(name=name, unused_params='(void)params;' if len(args_dispatch) == 0 else '', args=', '.join(args_dispatch))
return wrapper
def gen_dispatch(name, deps):
"""
Generates dispatch condition for the functions.
:param name:
:param deps:
:return:
"""
if len(deps):
ifdef = gen_deps_one_line(deps)
dispatch_code = '''
{ifdef}
{name}_wrapper,
#else
NULL,
#endif
'''.format(ifdef=ifdef, name=name)
else:
dispatch_code = '''
{name}_wrapper,
'''.format(name=name)
return dispatch_code
def parse_suite_headers(line_no, funcs_f):
"""
Parses function headers.
:param line_no:
:param funcs_f:
:return:
"""
headers = '#line %d "%s"\n' % (line_no + 1, funcs_f.name)
for line in funcs_f:
line_no += 1
if re.search(END_HEADER_REGEX, line):
break
headers += line
else:
raise InvalidFileFormat("file: %s - end header pattern [%s] not found!" % (funcs_f.name, END_HEADER_REGEX))
return line_no, headers
def parse_suite_deps(line_no, funcs_f):
"""
Parses function dependencies.
:param line_no:
:param funcs_f:
:return:
"""
deps = []
for line in funcs_f:
line_no += 1
m = re.search('depends_on\:(.*)', line.strip())
if m:
deps += [x.strip() for x in m.group(1).split(':')]
if re.search(END_DEP_REGEX, line):
break
else:
raise InvalidFileFormat("file: %s - end dependency pattern [%s] not found!" % (funcs_f.name, END_DEP_REGEX))
return line_no, deps
def parse_function_deps(line):
"""
:param line:
:return:
"""
deps = []
m = re.search(BEGIN_CASE_REGEX, line)
dep_str = m.group(1)
if len(dep_str):
m = re.search('depends_on:(.*)', dep_str)
if m:
deps = m.group(1).strip().split(':')
return deps
def parse_function_signature(line):
"""
Parsing function signature
:param line:
:return:
"""
args = []
args_dispatch = []
m = re.search('\s*void\s+(\w+)\s*\(', line, re.I)
if not m:
raise ValueError("Test function should return 'void'\n%s" % line)
name = m.group(1)
line = line[len(m.group(0)):]
arg_idx = 0
for arg in line[:line.find(')')].split(','):
arg = arg.strip()
if arg == '':
continue
if re.search('int\s+.*', arg.strip()):
args.append('int')
args_dispatch.append('*( (int *) params[%d] )' % arg_idx)
elif re.search('char\s*\*\s*.*', arg.strip()):
args.append('char*')
args_dispatch.append('(char *) params[%d]' % arg_idx)
else:
raise ValueError("Test function arguments can only be 'int' or 'char *'\n%s" % line)
arg_idx += 1
return name, args, args_dispatch
def parse_function_code(line_no, funcs_f, deps, suite_deps):
"""
:param line_no:
:param funcs_f:
:param deps:
:param suite_deps:
:return:
"""
code = '#line %d "%s"\n' % (line_no + 1, funcs_f.name)
for line in funcs_f:
line_no += 1
# Check function signature
m = re.match('.*?\s+(\w+)\s*\(', line, re.I)
if m:
# check if we have full signature i.e. split in more lines
if not re.match('.*\)', line):
for lin in funcs_f:
line += lin
line_no += 1
if re.search('.*?\)', line):
break
name, args, args_dispatch = parse_function_signature(line)
code += line.replace(name, 'test_' + name)
name = 'test_' + name
break
else:
raise InvalidFileFormat("file: %s - Test functions not found!" % funcs_f.name)
for line in funcs_f:
line_no += 1
if re.search(END_CASE_REGEX, line):
break
code += line
else:
raise InvalidFileFormat("file: %s - end case pattern [%s] not found!" % (funcs_f.name, END_CASE_REGEX))
# Add exit label if not present
if code.find('exit:') == -1:
s = code.rsplit('}', 1)
if len(s) == 2:
code = """
exit:
;;
}
""".join(s)
code += gen_function_wrapper(name, args_dispatch)
ifdef, endif = gen_deps(deps)
dispatch_code = gen_dispatch(name, suite_deps + deps)
return line_no, name, args, ifdef + code + endif, dispatch_code
def parse_functions(funcs_f):
"""
Returns functions code pieces
:param funcs_f:
:return:
"""
line_no = 0
suite_headers = ''
suite_deps = []
suite_functions = ''
func_info = {}
function_idx = 0
dispatch_code = ''
for line in funcs_f:
line_no += 1
if re.search(BEGIN_HEADER_REGEX, line):
line_no, headers = parse_suite_headers(line_no, funcs_f)
suite_headers += headers
elif re.search(BEGIN_DEP_REGEX, line):
line_no, deps = parse_suite_deps(line_no, funcs_f)
suite_deps += deps
elif re.search(BEGIN_CASE_REGEX, line):
deps = parse_function_deps(line)
line_no, func_name, args, func_code, func_dispatch = parse_function_code(line_no, funcs_f, deps, suite_deps)
suite_functions += func_code
# Generate dispatch code and enumeration info
assert func_name not in func_info, "file: %s - function %s re-declared at line %d" % \
(funcs_f.name, func_name, line_no)
func_info[func_name] = (function_idx, args)
dispatch_code += '/* Function Id: %d */\n' % function_idx
dispatch_code += func_dispatch
function_idx += 1
ifdef, endif = gen_deps(suite_deps)
func_code = ifdef + suite_functions + endif
return dispatch_code, suite_headers, func_code, func_info
def escaped_split(str, ch):
"""
Split str on character ch but ignore escaped \{ch}
:param str:
:param ch:
:return:
"""
if len(ch) > 1:
raise ValueError('Expected split character. Found string!')
out = []
part = ''
escape = False
for i in range(len(str)):
if not escape and str[i] == ch:
out.append(part)
part = ''
else:
part += str[i]
escape = not escape and str[i] == '\\'
if len(part):
out.append(part)
return out
def parse_test_data(data_f):
"""
Parses .data file
:param data_f:
:return:
"""
STATE_READ_NAME = 0
STATE_READ_ARGS = 1
state = STATE_READ_NAME
deps = []
for line in data_f:
line = line.strip()
if len(line) and line[0] == '#': # Skip comments
continue
# skip blank lines
if len(line) == 0:
continue
if state == STATE_READ_NAME:
# Read test name
name = line
state = STATE_READ_ARGS
elif state == STATE_READ_ARGS:
# Check dependencies
m = re.search('depends_on\:(.*)', line)
if m:
deps = m.group(1).split(':')
else:
# Read test vectors
parts = escaped_split(line, ':')
function = parts[0]
args = parts[1:]
yield name, function, deps, args
deps = []
state = STATE_READ_NAME
def gen_dep_check(dep_id, dep):
"""
Generate code for the dependency.
:param dep_id:
:param dep:
:return:
"""
if dep[0] == '!':
noT = '!'
dep = dep[1:]
else:
noT = ''
dep_check = '''
if ( dep_id == {id} )
{{
#if {noT}defined({macro})
return( DEPENDENCY_SUPPORTED );
#else
return( DEPENDENCY_NOT_SUPPORTED );
#endif
}}
else
'''.format(noT=noT, macro=dep, id=dep_id)
return dep_check
def gen_expression_check(exp_id, exp):
"""
Generates code for expression check
:param exp_id:
:param exp:
:return:
"""
exp_code = '''
if ( exp_id == {exp_id} )
{{
*out_value = {expression};
}}
else
'''.format(exp_id=exp_id, expression=exp)
return exp_code
def gen_from_test_data(data_f, out_data_f, func_info):
"""
Generates dependency checks, expression code and intermediate data file from test data file.
:param data_f:
:param out_data_f:
:param func_info:
:return:
"""
unique_deps = []
unique_expressions = []
dep_check_code = ''
expression_code = ''
for test_name, function_name, test_deps, test_args in parse_test_data(data_f):
out_data_f.write(test_name + '\n')
func_id, func_args = func_info['test_' + function_name]
if len(test_deps):
out_data_f.write('depends_on')
for dep in test_deps:
if dep not in unique_deps:
unique_deps.append(dep)
dep_id = unique_deps.index(dep)
dep_check_code += gen_dep_check(dep_id, dep)
else:
dep_id = unique_deps.index(dep)
out_data_f.write(':' + str(dep_id))
out_data_f.write('\n')
assert len(test_args) == len(func_args), \
"Invalid number of arguments in test %s. See function %s signature." % (test_name, function_name)
out_data_f.write(str(func_id))
for i in xrange(len(test_args)):
typ = func_args[i]
val = test_args[i]
# check if val is a non literal int val
if typ == 'int' and not re.match('\d+', val): # its an expression # FIXME: Handle hex format. Tip: instead try converting int(str, 10) and int(str, 16)
typ = 'exp'
if val not in unique_expressions:
unique_expressions.append(val)
# exp_id can be derived from len(). But for readability and consistency with case of existing let's
# use index().
exp_id = unique_expressions.index(val)
expression_code += gen_expression_check(exp_id, val)
val = exp_id
else:
val = unique_expressions.index(val)
out_data_f.write(':' + typ + ':' + str(val))
out_data_f.write('\n\n')
# void unused params
if len(dep_check_code) == 0:
dep_check_code = '(void) dep_id;\n'
if len(expression_code) == 0:
expression_code = '(void) exp_id;\n'
expression_code += '(void) out_value;\n'
return dep_check_code, expression_code
def gen_mbed_code(funcs_file, data_file, template_file, platform_file, help_file, suites_dir, c_file, out_data_file):
"""
Generate mbed-os test code.
:param funcs_file:
:param dat a_file:
:param template_file:
:param platform_file:
:param help_file:
:param suites_dir:
:param c_file:
:param out_data_file:
:return:
"""
for name, path in [('Functions file', funcs_file),
('Data file', data_file),
('Template file', template_file),
('Platform file', platform_file),
('Help code file', help_file),
('Suites dir', suites_dir)]:
if not os.path.exists(path):
raise IOError("ERROR: %s [%s] not found!" % (name, path))
snippets = {'generator_script' : os.path.basename(__file__)}
# Read helpers
with open(help_file, 'r') as help_f, open(platform_file, 'r') as platform_f:
snippets['test_common_helper_file'] = help_file
snippets['test_common_helpers'] = help_f.read()
snippets['test_platform_file'] = platform_file
snippets['platform_code'] = platform_f.read().replace('DATA_FILE',
out_data_file.replace('\\', '\\\\')) # escape '\'
# Function code
with open(funcs_file, 'r') as funcs_f, open(data_file, 'r') as data_f, open(out_data_file, 'w') as out_data_f:
dispatch_code, func_headers, func_code, func_info = parse_functions(funcs_f)
snippets['function_headers'] = func_headers
snippets['functions_code'] = func_code
snippets['dispatch_code'] = dispatch_code
dep_check_code, expression_code = gen_from_test_data(data_f, out_data_f, func_info)
snippets['dep_check_code'] = dep_check_code
snippets['expression_code'] = expression_code
snippets['test_file'] = c_file
snippets['test_main_file'] = template_file
snippets['test_case_file'] = funcs_file
snippets['test_case_data_file'] = data_file
# Read Template
# Add functions
#
with open(template_file, 'r') as template_f, open(c_file, 'w') as c_f:
line_no = 1
for line in template_f.readlines():
snippets['line_no'] = line_no + 1 # Increment as it sets next line number
code = line.format(**snippets)
c_f.write(code)
line_no += 1
def check_cmd():
"""
Command line parser.
:return:
"""
parser = argparse.ArgumentParser(description='Generate code for mbed-os tests.')
parser.add_argument("-f", "--functions-file",
dest="funcs_file",
help="Functions file",
metavar="FUNCTIONS",
required=True)
parser.add_argument("-d", "--data-file",
dest="data_file",
help="Data file",
metavar="DATA",
required=True)
parser.add_argument("-t", "--template-file",
dest="template_file",
help="Template file",
metavar="TEMPLATE",
required=True)
parser.add_argument("-s", "--suites-dir",
dest="suites_dir",
help="Suites dir",
metavar="SUITES",
required=True)
parser.add_argument("--help-file",
dest="help_file",
help="Help file",
metavar="HELPER",
required=True)
parser.add_argument("-p", "--platform-file",
dest="platform_file",
help="Platform code file",
metavar="PLATFORM_FILE",
required=True)
parser.add_argument("-o", "--out-dir",
dest="out_dir",
help="Dir where generated code and scripts are copied",
metavar="OUT_DIR",
required=True)
args = parser.parse_args()
data_file_name = os.path.basename(args.data_file)
data_name = os.path.splitext(data_file_name)[0]
out_c_file = os.path.join(args.out_dir, data_name + '.c')
out_data_file = os.path.join(args.out_dir, data_file_name)
out_c_file_dir = os.path.dirname(out_c_file)
out_data_file_dir = os.path.dirname(out_data_file)
for d in [out_c_file_dir, out_data_file_dir]:
if not os.path.exists(d):
os.makedirs(d)
gen_mbed_code(args.funcs_file, args.data_file, args.template_file, args.platform_file,
args.help_file, args.suites_dir, out_c_file, out_data_file)
if __name__ == "__main__":
check_cmd()

View file

@ -0,0 +1,644 @@
#line 2 "suites/desktop_test.function"
/**
* \brief Varifies that string is in string parameter format i.e. "<str>"
* It also strips enclosing '"' from the input string.
*
* \param str String parameter.
*
* \return 0 if success else 1
*/
int verify_string( char **str )
{
if( (*str)[0] != '"' ||
(*str)[strlen( *str ) - 1] != '"' )
{
mbedtls_fprintf( stderr,
"Expected string (with \"\") for parameter and got: %s\n", *str );
return( -1 );
}
(*str)++;
(*str)[strlen( *str ) - 1] = '\0';
return( 0 );
}
/**
* \brief Varifies that string is an integer. Also gives the converted
* integer value.
*
* \param str Input string.
* \param value Pointer to int for output value.
*
* \return 0 if success else 1
*/
int verify_int( char *str, int *value )
{
size_t i;
int minus = 0;
int digits = 1;
int hex = 0;
for( i = 0; i < strlen( str ); i++ )
{
if( i == 0 && str[i] == '-' )
{
minus = 1;
continue;
}
if( ( ( minus && i == 2 ) || ( !minus && i == 1 ) ) &&
str[i - 1] == '0' && str[i] == 'x' )
{
hex = 1;
continue;
}
if( ! ( ( str[i] >= '0' && str[i] <= '9' ) ||
( hex && ( ( str[i] >= 'a' && str[i] <= 'f' ) ||
( str[i] >= 'A' && str[i] <= 'F' ) ) ) ) )
{
digits = 0;
break;
}
}
if( digits )
{
if( hex )
*value = strtol( str, NULL, 16 );
else
*value = strtol( str, NULL, 10 );
return( 0 );
}
mbedtls_fprintf( stderr,
"Expected integer for parameter and got: %s\n", str );
return( KEY_VALUE_MAPPING_NOT_FOUND );
}
/**
* \brief Usage string.
*
*/
#define USAGE \
"Usage: %s [OPTIONS] files...\n\n" \
" Command line arguments:\n" \
" files... One or more test data file. If no file is specified\n" \
" the followimg default test case is used:\n" \
" %s\n\n" \
" Options:\n" \
" -v | --verbose Display full information about each test\n" \
" -h | --help Display this information\n\n", \
argv[0], \
"TESTCASE_FILENAME"
/**
* \brief Read a line from the passed file pointer.
*
* \param f FILE pointer
* \param buf Pointer to memory to hold read line.
* \param len Length of the buf.
*
* \return 0 if success else -1
*/
int get_line( FILE *f, char *buf, size_t len )
{
char *ret;
int i = 0, str_len = 0, has_string = 0;
/* Read until we get a valid line */
do
{
ret = fgets( buf, len, f );
if( ret == NULL )
return( -1 );
str_len = strlen( buf );
/* Skip empty line and comment */
if ( str_len == 0 || buf[0] == '#' )
continue;
has_string = 0;
for ( i = 0; i < str_len; i++ )
{
char c = buf[i];
if ( c != ' ' && c != '\t' && c != '\n' &&
c != '\v' && c != '\f' && c != '\r' )
{
has_string = 1;
break;
}
}
} while( !has_string );
/* Strip new line and carriage return */
ret = buf + strlen( buf );
if( ret-- > buf && *ret == '\n' )
*ret = '\0';
if( ret-- > buf && *ret == '\r' )
*ret = '\0';
return( 0 );
}
/**
* \brief Splits string delimited by ':'. Ignores '\:'.
*
* \param buf Input string
* \param len Input string length
* \param params Out params found
* \param params_len Out params array len
*
* \return Count of strings found.
*/
static int parse_arguments( char *buf, size_t len, char **params,
size_t params_len )
{
size_t cnt = 0, i;
char *cur = buf;
char *p = buf, *q;
params[cnt++] = cur;
while( *p != '\0' && p < buf + len )
{
if( *p == '\\' )
{
p++;
p++;
continue;
}
if( *p == ':' )
{
if( p + 1 < buf + len )
{
cur = p + 1;
assert( cnt < params_len );
params[cnt++] = cur;
}
*p = '\0';
}
p++;
}
/* Replace newlines, question marks and colons in strings */
for( i = 0; i < cnt; i++ )
{
p = params[i];
q = params[i];
while( *p != '\0' )
{
if( *p == '\\' && *(p + 1) == 'n' )
{
p += 2;
*(q++) = '\n';
}
else if( *p == '\\' && *(p + 1) == ':' )
{
p += 2;
*(q++) = ':';
}
else if( *p == '\\' && *(p + 1) == '?' )
{
p += 2;
*(q++) = '?';
}
else
*(q++) = *(p++);
}
*q = '\0';
}
return( cnt );
}
/**
* \brief Converts parameters into test function consumable parameters.
* Example: Input: {"int", "0", "char*", "Hello",
* "hex", "abef", "exp", "1"}
* Output: {
* 0, // Verified int
* "Hello", // Verified string
* 2, { 0xab, 0xef },// Converted len,hex pair
* 9600 // Evaluated expression
* }
*
*
* \param cnt Input string.
* \param params Out array of found strings.
* \param int_params_store Memory for storing processed integer parameters.
*
* \return 0 for success else 1
*/
static int convert_params( size_t cnt , char ** params , int * int_params_store )
{
char ** cur = params;
char ** out = params;
int ret = ( DISPATCH_TEST_SUCCESS );
while ( cur - params < (int) cnt )
{
char * type = *cur++;
char * val = *cur++;
if ( strcmp( type, "char*" ) == 0 )
{
if ( verify_string( &val ) == 0 )
{
*out++ = val;
}
else
{
ret = ( DISPATCH_INVALID_TEST_DATA );
break;
}
}
else if ( strcmp( type, "int" ) == 0 )
{
if ( verify_int ( val, int_params_store ) == 0 )
{
*out++ = (char *) int_params_store++;
}
else
{
ret = ( DISPATCH_INVALID_TEST_DATA );
break;
}
}
else if ( strcmp( type, "hex" ) == 0 )
{
*int_params_store = unhexify( (unsigned char *) val, val );
*out++ = (char *)int_params_store++;
*out++ = val;
}
else if ( strcmp( type, "exp" ) == 0 )
{
int exp_id = strtol( val, NULL, 10 );
if ( get_expression ( exp_id, int_params_store ) == 0 )
{
*out++ = (char *)int_params_store++;
}
else
{
ret = ( DISPATCH_INVALID_TEST_DATA );
break;
}
}
else
{
ret = ( DISPATCH_INVALID_TEST_DATA );
break;
}
}
return( ret );
}
/**
* \brief Tests snprintf implementation with test input.
*
* \param n Buffer test length.
* \param ref_buf Expected buffer.
* \param ref_ret Expected snprintf return value.
*
* \return 0 for success else 1
*/
static int test_snprintf( size_t n, const char ref_buf[10], int ref_ret )
{
int ret;
char buf[10] = "xxxxxxxxx";
const char ref[10] = "xxxxxxxxx";
ret = mbedtls_snprintf( buf, n, "%s", "123" );
if( ret < 0 || (size_t) ret >= n )
ret = -1;
if( strncmp( ref_buf, buf, sizeof( buf ) ) != 0 ||
ref_ret != ret ||
memcmp( buf + n, ref + n, sizeof( buf ) - n ) != 0 )
{
return( 1 );
}
return( 0 );
}
/**
* \brief Tests snprintf implementation.
*
* \param none
*
* \return 0 for success else 1
*/
static int run_test_snprintf( void )
{
return( test_snprintf( 0, "xxxxxxxxx", -1 ) != 0 ||
test_snprintf( 1, "", -1 ) != 0 ||
test_snprintf( 2, "1", -1 ) != 0 ||
test_snprintf( 3, "12", -1 ) != 0 ||
test_snprintf( 4, "123", 3 ) != 0 ||
test_snprintf( 5, "123", 3 ) != 0 );
}
/**
* \brief Desktop implementation of execute_tests().
* Parses command line and executes tests from
* supplied or default data file.
*
* \param argc Command line argument count.
* \param argv Argument array.
*
* \return Program exit status.
*/
int execute_tests( int argc , const char ** argv )
{
/* Local Configurations and options */
const char *default_filename = "DATA_FILE";
const char *test_filename = NULL;
const char **test_files = NULL;
int testfile_count = 0;
int option_verbose = 0;
/* Other Local variables */
int arg_index = 1;
const char *next_arg;
int testfile_index, ret, i, cnt;
int total_errors = 0, total_tests = 0, total_skipped = 0;
FILE *file;
char buf[5000];
char *params[50];
int int_params[50]; // Store for proccessed integer params.
void *pointer;
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
int stdout_fd = -1;
#endif /* __unix__ || __APPLE__ __MACH__ */
#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C) && \
!defined(TEST_SUITE_MEMORY_BUFFER_ALLOC)
unsigned char alloc_buf[1000000];
mbedtls_memory_buffer_alloc_init( alloc_buf, sizeof( alloc_buf ) );
#endif
/*
* The C standard doesn't guarantee that all-bits-0 is the representation
* of a NULL pointer. We do however use that in our code for initializing
* structures, which should work on every modern platform. Let's be sure.
*/
memset( &pointer, 0, sizeof( void * ) );
if( pointer != NULL )
{
mbedtls_fprintf( stderr, "all-bits-zero is not a NULL pointer\n" );
return( 1 );
}
/*
* Make sure we have a snprintf that correctly zero-terminates
*/
if( run_test_snprintf() != 0 )
{
mbedtls_fprintf( stderr, "the snprintf implementation is broken\n" );
return( 0 );
}
while( arg_index < argc )
{
next_arg = argv[ arg_index ];
if( strcmp(next_arg, "--verbose" ) == 0 ||
strcmp(next_arg, "-v" ) == 0 )
{
option_verbose = 1;
}
else if( strcmp(next_arg, "--help" ) == 0 ||
strcmp(next_arg, "-h" ) == 0 )
{
mbedtls_fprintf( stdout, USAGE );
mbedtls_exit( EXIT_SUCCESS );
}
else
{
/* Not an option, therefore treat all further arguments as the file
* list.
*/
test_files = &argv[ arg_index ];
testfile_count = argc - arg_index;
}
arg_index++;
}
/* If no files were specified, assume a default */
if ( test_files == NULL || testfile_count == 0 )
{
test_files = &default_filename;
testfile_count = 1;
}
/* Initialize the struct that holds information about the last test */
memset( &test_info, 0, sizeof( test_info ) );
/* Now begin to execute the tests in the testfiles */
for ( testfile_index = 0;
testfile_index < testfile_count;
testfile_index++ )
{
int unmet_dep_count = 0;
char *unmet_dependencies[20];
test_filename = test_files[ testfile_index ];
file = fopen( test_filename, "r" );
if( file == NULL )
{
mbedtls_fprintf( stderr, "Failed to open test file: %s\n",
test_filename );
return( 1 );
}
while( !feof( file ) )
{
if( unmet_dep_count > 0 )
{
mbedtls_fprintf( stderr,
"FATAL: Dep count larger than zero at start of loop\n" );
mbedtls_exit( MBEDTLS_EXIT_FAILURE );
}
unmet_dep_count = 0;
if( ( ret = get_line( file, buf, sizeof(buf) ) ) != 0 )
break;
mbedtls_fprintf( stdout, "%s%.66s", test_info.failed ? "\n" : "", buf );
mbedtls_fprintf( stdout, " " );
for( i = strlen( buf ) + 1; i < 67; i++ )
mbedtls_fprintf( stdout, "." );
mbedtls_fprintf( stdout, " " );
fflush( stdout );
total_tests++;
if( ( ret = get_line( file, buf, sizeof( buf ) ) ) != 0 )
break;
cnt = parse_arguments( buf, strlen( buf ), params,
sizeof( params ) / sizeof( params[0] ) );
if( strcmp( params[0], "depends_on" ) == 0 )
{
for( i = 1; i < cnt; i++ )
{
int dep_id = strtol( params[i], NULL, 10 );
if( dep_check( dep_id ) != DEPENDENCY_SUPPORTED )
{
if( 0 == option_verbose )
{
/* Only one count is needed if not verbose */
unmet_dep_count++;
break;
}
unmet_dependencies[ unmet_dep_count ] = strdup( params[i] );
if( unmet_dependencies[ unmet_dep_count ] == NULL )
{
mbedtls_fprintf( stderr, "FATAL: Out of memory\n" );
mbedtls_exit( MBEDTLS_EXIT_FAILURE );
}
unmet_dep_count++;
}
}
if( ( ret = get_line( file, buf, sizeof( buf ) ) ) != 0 )
break;
cnt = parse_arguments( buf, strlen( buf ), params,
sizeof( params ) / sizeof( params[0] ) );
}
// If there are no unmet dependencies execute the test
if( unmet_dep_count == 0 )
{
test_info.failed = 0;
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
/* Suppress all output from the library unless we're verbose
* mode
*/
if( !option_verbose )
{
stdout_fd = redirect_output( &stdout, "/dev/null" );
if( stdout_fd == -1 )
{
/* Redirection has failed with no stdout so exit */
exit( 1 );
}
}
#endif /* __unix__ || __APPLE__ __MACH__ */
ret = convert_params( cnt - 1, params + 1, int_params );
if ( DISPATCH_TEST_SUCCESS == ret )
{
int function_id = strtol( params[0], NULL, 10 );
ret = dispatch_test( function_id, (void **)( params + 1 ) );
}
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
if( !option_verbose && restore_output( &stdout, stdout_fd ) )
{
/* Redirection has failed with no stdout so exit */
exit( 1 );
}
#endif /* __unix__ || __APPLE__ __MACH__ */
}
if( unmet_dep_count > 0 || ret == DISPATCH_UNSUPPORTED_SUITE )
{
total_skipped++;
mbedtls_fprintf( stdout, "----" );
if( 1 == option_verbose && ret == DISPATCH_UNSUPPORTED_SUITE )
{
mbedtls_fprintf( stdout, "\n Test Suite not enabled" );
}
if( 1 == option_verbose && unmet_dep_count > 0 )
{
mbedtls_fprintf( stdout, "\n Unmet dependencies: " );
for( i = 0; i < unmet_dep_count; i++ )
{
mbedtls_fprintf( stdout, "%s ",
unmet_dependencies[i] );
free( unmet_dependencies[i] );
}
}
mbedtls_fprintf( stdout, "\n" );
fflush( stdout );
unmet_dep_count = 0;
}
else if( ret == DISPATCH_TEST_SUCCESS )
{
if( test_info.failed == 0 )
{
mbedtls_fprintf( stdout, "PASS\n" );
}
else
{
total_errors++;
mbedtls_fprintf( stdout, "FAILED\n" );
mbedtls_fprintf( stdout, " %s\n at line %d, %s\n",
test_info.test, test_info.line_no,
test_info.filename );
}
fflush( stdout );
}
else if( ret == DISPATCH_INVALID_TEST_DATA )
{
mbedtls_fprintf( stderr, "FAILED: FATAL PARSE ERROR\n" );
fclose( file );
mbedtls_exit( 2 );
}
else if( ret == DISPATCH_TEST_FN_NOT_FOUND )
{
mbedtls_fprintf( stderr, "FAILED: FATAL TEST FUNCTION NOT FUND\n" );
fclose( file );
mbedtls_exit( 2 );
}
else
total_errors++;
}
fclose( file );
/* In case we encounter early end of file */
for( i = 0; i < unmet_dep_count; i++ )
free( unmet_dependencies[i] );
}
mbedtls_fprintf( stdout, "\n----------------------------------------------------------------------------\n\n");
if( total_errors == 0 )
mbedtls_fprintf( stdout, "PASSED" );
else
mbedtls_fprintf( stdout, "FAILED" );
mbedtls_fprintf( stdout, " (%d / %d tests (%d skipped))\n",
total_tests - total_errors, total_tests, total_skipped );
#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C) && \
!defined(TEST_SUITE_MEMORY_BUFFER_ALLOC)
#if defined(MBEDTLS_MEMORY_DEBUG)
mbedtls_memory_buffer_alloc_status();
#endif
mbedtls_memory_buffer_alloc_free();
#endif
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
if( stdout_fd != -1 )
close_output( stdout );
#endif /* __unix__ || __APPLE__ __MACH__ */
return( total_errors != 0 );
}

View file

@ -0,0 +1,364 @@
#line 2 "embedded_test.function"
#include "greentea-client/test_env_c.h"
/**
* \brief Increments pointer and asserts that it does not overflow.
*
* \param p Pointer to byte array
* \param start Pointer to start of byte array
* \param len Length of byte array
* \param step Increment size
*
*/
#define INCR_ASSERT(p, start, len, step) do \
{ \
assert( p >= start ); \
assert( sizeof( *p ) == sizeof( *start ) ); \
/* <= is checked to support use inside a loop where \
pointer is incremented after reading data. */ \
assert( (uint32_t)( (p - start) + step ) <= len ); \
p += step; \
} \
while( 0 )
/**
* \brief 4 byte align unsigned char pointer
*
* \param p Pointer to byte array
* \param start Pointer to start of byte array
* \param len Length of byte array
*
*/
#define ALIGN_32BIT(p, start, len) do \
{ \
uint32_t align = ( - (uintptr_t)p ) % 4; \
INCR_ASSERT(p, start, len, align); \
} \
while( 0 )
/**
* \brief Verify dependencies. Dependency identifiers are
* encoded in the buffer as 8 bit unsigned integers.
*
* \param count Number of dependencies.
* \param dep_p Pointer to buffer.
*
* \return DEPENDENCY_SUPPORTED if success else DEPENDENCY_NOT_SUPPORTED.
*/
int verify_dependencies( uint8_t count, uint8_t * dep_p )
{
uint8_t i;
for ( i = 0; i < count; i++ )
{
if ( dep_check( (int)(dep_p[i]) ) != DEPENDENCY_SUPPORTED )
return( DEPENDENCY_NOT_SUPPORTED );
}
return( DEPENDENCY_SUPPORTED );
}
/**
* \brief Receives unsigned integer on serial interface.
* Integers are encoded in network order.
*
* \param none
*
* \return unsigned int
*/
uint32_t receive_uint32()
{
uint32_t value;
value = (uint8_t)greentea_getc() << 24;
value |= (uint8_t)greentea_getc() << 16;
value |= (uint8_t)greentea_getc() << 8;
value |= (uint8_t)greentea_getc();
return( (uint32_t)value );
}
/**
* \brief Parses out an unsigned 32 int value from the byte array.
* Integers are encoded in network order.
*
* \param p Pointer to byte array
*
* \return unsigned int
*/
uint32_t parse_uint32( uint8_t * p )
{
uint32_t value;
value = *p++ << 24;
value |= *p++ << 16;
value |= *p++ << 8;
value |= *p;
return( value );
}
/**
* \brief Receives test data on serial as greentea key,value pair:
* {{<length>;<byte array>}}
*
* \param data_len Out pointer to hold received data length.
*
* \return Byte array.
*/
uint8_t * receive_data( uint32_t * data_len )
{
uint32_t i = 0, errors = 0;
char c;
uint8_t * data = NULL;
/* Read opening braces */
i = 0;
while ( i < 2 )
{
c = greentea_getc();
/* Ignore any prevous CR LF characters */
if ( c == '\n' || c == '\r' )
continue;
i++;
if ( c != '{' )
return( NULL );
}
/* Read data length */
*data_len = receive_uint32();
data = (uint8_t *)malloc( *data_len );
assert( data != NULL );
greentea_getc(); // read ';' received after key i.e. *data_len
for( i = 0; i < *data_len; i++ )
data[i] = greentea_getc();
/* Read closing braces */
for( i = 0; i < 2; i++ )
{
c = greentea_getc();
if ( c != '}' )
{
errors++;
break;
}
}
if ( errors )
{
free( data );
data = NULL;
*data_len = 0;
}
return( data );
}
/**
* \brief Parses received byte array for test parameters.
*
* \param count Parameter count
* \param data Received Byte array
* \param data_len Byte array length
* \param error Parsing error out variable.
*
* \return Array of parsed parameters allocated on heap.
* Note: Caller has the responsibility to delete
* the memory after use.
*/
void ** parse_parameters( uint8_t count, uint8_t * data, uint32_t data_len,
int * error )
{
uint32_t i = 0;
char c;
void ** params = NULL;
void ** cur = NULL;
uint8_t * p = NULL;
params = (void **)malloc( sizeof( void *) * ( count + 1 ) );
assert( params != NULL );
params[count] = NULL;
cur = params;
p = data;
/* Parameters */
for( i = 0; i < count; i++ )
{
c = (char)*p;
INCR_ASSERT( p, data, data_len, 1 );
/* Align p to 4 bytes for int, expression, string len or hex length */
ALIGN_32BIT( p, data, data_len );
/* Network to host conversion */
*( (int32_t *)p ) = (int32_t)parse_uint32( p );
switch( c )
{
case 'E':
{
if ( get_expression( *( (int32_t *)p ), (int32_t *)p ) )
{
*error = KEY_VALUE_MAPPING_NOT_FOUND;
goto exit;
}
} /* Intentional fall through */
case 'I':
{
*cur++ = (void *)p;
INCR_ASSERT( p, data, data_len, sizeof( int32_t ) );
}
break;
case 'H':
{
*cur++ = (void *)p;
} /* Intentional fall through */
case 'S':
{
uint32_t sz = *( (int32_t *)p );
INCR_ASSERT( p, data, data_len, sizeof( int32_t ) );
*cur++ = (void *)p;
INCR_ASSERT( p, data, data_len, sz );
}
break;
default:
{
*error = DISPATCH_INVALID_TEST_DATA;
goto exit;
}
break;
}
}
exit:
if ( *error )
{
free( params );
params = NULL;
}
return( params );
}
/**
* \brief Sends greentea key and int value pair to host.
*
* \param key key string
* \param value integer value
*
* \return void
*/
void send_key_integer( char * key, int value )
{
char str[50];
snprintf( str, sizeof( str ), "%d", value );
greentea_send_kv_c( key, str );
}
/**
* \brief Sends test setup failure to the host.
*
* \param failure Test set failure
*
* \return void
*/
void send_failure( int failure )
{
send_key_integer( "F", failure );
}
/**
* \brief Sends test status to the host.
*
* \param status Test status (PASS=0/FAIL=!0)
*
* \return void
*/
void send_status( int status )
{
send_key_integer( "R", status );
}
/**
* \brief Embedded implementation of execute_tests().
* Ignores command line and received test data
* on serial.
*
* \param argc not used
* \param argv not used
*
* \return Program exit status.
*/
int execute_tests( int args, const char ** argv )
{
int ret = 0;
uint32_t data_len = 0;
uint8_t count = 0, function_id;
void ** params = NULL;
uint8_t * data = NULL, * p = NULL;
GREENTEA_SETUP_C( 180, "mbedtls_test" );
greentea_send_kv_c( "GO", " " );
while ( 1 )
{
ret = 0;
test_info.failed = 0;
data_len = 0;
data = receive_data( &data_len );
if ( data == NULL )
continue;
p = data;
do
{
/* Read dependency count */
count = *p;
assert( count < data_len );
INCR_ASSERT( p, data, data_len, sizeof( uint8_t ) );
ret = verify_dependencies( count, p );
if ( ret != DEPENDENCY_SUPPORTED )
break;
INCR_ASSERT( p, data, data_len, count );
/* Read function id */
function_id = *p;
INCR_ASSERT( p, data, data_len, sizeof( uint8_t ) );
/* Read number of parameters */
count = *p;
INCR_ASSERT( p, data, data_len, sizeof( uint8_t ) );
params = parse_parameters( count, p, data_len - (p - data), &ret );
if ( ret )
break;
ret = dispatch_test( function_id, params );
}
while ( 0 );
if ( data )
{
free(data);
data = NULL;
}
if ( params )
{
free( params );
params = NULL;
}
if ( ret )
send_failure( ret );
else
send_status( test_info.failed );
}
return( 0 );
}

View file

@ -0,0 +1,173 @@
#line 2 "suites/mbed_test.function"
/*
* *** THIS FILE HAS BEEN MACHINE GENERATED ***
*
* This file has been machine generated using the script:
* {generator_script}
*
* Test file : {test_file}
*
* The following files were used to create this file.
*
* Main code file : {test_main_file}
* Platform code file : {test_platform_file}
* Helper file : {test_common_helper_file}
* Test suite file : {test_case_file}
* Test suite data : {test_case_data_file}
*
*
* This file is part of mbed TLS (https://tls.mbed.org)
*/
#if !defined(MBEDTLS_CONFIG_FILE)
#include <mbedtls/config.h>
#else
#include MBEDTLS_CONFIG_FILE
#endif
/*----------------------------------------------------------------------------*/
/* Common helper code */
{test_common_helpers}
#line {line_no} "suites/mbed_test.function"
/*----------------------------------------------------------------------------*/
/* Test Suite Code */
#define TEST_SUITE_ACTIVE
{function_headers}
{functions_code}
#line {line_no} "suites/mbed_test.function"
/*----------------------------------------------------------------------------*/
/* Test dispatch code */
/**
* \brief Evaluates an expression/macro into its literal integer value.
* For optimizing space for embedded targets each expression/macro
* is identified by a unique identifier instead of string literals.
* Identifiers and evaluation code is generated by script:
* {generator_script}
*
* \param exp_id Expression identifier.
* \param out_value Pointer to int to hold the integer.
*
* \return 0 if exp_id is found. 1 otherwise.
*/
int get_expression( int32_t exp_id, int32_t * out_value )
{{
{expression_code}
#line {line_no} "suites/mbed_test.function"
{{
return( KEY_VALUE_MAPPING_NOT_FOUND );
}}
return( KEY_VALUE_MAPPING_FOUND );
}}
/**
* \brief Checks if the dependency i.e. the compile flag is set.
* For optimizing space for embedded targets each dependency
* is identified by a unique identifier instead of string literals.
* Identifiers and check code is generated by script:
* {generator_script}
*
* \param exp_id Dependency identifier.
*
* \return DEPENDENCY_SUPPORTED if set else DEPENDENCY_NOT_SUPPORTED
*/
int dep_check( int dep_id )
{{
{dep_check_code}
#line {line_no} "suites/mbed_test.function"
{{
return( DEPENDENCY_NOT_SUPPORTED );
}}
}}
/**
* \brief Function pointer type for test function wrappers.
*
*
* \param void ** Pointer to void pointers. Represents an array of test
* function parameters.
*
* \return void
*/
typedef void (*TestWrapper_t)( void ** );
/**
* \brief Table of test function wrappers. Used by dispatch_test().
* This table is populated by script:
* {generator_script}
*
*/
TestWrapper_t test_funcs[] =
{{
{dispatch_code}
#line {line_no} "suites/mbed_test.function"
}};
/**
* \brief Dispatches test functions based on function index.
*
* \param exp_id Test function index.
*
* \return DISPATCH_TEST_SUCCESS if found
* DISPATCH_TEST_FN_NOT_FOUND if not found
* DISPATCH_UNSUPPORTED_SUITE if not compile time enabled.
*/
int dispatch_test( int func_idx, void ** params )
{{
int ret = DISPATCH_TEST_SUCCESS;
TestWrapper_t fp = NULL;
if ( func_idx < (int)( sizeof(test_funcs)/sizeof( TestWrapper_t ) ) )
{{
fp = test_funcs[func_idx];
if ( fp )
fp( params );
else
ret = ( DISPATCH_UNSUPPORTED_SUITE );
}} else
{{
ret = ( DISPATCH_TEST_FN_NOT_FOUND );
}}
return( ret );
}}
{platform_code}
#line {line_no} "suites/mbed_test.function"
/*----------------------------------------------------------------------------*/
/* Main Test code */
/**
* \brief Program main. Invokes platform specific execute_tests().
*
* \param argc Command line arguments count.
* \param argv Array of command line arguments.
*
* \return Exit code.
*/
int main( int argc, const char *argv[] )
{{
return execute_tests( argc, argv );
}}

View file

@ -1,5 +1,5 @@
Decrypt empty buffer
depends_on:MBEDTLS_CHACHAPOLY_C:
depends_on:MBEDTLS_CHACHAPOLY_C
dec_empty_buf:
ChaCha20+Poly1305 Encrypt and decrypt 0 bytes