1*62c56f98SSadaf Ebrahimi#!/usr/bin/env python3 2*62c56f98SSadaf Ebrahimi# Test suites code generator. 3*62c56f98SSadaf Ebrahimi# 4*62c56f98SSadaf Ebrahimi# Copyright The Mbed TLS Contributors 5*62c56f98SSadaf Ebrahimi# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later 6*62c56f98SSadaf Ebrahimi 7*62c56f98SSadaf Ebrahimi""" 8*62c56f98SSadaf EbrahimiThis script is a key part of Mbed TLS test suites framework. For 9*62c56f98SSadaf Ebrahimiunderstanding the script it is important to understand the 10*62c56f98SSadaf Ebrahimiframework. This doc string contains a summary of the framework 11*62c56f98SSadaf Ebrahimiand explains the function of this script. 12*62c56f98SSadaf Ebrahimi 13*62c56f98SSadaf EbrahimiMbed TLS test suites: 14*62c56f98SSadaf Ebrahimi===================== 15*62c56f98SSadaf EbrahimiScope: 16*62c56f98SSadaf Ebrahimi------ 17*62c56f98SSadaf EbrahimiThe test suites focus on unit testing the crypto primitives and also 18*62c56f98SSadaf Ebrahimiinclude x509 parser tests. Tests can be added to test any Mbed TLS 19*62c56f98SSadaf Ebrahimimodule. However, the framework is not capable of testing SSL 20*62c56f98SSadaf Ebrahimiprotocol, since that requires full stack execution and that is best 21*62c56f98SSadaf Ebrahimitested as part of the system test. 22*62c56f98SSadaf Ebrahimi 23*62c56f98SSadaf EbrahimiTest case definition: 24*62c56f98SSadaf Ebrahimi--------------------- 25*62c56f98SSadaf EbrahimiTests are defined in a test_suite_<module>[.<optional sub module>].data 26*62c56f98SSadaf Ebrahimifile. A test definition contains: 27*62c56f98SSadaf Ebrahimi test name 28*62c56f98SSadaf Ebrahimi optional build macro dependencies 29*62c56f98SSadaf Ebrahimi test function 30*62c56f98SSadaf Ebrahimi test parameters 31*62c56f98SSadaf Ebrahimi 32*62c56f98SSadaf EbrahimiTest dependencies are build macros that can be specified to indicate 33*62c56f98SSadaf Ebrahimithe build config in which the test is valid. For example if a test 34*62c56f98SSadaf Ebrahimidepends on a feature that is only enabled by defining a macro. Then 35*62c56f98SSadaf Ebrahimithat macro should be specified as a dependency of the test. 36*62c56f98SSadaf Ebrahimi 37*62c56f98SSadaf EbrahimiTest function is the function that implements the test steps. This 38*62c56f98SSadaf Ebrahimifunction is specified for different tests that perform same steps 39*62c56f98SSadaf Ebrahimiwith different parameters. 40*62c56f98SSadaf Ebrahimi 41*62c56f98SSadaf EbrahimiTest parameters are specified in string form separated by ':'. 42*62c56f98SSadaf EbrahimiParameters can be of type string, binary data specified as hex 43*62c56f98SSadaf Ebrahimistring and integer constants specified as integer, macro or 44*62c56f98SSadaf Ebrahimias an expression. Following is an example test definition: 45*62c56f98SSadaf Ebrahimi 46*62c56f98SSadaf Ebrahimi AES 128 GCM Encrypt and decrypt 8 bytes 47*62c56f98SSadaf Ebrahimi depends_on:MBEDTLS_AES_C:MBEDTLS_GCM_C 48*62c56f98SSadaf Ebrahimi enc_dec_buf:MBEDTLS_CIPHER_AES_128_GCM:"AES-128-GCM":128:8:-1 49*62c56f98SSadaf Ebrahimi 50*62c56f98SSadaf EbrahimiTest functions: 51*62c56f98SSadaf Ebrahimi--------------- 52*62c56f98SSadaf EbrahimiTest functions are coded in C in test_suite_<module>.function files. 53*62c56f98SSadaf EbrahimiFunctions file is itself not compilable and contains special 54*62c56f98SSadaf Ebrahimiformat patterns to specify test suite dependencies, start and end 55*62c56f98SSadaf Ebrahimiof functions and function dependencies. Check any existing functions 56*62c56f98SSadaf Ebrahimifile for example. 57*62c56f98SSadaf Ebrahimi 58*62c56f98SSadaf EbrahimiExecution: 59*62c56f98SSadaf Ebrahimi---------- 60*62c56f98SSadaf EbrahimiTests are executed in 3 steps: 61*62c56f98SSadaf Ebrahimi- Generating test_suite_<module>[.<optional sub module>].c file 62*62c56f98SSadaf Ebrahimi for each corresponding .data file. 63*62c56f98SSadaf Ebrahimi- Building each source file into executables. 64*62c56f98SSadaf Ebrahimi- Running each executable and printing report. 65*62c56f98SSadaf Ebrahimi 66*62c56f98SSadaf EbrahimiGenerating C test source requires more than just the test functions. 67*62c56f98SSadaf EbrahimiFollowing extras are required: 68*62c56f98SSadaf Ebrahimi- Process main() 69*62c56f98SSadaf Ebrahimi- Reading .data file and dispatching test cases. 70*62c56f98SSadaf Ebrahimi- Platform specific test case execution 71*62c56f98SSadaf Ebrahimi- Dependency checking 72*62c56f98SSadaf Ebrahimi- Integer expression evaluation 73*62c56f98SSadaf Ebrahimi- Test function dispatch 74*62c56f98SSadaf Ebrahimi 75*62c56f98SSadaf EbrahimiBuild dependencies and integer expressions (in the test parameters) 76*62c56f98SSadaf Ebrahimiare specified as strings in the .data file. Their run time value is 77*62c56f98SSadaf Ebrahiminot known at the generation stage. Hence, they need to be translated 78*62c56f98SSadaf Ebrahimiinto run time evaluations. This script generates the run time checks 79*62c56f98SSadaf Ebrahimifor dependencies and integer expressions. 80*62c56f98SSadaf Ebrahimi 81*62c56f98SSadaf EbrahimiSimilarly, function names have to be translated into function calls. 82*62c56f98SSadaf EbrahimiThis script also generates code for function dispatch. 83*62c56f98SSadaf Ebrahimi 84*62c56f98SSadaf EbrahimiThe extra code mentioned here is either generated by this script 85*62c56f98SSadaf Ebrahimior it comes from the input files: helpers file, platform file and 86*62c56f98SSadaf Ebrahimithe template file. 87*62c56f98SSadaf Ebrahimi 88*62c56f98SSadaf EbrahimiHelper file: 89*62c56f98SSadaf Ebrahimi------------ 90*62c56f98SSadaf EbrahimiHelpers file contains common helper/utility functions and data. 91*62c56f98SSadaf Ebrahimi 92*62c56f98SSadaf EbrahimiPlatform file: 93*62c56f98SSadaf Ebrahimi-------------- 94*62c56f98SSadaf EbrahimiPlatform file contains platform specific setup code and test case 95*62c56f98SSadaf Ebrahimidispatch code. For example, host_test.function reads test data 96*62c56f98SSadaf Ebrahimifile from host's file system and dispatches tests. 97*62c56f98SSadaf Ebrahimi 98*62c56f98SSadaf EbrahimiTemplate file: 99*62c56f98SSadaf Ebrahimi--------- 100*62c56f98SSadaf EbrahimiTemplate file for example main_test.function is a template C file in 101*62c56f98SSadaf Ebrahimiwhich generated code and code from input files is substituted to 102*62c56f98SSadaf Ebrahimigenerate a compilable C file. It also contains skeleton functions for 103*62c56f98SSadaf Ebrahimidependency checks, expression evaluation and function dispatch. These 104*62c56f98SSadaf Ebrahimifunctions are populated with checks and return codes by this script. 105*62c56f98SSadaf Ebrahimi 106*62c56f98SSadaf EbrahimiTemplate file contains "replacement" fields that are formatted 107*62c56f98SSadaf Ebrahimistrings processed by Python string.Template.substitute() method. 108*62c56f98SSadaf Ebrahimi 109*62c56f98SSadaf EbrahimiThis script: 110*62c56f98SSadaf Ebrahimi============ 111*62c56f98SSadaf EbrahimiCore function of this script is to fill the template file with 112*62c56f98SSadaf Ebrahimicode that is generated or read from helpers and platform files. 113*62c56f98SSadaf Ebrahimi 114*62c56f98SSadaf EbrahimiThis script replaces following fields in the template and generates 115*62c56f98SSadaf Ebrahimithe test source file: 116*62c56f98SSadaf Ebrahimi 117*62c56f98SSadaf Ebrahimi__MBEDTLS_TEST_TEMPLATE__TEST_COMMON_HELPERS 118*62c56f98SSadaf Ebrahimi All common code from helpers.function 119*62c56f98SSadaf Ebrahimi is substituted here. 120*62c56f98SSadaf Ebrahimi__MBEDTLS_TEST_TEMPLATE__FUNCTIONS_CODE 121*62c56f98SSadaf Ebrahimi Test functions are substituted here 122*62c56f98SSadaf Ebrahimi from the input test_suit_xyz.function 123*62c56f98SSadaf Ebrahimi file. C preprocessor checks are generated 124*62c56f98SSadaf Ebrahimi for the build dependencies specified 125*62c56f98SSadaf Ebrahimi in the input file. This script also 126*62c56f98SSadaf Ebrahimi generates wrappers for the test 127*62c56f98SSadaf Ebrahimi functions with code to expand the 128*62c56f98SSadaf Ebrahimi string parameters read from the data 129*62c56f98SSadaf Ebrahimi file. 130*62c56f98SSadaf Ebrahimi__MBEDTLS_TEST_TEMPLATE__EXPRESSION_CODE 131*62c56f98SSadaf Ebrahimi This script enumerates the 132*62c56f98SSadaf Ebrahimi expressions in the .data file and 133*62c56f98SSadaf Ebrahimi generates code to handle enumerated 134*62c56f98SSadaf Ebrahimi expression Ids and return the values. 135*62c56f98SSadaf Ebrahimi__MBEDTLS_TEST_TEMPLATE__DEP_CHECK_CODE 136*62c56f98SSadaf Ebrahimi This script enumerates all 137*62c56f98SSadaf Ebrahimi build dependencies and generate 138*62c56f98SSadaf Ebrahimi code to handle enumerated build 139*62c56f98SSadaf Ebrahimi dependency Id and return status: if 140*62c56f98SSadaf Ebrahimi the dependency is defined or not. 141*62c56f98SSadaf Ebrahimi__MBEDTLS_TEST_TEMPLATE__DISPATCH_CODE 142*62c56f98SSadaf Ebrahimi This script enumerates the functions 143*62c56f98SSadaf Ebrahimi specified in the input test data file 144*62c56f98SSadaf Ebrahimi and generates the initializer for the 145*62c56f98SSadaf Ebrahimi function table in the template 146*62c56f98SSadaf Ebrahimi file. 147*62c56f98SSadaf Ebrahimi__MBEDTLS_TEST_TEMPLATE__PLATFORM_CODE 148*62c56f98SSadaf Ebrahimi Platform specific setup and test 149*62c56f98SSadaf Ebrahimi dispatch code. 150*62c56f98SSadaf Ebrahimi 151*62c56f98SSadaf Ebrahimi""" 152*62c56f98SSadaf Ebrahimi 153*62c56f98SSadaf Ebrahimi 154*62c56f98SSadaf Ebrahimiimport os 155*62c56f98SSadaf Ebrahimiimport re 156*62c56f98SSadaf Ebrahimiimport sys 157*62c56f98SSadaf Ebrahimiimport string 158*62c56f98SSadaf Ebrahimiimport argparse 159*62c56f98SSadaf Ebrahimi 160*62c56f98SSadaf Ebrahimi 161*62c56f98SSadaf Ebrahimi# Types recognized as signed integer arguments in test functions. 162*62c56f98SSadaf EbrahimiSIGNED_INTEGER_TYPES = frozenset([ 163*62c56f98SSadaf Ebrahimi 'char', 164*62c56f98SSadaf Ebrahimi 'short', 165*62c56f98SSadaf Ebrahimi 'short int', 166*62c56f98SSadaf Ebrahimi 'int', 167*62c56f98SSadaf Ebrahimi 'int8_t', 168*62c56f98SSadaf Ebrahimi 'int16_t', 169*62c56f98SSadaf Ebrahimi 'int32_t', 170*62c56f98SSadaf Ebrahimi 'int64_t', 171*62c56f98SSadaf Ebrahimi 'intmax_t', 172*62c56f98SSadaf Ebrahimi 'long', 173*62c56f98SSadaf Ebrahimi 'long int', 174*62c56f98SSadaf Ebrahimi 'long long int', 175*62c56f98SSadaf Ebrahimi 'mbedtls_mpi_sint', 176*62c56f98SSadaf Ebrahimi 'psa_status_t', 177*62c56f98SSadaf Ebrahimi]) 178*62c56f98SSadaf Ebrahimi# Types recognized as string arguments in test functions. 179*62c56f98SSadaf EbrahimiSTRING_TYPES = frozenset(['char*', 'const char*', 'char const*']) 180*62c56f98SSadaf Ebrahimi# Types recognized as hex data arguments in test functions. 181*62c56f98SSadaf EbrahimiDATA_TYPES = frozenset(['data_t*', 'const data_t*', 'data_t const*']) 182*62c56f98SSadaf Ebrahimi 183*62c56f98SSadaf EbrahimiBEGIN_HEADER_REGEX = r'/\*\s*BEGIN_HEADER\s*\*/' 184*62c56f98SSadaf EbrahimiEND_HEADER_REGEX = r'/\*\s*END_HEADER\s*\*/' 185*62c56f98SSadaf Ebrahimi 186*62c56f98SSadaf EbrahimiBEGIN_SUITE_HELPERS_REGEX = r'/\*\s*BEGIN_SUITE_HELPERS\s*\*/' 187*62c56f98SSadaf EbrahimiEND_SUITE_HELPERS_REGEX = r'/\*\s*END_SUITE_HELPERS\s*\*/' 188*62c56f98SSadaf Ebrahimi 189*62c56f98SSadaf EbrahimiBEGIN_DEP_REGEX = r'BEGIN_DEPENDENCIES' 190*62c56f98SSadaf EbrahimiEND_DEP_REGEX = r'END_DEPENDENCIES' 191*62c56f98SSadaf Ebrahimi 192*62c56f98SSadaf EbrahimiBEGIN_CASE_REGEX = r'/\*\s*BEGIN_CASE\s*(?P<depends_on>.*?)\s*\*/' 193*62c56f98SSadaf EbrahimiEND_CASE_REGEX = r'/\*\s*END_CASE\s*\*/' 194*62c56f98SSadaf Ebrahimi 195*62c56f98SSadaf EbrahimiDEPENDENCY_REGEX = r'depends_on:(?P<dependencies>.*)' 196*62c56f98SSadaf EbrahimiC_IDENTIFIER_REGEX = r'!?[a-z_][a-z0-9_]*' 197*62c56f98SSadaf EbrahimiCONDITION_OPERATOR_REGEX = r'[!=]=|[<>]=?' 198*62c56f98SSadaf Ebrahimi# forbid 0ddd which might be accidentally octal or accidentally decimal 199*62c56f98SSadaf EbrahimiCONDITION_VALUE_REGEX = r'[-+]?(0x[0-9a-f]+|0|[1-9][0-9]*)' 200*62c56f98SSadaf EbrahimiCONDITION_REGEX = r'({})(?:\s*({})\s*({}))?$'.format(C_IDENTIFIER_REGEX, 201*62c56f98SSadaf Ebrahimi CONDITION_OPERATOR_REGEX, 202*62c56f98SSadaf Ebrahimi CONDITION_VALUE_REGEX) 203*62c56f98SSadaf EbrahimiTEST_FUNCTION_VALIDATION_REGEX = r'\s*void\s+(?P<func_name>\w+)\s*\(' 204*62c56f98SSadaf EbrahimiFUNCTION_ARG_LIST_END_REGEX = r'.*\)' 205*62c56f98SSadaf EbrahimiEXIT_LABEL_REGEX = r'^exit:' 206*62c56f98SSadaf Ebrahimi 207*62c56f98SSadaf Ebrahimi 208*62c56f98SSadaf Ebrahimiclass GeneratorInputError(Exception): 209*62c56f98SSadaf Ebrahimi """ 210*62c56f98SSadaf Ebrahimi Exception to indicate error in the input files to this script. 211*62c56f98SSadaf Ebrahimi This includes missing patterns, test function names and other 212*62c56f98SSadaf Ebrahimi parsing errors. 213*62c56f98SSadaf Ebrahimi """ 214*62c56f98SSadaf Ebrahimi pass 215*62c56f98SSadaf Ebrahimi 216*62c56f98SSadaf Ebrahimi 217*62c56f98SSadaf Ebrahimiclass FileWrapper: 218*62c56f98SSadaf Ebrahimi """ 219*62c56f98SSadaf Ebrahimi This class extends the file object with attribute line_no, 220*62c56f98SSadaf Ebrahimi that indicates line number for the line that is read. 221*62c56f98SSadaf Ebrahimi """ 222*62c56f98SSadaf Ebrahimi 223*62c56f98SSadaf Ebrahimi def __init__(self, file_name) -> None: 224*62c56f98SSadaf Ebrahimi """ 225*62c56f98SSadaf Ebrahimi Instantiate the file object and initialize the line number to 0. 226*62c56f98SSadaf Ebrahimi 227*62c56f98SSadaf Ebrahimi :param file_name: File path to open. 228*62c56f98SSadaf Ebrahimi """ 229*62c56f98SSadaf Ebrahimi # private mix-in file object 230*62c56f98SSadaf Ebrahimi self._f = open(file_name, 'rb') 231*62c56f98SSadaf Ebrahimi self._line_no = 0 232*62c56f98SSadaf Ebrahimi 233*62c56f98SSadaf Ebrahimi def __iter__(self): 234*62c56f98SSadaf Ebrahimi return self 235*62c56f98SSadaf Ebrahimi 236*62c56f98SSadaf Ebrahimi def __next__(self): 237*62c56f98SSadaf Ebrahimi """ 238*62c56f98SSadaf Ebrahimi This method makes FileWrapper iterable. 239*62c56f98SSadaf Ebrahimi It counts the line numbers as each line is read. 240*62c56f98SSadaf Ebrahimi 241*62c56f98SSadaf Ebrahimi :return: Line read from file. 242*62c56f98SSadaf Ebrahimi """ 243*62c56f98SSadaf Ebrahimi line = self._f.__next__() 244*62c56f98SSadaf Ebrahimi self._line_no += 1 245*62c56f98SSadaf Ebrahimi # Convert byte array to string with correct encoding and 246*62c56f98SSadaf Ebrahimi # strip any whitespaces added in the decoding process. 247*62c56f98SSadaf Ebrahimi return line.decode(sys.getdefaultencoding()).rstrip()+ '\n' 248*62c56f98SSadaf Ebrahimi 249*62c56f98SSadaf Ebrahimi def __enter__(self): 250*62c56f98SSadaf Ebrahimi return self 251*62c56f98SSadaf Ebrahimi 252*62c56f98SSadaf Ebrahimi def __exit__(self, exc_type, exc_val, exc_tb): 253*62c56f98SSadaf Ebrahimi self._f.__exit__(exc_type, exc_val, exc_tb) 254*62c56f98SSadaf Ebrahimi 255*62c56f98SSadaf Ebrahimi @property 256*62c56f98SSadaf Ebrahimi def line_no(self): 257*62c56f98SSadaf Ebrahimi """ 258*62c56f98SSadaf Ebrahimi Property that indicates line number for the line that is read. 259*62c56f98SSadaf Ebrahimi """ 260*62c56f98SSadaf Ebrahimi return self._line_no 261*62c56f98SSadaf Ebrahimi 262*62c56f98SSadaf Ebrahimi @property 263*62c56f98SSadaf Ebrahimi def name(self): 264*62c56f98SSadaf Ebrahimi """ 265*62c56f98SSadaf Ebrahimi Property that indicates name of the file that is read. 266*62c56f98SSadaf Ebrahimi """ 267*62c56f98SSadaf Ebrahimi return self._f.name 268*62c56f98SSadaf Ebrahimi 269*62c56f98SSadaf Ebrahimi 270*62c56f98SSadaf Ebrahimidef split_dep(dep): 271*62c56f98SSadaf Ebrahimi """ 272*62c56f98SSadaf Ebrahimi Split NOT character '!' from dependency. Used by gen_dependencies() 273*62c56f98SSadaf Ebrahimi 274*62c56f98SSadaf Ebrahimi :param dep: Dependency list 275*62c56f98SSadaf Ebrahimi :return: string tuple. Ex: ('!', MACRO) for !MACRO and ('', MACRO) for 276*62c56f98SSadaf Ebrahimi MACRO. 277*62c56f98SSadaf Ebrahimi """ 278*62c56f98SSadaf Ebrahimi return ('!', dep[1:]) if dep[0] == '!' else ('', dep) 279*62c56f98SSadaf Ebrahimi 280*62c56f98SSadaf Ebrahimi 281*62c56f98SSadaf Ebrahimidef gen_dependencies(dependencies): 282*62c56f98SSadaf Ebrahimi """ 283*62c56f98SSadaf Ebrahimi Test suite data and functions specifies compile time dependencies. 284*62c56f98SSadaf Ebrahimi This function generates C preprocessor code from the input 285*62c56f98SSadaf Ebrahimi dependency list. Caller uses the generated preprocessor code to 286*62c56f98SSadaf Ebrahimi wrap dependent code. 287*62c56f98SSadaf Ebrahimi A dependency in the input list can have a leading '!' character 288*62c56f98SSadaf Ebrahimi to negate a condition. '!' is separated from the dependency using 289*62c56f98SSadaf Ebrahimi function split_dep() and proper preprocessor check is generated 290*62c56f98SSadaf Ebrahimi accordingly. 291*62c56f98SSadaf Ebrahimi 292*62c56f98SSadaf Ebrahimi :param dependencies: List of dependencies. 293*62c56f98SSadaf Ebrahimi :return: if defined and endif code with macro annotations for 294*62c56f98SSadaf Ebrahimi readability. 295*62c56f98SSadaf Ebrahimi """ 296*62c56f98SSadaf Ebrahimi dep_start = ''.join(['#if %sdefined(%s)\n' % (x, y) for x, y in 297*62c56f98SSadaf Ebrahimi map(split_dep, dependencies)]) 298*62c56f98SSadaf Ebrahimi dep_end = ''.join(['#endif /* %s */\n' % 299*62c56f98SSadaf Ebrahimi x for x in reversed(dependencies)]) 300*62c56f98SSadaf Ebrahimi 301*62c56f98SSadaf Ebrahimi return dep_start, dep_end 302*62c56f98SSadaf Ebrahimi 303*62c56f98SSadaf Ebrahimi 304*62c56f98SSadaf Ebrahimidef gen_dependencies_one_line(dependencies): 305*62c56f98SSadaf Ebrahimi """ 306*62c56f98SSadaf Ebrahimi Similar to gen_dependencies() but generates dependency checks in one line. 307*62c56f98SSadaf Ebrahimi Useful for generating code with #else block. 308*62c56f98SSadaf Ebrahimi 309*62c56f98SSadaf Ebrahimi :param dependencies: List of dependencies. 310*62c56f98SSadaf Ebrahimi :return: Preprocessor check code 311*62c56f98SSadaf Ebrahimi """ 312*62c56f98SSadaf Ebrahimi defines = '#if ' if dependencies else '' 313*62c56f98SSadaf Ebrahimi defines += ' && '.join(['%sdefined(%s)' % (x, y) for x, y in map( 314*62c56f98SSadaf Ebrahimi split_dep, dependencies)]) 315*62c56f98SSadaf Ebrahimi return defines 316*62c56f98SSadaf Ebrahimi 317*62c56f98SSadaf Ebrahimi 318*62c56f98SSadaf Ebrahimidef gen_function_wrapper(name, local_vars, args_dispatch): 319*62c56f98SSadaf Ebrahimi """ 320*62c56f98SSadaf Ebrahimi Creates test function wrapper code. A wrapper has the code to 321*62c56f98SSadaf Ebrahimi unpack parameters from parameters[] array. 322*62c56f98SSadaf Ebrahimi 323*62c56f98SSadaf Ebrahimi :param name: Test function name 324*62c56f98SSadaf Ebrahimi :param local_vars: Local variables declaration code 325*62c56f98SSadaf Ebrahimi :param args_dispatch: List of dispatch arguments. 326*62c56f98SSadaf Ebrahimi Ex: ['(char *) params[0]', '*((int *) params[1])'] 327*62c56f98SSadaf Ebrahimi :return: Test function wrapper. 328*62c56f98SSadaf Ebrahimi """ 329*62c56f98SSadaf Ebrahimi # Then create the wrapper 330*62c56f98SSadaf Ebrahimi wrapper = ''' 331*62c56f98SSadaf Ebrahimivoid {name}_wrapper( void ** params ) 332*62c56f98SSadaf Ebrahimi{{ 333*62c56f98SSadaf Ebrahimi{unused_params}{locals} 334*62c56f98SSadaf Ebrahimi {name}( {args} ); 335*62c56f98SSadaf Ebrahimi}} 336*62c56f98SSadaf Ebrahimi'''.format(name=name, 337*62c56f98SSadaf Ebrahimi unused_params='' if args_dispatch else ' (void)params;\n', 338*62c56f98SSadaf Ebrahimi args=', '.join(args_dispatch), 339*62c56f98SSadaf Ebrahimi locals=local_vars) 340*62c56f98SSadaf Ebrahimi return wrapper 341*62c56f98SSadaf Ebrahimi 342*62c56f98SSadaf Ebrahimi 343*62c56f98SSadaf Ebrahimidef gen_dispatch(name, dependencies): 344*62c56f98SSadaf Ebrahimi """ 345*62c56f98SSadaf Ebrahimi Test suite code template main_test.function defines a C function 346*62c56f98SSadaf Ebrahimi array to contain test case functions. This function generates an 347*62c56f98SSadaf Ebrahimi initializer entry for a function in that array. The entry is 348*62c56f98SSadaf Ebrahimi composed of a compile time check for the test function 349*62c56f98SSadaf Ebrahimi dependencies. At compile time the test function is assigned when 350*62c56f98SSadaf Ebrahimi dependencies are met, else NULL is assigned. 351*62c56f98SSadaf Ebrahimi 352*62c56f98SSadaf Ebrahimi :param name: Test function name 353*62c56f98SSadaf Ebrahimi :param dependencies: List of dependencies 354*62c56f98SSadaf Ebrahimi :return: Dispatch code. 355*62c56f98SSadaf Ebrahimi """ 356*62c56f98SSadaf Ebrahimi if dependencies: 357*62c56f98SSadaf Ebrahimi preprocessor_check = gen_dependencies_one_line(dependencies) 358*62c56f98SSadaf Ebrahimi dispatch_code = ''' 359*62c56f98SSadaf Ebrahimi{preprocessor_check} 360*62c56f98SSadaf Ebrahimi {name}_wrapper, 361*62c56f98SSadaf Ebrahimi#else 362*62c56f98SSadaf Ebrahimi NULL, 363*62c56f98SSadaf Ebrahimi#endif 364*62c56f98SSadaf Ebrahimi'''.format(preprocessor_check=preprocessor_check, name=name) 365*62c56f98SSadaf Ebrahimi else: 366*62c56f98SSadaf Ebrahimi dispatch_code = ''' 367*62c56f98SSadaf Ebrahimi {name}_wrapper, 368*62c56f98SSadaf Ebrahimi'''.format(name=name) 369*62c56f98SSadaf Ebrahimi 370*62c56f98SSadaf Ebrahimi return dispatch_code 371*62c56f98SSadaf Ebrahimi 372*62c56f98SSadaf Ebrahimi 373*62c56f98SSadaf Ebrahimidef parse_until_pattern(funcs_f, end_regex): 374*62c56f98SSadaf Ebrahimi """ 375*62c56f98SSadaf Ebrahimi Matches pattern end_regex to the lines read from the file object. 376*62c56f98SSadaf Ebrahimi Returns the lines read until end pattern is matched. 377*62c56f98SSadaf Ebrahimi 378*62c56f98SSadaf Ebrahimi :param funcs_f: file object for .function file 379*62c56f98SSadaf Ebrahimi :param end_regex: Pattern to stop parsing 380*62c56f98SSadaf Ebrahimi :return: Lines read before the end pattern 381*62c56f98SSadaf Ebrahimi """ 382*62c56f98SSadaf Ebrahimi headers = '#line %d "%s"\n' % (funcs_f.line_no + 1, funcs_f.name) 383*62c56f98SSadaf Ebrahimi for line in funcs_f: 384*62c56f98SSadaf Ebrahimi if re.search(end_regex, line): 385*62c56f98SSadaf Ebrahimi break 386*62c56f98SSadaf Ebrahimi headers += line 387*62c56f98SSadaf Ebrahimi else: 388*62c56f98SSadaf Ebrahimi raise GeneratorInputError("file: %s - end pattern [%s] not found!" % 389*62c56f98SSadaf Ebrahimi (funcs_f.name, end_regex)) 390*62c56f98SSadaf Ebrahimi 391*62c56f98SSadaf Ebrahimi return headers 392*62c56f98SSadaf Ebrahimi 393*62c56f98SSadaf Ebrahimi 394*62c56f98SSadaf Ebrahimidef validate_dependency(dependency): 395*62c56f98SSadaf Ebrahimi """ 396*62c56f98SSadaf Ebrahimi Validates a C macro and raises GeneratorInputError on invalid input. 397*62c56f98SSadaf Ebrahimi :param dependency: Input macro dependency 398*62c56f98SSadaf Ebrahimi :return: input dependency stripped of leading & trailing white spaces. 399*62c56f98SSadaf Ebrahimi """ 400*62c56f98SSadaf Ebrahimi dependency = dependency.strip() 401*62c56f98SSadaf Ebrahimi if not re.match(CONDITION_REGEX, dependency, re.I): 402*62c56f98SSadaf Ebrahimi raise GeneratorInputError('Invalid dependency %s' % dependency) 403*62c56f98SSadaf Ebrahimi return dependency 404*62c56f98SSadaf Ebrahimi 405*62c56f98SSadaf Ebrahimi 406*62c56f98SSadaf Ebrahimidef parse_dependencies(inp_str): 407*62c56f98SSadaf Ebrahimi """ 408*62c56f98SSadaf Ebrahimi Parses dependencies out of inp_str, validates them and returns a 409*62c56f98SSadaf Ebrahimi list of macros. 410*62c56f98SSadaf Ebrahimi 411*62c56f98SSadaf Ebrahimi :param inp_str: Input string with macros delimited by ':'. 412*62c56f98SSadaf Ebrahimi :return: list of dependencies 413*62c56f98SSadaf Ebrahimi """ 414*62c56f98SSadaf Ebrahimi dependencies = list(map(validate_dependency, inp_str.split(':'))) 415*62c56f98SSadaf Ebrahimi return dependencies 416*62c56f98SSadaf Ebrahimi 417*62c56f98SSadaf Ebrahimi 418*62c56f98SSadaf Ebrahimidef parse_suite_dependencies(funcs_f): 419*62c56f98SSadaf Ebrahimi """ 420*62c56f98SSadaf Ebrahimi Parses test suite dependencies specified at the top of a 421*62c56f98SSadaf Ebrahimi .function file, that starts with pattern BEGIN_DEPENDENCIES 422*62c56f98SSadaf Ebrahimi and end with END_DEPENDENCIES. Dependencies are specified 423*62c56f98SSadaf Ebrahimi after pattern 'depends_on:' and are delimited by ':'. 424*62c56f98SSadaf Ebrahimi 425*62c56f98SSadaf Ebrahimi :param funcs_f: file object for .function file 426*62c56f98SSadaf Ebrahimi :return: List of test suite dependencies. 427*62c56f98SSadaf Ebrahimi """ 428*62c56f98SSadaf Ebrahimi dependencies = [] 429*62c56f98SSadaf Ebrahimi for line in funcs_f: 430*62c56f98SSadaf Ebrahimi match = re.search(DEPENDENCY_REGEX, line.strip()) 431*62c56f98SSadaf Ebrahimi if match: 432*62c56f98SSadaf Ebrahimi try: 433*62c56f98SSadaf Ebrahimi dependencies = parse_dependencies(match.group('dependencies')) 434*62c56f98SSadaf Ebrahimi except GeneratorInputError as error: 435*62c56f98SSadaf Ebrahimi raise GeneratorInputError( 436*62c56f98SSadaf Ebrahimi str(error) + " - %s:%d" % (funcs_f.name, funcs_f.line_no)) 437*62c56f98SSadaf Ebrahimi if re.search(END_DEP_REGEX, line): 438*62c56f98SSadaf Ebrahimi break 439*62c56f98SSadaf Ebrahimi else: 440*62c56f98SSadaf Ebrahimi raise GeneratorInputError("file: %s - end dependency pattern [%s]" 441*62c56f98SSadaf Ebrahimi " not found!" % (funcs_f.name, 442*62c56f98SSadaf Ebrahimi END_DEP_REGEX)) 443*62c56f98SSadaf Ebrahimi 444*62c56f98SSadaf Ebrahimi return dependencies 445*62c56f98SSadaf Ebrahimi 446*62c56f98SSadaf Ebrahimi 447*62c56f98SSadaf Ebrahimidef parse_function_dependencies(line): 448*62c56f98SSadaf Ebrahimi """ 449*62c56f98SSadaf Ebrahimi Parses function dependencies, that are in the same line as 450*62c56f98SSadaf Ebrahimi comment BEGIN_CASE. Dependencies are specified after pattern 451*62c56f98SSadaf Ebrahimi 'depends_on:' and are delimited by ':'. 452*62c56f98SSadaf Ebrahimi 453*62c56f98SSadaf Ebrahimi :param line: Line from .function file that has dependencies. 454*62c56f98SSadaf Ebrahimi :return: List of dependencies. 455*62c56f98SSadaf Ebrahimi """ 456*62c56f98SSadaf Ebrahimi dependencies = [] 457*62c56f98SSadaf Ebrahimi match = re.search(BEGIN_CASE_REGEX, line) 458*62c56f98SSadaf Ebrahimi dep_str = match.group('depends_on') 459*62c56f98SSadaf Ebrahimi if dep_str: 460*62c56f98SSadaf Ebrahimi match = re.search(DEPENDENCY_REGEX, dep_str) 461*62c56f98SSadaf Ebrahimi if match: 462*62c56f98SSadaf Ebrahimi dependencies += parse_dependencies(match.group('dependencies')) 463*62c56f98SSadaf Ebrahimi 464*62c56f98SSadaf Ebrahimi return dependencies 465*62c56f98SSadaf Ebrahimi 466*62c56f98SSadaf Ebrahimi 467*62c56f98SSadaf EbrahimiARGUMENT_DECLARATION_REGEX = re.compile(r'(.+?) ?(?:\bconst\b)? ?(\w+)\Z', re.S) 468*62c56f98SSadaf Ebrahimidef parse_function_argument(arg, arg_idx, args, local_vars, args_dispatch): 469*62c56f98SSadaf Ebrahimi """ 470*62c56f98SSadaf Ebrahimi Parses one test function's argument declaration. 471*62c56f98SSadaf Ebrahimi 472*62c56f98SSadaf Ebrahimi :param arg: argument declaration. 473*62c56f98SSadaf Ebrahimi :param arg_idx: current wrapper argument index. 474*62c56f98SSadaf Ebrahimi :param args: accumulator of arguments' internal types. 475*62c56f98SSadaf Ebrahimi :param local_vars: accumulator of internal variable declarations. 476*62c56f98SSadaf Ebrahimi :param args_dispatch: accumulator of argument usage expressions. 477*62c56f98SSadaf Ebrahimi :return: the number of new wrapper arguments, 478*62c56f98SSadaf Ebrahimi or None if the argument declaration is invalid. 479*62c56f98SSadaf Ebrahimi """ 480*62c56f98SSadaf Ebrahimi # Normalize whitespace 481*62c56f98SSadaf Ebrahimi arg = arg.strip() 482*62c56f98SSadaf Ebrahimi arg = re.sub(r'\s*\*\s*', r'*', arg) 483*62c56f98SSadaf Ebrahimi arg = re.sub(r'\s+', r' ', arg) 484*62c56f98SSadaf Ebrahimi # Extract name and type 485*62c56f98SSadaf Ebrahimi m = ARGUMENT_DECLARATION_REGEX.search(arg) 486*62c56f98SSadaf Ebrahimi if not m: 487*62c56f98SSadaf Ebrahimi # E.g. "int x[42]" 488*62c56f98SSadaf Ebrahimi return None 489*62c56f98SSadaf Ebrahimi typ, _ = m.groups() 490*62c56f98SSadaf Ebrahimi if typ in SIGNED_INTEGER_TYPES: 491*62c56f98SSadaf Ebrahimi args.append('int') 492*62c56f98SSadaf Ebrahimi args_dispatch.append('((mbedtls_test_argument_t *) params[%d])->sint' % arg_idx) 493*62c56f98SSadaf Ebrahimi return 1 494*62c56f98SSadaf Ebrahimi if typ in STRING_TYPES: 495*62c56f98SSadaf Ebrahimi args.append('char*') 496*62c56f98SSadaf Ebrahimi args_dispatch.append('(char *) params[%d]' % arg_idx) 497*62c56f98SSadaf Ebrahimi return 1 498*62c56f98SSadaf Ebrahimi if typ in DATA_TYPES: 499*62c56f98SSadaf Ebrahimi args.append('hex') 500*62c56f98SSadaf Ebrahimi # create a structure 501*62c56f98SSadaf Ebrahimi pointer_initializer = '(uint8_t *) params[%d]' % arg_idx 502*62c56f98SSadaf Ebrahimi len_initializer = '((mbedtls_test_argument_t *) params[%d])->len' % (arg_idx+1) 503*62c56f98SSadaf Ebrahimi local_vars.append(' data_t data%d = {%s, %s};\n' % 504*62c56f98SSadaf Ebrahimi (arg_idx, pointer_initializer, len_initializer)) 505*62c56f98SSadaf Ebrahimi args_dispatch.append('&data%d' % arg_idx) 506*62c56f98SSadaf Ebrahimi return 2 507*62c56f98SSadaf Ebrahimi return None 508*62c56f98SSadaf Ebrahimi 509*62c56f98SSadaf EbrahimiARGUMENT_LIST_REGEX = re.compile(r'\((.*?)\)', re.S) 510*62c56f98SSadaf Ebrahimidef parse_function_arguments(line): 511*62c56f98SSadaf Ebrahimi """ 512*62c56f98SSadaf Ebrahimi Parses test function signature for validation and generates 513*62c56f98SSadaf Ebrahimi a dispatch wrapper function that translates input test vectors 514*62c56f98SSadaf Ebrahimi read from the data file into test function arguments. 515*62c56f98SSadaf Ebrahimi 516*62c56f98SSadaf Ebrahimi :param line: Line from .function file that has a function 517*62c56f98SSadaf Ebrahimi signature. 518*62c56f98SSadaf Ebrahimi :return: argument list, local variables for 519*62c56f98SSadaf Ebrahimi wrapper function and argument dispatch code. 520*62c56f98SSadaf Ebrahimi """ 521*62c56f98SSadaf Ebrahimi # Process arguments, ex: <type> arg1, <type> arg2 ) 522*62c56f98SSadaf Ebrahimi # This script assumes that the argument list is terminated by ')' 523*62c56f98SSadaf Ebrahimi # i.e. the test functions will not have a function pointer 524*62c56f98SSadaf Ebrahimi # argument. 525*62c56f98SSadaf Ebrahimi m = ARGUMENT_LIST_REGEX.search(line) 526*62c56f98SSadaf Ebrahimi arg_list = m.group(1).strip() 527*62c56f98SSadaf Ebrahimi if arg_list in ['', 'void']: 528*62c56f98SSadaf Ebrahimi return [], '', [] 529*62c56f98SSadaf Ebrahimi args = [] 530*62c56f98SSadaf Ebrahimi local_vars = [] 531*62c56f98SSadaf Ebrahimi args_dispatch = [] 532*62c56f98SSadaf Ebrahimi arg_idx = 0 533*62c56f98SSadaf Ebrahimi for arg in arg_list.split(','): 534*62c56f98SSadaf Ebrahimi indexes = parse_function_argument(arg, arg_idx, 535*62c56f98SSadaf Ebrahimi args, local_vars, args_dispatch) 536*62c56f98SSadaf Ebrahimi if indexes is None: 537*62c56f98SSadaf Ebrahimi raise ValueError("Test function arguments can only be 'int', " 538*62c56f98SSadaf Ebrahimi "'char *' or 'data_t'\n%s" % line) 539*62c56f98SSadaf Ebrahimi arg_idx += indexes 540*62c56f98SSadaf Ebrahimi 541*62c56f98SSadaf Ebrahimi return args, ''.join(local_vars), args_dispatch 542*62c56f98SSadaf Ebrahimi 543*62c56f98SSadaf Ebrahimi 544*62c56f98SSadaf Ebrahimidef generate_function_code(name, code, local_vars, args_dispatch, 545*62c56f98SSadaf Ebrahimi dependencies): 546*62c56f98SSadaf Ebrahimi """ 547*62c56f98SSadaf Ebrahimi Generate function code with preprocessor checks and parameter dispatch 548*62c56f98SSadaf Ebrahimi wrapper. 549*62c56f98SSadaf Ebrahimi 550*62c56f98SSadaf Ebrahimi :param name: Function name 551*62c56f98SSadaf Ebrahimi :param code: Function code 552*62c56f98SSadaf Ebrahimi :param local_vars: Local variables for function wrapper 553*62c56f98SSadaf Ebrahimi :param args_dispatch: Argument dispatch code 554*62c56f98SSadaf Ebrahimi :param dependencies: Preprocessor dependencies list 555*62c56f98SSadaf Ebrahimi :return: Final function code 556*62c56f98SSadaf Ebrahimi """ 557*62c56f98SSadaf Ebrahimi # Add exit label if not present 558*62c56f98SSadaf Ebrahimi if code.find('exit:') == -1: 559*62c56f98SSadaf Ebrahimi split_code = code.rsplit('}', 1) 560*62c56f98SSadaf Ebrahimi if len(split_code) == 2: 561*62c56f98SSadaf Ebrahimi code = """exit: 562*62c56f98SSadaf Ebrahimi ; 563*62c56f98SSadaf Ebrahimi}""".join(split_code) 564*62c56f98SSadaf Ebrahimi 565*62c56f98SSadaf Ebrahimi code += gen_function_wrapper(name, local_vars, args_dispatch) 566*62c56f98SSadaf Ebrahimi preprocessor_check_start, preprocessor_check_end = \ 567*62c56f98SSadaf Ebrahimi gen_dependencies(dependencies) 568*62c56f98SSadaf Ebrahimi return preprocessor_check_start + code + preprocessor_check_end 569*62c56f98SSadaf Ebrahimi 570*62c56f98SSadaf EbrahimiCOMMENT_START_REGEX = re.compile(r'/[*/]') 571*62c56f98SSadaf Ebrahimi 572*62c56f98SSadaf Ebrahimidef skip_comments(line, stream): 573*62c56f98SSadaf Ebrahimi """Remove comments in line. 574*62c56f98SSadaf Ebrahimi 575*62c56f98SSadaf Ebrahimi If the line contains an unfinished comment, read more lines from stream 576*62c56f98SSadaf Ebrahimi until the line that contains the comment. 577*62c56f98SSadaf Ebrahimi 578*62c56f98SSadaf Ebrahimi :return: The original line with inner comments replaced by spaces. 579*62c56f98SSadaf Ebrahimi Trailing comments and whitespace may be removed completely. 580*62c56f98SSadaf Ebrahimi """ 581*62c56f98SSadaf Ebrahimi pos = 0 582*62c56f98SSadaf Ebrahimi while True: 583*62c56f98SSadaf Ebrahimi opening = COMMENT_START_REGEX.search(line, pos) 584*62c56f98SSadaf Ebrahimi if not opening: 585*62c56f98SSadaf Ebrahimi break 586*62c56f98SSadaf Ebrahimi if line[opening.start(0) + 1] == '/': # //... 587*62c56f98SSadaf Ebrahimi continuation = line 588*62c56f98SSadaf Ebrahimi # Count the number of line breaks, to keep line numbers aligned 589*62c56f98SSadaf Ebrahimi # in the output. 590*62c56f98SSadaf Ebrahimi line_count = 1 591*62c56f98SSadaf Ebrahimi while continuation.endswith('\\\n'): 592*62c56f98SSadaf Ebrahimi # This errors out if the file ends with an unfinished line 593*62c56f98SSadaf Ebrahimi # comment. That's acceptable to not complicate the code further. 594*62c56f98SSadaf Ebrahimi continuation = next(stream) 595*62c56f98SSadaf Ebrahimi line_count += 1 596*62c56f98SSadaf Ebrahimi return line[:opening.start(0)].rstrip() + '\n' * line_count 597*62c56f98SSadaf Ebrahimi # Parsing /*...*/, looking for the end 598*62c56f98SSadaf Ebrahimi closing = line.find('*/', opening.end(0)) 599*62c56f98SSadaf Ebrahimi while closing == -1: 600*62c56f98SSadaf Ebrahimi # This errors out if the file ends with an unfinished block 601*62c56f98SSadaf Ebrahimi # comment. That's acceptable to not complicate the code further. 602*62c56f98SSadaf Ebrahimi line += next(stream) 603*62c56f98SSadaf Ebrahimi closing = line.find('*/', opening.end(0)) 604*62c56f98SSadaf Ebrahimi pos = closing + 2 605*62c56f98SSadaf Ebrahimi # Replace inner comment by spaces. There needs to be at least one space 606*62c56f98SSadaf Ebrahimi # for things like 'int/*ihatespaces*/foo'. Go further and preserve the 607*62c56f98SSadaf Ebrahimi # width of the comment and line breaks, this way positions in error 608*62c56f98SSadaf Ebrahimi # messages remain correct. 609*62c56f98SSadaf Ebrahimi line = (line[:opening.start(0)] + 610*62c56f98SSadaf Ebrahimi re.sub(r'.', r' ', line[opening.start(0):pos]) + 611*62c56f98SSadaf Ebrahimi line[pos:]) 612*62c56f98SSadaf Ebrahimi # Strip whitespace at the end of lines (it's irrelevant to error messages). 613*62c56f98SSadaf Ebrahimi return re.sub(r' +(\n|\Z)', r'\1', line) 614*62c56f98SSadaf Ebrahimi 615*62c56f98SSadaf Ebrahimidef parse_function_code(funcs_f, dependencies, suite_dependencies): 616*62c56f98SSadaf Ebrahimi """ 617*62c56f98SSadaf Ebrahimi Parses out a function from function file object and generates 618*62c56f98SSadaf Ebrahimi function and dispatch code. 619*62c56f98SSadaf Ebrahimi 620*62c56f98SSadaf Ebrahimi :param funcs_f: file object of the functions file. 621*62c56f98SSadaf Ebrahimi :param dependencies: List of dependencies 622*62c56f98SSadaf Ebrahimi :param suite_dependencies: List of test suite dependencies 623*62c56f98SSadaf Ebrahimi :return: Function name, arguments, function code and dispatch code. 624*62c56f98SSadaf Ebrahimi """ 625*62c56f98SSadaf Ebrahimi line_directive = '#line %d "%s"\n' % (funcs_f.line_no + 1, funcs_f.name) 626*62c56f98SSadaf Ebrahimi code = '' 627*62c56f98SSadaf Ebrahimi has_exit_label = False 628*62c56f98SSadaf Ebrahimi for line in funcs_f: 629*62c56f98SSadaf Ebrahimi # Check function signature. Function signature may be split 630*62c56f98SSadaf Ebrahimi # across multiple lines. Here we try to find the start of 631*62c56f98SSadaf Ebrahimi # arguments list, then remove '\n's and apply the regex to 632*62c56f98SSadaf Ebrahimi # detect function start. 633*62c56f98SSadaf Ebrahimi line = skip_comments(line, funcs_f) 634*62c56f98SSadaf Ebrahimi up_to_arg_list_start = code + line[:line.find('(') + 1] 635*62c56f98SSadaf Ebrahimi match = re.match(TEST_FUNCTION_VALIDATION_REGEX, 636*62c56f98SSadaf Ebrahimi up_to_arg_list_start.replace('\n', ' '), re.I) 637*62c56f98SSadaf Ebrahimi if match: 638*62c56f98SSadaf Ebrahimi # check if we have full signature i.e. split in more lines 639*62c56f98SSadaf Ebrahimi name = match.group('func_name') 640*62c56f98SSadaf Ebrahimi if not re.match(FUNCTION_ARG_LIST_END_REGEX, line): 641*62c56f98SSadaf Ebrahimi for lin in funcs_f: 642*62c56f98SSadaf Ebrahimi line += skip_comments(lin, funcs_f) 643*62c56f98SSadaf Ebrahimi if re.search(FUNCTION_ARG_LIST_END_REGEX, line): 644*62c56f98SSadaf Ebrahimi break 645*62c56f98SSadaf Ebrahimi args, local_vars, args_dispatch = parse_function_arguments( 646*62c56f98SSadaf Ebrahimi line) 647*62c56f98SSadaf Ebrahimi code += line 648*62c56f98SSadaf Ebrahimi break 649*62c56f98SSadaf Ebrahimi code += line 650*62c56f98SSadaf Ebrahimi else: 651*62c56f98SSadaf Ebrahimi raise GeneratorInputError("file: %s - Test functions not found!" % 652*62c56f98SSadaf Ebrahimi funcs_f.name) 653*62c56f98SSadaf Ebrahimi 654*62c56f98SSadaf Ebrahimi # Prefix test function name with 'test_' 655*62c56f98SSadaf Ebrahimi code = code.replace(name, 'test_' + name, 1) 656*62c56f98SSadaf Ebrahimi name = 'test_' + name 657*62c56f98SSadaf Ebrahimi 658*62c56f98SSadaf Ebrahimi # If a test function has no arguments then add 'void' argument to 659*62c56f98SSadaf Ebrahimi # avoid "-Wstrict-prototypes" warnings from clang 660*62c56f98SSadaf Ebrahimi if len(args) == 0: 661*62c56f98SSadaf Ebrahimi code = code.replace('()', '(void)', 1) 662*62c56f98SSadaf Ebrahimi 663*62c56f98SSadaf Ebrahimi for line in funcs_f: 664*62c56f98SSadaf Ebrahimi if re.search(END_CASE_REGEX, line): 665*62c56f98SSadaf Ebrahimi break 666*62c56f98SSadaf Ebrahimi if not has_exit_label: 667*62c56f98SSadaf Ebrahimi has_exit_label = \ 668*62c56f98SSadaf Ebrahimi re.search(EXIT_LABEL_REGEX, line.strip()) is not None 669*62c56f98SSadaf Ebrahimi code += line 670*62c56f98SSadaf Ebrahimi else: 671*62c56f98SSadaf Ebrahimi raise GeneratorInputError("file: %s - end case pattern [%s] not " 672*62c56f98SSadaf Ebrahimi "found!" % (funcs_f.name, END_CASE_REGEX)) 673*62c56f98SSadaf Ebrahimi 674*62c56f98SSadaf Ebrahimi code = line_directive + code 675*62c56f98SSadaf Ebrahimi code = generate_function_code(name, code, local_vars, args_dispatch, 676*62c56f98SSadaf Ebrahimi dependencies) 677*62c56f98SSadaf Ebrahimi dispatch_code = gen_dispatch(name, suite_dependencies + dependencies) 678*62c56f98SSadaf Ebrahimi return (name, args, code, dispatch_code) 679*62c56f98SSadaf Ebrahimi 680*62c56f98SSadaf Ebrahimi 681*62c56f98SSadaf Ebrahimidef parse_functions(funcs_f): 682*62c56f98SSadaf Ebrahimi """ 683*62c56f98SSadaf Ebrahimi Parses a test_suite_xxx.function file and returns information 684*62c56f98SSadaf Ebrahimi for generating a C source file for the test suite. 685*62c56f98SSadaf Ebrahimi 686*62c56f98SSadaf Ebrahimi :param funcs_f: file object of the functions file. 687*62c56f98SSadaf Ebrahimi :return: List of test suite dependencies, test function dispatch 688*62c56f98SSadaf Ebrahimi code, function code and a dict with function identifiers 689*62c56f98SSadaf Ebrahimi and arguments info. 690*62c56f98SSadaf Ebrahimi """ 691*62c56f98SSadaf Ebrahimi suite_helpers = '' 692*62c56f98SSadaf Ebrahimi suite_dependencies = [] 693*62c56f98SSadaf Ebrahimi suite_functions = '' 694*62c56f98SSadaf Ebrahimi func_info = {} 695*62c56f98SSadaf Ebrahimi function_idx = 0 696*62c56f98SSadaf Ebrahimi dispatch_code = '' 697*62c56f98SSadaf Ebrahimi for line in funcs_f: 698*62c56f98SSadaf Ebrahimi if re.search(BEGIN_HEADER_REGEX, line): 699*62c56f98SSadaf Ebrahimi suite_helpers += parse_until_pattern(funcs_f, END_HEADER_REGEX) 700*62c56f98SSadaf Ebrahimi elif re.search(BEGIN_SUITE_HELPERS_REGEX, line): 701*62c56f98SSadaf Ebrahimi suite_helpers += parse_until_pattern(funcs_f, 702*62c56f98SSadaf Ebrahimi END_SUITE_HELPERS_REGEX) 703*62c56f98SSadaf Ebrahimi elif re.search(BEGIN_DEP_REGEX, line): 704*62c56f98SSadaf Ebrahimi suite_dependencies += parse_suite_dependencies(funcs_f) 705*62c56f98SSadaf Ebrahimi elif re.search(BEGIN_CASE_REGEX, line): 706*62c56f98SSadaf Ebrahimi try: 707*62c56f98SSadaf Ebrahimi dependencies = parse_function_dependencies(line) 708*62c56f98SSadaf Ebrahimi except GeneratorInputError as error: 709*62c56f98SSadaf Ebrahimi raise GeneratorInputError( 710*62c56f98SSadaf Ebrahimi "%s:%d: %s" % (funcs_f.name, funcs_f.line_no, 711*62c56f98SSadaf Ebrahimi str(error))) 712*62c56f98SSadaf Ebrahimi func_name, args, func_code, func_dispatch =\ 713*62c56f98SSadaf Ebrahimi parse_function_code(funcs_f, dependencies, suite_dependencies) 714*62c56f98SSadaf Ebrahimi suite_functions += func_code 715*62c56f98SSadaf Ebrahimi # Generate dispatch code and enumeration info 716*62c56f98SSadaf Ebrahimi if func_name in func_info: 717*62c56f98SSadaf Ebrahimi raise GeneratorInputError( 718*62c56f98SSadaf Ebrahimi "file: %s - function %s re-declared at line %d" % 719*62c56f98SSadaf Ebrahimi (funcs_f.name, func_name, funcs_f.line_no)) 720*62c56f98SSadaf Ebrahimi func_info[func_name] = (function_idx, args) 721*62c56f98SSadaf Ebrahimi dispatch_code += '/* Function Id: %d */\n' % function_idx 722*62c56f98SSadaf Ebrahimi dispatch_code += func_dispatch 723*62c56f98SSadaf Ebrahimi function_idx += 1 724*62c56f98SSadaf Ebrahimi 725*62c56f98SSadaf Ebrahimi func_code = (suite_helpers + 726*62c56f98SSadaf Ebrahimi suite_functions).join(gen_dependencies(suite_dependencies)) 727*62c56f98SSadaf Ebrahimi return suite_dependencies, dispatch_code, func_code, func_info 728*62c56f98SSadaf Ebrahimi 729*62c56f98SSadaf Ebrahimi 730*62c56f98SSadaf Ebrahimidef escaped_split(inp_str, split_char): 731*62c56f98SSadaf Ebrahimi """ 732*62c56f98SSadaf Ebrahimi Split inp_str on character split_char but ignore if escaped. 733*62c56f98SSadaf Ebrahimi Since, return value is used to write back to the intermediate 734*62c56f98SSadaf Ebrahimi data file, any escape characters in the input are retained in the 735*62c56f98SSadaf Ebrahimi output. 736*62c56f98SSadaf Ebrahimi 737*62c56f98SSadaf Ebrahimi :param inp_str: String to split 738*62c56f98SSadaf Ebrahimi :param split_char: Split character 739*62c56f98SSadaf Ebrahimi :return: List of splits 740*62c56f98SSadaf Ebrahimi """ 741*62c56f98SSadaf Ebrahimi if len(split_char) > 1: 742*62c56f98SSadaf Ebrahimi raise ValueError('Expected split character. Found string!') 743*62c56f98SSadaf Ebrahimi out = re.sub(r'(\\.)|' + split_char, 744*62c56f98SSadaf Ebrahimi lambda m: m.group(1) or '\n', inp_str, 745*62c56f98SSadaf Ebrahimi len(inp_str)).split('\n') 746*62c56f98SSadaf Ebrahimi out = [x for x in out if x] 747*62c56f98SSadaf Ebrahimi return out 748*62c56f98SSadaf Ebrahimi 749*62c56f98SSadaf Ebrahimi 750*62c56f98SSadaf Ebrahimidef parse_test_data(data_f): 751*62c56f98SSadaf Ebrahimi """ 752*62c56f98SSadaf Ebrahimi Parses .data file for each test case name, test function name, 753*62c56f98SSadaf Ebrahimi test dependencies and test arguments. This information is 754*62c56f98SSadaf Ebrahimi correlated with the test functions file for generating an 755*62c56f98SSadaf Ebrahimi intermediate data file replacing the strings for test function 756*62c56f98SSadaf Ebrahimi names, dependencies and integer constant expressions with 757*62c56f98SSadaf Ebrahimi identifiers. Mainly for optimising space for on-target 758*62c56f98SSadaf Ebrahimi execution. 759*62c56f98SSadaf Ebrahimi 760*62c56f98SSadaf Ebrahimi :param data_f: file object of the data file. 761*62c56f98SSadaf Ebrahimi :return: Generator that yields line number, test name, function name, 762*62c56f98SSadaf Ebrahimi dependency list and function argument list. 763*62c56f98SSadaf Ebrahimi """ 764*62c56f98SSadaf Ebrahimi __state_read_name = 0 765*62c56f98SSadaf Ebrahimi __state_read_args = 1 766*62c56f98SSadaf Ebrahimi state = __state_read_name 767*62c56f98SSadaf Ebrahimi dependencies = [] 768*62c56f98SSadaf Ebrahimi name = '' 769*62c56f98SSadaf Ebrahimi for line in data_f: 770*62c56f98SSadaf Ebrahimi line = line.strip() 771*62c56f98SSadaf Ebrahimi # Skip comments 772*62c56f98SSadaf Ebrahimi if line.startswith('#'): 773*62c56f98SSadaf Ebrahimi continue 774*62c56f98SSadaf Ebrahimi 775*62c56f98SSadaf Ebrahimi # Blank line indicates end of test 776*62c56f98SSadaf Ebrahimi if not line: 777*62c56f98SSadaf Ebrahimi if state == __state_read_args: 778*62c56f98SSadaf Ebrahimi raise GeneratorInputError("[%s:%d] Newline before arguments. " 779*62c56f98SSadaf Ebrahimi "Test function and arguments " 780*62c56f98SSadaf Ebrahimi "missing for %s" % 781*62c56f98SSadaf Ebrahimi (data_f.name, data_f.line_no, name)) 782*62c56f98SSadaf Ebrahimi continue 783*62c56f98SSadaf Ebrahimi 784*62c56f98SSadaf Ebrahimi if state == __state_read_name: 785*62c56f98SSadaf Ebrahimi # Read test name 786*62c56f98SSadaf Ebrahimi name = line 787*62c56f98SSadaf Ebrahimi state = __state_read_args 788*62c56f98SSadaf Ebrahimi elif state == __state_read_args: 789*62c56f98SSadaf Ebrahimi # Check dependencies 790*62c56f98SSadaf Ebrahimi match = re.search(DEPENDENCY_REGEX, line) 791*62c56f98SSadaf Ebrahimi if match: 792*62c56f98SSadaf Ebrahimi try: 793*62c56f98SSadaf Ebrahimi dependencies = parse_dependencies( 794*62c56f98SSadaf Ebrahimi match.group('dependencies')) 795*62c56f98SSadaf Ebrahimi except GeneratorInputError as error: 796*62c56f98SSadaf Ebrahimi raise GeneratorInputError( 797*62c56f98SSadaf Ebrahimi str(error) + " - %s:%d" % 798*62c56f98SSadaf Ebrahimi (data_f.name, data_f.line_no)) 799*62c56f98SSadaf Ebrahimi else: 800*62c56f98SSadaf Ebrahimi # Read test vectors 801*62c56f98SSadaf Ebrahimi parts = escaped_split(line, ':') 802*62c56f98SSadaf Ebrahimi test_function = parts[0] 803*62c56f98SSadaf Ebrahimi args = parts[1:] 804*62c56f98SSadaf Ebrahimi yield data_f.line_no, name, test_function, dependencies, args 805*62c56f98SSadaf Ebrahimi dependencies = [] 806*62c56f98SSadaf Ebrahimi state = __state_read_name 807*62c56f98SSadaf Ebrahimi if state == __state_read_args: 808*62c56f98SSadaf Ebrahimi raise GeneratorInputError("[%s:%d] Newline before arguments. " 809*62c56f98SSadaf Ebrahimi "Test function and arguments missing for " 810*62c56f98SSadaf Ebrahimi "%s" % (data_f.name, data_f.line_no, name)) 811*62c56f98SSadaf Ebrahimi 812*62c56f98SSadaf Ebrahimi 813*62c56f98SSadaf Ebrahimidef gen_dep_check(dep_id, dep): 814*62c56f98SSadaf Ebrahimi """ 815*62c56f98SSadaf Ebrahimi Generate code for checking dependency with the associated 816*62c56f98SSadaf Ebrahimi identifier. 817*62c56f98SSadaf Ebrahimi 818*62c56f98SSadaf Ebrahimi :param dep_id: Dependency identifier 819*62c56f98SSadaf Ebrahimi :param dep: Dependency macro 820*62c56f98SSadaf Ebrahimi :return: Dependency check code 821*62c56f98SSadaf Ebrahimi """ 822*62c56f98SSadaf Ebrahimi if dep_id < 0: 823*62c56f98SSadaf Ebrahimi raise GeneratorInputError("Dependency Id should be a positive " 824*62c56f98SSadaf Ebrahimi "integer.") 825*62c56f98SSadaf Ebrahimi _not, dep = ('!', dep[1:]) if dep[0] == '!' else ('', dep) 826*62c56f98SSadaf Ebrahimi if not dep: 827*62c56f98SSadaf Ebrahimi raise GeneratorInputError("Dependency should not be an empty string.") 828*62c56f98SSadaf Ebrahimi 829*62c56f98SSadaf Ebrahimi dependency = re.match(CONDITION_REGEX, dep, re.I) 830*62c56f98SSadaf Ebrahimi if not dependency: 831*62c56f98SSadaf Ebrahimi raise GeneratorInputError('Invalid dependency %s' % dep) 832*62c56f98SSadaf Ebrahimi 833*62c56f98SSadaf Ebrahimi _defined = '' if dependency.group(2) else 'defined' 834*62c56f98SSadaf Ebrahimi _cond = dependency.group(2) if dependency.group(2) else '' 835*62c56f98SSadaf Ebrahimi _value = dependency.group(3) if dependency.group(3) else '' 836*62c56f98SSadaf Ebrahimi 837*62c56f98SSadaf Ebrahimi dep_check = ''' 838*62c56f98SSadaf Ebrahimi case {id}: 839*62c56f98SSadaf Ebrahimi {{ 840*62c56f98SSadaf Ebrahimi#if {_not}{_defined}({macro}{_cond}{_value}) 841*62c56f98SSadaf Ebrahimi ret = DEPENDENCY_SUPPORTED; 842*62c56f98SSadaf Ebrahimi#else 843*62c56f98SSadaf Ebrahimi ret = DEPENDENCY_NOT_SUPPORTED; 844*62c56f98SSadaf Ebrahimi#endif 845*62c56f98SSadaf Ebrahimi }} 846*62c56f98SSadaf Ebrahimi break;'''.format(_not=_not, _defined=_defined, 847*62c56f98SSadaf Ebrahimi macro=dependency.group(1), id=dep_id, 848*62c56f98SSadaf Ebrahimi _cond=_cond, _value=_value) 849*62c56f98SSadaf Ebrahimi return dep_check 850*62c56f98SSadaf Ebrahimi 851*62c56f98SSadaf Ebrahimi 852*62c56f98SSadaf Ebrahimidef gen_expression_check(exp_id, exp): 853*62c56f98SSadaf Ebrahimi """ 854*62c56f98SSadaf Ebrahimi Generates code for evaluating an integer expression using 855*62c56f98SSadaf Ebrahimi associated expression Id. 856*62c56f98SSadaf Ebrahimi 857*62c56f98SSadaf Ebrahimi :param exp_id: Expression Identifier 858*62c56f98SSadaf Ebrahimi :param exp: Expression/Macro 859*62c56f98SSadaf Ebrahimi :return: Expression check code 860*62c56f98SSadaf Ebrahimi """ 861*62c56f98SSadaf Ebrahimi if exp_id < 0: 862*62c56f98SSadaf Ebrahimi raise GeneratorInputError("Expression Id should be a positive " 863*62c56f98SSadaf Ebrahimi "integer.") 864*62c56f98SSadaf Ebrahimi if not exp: 865*62c56f98SSadaf Ebrahimi raise GeneratorInputError("Expression should not be an empty string.") 866*62c56f98SSadaf Ebrahimi exp_code = ''' 867*62c56f98SSadaf Ebrahimi case {exp_id}: 868*62c56f98SSadaf Ebrahimi {{ 869*62c56f98SSadaf Ebrahimi *out_value = {expression}; 870*62c56f98SSadaf Ebrahimi }} 871*62c56f98SSadaf Ebrahimi break;'''.format(exp_id=exp_id, expression=exp) 872*62c56f98SSadaf Ebrahimi return exp_code 873*62c56f98SSadaf Ebrahimi 874*62c56f98SSadaf Ebrahimi 875*62c56f98SSadaf Ebrahimidef write_dependencies(out_data_f, test_dependencies, unique_dependencies): 876*62c56f98SSadaf Ebrahimi """ 877*62c56f98SSadaf Ebrahimi Write dependencies to intermediate test data file, replacing 878*62c56f98SSadaf Ebrahimi the string form with identifiers. Also, generates dependency 879*62c56f98SSadaf Ebrahimi check code. 880*62c56f98SSadaf Ebrahimi 881*62c56f98SSadaf Ebrahimi :param out_data_f: Output intermediate data file 882*62c56f98SSadaf Ebrahimi :param test_dependencies: Dependencies 883*62c56f98SSadaf Ebrahimi :param unique_dependencies: Mutable list to track unique dependencies 884*62c56f98SSadaf Ebrahimi that are global to this re-entrant function. 885*62c56f98SSadaf Ebrahimi :return: returns dependency check code. 886*62c56f98SSadaf Ebrahimi """ 887*62c56f98SSadaf Ebrahimi dep_check_code = '' 888*62c56f98SSadaf Ebrahimi if test_dependencies: 889*62c56f98SSadaf Ebrahimi out_data_f.write('depends_on') 890*62c56f98SSadaf Ebrahimi for dep in test_dependencies: 891*62c56f98SSadaf Ebrahimi if dep not in unique_dependencies: 892*62c56f98SSadaf Ebrahimi unique_dependencies.append(dep) 893*62c56f98SSadaf Ebrahimi dep_id = unique_dependencies.index(dep) 894*62c56f98SSadaf Ebrahimi dep_check_code += gen_dep_check(dep_id, dep) 895*62c56f98SSadaf Ebrahimi else: 896*62c56f98SSadaf Ebrahimi dep_id = unique_dependencies.index(dep) 897*62c56f98SSadaf Ebrahimi out_data_f.write(':' + str(dep_id)) 898*62c56f98SSadaf Ebrahimi out_data_f.write('\n') 899*62c56f98SSadaf Ebrahimi return dep_check_code 900*62c56f98SSadaf Ebrahimi 901*62c56f98SSadaf Ebrahimi 902*62c56f98SSadaf EbrahimiINT_VAL_REGEX = re.compile(r'-?(\d+|0x[0-9a-f]+)$', re.I) 903*62c56f98SSadaf Ebrahimidef val_is_int(val: str) -> bool: 904*62c56f98SSadaf Ebrahimi """Whether val is suitable as an 'int' parameter in the .datax file.""" 905*62c56f98SSadaf Ebrahimi if not INT_VAL_REGEX.match(val): 906*62c56f98SSadaf Ebrahimi return False 907*62c56f98SSadaf Ebrahimi # Limit the range to what is guaranteed to get through strtol() 908*62c56f98SSadaf Ebrahimi return abs(int(val, 0)) <= 0x7fffffff 909*62c56f98SSadaf Ebrahimi 910*62c56f98SSadaf Ebrahimidef write_parameters(out_data_f, test_args, func_args, unique_expressions): 911*62c56f98SSadaf Ebrahimi """ 912*62c56f98SSadaf Ebrahimi Writes test parameters to the intermediate data file, replacing 913*62c56f98SSadaf Ebrahimi the string form with identifiers. Also, generates expression 914*62c56f98SSadaf Ebrahimi check code. 915*62c56f98SSadaf Ebrahimi 916*62c56f98SSadaf Ebrahimi :param out_data_f: Output intermediate data file 917*62c56f98SSadaf Ebrahimi :param test_args: Test parameters 918*62c56f98SSadaf Ebrahimi :param func_args: Function arguments 919*62c56f98SSadaf Ebrahimi :param unique_expressions: Mutable list to track unique 920*62c56f98SSadaf Ebrahimi expressions that are global to this re-entrant function. 921*62c56f98SSadaf Ebrahimi :return: Returns expression check code. 922*62c56f98SSadaf Ebrahimi """ 923*62c56f98SSadaf Ebrahimi expression_code = '' 924*62c56f98SSadaf Ebrahimi for i, _ in enumerate(test_args): 925*62c56f98SSadaf Ebrahimi typ = func_args[i] 926*62c56f98SSadaf Ebrahimi val = test_args[i] 927*62c56f98SSadaf Ebrahimi 928*62c56f98SSadaf Ebrahimi # Pass small integer constants literally. This reduces the size of 929*62c56f98SSadaf Ebrahimi # the C code. Register anything else as an expression. 930*62c56f98SSadaf Ebrahimi if typ == 'int' and not val_is_int(val): 931*62c56f98SSadaf Ebrahimi typ = 'exp' 932*62c56f98SSadaf Ebrahimi if val not in unique_expressions: 933*62c56f98SSadaf Ebrahimi unique_expressions.append(val) 934*62c56f98SSadaf Ebrahimi # exp_id can be derived from len(). But for 935*62c56f98SSadaf Ebrahimi # readability and consistency with case of existing 936*62c56f98SSadaf Ebrahimi # let's use index(). 937*62c56f98SSadaf Ebrahimi exp_id = unique_expressions.index(val) 938*62c56f98SSadaf Ebrahimi expression_code += gen_expression_check(exp_id, val) 939*62c56f98SSadaf Ebrahimi val = exp_id 940*62c56f98SSadaf Ebrahimi else: 941*62c56f98SSadaf Ebrahimi val = unique_expressions.index(val) 942*62c56f98SSadaf Ebrahimi out_data_f.write(':' + typ + ':' + str(val)) 943*62c56f98SSadaf Ebrahimi out_data_f.write('\n') 944*62c56f98SSadaf Ebrahimi return expression_code 945*62c56f98SSadaf Ebrahimi 946*62c56f98SSadaf Ebrahimi 947*62c56f98SSadaf Ebrahimidef gen_suite_dep_checks(suite_dependencies, dep_check_code, expression_code): 948*62c56f98SSadaf Ebrahimi """ 949*62c56f98SSadaf Ebrahimi Generates preprocessor checks for test suite dependencies. 950*62c56f98SSadaf Ebrahimi 951*62c56f98SSadaf Ebrahimi :param suite_dependencies: Test suite dependencies read from the 952*62c56f98SSadaf Ebrahimi .function file. 953*62c56f98SSadaf Ebrahimi :param dep_check_code: Dependency check code 954*62c56f98SSadaf Ebrahimi :param expression_code: Expression check code 955*62c56f98SSadaf Ebrahimi :return: Dependency and expression code guarded by test suite 956*62c56f98SSadaf Ebrahimi dependencies. 957*62c56f98SSadaf Ebrahimi """ 958*62c56f98SSadaf Ebrahimi if suite_dependencies: 959*62c56f98SSadaf Ebrahimi preprocessor_check = gen_dependencies_one_line(suite_dependencies) 960*62c56f98SSadaf Ebrahimi dep_check_code = ''' 961*62c56f98SSadaf Ebrahimi{preprocessor_check} 962*62c56f98SSadaf Ebrahimi{code} 963*62c56f98SSadaf Ebrahimi#endif 964*62c56f98SSadaf Ebrahimi'''.format(preprocessor_check=preprocessor_check, code=dep_check_code) 965*62c56f98SSadaf Ebrahimi expression_code = ''' 966*62c56f98SSadaf Ebrahimi{preprocessor_check} 967*62c56f98SSadaf Ebrahimi{code} 968*62c56f98SSadaf Ebrahimi#endif 969*62c56f98SSadaf Ebrahimi'''.format(preprocessor_check=preprocessor_check, code=expression_code) 970*62c56f98SSadaf Ebrahimi return dep_check_code, expression_code 971*62c56f98SSadaf Ebrahimi 972*62c56f98SSadaf Ebrahimi 973*62c56f98SSadaf Ebrahimidef get_function_info(func_info, function_name, line_no): 974*62c56f98SSadaf Ebrahimi """Look up information about a test function by name. 975*62c56f98SSadaf Ebrahimi 976*62c56f98SSadaf Ebrahimi Raise an informative expression if function_name is not found. 977*62c56f98SSadaf Ebrahimi 978*62c56f98SSadaf Ebrahimi :param func_info: dictionary mapping function names to their information. 979*62c56f98SSadaf Ebrahimi :param function_name: the function name as written in the .function and 980*62c56f98SSadaf Ebrahimi .data files. 981*62c56f98SSadaf Ebrahimi :param line_no: line number for error messages. 982*62c56f98SSadaf Ebrahimi :return Function information (id, args). 983*62c56f98SSadaf Ebrahimi """ 984*62c56f98SSadaf Ebrahimi test_function_name = 'test_' + function_name 985*62c56f98SSadaf Ebrahimi if test_function_name not in func_info: 986*62c56f98SSadaf Ebrahimi raise GeneratorInputError("%d: Function %s not found!" % 987*62c56f98SSadaf Ebrahimi (line_no, test_function_name)) 988*62c56f98SSadaf Ebrahimi return func_info[test_function_name] 989*62c56f98SSadaf Ebrahimi 990*62c56f98SSadaf Ebrahimi 991*62c56f98SSadaf Ebrahimidef gen_from_test_data(data_f, out_data_f, func_info, suite_dependencies): 992*62c56f98SSadaf Ebrahimi """ 993*62c56f98SSadaf Ebrahimi This function reads test case name, dependencies and test vectors 994*62c56f98SSadaf Ebrahimi from the .data file. This information is correlated with the test 995*62c56f98SSadaf Ebrahimi functions file for generating an intermediate data file replacing 996*62c56f98SSadaf Ebrahimi the strings for test function names, dependencies and integer 997*62c56f98SSadaf Ebrahimi constant expressions with identifiers. Mainly for optimising 998*62c56f98SSadaf Ebrahimi space for on-target execution. 999*62c56f98SSadaf Ebrahimi It also generates test case dependency check code and expression 1000*62c56f98SSadaf Ebrahimi evaluation code. 1001*62c56f98SSadaf Ebrahimi 1002*62c56f98SSadaf Ebrahimi :param data_f: Data file object 1003*62c56f98SSadaf Ebrahimi :param out_data_f: Output intermediate data file 1004*62c56f98SSadaf Ebrahimi :param func_info: Dict keyed by function and with function id 1005*62c56f98SSadaf Ebrahimi and arguments info 1006*62c56f98SSadaf Ebrahimi :param suite_dependencies: Test suite dependencies 1007*62c56f98SSadaf Ebrahimi :return: Returns dependency and expression check code 1008*62c56f98SSadaf Ebrahimi """ 1009*62c56f98SSadaf Ebrahimi unique_dependencies = [] 1010*62c56f98SSadaf Ebrahimi unique_expressions = [] 1011*62c56f98SSadaf Ebrahimi dep_check_code = '' 1012*62c56f98SSadaf Ebrahimi expression_code = '' 1013*62c56f98SSadaf Ebrahimi for line_no, test_name, function_name, test_dependencies, test_args in \ 1014*62c56f98SSadaf Ebrahimi parse_test_data(data_f): 1015*62c56f98SSadaf Ebrahimi out_data_f.write(test_name + '\n') 1016*62c56f98SSadaf Ebrahimi 1017*62c56f98SSadaf Ebrahimi # Write dependencies 1018*62c56f98SSadaf Ebrahimi dep_check_code += write_dependencies(out_data_f, test_dependencies, 1019*62c56f98SSadaf Ebrahimi unique_dependencies) 1020*62c56f98SSadaf Ebrahimi 1021*62c56f98SSadaf Ebrahimi # Write test function name 1022*62c56f98SSadaf Ebrahimi func_id, func_args = \ 1023*62c56f98SSadaf Ebrahimi get_function_info(func_info, function_name, line_no) 1024*62c56f98SSadaf Ebrahimi out_data_f.write(str(func_id)) 1025*62c56f98SSadaf Ebrahimi 1026*62c56f98SSadaf Ebrahimi # Write parameters 1027*62c56f98SSadaf Ebrahimi if len(test_args) != len(func_args): 1028*62c56f98SSadaf Ebrahimi raise GeneratorInputError("%d: Invalid number of arguments in test " 1029*62c56f98SSadaf Ebrahimi "%s. See function %s signature." % 1030*62c56f98SSadaf Ebrahimi (line_no, test_name, function_name)) 1031*62c56f98SSadaf Ebrahimi expression_code += write_parameters(out_data_f, test_args, func_args, 1032*62c56f98SSadaf Ebrahimi unique_expressions) 1033*62c56f98SSadaf Ebrahimi 1034*62c56f98SSadaf Ebrahimi # Write a newline as test case separator 1035*62c56f98SSadaf Ebrahimi out_data_f.write('\n') 1036*62c56f98SSadaf Ebrahimi 1037*62c56f98SSadaf Ebrahimi dep_check_code, expression_code = gen_suite_dep_checks( 1038*62c56f98SSadaf Ebrahimi suite_dependencies, dep_check_code, expression_code) 1039*62c56f98SSadaf Ebrahimi return dep_check_code, expression_code 1040*62c56f98SSadaf Ebrahimi 1041*62c56f98SSadaf Ebrahimi 1042*62c56f98SSadaf Ebrahimidef add_input_info(funcs_file, data_file, template_file, 1043*62c56f98SSadaf Ebrahimi c_file, snippets): 1044*62c56f98SSadaf Ebrahimi """ 1045*62c56f98SSadaf Ebrahimi Add generator input info in snippets. 1046*62c56f98SSadaf Ebrahimi 1047*62c56f98SSadaf Ebrahimi :param funcs_file: Functions file object 1048*62c56f98SSadaf Ebrahimi :param data_file: Data file object 1049*62c56f98SSadaf Ebrahimi :param template_file: Template file object 1050*62c56f98SSadaf Ebrahimi :param c_file: Output C file object 1051*62c56f98SSadaf Ebrahimi :param snippets: Dictionary to contain code pieces to be 1052*62c56f98SSadaf Ebrahimi substituted in the template. 1053*62c56f98SSadaf Ebrahimi :return: 1054*62c56f98SSadaf Ebrahimi """ 1055*62c56f98SSadaf Ebrahimi snippets['test_file'] = c_file 1056*62c56f98SSadaf Ebrahimi snippets['test_main_file'] = template_file 1057*62c56f98SSadaf Ebrahimi snippets['test_case_file'] = funcs_file 1058*62c56f98SSadaf Ebrahimi snippets['test_case_data_file'] = data_file 1059*62c56f98SSadaf Ebrahimi 1060*62c56f98SSadaf Ebrahimi 1061*62c56f98SSadaf Ebrahimidef read_code_from_input_files(platform_file, helpers_file, 1062*62c56f98SSadaf Ebrahimi out_data_file, snippets): 1063*62c56f98SSadaf Ebrahimi """ 1064*62c56f98SSadaf Ebrahimi Read code from input files and create substitutions for replacement 1065*62c56f98SSadaf Ebrahimi strings in the template file. 1066*62c56f98SSadaf Ebrahimi 1067*62c56f98SSadaf Ebrahimi :param platform_file: Platform file object 1068*62c56f98SSadaf Ebrahimi :param helpers_file: Helper functions file object 1069*62c56f98SSadaf Ebrahimi :param out_data_file: Output intermediate data file object 1070*62c56f98SSadaf Ebrahimi :param snippets: Dictionary to contain code pieces to be 1071*62c56f98SSadaf Ebrahimi substituted in the template. 1072*62c56f98SSadaf Ebrahimi :return: 1073*62c56f98SSadaf Ebrahimi """ 1074*62c56f98SSadaf Ebrahimi # Read helpers 1075*62c56f98SSadaf Ebrahimi with open(helpers_file, 'r') as help_f, open(platform_file, 'r') as \ 1076*62c56f98SSadaf Ebrahimi platform_f: 1077*62c56f98SSadaf Ebrahimi snippets['test_common_helper_file'] = helpers_file 1078*62c56f98SSadaf Ebrahimi snippets['test_common_helpers'] = help_f.read() 1079*62c56f98SSadaf Ebrahimi snippets['test_platform_file'] = platform_file 1080*62c56f98SSadaf Ebrahimi snippets['platform_code'] = platform_f.read().replace( 1081*62c56f98SSadaf Ebrahimi 'DATA_FILE', out_data_file.replace('\\', '\\\\')) # escape '\' 1082*62c56f98SSadaf Ebrahimi 1083*62c56f98SSadaf Ebrahimi 1084*62c56f98SSadaf Ebrahimidef write_test_source_file(template_file, c_file, snippets): 1085*62c56f98SSadaf Ebrahimi """ 1086*62c56f98SSadaf Ebrahimi Write output source file with generated source code. 1087*62c56f98SSadaf Ebrahimi 1088*62c56f98SSadaf Ebrahimi :param template_file: Template file name 1089*62c56f98SSadaf Ebrahimi :param c_file: Output source file 1090*62c56f98SSadaf Ebrahimi :param snippets: Generated and code snippets 1091*62c56f98SSadaf Ebrahimi :return: 1092*62c56f98SSadaf Ebrahimi """ 1093*62c56f98SSadaf Ebrahimi 1094*62c56f98SSadaf Ebrahimi # Create a placeholder pattern with the correct named capture groups 1095*62c56f98SSadaf Ebrahimi # to override the default provided with Template. 1096*62c56f98SSadaf Ebrahimi # Match nothing (no way of escaping placeholders). 1097*62c56f98SSadaf Ebrahimi escaped = "(?P<escaped>(?!))" 1098*62c56f98SSadaf Ebrahimi # Match the "__MBEDTLS_TEST_TEMPLATE__PLACEHOLDER_NAME" pattern. 1099*62c56f98SSadaf Ebrahimi named = "__MBEDTLS_TEST_TEMPLATE__(?P<named>[A-Z][_A-Z0-9]*)" 1100*62c56f98SSadaf Ebrahimi # Match nothing (no braced placeholder syntax). 1101*62c56f98SSadaf Ebrahimi braced = "(?P<braced>(?!))" 1102*62c56f98SSadaf Ebrahimi # If not already matched, a "__MBEDTLS_TEST_TEMPLATE__" prefix is invalid. 1103*62c56f98SSadaf Ebrahimi invalid = "(?P<invalid>__MBEDTLS_TEST_TEMPLATE__)" 1104*62c56f98SSadaf Ebrahimi placeholder_pattern = re.compile("|".join([escaped, named, braced, invalid])) 1105*62c56f98SSadaf Ebrahimi 1106*62c56f98SSadaf Ebrahimi with open(template_file, 'r') as template_f, open(c_file, 'w') as c_f: 1107*62c56f98SSadaf Ebrahimi for line_no, line in enumerate(template_f.readlines(), 1): 1108*62c56f98SSadaf Ebrahimi # Update line number. +1 as #line directive sets next line number 1109*62c56f98SSadaf Ebrahimi snippets['line_no'] = line_no + 1 1110*62c56f98SSadaf Ebrahimi template = string.Template(line) 1111*62c56f98SSadaf Ebrahimi template.pattern = placeholder_pattern 1112*62c56f98SSadaf Ebrahimi snippets = {k.upper():v for (k, v) in snippets.items()} 1113*62c56f98SSadaf Ebrahimi code = template.substitute(**snippets) 1114*62c56f98SSadaf Ebrahimi c_f.write(code) 1115*62c56f98SSadaf Ebrahimi 1116*62c56f98SSadaf Ebrahimi 1117*62c56f98SSadaf Ebrahimidef parse_function_file(funcs_file, snippets): 1118*62c56f98SSadaf Ebrahimi """ 1119*62c56f98SSadaf Ebrahimi Parse function file and generate function dispatch code. 1120*62c56f98SSadaf Ebrahimi 1121*62c56f98SSadaf Ebrahimi :param funcs_file: Functions file name 1122*62c56f98SSadaf Ebrahimi :param snippets: Dictionary to contain code pieces to be 1123*62c56f98SSadaf Ebrahimi substituted in the template. 1124*62c56f98SSadaf Ebrahimi :return: 1125*62c56f98SSadaf Ebrahimi """ 1126*62c56f98SSadaf Ebrahimi with FileWrapper(funcs_file) as funcs_f: 1127*62c56f98SSadaf Ebrahimi suite_dependencies, dispatch_code, func_code, func_info = \ 1128*62c56f98SSadaf Ebrahimi parse_functions(funcs_f) 1129*62c56f98SSadaf Ebrahimi snippets['functions_code'] = func_code 1130*62c56f98SSadaf Ebrahimi snippets['dispatch_code'] = dispatch_code 1131*62c56f98SSadaf Ebrahimi return suite_dependencies, func_info 1132*62c56f98SSadaf Ebrahimi 1133*62c56f98SSadaf Ebrahimi 1134*62c56f98SSadaf Ebrahimidef generate_intermediate_data_file(data_file, out_data_file, 1135*62c56f98SSadaf Ebrahimi suite_dependencies, func_info, snippets): 1136*62c56f98SSadaf Ebrahimi """ 1137*62c56f98SSadaf Ebrahimi Generates intermediate data file from input data file and 1138*62c56f98SSadaf Ebrahimi information read from functions file. 1139*62c56f98SSadaf Ebrahimi 1140*62c56f98SSadaf Ebrahimi :param data_file: Data file name 1141*62c56f98SSadaf Ebrahimi :param out_data_file: Output/Intermediate data file 1142*62c56f98SSadaf Ebrahimi :param suite_dependencies: List of suite dependencies. 1143*62c56f98SSadaf Ebrahimi :param func_info: Function info parsed from functions file. 1144*62c56f98SSadaf Ebrahimi :param snippets: Dictionary to contain code pieces to be 1145*62c56f98SSadaf Ebrahimi substituted in the template. 1146*62c56f98SSadaf Ebrahimi :return: 1147*62c56f98SSadaf Ebrahimi """ 1148*62c56f98SSadaf Ebrahimi with FileWrapper(data_file) as data_f, \ 1149*62c56f98SSadaf Ebrahimi open(out_data_file, 'w') as out_data_f: 1150*62c56f98SSadaf Ebrahimi dep_check_code, expression_code = gen_from_test_data( 1151*62c56f98SSadaf Ebrahimi data_f, out_data_f, func_info, suite_dependencies) 1152*62c56f98SSadaf Ebrahimi snippets['dep_check_code'] = dep_check_code 1153*62c56f98SSadaf Ebrahimi snippets['expression_code'] = expression_code 1154*62c56f98SSadaf Ebrahimi 1155*62c56f98SSadaf Ebrahimi 1156*62c56f98SSadaf Ebrahimidef generate_code(**input_info): 1157*62c56f98SSadaf Ebrahimi """ 1158*62c56f98SSadaf Ebrahimi Generates C source code from test suite file, data file, common 1159*62c56f98SSadaf Ebrahimi helpers file and platform file. 1160*62c56f98SSadaf Ebrahimi 1161*62c56f98SSadaf Ebrahimi input_info expands to following parameters: 1162*62c56f98SSadaf Ebrahimi funcs_file: Functions file object 1163*62c56f98SSadaf Ebrahimi data_file: Data file object 1164*62c56f98SSadaf Ebrahimi template_file: Template file object 1165*62c56f98SSadaf Ebrahimi platform_file: Platform file object 1166*62c56f98SSadaf Ebrahimi helpers_file: Helper functions file object 1167*62c56f98SSadaf Ebrahimi suites_dir: Test suites dir 1168*62c56f98SSadaf Ebrahimi c_file: Output C file object 1169*62c56f98SSadaf Ebrahimi out_data_file: Output intermediate data file object 1170*62c56f98SSadaf Ebrahimi :return: 1171*62c56f98SSadaf Ebrahimi """ 1172*62c56f98SSadaf Ebrahimi funcs_file = input_info['funcs_file'] 1173*62c56f98SSadaf Ebrahimi data_file = input_info['data_file'] 1174*62c56f98SSadaf Ebrahimi template_file = input_info['template_file'] 1175*62c56f98SSadaf Ebrahimi platform_file = input_info['platform_file'] 1176*62c56f98SSadaf Ebrahimi helpers_file = input_info['helpers_file'] 1177*62c56f98SSadaf Ebrahimi suites_dir = input_info['suites_dir'] 1178*62c56f98SSadaf Ebrahimi c_file = input_info['c_file'] 1179*62c56f98SSadaf Ebrahimi out_data_file = input_info['out_data_file'] 1180*62c56f98SSadaf Ebrahimi for name, path in [('Functions file', funcs_file), 1181*62c56f98SSadaf Ebrahimi ('Data file', data_file), 1182*62c56f98SSadaf Ebrahimi ('Template file', template_file), 1183*62c56f98SSadaf Ebrahimi ('Platform file', platform_file), 1184*62c56f98SSadaf Ebrahimi ('Helpers code file', helpers_file), 1185*62c56f98SSadaf Ebrahimi ('Suites dir', suites_dir)]: 1186*62c56f98SSadaf Ebrahimi if not os.path.exists(path): 1187*62c56f98SSadaf Ebrahimi raise IOError("ERROR: %s [%s] not found!" % (name, path)) 1188*62c56f98SSadaf Ebrahimi 1189*62c56f98SSadaf Ebrahimi snippets = {'generator_script': os.path.basename(__file__)} 1190*62c56f98SSadaf Ebrahimi read_code_from_input_files(platform_file, helpers_file, 1191*62c56f98SSadaf Ebrahimi out_data_file, snippets) 1192*62c56f98SSadaf Ebrahimi add_input_info(funcs_file, data_file, template_file, 1193*62c56f98SSadaf Ebrahimi c_file, snippets) 1194*62c56f98SSadaf Ebrahimi suite_dependencies, func_info = parse_function_file(funcs_file, snippets) 1195*62c56f98SSadaf Ebrahimi generate_intermediate_data_file(data_file, out_data_file, 1196*62c56f98SSadaf Ebrahimi suite_dependencies, func_info, snippets) 1197*62c56f98SSadaf Ebrahimi write_test_source_file(template_file, c_file, snippets) 1198*62c56f98SSadaf Ebrahimi 1199*62c56f98SSadaf Ebrahimi 1200*62c56f98SSadaf Ebrahimidef main(): 1201*62c56f98SSadaf Ebrahimi """ 1202*62c56f98SSadaf Ebrahimi Command line parser. 1203*62c56f98SSadaf Ebrahimi 1204*62c56f98SSadaf Ebrahimi :return: 1205*62c56f98SSadaf Ebrahimi """ 1206*62c56f98SSadaf Ebrahimi parser = argparse.ArgumentParser( 1207*62c56f98SSadaf Ebrahimi description='Dynamically generate test suite code.') 1208*62c56f98SSadaf Ebrahimi 1209*62c56f98SSadaf Ebrahimi parser.add_argument("-f", "--functions-file", 1210*62c56f98SSadaf Ebrahimi dest="funcs_file", 1211*62c56f98SSadaf Ebrahimi help="Functions file", 1212*62c56f98SSadaf Ebrahimi metavar="FUNCTIONS_FILE", 1213*62c56f98SSadaf Ebrahimi required=True) 1214*62c56f98SSadaf Ebrahimi 1215*62c56f98SSadaf Ebrahimi parser.add_argument("-d", "--data-file", 1216*62c56f98SSadaf Ebrahimi dest="data_file", 1217*62c56f98SSadaf Ebrahimi help="Data file", 1218*62c56f98SSadaf Ebrahimi metavar="DATA_FILE", 1219*62c56f98SSadaf Ebrahimi required=True) 1220*62c56f98SSadaf Ebrahimi 1221*62c56f98SSadaf Ebrahimi parser.add_argument("-t", "--template-file", 1222*62c56f98SSadaf Ebrahimi dest="template_file", 1223*62c56f98SSadaf Ebrahimi help="Template file", 1224*62c56f98SSadaf Ebrahimi metavar="TEMPLATE_FILE", 1225*62c56f98SSadaf Ebrahimi required=True) 1226*62c56f98SSadaf Ebrahimi 1227*62c56f98SSadaf Ebrahimi parser.add_argument("-s", "--suites-dir", 1228*62c56f98SSadaf Ebrahimi dest="suites_dir", 1229*62c56f98SSadaf Ebrahimi help="Suites dir", 1230*62c56f98SSadaf Ebrahimi metavar="SUITES_DIR", 1231*62c56f98SSadaf Ebrahimi required=True) 1232*62c56f98SSadaf Ebrahimi 1233*62c56f98SSadaf Ebrahimi parser.add_argument("--helpers-file", 1234*62c56f98SSadaf Ebrahimi dest="helpers_file", 1235*62c56f98SSadaf Ebrahimi help="Helpers file", 1236*62c56f98SSadaf Ebrahimi metavar="HELPERS_FILE", 1237*62c56f98SSadaf Ebrahimi required=True) 1238*62c56f98SSadaf Ebrahimi 1239*62c56f98SSadaf Ebrahimi parser.add_argument("-p", "--platform-file", 1240*62c56f98SSadaf Ebrahimi dest="platform_file", 1241*62c56f98SSadaf Ebrahimi help="Platform code file", 1242*62c56f98SSadaf Ebrahimi metavar="PLATFORM_FILE", 1243*62c56f98SSadaf Ebrahimi required=True) 1244*62c56f98SSadaf Ebrahimi 1245*62c56f98SSadaf Ebrahimi parser.add_argument("-o", "--out-dir", 1246*62c56f98SSadaf Ebrahimi dest="out_dir", 1247*62c56f98SSadaf Ebrahimi help="Dir where generated code and scripts are copied", 1248*62c56f98SSadaf Ebrahimi metavar="OUT_DIR", 1249*62c56f98SSadaf Ebrahimi required=True) 1250*62c56f98SSadaf Ebrahimi 1251*62c56f98SSadaf Ebrahimi args = parser.parse_args() 1252*62c56f98SSadaf Ebrahimi 1253*62c56f98SSadaf Ebrahimi data_file_name = os.path.basename(args.data_file) 1254*62c56f98SSadaf Ebrahimi data_name = os.path.splitext(data_file_name)[0] 1255*62c56f98SSadaf Ebrahimi 1256*62c56f98SSadaf Ebrahimi out_c_file = os.path.join(args.out_dir, data_name + '.c') 1257*62c56f98SSadaf Ebrahimi out_data_file = os.path.join(args.out_dir, data_name + '.datax') 1258*62c56f98SSadaf Ebrahimi 1259*62c56f98SSadaf Ebrahimi out_c_file_dir = os.path.dirname(out_c_file) 1260*62c56f98SSadaf Ebrahimi out_data_file_dir = os.path.dirname(out_data_file) 1261*62c56f98SSadaf Ebrahimi for directory in [out_c_file_dir, out_data_file_dir]: 1262*62c56f98SSadaf Ebrahimi if not os.path.exists(directory): 1263*62c56f98SSadaf Ebrahimi os.makedirs(directory) 1264*62c56f98SSadaf Ebrahimi 1265*62c56f98SSadaf Ebrahimi generate_code(funcs_file=args.funcs_file, data_file=args.data_file, 1266*62c56f98SSadaf Ebrahimi template_file=args.template_file, 1267*62c56f98SSadaf Ebrahimi platform_file=args.platform_file, 1268*62c56f98SSadaf Ebrahimi helpers_file=args.helpers_file, suites_dir=args.suites_dir, 1269*62c56f98SSadaf Ebrahimi c_file=out_c_file, out_data_file=out_data_file) 1270*62c56f98SSadaf Ebrahimi 1271*62c56f98SSadaf Ebrahimi 1272*62c56f98SSadaf Ebrahimiif __name__ == "__main__": 1273*62c56f98SSadaf Ebrahimi try: 1274*62c56f98SSadaf Ebrahimi main() 1275*62c56f98SSadaf Ebrahimi except GeneratorInputError as err: 1276*62c56f98SSadaf Ebrahimi sys.exit("%s: input error: %s" % 1277*62c56f98SSadaf Ebrahimi (os.path.basename(sys.argv[0]), str(err))) 1278