Added framework as a flattened directory

Signed-off-by: Minos Galanakis <minos.galanakis@arm.com>
diff --git a/framework/scripts/generate_test_code.py b/framework/scripts/generate_test_code.py
new file mode 100755
index 0000000..2ac00ad
--- /dev/null
+++ b/framework/scripts/generate_test_code.py
@@ -0,0 +1,1297 @@
+#!/usr/bin/env python3
+# Test suites code generator.
+#
+# Copyright The Mbed TLS Contributors
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+"""
+This script is a key part of Mbed TLS test suites framework. For
+understanding the script it is important to understand the
+framework. This doc string contains a summary of the framework
+and explains the function of this script.
+
+Mbed TLS test suites:
+=====================
+Scope:
+------
+The test suites focus on unit testing the crypto primitives and also
+include x509 parser tests. Tests can be added to test any Mbed TLS
+module. However, the framework is not capable of testing SSL
+protocol, since that requires full stack execution and that is best
+tested as part of the system test.
+
+Test case definition:
+---------------------
+Tests are defined in a test_suite_<module>[.<optional sub module>].data
+file. A test definition contains:
+ test name
+ optional build macro dependencies
+ test function
+ test parameters
+
+Test dependencies are build macros that can be specified to indicate
+the build config in which the test is valid. For example if a test
+depends on a feature that is only enabled by defining a macro. Then
+that macro should be specified as a dependency of the test.
+
+Test function is the function that implements the test steps. This
+function is specified for different tests that perform same steps
+with different parameters.
+
+Test parameters are specified in string form separated by ':'.
+Parameters can be of type string, binary data specified as hex
+string and integer constants specified as integer, macro or
+as an expression. Following is an example test definition:
+
+ AES 128 GCM Encrypt and decrypt 8 bytes
+ depends_on:MBEDTLS_AES_C:MBEDTLS_GCM_C
+ enc_dec_buf:MBEDTLS_CIPHER_AES_128_GCM:"AES-128-GCM":128:8:-1
+
+Test functions:
+---------------
+Test functions are coded in C in test_suite_<module>.function files.
+Functions file is itself not compilable and contains special
+format patterns to specify test suite dependencies, start and end
+of functions and function dependencies. Check any existing functions
+file for example.
+
+Execution:
+----------
+Tests are executed in 3 steps:
+- Generating test_suite_<module>[.<optional sub module>].c file
+  for each corresponding .data file.
+- Building each source file into executables.
+- Running each executable and printing report.
+
+Generating C test source requires more than just the test functions.
+Following extras are required:
+- Process main()
+- Reading .data file and dispatching test cases.
+- Platform specific test case execution
+- Dependency checking
+- Integer expression evaluation
+- Test function dispatch
+
+Build dependencies and integer expressions (in the test parameters)
+are specified as strings in the .data file. Their run time value is
+not known at the generation stage. Hence, they need to be translated
+into run time evaluations. This script generates the run time checks
+for dependencies and integer expressions.
+
+Similarly, function names have to be translated into function calls.
+This script also generates code for function dispatch.
+
+The extra code mentioned here is either generated by this script
+or it comes from the input files: helpers file, platform file and
+the template file.
+
+Helper file:
+------------
+Helpers file contains common helper/utility functions and data.
+
+Platform file:
+--------------
+Platform file contains platform specific setup code and test case
+dispatch code. For example, host_test.function reads test data
+file from host's file system and dispatches tests.
+
+Template file:
+---------
+Template file for example main_test.function is a template C file in
+which generated code and code from input files is substituted to
+generate a compilable C file. It also contains skeleton functions for
+dependency checks, expression evaluation and function dispatch. These
+functions are populated with checks and return codes by this script.
+
+Template file contains "replacement" fields that are formatted
+strings processed by Python string.Template.substitute() method.
+
+This script:
+============
+Core function of this script is to fill the template file with
+code that is generated or read from helpers and platform files.
+
+This script replaces following fields in the template and generates
+the test source file:
+
+__MBEDTLS_TEST_TEMPLATE__TEST_COMMON_HELPERS
+            All common code from helpers.function
+            is substituted here.
+__MBEDTLS_TEST_TEMPLATE__FUNCTIONS_CODE
+            Test functions are substituted here
+            from the input test_suit_xyz.function
+            file. C preprocessor checks are generated
+            for the build dependencies specified
+            in the input file. This script also
+            generates wrappers for the test
+            functions with code to expand the
+            string parameters read from the data
+            file.
+__MBEDTLS_TEST_TEMPLATE__EXPRESSION_CODE
+            This script enumerates the
+            expressions in the .data file and
+            generates code to handle enumerated
+            expression Ids and return the values.
+__MBEDTLS_TEST_TEMPLATE__DEP_CHECK_CODE
+            This script enumerates all
+            build dependencies and generate
+            code to handle enumerated build
+            dependency Id and return status: if
+            the dependency is defined or not.
+__MBEDTLS_TEST_TEMPLATE__DISPATCH_CODE
+            This script enumerates the functions
+            specified in the input test data file
+            and generates the initializer for the
+            function table in the template
+            file.
+__MBEDTLS_TEST_TEMPLATE__PLATFORM_CODE
+            Platform specific setup and test
+            dispatch code.
+
+"""
+
+
+import os
+import re
+import sys
+import string
+import argparse
+
+
+# Types recognized as signed integer arguments in test functions.
+SIGNED_INTEGER_TYPES = frozenset([
+    'char',
+    'short',
+    'short int',
+    'int',
+    'int8_t',
+    'int16_t',
+    'int32_t',
+    'int64_t',
+    'intmax_t',
+    'long',
+    'long int',
+    'long long int',
+    'mbedtls_mpi_sint',
+    'psa_status_t',
+])
+# Types recognized as string arguments in test functions.
+STRING_TYPES = frozenset(['char*', 'const char*', 'char const*'])
+# Types recognized as hex data arguments in test functions.
+DATA_TYPES = frozenset(['data_t*', 'const data_t*', 'data_t const*'])
+
+BEGIN_HEADER_REGEX = r'/\*\s*BEGIN_HEADER\s*\*/'
+END_HEADER_REGEX = r'/\*\s*END_HEADER\s*\*/'
+
+BEGIN_SUITE_HELPERS_REGEX = r'/\*\s*BEGIN_SUITE_HELPERS\s*\*/'
+END_SUITE_HELPERS_REGEX = r'/\*\s*END_SUITE_HELPERS\s*\*/'
+
+BEGIN_DEP_REGEX = r'BEGIN_DEPENDENCIES'
+END_DEP_REGEX = r'END_DEPENDENCIES'
+
+BEGIN_CASE_REGEX = r'/\*\s*BEGIN_CASE\s*(?P<depends_on>.*?)\s*\*/'
+END_CASE_REGEX = r'/\*\s*END_CASE\s*\*/'
+
+DEPENDENCY_REGEX = r'depends_on:(?P<dependencies>.*)'
+# This can be something like [!]MBEDTLS_xxx
+C_IDENTIFIER_REGEX = r'!?[a-z_][a-z0-9_]*'
+# This is a generic relation operator: ==, !=, >[=], <[=]
+CONDITION_OPERATOR_REGEX = r'[!=]=|[<>]=?'
+# This can be (almost) anything as long as:
+# - it starts with a number or a letter or a "("
+# - it contains only
+#       - numbers
+#       - letters
+#       - spaces
+#       - math operators, i.e "+", "-", "*", "/"
+#       - bitwise operators, i.e. "^", "|", "&", "~", "<<", ">>"
+#       - parentheses, i.e. "()"
+CONDITION_VALUE_REGEX = r'[\w|\(][\s\w\(\)\+\-\*\/\^\|\&\~\<\>]*'
+CONDITION_REGEX = r'({})(?:\s*({})\s*({}))?$'.format(C_IDENTIFIER_REGEX,
+                                                     CONDITION_OPERATOR_REGEX,
+                                                     CONDITION_VALUE_REGEX)
+# Match numerical values that start with a 0 because they can be accidentally
+# octal or accidentally decimal. Hexadecimal values starting with '0x' are
+# valid of course.
+AMBIGUOUS_INTEGER_REGEX = r'\b0[0-9]+'
+TEST_FUNCTION_VALIDATION_REGEX = r'\s*void\s+(?P<func_name>\w+)\s*\('
+FUNCTION_ARG_LIST_END_REGEX = r'.*\)'
+EXIT_LABEL_REGEX = r'^exit:'
+
+
+class GeneratorInputError(Exception):
+    """
+    Exception to indicate error in the input files to this script.
+    This includes missing patterns, test function names and other
+    parsing errors.
+    """
+    pass
+
+
+class FileWrapper:
+    """
+    This class extends the file object with attribute line_no,
+    that indicates line number for the line that is read.
+    """
+
+    def __init__(self, file_name) -> None:
+        """
+        Instantiate the file object and initialize the line number to 0.
+
+        :param file_name: File path to open.
+        """
+        # private mix-in file object
+        self._f = open(file_name, 'rb')
+        self._line_no = 0
+
+    def __iter__(self):
+        return self
+
+    def __next__(self):
+        """
+        This method makes FileWrapper iterable.
+        It counts the line numbers as each line is read.
+
+        :return: Line read from file.
+        """
+        line = self._f.__next__()
+        self._line_no += 1
+        # Convert byte array to string with correct encoding and
+        # strip any whitespaces added in the decoding process.
+        return line.decode(sys.getdefaultencoding()).rstrip()+ '\n'
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self._f.__exit__(exc_type, exc_val, exc_tb)
+
+    @property
+    def line_no(self):
+        """
+        Property that indicates line number for the line that is read.
+        """
+        return self._line_no
+
+    @property
+    def name(self):
+        """
+        Property that indicates name of the file that is read.
+        """
+        return self._f.name
+
+
+def split_dep(dep):
+    """
+    Split NOT character '!' from dependency. Used by gen_dependencies()
+
+    :param dep: Dependency list
+    :return: string tuple. Ex: ('!', MACRO) for !MACRO and ('', MACRO) for
+             MACRO.
+    """
+    return ('!', dep[1:]) if dep[0] == '!' else ('', dep)
+
+
+def gen_dependencies(dependencies):
+    """
+    Test suite data and functions specifies compile time dependencies.
+    This function generates C preprocessor code from the input
+    dependency list. Caller uses the generated preprocessor code to
+    wrap dependent code.
+    A dependency in the input list can have a leading '!' character
+    to negate a condition. '!' is separated from the dependency using
+    function split_dep() and proper preprocessor check is generated
+    accordingly.
+
+    :param dependencies: List of dependencies.
+    :return: if defined and endif code with macro annotations for
+             readability.
+    """
+    dep_start = ''.join(['#if %sdefined(%s)\n' % (x, y) for x, y in
+                         map(split_dep, dependencies)])
+    dep_end = ''.join(['#endif /* %s */\n' %
+                       x for x in reversed(dependencies)])
+
+    return dep_start, dep_end
+
+
+def gen_dependencies_one_line(dependencies):
+    """
+    Similar to gen_dependencies() but generates dependency checks in one line.
+    Useful for generating code with #else block.
+
+    :param dependencies: List of dependencies.
+    :return: Preprocessor check code
+    """
+    defines = '#if ' if dependencies else ''
+    defines += ' && '.join(['%sdefined(%s)' % (x, y) for x, y in map(
+        split_dep, dependencies)])
+    return defines
+
+
+def gen_function_wrapper(name, local_vars, args_dispatch):
+    """
+    Creates test function wrapper code. A wrapper has the code to
+    unpack parameters from parameters[] array.
+
+    :param name: Test function name
+    :param local_vars: Local variables declaration code
+    :param args_dispatch: List of dispatch arguments.
+           Ex: ['(char *) params[0]', '*((int *) params[1])']
+    :return: Test function wrapper.
+    """
+    # Then create the wrapper
+    wrapper = '''
+static void {name}_wrapper( void ** params )
+{{
+{unused_params}{locals}
+    {name}( {args} );
+}}
+'''.format(name=name,
+           unused_params='' if args_dispatch else '    (void)params;\n',
+           args=', '.join(args_dispatch),
+           locals=local_vars)
+    return wrapper
+
+
+def gen_dispatch(name, dependencies):
+    """
+    Test suite code template main_test.function defines a C function
+    array to contain test case functions. This function generates an
+    initializer entry for a function in that array. The entry is
+    composed of a compile time check for the test function
+    dependencies. At compile time the test function is assigned when
+    dependencies are met, else NULL is assigned.
+
+    :param name: Test function name
+    :param dependencies: List of dependencies
+    :return: Dispatch code.
+    """
+    if dependencies:
+        preprocessor_check = gen_dependencies_one_line(dependencies)
+        dispatch_code = '''
+{preprocessor_check}
+    {name}_wrapper,
+#else
+    NULL,
+#endif
+'''.format(preprocessor_check=preprocessor_check, name=name)
+    else:
+        dispatch_code = '''
+    {name}_wrapper,
+'''.format(name=name)
+
+    return dispatch_code
+
+
+def parse_until_pattern(funcs_f, end_regex):
+    """
+    Matches pattern end_regex to the lines read from the file object.
+    Returns the lines read until end pattern is matched.
+
+    :param funcs_f: file object for .function file
+    :param end_regex: Pattern to stop parsing
+    :return: Lines read before the end pattern
+    """
+    headers = '#line %d "%s"\n' % (funcs_f.line_no + 1, funcs_f.name)
+    for line in funcs_f:
+        if re.search(end_regex, line):
+            break
+        headers += line
+    else:
+        raise GeneratorInputError("file: %s - end pattern [%s] not found!" %
+                                  (funcs_f.name, end_regex))
+
+    return headers
+
+
+def validate_dependency(dependency):
+    """
+    Validates a C macro and raises GeneratorInputError on invalid input.
+    :param dependency: Input macro dependency
+    :return: input dependency stripped of leading & trailing white spaces.
+    """
+    dependency = dependency.strip()
+    m = re.search(AMBIGUOUS_INTEGER_REGEX, dependency)
+    if m:
+        raise GeneratorInputError('Ambiguous integer literal: '+ m.group(0))
+    if not re.match(CONDITION_REGEX, dependency, re.I):
+        raise GeneratorInputError('Invalid dependency %s' % dependency)
+    return dependency
+
+
+def parse_dependencies(inp_str):
+    """
+    Parses dependencies out of inp_str, validates them and returns a
+    list of macros.
+
+    :param inp_str: Input string with macros delimited by ':'.
+    :return: list of dependencies
+    """
+    dependencies = list(map(validate_dependency, inp_str.split(':')))
+    return dependencies
+
+
+def parse_suite_dependencies(funcs_f):
+    """
+    Parses test suite dependencies specified at the top of a
+    .function file, that starts with pattern BEGIN_DEPENDENCIES
+    and end with END_DEPENDENCIES. Dependencies are specified
+    after pattern 'depends_on:' and are delimited by ':'.
+
+    :param funcs_f: file object for .function file
+    :return: List of test suite dependencies.
+    """
+    dependencies = []
+    for line in funcs_f:
+        match = re.search(DEPENDENCY_REGEX, line.strip())
+        if match:
+            try:
+                dependencies = parse_dependencies(match.group('dependencies'))
+            except GeneratorInputError as error:
+                raise GeneratorInputError(
+                    str(error) + " - %s:%d" % (funcs_f.name, funcs_f.line_no))
+        if re.search(END_DEP_REGEX, line):
+            break
+    else:
+        raise GeneratorInputError("file: %s - end dependency pattern [%s]"
+                                  " not found!" % (funcs_f.name,
+                                                   END_DEP_REGEX))
+
+    return dependencies
+
+
+def parse_function_dependencies(line):
+    """
+    Parses function dependencies, that are in the same line as
+    comment BEGIN_CASE. Dependencies are specified after pattern
+    'depends_on:' and are delimited by ':'.
+
+    :param line: Line from .function file that has dependencies.
+    :return: List of dependencies.
+    """
+    dependencies = []
+    match = re.search(BEGIN_CASE_REGEX, line)
+    dep_str = match.group('depends_on')
+    if dep_str:
+        match = re.search(DEPENDENCY_REGEX, dep_str)
+        if match:
+            dependencies += parse_dependencies(match.group('dependencies'))
+
+    return dependencies
+
+
+ARGUMENT_DECLARATION_REGEX = re.compile(r'(.+?) ?(?:\bconst\b)? ?(\w+)\Z', re.S)
+def parse_function_argument(arg, arg_idx, args, local_vars, args_dispatch):
+    """
+    Parses one test function's argument declaration.
+
+    :param arg: argument declaration.
+    :param arg_idx: current wrapper argument index.
+    :param args: accumulator of arguments' internal types.
+    :param local_vars: accumulator of internal variable declarations.
+    :param args_dispatch: accumulator of argument usage expressions.
+    :return: the number of new wrapper arguments,
+             or None if the argument declaration is invalid.
+    """
+    # Normalize whitespace
+    arg = arg.strip()
+    arg = re.sub(r'\s*\*\s*', r'*', arg)
+    arg = re.sub(r'\s+', r' ', arg)
+    # Extract name and type
+    m = ARGUMENT_DECLARATION_REGEX.search(arg)
+    if not m:
+        # E.g. "int x[42]"
+        return None
+    typ, _ = m.groups()
+    if typ in SIGNED_INTEGER_TYPES:
+        args.append('int')
+        args_dispatch.append('((mbedtls_test_argument_t *) params[%d])->sint' % arg_idx)
+        return 1
+    if typ in STRING_TYPES:
+        args.append('char*')
+        args_dispatch.append('(char *) params[%d]' % arg_idx)
+        return 1
+    if typ in DATA_TYPES:
+        args.append('hex')
+        # create a structure
+        pointer_initializer = '(uint8_t *) params[%d]' % arg_idx
+        len_initializer = '((mbedtls_test_argument_t *) params[%d])->len' % (arg_idx+1)
+        local_vars.append('    data_t data%d = {%s, %s};\n' %
+                          (arg_idx, pointer_initializer, len_initializer))
+        args_dispatch.append('&data%d' % arg_idx)
+        return 2
+    return None
+
+ARGUMENT_LIST_REGEX = re.compile(r'\((.*?)\)', re.S)
+def parse_function_arguments(line):
+    """
+    Parses test function signature for validation and generates
+    a dispatch wrapper function that translates input test vectors
+    read from the data file into test function arguments.
+
+    :param line: Line from .function file that has a function
+                 signature.
+    :return: argument list, local variables for
+             wrapper function and argument dispatch code.
+    """
+    # Process arguments, ex: <type> arg1, <type> arg2 )
+    # This script assumes that the argument list is terminated by ')'
+    # i.e. the test functions will not have a function pointer
+    # argument.
+    m = ARGUMENT_LIST_REGEX.search(line)
+    arg_list = m.group(1).strip()
+    if arg_list in ['', 'void']:
+        return [], '', []
+    args = []
+    local_vars = []
+    args_dispatch = []
+    arg_idx = 0
+    for arg in arg_list.split(','):
+        indexes = parse_function_argument(arg, arg_idx,
+                                          args, local_vars, args_dispatch)
+        if indexes is None:
+            raise ValueError("Test function arguments can only be 'int', "
+                             "'char *' or 'data_t'\n%s" % line)
+        arg_idx += indexes
+
+    return args, ''.join(local_vars), args_dispatch
+
+
+def generate_function_code(name, code, local_vars, args_dispatch,
+                           dependencies):
+    """
+    Generate function code with preprocessor checks and parameter dispatch
+    wrapper.
+
+    :param name: Function name
+    :param code: Function code
+    :param local_vars: Local variables for function wrapper
+    :param args_dispatch: Argument dispatch code
+    :param dependencies: Preprocessor dependencies list
+    :return: Final function code
+    """
+    # Add exit label if not present
+    if code.find('exit:') == -1:
+        split_code = code.rsplit('}', 1)
+        if len(split_code) == 2:
+            code = """exit:
+    ;
+}""".join(split_code)
+
+    code += gen_function_wrapper(name, local_vars, args_dispatch)
+    preprocessor_check_start, preprocessor_check_end = \
+        gen_dependencies(dependencies)
+    return preprocessor_check_start + code + preprocessor_check_end
+
+COMMENT_START_REGEX = re.compile(r'/[*/]')
+
+def skip_comments(line, stream):
+    """Remove comments in line.
+
+    If the line contains an unfinished comment, read more lines from stream
+    until the line that contains the comment.
+
+    :return: The original line with inner comments replaced by spaces.
+             Trailing comments and whitespace may be removed completely.
+    """
+    pos = 0
+    while True:
+        opening = COMMENT_START_REGEX.search(line, pos)
+        if not opening:
+            break
+        if line[opening.start(0) + 1] == '/': # //...
+            continuation = line
+            # Count the number of line breaks, to keep line numbers aligned
+            # in the output.
+            line_count = 1
+            while continuation.endswith('\\\n'):
+                # This errors out if the file ends with an unfinished line
+                # comment. That's acceptable to not complicate the code further.
+                continuation = next(stream)
+                line_count += 1
+            return line[:opening.start(0)].rstrip() + '\n' * line_count
+        # Parsing /*...*/, looking for the end
+        closing = line.find('*/', opening.end(0))
+        while closing == -1:
+            # This errors out if the file ends with an unfinished block
+            # comment. That's acceptable to not complicate the code further.
+            line += next(stream)
+            closing = line.find('*/', opening.end(0))
+        pos = closing + 2
+        # Replace inner comment by spaces. There needs to be at least one space
+        # for things like 'int/*ihatespaces*/foo'. Go further and preserve the
+        # width of the comment and line breaks, this way positions in error
+        # messages remain correct.
+        line = (line[:opening.start(0)] +
+                re.sub(r'.', r' ', line[opening.start(0):pos]) +
+                line[pos:])
+    # Strip whitespace at the end of lines (it's irrelevant to error messages).
+    return re.sub(r' +(\n|\Z)', r'\1', line)
+
+def parse_function_code(funcs_f, dependencies, suite_dependencies):
+    """
+    Parses out a function from function file object and generates
+    function and dispatch code.
+
+    :param funcs_f: file object of the functions file.
+    :param dependencies: List of dependencies
+    :param suite_dependencies: List of test suite dependencies
+    :return: Function name, arguments, function code and dispatch code.
+    """
+    line_directive = '#line %d "%s"\n' % (funcs_f.line_no + 1, funcs_f.name)
+    code = ''
+    has_exit_label = False
+    for line in funcs_f:
+        # Check function signature. Function signature may be split
+        # across multiple lines. Here we try to find the start of
+        # arguments list, then remove '\n's and apply the regex to
+        # detect function start.
+        line = skip_comments(line, funcs_f)
+        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:
+            # 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):
+                for lin in funcs_f:
+                    line += skip_comments(lin, funcs_f)
+                    if re.search(FUNCTION_ARG_LIST_END_REGEX, line):
+                        break
+            args, local_vars, args_dispatch = parse_function_arguments(
+                line)
+            code += line
+            break
+        code += line
+    else:
+        raise GeneratorInputError("file: %s - Test functions not found!" %
+                                  funcs_f.name)
+
+    # Make the test function static
+    code = code.replace('void', 'static void', 1)
+
+    # Prefix test function name with 'test_'
+    code = code.replace(name, 'test_' + name, 1)
+    name = 'test_' + name
+
+    # If a test function has no arguments then add 'void' argument to
+    # avoid "-Wstrict-prototypes" warnings from clang
+    if len(args) == 0:
+        code = code.replace('()', '(void)', 1)
+
+    for line in funcs_f:
+        if re.search(END_CASE_REGEX, line):
+            break
+        if not has_exit_label:
+            has_exit_label = \
+                re.search(EXIT_LABEL_REGEX, line.strip()) is not None
+        code += line
+    else:
+        raise GeneratorInputError("file: %s - end case pattern [%s] not "
+                                  "found!" % (funcs_f.name, END_CASE_REGEX))
+
+    code = line_directive + code
+    code = generate_function_code(name, code, local_vars, args_dispatch,
+                                  dependencies)
+    dispatch_code = gen_dispatch(name, suite_dependencies + dependencies)
+    return (name, args, code, dispatch_code)
+
+
+def parse_functions(funcs_f):
+    """
+    Parses a test_suite_xxx.function file and returns information
+    for generating a C source file for the test suite.
+
+    :param funcs_f: file object of the functions file.
+    :return: List of test suite dependencies, test function dispatch
+             code, function code and a dict with function identifiers
+             and arguments info.
+    """
+    suite_helpers = ''
+    suite_dependencies = []
+    suite_functions = ''
+    func_info = {}
+    function_idx = 0
+    dispatch_code = ''
+    for line in funcs_f:
+        if re.search(BEGIN_HEADER_REGEX, line):
+            suite_helpers += parse_until_pattern(funcs_f, END_HEADER_REGEX)
+        elif re.search(BEGIN_SUITE_HELPERS_REGEX, line):
+            suite_helpers += parse_until_pattern(funcs_f,
+                                                 END_SUITE_HELPERS_REGEX)
+        elif re.search(BEGIN_DEP_REGEX, line):
+            suite_dependencies += parse_suite_dependencies(funcs_f)
+        elif re.search(BEGIN_CASE_REGEX, line):
+            try:
+                dependencies = parse_function_dependencies(line)
+            except GeneratorInputError as error:
+                raise GeneratorInputError(
+                    "%s:%d: %s" % (funcs_f.name, funcs_f.line_no,
+                                   str(error)))
+            func_name, args, func_code, func_dispatch =\
+                parse_function_code(funcs_f, dependencies, suite_dependencies)
+            suite_functions += func_code
+            # Generate dispatch code and enumeration info
+            if func_name in func_info:
+                raise GeneratorInputError(
+                    "file: %s - function %s re-declared at line %d" %
+                    (funcs_f.name, func_name, funcs_f.line_no))
+            func_info[func_name] = (function_idx, args)
+            dispatch_code += '/* Function Id: %d */\n' % function_idx
+            dispatch_code += func_dispatch
+            function_idx += 1
+
+    func_code = (suite_helpers +
+                 suite_functions).join(gen_dependencies(suite_dependencies))
+    return suite_dependencies, dispatch_code, func_code, func_info
+
+
+def escaped_split(inp_str, split_char):
+    """
+    Split inp_str on character split_char but ignore if escaped.
+    Since, return value is used to write back to the intermediate
+    data file, any escape characters in the input are retained in the
+    output.
+
+    :param inp_str: String to split
+    :param split_char: Split character
+    :return: List of splits
+    """
+    if len(split_char) > 1:
+        raise ValueError('Expected split character. Found string!')
+    out = re.sub(r'(\\.)|' + split_char,
+                 lambda m: m.group(1) or '\n', inp_str,
+                 len(inp_str)).split('\n')
+    out = [x for x in out if x]
+    return out
+
+
+def parse_test_data(data_f):
+    """
+    Parses .data file for each test case name, test function name,
+    test dependencies and test arguments. This information is
+    correlated with the test functions file for generating an
+    intermediate data file replacing the strings for test function
+    names, dependencies and integer constant expressions with
+    identifiers. Mainly for optimising space for on-target
+    execution.
+
+    :param data_f: file object of the data file.
+    :return: Generator that yields line number, test name, function name,
+             dependency list and function argument list.
+    """
+    __state_read_name = 0
+    __state_read_args = 1
+    state = __state_read_name
+    dependencies = []
+    name = ''
+    for line in data_f:
+        line = line.strip()
+        # Skip comments
+        if line.startswith('#'):
+            continue
+
+        # Blank line indicates end of test
+        if not line:
+            if state == __state_read_args:
+                raise GeneratorInputError("[%s:%d] Newline before arguments. "
+                                          "Test function and arguments "
+                                          "missing for %s" %
+                                          (data_f.name, data_f.line_no, name))
+            continue
+
+        if state == __state_read_name:
+            # Read test name
+            name = line
+            state = __state_read_args
+        elif state == __state_read_args:
+            # Check dependencies
+            match = re.search(DEPENDENCY_REGEX, line)
+            if match:
+                try:
+                    dependencies = parse_dependencies(
+                        match.group('dependencies'))
+                except GeneratorInputError as error:
+                    raise GeneratorInputError(
+                        str(error) + " - %s:%d" %
+                        (data_f.name, data_f.line_no))
+            else:
+                # Read test vectors
+                parts = escaped_split(line, ':')
+                test_function = parts[0]
+                args = parts[1:]
+                yield data_f.line_no, name, test_function, dependencies, args
+                dependencies = []
+                state = __state_read_name
+    if state == __state_read_args:
+        raise GeneratorInputError("[%s:%d] Newline before arguments. "
+                                  "Test function and arguments missing for "
+                                  "%s" % (data_f.name, data_f.line_no, name))
+
+
+def gen_dep_check(dep_id, dep):
+    """
+    Generate code for checking dependency with the associated
+    identifier.
+
+    :param dep_id: Dependency identifier
+    :param dep: Dependency macro
+    :return: Dependency check code
+    """
+    if dep_id < 0:
+        raise GeneratorInputError("Dependency Id should be a positive "
+                                  "integer.")
+    _not, dep = ('!', dep[1:]) if dep[0] == '!' else ('', dep)
+    if not dep:
+        raise GeneratorInputError("Dependency should not be an empty string.")
+
+    dependency = re.match(CONDITION_REGEX, dep, re.I)
+    if not dependency:
+        raise GeneratorInputError('Invalid dependency %s' % dep)
+
+    _defined = '' if dependency.group(2) else 'defined'
+    _cond = dependency.group(2) if dependency.group(2) else ''
+    _value = dependency.group(3) if dependency.group(3) else ''
+
+    dep_check = '''
+        case {id}:
+            {{
+#if {_not}{_defined}({macro}{_cond}{_value})
+                ret = DEPENDENCY_SUPPORTED;
+#else
+                ret = DEPENDENCY_NOT_SUPPORTED;
+#endif
+            }}
+            break;'''.format(_not=_not, _defined=_defined,
+                             macro=dependency.group(1), id=dep_id,
+                             _cond=_cond, _value=_value)
+    return dep_check
+
+
+def gen_expression_check(exp_id, exp):
+    """
+    Generates code for evaluating an integer expression using
+    associated expression Id.
+
+    :param exp_id: Expression Identifier
+    :param exp: Expression/Macro
+    :return: Expression check code
+    """
+    if exp_id < 0:
+        raise GeneratorInputError("Expression Id should be a positive "
+                                  "integer.")
+    if not exp:
+        raise GeneratorInputError("Expression should not be an empty string.")
+    exp_code = '''
+        case {exp_id}:
+            {{
+                *out_value = {expression};
+            }}
+            break;'''.format(exp_id=exp_id, expression=exp)
+    return exp_code
+
+
+def write_dependencies(out_data_f, test_dependencies, unique_dependencies):
+    """
+    Write dependencies to intermediate test data file, replacing
+    the string form with identifiers. Also, generates dependency
+    check code.
+
+    :param out_data_f: Output intermediate data file
+    :param test_dependencies: Dependencies
+    :param unique_dependencies: Mutable list to track unique dependencies
+           that are global to this re-entrant function.
+    :return: returns dependency check code.
+    """
+    dep_check_code = ''
+    if test_dependencies:
+        out_data_f.write('depends_on')
+        for dep in test_dependencies:
+            if dep not in unique_dependencies:
+                unique_dependencies.append(dep)
+                dep_id = unique_dependencies.index(dep)
+                dep_check_code += gen_dep_check(dep_id, dep)
+            else:
+                dep_id = unique_dependencies.index(dep)
+            out_data_f.write(':' + str(dep_id))
+        out_data_f.write('\n')
+    return dep_check_code
+
+
+INT_VAL_REGEX = re.compile(r'-?(\d+|0x[0-9a-f]+)$', re.I)
+def val_is_int(val: str) -> bool:
+    """Whether val is suitable as an 'int' parameter in the .datax file."""
+    if not INT_VAL_REGEX.match(val):
+        return False
+    # Limit the range to what is guaranteed to get through strtol()
+    return abs(int(val, 0)) <= 0x7fffffff
+
+def write_parameters(out_data_f, test_args, func_args, unique_expressions):
+    """
+    Writes test parameters to the intermediate data file, replacing
+    the string form with identifiers. Also, generates expression
+    check code.
+
+    :param out_data_f: Output intermediate data file
+    :param test_args: Test parameters
+    :param func_args: Function arguments
+    :param unique_expressions: Mutable list to track unique
+           expressions that are global to this re-entrant function.
+    :return: Returns expression check code.
+    """
+    expression_code = ''
+    for i, _ in enumerate(test_args):
+        typ = func_args[i]
+        val = test_args[i]
+
+        # Pass small integer constants literally. This reduces the size of
+        # the C code. Register anything else as an expression.
+        if typ == 'int' and not val_is_int(val):
+            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')
+    return expression_code
+
+
+def gen_suite_dep_checks(suite_dependencies, dep_check_code, expression_code):
+    """
+    Generates preprocessor checks for test suite dependencies.
+
+    :param suite_dependencies: Test suite dependencies read from the
+            .function file.
+    :param dep_check_code: Dependency check code
+    :param expression_code: Expression check code
+    :return: Dependency and expression code guarded by test suite
+             dependencies.
+    """
+    if suite_dependencies:
+        preprocessor_check = gen_dependencies_one_line(suite_dependencies)
+        dep_check_code = '''
+{preprocessor_check}
+{code}
+#endif
+'''.format(preprocessor_check=preprocessor_check, code=dep_check_code)
+        expression_code = '''
+{preprocessor_check}
+{code}
+#endif
+'''.format(preprocessor_check=preprocessor_check, code=expression_code)
+    return dep_check_code, expression_code
+
+
+def get_function_info(func_info, function_name, line_no):
+    """Look up information about a test function by name.
+
+    Raise an informative expression if function_name is not found.
+
+    :param func_info: dictionary mapping function names to their information.
+    :param function_name: the function name as written in the .function and
+                          .data files.
+    :param line_no: line number for error messages.
+    :return Function information (id, args).
+    """
+    test_function_name = 'test_' + function_name
+    if test_function_name not in func_info:
+        raise GeneratorInputError("%d: Function %s not found!" %
+                                  (line_no, test_function_name))
+    return func_info[test_function_name]
+
+
+def gen_from_test_data(data_f, out_data_f, func_info, suite_dependencies):
+    """
+    This function reads test case name, dependencies and test vectors
+    from the .data file. This information is correlated with the test
+    functions file for generating an intermediate data file replacing
+    the strings for test function names, dependencies and integer
+    constant expressions with identifiers. Mainly for optimising
+    space for on-target execution.
+    It also generates test case dependency check code and expression
+    evaluation code.
+
+    :param data_f: Data file object
+    :param out_data_f: Output intermediate data file
+    :param func_info: Dict keyed by function and with function id
+           and arguments info
+    :param suite_dependencies: Test suite dependencies
+    :return: Returns dependency and expression check code
+    """
+    unique_dependencies = []
+    unique_expressions = []
+    dep_check_code = ''
+    expression_code = ''
+    for line_no, test_name, function_name, test_dependencies, test_args in \
+            parse_test_data(data_f):
+        out_data_f.write(test_name + '\n')
+
+        # Write dependencies
+        dep_check_code += write_dependencies(out_data_f, test_dependencies,
+                                             unique_dependencies)
+
+        # Write test function name
+        func_id, func_args = \
+            get_function_info(func_info, function_name, line_no)
+        out_data_f.write(str(func_id))
+
+        # Write parameters
+        if len(test_args) != len(func_args):
+            raise GeneratorInputError("%d: Invalid number of arguments in test "
+                                      "%s. See function %s signature." %
+                                      (line_no, test_name, function_name))
+        expression_code += write_parameters(out_data_f, test_args, func_args,
+                                            unique_expressions)
+
+        # Write a newline as test case separator
+        out_data_f.write('\n')
+
+    dep_check_code, expression_code = gen_suite_dep_checks(
+        suite_dependencies, dep_check_code, expression_code)
+    return dep_check_code, expression_code
+
+
+def add_input_info(funcs_file, data_file, template_file,
+                   c_file, snippets):
+    """
+    Add generator input info in snippets.
+
+    :param funcs_file: Functions file object
+    :param data_file: Data file object
+    :param template_file: Template file object
+    :param c_file: Output C file object
+    :param snippets: Dictionary to contain code pieces to be
+                     substituted in the template.
+    :return:
+    """
+    snippets['test_file'] = c_file
+    snippets['test_main_file'] = template_file
+    snippets['test_case_file'] = funcs_file
+    snippets['test_case_data_file'] = data_file
+
+
+def read_code_from_input_files(platform_file, helpers_file,
+                               out_data_file, snippets):
+    """
+    Read code from input files and create substitutions for replacement
+    strings in the template file.
+
+    :param platform_file: Platform file object
+    :param helpers_file: Helper functions file object
+    :param out_data_file: Output intermediate data file object
+    :param snippets: Dictionary to contain code pieces to be
+                     substituted in the template.
+    :return:
+    """
+    # Read helpers
+    with open(helpers_file, 'r') as help_f, open(platform_file, 'r') as \
+            platform_f:
+        snippets['test_common_helper_file'] = helpers_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 '\'
+
+
+def write_test_source_file(template_file, c_file, snippets):
+    """
+    Write output source file with generated source code.
+
+    :param template_file: Template file name
+    :param c_file: Output source file
+    :param snippets: Generated and code snippets
+    :return:
+    """
+
+    # Create a placeholder pattern with the correct named capture groups
+    # to override the default provided with Template.
+    # Match nothing (no way of escaping placeholders).
+    escaped = "(?P<escaped>(?!))"
+    # Match the "__MBEDTLS_TEST_TEMPLATE__PLACEHOLDER_NAME" pattern.
+    named = "__MBEDTLS_TEST_TEMPLATE__(?P<named>[A-Z][_A-Z0-9]*)"
+    # Match nothing (no braced placeholder syntax).
+    braced = "(?P<braced>(?!))"
+    # If not already matched, a "__MBEDTLS_TEST_TEMPLATE__" prefix is invalid.
+    invalid = "(?P<invalid>__MBEDTLS_TEST_TEMPLATE__)"
+    placeholder_pattern = re.compile("|".join([escaped, named, braced, invalid]))
+
+    with open(template_file, 'r') as template_f, open(c_file, 'w') as c_f:
+        for line_no, line in enumerate(template_f.readlines(), 1):
+            # Update line number. +1 as #line directive sets next line number
+            snippets['line_no'] = line_no + 1
+            template = string.Template(line)
+            template.pattern = placeholder_pattern
+            snippets = {k.upper():v for (k, v) in snippets.items()}
+            code = template.substitute(**snippets)
+            c_f.write(code)
+
+
+def parse_function_file(funcs_file, snippets):
+    """
+    Parse function file and generate function dispatch code.
+
+    :param funcs_file: Functions file name
+    :param snippets: Dictionary to contain code pieces to be
+                     substituted in the template.
+    :return:
+    """
+    with FileWrapper(funcs_file) as funcs_f:
+        suite_dependencies, dispatch_code, func_code, func_info = \
+            parse_functions(funcs_f)
+        snippets['functions_code'] = func_code
+        snippets['dispatch_code'] = dispatch_code
+        return suite_dependencies, func_info
+
+
+def generate_intermediate_data_file(data_file, out_data_file,
+                                    suite_dependencies, func_info, snippets):
+    """
+    Generates intermediate data file from input data file and
+    information read from functions file.
+
+    :param data_file: Data file name
+    :param out_data_file: Output/Intermediate data file
+    :param suite_dependencies: List of suite dependencies.
+    :param func_info: Function info parsed from functions file.
+    :param snippets: Dictionary to contain code pieces to be
+                     substituted in the template.
+    :return:
+    """
+    with FileWrapper(data_file) as data_f, \
+            open(out_data_file, 'w') as out_data_f:
+        dep_check_code, expression_code = gen_from_test_data(
+            data_f, out_data_f, func_info, suite_dependencies)
+        snippets['dep_check_code'] = dep_check_code
+        snippets['expression_code'] = expression_code
+
+
+def generate_code(**input_info):
+    """
+    Generates C source code from test suite file, data file, common
+    helpers file and platform file.
+
+    input_info expands to following parameters:
+    funcs_file: Functions file object
+    data_file: Data file object
+    template_file: Template file object
+    platform_file: Platform file object
+    helpers_file: Helper functions file object
+    suites_dir: Test suites dir
+    c_file: Output C file object
+    out_data_file: Output intermediate data file object
+    :return:
+    """
+    funcs_file = input_info['funcs_file']
+    data_file = input_info['data_file']
+    template_file = input_info['template_file']
+    platform_file = input_info['platform_file']
+    helpers_file = input_info['helpers_file']
+    suites_dir = input_info['suites_dir']
+    c_file = input_info['c_file']
+    out_data_file = input_info['out_data_file']
+    for name, path in [('Functions file', funcs_file),
+                       ('Data file', data_file),
+                       ('Template file', template_file),
+                       ('Platform file', platform_file),
+                       ('Helpers code file', helpers_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_code_from_input_files(platform_file, helpers_file,
+                               out_data_file, snippets)
+    add_input_info(funcs_file, data_file, template_file,
+                   c_file, snippets)
+    suite_dependencies, func_info = parse_function_file(funcs_file, snippets)
+    generate_intermediate_data_file(data_file, out_data_file,
+                                    suite_dependencies, func_info, snippets)
+    write_test_source_file(template_file, c_file, snippets)
+
+
+def main():
+    """
+    Command line parser.
+
+    :return:
+    """
+    parser = argparse.ArgumentParser(
+        description='Dynamically generate test suite code.')
+
+    parser.add_argument("-f", "--functions-file",
+                        dest="funcs_file",
+                        help="Functions file",
+                        metavar="FUNCTIONS_FILE",
+                        required=True)
+
+    parser.add_argument("-d", "--data-file",
+                        dest="data_file",
+                        help="Data file",
+                        metavar="DATA_FILE",
+                        required=True)
+
+    parser.add_argument("-t", "--template-file",
+                        dest="template_file",
+                        help="Template file",
+                        metavar="TEMPLATE_FILE",
+                        required=True)
+
+    parser.add_argument("-s", "--suites-dir",
+                        dest="suites_dir",
+                        help="Suites dir",
+                        metavar="SUITES_DIR",
+                        required=True)
+
+    parser.add_argument("--helpers-file",
+                        dest="helpers_file",
+                        help="Helpers file",
+                        metavar="HELPERS_FILE",
+                        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_name + '.datax')
+
+    out_c_file_dir = os.path.dirname(out_c_file)
+    out_data_file_dir = os.path.dirname(out_data_file)
+    for directory in [out_c_file_dir, out_data_file_dir]:
+        if not os.path.exists(directory):
+            os.makedirs(directory)
+
+    generate_code(funcs_file=args.funcs_file, data_file=args.data_file,
+                  template_file=args.template_file,
+                  platform_file=args.platform_file,
+                  helpers_file=args.helpers_file, suites_dir=args.suites_dir,
+                  c_file=out_c_file, out_data_file=out_data_file)
+
+
+if __name__ == "__main__":
+    try:
+        main()
+    except GeneratorInputError as err:
+        sys.exit("%s: input error: %s" %
+                 (os.path.basename(sys.argv[0]), str(err)))